Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 11 of 11

Thread: Teensy 3.2 + Snooze + LoRaWAN LMIC stack problem

  1. #1

    Teensy 3.2 + Snooze + LoRaWAN LMIC stack problem

    Hii guys,

    I'm using an Teensy 3.2 with an SX1276 LoRa chip. For the LoRaWAN stack I'm using the LMIC v 1.5 library (Link).

    Because it's battery powered I'm using duffs Snooze library in combination with an 32kHz crystal + 3V battery to put the Teensy in hibernate mode while not sending anything. Once every x minutes it wakes up, takes measurements and sends it's data to The Things Network.

    The code works perfectly without putting it into hibernate or sleep mode, but when I insert the code for the hibernate it start acting weird. I get huge delays in sending the data. I think the millis() counter is out of sync with the LMIC stack, had the same problem using Arduino but fixed it with this piece of code:

    Code:
    //Give the AVR back the slept time back (simple version)
          cli();
          timer0_overflow_count += 8 * 64 * clockCyclesPerMicrosecond(); //give back 60 seconds of sleep
          sei();
    
    os_getTime();   //VERY IMPORTANT after sleep to update os_time and not cause txbeg and os_time out of sync which causes send delays with the RFM95 on and eating power
    Now I have no idea how to do this with the Teensy. Here's my simplified code of my Teensy + Snooze + LMIC without sensors (not relevant for my question):

    Code:
    #include <lmic.h>
    #include <hal/hal.h>
    #include <SPI.h>
    #include <Snooze.h>
    #include <i2c_t3.h>
    
    // LoRaWAN NwkSKey, network session key
    // This is the default Semtech key, which is used by the early prototype TTN
    // network.
    static const PROGMEM u1_t NWKSKEY[16] = { MyNWKSKEY };
    
    // LoRaWAN AppSKey, application session key
    // This is the default Semtech key, which is used by the early prototype TTN
    // network.
    static const u1_t PROGMEM APPSKEY[16] = { MyAPPSKEY };
    
    // LoRaWAN end-device address (DevAddr)
    static const u4_t DEVADDR = myDEVADDR ; // <-- Change this address for every node!
    
    // 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 osjob_t sendjob;
    
    // Schedule TX every this many seconds (might become longer due to duty
    // cycle limitations).
    const unsigned TX_INTERVAL = 60;
    
    // Pin mapping
    const lmic_pinmap lmic_pins = {
      .nss = 10,
      .rxtx = LMIC_UNUSED_PIN,
      .rst = 4,
      .dio = {2, 3, LMIC_UNUSED_PIN},
    };
    
    int who;
    
    SnoozeAlarm alarm;
    SnoozeBlock config_teensy32(alarm);
    
    void onEvent (ev_t ev) {
      Serial.print(os_getTime());
      Serial.print(": ");
      switch (ev) {
        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_JOINING:
          Serial.println(F("EV_JOINING"));
          break;
        case EV_JOINED:
          Serial.println(F("EV_JOINED"));
          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"));
            WinterSlaap();
          }
          else {
            WinterSlaap();
          }
          break;
        case EV_LOST_TSYNC:
          Serial.println(F("EV_LOST_TSYNC"));
          break;
        case EV_RESET:
          Serial.println(F("EV_RESET"));
          break;
        case EV_RXCOMPLETE:
          // data received in ping slot
          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 do_send(osjob_t* j) {
      // Check if there is not a current TX/RX job running
      if (LMIC.opmode & OP_TXRXPEND) {
        Serial.println(F("OP_TXRXPEND, not sending"));
      } else {
        byte payload [1];
        payload [0] = 1;
        LMIC_setTxData2(1, payload, sizeof(payload), 0);
        Serial.println(F("Packet queued"));
      }
      // Next TX is scheduled after TX_COMPLETE event.
    }
    
    void setup() {
      Serial.begin(115200);
      Serial.println(F("Starting"));
    
      pinMode (21, OUTPUT);
      digitalWrite (21, HIGH);
    
      //alarm.setRtcTimer(0, 0, 20);// Uren, minuten, secondes
    
      // LMIC init
      os_init();
      // Reset the MAC state. Session and pending data transfers will be discarded.
      LMIC_reset();
      
      os_setTimedCallback(&sendjob, os_getTime() + ms2osticks(10), do_send);
      // Set static session parameters. Instead of dynamically establishing a session
      // by joining the network, precomputed session parameters are be provided.
    #ifdef PROGMEM
      // On AVR, these values are stored in flash and only copied to RAM
      // once. Copy them to a temporary buffer here, LMIC_setSession will
      // copy them into a buffer of its own again.
      uint8_t appskey[sizeof(APPSKEY)];
      uint8_t nwkskey[sizeof(NWKSKEY)];
      memcpy_P(appskey, APPSKEY, sizeof(APPSKEY));
      memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY));
      LMIC_setSession (0x1, DEVADDR, nwkskey, appskey);
    #else
      // If not running an AVR with PROGMEM, just use the arrays directly
      LMIC_setSession (0x1, DEVADDR, NWKSKEY, APPSKEY);
    #endif
    
    #if defined(CFG_eu868)
      // Set up the channels used by the Things Network, which corresponds
      // to the defaults of most gateways. Without this, only three base
      // channels from the LoRaWAN specification are used, which certainly
      // works, so it is good for debugging, but can overload those
      // frequencies, so be sure to configure the full frequency range of
      // your network here (unless your network autoconfigures them).
      // Setting up channels should happen after LMIC_setSession, as that
      // configures the minimal channel set.
      // NA-US channels 0-71 are configured automatically
      LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
      LMIC_setupChannel(1, 868300000, DR_RANGE_MAP(DR_SF12, DR_SF7B), BAND_CENTI);      // g-band
      LMIC_setupChannel(2, 868500000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
      LMIC_setupChannel(3, 867100000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
      LMIC_setupChannel(4, 867300000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
      LMIC_setupChannel(5, 867500000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
      LMIC_setupChannel(6, 867700000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
      LMIC_setupChannel(7, 867900000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
      LMIC_setupChannel(8, 868800000, DR_RANGE_MAP(DR_FSK,  DR_FSK),  BAND_MILLI);      // g2-band
      // TTN defines an additional channel at 869.525Mhz using SF9 for class B
      // devices' ping slots. LMIC does not have an easy way to define set this
      // frequency and support for class B is spotty and untested, so this
      // frequency is not configured here.
    #elif defined(CFG_us915)
      // NA-US channels 0-71 are configured automatically
      // but only one group of 8 should (a subband) should be active
      // TTN recommends the second sub band, 1 in a zero based count.
      // https://github.com/TheThingsNetwork/gateway-conf/blob/master/US-global_conf.json
      LMIC_selectSubBand(1);
    #endif
    
      // 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);
    
      // Start job
      do_send(&sendjob);
    }
    
    void loop() {
      os_runloop_once();
      who = Snooze.hibernate( config_teensy32 );
      digitalWrite (21, HIGH);
      if (who == 35) {
        digitalWrite (21, HIGH);
        Serial.begin(115200);
        Serial.println ("Uit slaap!");
        delay(5000);
        
        //Give the AVR back the slept time back (simple version)
        //cli();
        //timer0_overflow_count += 8 * 64 * clockCyclesPerMicrosecond(); //give back seconds of sleep
        //sei();
    
        os_getTime();   //VERY IMPORTANT after sleep to update os_time and not cause txbeg and os_time out of sync which causes send delays with the RFM95 on and eating power
        //Do here whatever needs to be done after each of the sleepcycle (e.g. check for a condition to break for send or take measurements for mean values etc.)
    
        os_setTimedCallback(&sendjob, os_getTime() + ms2osticks(10), do_send);  //do a send
    
        Serial.println(F("go to sleep ... "));
        Serial.println ("delay van 5sec");
        delay (5000);
      }
    }
    
    void WinterSlaap (){
      digitalWrite (21, LOW);
      Serial.flush();
      Snooze.hibernate( alarm );  
    }
    Anyone got experience with the Snooze + LMIC library? Thanks in advance
    Last edited by EmielcpNL; 07-23-2019 at 08:08 AM.

  2. #2
    Senior Member duff's Avatar
    Join Date
    Jan 2013
    Location
    Las Vegas
    Posts
    980
    Never used the LMIC Library but Serial printing (USB) and sleeping are not going to work as you expect and could be some of your issues. For a more robust printing solution use the Hardware Serial ports for debug messages since they will come up faster and have less headaches. You can use a second Teensy connected by serial to this Teensy. Also if this library or you use any Timers then they might need reinitializing after waking from sleeping.

  3. #3
    Alright thank you for your reply. I will try it today

    Quote Originally Posted by duff View Post
    Also if this library or you use any Timers then they might need reinitializing after waking from sleeping.
    Got an example on how to do this?

  4. #4
    Changed the serial printing but didn't solve my problem. Still delays in sending my data

  5. #5
    Can't edit the post...

    @duff did I use the Snooze library correctly?

  6. #6
    Senior Member duff's Avatar
    Join Date
    Jan 2013
    Location
    Las Vegas
    Posts
    980
    Quote Originally Posted by EmielcpNL View Post
    @duff did I use the Snooze library correctly?
    Your example looks fine.

    Quote Originally Posted by EmielcpNL View Post
    Changed the serial printing but didn't solve my problem. Still delays in sending my data
    I looked into this library a little and it uses micros to keep track of it's "os" time. When you hibernate sleep the clock that runs micros is off, so it won't be updated during that sleep mode. This might be the issue you are having. Not sure how to fix it on that libraries end though and I don't have any of this equipment. Can you reinitialize the libraries "os" time some how?

  7. #7
    Quote Originally Posted by duff View Post
    Your example looks fine.

    I looked into this library a little and it uses micros to keep track of it's "os" time. When you hibernate sleep the clock that runs micros is off, so it won't be updated during that sleep mode. This might be the issue you are having. Not sure how to fix it on that libraries end though and I don't have any of this equipment. Can you reinitialize the libraries "os" time some how?
    Okay good to know! Well, I found this modified LMIC library by Pham (Link). It adds an function to count the time while sleeping: hal_sleep_lowpower(u1_t sleepval)

    So I added this to my code after waking up, still the same problem...

  8. #8
    Guy who made the custom lmic library said:

    Well, I tested Snooze in standalone with a Teensy32 and it appears that the millis()/micros() counter is preserved. So normally there is no need to change anything to LMIC (not need to insert call to hal_sleep_lowpower()). However, I tested the LMIC example with Snooze on a Teensy and it is not working (the second TX is not scheduled correctly after the sleep). Currently I have no clue on why it is not working. My knowledge of LMIC may be quite limited.

    regards,

  9. #9
    Senior Member duff's Avatar
    Join Date
    Jan 2013
    Location
    Las Vegas
    Posts
    980
    Quote Originally Posted by EmielcpNL View Post
    Guy who made the custom lmic library said:

    Well, I tested Snooze in standalone with a Teensy32 and it appears that the millis()/micros() counter is preserved. So normally there is no need to change anything to LMIC (not need to insert call to hal_sleep_lowpower()). However, I tested the LMIC example with Snooze on a Teensy and it is not working (the second TX is not scheduled correctly after the sleep). Currently I have no clue on why it is not working. My knowledge of LMIC may be quite limited.

    regards,
    millis/micros will be updated when woken if you use the timer driver not the rtc driver inwhich you do currently. Try the Timer driver instead and see if that helps.

  10. #10
    Quote Originally Posted by duff View Post
    millis/micros will be updated when woken if you use the timer driver not the rtc driver inwhich you do currently. Try the Timer driver instead and see if that helps.
    Tried it with the code below, change every alarm to timer (20seconds). Changed who == 35 to 36. Exacly the same result! It's weird, because with the fix on the Arduino they do the same (update millis) so it should work right? Here's how they do it on AVR chips (arduino):

    Code:
     //Give the AVR back the slept time back (simple version)
        //cli();
        //timer0_overflow_count += 8 * 64 * clockCyclesPerMicrosecond(); //give back seconds of sleep
        //sei();
    Code I used for timer instead of RTC:

    Code:
    #include <lmic.h>
    #include <hal/hal.h>
    #include <SPI.h>
    #include <Snooze.h>
    #include <i2c_t3.h>
    
    // LoRaWAN NwkSKey, network session key
    // This is the default Semtech key, which is used by the early prototype TTN
    // network.
    static const PROGMEM u1_t NWKSKEY[16] = { 0x09, 0x47, 0x9E, 0x7C, 0xA2, 0xEF, 0x71, 0xFB, 0x11, 0x03, 0x85, 0x53, 0x52, 0xF0, 0xEB, 0x0A };
    
    // LoRaWAN AppSKey, application session key
    // This is the default Semtech key, which is used by the early prototype TTN
    // network.
    static const u1_t PROGMEM APPSKEY[16] = { 0xC0, 0x5B, 0x13, 0x22, 0x55, 0xC2, 0xC4, 0xF0, 0x30, 0x34, 0xE7, 0x72, 0x6A, 0x81, 0xF4, 0xC5 };
    
    // LoRaWAN end-device address (DevAddr)
    static const u4_t DEVADDR = 0x2601190B ; // <-- Change this address for every node!
    
    // 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 osjob_t sendjob;
    
    // Schedule TX every this many seconds (might become longer due to duty
    // cycle limitations).
    // const unsigned TX_INTERVAL = 60;
    
    // Pin mapping
    const lmic_pinmap lmic_pins = {
      .nss = 10,
      .rxtx = LMIC_UNUSED_PIN,
      .rst = 4,
      .dio = {2, 3, LMIC_UNUSED_PIN},
    };
    
    #define resetPin 8
    
    int who;
    
    SnoozeTimer timer;
    SnoozeBlock config_teensy32(timer);
    
    void onEvent (ev_t ev) {
      Serial.print(os_getTime());
      Serial.print(": ");
      switch (ev) {
        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_JOINING:
          Serial.println(F("EV_JOINING"));
          break;
        case EV_JOINED:
          Serial.println(F("EV_JOINED"));
          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"));
            WinterSlaap();
          }
          else {
            WinterSlaap();
          }
          break;
        case EV_LOST_TSYNC:
          Serial.println(F("EV_LOST_TSYNC"));
          break;
        case EV_RESET:
          Serial.println(F("EV_RESET"));
          break;
        case EV_RXCOMPLETE:
          // data received in ping slot
          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 do_send(osjob_t* j) {
      // Check if there is not a current TX/RX job running
      if (LMIC.opmode & OP_TXRXPEND) {
        Serial.println(F("OP_TXRXPEND, not sending"));
      } else {
        byte payload [1];
        payload [0] = 2;
        LMIC_setTxData2(1, payload, sizeof(payload), 0);
        Serial.println(F("Packet queued"));
      }
      // Next TX is scheduled after TX_COMPLETE event.
    }
    
    void setup() {
      Serial.begin(115200);
      Serial.println(F("Starting"));
    
      pinMode (21, OUTPUT);
      digitalWrite (21, HIGH);
    
      timer.setTimer(20000);// milliseconde
    
      // LMIC init
      os_init();
      // Reset the MAC state. Session and pending data transfers will be discarded.
      LMIC_reset();
      
      os_setTimedCallback(&sendjob, os_getTime() + ms2osticks(10), do_send);
      // Set static session parameters. Instead of dynamically establishing a session
      // by joining the network, precomputed session parameters are be provided.
    #ifdef PROGMEM
      // On AVR, these values are stored in flash and only copied to RAM
      // once. Copy them to a temporary buffer here, LMIC_setSession will
      // copy them into a buffer of its own again.
      uint8_t appskey[sizeof(APPSKEY)];
      uint8_t nwkskey[sizeof(NWKSKEY)];
      memcpy_P(appskey, APPSKEY, sizeof(APPSKEY));
      memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY));
      LMIC_setSession (0x1, DEVADDR, nwkskey, appskey);
    #else
      // If not running an AVR with PROGMEM, just use the arrays directly
      LMIC_setSession (0x1, DEVADDR, NWKSKEY, APPSKEY);
    #endif
    
    #if defined(CFG_eu868)
      // Set up the channels used by the Things Network, which corresponds
      // to the defaults of most gateways. Without this, only three base
      // channels from the LoRaWAN specification are used, which certainly
      // works, so it is good for debugging, but can overload those
      // frequencies, so be sure to configure the full frequency range of
      // your network here (unless your network autoconfigures them).
      // Setting up channels should happen after LMIC_setSession, as that
      // configures the minimal channel set.
      // NA-US channels 0-71 are configured automatically
      LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
      LMIC_setupChannel(1, 868300000, DR_RANGE_MAP(DR_SF12, DR_SF7B), BAND_CENTI);      // g-band
      LMIC_setupChannel(2, 868500000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
      LMIC_setupChannel(3, 867100000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
      LMIC_setupChannel(4, 867300000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
      LMIC_setupChannel(5, 867500000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
      LMIC_setupChannel(6, 867700000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
      LMIC_setupChannel(7, 867900000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
      LMIC_setupChannel(8, 868800000, DR_RANGE_MAP(DR_FSK,  DR_FSK),  BAND_MILLI);      // g2-band
      // TTN defines an additional channel at 869.525Mhz using SF9 for class B
      // devices' ping slots. LMIC does not have an easy way to define set this
      // frequency and support for class B is spotty and untested, so this
      // frequency is not configured here.
    #elif defined(CFG_us915)
      // NA-US channels 0-71 are configured automatically
      // but only one group of 8 should (a subband) should be active
      // TTN recommends the second sub band, 1 in a zero based count.
      // https://github.com/TheThingsNetwork/gateway-conf/blob/master/US-global_conf.json
      LMIC_selectSubBand(1);
    #endif
    
      // 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);
    
      // Start job
      do_send(&sendjob);
    }
    
    void loop() {
      os_runloop_once();
      who = Snooze.hibernate( config_teensy32 );
      digitalWrite (21, HIGH);
      if (who == 36) {
        digitalWrite (21, HIGH);
        Serial.begin(115200);
        Serial.println ("Uit slaap!");
        delay(5000);
    
        os_getTime();   //VERY IMPORTANT after sleep to update os_time and not cause txbeg and os_time out of sync which causes send delays with the RFM95 on and eating power
        //Do here whatever needs to be done after each of the sleepcycle (e.g. check for a condition to break for send or take measurements for mean values etc.)
    
        os_setTimedCallback(&sendjob, os_getTime() + ms2osticks(10), do_send);  //do a send
    
        Serial.println(F("go to sleep ... "));
        Serial.println ("delay van 5sec");
        delay (5000);
      }
    }
    
    void WinterSlaap (){
      digitalWrite (21, LOW);
      Serial.flush();
      Snooze.hibernate( timer );  
    }

  11. #11
    Temp fix: after hibernate I reset the teensy with transistor. Not the most elegant way, but it does the job

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •