Confused about how to code for downlinks

Hi,

I have the sketch below sending a message a bit over every 2 minutes to try and get a downlink.

This device is registered on TTNv3. The uplinks show up in the TTNv3 console.

If I schedule a confirmed downlink, the console says the downlink is sent after the next uplink, and never sent again. Which implies the TTNv3 server got a confirmation of the downlink.

But I never see the downlink data in the serial console.

TTNv3 uses a 5 second rx1 delay. I think this means the rx1 window opens 5 seconds after the uplink is done and lasts for 1 second. Then a second later rx2 opens for a second.

The code below is constantly calling model.available() - is that right?

Where is rx1 and rx2 handled - in the modem firmware before endPacket() returns or would I expect the downlink data to arrive ~5 seconds after the endPacket call returns?

I think downlinks in general are working - they are required for the join and for other control purposes.

I have been able to get downlinks from LMIC on a Feather.

But I can't figure out how I'm supposed to get the application level ones using the MKRWAN library and an MKR1300 board.

I upgraded the firmware to 1.2.3 today, and the library & toolchain is up to date.

Regards, David.


LoRaModem modem;

String appEui = "0000000000000000";
String appKey = "...";

unsigned long now;
unsigned long nextPacketTime;

void setup() {

  // put your setup code here, to run once:
  Serial.begin(115200);
  while (!Serial);

  if (!modem.begin(AS923)) {
    Serial.println("Failed to start module");
    while (1) {}
  };

  Serial.print("LoRaWAN module firmware version: ");
  Serial.println(modem.version());
  Serial.print("Device EUI is: ");
  Serial.println(modem.deviceEUI());

  boolean connected = modem.joinOTAA(appEui, appKey);
  if (!connected) {
    Serial.println("Could not join.");
    while (1) {}
  }

  Serial.println("Join ok.");

  modem.setPort(3);

  nextPacketTime = millis() + 2500;
}

uint32_t counter = 0;
uint8_t data[4] = { 0xFE, 0xFE, 0xFE, 0xFE };
int rcv[64];
int rc;

void loop() {
  now = millis();
  if (now > nextPacketTime) {
    // Send the counter in big-endian format.
    uint8_t *ptr = (uint8_t *)&counter;
    data[0] = ptr[3];
    data[1] = ptr[2];
    data[2] = ptr[1];
    data[3] = ptr[0];
    
    modem.beginPacket();
    rc = modem.write(data, 4);
    rc = modem.endPacket();
    Serial.print("endPacket returned ");  Serial.println(rc);

    nextPacketTime = now + (1000 * 60 * 2) + 3000;
    counter++;
  }

  if (!modem.available()) {
    return;
  }

  int i = 0;
  while (modem.available() && i < 64) {
    rcv[i++] = modem.read();
  }
  
  Serial.print("Received: ");
  for (unsigned int j = 0; j < i; j++) {
    Serial.print((rcv[j] & 0xF0) >> 4, HEX);
    Serial.print(rcv[j] & 0x0F, HEX);
    Serial.print(" ");
  }
  Serial.println();
}```