Problem with uplink payload on TTN/TTS using HT-M02 V2 and Lora32 V3

I am having trouble with the uplink message after receiving the join request. My LoRa32 V3 when i switch on has no issues joining the network as seen in this first screenshot

however, every other subsequent uplink is not received.

on the gateway side it does seem to have no issues, so i’m really lost with what exactly the issue is.

Please help :frowning:

Well… we’d need to see your code on the LoRa32 to be able to comment on it and hopefully find the problem… so, share what you have! (And please format your code using code blocks :slight_smile: )

oh man i have not used code blocks in forever, my code is nicely formatted on Arduino IDE, is the format the same? or did you meant blockquote? just gonna try my luck first

#include “LoRaWan_APP.h”

#include “HT_SSD1306Wire.h”

#include “Arduino.h”

// OLED Display Configuration

#ifdef WIRELESS_STICK_V3

static SSD1306Wire display(0x3c, 500000, SDA_OLED, SCL_OLED, GEOMETRY_64_32, RST_OLED);

#else

static SSD1306Wire display(0x3c, 500000, SDA_OLED, SCL_OLED, GEOMETRY_128_64, RST_OLED);

#endif

// Device Configuration

const String TAG_ID = “MT1”; // Maintenance Tag ID

// OTAA Parameters

uint8_t devEui[] = { 0x3A, 0x43, 0xCA, 0x48, 0x00, 0x00, 0xBC, 0x6A };

uint8_t appEui[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

uint8_t appKey[] = { 0x74, 0xD6, 0x6E, 0x63, 0x45, 0x82, 0x48, 0x27, 0xFE, 0xC5, 0xB7, 0x70, 0xBA, 0x2B, 0x50, 0x45 };

// ABP Parameters (required by library but not used in OTAA mode)

uint8_t nwkSKey[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

uint8_t appSKey[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

uint32_t devAddr = ( uint32_t )0x00000000;

// LoRaWAN Configuration

uint16_t userChannelsMask[6]={ 0x00FF,0x0000,0x0000,0x0000,0x0000,0x0000 };

LoRaMacRegion_t loraWanRegion = LORAMAC_REGION_AS923; // Singapore frequency plan

DeviceClass_t loraWanClass = CLASS_A;

bool overTheAirActivation = true;

bool loraWanAdr = false; // Disable ADR for more stable connection

bool isTxConfirmed = false; // Changed to unconfirmed messages

uint8_t appPort = 85; // Changed to match the port being used in transmissions

uint8_t confirmedNbTrials = 4; // Reduced retries

uint32_t appTxDutyCycle = 30000; // Increased to 30 seconds for better stability

// Panic Button Configuration

const int PRG_BUTTON_PIN = 0; // GPIO0 on Heltec board

unsigned long lastDebounceTime = 0;

unsigned long debounceDelay = 50;

int lastButtonState = HIGH;

int buttonState;

unsigned long lastPanicTime = 0;

const unsigned long PANIC_COOLDOWN = 10000; // 10 second cooldown

bool isPanicMode = false;

unsigned long lastTransmitTime = 0;

// Display Functions

void VextON(void) {

pinMode(Vext, OUTPUT);

digitalWrite(Vext, LOW);

}

void displayStatus(const String& status, const String& detail) {

display.clear();

display.setTextAlignment(TEXT_ALIGN_LEFT);

display.setFont(ArialMT_Plain_16);

display.drawString(0, 0, status);

display.setFont(ArialMT_Plain_10);

display.drawString(0, 20, detail);

display.drawString(0, 35, "Tag ID: " + TAG_ID);

display.display();

}

void downLinkDataHandle(McpsIndication_t *mcpsIndication) {

Serial.printf("Received downlink: %s, RXSIZE %d, PORT %d, DATA: ", mcpsIndication->RxSlot?"RXWIN2":"RXWIN1", mcpsIndication->BufferSize, mcpsIndication->Port);

for(uint8_t i=0; i<mcpsIndication->BufferSize; i++) {

    Serial.printf("%02X", mcpsIndication->Buffer[i]);

}

Serial.println();

}

void joinCallback(void) {

Serial.println("Join success!");

deviceState = DEVICE_STATE_SEND;

}

void prepareTxFrame(uint8_t port, bool isPanic) {

appDataSize = 4;

appData[0] = isPanic ? 0xFF : 0x00;

appData[1] = TAG_ID.charAt(0);

appData[2] = TAG_ID.charAt(1);

appData[3] = TAG_ID.charAt(2);



String msgType = isPanic ? "PANIC ALERT" : "Heartbeat";

Serial.println("\n--- Transmitting Message ---");

Serial.printf("Type: %s\n", msgType.c_str());

Serial.printf("Tag ID: %c%c%c\n", appData[1], appData[2], appData[3]);

Serial.print("Raw Payload (hex): ");

for(uint8_t i = 0; i < appDataSize; i++) {

    Serial.printf("%02X ", appData[i]);

}

Serial.println("\n------------------------");

}

void setup() {

Serial.begin(115200);

while (!Serial);



VextON();

delay(100);

display.init();

display.clear();

display.display();



pinMode(PRG_BUTTON_PIN, INPUT_PULLUP);

buttonState = digitalRead(PRG_BUTTON_PIN);

lastButtonState = buttonState;



// Initialize LoRaWAN with AS923 configuration

Mcu.begin(HELTEC_BOARD, SLOW_CLK_TPYE);

LoRaWAN.init(loraWanClass, loraWanRegion);

LoRaWAN.setDefaultDR(1);  // Changed to DR1 for better range



displayStatus("Joining", "Network...");

deviceState = DEVICE_STATE_JOIN;

}

void loop() {

unsigned long currentTime = millis();

bool flag = false;



if (deviceState == DEVICE_STATE_JOIN && flag == false) {

    Serial.println("Attempting to join network...");

    LoRaWAN.join();

    delay(5000);

   

    if (deviceState != DEVICE_STATE_JOIN) {

        Serial.println("Join successful!");

        displayStatus("Connected", "Network OK");

        delay(1000);

        LoRaWAN.setDefaultDR(1);  // Keep at DR1 for better stability

        deviceState = DEVICE_STATE_SEND;

        lastTransmitTime = currentTime;  // Initialize timing

    } else {

        Serial.println("Join failed, retrying...");

        displayStatus("Joining", "Retrying...");

    }

    flag = true;

}



// Button handling with debounce

int reading = digitalRead(PRG_BUTTON_PIN);

if (reading != lastButtonState) {

    lastDebounceTime = currentTime;

}



if ((currentTime - lastDebounceTime) > debounceDelay) {

    if (reading != buttonState) {

        buttonState = reading;

        if (buttonState == LOW) {

            if (!isPanicMode) {

                isPanicMode = true;

                isTxConfirmed = true;

                confirmedNbTrials = 8;

                displayStatus("PANIC MODE", "Alert Active!");

                prepareTxFrame(appPort, true);

                deviceState = DEVICE_STATE_SEND;

                lastTransmitTime = currentTime - appTxDutyCycle;  // Force immediate send

                lastPanicTime = currentTime;

            }

        }

    }

}

lastButtonState = reading;

if (currentTime - lastTransmitTime >= appTxDutyCycle) {

    switch(deviceState) {

        case DEVICE_STATE_SEND: {

            prepareTxFrame(appPort, isPanicMode);

            LoRaWAN.send();

            Serial.println("Uplink sent");

            lastTransmitTime = currentTime;

            deviceState = DEVICE_STATE_CYCLE;

            break;

        }

        case DEVICE_STATE_CYCLE: {

            LoRaWAN.cycle(appTxDutyCycle);

            deviceState = DEVICE_STATE_SLEEP;

            break;

        }

        case DEVICE_STATE_SLEEP: {

            LoRaWAN.sleep(loraWanClass);

            if (isPanicMode && (currentTime - lastPanicTime >= PANIC_COOLDOWN)) {

                isPanicMode = false;

                isTxConfirmed = false;

                confirmedNbTrials = 4;

                displayStatus("Normal Mode", "Monitoring...");

            }

            deviceState = DEVICE_STATE_SEND;

            break;

        }

        default:

            deviceState = DEVICE_STATE_SEND;

            break;

    }

}

}

Not a perfect code block, but we’ve seen a lot worse frequently! I’d give it a pass.

It appears that you have broken up the original state machine from Heltec’s examples. The frustrating thing is that that isn’t really an option without us even knowing why - the state machine is hidden inside Heltec’s proprietary code. I’d try going back to the original loop() structure from their examples and see if that works - and take it from there in incremental steps to what you hope it should do.

Alternatively, ditch their code and use RadioLib.

And here’s how to format a code block the best way:

```cpp

... insert code here ...

```

So that’s a triple backtick (below Escape key) tagged with the programming language extension, in this case cpp.

The results of breaching the TTN Fair Use Policy are undefined - and in most jurisdictions this would be a breach of federal / national law.

The gateway screen shot was useful but with the corresponding time period on the device console showing the ‘every other uplink’ would get you a bonus upgrade to exceptionally useful.

sorry i don’t really understand the fair use policy, even after reading up on it. as for the law in Singapore this is what the committee has to say about it

i also understand from what i have searched that Chirpstack does not have such fair use policy only that it has to comply with the government limits

Which bit needs clarification?

On The Things Network’s The Things Stack Sandbox a Fair Use Policy applies which limits the uplink airtime to 30 seconds per day (24 hours) per node and the downlink messages to 10 messages per day (24 hours) per node .

Obviously I wouldn’t have known you are in Singapore … that said, getting a committee member to tell you isn’t what I’d call legal advice. FWIW, I’m not aware of any duty cycle limits but then I’m not a lawyer, so YMMV.

That’s because Chirpstack doesn’t provide you with a big set of servers for you to use for free that are shared with other users. So you’d have to setup a CS instance of your own or rent space on one.

I mentioned the FUP as you are losing uplinks on a system where there are restrictions.

The gateway screen shot doesn’t show any uplinks so it’s hard to know what DR/SF your device is operating at so it’s a bit hard to comment further.

some more insight on what might be going on.

The EU has restrictions on how much we can use the ISM band and yet we still lose packets. In a free-for-all radio environment, using LoRaWAN / ISM for a panic button may need some serious testing to establish how reliable it will be.

i decided to try chirpstack again, its successful this time but how can i add the node? the setup seems to be quite different to TTN/TTS

I had a similar issue recently. I admit that I’m very new to all of this so please take my reply with at least a few grains of salt, but I saw basically the same thing.

It seems like the LoRa board is connecting to TTN, but not transmitting data. I ended up needing to apply an uplink payload formatter and found one that finally gave something workable when I started digging on the Heltec site. I used Java script and the following appears to work for the data uplinked from Heltec LoRa 32(v3) using the example sketch “LoRaWAN_TTN” for the same library. Note that the temperature data is divided by 4 since I multiplied by 4 in the arduino sketch to preserve one more decimal place. I wanted to multiply by 100, but this exceeds the allowed values for uint8_t data.

function Decoder(bytes, port) {
  var decoded = {};

  // Decode bytes to int
  var testShort0 = (bytes[0]);
  var testShort1 = (bytes[1] / 4);
  // Decode int 
  decoded.count = testShort0;  
  decoded.temperature = testShort1;


  return decoded;
}