🛠 Uplinks confirmés robustes et duty cycle
Objectives
Ensure reliable LoRaWAN transmissions from the MKR WAN 1310: confirmed uplinks with ACK, respecting the firmware floor of one transmission every 2 minutes (EU duty cycle, not modifiable), RAM queue, backoff on failure, and workaround for the frame counter bug that blocks transmission after ~255 sends (MKRWAN issue #141).
Hardware
- 1 MKR WAN 1310 board + 868 MHz antenna, already joined via OTAA (see the TTN tutorial)
- TTN account to verify ACKs on the server side
Complete code
// Uplink confirme robuste avec file d'attente — MKR WAN 1310
#include <MKRWAN.h>
LoRaModem modem;
String appEui = "0000000000000000";
String appKey = "00000000000000000000000000000000";
const int TAILLE_FILE = 8;
String file[TAILLE_FILE];
int tete = 0, queue = 0, nbFile = 0;
unsigned long dernierEnvoi = 0;
unsigned long totalEnvois = 0;
void setup {
Serial.begin(115200);
while (!Serial);
if (!modem.begin(EU868)) { Serial.println("Module KO"); while (1); }
// Le bug #141 (blocage apres ~255 trames) se contourne en re-joignant
// periodiquement : rebegin + joinOTAA tous les 200 envois.
if (!modem.joinOTAA(appEui, appKey)) { Serial.println("Join KO"); while (1); }
modem.minPollInterval(60);
}
void loop {
// Production : ici un capteur ou un bouton empile des messages
if (millis % 60000 < 100 && nbFile < TAILLE_FILE) {
file[queue] = "T=" + String(analogRead(A0));
queue = (queue + 1) % TAILLE_FILE; nbFile++;
}
// Consommation : 1 message / 2 min maximum (plancher firmware EU)
if (nbFile > 0 && millis - dernierEnvoi >= 125000UL) {
modem.beginPacket;
modem.print(file[tete]);
int err = modem.endPacket(true); // true = uplink_confirme (attend ACK)
if (err > 0) {
tete = (tete + 1) % TAILLE_FILE; nbFile--;
totalEnvois++;
Serial.println("ACK recu");
} else {
Serial.println("Sans ACK : on garde en file, backoff 2 min");
}
dernierEnvoi = millis;
if (totalEnvois > 0 && totalEnvois % 200 == 0) {
modem.begin(EU868); // reinitialise le compteur de trames
modem.joinOTAA(appEui, appKey);
}
}
}
Expected verification
Each confirmed message disappears from the queue and appears as "confirmed" in TTN live data; a message without ACK remains in the queue and is retried in the next cycle. After several hours, transmission no longer blocks thanks to periodic rejoining.
Common errors
- Believing
endPacket(true)is infallible: without an ACK, the function returns an error but does not retransmit on its own — it is up to the sketch to take control. - Filling the gap with
delay(1000)between two sends: the firmware refuses any transmission less than 2 minutes apart, regardless of software configuration. - Forgetting the workaround for bug #141 and seeing the board go "silent" after 255 frames without an error message.
- Ignoring a full queue: beyond 8 messages, decide between overwriting, discarding, or storing in 2 MB flash (see the Eslov + flash tutorial).
- In Class A, downlinks only arrive in the two RX windows following an uplink: an invisible server ACK often means a missed window, not an incorrect key.
Other tutorials for this board
Code examples for this board
join-otaa.inoArduinouplink-confirme.inoArduinodownlink.inoArduinoactivation-abp.inoArduinosuivi-adr.inoArduinoclasse-c.inoArduino