First node problems - compiling

Hello,
I’ve been trying to get started with my first node since this spring.
My garage is a bit further away and the status of it’s doors is what I want to monitor. I have a TTN Indoor Gateway running at the house. I’m familiar with Arduino through MySensors for home automation but that’s as far as I’m able to code - or understand it.

I decided to build my first node with an Arduino Pro Mini 3.3V and RFM95 module. I have microswitches that monitor the garage doors. I built the node per: https://www.thethingsnetwork.org/labs/story/build-the-cheapest-possible-node-yourself

And borrowed and modified code from: https://github.com/tijnonlijn/RFM-node/blob/master/PIR_example.ino

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

// LoRaWAN NwkSKey, network session key
static const PROGMEM u1_t NWKSKEY[16] = { NOT MY REAL KEYS };
// LoRaWAN AppSKey, application session key
static const u1_t PROGMEM APPSKEY[16] = { NOT MY REAL KEYS EITHER };
// LoRaWAN end-device address (DevAddr)
static const u4_t DEVADDR = 0xFUBARTOO ; // <-- Change this address for every node!

void os_getArtEui (u1_t* buf) { }
void os_getDevEui (u1_t* buf) { }
void os_getDevKey (u1_t* buf) { }

// HERE YOUR CODE!!
int Door1State = LOW;
int Door1StateChanged = 0;
int Door1Pin = 8;
int Door2State = LOW;
int Door2StateChanged = 0;
int Door2Pin = 9;
static uint8_t mydata[] = "0 , 0";
// END HERE !!

static osjob_t sendjob;
const unsigned TX_INTERVAL = 60;

// Pin mapping
const lmic_pinmap lmic_pins = {
    .nss = 6,
    .rxtx = LMIC_UNUSED_PIN,
    .rst = 5,
    .dio = {2, 3, 4},
};

#define DEBUG

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;
            break;
        case EV_TXCOMPLETE:
            Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
            if(LMIC.dataLen) {
                // data received in rx slot after tx
                Serial.print(F("Data Received: "));
                Serial.write(LMIC.frame+LMIC.dataBeg, LMIC.dataLen);
                Serial.println();
            }
            // Schedule next transmission
            os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
            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){
    if (LMIC.opmode & OP_TXRXPEND) {
        Serial.println(F("OP_TXRXPEND, not sending"));
    } else {
        LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0);
        Serial.println(F("Packet queued"));
    }
}

void setup() {
    Serial.begin(57600);
    Serial.println(F("Starting"));

    os_init();
    LMIC_reset();
    #ifdef PROGMEM
    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
    LMIC_setSession (0x1, DEVADDR, NWKSKEY, APPSKEY);
    #endif
    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
    LMIC_setLinkCheckMode(0);
    LMIC_setDrTxpow(DR_SF7,14);

      do_send(&sendjob);  

}

void loop() {


     if(digitalRead(Door1Pin) == HIGH){
      if (Door1State == LOW) {
        Serial.println("Door1 open");
        Door1State = HIGH;
        mydata[0] = '1';
        Door1StateChanged = 1;
        } 
     }
     else if (digitalRead(Door1Pin) == LOW) {
      if (Door1State == HIGH) {
        Serial.println("Door1 closed");
        Door1State = LOW;
        mydata[0] = '0'; 
        Door1StateChanged = 1;
        }
     }
    
    if (Door1StateChanged == 1) {
        Serial.print("Door1Pin:");
        Serial.println(digitalRead(Door1Pin));
        Door1StateChanged = 0;
      }

     if(digitalRead(Door2Pin) == HIGH){
      if (Door2State == LOW) {
        Serial.println("Door2 open");
        Door2State = HIGH;
        mydata[1] = '1';
        Door2StateChanged = 1;
        }
     }
     else if (digitalRead(Door2Pin) == LOW) {
      if (Door2State == HIGH) {
        Serial.println("Door2 closed");
        Door2State = LOW;
        mydata[1] = '0'; 
        Door2StateChanged = 1;
        }
     }
    
    if (Door2StateChanged == 1) {
        Serial.print("Door2Pin:");
        Serial.println(digitalRead(Door2Pin));
        Door1StateChanged = 0;
      }

  
   os_runloop_once();
}

My first and foremost problem is that Arduino IDE 1.8.10 doesn’t compile.
I have problems selecting and installing LMIC -library and what other lib’s are needed.

I have researched the forum, but can’t find a good answer or at least one that is current. Most LMIC librarys say “modified” from IBM LMIC - but IDE says “invalid library” or if I move the files to the library the compiler turns up a LOT of errors.

Can someone help me make the code above work or give me some pointers?

It’s getting a bit tricky to fit the LMIC in to a standard Arduino, as you’ll see from my work:

I’m in the process of swapping over the library, I’ll review the assist I gave someone else and add more shortly

The latest version of this library worked for me 2 weeks ago on Mac & Windows:

If you just want a node that works for this use case without coding you could take a look at the very affordable (for LoRaWAN nodes) Dragino LDS01.

If you want to code, get a controller with more flash memory, the old Arduino boards lack the memory required for up to date and (more or less) compliant LoRaWAN stacks.

I’m not looking to buy nodes. Full stop. It’s simply not the reason I’m building nodes and automation.
I don’t mind using another dev-board…I probably should learn new tricks anyway. I tried SimpleLMIC last night, and it went to 104% of memory.
Any personal recommendations considering the ProMini’s I would be moving away from - price/connectivity/power use/good-for-a-n00b etc? I don’t need a complete list, just an “I like these”-type of recommendation :slight_smile:

I reinstalled the lib recommended above and presto, compiled perfectly. I must have had another lib installed as well that conflicted. I deleted them all before reinstalling. This code compiled with 67% prog storage and 47% dyn memory - so maybe the Arduinos can soldier on for a while…
I’ll upload now and see what problems arise next. Thanks for the help!

Personally, as the author of the thing that worked for you I:

Use ProMini’s + RFM95 for VERY simple, usually single sensor, systems with little or no alarm facilities.

Use ProMini’s + RAK4200 for co-development with clients who can use the world of Arduino libraries to prototype but then have a blackbox to do the LoRaWAN with. I can do most of what’s needed this way and have space for more sophisticated acknowledgement, resend & alarm functionality.

Use Adafruit Feathers, RAK4260 and other Cortex based gift wrapped packages (I’m more a software + basic MCUs guy, definitely not an RF one) for more demanding applications and the BME680 IAQ holy grail.

Yikes, the prices!
Really makes the previously recommended Dragino LDS01 seem cheap.

Since this is a beginner thread and I’m hijacking it myself - 1) what are “more sophisticated acknowledgement, resend & alarm functionality”?

Also, can someone help with my code. I made improvements to send when there’s a change but also send “I’m alive” every 60mins.
2) I don’t understand “calling dosend();” - I copied from other parts but it doesn’t seem to work. Just coding dosend(); doesn’t compile.
3) Since I only have to send two bits (doors 1 and 2 - either 1 or 0), am I sending unnecessarily too much data using: static uint8_t mydata[] = “0 , 0”;?

To abbreviate, here’s the bits I changed in the previous code.

unsigned TX_INTERVAL = 60; //previous "const" removed


        case EV_TXCOMPLETE:
            Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
            TX_INTERVAL = 3600;  // Change reporting complete, go back to longer interval
            Serial.println("Transmit repeat set back to 60min");
            if(LMIC.dataLen) {
                // data received in rx slot after tx
                Serial.print(F("Data Received: "));
                Serial.write(LMIC.frame+LMIC.dataBeg, LMIC.dataLen);
                Serial.println();
            }


void loop() {


     if(digitalRead(Door1Pin) == HIGH){
      if (Door1State == LOW) {
        Serial.println("Door1 open");
        Door1State = HIGH;
        mydata[0] = '1';
        Door1StateChanged = 1;
        } 
     }
     else if (digitalRead(Door1Pin) == LOW) {
      if (Door1State == HIGH) {
        Serial.println("Door1 closed");
        Door1State = LOW;
        mydata[0] = '0'; 
        Door1StateChanged = 1;
        }
     }
    
     if(digitalRead(Door2Pin) == HIGH){
      if (Door2State == LOW) {
        Serial.println("Door2 open");
        Door2State = HIGH;
        mydata[1] = '1';
        Door2StateChanged = 1;
        }
     }
     else if (digitalRead(Door2Pin) == LOW) {
      if (Door2State == HIGH) {
        Serial.println("Door2 closed");
        Door2State = LOW;
        mydata[1] = '0'; 
        Door2StateChanged = 1;
        }
     }
     
    if (Door1StateChanged == 1 || Door2StateChanged == 1) {
        Serial.print("Door1Pin:");
        Serial.println(digitalRead(Door1Pin));
        Serial.print("Door2Pin:");
        Serial.println(digitalRead(Door2Pin));
        TX_INTERVAL = 20;  // Change detected, try to push change to TTN
        Serial.println("Transmit repeat set to 20s until sent");
        do_send(&sendjob);
        Door1StateChanged = 0;
        Door2StateChanged = 0;
      }

delay(3000); // slow things down for debugging
  
os_runloop_once();
}

