TheThingsNetwork / arduino-device-lib

Arduino Library for TTN Devices
MIT License
207 stars 96 forks source link

Node: Hangs when you use `TheThingsNetwork.*` inside of `TheThingsNode.on*` #99

Closed FokkeZB closed 7 years ago

FokkeZB commented 7 years ago

Example:

#include <TheThingsNetwork.h>
#include <TheThingsNode.h>

#define loraSerial Serial1
#define debugSerial Serial

TheThingsNetwork ttn(loraSerial, debugSerial, TTN_FP_EU868);
TheThingsNode node;

int count = 0;

void setup() {
  loraSerial.begin(57600);
  debugSerial.begin(9600);

  while (!debugSerial && millis() < 10000);

  node.onButtonRelease(onButtonRelease);

  node.setColor(TTN_GREEN);
}

void onButtonRelease() {
  debugSerial.println("-- BUTTON RELEASE " + String(count));

  // using a var outside the function scope works fine
  count++;

  // using a function of node works fine
  node.setColor(TTN_BLUE);

  // Enable the next line and the Node will hang.
  // The above println won't show up in Serial Monitor,
  // but the LED does change to blue.
  //ttn.showStatus();
}

void loop() {
  debugSerial.println("-- LOOP");

  delay(10000);
}
FokkeZB commented 7 years ago

When I edit showStatus() to not call readValue() it works fine:

void TheThingsNetwork::showStatus() {
  debugPrint(F("EUI: "));
  // debugPrintLn(readValue(F("sys get hweui")));
  // debugPrint(F("Battery: "));
  // debugPrintLn(readValue(F("sys get vdd")));
  // debugPrint(F("AppEUI: "));
  // debugPrintLn(readValue(F("mac get appeui")));
  // debugPrint(F("DevEUI: "));
  // debugPrintLn(readValue(F("mac get deveui")));

  // if (this->model == F("RN2483")) {
  //   debugPrint(F("Band: "));
  //   debugPrintLn(readValue(F("mac get band")));
  // }

  // debugPrint(F("Data Rate: "));
  // debugPrintLn(readValue(F("mac get dr")));
  // debugPrint(F("RX Delay 1: "));
  // debugPrintLn(readValue(F("mac get rxdelay1")));
  // debugPrint(F("RX Delay 2: "));
  // debugPrintLn(readValue(F("mac get rxdelay2")));
}

So it looks like (but serial monitor no longer works, so I can't verify) that when called from a callback - the modemStream does not print anything, causing us to hang in readLine(), which readValue() uses.

johanstokking commented 7 years ago

Apparently, the serial is not available for reading and/or writing when calling from an interrupt. Check with the product agency.

FokkeZB commented 7 years ago

That might well be it. Probably with ttn.showStatus() commented out, the debugSerial.println() works because it is processed after the interrupt has finished. But when readLine() waits for ever, it will never finish.

https://www.sparkfun.com/tutorials/326#comment-53762de7ce395f1a638b4567

Just a thing to note, Serial.prints() are interrupt driven so you do not want them inside your ISR as there’s a good chance they won’t work properly. Better to store the value you want and print it later in your loop().

Also: http://forum.arduino.cc/index.php?topic=131801.0

FokkeZB commented 7 years ago

OK, this is quite an issue. This means you cannot use ttn.sendBytes() inside a node.on*(), while this is the typical use case for it.

This would lead you to ugly code (with all kinds of edge cases) like:

bool buttonPressed = false;

void onButtonRelease() {
  buttonPressed = true;
}

void loop() {

  if (buttonPressed) {
    byte payload = { 0x01 };
    ttn.sendBytes(payload, 1);
  }

  delay(10000);
}