Skip to content

Heltec Wireless Stick Lite: Lorawan Uplink not working #206

@deepkap

Description

@deepkap

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("]");

}`

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions