Why I Hate Arduino

The title of this blog entry is going to make a lot more enemies for me. Not that I need any help there. But perhaps the content will take some of the edge off of it. I really don't hate Arduino, but there are a lot of things about it that bother or concern me, and some that just downright irritate me. And, to be honest, many of the things that bother me about Arduino is how people (try to) use it beyond it's reasonable capabilities. But the Arduino does little to alleviate that, and that again annoys me. So this is my rant against Arduino in general, and in specific about how it's often (mis)used. If you are an Arduino fan and don't want to get annoyed, you should quit reading now. If you choose to go on and then you get annoyed, don't blame me: I warned you. If you want to learn why Arduino annoys me and possibly to avoid some of those annoyances yourself, feel free to read on. If you end up getting mad about something I write, oh well. Don't try to change my mind. I have been doing this too long.

Interstellar Travel for Dummies

Arduino is a great platform. The AVR processors it is built on is my favorite of any 8 bit processor ever built. I have used a few: 8080, z80, 6502, 6800, 6809, 8048/8035, 8051, and I am familiar with several more. GCC is a great compiler suite and I often use it on several different platforms, including the AVR processors. The AVR-GCC compiler and libraries that Arduino is built on is first rate and accomplishes it's goals remarkably well. But let's face it, sometimes developing embedded systems can be very hard. That is why people who do it for a living are paid well. And that is why Arduino was created: to make it easier for beginners. But that facade over a sometimes difficult problem is, in the end, just a facade, and if you push too far it will crack, exposing the problematic issues underlying the pretty front.

Imagine someone wrote and published a book called "Interstellar Travel for Dummies" that outlined how to build a "simple" spaceship capable of travelling to other star systems, and suggested some planned missions. That's great. Now "ordinary" people can have their own interstellar spaceship and travel the universe. As long as they use one of the pre-planned "demo" missions or a minor variant of it. But if they want to do something completely different from one of the pre-planned missions, they will quickly find that underneath, it really is rocket science and not very easy. If there aren't 53 warning labels of the dangers on every page of the book you can certainly bet the first time someone got lost in space the publisher would get all kinds of sued. And even with the warnings, there would still be lots of liability suits. How does that apply to Arduino? Arduino takes a complex system and environment and puts a pretty and simple(r) face on it. It puts you (the beginner) into a comfort zone: "anybody" can do it. And as long as you use one of the demos, or a minor variation, it works (mostly). Stray too far, though, and you find it isn't quite so simple. The complexity starts poking through. You quickly get in over your head. Some people take the plunge, dive in and learn. But all too often, they end up missing critical information. They often turn to "experienced" Arduino users, who learned the same way. Very frequently, those "experienced" users are missing many of the same critical details.

Arduino from 50,000 feet

The idea of Arduino is good: simplify embedded systems creation so that anyone can do it. But the foundation it is built on isn't up to the task. Put a fresh coat of paint on an old rusty car and before long the rust is peeking through. Here are some of the problems I see with the foundation of Arduino.

1. C and C++ (sort of). Arduino is built on C, and to a lesser extent, C++. C was never intended as anything but a tool for experienced programmers. Even there it has been derided for 40 years for allowing too much freedom. Pros like it because it is concise and powerful. Powerful enough to let you shoot yourself in the foot while hanging by the neck. C++ is an extremely complex language that VERY few people fully understand. It was never intended for small systems and is poorly supported on AVR. From the AVRLIBC reference, which is what Arduino uses, "When programming C++ in space- and runtime-sensitive environments like microcontrollers, extra care should be taken to avoid unwanted side effects of the C++ calling conventions like implied copy constructors that could be called upon function invocation etc. These things could easily add up into a considerable amount of time and program memory wasted. Thus, casual inspection of the generated assembler code (using the -S compiler option) seems to be warranted."

If you don't know what that means, you shouldn't be trying to use C++. Although C isn't a complex language, it's full of gotchas that bite pros all the time.

