Feather M0 EV_JOIN_TXCOMPLETE: no JoinAccept problem

Hi,

Could I get your collective guru like help on an issue I am having please?

I have a Feather M0 and I am trying to a connect it with OTAA in the EU. I have the board soldered and a jumper between 6 and io1. Pic is attached:

IMG_0396

My issue is that I am getting:

06:33:36.561 -> 8890728: EV_TXSTART
06:33:42.866 -> 9284437: EV_JOIN_TXCOMPLETE: no JoinAccept
06:34:48.521 -> 13388293: EV_TXSTART
06:34:54.858 -> 13785217: EV_JOIN_TXCOMPLETE: no JoinAccept
06:36:57.342 -> 21441679: EV_TXSTART
06:37:03.713 -> 21838604: EV_JOIN_TXCOMPLETE: no JoinAccept
06:39:06.862 -> 29535310: EV_TXSTART
06:39:13.195 -> 29932236: EV_JOIN_TXCOMPLETE: no JoinAccept

Now somewhere along the line I was getting the same problem but I was getting traffic at the gateway:
Screenshot from 2020-07-16 06-43-32

When I say ‘somewhere along the line’ I mean I have been reading other people’s issues and fiddling about and forgotten what I had at the time to get the traffic at the gateway.

Now I think its something with the region selection. This is because I was I was trying to alter the original lmic_project_config.h to match EU instead of the US as I thought that maybe the added project_config folder in the sketch folder wasn’t being ‘listening to’. When I did this I got an error about LMIC_selectSubBand. I commented this out as I had read it may not be needed and I get back to the cycling TXSTART Join etc. When take the comment out for LMIC_selectSubBand(1) and put the original lmic_project_config.h back to US I get the cycling again. It seems to be the change to EU it doesn’t like. I have also taken out the project_config folder in the sketch folder and tried all this with the same result.

To me it seems not to like the EU for some reason? Or am I just reading too much into selectSubBand being required to select a band?

So after all that any advice would be appreciated :slight_smile:

I am attaching the sketch if its useful to see.

