Characters arrive as gibberish when sent over LoRa

I'm trying to send NMEA 0183 sentences through LoRa. Every dinstict NMEA message looks like several char arrays (sentences) followed by '\n':

$WIMWV,341.9,T,2.0,N,A*28
$WIMWD,341.9,T,341.9,M,2.0,N,1.0,M*59
$WIMWV,351.0,R,13.0,M,A*15

When I send this data over LoRa they arrive as gibberish like this:

$W100⸮}⸮Oew⸮⸮f⸮O⸮_⸮c⸮_3k⸮'u7⸮⸮g⸮w=⸮⸮⸮q⸮Hھ⸮=⸮S ߷⸮⸮+z⸮z⸮⸮s⸮⸮⸮⸮=7⸮⸮l⸮⸮⸮⸮c̽⸮⸮z⸮⸮⸮2⸮⸮[⸮Ѭ1:⸮⸮⸮⸮⸮⸮⸮⸮]⸮⸮⸮⸮⸮⸮o⸮⸮
{
$W100⸮}⸮Oew⸮⸮f⸮O⸮_⸮c⸮_3k⸮'u7⸮⸮g⸮w=⸮⸮⸮q⸮Hھ⸮=⸮S ߷⸮⸮+z⸮z⸮⸮s⸮⸮⸮⸮=7⸮⸮l⸮⸮⸮⸮c̽⸮⸮z⸮⸮⸮2⸮⸮[⸮Ѭ1:⸮⸮⸮⸮⸮⸮⸮⸮]⸮⸮⸮⸮⸮⸮o⸮⸮
{
$W100⸮}⸮Oew⸮⸮f⸮O⸮_⸮c⸮_3k⸮'u7⸮⸮g⸮w=⸮⸮⸮q⸮Hھ⸮=⸮S ߷⸮⸮+z⸮z⸮⸮s⸮⸮⸮⸮=7⸮⸮l⸮⸮⸮⸮c̽⸮⸮z⸮⸮⸮2⸮⸮[⸮Ѭ1:⸮⸮⸮⸮⸮⸮⸮⸮]⸮⸮⸮⸮⸮⸮o⸮⸮

For parsing the NMEA sentences on the transmitter node (Arduino Yun) I use this library

For transmitting/receiving the data over LoRa I use this library

The first node takes the data through TCP and transmit them through LoRa, this is its code:

void setup() {
  Bridge.begin();       
  Serial.begin(9600);
  server.noListenOnLocalhost();
  server.begin();
  if (!rf95.init())
    Serial.println("LoRa object failed to initialize");     
  rf95.setTxPower(20, false);

 void loop() {

  //Accept clients that trying to connect
  client = server.accept();
  if (client) {
    Serial.println("Client connected");
  }

  // Send TCP data through LoRa

  if(client.available()) {
    char c = client.read();
    if (nmea.update(c)) {
      Serial.write(nmea.getSentence());
      Serial.println();
      rf95.send(nmea.getSentence(), sizeof(nmea.getSentence()));
      rf95.waitPacketSent();
    }
  }
    
 }

On the receiving node I have this code:

void setup() {
  Bridge.begin();       
  Serial.begin(9600);
  if (!rf95.init())
    Serial.println("LoRa object failed to initialize");     
  rf95.setTxPower(20, false);
  pinMode(13, OUTPUT);
}

void loop() {
    if (rf95.available()) {
      uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];
      uint8_t len = sizeof(buf);
      if (rf95.recv(buf, &len)) {
                Serial.println((char *)buf);

      }    
    }
}

Note 1: When those data arrive on the receiving node, then they will be sent through TCP to a computer program. It's not critical to be human readable on the Serial monitor, but to arrive exactly as they are on the application.

Note 2: the "server" object is of BridgeServer class for Arduino Yun.

what's the speed/baud settings for your Serial monitor ? (where you see the gibberish text)

Post ALL the code, after making sure that it compiles without errors.

J-M-L:
what’s the speed/baud settings for your Serial monitor ? (where you see the gibberish text)

They are both at 9600.

jremington:
Post ALL the code, after making sure that it compiles without errors.

This is the transmitter node:

#include <NMEA0183.h>
#include <Process.h>
#include <Ethernet.h>
#include <Bridge.h>
#include <BridgeServer.h>
#include <BridgeClient.h>
#include <SPI.h>
#include <RH_RF95.h>

IPAddress ip(172, 31, 3, 100);
BridgeClient client;
BridgeServer server;

RH_RF95 rf95;
NMEA0183 nmea;


void setup() {
 Bridge.begin();       
 Serial.begin(9600);
 server.noListenOnLocalhost();
 server.begin();
 if (!rf95.init())
   Serial.println("LoRa object failed to initialize");     
 rf95.setTxPower(20, false);
}

void loop() {
 
 //Accept clients that trying to connect
 client = server.accept();
 if (client) {
   Serial.println("Client connected");
 }

 // Serial print the TCP incoming data
 if(client.available()) {
   char c = client.read();
   if (nmea.update(c)) {
     Serial.write(nmea.getSentence());
     Serial.println();
     rf95.send((char *)nmea.getSentence(), sizeof(nmea.getSentence()));
     rf95.waitPacketSent();
   }
 }  

}

This is the receiver node:

#include <Process.h>
#include <Ethernet.h>
#include <Bridge.h>
#include <BridgeServer.h>
#include <BridgeClient.h>
#include <SPI.h>
#include <RH_RF95.h>

IPAddress ip(192, 168, 1, 15);
BridgeClient client;

RH_RF95 rf95;

void setup() {
 Bridge.begin();       
 Serial.begin(9600);
 if (!rf95.init())
   Serial.println("LoRa object failed to initialize");     
 rf95.setTxPower(20, false);
 pinMode(13, OUTPUT);
}

void loop() {
   if (rf95.available()) {
     uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];
     uint8_t len = sizeof(buf);
     if (rf95.recv(buf, &len)) {
         Serial.println((char *)buf);

     }    
   }
}

Please edit your last post to add code tags.

Probably a missing null-termination:

void loop() {
   if (rf95.available()) {
     uint8_t len = RH_RF95_MAX_MESSAGE_LEN;
     uint8_t buf[len + 1]; //Allow space for null termination
     if (rf95.recv(buf, &len)) {
         buf[len] = 0; //Set null termination
         Serial.println((char *)buf);
     }    
   }
}

EDIT: This does not look right either:

rf95.send(nmea.getSentence(), sizeof(nmea.getSentence()));

strlen should be used instead of sizeof.

Did the the examples in the library your using work correctly ?

Who wrote the code you posted ?

I note you changed the title of you post to;

“Characters arrive as gibberish when sent over LoRa”

So did you try the same basic code using RF methods other than LoRa and the characters arrived correctly ?

+1 for reply #5. There appears to be more than one error.

Danois90:
Probably a missing null-termination:

void loop() {

if (rf95.available()) {
    uint8_t len = RH_RF95_MAX_MESSAGE_LEN;
    uint8_t buf[len + 1]; //Allow space for null termination
    if (rf95.recv(buf, &len)) {
        buf[len] = 0; //Set null termination
        Serial.println((char *)buf);
    }   
  }
}




EDIT: This does not look right either:



rf95.send(nmea.getSentence(), sizeof(nmea.getSentence()));




strlen should be used instead of sizeof.

Reply #5 solves my problem.
Missing null terminator got me reading garbage from all over the place. It’s clear.

About using strlen() instead of sizeof(), as far as I can understand, strlen() counts characters until it meets a null terminator and sizeof() counts how many blocks of memory are allocated for the character array
I still don’t understand though why strlen() has to be used in this situation instead of sizeof() as they both return an int.

Either way, thank you for the precious help!

I still don't understand though why strlen() has to be used in this situation instead of sizeof()

It depends, of course!

strlen() tells you the number of bytes of useful C-string data in the buffer (minus the zero terminating byte).

sizeof() tells you what you already know -- how much space was allocated for the send buffer, much of which may be filled with garbage.

However, if you send binary data, use of sizeof() is required, because strlen() works only with C-strings.