Arduino C legacy, libraries and codebender

Arduino did an excellent job hiding the inherent complexity of compiling code for embedded microcontrollers (or lately microprocessors). This allowed anyone to easily program their boards without needing to understand how code and libraries get compiled to the blob of machine code that gets its way into your board to do ‘stuff’. Heck, you don't even need to know what a microcontroller is anymore!

The downside is that eventually, as you realize more and more complex ideas or try to squeeze more functionality into your code, you eventually hit cryptic compiler errors or unexpected behavior that surfaces from the archaic C/C++ world living underneath. In this post I'll try to give you some insight into what happens under the hood, without bothering you with any of the details computer science students will get to learn in their classes.

Brains! I want brains!

As you probably already know the brains of each Arduino board is a microcontroller. We like to see it as 'brains', but in reality it's really... dumb. As the common saying goes, if we were to ask a microcontroller to 'jump off a cliff', it would. And not at all because it's loyal or just plainly submissive; it really doesn't know how to do anything other than what it is programmed for. A microcontroller without a program simply doesn't do anything. The program that gets into the microcontroller manipulates electric signals in such a way that it controls the flow and results of the next operation, each one paving the way for some useful function or an output that can be understood by humans.

While this introduction is quite generic, there's no reason to go into the details. All you need to grasp is that a microcontroller only responds to (and with) electric signals; the zeros and ones that (you already know) are called bits. We group bits into bytes that we represent by numbers and letters to create machine code that the microcontroller understands, but I bet you, doesn't make any sense to us. Just look at the next perfectly valid program for a microcontroller:

:100000000C9461000C947E000C947E000C947E0095
:100010000C947E000C947E000C947E000C947E0068
:100020000C947E000C947E000C947E000C947E0058
:100030000C947E000C947E000C947E000C947E0048
:100040000C94A0000C947E000C947E000C947E0016
:100050000C947E000C947E000C947E000C947E0028
:100060000C947E000C947E00000000002400270009
:100070002A0000000000250028002B0000000000DE
:1000800023002600290004040404040404040202DA
:100090000202020203030303030301020408102007
:1000A0004080010204081020010204081020000012
:1000B0000007000201000003040600000000000029
:1000C000000011241FBECFEFD8E0DEBFCDBF11E08E
:1000D000A0E0B1E0E0E4F4E002C005900D92A230AF
:1000E000B107D9F711E0A2E0B1E001C01D92AB3039
:1000F000B107E1F70E940F020C9480000C940000FD
:10010000F8940C941E028091000161E00E94BB01F2
:1001100068EE73E080E090E00E94E80080910001CA
:1001200060E00E94BB0168EE73E080E090E00E9416
:10013000E80008958091000161E00E947C0108952B
:100140001F920F920FB60F9211242F933F938F930C
:100150009F93AF93BF938091060190910701A09167
:100160000801B091090130910A010196A11DB11D4C
:10017000232F2D5F2D3720F02D570196A11DB11D86
:1001800020930A018093060190930701A093080130
:10019000B09309018091020190910301A0910401A3
:1001A000B09105010196A11DB11D809302019093AC
:1001B0000301A0930401B0930501BF91AF919F91FA
:1001C0008F913F912F910F900FBE0F901F90189518
:1001D0009B01AC017FB7F8948091020190910301DB
:1001E000A0910401B091050166B5A89B05C06F3FC1
:1001F00019F00196A11DB11D7FBFBA2FA92F982F0D
:100200008827860F911DA11DB11D62E0880F991FDF
:10021000AA1FBB1F6A95D1F7BC012DC0FFB7F89488
:100220008091020190910301A0910401B091050118
:10023000E6B5A89B05C0EF3F19F00196A11DB11DC1
:10024000FFBFBA2FA92F982F88278E0F911DA11DB0
:10025000B11DE2E0880F991FAA1FBB1FEA95D1F7D5
:10026000861B970B885E9340C8F221503040404077
:10027000504068517C4F211531054105510571F6FB
:100280000895789484B5826084BD84B5816084BD0E
:1002900085B5826085BD85B5816085BDEEE6F0E0FF
:1002A000808181608083E1E8F0E01082808182605B
:1002B0008083808181608083E0E8F0E080818160DC
:1002C0008083E1EBF0E0808184608083E0EBF0E00C
:1002D000808181608083EAE7F0E0808184608083B0
:1002E000808182608083808181608083808180685A
:1002F00080831092C1000895CF93DF93482F50E080
:10030000CA0186569F4FFC0134914A575F4FFA014C
:100310008491882369F190E0880F991FFC01E859C6
:10032000FF4FA591B491FC01EE58FF4FC591D491B8
:10033000662351F42FB7F8948C91932F90958923CD
:100340008C93888189230BC0623061F42FB7F894B5
:100350008C91932F909589238C938881832B88830C
:100360002FBF06C09FB7F8948C91832B8C939FBFAF
:10037000DF91CF910895482F50E0CA0182559F4FD9
:10038000FC012491CA0186569F4FFC0194914A5763
:100390005F4FFA013491332309F440C0222351F115
:1003A000233071F0243028F42130A1F0223011F5EF
:1003B00014C02630B1F02730C1F02430D9F404C085
:1003C000809180008F7703C0809180008F7D809323
:1003D000800010C084B58F7702C084B58F7D84BD46
:1003E00009C08091B0008F7703C08091B0008F7DED
:1003F0008093B000E32FF0E0EE0FFF1FEE58FF4FA9
:10040000A591B4912FB7F894662321F48C9190951F
:10041000892302C08C91892B8C932FBF0895CF9391
:10042000DF930E9441010E949A00C0E0D0E00E9448
:1004300083002097E1F30E940000F9CFF894FFCFEA
:020440000D00AD
:00000001FF

Neat, right? This is the infamous "Blink" example, compiled to a representation that can be uploaded (flashed) to an Uno and blink a LED! And all this came out of this (pretty much) straightforward code:

void setup() {
  pinMode(13, OUTPUT);
}

void loop() {
  digitalWrite(13, HIGH);
  delay(1000);
  digitalWrite(13, LOW);
  delay(1000);
}

The (humanly readable) code above, tells the microcontroller to set up output 13 as digital, set it to HIGH in order to allow current to pass through a LED that is connected to it, causing it to light up. Then we tell it to wait for exactly 1000ms (or better put, a second), cut off the current flow on output 13 (shutting off the LED) and then wait another second, repeating the process ad infinitum. If a microcontroller had real brains, all you would need to tell it would be to, well, blink the led on port 13! But you have to be explicit because it really doesn't get it. All this should make you wonder, if it isn't smart enough to know how to blink a LED, does it really know the concept of time or how to delay a second? Not really. Despite that, you may have seen that we used delay() as if the microprocessor knew what to do with it. Let's see how that happens.

A perfect dance

Every time you compile, a perfect dance is performed. The compiler takes your code and tries to figure out how to compile it to an intermediate object that does what the code requests. You didn't specify the details of how delay(1000) actually causes the microprocessor to delay one second, because some person has already done all the hard work for you. The compiler can figure out that at some library somewhere, the code for delay() exists (and that's because it exists in a library that is automatically included with your code). When everything is compiled, the linker, working en pair with the compiler, figures out how to link the parts of your code with the missing parts like delay(), by adding the implementation of delay() from the library. The end result is some compact machine code that does what you commanded.

There are some libraries that cover basic stuff and are so necessary that they are always included and compiled together with your code. These are called system libraries. This doesn't mean they will also be linked into your machine code if they are not needed. The linker is ’smart’ enough to not bloat your machine code with unnecessary parts. System libraries include basic Arduino functions like serial output, setting of outputs, reading analog values out of pins, etc. Remember digitalWrite() before? That's also on a system library.

Where's my library card?

Quite often, you need to do things that are more complex than basic Arduino functions, like talking to a sensor. Fortunately in the Arduino world, everything is almost certainly already done for you in a library. You only need to include that library and manipulate the functions it provides to your benefit. In codebender you'll find a ton of (or better put, more than 500) libraries that are already installed for you, ready to be included in your code. You don't have to go looking, downloading, installing, figuring out incompatibilities and going through all the hassle that comes with it. Rejoice!

When you get to actually include a library in your code, some elements of that C/C++ legacy start to reappear. You probably have seen that #include statements add a library in your code. You maybe have even noticed that sometimes they are used like #include <MyLibrary.h> and others like #include "MyLibrary.h". It looks like the same thing and while for simple cases it might work the same, there are some subtle differences.

To #include <> or "", that's the question

The #include <> notation tells the compiler to go looking for the mentioned library outside of your code. It might find what is missing in the system libraries or in the codebender supplied libraries. The fun part is that if it doesn't find what you specified and also no function is unknown anywhere in your code, nothing bad will happen! The compiler won't care and the linker won't find any missing links, allowing the compilation to succeed.

On the other hand, when you use the #include "" notation, you tell the compiler that it should look for a file with that exact name included in your sketch, together with your ino file. In codebender, if you include a file like that and it's missing, the compiler will complain and abort the compilation because it feels that it is important to you, and it shouldn't continue without it (despite being eventualy used or not). There's also one more thing to remember. If you specify the name of a file that doesn't exist in your sketch, but exists in a system or codebender library, the compiler will look there and use that one!

This is getting personal

Let's see now what happens with another really useful feature of codebender, the ability to add personal libraries. A personal library is used in all your sketches together with system or codebender libraries. You can include it with <> as with any other library. The really useful part is that it also allows you to mask a codebender library with your own, perhaps because you want to use an older, or modified version.

Excuse me please, I have a request

And since we're on the topic of libraries, if you'd like to see a library that you use often as part of codebender libraries, make a request for it. We thoroughly test each library before adding it to make sure it doesn't break existing sketches. For updating a library, it means we compile each sketch with the library we already have, then with the library we're about to add, times the number of common Arduino boards. With more than 100.000 unique sketches in codebender, that's a lot of compiles just to make sure your sketches keep compiling! This causes us to not be as fast to clear the library request queue as we'd like, but we're working on a new system that will make things much faster.