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)

3 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.

1 Like

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.

2 Likes

Even though the issue here seems to be rather old, I’m running into it anew.

My setup:

  • Feather M0 LoRa with the D-stepping silicon (so there should be no problem with SPI timing)
  • Latest Adafruit SAMD BSP, v. 1.6.1
  • MCCI LMIC 3.2.0

Pinmap:

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
}

I used the example application ten-otaa-feather and adjusted the band for eu-868. I registered an application and my device at the TTN console and correctly entered APPEUI, DEVEUI and APPKEY, respecting endianness. I adjusted the clock error tolerance and set the RX2 listening mode to SF9 - all recommendations from various threads here.

Yet, OTAA joining repeatably fails on the first attempts, even though I can see the packets arriving in the TTN console. That is, even the first join packet at SF7, 125kHz arrives at the TTN, but the reply seems to get lost. In the TTN console, I do see how the radio increases the spreading factor with every other attempt. After about 10 trials, at SF10 or SF11, a join finally succeeds. However, by then, four or five gateways are picking up the signal (I’m in central Berlin) - so, coverage is definitively not the problem.

I found similar problems in various threads here, dating back to 2016. By now, boards, libraries and the TTN have evolved, so I suspect things should work fine. What am I doing wrong? How can I pinpoint the problem?

Thanks very much for your help!

I can confirm that this problem persists, even with severe bugfixes of MCCI LMIC 3.2.0. I did deep analysis of timing, RF radio etc., with the conclusion that the join accept message is for some reason not sent by the gateway. My assumption is that this is a network congestion problem, maybe caused by duty cycle limits of EU868 band or by some bottleneck in TTNv2 backend. Could not yet test with v3. I gave up my efforts trying to fix this with TTN.

1 Like

See Using LMIC_setClockError on MCCI LMIC for notes about LMIC_ENABLE_arbitrary_clock_error and differences between SFs.

To debug you’ll need to keep an eye on everything: SF, maybe even on the frequencies that work/don’t work, and which of the gateway(s) that received the Join Request is selected to transmit the Join Accept and which work and which don’t. (But you’ll need access to the gateways to see that.)

For OTAA, that’s an error. It should only be configured for ABP in EU868. It will probably only affect joins on SF12 though, if anything at all.

Thanks very much for your suggestions!

I removed the RX listening mode and tried the LMIC_ENABLE_arbitrary_clock_error flag, but to no avail. Going through the different SFs for the JOIN request via LMIC_setDrTxpow did not seem to work, either. The TTN Console always reported SF7 for the first JOIN message it received.

Unfortunately, I do not have access to my own gateway, so I cannot see if the network always transmits a JOIN_ACCEPT message, and via which gateway of 4-5 I am in range of.

I’m also having trouble with two additional aspects. Maybe someone here can give me a link to additional resources?