onEvent is never called

Hello,

I use the LMiC library with an ESP32 development board and the following code:

#include <lmic.h>
#include <hal/hal.h>

uint8_t NWKSKEY[16] = ...;
uint8_t APPSKEY[16] = ...;
uint32_t DEVADDR = ...;

// These callbacks are only used in over-the-air activation, so they are
// left empty here (we cannot leave them out completely unless
// DISABLE_JOIN is set in config.h, otherwise the linker will complain).
void os_getArtEui (u1_t* buf) { }
void os_getDevEui (u1_t* buf) { }
void os_getDevKey (u1_t* buf) { }

static uint8_t mydata[] = "Hello, world!";

static osjob_t SendJob;

const lmic_pinmap lmic_pins = {
  .nss = 5,
  .rxtx = LMIC_UNUSED_PIN,
  .rst = 14,
  .dio = {24, 33, 32},
};

void onEvent(ev_t Event)
{
  Serial.print(os_getTime());
  Serial.print(": ");
  Serial.println("Event");
  switch(Event)
  {
    case EV_JOINING:
      Serial.println(F("EV_JOINING"));
      break;

    case EV_JOINED:
      Serial.println(F("EV_JOINED"));
      break;

    case EV_SCAN_TIMEOUT:
      Serial.println(F("EV_SCAN_TIMEOUT"));
      break;
    case EV_BEACON_FOUND:
      Serial.println(F("EV_BEACON_FOUND"));
      break;
    case EV_BEACON_MISSED:
      Serial.println(F("EV_BEACON_MISSED"));
      break;
    case EV_BEACON_TRACKED:
      Serial.println(F("EV_BEACON_TRACKED"));
      break;

    case EV_RFU1:
      Serial.println(F("EV_RFU1"));
      break;
    case EV_JOIN_FAILED:
      Serial.println(F("EV_JOIN_FAILED"));
      break;
    case EV_REJOIN_FAILED:
      Serial.println(F("EV_REJOIN_FAILED"));
      break;
    case EV_TXCOMPLETE:
      Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
      if(LMIC.txrxFlags & TXRX_ACK)
      {
        Serial.println(F("Received ack"));
      }

      if(LMIC.dataLen)
      {
        Serial.println(F("Received "));
        Serial.println(LMIC.dataLen);
        Serial.println(F(" bytes of payload"));
      }

      break;
    case EV_LOST_TSYNC:
      Serial.println(F("EV_LOST_TSYNC"));
      break;
    case EV_RESET:
      Serial.println(F("EV_RESET"));
      break;
    case EV_RXCOMPLETE:
      Serial.println(F("EV_RXCOMPLETE"));
      break;
    case EV_LINK_DEAD:
      Serial.println(F("EV_LINK_DEAD"));
      break;
    case EV_LINK_ALIVE:
      Serial.println(F("EV_LINK_ALIVE"));
      break;
    default:
      Serial.println(F("Unknown event"));
      break;
  }
}

void SendJob_Callback(osjob_t* Job)
{
  // Check if there is not a current TX/RX job running
  if(LMIC.opmode & OP_TXRXPEND)
  {
    Serial.println(F("OP_TXRXPEND, not sending"));
  } 
  else
  {
    Serial.println(F("Packet queued"));
  }

  LMIC_setTxData2(1, mydata, sizeof(mydata) - 1, 0);

  // Reschedule the job every 120 seconds
  os_setTimedCallback(Job, os_getTime() + sec2osticks(10), SendJob_Callback);
}

void ttnTask(void* Context)
{
  os_init();

  // Reset the MAC state. Session and pending data transfers will be discarded.
  LMIC_reset();
  LMIC_setSession(0x1, DEVADDR, NWKSKEY, APPSKEY);
  LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI);
  
  // Disable link check validation
  LMIC_setLinkCheckMode(0);
  
  // TTN uses SF9 for its RX2 window.
  LMIC.dn2Dr = DR_SF9;
  
  // Set data rate and transmit power for uplink (note: txpow seems to be ignored by the library)
  LMIC_setDrTxpow(DR_SF7, 14);

  LMIC_startJoining();
  
  SendJob_Callback(&SendJob);

  for(;;)
  {
    os_runloop_once();
    vTaskDelay(1);
  }
}

void setup()
{
  Serial.begin(115200);

  xTaskCreatePinnedToCore(ttnTask, "ttnTask", 2048, NULL, 5, NULL, 1);
}

void loop()
{
}

The first transmission is transmitted successfully, but all other messages don´t get sent.

OP_TXRXPEND, not sending
103140753: engineUpdate, opmode=0x888
103140787: Scheduled job 0x3ffc011c, cb 0x400d0e64 at 103765786
103765810: Running job 0x3ffc011c, cb 0x400d0e64, deadline 103765786

I read this article and the author says that this will happen when the DI1 isn´t connected, but I have connected DI0 - DI2.

What is wrong with this code?

Thank you!

Where did you get it from?

DIO1 is used to notify timeout of the receiving window so it is worth double checking that it is correct - can’t comment further without knowing actually which board it is.

You have the xTaskCreatePinnedToCore but most of the LMIC examples have the os_runloop_once() in the loop() and they usually reschedule the next send at the bottom of the EV_TXCOMPLETE event. I raise this as an issue because the LMIC has it’s own internal scheduler, so you’ve nested two.

The SendJob_Callback is somewhat odd - if the device is sending, it prints a message but then goes on to schedule a send, I think a } may have been moved.

I’m not sure what the side effects of scheduling a send job from within the send job, and I’m pretty sure once every 2 minutes isn’t in the fair use policy. But it appears to actually be set to 10 seconds this may be causing the LMIC issues with the duty cycle and it consequently refuses to send.

These are pointers for you to investigate - you might want to have a look at the MCCI LMIC repro and see how your code differs from the supplied examples as I’ve noted above.

I found the error. I messed up the wire connections and use an GPIO which doesn´t exist on my board :smiley:

Great!

You deleted the information about using “the original code from the Git repository” which represents useful information for those debugging their nodes, which is why I mention it here. Do you mean the MCCI LMIC code?

And where did you get the code you used in the first place?

Hi Nick,

I took this code during the first test. The original code was some test code from me, based on the linked example code and some modifications. I wanted to test the scheduling because I´m looking for a solution to avoid timing issues with calling os_runloop_once (I found a solution like this in another forum, but I don´t have the link anymore).

If you have timing requirements in your part of the code that are critical then I’d use a state machine to decide who gets priority - aka some flag variables that tell the code if LMIC is free to run or if your code is in charge. The reason being is that the LMIC is pretty sensitive about timing when performing an uplink and I’ve found it better to give it total control from LMIC_setTxData2 to it returning a EV_TXCOMPLETE.

If your timing doesn’t allow for that, you may want to look at an external MCU+radio module.

Of course, YMMV but that’s the fun of developing!