LMIC Unknown event: 20

Hey @all :slight_smile:

I’ve been trying for months now to send a simple sensor value to my gateway in order to visualize it iwo.
I have currently done a test with the following hardware: Arduino pro mini 3.3v 8Mhz with an SX1276 Lora module I set the config.h to EU868 Mhz, the data are also received by my gateway but iwas not right because in the range Application, no data arrives at my sensor. Can anyone help me further? I am desperate

this showing my console on the arduino:

Preformatted text
Starting
Humidity: 0
Temperature: 0
Packet queued
2943: EV_JOINING
242557: EV_TXSTART
638184: Unknown event: 20
Preformatted text

and this shows my Gateway: Bildschirmfoto 2021-01-04 um 22.42.53 Bildschirmfoto 2021-01-04 um 22.43.22 Bildschirmfoto 2021-01-04 um 22.44.15

regards from germany

It looks like your node does not join TTN. There are several possible reasons, most common is AppEUI, DevEUI and AppKey not matching the application credentials (possibly because of wrong byte ordering). If you want an answer and not just a load of speculation you need to post (a pointer) to the exact code you are using, and a pointer to the exact version of the stack. (Site and version)

thank you for the very fast reply

this is the arduino code:

/*******************************************************************************
 * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
 * Copyright (c) 2018 Terry Moore, MCCI
 *
 * Permission is hereby granted, free of charge, to anyone
 * obtaining a copy of this document and accompanying files,
 * to do whatever they want with them without any restriction,
 * including, but not limited to, copying, modification and redistribution.
 * NO WARRANTY OF ANY KIND IS PROVIDED.
 *
 * This example sends a valid LoRaWAN packet with payload "Hello,
 * world!", using frequency and encryption settings matching those of
 * the The Things Network.
 *
 * This uses OTAA (Over-the-air activation), where where a DevEUI and
 * application key is configured, which are used in an over-the-air
 * activation procedure where a DevAddr and session keys are
 * assigned/generated for use with all further communication.
 *
 * Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in
 * g1, 0.1% in g2), but not the TTN fair usage policy (which is probably
 * violated by this sketch when left running for longer)!

 * To use this sketch, first register your application and device with
 * the things network, to set or generate an AppEUI, DevEUI and AppKey.
 * Multiple devices can use the same AppEUI, but each device has its own
 * DevEUI and AppKey.
 *
 * Do not forget to define the radio type correctly in config.h.
 *
 *******************************************************************************/

#include <lmic.h>
#include <hal/hal.h>
#include <SPI.h>
#include <DHT.h>
#include <DHT_U.h>
#include <Adafruit_Sensor.h>


//
// For normal use, we require that you edit the sketch to replace FILLMEIN
// with values assigned by the TTN console. However, for regression tests,
// we want to be able to compile these scripts. The regression tests define
// COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non-
// working but innocuous value.
//
#ifdef COMPILE_REGRESSION_TEST
# define FILLMEIN 0
#else
# warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!"
# define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN)
#endif

// This EUI must be in little-endian format, so least-significant-byte
// first. When copying an EUI from ttnctl output, this means to reverse
// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3,
// 0x70.
static const u1_t PROGMEM APPEUI[8]={ 0x70, 0xB3, 0xD5, 0x7E, 0xD0, 0x03, 0x7F, 0x29 };
void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);}

// This should also be in little endian format, see above.
static const u1_t PROGMEM DEVEUI[8]={ 0xA1, 0xB4, 0xB6, 0xC2, 0x5B, 0x88, 0x99, 0x11 };
void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);}

// This key should be in big endian format (or, since it is not really a
// number but a block of memory, endianness does not really apply). In
// practice, a key taken from ttnctl can be copied as-is.
static const u1_t PROGMEM APPKEY[16]={ 0x0F, 0x84, 0x94, 0x1B, 0x54, 0x93, 0x9F, 0x4C, 0xE4, 0x99, 0x53, 0x05, 0x12, 0xED, 0x42, 0xB8 };
void os_getDevKey (u1_t* buf) {  memcpy_P(buf, APPKEY, 16);}

