LMIC-node - attachInterrupt


LMIC-node is a project that makes deployment for LoraWan nodes easy, so it has it all, you need to do very little to get you going.

I have the basics in my code to take the measurement on a DS18B20 and the state of a reed switch, line 694 onto 798 ( LMIC-node-temp-reed_switch/LMIC-node.cpp at main · JohanScheepers/LMIC-node-temp-reed_switch · GitHub )

I want if the change of the reed switch trigger (LOW, HIGH), a attachInterrupt, triggers a measurement immediately and a scheduleUplink happens ( actually it needs to happen twice with a delay(5000) in between)

I have done some searching but not to sure where to add it in the code. :unamused:

Some pointers will be appreciated. Thank you.

The interrupts on the board I us is as below so will need to change Pin 11

  • #0 / RX - GPIO #0, also receive (input) pin for Serial1 and Interrupt #2

  • #1 / TX - GPIO #1, also transmit (output) pin for Serial1 and Interrupt #3

  • #2 / SDA - GPIO #2, also the I2C (Wire) data pin. There’s no pull up on this pin by default so when using with I2C, you may need a 2.2K-10K pullup. Also Interrupt #1

  • #3 / SCL - GPIO #3, also the I2C (Wire) clock pin. There’s no pull up on this pin by default so when using with I2C, you may need a 2.2K-10K pullup. Can also do PWM output and act as Interrupt #0.

If low-power is of no concern, you can continously poll a global variable volatile bool reedTrigger; for the value true in loop(), which the interrupt routine attached through attachInterrupt() sets to true. After processing the trigger in the loop(), the variable is reset to false.

In an interrupt service routine (ISR) you shouldn’t do much, especially not delaying or doing lengthy operations like scheduling a LoRaWAN operation.

Also see Arduino Interrupts Tutorial - The Robotics Back-End in regards to the “Keep the interrupts fast” chapter.

Thanks for the reply.

If this is what you want to achieve with the ISR, send a LoRaWAN, how ells do you do it?

In thread mode instead of interrupt mode, with e.g. the synchronization mechanism linked above.

You may try it and call LMIC functions from an ISR, but, ‘there be dragons’.

My ISR is calling the “blink” function, but I need it to initiate the “processWork” and then “scheduleUplink”.

So my “blink” function is doing the wrong thing.

void blink() {
  state = ++state;

void processWork(ostime_t doWorkJobTimeStamp)

    // This function is called from the doWorkCallback() 
    // callback function when the doWork job is executed.

    // Uses globals: payloadBuffer and LMIC data structure.

    // This is where the main work is performed like
    // reading sensor and GPS data and schedule uplink
    // messages if anything needs to be transmitted.

    // Skip processWork if using OTAA and still joining.
    if (LMIC.devaddr != 0)
        // Collect input data.
        // For simplicity LMIC-node uses a counter to simulate a sensor. 
        // The counter is increased automatically by getCounterValue()
        // and can be reset with a 'reset counter' command downlink message.

        ostime_t timestamp = os_getTime();


        //Sensor A measure
            sensorsA.getAddress(thermometerA, 0);
            sensorsA.setResolution(thermometerA, 12);
            float tempA = sensorsA.getTempC(thermometerA);
            int16_t int16_temperatureA;
            int16_temperatureA = 100*tempA;

            pinMode(buttonPin, INPUT_PULLUP);
            int16_t int16_reedSwitch;

            buttonState = digitalRead(buttonPin);
            int16_reedSwitch = buttonState;

        #ifdef USE_DISPLAY
            // Interval and Counter values are combined on a single row.
            // This allows to keep the 3rd row empty which makes the
            // information better readable on the small display.
            display.setCursor(COL_0, INTERVAL_ROW);
            display.print(" Ctr:");
        #ifdef USE_SERIAL
            printEvent(timestamp, "Input data collected", PrintTarget::Serial);
            printSpaces(serial, MESSAGE_INDENT);
            Serial.print("sensorsA : ");
            Serial.print("Reed Switch :");

        // For simplicity LMIC-node will try to send an uplink
        // message every time processWork() is executed.

        // Schedule uplink message if possible
        if (LMIC.opmode & OP_TXRXPEND)
            // TxRx is currently pending, do not send.
            #ifdef USE_SERIAL
                printEvent(timestamp, "Uplink not scheduled because TxRx pending", PrintTarget::Serial);
            #ifdef USE_DISPLAY
                printEvent(timestamp, "UL not scheduled", PrintTarget::Display);
            // Prepare uplink payload.
            uint8_t fPort = 10;
            payloadBuffer[0] = int16_temperatureA >> 8;
            payloadBuffer[1] = int16_temperatureA;
            payloadBuffer[2] = int16_reedSwitch;

            uint8_t payloadLength = 3;

            scheduleUplink(fPort, payloadBuffer, payloadLength);

void processDownlink(ostime_t txCompleteTimestamp, uint8_t fPort, uint8_t* data, uint8_t dataLength)
    // This function is called from the onEvent() event handler
    // on EV_TXCOMPLETE when a downlink message was received.

    // Implements a 'reset counter' command that can be sent via a downlink message.
    // To send the reset counter command to the node, send a downlink message
    // (e.g. from the TTN Console) with single byte value resetCmd on port cmdPort.

    #define OnboardLed 13
    const uint8_t cmdPort = 100;
    const uint8_t resetCmd= 0xC0;

    if (fPort == cmdPort && dataLength == 1 && data[0] == resetCmd)
        #ifdef USE_SERIAL
            printSpaces(serial, MESSAGE_INDENT);
            serial.println(F("Reset cmd received"));
        ostime_t timestamp = os_getTime();

        pinMode(OnboardLed, OUTPUT);
        pinMode(greenLed, OUTPUT);

        digitalWrite(OnboardLed, HIGH);
        digitalWrite(greenLed, HIGH);
        digitalWrite(OnboardLed, LOW);
        digitalWrite(greenLed, LOW);
        //printEvent(timestamp, "Counter reset", PrintTarget::All, false);

//  █ █ █▀▀ █▀▀ █▀▄   █▀▀ █▀█ █▀▄ █▀▀   █▀▀ █▀█ █▀▄
//  █ █ ▀▀█ █▀▀ █▀▄   █   █ █ █ █ █▀▀   █▀▀ █ █ █ █
//  ▀▀▀ ▀▀▀ ▀▀▀ ▀ ▀   ▀▀▀ ▀▀▀ ▀▀  ▀▀▀   ▀▀▀ ▀ ▀ ▀▀ 

void setup() 
    // boardInit(InitType::Hardware) must be called at start of setup() before anything else.
    bool hardwareInitSucceeded = boardInit(InitType::Hardware);

    #ifdef USE_DISPLAY 

    #ifdef USE_SERIAL


    #if defined(USE_SERIAL) || defined(USE_DISPLAY)

    if (!hardwareInitSucceeded)
        #ifdef USE_SERIAL
            serial.println(F("Error: hardware init failed."));
        #ifdef USE_DISPLAY
            // Following mesage shown only if failure was unrelated to I2C.
            display.setCursor(COL_0, FRMCNTRS_ROW);
            display.print(F("HW init failed"));

    //pinMode(buttonPin, INPUT);

	attachInterrupt(digitalPinToInterrupt(buttonPin), blink, CHANGE);