SX1276 on Arduino Uno R3 can't join TTN via OTAA

Hi,
I’m trying to connect an SX1276 (from Amazon.de) on Arduino Uno R3 to TTN via OTAA but in the console there is no activity.

There already exist similar topics but nothing helped me.

I use the “MCCI LoRaWAN LMIC library” v4.1.1 and just adapted the example sketch “ttn_otaa”… entered the EUIs and AppKey like in my application on TTN and adjusted the Pinmap. That’s the 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]={ 0x00, 0x00, 0x00, 0x00, 0x00, 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]={ 0x0B, 0x8C, 0x06, 0xD0, 0x7E, 0xD5, 0xB3, 0x70 };
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] = { ***** };
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 = 9,
    .dio = {4, 3, 2},
};

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(1);
            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();
}

My pin connections (Arduino - SX1276):

  • 3.3V - VCC
  • GND - GND
  • 4 - DIO0
  • 3 - DIO1
  • 2 - DIO2
  • 9 - REST
  • 10 - NSS
  • 13 - SCK
  • 11 - MOSI
  • 12 - MISO

My settings in TTN:

  • frequency plan: Europe 863-870 MHz (SF9 for RX2 - recommended)
  • LoRaWAN version: LoRaWAN Specification 1.0.3
  • Regional Parameters version: RP001 Regional Parameters 1.0.3 Revision A

All I get in the Serial Monitor is:

14:37:58.742 → Packet queued
14:37:58.816 → 1759: EV_JOINING
14:37:58.816 → 2038: EV_TXSTART
14:38:05.195 → 403785: EV_JOIN_TXCOMPLETE: no JoinAccept
14:38:05.289 → 404943: EV_TXSTART
14:38:11.358 → 785328: EV_JOIN_TXCOMPLETE: no JoinAccept

There also appeared failures (radio.c:923 / radio.c:1065) but not always. I think they deal with sleep mode but I’m not sure if that’s the problem in general.

My distance to the gateway is around 1.3km and I have visual contact.

So does anyone know what the problem could be in this case? I’m not even sure if it has to do with the code, settings in TTN, hardware or something else.

Thanks and kind regards!

Search for “developing without owning a gateway of my own in the hope that I’ve got Line of Sight” :wink:

What does the code say on the radio.c lines - we are volunteers here, no time to start ferreting about in source code.

You MUST increase the uplink delay as per the Fair Use Policy.

Suspending thread pending confirmation of adherence to FUP

Thanks a lot for your reply!

These are the radio.c lines (some more lines around the ASSERT() for context):

  • radio.c:923:
static void rxlora (u1_t rxmode) {
    // select LoRa modem (from sleep mode)
    opmodeLora();
    ASSERT((readReg(RegOpMode) & OPMODE_LORA) != 0); //important line 923
    // enter standby mode (warm up))
    opmode(OPMODE_STANDBY);
    // don't use MAC settings at startup
    if(rxmode == RXMODE_RSSI) { // use fixed settings for rssi scan
        writeReg(LORARegModemConfig1, RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG1);
        writeReg(LORARegModemConfig2, RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2);
    } else { // single or continuous rx mode
        // configure LoRa modem (cfg1, cfg2)
        configLoraModem();
        // configure frequency
        configChannel();
    }
    // set LNA gain
    writeReg(RegLna, LNA_RX_GAIN);
    // set max payload size
    writeReg(LORARegPayloadMaxLength, MAX_LEN_FRAME);
  • radio.c:1065:
static void startrx (u1_t rxmode) {
    ASSERT( (readReg(RegOpMode) & OPMODE_MASK) == OPMODE_SLEEP ); // important line 1065
    if(getSf(LMIC.rps) == FSK) { // FSK modem
        rxfsk(rxmode);
    } else { // LoRa modem
        rxlora(rxmode);
    }
    // the radio will go back to STANDBY mode as soon as the RX is finished
    // or timed out, and the corresponding IRQ will inform us about completion.
}

I’m not sure how to increase uplink delay. I thought its “TX_INTERVAL” but that doesn’t seem to change anything and its already at 60 seconds.

And does this suggest that is an acceptable rate?

https://avbentem.github.io/airtime-calculator/ttn/eu868/15

Key is if it doesnt work after a TX or a few Tx’s then frankly it ain’t going to! And just banging away breachung FUP gets people upset! :wink: Trick (as Nick would often tell you) if get a button working and press to test, then all should be good wrt FUP

“Already at 60 seconds” is so totally the wrong answer.

Please post the link to where the FUP is so that we know you have read it.

Then we can look at the fix.

PS: The formatting tool for code & logs is </> will format the code so that it’s readable.

Oh thanks! I didn’t find something like that.
But how do I know the Spreading Factor? I understand what it is about but not how to figure out which is used… Do I have to test or is it determined in the program or what do I have to do?

That’s what I read about FUP.

Somewhere in the www I read that 20 seconds delay are enough for most devices. That’s why I assumed the pre-set 60 seconds should be fine.

But “TX_INTERVAL” is the right variable to change, isn’t it?

Ah and sorry for the format-mistake!

20 minutes would be short, 20 hours would be long.

Overall 80% of the internet is people posting BS, be it product marketing or videos of puppies. Apart from a few select sites on LoRaWAN (this forum, the docs, Disk91 and a few others), 95% of the sites on LoRaWAN are totally misguided - including the product vendors.

Yes

1 Like

Leave that until you’ve got your device working but mostly you leave that because a working device will manage it for you.

Seems a dumb question, but do you have an antenna on the device?

What is the reported RSSI & SNR of the Join Request on the gateway console? Just the numbers, no need for all the JSON or screenshots.

Yes, I do have an antenna on the device. It‘s the same as shown on Amazon (I sent the link in my first post.).

Unfortunately I do not have access to the gateway since its not my own but belongs to the city council.

When I get a moment I’ll scroll up and find and click that link.

My bad, I forgot:

The forum is littered with such instances. You’ve no way of knowing if the JR is being heard by a gateway without access to the console. So either get a gateway or get very close to it.

1 Like

Alright. Too bad. So I will have to get close to the gateway (I already did that before but I’ll give it a second try) or if that doesn’t help I’ll try to find someone who owns a gateway and can tell me if there is any traffic with my device.
Thank you for your patience!