static uint8_t btn_activated[1] = { 0x01};
static osjob_t sendjob;

// Schedule TX every this many seconds (might become longer due to duty
// cycle limitations).
const unsigned TX_INTERVAL = 7;

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

//------ Added ----------------
#define LED_YELLOW 8
#define LED_GREEN  6

#define DHT_PIN 7
#define BTN_PIN 9

// DHT11 or DHT22
#define DHTTYPE DHT22

// Initialize dht
DHT dht(DHT_PIN, DHTTYPE);

int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button
//-----------------------------

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"));
            {
              u4_t netid = 0;
              devaddr_t devaddr = 0;
              u1_t nwkKey[16];
              u1_t artKey[16];
              LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey);
              Serial.print("netid: ");
              Serial.println(netid, DEC);
              Serial.print("devaddr: ");
              Serial.println(devaddr, HEX);
              Serial.print("artKey: ");
              for (int i=0; i<sizeof(artKey); ++i) {
                Serial.print(artKey[i], HEX);
              }
              Serial.println("");
              Serial.print("nwkKey: ");
              for (int i=0; i<sizeof(nwkKey); ++i) {
                Serial.print(nwkKey[i], HEX);
              }
              Serial.println("");
            }
            // Disable link check validation (automatically enabled
            // during join, but because slow data rates change max TX
        // size, we don't use it in this example.
            LMIC_setLinkCheckMode(0);
            break;
        /*
        || This event is defined but not used in the code. No
        || point in wasting codespace on it.
        ||
        || 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.print(F("Received "));
              Serial.print(LMIC.dataLen);
              Serial.println(F(" bytes of payload"));

              //------ Added ----------------
              if (LMIC.dataLen == 1) {
                uint8_t result = LMIC.frame[LMIC.dataBeg + 0];
                if (result == 0)  {
                  Serial.println("RESULT 0");
                  digitalWrite(LED_YELLOW, LOW);
                  digitalWrite(LED_GREEN, LOW);
                }              
                if (result == 1)  {
                  Serial.println("RESULT 1");
                  digitalWrite(LED_YELLOW, HIGH);
                  digitalWrite(LED_GREEN, LOW);                  
                } 
                if (result == 2)  {
                  Serial.println("RESULT 2");
                  digitalWrite(LED_YELLOW, LOW);
                  digitalWrite(LED_GREEN, HIGH);                     
                } 
                if (result == 3)  {
                  Serial.println("RESULT 3");
                  digitalWrite(LED_YELLOW, HIGH);
                  digitalWrite(LED_GREEN, HIGH);                       
                }                                             
              }
             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;
        /*
        || This event is defined but not used in the code. No
        || point in wasting codespace on it.
        ||
        || case EV_SCAN_FOUND:
        ||    Serial.println(F("EV_SCAN_FOUND"));
        ||    break;
        */
        case EV_TXSTART:
            Serial.println(F("EV_TXSTART"));
            break;
        default:
            Serial.print(F("Unknown event: "));
            Serial.println((unsigned) ev);
            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 {
        uint32_t humidity = dht.readHumidity(false) * 100;
        uint32_t temperature = dht.readTemperature(false) * 100;
        
        Serial.println("Humidity: " + String(humidity));
        Serial.println("Temperature: " + String(temperature));
        
        byte payload[4];
        payload[0] = highByte(humidity);
        payload[1] = lowByte(humidity);
        payload[2] = highByte(temperature);
        payload[3] = lowByte(temperature); 
    
        //Prepare upstream data transmission at the next possible time.
        LMIC_setTxData2(1, payload, sizeof(payload), 0);
        Serial.println(F("Packet queued"));
    }
    // Next TX is scheduled after TX_COMPLETE event.
}

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

    //------ Added ----------------
    pinMode(LED_YELLOW, OUTPUT);
    pinMode(LED_GREEN, OUTPUT);
    pinMode(BTN_PIN, INPUT);

    digitalWrite(BTN_PIN, LOW); 

    dht.begin();
    //-----------------------------
    
    #ifdef VCC_ENABLE
    // For Pinoccio Scout boards
    pinMode(VCC_ENABLE, OUTPUT);
    digitalWrite(VCC_ENABLE, HIGH);
    delay(1000);
    #endif

    // LMIC init
    os_init();
    // Reset the MAC state. Session and pending data transfers will be discarded.
    LMIC_reset();

    // Use with Arduino Pro Mini ATmega328P 3.3V 8 MHz
    // Let LMIC compensate for +/- 1% clock error
    LMIC_setClockError(MAX_CLOCK_ERROR * 1 / 100); 

    // Start job (sending automatically starts OTAA too)
    do_send(&sendjob);
}

void loop() {
    //------ Added ----------------
    // read the state of the button value:
    buttonState = digitalRead(BTN_PIN);

    // compare the buttonState to its previous state
    if (buttonState != lastButtonState) {

        if (buttonState == HIGH) {
            // if the current state is HIGH then the button went from off to on:
            LMIC_setTxData2(1, btn_activated, sizeof(btn_activated), 0);
            Serial.println(F("Button On"));
        } else {
            // if the current state is LOW then the button went from on to off:
            Serial.println(F("Button Off"));
        }
        // Delay a little bit to avoid bouncing
        delay(50);
    }

    // save the current state as the last state, for next time through the loop
    lastButtonState = buttonState;
    //-----------------------------
    
    os_runloop_once();
} 

I have attached the screenshots if I should post something else just let me know. greetingsBildschirmfoto 2021-01-05 um 00.08.12 Bildschirmfoto 2021-01-05 um 00.08.40 Bildschirmfoto 2021-01-05 um 00.08.59

Unknown even 20 has been explained here and at the MCCI LMiC repo itself many times. It comes from using a sketch that has not been updated to match improvements in the LMiC library, though technically that by itself is not an issue; it’s just being unable to translate a purely advisory message from number to name.

Beware that trying to run LMiC on an ATmega328 is a squeeze and not generally recommended; it can be done, but best results are usually had with ARM-based boards like the feather or MCCI’s STM32L073 BSP for the Murata module they use in their own boards.

Next time please properly format your message. Look at the preview to see if code becomes a code block and not just text.

In your screen shots you are using ABP mode while the code is for OTAA. Change the device so the activation method in the code and on TTN match. I would suggest to switch the device back to OTAA in the TTN console.

@cslorabox
Thank you for your answer
I have test many libs and i get every time no data in the sensor option only the message in the gateway info
I only want for my first steps the sx1276 with arduino pro mini 3,3v and dht22 or ds18b20 sensor for making a chep solar powered sensor node. Maybe you can send me a link for a good working lib and code that would be great :slight_smile:

@kersing
sorry for the wrong post format i try it before but i dont know how i must format the code to post it well

I have now changed the sensor to otaa but i get the same result :confused:

Greetings

Please read this message on formatting your posts.

Are the EUIs and the AppKey the same? Did you check on the correct byte order to enter them in the code? (In the TTN gateway page you can see both EUIs when an OTAA request is received, do those match the information on the device page?)

1 Like

I think the first port of call would be to get an un-modified ABP example working.

And please please do not alter the code to send every 7 seconds - using ABP for testing means you can hit the reset button on demand to trigger a send.

Once you have it working, please review the TTN fair use policy as well as your own legal requirements for how often you send at various data rates.

The best known good Arduino LMIC library is this:

I haven’t transferred my production code base over to this yet so if it turns out to be too big for the ATmega328, then you could use this, which is the library I have been using until recently. The fact it is deprecated does not stop it working - at least to get you going:

Both of the libraries have comprehensive instructions on configuration for you to read.

Please don’t add your own code in until you have got a working ABP.

1 Like

To start with, use the examples ttn-abp.ino and ttn-otaa.ino that are included with the LMIC library that you are using.
To prevent running into version incompatibilities: use the examples included with your library and do not use examples from a different version of the same library or from a different library/repository.

As @cslorabox and @descartes already mentioned you may quickly run out of memory when using MCCI LMIC with ATmega328 MCU boards like a Pro Mini (when doing anything more than bare basics). If so then the older Classic LMIC may be a good alternative.
(That said, memory size is probably unrelated to your current issue.)

Also see: Arduino LMIC library naming: Classic LMIC and MCCI LMIC [HowTo]

1 Like

@kersing
thank you, i have read the formatting post :slight_smile:

@kersing
@descartes
@bluejedi
The first problem The first problem was that I did not know the formats lsb or msp it is not really clear in the www which format is used where thanks to an article I have now set it correctly and the version with the lib: and the example sketch for ADB worked (see the first 2 screenshots). But I can’t get the OTAA sketch to run (see the 3 screeshot from the Arduino terminal (no join accept). i read some post and test some fixes but nothing worked example: Feather M0 EV_JOIN_TXCOMPLETE: no JoinAccept problem

ps. i know the TTN fair use policy i have only for my short test used the 7 seconds but i now leave it at 60 seconds and i never will use so much traffic i only want sensor nodes for 2-6 data transfers per day. so all good :wink:

any ideas what could be wrong with the otaa example ?

I only adjusted the pin assignment of my Arduino Pro Minis + SX1276 Lora module and the keys

in the otaa-code :



/*******************************************************************************
 * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
 * Copyright (c) 2018 Terry Moore, MCCI
 *
 * Permission is hereby granted, free of charge, to anyone
 * obtaining a copy of this document and accompanying files,
 * to do whatever they want with them without any restriction,
 * including, but not limited to, copying, modification and redistribution.
 * NO WARRANTY OF ANY KIND IS PROVIDED.
 *
 * This example sends a valid LoRaWAN packet with payload "Hello,
 * world!", using frequency and encryption settings matching those of
 * the The Things Network.
 *
 * This uses OTAA (Over-the-air activation), where where a DevEUI and
 * application key is configured, which are used in an over-the-air
 * activation procedure where a DevAddr and session keys are
 * assigned/generated for use with all further communication.
 *
 * Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in
 * g1, 0.1% in g2), but not the TTN fair usage policy (which is probably
 * violated by this sketch when left running for longer)!

 * To use this sketch, first register your application and device with
 * the things network, to set or generate an AppEUI, DevEUI and AppKey.
 * Multiple devices can use the same AppEUI, but each device has its own
 * DevEUI and AppKey.
 *
 * Do not forget to define the radio type correctly in
 * arduino-lmic/project_config/lmic_project_config.h or from your BOARDS.txt.
 *
 *******************************************************************************/

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

//
// For normal use, we require that you edit the sketch to replace FILLMEIN
// with values assigned by the TTN console. However, for regression tests,
// we want to be able to compile these scripts. The regression tests define
// COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non-
// working but innocuous value.
//
#ifdef COMPILE_REGRESSION_TEST
# define FILLMEIN 0
#else
# warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!"
# define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN)
#endif

// This EUI must be in little-endian format, so least-significant-byte
// first. When copying an EUI from ttnctl output, this means to reverse
// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3,0x70.

static const u1_t PROGMEM APPEUI[8]= { 0x19, 0x8A, 0x03, 0xD0, 0x7E, 0xD5, 0xB3, 0x70 };
void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);}

// This should also be in little endian format, see above.
static const u1_t PROGMEM DEVEUI[8]= { 0x0E, 0xBF, 0x84, 0x8B, 0x6D, 0x77, 0xFF, 0x22 };
void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);}

// This key should be in big endian format (or, since it is not really a
// number but a block of memory, endianness does not really apply). In
// practice, a key taken from ttnctl can be copied as-is.
static const u1_t PROGMEM APPKEY[16] = { 0x2C, 0x0F, 0x12, 0xA5, 0x78, 0x3A, 0xDD, 0x0F, 0x55, 0x24, 0xC5, 0x77, 0x45, 0x61, 0x32, 0x0F  };
void os_getDevKey (u1_t* buf) {  memcpy_P(buf, APPKEY, 16);}

static uint8_t mydata[] = "Hello, world!";
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 = 5,
    .dio = {2, 3, 4},
};

void printHex2(unsigned v) {
    v &= 0xff;
    if (v < 16)
        Serial.print('0');
    Serial.print(v, HEX);
}

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"));
            {
              u4_t netid = 0;
              devaddr_t devaddr = 0;
              u1_t nwkKey[16];
              u1_t artKey[16];
              LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey);
              Serial.print("netid: ");
              Serial.println(netid, DEC);
              Serial.print("devaddr: ");
              Serial.println(devaddr, HEX);
              Serial.print("AppSKey: ");
              for (size_t i=0; i<sizeof(artKey); ++i) {
                if (i != 0)
                  Serial.print("-");
                printHex2(artKey[i]);
              }
              Serial.println("");
              Serial.print("NwkSKey: ");
              for (size_t i=0; i<sizeof(nwkKey); ++i) {
                      if (i != 0)
                              Serial.print("-");
                      printHex2(nwkKey[i]);
              }
              Serial.println();
            }
            // Disable link check validation (automatically enabled
            // during join, but because slow data rates change max TX
	          // size, we don't use it in this example.
            LMIC_setLinkCheckMode(0);
            break;
        /*
        || This event is defined but not used in the code. No
        || point in wasting codespace on it.
        ||
        || 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.print(F("Received "));
              Serial.print(LMIC.dataLen);
              Serial.println(F(" bytes of payload"));
            }
            // 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;
        /*
        || This event is defined but not used in the code. No
        || point in wasting codespace on it.
        ||
        || case EV_SCAN_FOUND:
        ||    Serial.println(F("EV_SCAN_FOUND"));
        ||    break;
        */
        case EV_TXSTART:
            Serial.println(F("EV_TXSTART"));
            break;
        case EV_TXCANCELED:
            Serial.println(F("EV_TXCANCELED"));
            break;
        case EV_RXSTART:
            /* do not print anything -- it wrecks timing */
            break;
        case EV_JOIN_TXCOMPLETE:
            Serial.println(F("EV_JOIN_TXCOMPLETE: no JoinAccept"));
            break;

        default:
            Serial.print(F("Unknown event: "));
            Serial.println((unsigned) ev);
            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 {
        // Prepare upstream data transmission at the next possible time.
        LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0);
        Serial.println(F("Packet queued"));
    }
    // Next TX is scheduled after TX_COMPLETE event.
}

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

    #ifdef VCC_ENABLE
    // For Pinoccio Scout boards
    pinMode(VCC_ENABLE, OUTPUT);
    digitalWrite(VCC_ENABLE, HIGH);
    delay(1000);
    #endif

    // LMIC init
    os_init();
    // Reset the MAC state. Session and pending data transfers will be discarded.
    LMIC_reset();

    // Start job (sending automatically starts OTAA too)
    do_send(&sendjob);
}

void loop() {
    os_runloop_once();
}

greetings Marc

Do you see a OTAA response in the gateway traffic page? And an OTAA request in the application data?

UPDATE:

Now it worked because of this 3 Lines i have read about on this Homepage:

´´´
void setup() {

...

LMIC_setAdrMode(1);
LMIC_setLinkCheckMode(1);
LMIC_setClockError(MAX_CLOCK_ERROR * 1 / 100);

}

I dont now why but maybe someone can tell me why in all sketches the linkcheck is diabled and thats why i get this “no join” error?

see picture ( waiting rx windows ) what is waiting?

regards Marc :wink:join accept

And somebody know what this mean?

OTAA will not work if downlink messages are not working properly.
ABP does not need downlink messages which is why you should always test with ABP first.

There can be several reasons for downlinks not working (properly).
This is usually caused by either one of the following:

  1. DIO1 is not connected or not properly mapped to the correct GPIO pin.
  2. Timing issues.

It was probably LMIC_setClockError(MAX_CLOCK_ERROR * 1 / 100); that made it work.

This was often needed for ATmega328 to fix timing issues when using Classic LMIC or older versions of MCCI LMIC.
To my understanding use of LMIC_setClockError() should not be needed with recent versions of MCCI LMIC but I may be mistaking and have not tested this myself.

1 Like
  1. LinkCheck is not the magic, the setClockError is.
  2. To understand what the waiting is for I suggest you read the LoRaWAN specification and learn what is expected a class A device does.
1 Like

I’ve not used it since “the summer of testing” - that is to say, none of my production units use it. The acid test is to try a downlink using ABP - it that fails and you can confirm the gateway transmitted on time, then it’s time to look at this.

Ironically, this can also be what breaks things in some versions of LMiC.

It’s important that the node be listening when the network replies. If there’s clock error, the node may want to start listening earlier. But naive implementations used to also stop listening earlier, and give up before the actual correct time of transmission.

If there’s uncertainty of node timing, overall the best thing to do is to use a scope or logic analyzer to determine what the timing actually is, vs what it should be.

Do you have any references of such ‘naive implementations’? Does this concern Classic LMIC and MCCI LMIC?

Keep in mind that many users will not have (access to) such equipment.

Maybe you can write a tutorial to guide users how to do this?

1 Like

This has been covered here many times before, and is an example of the many sorts of things which MCCI fixed, that lead Matthijs Kooijman to deprecate his repo because he was not going to try to fix.

Hey @all :wink:

Now i am able to send the Temp and Humidity to TTN and see the payload and the Values but when i test it to send to my ThingSpeak Account then the Values are not correct because the payload must be in a ThingSpeak Format. Maybe anybody know how i format i correct !?

I only want Sensor Nodes with 3 Fields ( Temp / Humidity / BattVoltage ).

This is the Arduino Code is use:

 * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
 * Copyright (c) 2018 Terry Moore, MCCI
 *
 * Permission is hereby granted, free of charge, to anyone
 * obtaining a copy of this document and accompanying files,
 * to do whatever they want with them without any restriction,
 * including, but not limited to, copying, modification and redistribution.
 * NO WARRANTY OF ANY KIND IS PROVIDED.
 *
 * This example sends a valid LoRaWAN packet with payload "Hello,
 * world!", using frequency and encryption settings matching those of
 * the The Things Network.
 *
 * This uses OTAA (Over-the-air activation), where where a DevEUI and
 * application key is configured, which are used in an over-the-air
 * activation procedure where a DevAddr and session keys are
 * assigned/generated for use with all further communication.
 *
 * Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in
 * g1, 0.1% in g2), but not the TTN fair usage policy (which is probably
 * violated by this sketch when left running for longer)!

 * To use this sketch, first register your application and device with
 * the things network, to set or generate an AppEUI, DevEUI and AppKey.
 * Multiple devices can use the same AppEUI, but each device has its own
 * DevEUI and AppKey.
 *
 * Do not forget to define the radio type correctly in config.h.
 *
 *******************************************************************************/

#include <lmic.h>
#include <hal/hal.h>
#include <SPI.h>
#include <DHT.h>
#include <DHT_U.h>
#include <Adafruit_Sensor.h>


//
// For normal use, we require that you edit the sketch to replace FILLMEIN
// with values assigned by the TTN console. However, for regression tests,
// we want to be able to compile these scripts. The regression tests define
// COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non-
// working but innocuous value.
//
#ifdef COMPILE_REGRESSION_TEST
# define FILLMEIN 0
#else
# warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!"
# define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN)
#endif

// This EUI must be in little-endian format, so least-significant-byte
// first. When copying an EUI from ttnctl output, this means to reverse
// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3,
// 0x70.
static const u1_t PROGMEM APPEUI[8]= { 0x19, 0x8A, 0x03, 0xD0, 0x7E, 0xD5, 0xB3, 0x70 };
void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);}