“Doesn’t compile” is not a meaningful report.

Give the actual line of code which breaks the build, and the specific error messages which result.

Also you seem to be using an ATmega328p; beware that you’ll rapidly run out of memory on that, you really should switch to one of the ARM-based Arduino compatibles.

Sending printable character strings is strongly discouraged as it doesn’t scale. You really should use numeric values and a bitmask where each door would be a different bit in a byte.

eg
#define DOOR_0 (1<<0) //bit value is 1
#define DOOR_1 (1<<1) //bit value is 2
uint8_t doors = 0;
if (door_0_is_open) doors |= DOOR_0;
if (door_1_is_open) doors |= DOOR_1;

That said, given the 13-byte protocol overhead, there’s not a huge difference in airtime between sending the 1 byte you need and the 5 or 6 you currently are. But as soon as you start trying to do more things it rapidly gets unreasonable, and since this is a learning project, learn to do it right.

In my code I do not use the LMIC mini-RTOS to decide when to send, so the main Loop() decides when to send or sleep and when it is time to send, it calls doSend() - you won’t be able to mix & match between my code base and the generic examples that come with the matthijskooijman repro but you should be able to get one OR the other working.

Acknowledgement of transmissions under varying circumstances, particularly if an alarm has been raised - for instance, change uplink interval to 120 seconds when alarmed until a downlink is received acknowledging the alarm - but only for applications that don’t alarm very often. Alarm functions usually need to include some hysteresis which can be coded, downlinked or learn’t. If a data point is missing, it is feasible to request a retransmission, again, for certain use cases.

Sorry, you’re right. Not really much to go on. Also yes, this is definitely a learning project for me and that’s why I try to understand the limitations out of the gate. Also that’s why I’m keeping this first node as simple as possible, yet as something that will tie into my actual home automation vs. “hello world”.

“Doesn’t compile” in this case => If the line in my void loop is:
do_send();
compiler error: "too few arguments to function ‘void do_send(osjob_t*)’ "

If I have
do_send(&sendjob);
The compiler doesn’t complain, but TTN doesn’t receive data. The debug on my serial (I grounded Door1pin):

Packet queued
Door1 open
Door1Pin:1
Door2Pin:0
Transmit repeat set to 20s
1501661: EV_TXCOMPLETE (includes waiting for RX windows)
Transmit repeat set to 60min

But nothing in TTN console-Data :thinking:

@descartes I’m following your answer to question 2: if I try your code base, it will get too heavy for the ProMini? If not too heavy, would you happen to have/share a bit of code for something similar that I could try to dig into and bastardize to my project?

“Doesn’t compile” in this case => If the line in my void loop is:
do_send();
compiler error: "too few arguments to function ‘void do_send(osjob_t*)’ "

Well, that’s pretty clear

If I have
do_send(&sendjob);
The compiler doesn’t complain, but TTN doesn’t receive data. The debug on my serial (I grounded Door1pin):

Does the gateway’s raw traffic page or its own log indicate any raw packet was received?

do_send() is from the LMIC example. Whereas doSend() is from my example. Are you merging the code bases?

You absolutely have to call do_send as do_send(&sendjob) as that is how it works, it’s not the sort of code where you hack bits out just because. And you are getting a EV_TXCOMPLETE which is the LMIC saying that it’s processed the packet, passed it to the radio, the radio says it’s sent it. Generally, unless you don’t have an antenna, that means the device has worked.

The entire point of my code base is that it DOES fit in to a ProMini.

This is a working setup for an ATmega328 with a RFM95 LoRa radio using the MCCI LoRaWAN LMIC library version 2.2.2 compiled on my iMac using Arduino 1.8.9. It’s for our temp & humidity sensor. I’ve included more complex examples as well.

