-
Notifications
You must be signed in to change notification settings - Fork 266
Description
Downlink is working, but uplink is not working.
Hardware: Heltec Wireless Stick Lite
Library Version: 7.4.0
`#ifndef LORAWAN_CONFIG_H
#define LORAWAN_CONFIG_H
// LoRaWAN ABP Configuration
// Device Address (4 bytes)
#define LORAWAN_DEV_ADDR 0x08AA8BB8
// Network Session Key (16 bytes)
#define LORAWAN_NWKSKEY {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x64, 0xb7, 0x08, 0xaa, 0x8b, 0xb8
}
// Application Session Key (16 bytes)
#define LORAWAN_APPSKEY {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x64, 0xb7, 0x08, 0xaa, 0x8b, 0xb8
}
// LoRaWAN Settings
#define LORAWAN_USE_ADR true // Enable Adaptive Data Rate
#define LORAWAN_INITIAL_DR 5 // Initial Data Rate (DR5 = SF7BW125)
#define LORAWAN_TX_POWER 14 // TX Power in dBm
#define LORAWAN_UPLINK_INTERVAL 30 // Uplink interval in seconds
#define LORAWAN_CONFIRMED_MSGS true // Use confirmed messages
#define LORAWAN_APP_PORT 2 // Use port 2
// Downlink configuration
#define LORAWAN_DOWNLINK_LISTEN_TIME 2000 // Time to listen for downlinks after uplink (ms)
// Pin definitions for Heltec Wireless Stick Lite
#define LORA_NSS 18
#define LORA_DIO0 26
#define LORA_DIO1 35
#define LORA_RST 14
#define LORA_MOSI 27
#define LORA_MISO 19
#define LORA_SCK 5
#endif`
`#include <Arduino.h>
#include <RadioLib.h>
#include "lorawan_config.h"
// RadioLib LoRaWAN Class definitions
#ifndef RADIOLIB_LORAWAN_CLASS_C
#define RADIOLIB_LORAWAN_CLASS_C 0x02
#endif
// Create the radio module instance using pins from config
SPIClass spi(HSPI);
SX1276 radio = new Module(LORA_NSS, LORA_DIO0, LORA_RST, LORA_DIO1, spi);
// Create the LoRaWAN node instance
LoRaWANNode node(&radio, &EU868);
// ABP credentials from config file
uint32_t devAddr = LORAWAN_DEV_ADDR;
uint8_t nwkSKey[] = LORAWAN_NWKSKEY;
uint8_t appSKey[] = LORAWAN_APPSKEY;
// For LoRaWAN 1.0.x compatibility
uint8_t fNwkSIntKey[] = LORAWAN_NWKSKEY;
uint8_t sNwkSIntKey[] = LORAWAN_NWKSKEY;
uint8_t nwkSEncKey[] = LORAWAN_NWKSKEY;
// LoRaWAN settings from config
bool useADR = LORAWAN_USE_ADR;
uint8_t dataRate = LORAWAN_INITIAL_DR;
uint8_t txPower = LORAWAN_TX_POWER;
uint32_t uplinkInterval = LORAWAN_UPLINK_INTERVAL * 1000;
uint32_t lastUplink = 0;
bool useConfirmedMsgs = LORAWAN_CONFIRMED_MSGS;
uint8_t appPort = LORAWAN_APP_PORT;
// Function declarations
void sendUplink();
void handleDownlink(uint8_t* downlinkPayload, size_t downlinkSize, LoRaWANEvent_t* downlinkEvent);
void printDownlinkMessage(uint8_t* data, size_t length, uint8_t port);
void setup() {
Serial.begin(115200);
delay(1000);
while (!Serial);
Serial.println("LoRaWAN Device Starting...");
// Initialize SPI with custom pins
spi.begin(LORA_SCK, LORA_MISO, LORA_MOSI, LORA_NSS);
// Initialize the radio
int state = radio.begin(868.1, 125.0, 7, 5, RADIOLIB_SX127X_SYNC_WORD, txPower);
if (state != RADIOLIB_ERR_NONE) {
Serial.print("Radio initialization failed: ");
Serial.println(state);
while (true);
}
// Initialize LoRaWAN node in ABP mode
node.beginABP(devAddr, NULL, NULL, nwkSEncKey, appSKey);
// Activate the ABP session
state = node.activateABP();
if (state != RADIOLIB_LORAWAN_NEW_SESSION &&
state != RADIOLIB_LORAWAN_SESSION_RESTORED &&
state != RADIOLIB_ERR_NONE) {
Serial.print("LoRaWAN activation failed: ");
Serial.println(state);
while (true);
}
// Wait for full activation
delay(1000);
if (!node.isActivated()) {
Serial.println("LoRaWAN node not activated");
while (true);
}
// Configure Class C operation
state = node.setClass(RADIOLIB_LORAWAN_CLASS_C);
if (state == RADIOLIB_ERR_NONE) {
// For Class C, we need to start continuous listening
// This might be required for some RadioLib versions
delay(100); // Let the class change settle
} else {
Serial.print("Class C configuration failed: ");
Serial.println(state);
// Continue anyway - the device might still work in Class A mode
}
// Configure LoRaWAN settings to match working Heltec code
if (useADR) {
node.setADR(true);
} else {
node.setDatarate(dataRate);
}
// Additional RadioLib configuration to match Heltec behavior
// Set confirmed message trials (like Heltec confirmedNbTrials = 5)
// Note: This might need to be configured differently in RadioLib
}
void loop() {
if (!node.isActivated()) {
int state = node.activateABP();
if (state != RADIOLIB_ERR_NONE &&
state != RADIOLIB_LORAWAN_NEW_SESSION &&
state != RADIOLIB_LORAWAN_SESSION_RESTORED) {
delay(5000);
return;
}
}
if (millis() - lastUplink > uplinkInterval) {
sendUplink();
lastUplink = millis();
// CRITICAL: Restart Class C listening after uplink
// This ensures continuous downlink reception capability
delay(3000); // Wait for RX1/RX2 windows to complete
int classCRestart = node.setClass(RADIOLIB_LORAWAN_CLASS_C);
// Continue regardless of restart result
}
// Check for Class C downlinks
uint8_t classCPayload[255];
size_t classCLen = 0;
LoRaWANEvent_t classCEvent;
// Try getDownlinkClassC first
int16_t state = node.getDownlinkClassC(classCPayload, &classCLen, &classCEvent);
if (state > 0) {
Serial.print("getDownlinkClassC returned: ");
Serial.print(state);
Serial.print(", length: ");
Serial.println(classCLen);
if (classCLen > 0) {
Serial.println();
Serial.println("=== CLASS C DOWNLINK RECEIVED ===");
handleDownlink(classCPayload, classCLen, &classCEvent);
Serial.println();
}
} else if (state < 0 && state != RADIOLIB_ERR_RX_TIMEOUT) {
// Only print errors that are not timeouts
Serial.print("getDownlinkClassC error: ");
Serial.println(state);
}
// Ignore timeout errors as they are expected
delay(100); // Shorter delay for better downlink responsiveness
}
void sendUplink() {
uint32_t fcntBefore = node.getFCntUp();
if (!node.isActivated()) {
Serial.println("Cannot send uplink - not activated");
return;
}
// Create payload with uptime in seconds only
uint32_t uptimeSeconds = millis() / 1000;
uint8_t payload[4];
// Pack uptime as 4-byte unsigned integer (big-endian)
payload[0] = (uptimeSeconds >> 24) & 0xFF;
payload[1] = (uptimeSeconds >> 16) & 0xFF;
payload[2] = (uptimeSeconds >> 8) & 0xFF;
payload[3] = uptimeSeconds & 0xFF;
// Show payload for debugging
Serial.print("Sending payload: ");
for(int i = 0; i < 4; i++) {
if(payload[i] < 16) Serial.print("0");
Serial.print(payload[i], HEX);
Serial.print(" ");
}
Serial.print("(uptime: ");
Serial.print(uptimeSeconds);
Serial.println(" seconds)");
// Transmission with downlink reception
uint8_t downlinkPayload[255];
size_t downlinkSize = 0;
LoRaWANEvent_t uplinkEvent;
LoRaWANEvent_t downlinkEvent;
// Send uplink and listen for downlink in RX windows
// Use confirmed uplink and port 2 to match Heltec configuration
int state = node.sendReceive(payload, sizeof(payload), appPort,
downlinkPayload, &downlinkSize,
useConfirmedMsgs, &uplinkEvent, &downlinkEvent);
Serial.print("sendReceive returned: ");
Serial.print(state);
Serial.print(", downlink size: ");
Serial.println(downlinkSize);
uint32_t fcntAfter = node.getFCntUp();
if (fcntAfter > fcntBefore) {
Serial.print("✓ Uplink sent - FCnt: ");
Serial.print(fcntAfter);
Serial.print(", Error code: ");
Serial.println(state);
} else {
Serial.print("✗ Uplink failed - Error code: ");
Serial.println(state);
}
// Check if we received a downlink during the transmission
if (state > 0 && downlinkSize > 0) {
handleDownlink(downlinkPayload, downlinkSize, &downlinkEvent);
} else {
Serial.println("No downlink received");
}
}
void handleDownlink(uint8_t* downlinkPayload, size_t downlinkSize, LoRaWANEvent_t* downlinkEvent) {
if (downlinkSize > 0) {
Serial.println("=== DOWNLINK RECEIVED ===");
Serial.print("Port: ");
Serial.println(downlinkEvent->fPort);
Serial.print("Size: ");
Serial.print(downlinkSize);
Serial.println(" bytes");
// Print the downlink message in multiple formats
printDownlinkMessage(downlinkPayload, downlinkSize, downlinkEvent->fPort);
Serial.println("========================");
}
}
void printDownlinkMessage(uint8_t* data, size_t length, uint8_t port) {
// Print as hex bytes
Serial.print("Hex: ");
for (size_t i = 0; i < length; i++) {
if (data[i] < 16) Serial.print("0");
Serial.print(data[i], HEX);
Serial.print(" ");
}
Serial.println();
// Print as ASCII text (if printable characters)
Serial.print("ASCII: \"");
for (size_t i = 0; i < length; i++) {
if (data[i] >= 32 && data[i] <= 126) {
Serial.print((char)data[i]);
} else {
Serial.print(".");
}
}
Serial.println("\"");
// Print as decimal values
Serial.print("Decimal: ");
for (size_t i = 0; i < length; i++) {
Serial.print(data[i]);
if (i < length - 1) Serial.print(", ");
}
Serial.println();
// Print raw byte values for debugging
Serial.print("Raw bytes: [");
for (size_t i = 0; i < length; i++) {
Serial.print("0x");
if (data[i] < 16) Serial.print("0");
Serial.print(data[i], HEX);
if (i < length - 1) Serial.print(", ");
}
Serial.println("]");
}`