Not receiving what I send?

I will really appreciate some ideas on what to do next. My setup is an Arduino Uno with a Dragino shield. When I send 1 byte from the Uno+Dragino then I receive 1 byte, when I send 2 bytes I receive 2 bytes in the test TTN Application. But my problem is that I can not work out the relation to what I send, and and what I then receive. What do I need to do to receive the actual value that I send?

For example:

Send value 0x05 = received payload: 3D, testvalue:61
Send value 0x04 = received payload: 3C, testvalue:60
Send value 0x03 = received payload: 3B, testvalue:59
Send value 0x02 = received payload: 3A, testvalue:58
Send value 0x01 = received payload: 39, testvalue:57
Send value 0x00 = received payload: 38, testvalue:56

image

Here is the Payload Formatting that I apply to get “testvalue” above:
image

Here is the modified LMIC example code that I run on the Uno + Dragino:

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

// LoRaWAN NwkSKey, network session key
//static const PROGMEM u1_t NWKSKEY[16] = { 0x05, 0x31, 0x4F, 0x8D, 0x53, 0xD3, 0x70, 0x75, 0xC9, 0x00, 0x1D, 0xD2, 0x5E, 0x56, 0xBB, 0xA0 };
static const PROGMEM u1_t NWKSKEY[16] = { 0x5B, 0xC5, 0xD6, 0x71, 0x2C, 0x00, 0xAC, 0xBC, 0x0A, 0x41, 0x9B, 0xAF, 0xC5, 0x58, 0x1E, 0x5E };

// LoRaWAN AppSKey, application session key
static const u1_t PROGMEM APPSKEY[16] = { 0x70, 0xB3, 0xD5, 0x7E, 0xD0, 0x01, 0x3D, 0x67 };

// LoRaWAN end-device address (DevAddr)
static const u4_t DEVADDR = 0x26062F90; // <-- 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) { }

byte myValue = 0x05;

static byte mydata[1] = { myValue};
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 (Dragino Shield)
const lmic_pinmap lmic_pins = {
    .nss = 10,
    .rxtx = LMIC_UNUSED_PIN,
    .rst = 9,
    .dio = {2, 6, 7},
};

void onEvent (ev_t ev) {
}

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.
        
        for (int i = 0; i < sizeof(mydata); i++) {
          Serial.println(mydata[i]);
        }
        
        LMIC_setTxData2(1, mydata, sizeof(mydata), 0);
    }
    // Next TX is scheduled after TX_COMPLETE event.
}

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

//    #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();

    // 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

    // Setup the TTN Channels for NZ
    LMIC_selectChannels();

    // 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);

    // Let LMIC offset for +/- 1% clock error
    LMIC_setClockError(MAX_CLOCK_ERROR*10/100);

    // Start job
    do_send(&sendjob);
}

void loop() {
    os_runloop_once();
}

A secret AppSKey should be 16 bytes, so this is too short. (Also, as it starts with 0x70, it looks like an AppEUI, which for TTN always starts with 0x70.)

First, TTN will try to validate the Message Integrity Code (MIC) using the secret NwkSKey of all devices it can find for the given DevAddr. (A DevAddr is not unique, but the combination of DevAddr and NwkSKey should be.) As soon as a calculated MIC matches the one that’s in the LoRaWAN headers, TTN knows that it found the correct device. And at that point it also knows that the value as transmitted by the device was not altered in any way.

So far, so good.

Next, it tries to decrypt the encrypted application payload using the secret AppSKey it knows for that device. But as in your case the device has used the wrong key during encryption, TTN just comes up with a different value when decrypting that with the correct key, and it has no way to validate if the decrypted value is correct and just gives you the value it has found.

(Now that you’ve publicly posted the secret NwkSKey, make sure to generate new keys in TTN Console.)

Thanks for your feedback and advise @arjanvanb.
I makes perfect sense what you are describing and I did not know how the encryption/decryption mechanism works. I will make the changes, try again and report back!

If you still have access to erroneous full LoRaWAN packets, then you can paste those into an online LoRaWAN decoder, along with the proper NwkSKey and the wrong 8-byte AppSKey, padded with zeroes. You can then see that decrypting with the wrong key actually gives you the expected value.

(Given the number 16 in PROGMEM APPSKEY[16], the missing 8 bytes will probably have been set to be zeroes, so I guess the application payload was encrypted using 70B3D57ED0013D670000000000000000.)

Or, if you don’t have those erroneous payloads anymore, that decoder will also show you what happens if you change just a single bit or byte of the secret AppSKey.

Hi @arjanvanb,
Many thanks - once I added the correct keys, everything worked perfectly!

1 Like