2. Arduino is not a PC. Occasionally you see the people that have done "some programming." Usually it was in school, and since the current "save the world" fad is Object Oriented Design, they were taught object oriented programming. They decide that is what Arduino needs. All sorts of UML diagrams and class hierarchies are laid out with the intent of making some Universal Robot Class Library. Since Arduino seems to use / support C++ (it doesn't really) it seems logical. But wait a minute. A "standard" Arduino (ATMega 328) has 32K of flash memory and 2K of RAM. Not 2 Gigabytes. Not 2 Megabytes. Two kilobytes. As in 2048. Where are we gonna store all those objects and references and base objects and so on? In a PC a dozen or so 4 byte virtual function table pointers are insignificant. In an Arduino they take up several percent of your entire RAM. And they hold no useful information. And what about the difference between the 32K flash and 2K RAM I so casually mentioned above? C, and by extension C++, are designed for a single unified memory space, not separate code (read only) and data space. The GCC and AVRLIBC have come up with some very good work-arounds for that, but it isn't pretty (or simple!). Clean, tight, efficient code is the only game in town in embedded systems. It has only been in very recent years that C++ has started being used in smallish embedded systems, and then it is only used in limited ways and very carefully. It still causes problems (Google "static object initialization in C++"). By masquerading as a C++ environment Arduino is simply asking for trouble. Dynamic memory allocation using malloc( ) and new, which is often the bread and butter of PC programmers, is better forgotten with 2K of RAM. Most embedded programmers are painfully aware of that. What about Arduino programmers? And what about all those cool standard libraries? All kinds of nifty things available, like math routines. But when you decide you need to take the sin() of an angle, your code size suddenly doubles by just adding a couple lines of code. On a PC, adding 8K of code wouldn't even be noticed. On an Arduino it is over 1/4 of the available program space. How about strings? I won't start down that path.

3. Documentation. All of the Arduino documentation I have read falls into the "feel good" class of education. Encourage peoople they can do anything, no matter how unrealistic, and never mention any pitfalls or dangers or risk of failure. There are usually ways to work around all the problems I have mentioned, but rarely is it simple. In looking at the Arduino docs, I have rarely seen it laid out anywhere that there are limitations. Sometimes, tucked away almost unnoticed is a short mention of some limitation. I could forgive the Arduino community much if they laid out limitations and work arounds somewhere, but that wouldn't fit their "anyone can do it" party line. Programming and rocket science can be very difficult. How many times have buildings full of PhDs shipped buggy software or launched failed rockets? Let's at least acknowledge that it isn't always easy and simple.

Arduino from the Treetops

Perhaps you think I'm being too harsh and none of the issues I have mentioned are real problems. Looking through projects on LMR and other places I see many of these problems manifested, but rarely recognized for what they are. The forums are full of people running into problems that are often caused by the very issues I have listed. Worse, I often see "experienced" Arduino users giving bad advice because they don't really understand what is happening. And often, reading between the lines, future problems are seen waiting to hit once the immediate problem is "solved."

Enough generalization. Here are some specifics. This post How to convert strings to double by pixelatedpic was actually the post that finally triggered me to write this. It is full of the very issues I have mentioned, and more waiting in the wings. Let's take a look. But before I go on let me state that I have no issue with the poster(s) of the examples I use In fact, I will make sure to use examples where it is clearly the environment that is at fault, not the person asking the questions.

First, what is a string? That is a very good question. One without a single good answer. The answer is, "it depends." One simple definition that usually applies is "a group of characters taken as a single unit." That (or something close) is the general computer science definition. In C it is pretty close to that. C uses an array of characters, the last of which is a NULL ( character 0) to represent a string. Note "array of characters" and NULL terminated. A string in C is not a datatype. Standard C++ decided to fix that by defining a String datatype (class) that operates on strings as a unit. It's a user-defined data type the internals of which you aren't supposed to know or care about. In reality, it holds an array of characters and some overhead data. It has some really nice methods to do some really cool stuff with strings. Very handy. But it insidiously hides a lot of things behind the scenes that we small embedded developers need to know about. Notice there is no length specified for a "string." It can be any length. In C and C++ it can change at the programmer's whim. How do we reserve memory for something whose size we don't know ahead of time? We don't. We hold a pointer to some block of memory which will be allocated dynamically when we need it. See above about dynamic memory allocation on small systems. We have already hit two of the issues I mentioned above, pointer overhead and dynamic allocation, and we haven't done anything but declare an empty string variable. What if we now want to assign it a constant value, like this:
String my_string = "My constant string.";
That one single, simple line of code is going to have several bad effects that most people would never imagine. First, the string literal, "My constant string", has to be stored into the flash so that it is available whenever your program runs. Then, memory (RAM) has to be set aside for your string variable. But that is an object, not just an array, so it has some other fields along with the data you want. The "string object" has to be constructed, so the compiler has to create or load a "constructor" function to build your string variable. Depending on where that line of code is determines exactly how and where the memory gets set aside and the object constructed. Once the string object is constructed with no string data, the characters of the string ( My constant string) have to be copied from the flash into RAM, where it can be written as well as read. Now, if we want to actually do something with that string, we have to either use one of the C++ String class methods on the String itself, or use one of the C++ methods to extract the C char array and then use a C function to operate on it. In either case, we get the vagaries of C++ "behind the scenes" code, which not only takes up more code space, but may also use more RAM. And it's interesting that at this point we are now using TWO languages that are not beginner friendly.

What about the actual question being asked? The poster wants to convert a string to a double, presumably to be used in some future calculation. Several experienced Arduino users offer suggestions, finally getting to merser's example that should work:
String lati = "4.343433";
char buf[lati.length()];
lati.toCharArray(buf,lati.length()+1);
double val=atof(buf,6); // Edit the 6 to how many digits precision you need
That should work, but I wouldn't call it simple. And note again, we are mixing C and C++. They are NOT the same language. But even more important, there is a very subtle flaw here that is lurking, ready to bite later. Merser points us to it, possibly without knowing. But it was waiting in the original post, too, looking for a chance to pounce.

What about that double the poster wants? I am going to reach a bit here into guesswork. But even if my guesses are wrong in this case, the hazard still waits for many in similar situations. The string is coming from a GPS unit, and he calls it lat or lati. I assume it is latitude in decimal degrees. That's a pretty safe guess. But what does he want to do with it? This being LMR I am going to guess he wants to use it to locate a small robot. That is great, and the code so far gets us on our way. It compiles and will probably work(mostly) as expected. But let's look at some numbers Latitude is an angle. If you drew a line from the center of the Earth to the surface, latitude is the angle of that line north or south of the equator. 0 Latitude is on the equator, and the poles are at 90 and -90 degrees latitude. The distance from the north pole to the south pole is about 20,000 km. The latitude angle can easily be converted to a distance. If the poster wants to find a point on the Earth to locate his robot, he must convert the latitude (and longitude) to distances. We might want our robot to know it's position within 1 cm. 20,000 km is 2,000,000,000 cm. So to specify a point on the Earth anywhere between the two poles, we need a variable that can specify one part in 2,000,000,000. That requires a number with at least 31 binary digits of precision. A double (IEEE double precision floating point ) has 64 total bits, 53 of which specify the precision. So a double is great. It has needed precision with extra bits to keep us from getting into trouble with intermediate calculations. There is just one slight problem.
Arduino doesn't have doubles.

Yes, I know you can declare a variable as double. And the code will compile and run just fine. Except that the compiler automatically changes all doubles to float. What does that mean to us in this situation? A float (IEEE 32 bit floating point) is a 32 bit number, 24 bits of which specify the precision. Twenty four bits gives 16,777,216 possible values, for a resolution about 1 in 16,000,000. Dividing our distance between poles of 20,000 km by 16,777,216 gives us a resoltuion of about 1.2 meters. That means, if everything else is absolutely perfect, with no errors anywhere, the best resolution we could hope for would be 1.2 meters. Probably not very useful for an indoor robot, and certainly not a desktop bot. There are plenty of other errors that will creep in And just to get that resolution will take some pretty fancy tricks. But the fact that we were led to believe our calculations were going to be much more accurate with very little warning otherwise, is just setting us up for failure. And there is no easy solution. The longitude calculation is even worse. It has to go all the way around the Earth, not halfway, so the distance is about 40,000 km and the final best resolution is 2.4 meters (about 8 feet). Arduino plays a nasty game of charades, telling us we have double precision values when we don't, that prevents us from doing accurate math. The math problem is foundational: it comes from GCC and the AVRLIBC library, but the fact Arduino builds on that and passes it on is entirely their fault.

On the ground running

This long tirade has merely scratched the surface of flaws in the Arduino way. I could write much more. This has mostly been taken from one very simple example post on LMR. Browse through the forums and projects on LMR and you will see many more, with problems both obvious and not obvious. What should we get from all this? First, I don't really hate Arduino. The Arduino system has brought embedded systems design and creation to a wide audience that otherwise never would have taken the plunge. That is a good thing.

Next, the goal of making embedded systems simple and easy is a great idea. Look at all the great things that have been created with Arduino. Look at all the other similar projects and copycats. But rocket science will always be rocket science, and full of difficulties. So will embedded systems design. We need to be aware of the problems: the dangers should be made perfectly clear from the start. Arduino has done a lot to reach that goal, but the foundation of C and C++ and AVR isn't the right one. Little has been done to overcome the problems inherent in the foundation tools. Arduino has been around for ten years now. They release new IDEs on a regular basis, but they never address the underlying difficulties. A better foundation, perhaps based on a different processor, language, and library system, might help. But when Arduino has stepped up from the AVR processors to ARM, they haven't moved away from the problems. Instead, they have insisted on as much compatability as possible.

Embedded systems programming is often quite challenging, and probably always will be. It is important to separate the problems associated with the problem to be solved from the problems caused by the tools used. As much as possible should be done to make the tools not only easy to use, but to help us with solving our problem and look over our shoulders to keep us from causing ourselves problems. Good IDEs on PCs do a lot of that. Check out Visual Studio with Intellisense, and other tools. As much as I like C and C++ I don't think they are appropriate for beginning and casual programmers. There are other languages, and other systems, that help in that regard. I hesitate to even say it, but the Propellor from Parallax is a step in the right direction. The language (SPIN) is well defined and relatively safe from what I have seen. (At least that will make Duane Degn happy). But the propellor still falls short in quite a few areas.

To sum up, then, I don't really hate Arduino. The Arduino system has started us down a path to make embedded systems available to many who otherwise wouldn't take the plunge. I do get really frustrated with it a lot, mainly because instead of continuing down the path, creating good IDEs and programming systems, newer versions with better capabilities, and writing better documentation, they just keep putting out more of the same and leave those beginners with no place to turn. If you are interested in continuing down the path of building embedded systems, I encourage you to check out what other options there are, then come back and demand Arduino step up the pace, making their system more powerful AND easier to succeed with.