I use a small PCB to mount the RFM95 with an Arduino Pro Mini connected on the other side. Minimum connections are:

For now, I highly recommend, with extra bells & emphasis, that you use the example for ABP from the matthijskooijman repro linked above with NO alterations other than putting your credentials in. The example has the pins setup as per the hardware build that you linked to.

The matthijskooijman repro works with the latest Arduino IDE, 1.8.13, on both Windows & macOS.

However tempting it may be to skip to adding your microswitch code, that bit is trivial compared to getting a working node that shows uplinks on it’s console page.

The code at the very top, that I had compiling problems with, did connect and send data. It sends periodically, so I dove in pretty immediately after that success to turn the code to send-when-changed.

I first just changed the interval times, but since that alone didn’t work (changing the interval in the loop doesn’t affect the delay already at play after a successful TXRX), I added the do_send(&sendjob); -line. As pointed out, by just hacking bits out. The syntax is a little difficult to figure out even after searching the forums.

“Merging the code bases” sounds tricky or do you mean am I messing up using the commands from different bases? I looked at your TinyThing-examples and though the code is long, it seems I could probably follow it. I understand the settings.h is what defines your commands i.e. doSend?

EDIT: I must have hardware rot somewhere as even the ABP-example is not connecting with the correct keys. Will investigate.

It should do as you change the send interval on EV_TXCOMPLETE but the bottom of that case section should then have:

os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);

which sets the time of the next send. do_send() triggers an immediate send.

My code doesn’t use the os_setTimedCallback delay/timer mechanism as that system is not suited for the device to go to sleep. If you have permanent power, this is not a problem for your application. If you do have power, I’d start again with a fresh example, OTAA preferably, get that working and add code in slowly with our assistance.

Isn’t providing your own scheduling in the main loop ‘asking for problems’ because it can cause conflicts with LMIC’s scheduling?

Isn’t it better to leave the scheduling to LMIC: schedule regular jobs (e.g. checking status, reading sensors) using the LMIC scheduler and within such job(s) determine if something needs to be transmitted and if so schedule a send job for that?

1 Like

The problem from an extensive number of reports & issues in various places was that if you sleep, you take the OS timer by surprise and it loses track and trying to update it the timer value isn’t simple.

So I remove the send & data collect jobs which are external to the stack and put a flag in to allow the stack to run to completion for sends, so it can use the OS timer - which is almost entirely about waiting for the right receive windows and could therefore be done in a much simpler way

Ideetron’s early implementation literally just used a delay. The mini-RTOS in LMIC makes things complicated and if you then put it on an ESP with its RTOS implementation that wraps around it, try debugging that!

The office nodes have been running for months now - 60,000+ transmits each - so no problems thus far.

OK, so found my cold solder and fixed it. Nothing got through but I reset the “frames counter” on Console and it started working. What is it?

@descartes my loop-function’s meaningful part is:

     if (Door1StateChanged == 1 || Door2StateChanged == 1) {
        Serial.print("Door1Pin:");
        Serial.println(digitalRead(Door1Pin));
        Serial.print("Door2Pin:");
        Serial.println(digitalRead(Door2Pin));
        TX_INTERVAL = 0;  // Change detected, push change faster
        Serial.println("Transmiting now");
        os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
        Door1StateChanged = 0;
        Door2StateChanged = 0;
      }

…and that seems to work. Only to lead to problems decoding. I watched the video and racked my brain. This coding is something I’m completely new to. Before I move to the advice from @cslorabox , I will try to make sense of this:

My decoder:

function Decoder(bytes, port) {
  var door1 = (bytes[0]);
  var door2 = (bytes[1]);
    
  return  {
    Door1: door1,
    Door2: door2
  }
}

Debug: Door1Pin:1 Door2Pin:0
Data: 31302C2030

Output:
{
“Door1”: 49,
“Door2”: 48
}
What’s wrong with my decoder?

About half way down this: https://www.thethingsnetwork.org/docs/lorawan/security.html

We have a saying here, all the code or no code at all. I/we need to see the part where you setup the payload because it seems you’ve used characters rather than numbers, 48 is ASCII for 0 and 49 = 1.

Which means you are very close to success, nothing wrong with your decoder, just what you are sending it.

But please don’t change TX_INTERVAL to 0, that’s way way too fast, for fair use, for your device to keep up, for your local legal duty cycle.

1 Like

Whole code minus keys & devAddr:

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

