Need help with basic OTAA sketch using LMIC Library

good morning,

I’m trying to use a basic standard sketch to send data over OTAA to TTN.
The sketch is comming from the LMIC library.

If I send the data with the confirmed flag, I get the TX_COMPLETE Event and I can reschedule the next windows. If I don’t use it, I’m never getting an answer from the gataway and then not able to reschedule.

The code below.

#include <Arduino.h>
#include <lmic.h>
#include <hal/hal.h>
#include <SPI.h>
#include <SSD1306.h>
#include <hcsr04.h>

// 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]={ };
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]={  } ;
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.
// The key shown here is the semtech default key.
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 = 3600;

// Pin mapping

const lmic_pinmap lmic_pins = {
  .nss = 18,
  .rxtx = LMIC_UNUSED_PIN,
  .rst = 14,
  .dio = {26, 33, 32},
};

#define OLED_I2C_ADDR 0x3C
#define OLED_RESET 16
#define OLED_SDA 21
#define OLED_SCL 22

SSD1306 display (OLED_I2C_ADDR, OLED_SDA, OLED_SCL);

#define TRIG_PIN 25
#define ECHO_PIN 34
UltraSonicDistanceSensor distanceSensor(TRIG_PIN, ECHO_PIN);
char TTN_response[30];

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 {
	    int distance = distanceSensor.measureDistanceCm();
        Serial.println(distance);
	    mydata[0] = highByte(distance);
	    mydata[1] = lowByte(distance);

        display.drawString(0,40,"Distance: " + String(distance)+ " cm");
        display.display();
        LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0);
        Serial.println(F("Packet queued"));
        display.drawString(0, 20, "PACKET QUEUED");
    }
    // Next TX is scheduled after TX_COMPLETE event.
}

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:
            //display.clear();
            display.drawString(0 , 0 ,  "Joined!");
            display.display();
            Serial.println(F("EV_JOINED"));

            // Disable link check validation (automatically enabled
            // during join, but not supported by TTN at this time).
            LMIC_setLinkCheckMode(0);
            break;
        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)"));
            display.clear();
            display.drawString (0, 0, "EV_TXCOMPLETE event!");
            if (LMIC.txrxFlags & TXRX_ACK) {
              Serial.println(F("Received ack"));
              display.drawString (0, 20, "Received ACK.");
            }

            // 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;
         default:
            Serial.println(F("Unknown event"));
            break;
    }
}


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

    #ifdef VCC_ENABLE
    // For Pinoccio Scout boards
    pinMode(VCC_ENABLE, OUTPUT);
    digitalWrite(VCC_ENABLE, HIGH);
    delay(1000);
    #endif
    
    display.init ();
    display.flipScreenVertically ();
    display.setFont (ArialMT_Plain_10);

    display.setTextAlignment (TEXT_ALIGN_LEFT);

    display.drawString (0, 0, "Starting....");
    display.display ();

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

}

I can’t find what I did wrong.
Thanks in advance for you help

Patrice

EV_TXCOMPLETE is an internal event in the LMIC library itself. AFAIK, you get this if the entire send-receive cycle is completed, regardless of any acknowledgement from the gateway. So you should also get this event when you send without ack. Perhaps if something is really wrong with your node, like a problem in the wiring to the LoRa chip, you might not get this event.

To diagnose problems like these, it helps enormously if you can get a view from the gateway side, see what (and if) it’s actually receiving.

Without ack

image

With ack

image

6064: EV_JOINING
523968: EV_JOINED
592900: EV_TXCOMPLETE (includes waiting for RX windows)
592987: Received ack

The difference between with and without is this EV_TXCOMPLETE event (and the Received acl)
I’m using a TTGO Lora32 V2 and PlatformIO.

I found the problem. With a TTGO Lora board, pin 33 and dio101 need to be connected. When done, it works like a charm

For anyone else that come across this thread, the minimum connections are DIO0 and DIO1 - so yes, pin 33 is definitely required. The DIO2 (pin 32 in this case) has use for more advanced cases.

Thanks. Is there any documentation on this?

On DIO2? It’s things like the Channel Activity Detect - all detailed in the data sheet for the relevant radio chipset. But for LoRaWAN devices, it’s not relevant.

ESP32 boards and required pin mappings are already clearly documented on the forum here:
Big ESP32 + SX127x topic part 3
Searching for ESP32 or ESP32 and dio would have brought you there…

Technical details about the SX1276 and its DIO ports can be found in the Semtech SX1276 datasheet.

DIO0 and DIO1 are required for LoRa modulation, DIO2 is used for FSK modulation which is not used for TTN.