// This should also be in little endian format, see above.
static const u1_t PROGMEM DEVEUI[8]= { 0x0E, 0xBF, 0x84, 0x8B, 0x6D, 0x77, 0xFF, 0x22 };
void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);}

// This key should be in big endian format (or, since it is not really a
// number but a block of memory, endianness does not really apply). In
// practice, a key taken from ttnctl can be copied as-is.
static const u1_t PROGMEM APPKEY[16]= { 0x0F, 0x32, 0x61, 0x45, 0x77, 0xC5, 0x24, 0x55, 0x0F, 0xDD, 0x3A, 0x78, 0xA5, 0x12, 0x0F, 0x2C };
void os_getDevKey (u1_t* buf) {  memcpy_P(buf, APPKEY, 16);}

static uint8_t btn_activated[1] = { 0x01};
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 = 5,
    .dio = {2, 3, LMIC_UNUSED_PIN},
};

//------ Added ----------------
#define LED_YELLOW 8
#define LED_GREEN  6

#define DHT_PIN 7
#define BTN_PIN 9

// DHT11 or DHT22
#define DHTTYPE DHT22

// Initialize dht
DHT dht(DHT_PIN, DHTTYPE);

int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button
//-----------------------------

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"));
            {
              u4_t netid = 0;
              devaddr_t devaddr = 0;
              u1_t nwkKey[16];
              u1_t artKey[16];
              LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey);
              Serial.print("netid: ");
              Serial.println(netid, DEC);
              Serial.print("devaddr: ");
              Serial.println(devaddr, HEX);
              Serial.print("artKey: ");
              for (int i=0; i<sizeof(artKey); ++i) {
                Serial.print(artKey[i], HEX);
              }
              Serial.println("");
              Serial.print("nwkKey: ");
              for (int i=0; i<sizeof(nwkKey); ++i) {
                Serial.print(nwkKey[i], HEX);
              }
              Serial.println("");
            }
            // Disable link check validation (automatically enabled
            // during join, but because slow data rates change max TX
        // size, we don't use it in this example.
            LMIC_setLinkCheckMode(0);
            break;
        /*
        || This event is defined but not used in the code. No
        || point in wasting codespace on it.
        ||
        || 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.print(F("Received "));
              Serial.print(LMIC.dataLen);
              Serial.println(F(" bytes of payload"));

              //------ Added ----------------
              if (LMIC.dataLen == 1) {
                uint8_t result = LMIC.frame[LMIC.dataBeg + 0];
                if (result == 0)  {
                  Serial.println("RESULT 0");
                  digitalWrite(LED_YELLOW, LOW);
                  digitalWrite(LED_GREEN, LOW);
                }              
                if (result == 1)  {
                  Serial.println("RESULT 1");
                  digitalWrite(LED_YELLOW, HIGH);
                  digitalWrite(LED_GREEN, LOW);                  
                } 
                if (result == 2)  {
                  Serial.println("RESULT 2");
                  digitalWrite(LED_YELLOW, LOW);
                  digitalWrite(LED_GREEN, HIGH);                     
                } 
                if (result == 3)  {
                  Serial.println("RESULT 3");
                  digitalWrite(LED_YELLOW, HIGH);
                  digitalWrite(LED_GREEN, HIGH);                       
                }                                             
              }
             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;
        /*
        || This event is defined but not used in the code. No
        || point in wasting codespace on it.
        ||
        || case EV_SCAN_FOUND:
        ||    Serial.println(F("EV_SCAN_FOUND"));
        ||    break;
        */
        case EV_TXSTART:
            Serial.println(F("EV_TXSTART"));
            break;
        default:
            Serial.print(F("Unknown event: "));
            Serial.println((unsigned) ev);
            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 {
        uint32_t humidity = dht.readHumidity(false) * 100;
        uint32_t temperature = dht.readTemperature(false) * 100;
        
        Serial.println("Humidity: " + String(humidity));
        Serial.println("Temperature: " + String(temperature));
        
        byte payload[4];
        payload[0] = highByte(humidity);
        payload[1] = lowByte(humidity);
        payload[2] = highByte(temperature);
        payload[3] = lowByte(temperature); 
    
        //Prepare upstream data transmission at the next possible time.
        LMIC_setTxData2(1, payload, sizeof(payload), 0);
        Serial.println(F("Packet queued"));
    }
    // Next TX is scheduled after TX_COMPLETE event.
}

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

    //------ Added ----------------
    pinMode(LED_YELLOW, OUTPUT);
    pinMode(LED_GREEN, OUTPUT);
    pinMode(BTN_PIN, INPUT);

    digitalWrite(BTN_PIN, LOW); 

    dht.begin();
    //-----------------------------
    
    #ifdef VCC_ENABLE
    // For Pinoccio Scout boards
    pinMode(VCC_ENABLE, OUTPUT);
    digitalWrite(VCC_ENABLE, HIGH);
    delay(1000);
    #endif

    // LMIC init
    os_init();
    // Reset the MAC state. Session and pending data transfers will be discarded.
    LMIC_reset();

    // Use with Arduino Pro Mini ATmega328P 3.3V 8 MHz
    // Let LMIC compensate for +/- 1% clock error
    LMIC_setClockError(MAX_CLOCK_ERROR * 1 / 100); 

    // Start job (sending automatically starts OTAA too)
    do_send(&sendjob);
}

void loop() {
    //------ Added ----------------
    // read the state of the button value:
    buttonState = digitalRead(BTN_PIN);

    // compare the buttonState to its previous state
    if (buttonState != lastButtonState) {

        if (buttonState == HIGH) {
            // if the current state is HIGH then the button went from off to on:
            LMIC_setTxData2(1, btn_activated, sizeof(btn_activated), 0);
            Serial.println(F("Button On"));
        } else {
            // if the current state is LOW then the button went from on to off:
            Serial.println(F("Button Off"));
        }
        // Delay a little bit to avoid bouncing
        delay(50);
    }

    // save the current state as the last state, for next time through the loop
    lastButtonState = buttonState;
    //-----------------------------
    
    os_runloop_once();
}

and the payload decoder in TTN is this:

    if(bytes.length == 1) {
        if(bytes[0] == 1) {
            return {
                'button': 'activated'
            }
        } else {
            return {
                'error': 'button action unknown'
            }   
        }
    } else if(bytes.length == 4) {
        var humidity = (bytes[0]<<8) | bytes[1];
        var temperature = (bytes[2]<<8) | bytes[3];
        return {
            'humidity': humidity/ 100,
            'temperature': temperature/100
        }
    } else {
        return {
            'error': 'payload unknown'
        }
    }
} 

and thats the Thing Speak (right) decoder format :

function Decoder(b, port) { 
  var var1 = b[0];
  var var2 = b[1];
  var var3 = b[2];
  var var4 = b[3];
  var var5 = (b[4] << 8) | b[5];
  var var6 = (b[6] << 8) | b[7];
  var var7 = (b[8] << 8) | b[9];
  var var8 = (b[10] << 8) | b[11];
  var lat = (b[12] | b[13]<<8 | b[14]<<16 | (b[14] & 0x80 ? 0xFF<<24 : 0)) / 10000;
  var lon = (b[15] | b[16]<<8 | b[17]<<16 | (b[17] & 0x80 ? 0xFF<<24 : 0)) / 10000;
  var height = b[18];
  var myStatus = b[19];

  return {
    field1: var1,
    field2: var2,
    field3: var3,
    field4: var4,
    field5: var5,
    field6: var6,
    field7: var7,
    field8: var8,

    latitude: lat,
    longitude: lon,
    elevation: height
  }
}

But i don´t know how to change the payload format in the Arduino code to match the decoder above ?!

thank you all for any help and Info

regards from Germany Bildschirmfoto 2021-01-07 um 17.31.36 Bildschirmfoto 2021-01-07 um 17.32.02

You’d be better starting another thread for that, in fact you’d be better searching the forum first as there are lots of threads on encoding/decoding data.

1 Like