I’m using a Ticker to periodically a function whose purpose is to send a packet to a multicast address every few seconds. It’s a form of simple service announcement.
This is fine and the sendMulticast method is called and I send a precreated packet to a UDP socket that’s been set up before wnd which is working fine for other data.
// send a packet to a precreated address
nsapi_error_t status = udpSocket.sendto(address, data, sze);
This however hard faults immediately. In another case I handle a UDP socket using a sigio() callback and there I can in the interrupt handler read data from the socket and also write replies. What is special about Ticker that it should crash if I write to a socket in its interrupt handler? I handle a few serial buses (RS-485, Modbus) the same way and read in their interrupt handlers as well as write without issues. I also use a Ticker to handle some RS-485 sending and that also works fine. Is UDPSocket (LWIP) somehow special in that it somehow breaks when used according to the docs? The Ticker docs actually do not really say anything about what you can and can not do in an interrupt handler.
Oh, and I see nothing on the serial console. It just hard faults and the hardware watchdog reboots the board after a while. Sometimes I do see errors on the serial console, but 95% of crashes just go boom.
One way to get out of an ISR is to use an EventQueue, but that requires a thread too, and we simply do not have memory for more threads. This board hard 32kB of memory and we’re constantly shaving off bytes here and there to keep the whole thing alive.
I did try to use an EventQueue too, as it seems that it doesn’t require a thread after all. However the docs are somewhat sparse, so I’m probably using it wrong as I get no events to be sent. I saw this example in the code:
#include "mbed.h"
void handler(int data) { ... }
class Device {
public:
void handler(int data) { ... }
};
Device dev;
// queue with not internal storage for dynamic events
// accepts only user allocated events
static EventQueue queue(0);
// Create events
static auto e1 = make_user_allocated_event(&dev, Device::handler, 2);
static auto e2 = queue.make_user_allocated_event(handler, 3);
int main()
{
e1.call_on(&queue);
e2.call();
queue.dispatch(1);
}
This seems like it’s enough to post my events to the queue, something that would call sendMulticast and then call eventQueue.dispatch(1) to allow it to dispatch events for 1ms and thus dispatch the one added event. Doesn’t seem to work that way though?
Ah, I had a copy and paste error and my event called the wrong function. But even then the code hard faults when it tries to send the packet to the socket. So the ISR perhaps isn’t the problem, as the event handler is run in a “user context”. I would assume it to be totally impossible for a call to UDPSocket.sendTo() to totally crash, to me it seems a bit like a bug.
Thank you for the help and for following my adventures this far. I will now try to dig onwards and see what I can come up with.
I believe sendto() cannot be called in the ISR context because of Mutex. Maybe I don’t fully understand what you are trying to do… But can’t you just do something like this using EventQueue?
#include "mbed.h"
EventQueue *queue = mbed_event_queue();
NetworkInterface* net;
Ticker ticker;
void udpSend() {
UDPSocket sock;
sock.open(net);
char out_buffer[] = "time";
nsapi_error_t status = sock.sendto("time.nist.gov", 37, out_buffer, sizeof(out_buffer));
printf("status: %d\n\r", status);
sock.close();
}
void tickerCallback() {
queue->call(&udpSend); // This defers the execution to a user context
}
int main() {
net = NetworkInterface::get_default_instance();
net->connect();
ticker.attach(&tickerCallback, 1.0);
queue->dispatch_forever();
}
I don’t have a spare thread that could do queue->dispatch_forever(), I’m trying to manage without new threads. And based on the example I found it should work just fine.
This works without crashes. But when I print the two counters after running some minutes I see that announcementCallbackCounter has incremented to about 200 (running for 400 seconds or so), but that sendMulticastCounter is just 5. From what I understand those should be exactly equal. Checking the code for dispatch() I see that eventually this is called:
// Dispatch events
//
// Executes events until the specified milliseconds have passed. If ms is
// negative, equeue_dispatch will dispatch events indefinitely or until
// equeue_break is called on this queue.
//
// When called with a finite timeout, the equeue_dispatch function is
// guaranteed to terminate. When called with a timeout of 0, the
// equeue_dispatch does not wait and is irq safe.
void equeue_dispatch(equeue_t *queue, int ms);
So calling it with a timeout of 0 ms should clear the queue but not wait for any new events. But that definitely does not happen here.
No, I can not do any logging in those functions, as that crashes immediately. Can the issue be that I’m doing all this setup already in a thread, not the main context?
All in all, not terribly impressed right now. But will soldier on. Very thankful for all the help so far!
This board hard 32kB of memory and we’re constantly shaving off bytes here and there to keep the whole thing alive.
The LPC1768 is equipped with 64kB (2 x 32kB). The upper 32kB block is reserved/used for/by the Ethernet, USB or CAN drivers.
You can use an EventQueue to periodically call a callback rather than a Ticker for example as below. Please notice that it is not intended as a working program.
If I try to use an event queue to handle incoming TCP connections and I/O on accepted TCP sockets, I find that the event queue is slow. Often it takes 1-2 seconds before my “user space” callback is called and that is too slow. Handling all I/O directly in a sigio() handler gives good response times, but my app seems to freeze after I close() a socket. The docs seem to imply I must call close(), and that will free resources. Calling delete on the sockets hard faults immediately. But close() freezes, even though I immediately after that stop using the pointer.
But from my understanding EventQueue.call() should be pretty much without any delay? The thread that serves the event queue is not doing anything else at all.
Is there really no good way to create a poll()/select() like thing with MBed? Handling a bit of I/O has turned into a real nightmare of frustrating issues.
Yeah, not calling close() on client sockets after the connection has closed seems to fix my freeze. But that’s likely not good, probably a massive resource leak?
Trying the EventQueue route again, but it’s just too slow, the client in the other end times out before the MBed server has responded. It takes 3 seconds after the accept() until the data is received. By looking at Wireshark I see that the packet arrived immediately after the accepting handshake. So something makes this not work sanely at all.
The three seconds is between the two log messages. Also my handleIncomingClient is always called twice. The first accepts the incoming client and the second comes immediately after and fails with -3005 (NSAPI_ERROR_NO_SOCKET). Why would this get triggered twice? Or can sigio not be used as a reliable mechanism for detecting incoming clients?
To me this looks like a “by the book” approach to handling clients, but it can’t be this slow?
You can create as many additional threads for additional tasks, like air conditioning, motor drivers etc., as you need and the MPU RAM allows for. You can also let the other threads run longer by increasing the sleep time of the recvPoll thread. They will all run in parallel.
There isn’t much memory, so keeping threads to a minimum is paramount. We already have to have a TCP/IP stack there, file system, USB handling etc. The thread I created for the event queue was about as much as can be afforded now. I even gave it a much higher priority, but I still see delays between accepting a connection to reading the first bytes between 300ms to 3000ms.