Test transmisji LoRaWAN (bez czujnika)
Uproszczony test weryfikujący samo połączenie LoRaWAN: XIAO ESP32S3 → Grove Wio-E5 → bramka → TTN. Wysyłany jest licznik pakietów — żadnych czujników nie potrzeba.
Sprzęt
| Urządzenie | Rola |
|---|---|
| XIAO ESP32S3 | mikrokontroler / LoRaWAN node |
| Grove – Wio-E5 (LoRa-E5) | moduł LoRa (AT commands przez UART) |
| SenseCAP M2 Multi-Platform Gateway (SX1302, EU868) | bramka LoRaWAN |
| Grove Shield for XIAO | rozszerzenie złączy Grove na XIAO |
Połączenie
Grove Wio-E5 wpinasz do portu UART na Grove Shield for XIAO.
XIAO ESP32S3 (przez Grove Shield for XIAO)
└─ [UART Grove port] ──► Grove Wio-E5
D6 / GPIO43 (TX) → E5 RX
D7 / GPIO44 (RX) → E5 TX
Konfiguracja bramki i TTN
Zarejestruj bramkę SenseCAP M2 (zobacz Bramka) na TTN, a następnie zarejestruj end-node OTAA w tej samej aplikacji TTN. Potrzebujesz: DevEUI, AppEUI, AppKey.
Kod
// UART do Grove Wio-E5
// TX = GPIO43 (D6), RX = GPIO44 (D7)
#define E5_SERIAL Serial1
#define E5_RX_PIN 44
#define E5_TX_PIN 43
// Klucze TTN — uzupełnij
const char* DEV_EUI = "XXXXXXXXXXXXXXXX";
const char* APP_EUI = "0000000000000000";
const char* APP_KEY = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
// Interwał wysyłania — minimum 60 s dla EU868 (duty cycle 1%)
const uint32_t SEND_INTERVAL_MS = 60000;
static uint32_t packetCount = 0;
// -------------------------------------------------------
bool sendAT(const char* cmd, const char* expected, uint32_t timeout_ms = 4000) {
while (E5_SERIAL.available()) E5_SERIAL.read();
E5_SERIAL.println(cmd);
String resp = "";
uint32_t t0 = millis();
while (millis() - t0 < timeout_ms) {
while (E5_SERIAL.available()) resp += (char)E5_SERIAL.read();
if (resp.indexOf(expected) >= 0) {
Serial.print("[OK] "); Serial.println(resp);
return true;
}
}
Serial.print("[FAIL] "); Serial.print(cmd);
Serial.print(" → "); Serial.println(resp);
return false;
}
bool joinOTAA() {
char buf[80];
sendAT("AT+RESET", "+RESET:", 3000);
delay(2000);
if (!sendAT("AT+MODE=LWOTAA", "+MODE: LWOTAA")) return false;
if (!sendAT("AT+DR=EU868", "+DR: EU868")) return false;
if (!sendAT("AT+CH=NUM,0-7", "+CH: NUM")) return false;
if (!sendAT("AT+CLASS=A", "+CLASS: A")) return false;
snprintf(buf, sizeof(buf), "AT+ID=DevEui,\"%s\"", DEV_EUI);
if (!sendAT(buf, "+ID: DevEui")) return false;
snprintf(buf, sizeof(buf), "AT+ID=AppEui,\"%s\"", APP_EUI);
if (!sendAT(buf, "+ID: AppEui")) return false;
snprintf(buf, sizeof(buf), "AT+KEY=AppKey,\"%s\"", APP_KEY);
if (!sendAT(buf, "+KEY: APPKEY")) return false;
Serial.println("Łączenie z siecią LoRaWAN (OTAA)...");
return sendAT("AT+JOIN", "+JOIN: Network joined", 30000);
}
void sendPayload(const uint8_t* data, uint8_t len) {
char cmd[len * 2 + 20];
strcpy(cmd, "AT+MSGHEX=\"");
for (uint8_t i = 0; i < len; i++) sprintf(cmd + strlen(cmd), "%02X", data[i]);
strcat(cmd, "\"");
sendAT(cmd, "+MSGHEX:", 10000);
}
// -------------------------------------------------------
void setup() {
Serial.begin(115200);
delay(1000);
E5_SERIAL.begin(9600, SERIAL_8N1, E5_RX_PIN, E5_TX_PIN);
delay(500);
if (!joinOTAA()) {
Serial.println("JOIN nieudany! Sprawdź: klucze TTN, zasięg bramki, pasmo EU868.");
while (1) delay(5000);
}
Serial.println("Połączono z LoRaWAN!");
}
void loop() {
packetCount++;
// Payload: 4 bajty — licznik pakietów (uint32, big-endian)
uint8_t payload[4];
payload[0] = (packetCount >> 24) & 0xFF;
payload[1] = (packetCount >> 16) & 0xFF;
payload[2] = (packetCount >> 8) & 0xFF;
payload[3] = packetCount & 0xFF;
Serial.printf("Wysyłam pakiet #%lu\n", (unsigned long)packetCount);
sendPayload(payload, sizeof(payload));
delay(SEND_INTERVAL_MS);
}
Dekoder payloadu na TTN
- TTN Console → aplikacja → Payload formatters → Uplink.
- Wybierz Custom Javascript formatter, wklej:
function decodeUplink(input) {
var b = input.bytes;
return {
data: {
counter: (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]
},
warnings: [],
errors: []
};
}
Weryfikacja
Serial Monitor (115200 baud):
[OK] +MODE: LWOTAA
[OK] +DR: EU868
...
Łączenie z siecią LoRaWAN (OTAA)...
[OK] +JOIN: Network joined
Połączono z LoRaWAN!
Wysyłam pakiet #1
[OK] +MSGHEX: Done
Wysyłam pakiet #2
...
W TTN Console → aplikacja → Live data każdy odebrany uplink powinien pokazać counter: 1, counter: 2 itd.
Problemy z JOIN
- Bramka musi widnieć jako Connected w TTN Console.
- Klucze muszą być identyczne w kodzie i na TTN.
- Przy
+JOIN: Join failedpoczekaj minutę przed kolejną próbą (EU868 duty cycle).