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.
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.
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.