//KEYS and DEV removed for posting ---

void os_getArtEui (u1_t* buf) { }
void os_getDevEui (u1_t* buf) { }
void os_getDevKey (u1_t* buf) { }

// HERE YOUR CODE!!
int Door1State = LOW;
int Door1StateChanged = 0;
int Door1Pin = 8;
int Door2State = LOW;
int Door2StateChanged = 0;
int Door2Pin = 9;
static uint8_t mydata[] = "0 , 0";
// END HERE !!

static osjob_t sendjob;
unsigned TX_INTERVAL = 60;

// Pin mapping
const lmic_pinmap lmic_pins = {
    .nss = 6,
    .rxtx = LMIC_UNUSED_PIN,
    .rst = 5,
    .dio = {2, 3, 4},
};

#define DEBUG

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;
            break;
        case EV_TXCOMPLETE:
            Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
            TX_INTERVAL = 3600;  // Change reporting complete, go back to longer interval
            Serial.println("Transmit repeat set to 60min");
            if(LMIC.dataLen) {
                // data received in rx slot after tx
                Serial.print(F("Data Received: "));
                Serial.write(LMIC.frame+LMIC.dataBeg, LMIC.dataLen);
                Serial.println();
            }
            // Schedule next transmission
            os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
            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){
    if (LMIC.opmode & OP_TXRXPEND) {
        Serial.println(F("OP_TXRXPEND, not sending"));
    } else {
        LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0);
        Serial.println(F("Packet queued"));
    }
}

void setup() {
    Serial.begin(57600);
    Serial.println(F("Starting"));

    pinMode(Door1Pin, INPUT_PULLUP);
    pinMode(Door2Pin, INPUT_PULLUP);

    os_init();
    LMIC_reset();
    #ifdef PROGMEM
    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
    LMIC_setSession (0x1, DEVADDR, NWKSKEY, APPSKEY);
    #endif
    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
    LMIC_setLinkCheckMode(0);
    LMIC_setDrTxpow(DR_SF7,14);

      do_send(&sendjob);  

}

void loop() {


     if(digitalRead(Door1Pin) == HIGH){
      if (Door1State == LOW) {
        Serial.println("Door1 open");
        Door1State = HIGH;
        mydata[0] = '1';
        Door1StateChanged = 1;
        } 
     }
     else if (digitalRead(Door1Pin) == LOW) {
      if (Door1State == HIGH) {
        Serial.println("Door1 closed");
        Door1State = LOW;
        mydata[0] = '0'; 
        Door1StateChanged = 1;
        }
     }
    
     if(digitalRead(Door2Pin) == HIGH){
      if (Door2State == LOW) {
        Serial.println("Door2 open");
        Door2State = HIGH;
        mydata[1] = '1';
        Door2StateChanged = 1;
        }
     }
     else if (digitalRead(Door2Pin) == LOW) {
      if (Door2State == HIGH) {
        Serial.println("Door2 closed");
        Door2State = LOW;
        mydata[1] = '0'; 
        Door2StateChanged = 1;
        }
     }
     
    if (Door1StateChanged == 1 || Door2StateChanged == 1) {
        Serial.print("Door1Pin:");
        Serial.println(digitalRead(Door1Pin));
        Serial.print("Door2Pin:");
        Serial.println(digitalRead(Door2Pin));
        TX_INTERVAL = 5;  // Change detected, push change faster
        Serial.println("Transmiting now, 5s interval until sent");
        os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
        Door1StateChanged = 0;
        Door2StateChanged = 0;
      }

delay(3000);
  
   os_runloop_once();
}

Pushed the “send immediately” interval to 5s. It goes up to 60min once EV_TXCOMPLETE.

uint8_t mydata[] = { 0x00, 0x00 }; - not sure what yours does exactly

mydata[0] = 1; - you were assigning a character rather than a number, as suggest in previous post.

It won’t win you any support going from 0 to 5s, either add a test push button to trigger an uplink or make it reasonable, like 5 minutes. If you need to know when the door changes state, check for that and uplink on change, unless it is a revolving door, then find a different scheme!

But as EV_TXCOMPLETE is at the end of an uplink, if you change it to 5s and then back to 60 minutes after it’s transmitted, you’ll be on 60 minutes all the time.

Oh, I see, it’s to trigger an early send - why not just call do_send() rather than schedule a future event by overriding a constant.