ssilverman / QNEthernet

An lwIP-based Ethernet library for Teensy 4.1 and possibly some other platforms
GNU Affero General Public License v3.0
81 stars 24 forks source link

Sending UDP Packet from Interrupt Context #35

Closed flybrianfly closed 1 year ago

flybrianfly commented 1 year ago

Hello, I'm trying to send a UDP packet from interrupt context. I know that's generally not advised, but I'm wondering if you know of a potential solution.

This is a minimum example:

#include "QNEthernet.h"
#include <TimerOne.h>

namespace qn = qindesign::network;

qn::EthernetUDP udp(100);

IPAddress ip{192, 168, 1, 254};
IPAddress mask{255, 255, 255, 0};
IPAddress gateway{192, 168, 1, 1};
IPAddress broadcast_ip;

uint8_t buf[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};

int port = 2020;

void Send() {
  Serial.println("SENDING");
  udp.send(broadcast_ip, port, buf, sizeof(buf));
}

void setup() {
  Serial.begin(115200);
  while (!Serial) {}
  if (!qn::Ethernet.begin(ip, mask, gateway)) {
    Serial.println("ERROR: unable to start ethernet interface");
    while (1) {}
  }
  broadcast_ip = qn::Ethernet.broadcastIP();
  Serial.println("START");
  Timer1.initialize(10000);
  Timer1.attachInterrupt(Send);
}

void loop() {}

Works if I send in loop, but not if I try to send in interrupt context. I see the "START" on the serial monitor and that's it. In the actual code, I have a datalogging task in loop (flushing a circular buffer to SD) and a data acquisition and telemetry task in interrupt context. Would like to use UDP to send telemetry packets and can't afford the jitter from doing it in the loop alongside the datalogging task.

ssilverman commented 1 year ago

I’m curious, what kind of sending jitter are you seeing when you put the UDP transmission in loop()?

ssilverman commented 1 year ago

Question two: Is it possible to split up the flushing so that sending UDP packets (rather, checking a timeout and possibly sending) gets interleaved with the flush operation(s)?

flybrianfly commented 1 year ago

Thanks for the suggestion, yes, I think I can get the datalog to play nice enough with interleaving.

ssilverman commented 1 year ago

I just fixed the assertion failure output so the message is actually sent out before the abort() call. See the new https://github.com/ssilverman/QNEthernet/commit/3df2b02c9343a2b92a149c5b64c54e5f4aad796a commit and the one preceding it. Now it should be apparent why a freeze happens. WIth your code, you should now see something like this:

Assertion "Function called from interrupt context" failed at line 173 in /Users/you/Documents/Arduino/libraries/QNEthernet/src/sys_arch.cpp

The library makes the core assumption that it's called in a non-concurrent way. A bunch of things would need to change to make it be able to run in a concurrent context. It's not that it's not advised, it's actively disallowed.