Summer Project Part 5: When is a function not a function?
Another post! Looks like I'm on a roll in this series :P
In the last post, I looked at the box I designed that was ready for 3D printing. That process has now been completed, and I'm now in possession of an (almost) luminous orange and pink box that could almost glow in the dark.......
I also looked at the libraries that I'll be using and how to manage the (rather limited) amount of memory available in the AVR microprocessor.
Since last time, I've somehow managed to shave a further 6% program space off (though I'm not sure how I've done it), so most recently I've been implementing 2 additional features:
- An additional layer of AES encryption, to prevent The Things Network for having access to the decrypted data
- GPS delta checking (as I'm calling it), to avoid sending multiple messages when the device hasn't moved
After all was said and done, I'm now at 97% program space and 47% global variable RAM usage.
To implement the additional AES encryption layer, I abused LMiC's IDEETRON AES-128 (ECB mode) implementation, which is stored in src/aes/ideetron/AES-128_V10.cpp
.
It's worth noting here that if you're doing crypto yourself, it's seriously not recommended that you use ECB mode. Please don't. The only reason that I used it here is because I already had an implementation to hand that was being compiled into my program, I didn't have the program space to add another one, and my messages all start with a random 32-bit unsigned integer that will provide a measure of protection against collision attacks and other nastiness.
Specifically, it's the method with this signature:
void lmic_aes_encrypt(unsigned char *Data, unsigned char *Key);
Since this is an internal LMiC function declared in a .cpp source file with no obvious header file twin, I needed to declare the prototype in my source code as above - as the method will only be discovered by the compiler when linking the object files together (see this page for more information about the C++ compilation process. While it's for regular Linux executable binaries, it still applies here since the Arduino toolchain spits out a very similar binary that's uploaded to the microprocessor via a programmer).
However, once I'd sorted out all the typing issues, I slammed into this error:
/tmp/ccOLIbBm.ltrans0.ltrans.o: In function `transmit_send':
sketch/transmission.cpp:89: undefined reference to `lmic_aes_encrypt(unsigned char*, unsigned char*)'
collect2: error: ld returned 1 exit status
Very strange. What's going on here? I declared that method via a prototype, didn't I?
Of course, it's not quite that simple. The thing is, the file I mentioned above isn't the first place that a prototype for that method is defined in LMiC. It's actually in other.c
, line 35 as a C function. Since C and C++ (for all their similarities) are decidedly different, apparently to call a C function in C++ code you need to declare the function prototype as extern "C"
, like this:
extern "C" void lmic_aes_encrypt(unsigned char *Data, unsigned char *Key);
This cleaned the error right up. Turns out that even if a function body is defined in C++, what matters is where the original prototype is declared.
I'm hoping to release the source code, but I need to have a discussion with my supervisor about that at the end of the project.
Found this interesting? Come across some equally nasty bugs? Comment below!