0

I'm trying to send NMEA 0183 sentences through LoRa. Every dinstict NMEA message looks like several char arrays (sentences) followed by '\n'. In the below picture we see one NMEA distinct message:

enter image description here

This message arrives in the Arduino input buffer every 1 second with differentiated values (numbers) and it has to be sent over LoRa as fast as possible. In order to send it over LoRa I need to read all bytes on the input buffer and assign them to an array and send them as an array, not one by one. I know that Serial.read() reads only one byte, and that's the problem. How can I read all bytes and send them as a whole?

This is my code:

  if (Serial.available()) {
     c = Serial.read();
     rf95.send((char *)c, sizeof(c));
     rf95.waitPacketSent();

I use RadioHead library, and the definition of rf.95.send is: RH_RF95::send (const uint8_t * data, uint8_t len)

c is of char type.

4
  • There is no such thing as "all the bytes ... as a whole" - serial is only individual bytes. Anything more than that is merely a wrapper around reading single bytes.
    – Majenko
    Commented Aug 21, 2020 at 12:22
  • What I mean with "whole", is an array Commented Aug 21, 2020 at 12:31
  • @Majenko, for sending over LoRa it matters? is there some overhead? a frame/ envelope?
    – Juraj
    Commented Aug 21, 2020 at 13:09
  • The reception of serial is often the slowest portion. Anything else you do is pretty much insignificant.
    – Majenko
    Commented Aug 21, 2020 at 13:31

3 Answers 3

1

@DataFiddler suggested something like readBytesUntil. In fact, you can just use readBytesUntil. It returns either when timeout happens or when you reach the specified terminator. It consumes, but does not include the terminator. The timeout defaults to 1000 milliseconds, and can be modified with the setTimeout() method.

One downside is that you can't differentiate timeout, full buffer, and terminator reached. This shouldn't be a problem except for serial transmission errors. If your data is coming in at the NMEA standard 4800 baud, you could reasonably change the timeout to 5 milliseconds and still work.


One thing that might bite you, is the line:

     rf95.send((char *)c, sizeof(c));

It is a mistake to cast the first parameter. The error you got indicated you should write:

     rf95.send(&c, sizeof(c));

But the sizeof is also wrong. The return type of Serial::read() is int, in part because there are 257 possible values. If you declared c as an int, its size would be 2, not 1.

In any case, if you are using readBytesUntil, you pass the same buffer you passed it to rf95.send(), and the return value from the first as the second parameter of the second.

2
  • You are right about "sizeof()", strlen() must be used Commented Aug 22, 2020 at 0:41
  • NO. strlen() is NOT appropriate. You already know the number of characters you have read. Just use that. In the original example, there was 1 character, so the constant 1 is correct. In using readBytesUntil, That returns the number of bytes read, which is what should be used.
    – David G.
    Commented Aug 22, 2020 at 1:55
0

All more complex Serial reading methods are based on read().

Luckily there's a terminating character '\n' in your case, so you can implement something similar to readBytesUntil by yourself, if you want to.

1
  • I suppose I could loop through all data and add them to an array and avoid all '\n' characters except from the las one? Commented Aug 21, 2020 at 16:08
0

I was missing a null termination character. That got me reading garbage from all over the buffer. Adding a '0' at the end of the buffer's length is mandatory. Plus, as David G. noticed, sizeof() function is incorrect. strlen() must be used.

5
  • Just use a counted string, not a null terminated string. Never do strlen() as you know the length already.
    – David G.
    Commented Aug 22, 2020 at 1:56
  • The length is not fixed. Sentences can be more or less depending on what sensors must be used every certain moment, and that can happen in run-time. Commented Aug 22, 2020 at 9:56
  • 1
    The length is not fixed, but the length is KNOWN (after you read it). In fact, you need to know the length to insert the null. Why insert the null at the known length, and then use strlen to find the length?
    – David G.
    Commented Aug 22, 2020 at 11:08
  • The transmitter node doesn't read the length of the transmitting char array until this function: rf95.send((char *)nmea.getSentence(), strlen(nmea.getSentence())); The receiver node knows the length after accepting it and reading it but there is no strlen() function in there. Commented Aug 23, 2020 at 10:11
  • Well, what the call probably should be is rf95.send(nmea.getSentence(), nmea.getSentenceLength()); First off: don't cast. If you seem to need to cast, you've done something wrong. Figure out what. Secondly: your nmea wrapper class should know the length of the data it has, so ask it directly.
    – David G.
    Commented Aug 23, 2020 at 22:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.