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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions