How does LoRa.print(xxx); work

Ladies and Gentleman,
I am looking for some insight into the workings of an Arduino library. My background is in C and this C++ stuff is challenging my diminishing brain cells.
I have successfully implemented a LoRa example sender and receiver code for a pair of TTGO LORA32 433Mhz ESP32 development boards. They work as advertised.
I am now trying to get into my thick skull how things are done in the Arduino world.
The piece of code currently boggling my mind is:
// send packet
LoRa.beginPacket(); This I can follow in the LoRa library
LoRa.print(“Hello…”);
LoRa.print(counter);
LoRa.endPacket(); This I can follow in the LoRa library
I can’t seem to find any code to implement LoRa.print(……) command
How does the string ”Hello…” and the counter value get to the chip data stack?
Is it secret Arduino business or witchcraft?
If somebody could help I would appreciate it.
Regards,
John G

Hi John,

I could probably help you with the main body of your code, but when it comes to most of the libraries I just treat it like its secret Arduino business or witchcraft. Maybe someone else knows the answer to this one though!

It’s confusing isn’t it? Arduino deliberately hides these details to maintain the illusion of magic. As a C programmer, you’re probably not a fan of such witchcraft. Good news is, you can peek behind the curtain if you work at it:

The LoRa class you’re using (I’ve made the safe assumption it is equivalent to this one) is a child of the Stream class. Having child classes is the big feature in C++ that enables object-orientated programming. It means that everything the Stream class has, the LoRaClass adopts. You can see where this happens here:

The : public Stream bit means that LoRaClass inherits all the public members of Stream. Your mysterious print() function happens to be one of those members.

So let’s go looking for the Stream class. Unfortunately this is where Arduino plays hide and seek. As far as I know there’s no public repository of the library source code, but you can find it on your hard drive using the advice here.

I found an unofficial copy here - it might not be the same, but it’s good enough to tell the story.

If you go looking in Stream.h, you’ll still not find the definition of print(). That means you have to go up a level again: Stream is a child of Print, so let’s take a look at Print.h. Sure enough, there it is:

If you look at the corresponding Print.cpp file, you’ll find the definition of print() eventually calls write. Here’s where another C++ trick is used. write is actually defined as a pure virtual function:

virtual size_t write(uint8_t) = 0;

That means it must be defined in a child class. So we go back down from Print to Stream to LoRaClass looking for a definition of write. Sure enough, we find it in LoRa.cpp:

It simply loops through the characters in the string (which has been converted to a buffer by the print/write process), writing each character to the FIFO register.

And there you have it, no witchcraft, just some crafty obfuscation.

3 Likes

Heath,
Many thanks for the effort in running me through the magic.
I would love to say “yep, got it” but it will take a few readings. I do get the streaming concept as it is the same in my current C compiler but we direct the stream to an interface function.
printf(lcd_putc,"Total %lX ",total_flow);

When I migrated myself from assembler to the world of C I thought the disruption to my thinking was to be a one time experience but now its C++ doing it all over again!
It took a few years to become comfortable with C so I may give it a year or to to soak in the C++ magic,
Once again many thanks.

1 Like

Yes, give it plenty of time. I would suggest that the jump from machine language to high level language is about as dramatic as the jump from procedural to object-orientated. Arduino allows you to jump past all that and straight into flashing LEDs and sending LoRa packets, but not without leaving a few details behind.

In case it helps, I wouldn’t try to compare to printf and streaming just yet. LoRa.print in this case really doesn’t rely on any of those concepts (despite the very similar names!). Instead, take a look at the Print, Stream and LoRaClass header files, and then imagine LoRaClass is the superset of all three - it inherits everything from the classes above it. Then you can see that the call to print calls a few of the write functions, which ultimately just loops over the characters in 'Hello...', adding each one to a FIFO. I think once that becomes clear the mysteriousness will start to dissipate and you can rely on your existing C knowledge.

1 Like