#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]= { 0x70, 0xB3, 0xD5, 0x7E, 0xD0, 0x03, 0x15, 0xE7 };
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]= { 0x00, 0x22, 0xBF, 0xD0, 0x77, 0xC2, 0x17, 0xAE };
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 the TTN console can be copied as-is.
static const u1_t PROGMEM APPKEY[16] = { 0x96, 0x54, 0x5B, 0xBC, 0x56, 0x6F, 0x94, 0x58, 0xD7, 0x20, 0x17, 0xD9, 0x32, 0x23, 0xC1, 0x38 };
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
//
// Adafruit BSPs are not consistent -- m0 express defs ARDUINO_SAMD_FEATHER_M0,
// m0 defs ADAFRUIT_FEATHER_M0
//
#if defined(ARDUINO_SAMD_FEATHER_M0) || defined(ADAFRUIT_FEATHER_M0)
// Pin mapping for Adafruit Feather M0 LoRa, etc.
const lmic_pinmap lmic_pins = {
    .nss = 8,
    .rxtx = LMIC_UNUSED_PIN,
    .rst = 4,
    .dio = {3, 6, LMIC_UNUSED_PIN},
    .rxtx_rx_active = 0,
    .rssi_cal = 8,              // LBT cal for the Adafruit Feather M0 LoRa, in dB
    .spi_freq = 8000000,
};
#elif defined(ARDUINO_AVR_FEATHER32U4)
// Pin mapping for Adafruit Feather 32u4 LoRa, etc.
// Just like Feather M0 LoRa, but uses SPI at 1MHz; and that's only
// because MCCI doesn't have a test board; probably higher frequencies
// will work.
const lmic_pinmap lmic_pins = {
    .nss = 8,
    .rxtx = LMIC_UNUSED_PIN,
    .rst = 4,
    .dio = {7, 6, LMIC_UNUSED_PIN},
    .rxtx_rx_active = 0,
    .rssi_cal = 8,              // LBT cal for the Adafruit Feather 32U4 LoRa, in dB
    .spi_freq = 1000000,
};
#elif defined(ARDUINO_CATENA_4551)
// Pin mapping for Murata module / Catena 4551
const lmic_pinmap lmic_pins = {
        .nss = 7,
        .rxtx = 29,
        .rst = 8,
        .dio = { 25,    // DIO0 (IRQ) is D25
                 26,    // DIO1 is D26
                 27,    // DIO2 is D27
               },
        .rxtx_rx_active = 1,
        .rssi_cal = 10,
        .spi_freq = 8000000     // 8MHz
};
#else
# error "Unknown target"
#endif

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;
            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.println(F("Received "));
              Serial.println(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() {
    delay(5000);
    while (! Serial)
        ;
    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();
    LMIC_setClockError(MAX_CLOCK_ERROR * 3 / 100);
    LMIC_setLinkCheckMode(0);
    LMIC_setDrTxpow(DR_SF7,14);
    // LMIC_selectSubBand(1);

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

void loop() {
    os_runloop_once();
}

Read carefully - the last bytes should be 0xD5, 0xB3,0x70 and have a look at your declaration.
APPEUI and DEVEUI should be in little endian format (LSB) by default TTN shows in big endian (MSB)

2 Likes

Ooooh. Right I’ll try that. Sorry very used to just copying and pasting from TTN with the older LMIC library that I didn’t even cotton on.

Hi,

Ok that made some difference. I know get upstream shown on the gateway but TTN doesn’t see it from what I can see. Output on serial is:

3:35:47.910 -> Starting
13:35:47.910 -> Packet queued
13:35:47.910 -> 314337: EV_JOINING
13:35:52.258 -> 586006: EV_TXSTART
13:35:58.565 -> 979729: EV_JOIN_TXCOMPLETE: no JoinAccept
13:37:02.104 -> 4954328: EV_TXSTART
13:37:08.377 -> 5348054: EV_JOIN_TXCOMPLETE: no JoinAccept
13:38:08.548 -> 9112182: EV_TXSTART
13:38:14.853 -> 9505909: EV_JOIN_TXCOMPLETE: no JoinAccept

On the console for the gateway:
Screenshot from 2020-07-16 13-41-19

I have a Lora32U4 and that connects quite happily so pretty sure the gateway works. Any suggestions?

It’s likely that the join is arriving in the second transmission slot so you’ll need a

to set that up so it knows what to listen for.

My entire setup() of a working FeatherMO (OTAA) The ABP section is commented-out …

void setup() {
    Serial.begin(115200);
    pinMode(PIN_LED,OUTPUT);

    digitalWrite(PIN_LED,LOW);
    while(!Serial && millis() < 10000); // Wait 10 seconds ....
    digitalWrite(PIN_LED,LOW);

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

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

    #if defined(CFG_eu868)
    // Set up the channels used by the Things Network, which corresponds
    // to the defaults of most gateways. Without this, only three base
    // channels from the LoRaWAN specification are used, which certainly
    // works, so it is good for debugging, but can overload those
    // frequencies, so be sure to configure the full frequency range of
    // your network here (unless your network autoconfigures them).
    // Setting up channels should happen after LMIC_setSession, as that
    // configures the minimal channel set.
    // NA-US channels 0-71 are configured automatically
    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
    // TTN defines an additional channel at 869.525Mhz using SF9 for class B
    // devices' ping slots. LMIC does not have an easy way to define set this
    // frequency and support for class B is spotty and untested, so this
    // frequency is not configured here.
    #elif defined(CFG_us915)
    // NA-US channels 0-71 are configured automatically
    // but only one group of 8 should (a subband) should be active
    // TTN recommends the second sub band, 1 in a zero based count.
    // https://github.com/TheThingsNetwork/gateway-conf/blob/master/US-global_conf.json
    LMIC_selectSubBand(1);
    #endif

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

    // Start job
    lpp.reset();
    lpp.addDigitalInput(1, 255);
    lpp.addGPS(3,50.239897,3.79, 5);
    do_send(&sendjob);

}

void loop() {
    os_runloop_once();
}
2 Likes

Hi, thanks for the reply. I tried putting that in a couple of places but it didn’t seem to alter anything. I have to admit to not knowing much about what it does though.

Hi weradis,

What library does that use? I am fairly new to this and I don’t recognize the code.

Did you look at the file or just the bit shown on the forum?

For Class A devices, when they uplink (send) they then listen for two specific periods for any reply (downlink). As per the comment, TTN uses a particular setup for the second transmission - this is the one quite likely to have the join response - so your device has to be listening with the correct settings.

It goes in the setup - but I’d look at Willem’s code as it is specifically for the Feather - you can see he has the same line as me.

1 Like

Hi,

Yeah I had a look at the code and tried there and the init as I was kinda grasping at straws.

So I made the assumption to drop the setup into ‘my’ code and just commented out the lpp. Seemed to compile ok but didn’t have an effect. To be honest I might be wasting people’s time with my level of knowledge for this issue.

It would have to be after the init and the reset …

And is only needed once.

You’ve got two code examples to look at - maybe you need a break - get a beer and come back refreshed.

1 Like

What a sensible plan! :slight_smile:

Hi,

Ok plopped it in after the init and the reset. I waited for a few loops of the no JoinAccept and still not connecting. At this point I’ll thank you for all your help. I don’t want to waste anyone’s time. I have multiple different boards winging their way in from China so I’ll probably have more questions in the future.

Thanks again.

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

LMIC_setClockError(MAX_CLOCK_ERROR * 3 / 100);
LMIC_setLinkCheckMode(0);
LMIC.dn2Dr = DR_SF9;
LMIC_setDrTxpow(DR_SF7,14);
//LMIC_selectSubBand(1);

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

Hi,
Willem messaged me and I found I had made a very stupid error when manually converting the big to little endian. I feel quite embarrassed but I wanted to make sure he is thanked.

Again thank you for your time everyone. This has been driving me crazy for days. Before this I had all sorts of issues on Windows and the Com ports refusing to work. Irritated me so much I installed Ubuntu and set it all up again.

No, not for the OTAA Join Accept downlinks; only for regular downlinks.

FYI: TTN Console can do that for you.

1 Like