5 Common Arduino Programming Mistakes

Code compiles but doesn't work? Check these 5 mistakes.

5 common arduino programming mistakes

Whenever someone sends me some code that doesn’t work, there are a few common Arduino programming mistakes that I check. Some of these mistakes I make myself.  In most cases my code will compile just fine. Sometimes, these mistakes won’t generate any compiler error.

When my Arduino code is acting up, these are the first things I check. Here are my 5 common Arduino programming mistakes, I use to debug non-working code.

1. Equals and Assignment

When programming C and C++ a very common mistake is confusing the assignment operator and the comparison operator. While a warning level can be set to inform you, the default level used by the Arduino IDE will ignore this simple if-statement without a warning.

if (foo = bar) {

See the problem? Instead of using ‘==‘, the code is using ‘=‘. Instead of comparing foo to bar, the compiler will assign the value of bar to foo and return that value. In C zero (0) is false and anything else is true. So unless bar contains the value zero (0), that if-statement will always evaluate true.

I’ve written a way to avoid mixing up C’s comparison and assignment operator, but even I still make this mistake to this day.

2. Float and Integer Math

Once you get bit by this error, it is usually easy to spot. When doing floating point math, don’t let unintended casting to integers cause problems.

int bar = 4;
float foo = 10 / bar; // verify if this is right
Serial.println(foo);

What would you expect the Serial.println() to output? The answer is “2″. Even though ten divided by four is 2.5, the compiler is only looking at the integers and does integer math. Then it tries to store the result into the float. At that point, though, the decimal never existed.

There’re a couple of ways to avoid this issue. First, you could cast the integers into a float, so the compiler knows to treat them as floats instead of integers:

int bar = 4;
float foo = (float)10 / (float)bar;
Serial.println(foo);

Or, as long as one value on the right is a float, the compiler will upgrade (promote?) the other values to a float as well. Adding a decimal to the constant 10 is an easy way turn “10” into a float. What decimal? Why zero (0) of course!

int bar = 4;
float foo = 10.0 / bar; // verify if this is right
Serial.println(foo);

I prefer this method. It’s a little cleaner to me when I read it. The decimal value “10.0” is an immediate flag in my mind I want floating point math done.

3. Confusing Analog and Digital Pins

On Arduino boards, it isn’t necessary to call pinMode on Analog Pins—when used as analog inputs. In fact, I find it confusing since analog pins can function as a digital pin. Wouldn’t it be nice to glance at setup() and identify which pins are analog or digital?

I agree, so I add a (seemingly) pointless analogRead to my setup(), like this one.

void setup() {
   Serial.begin(9600);
   pinMode(13, OUTPUT);
   pinMode(2, INPUT_PULLUP);
   analogRead(A1); // My temperature sensor
}

Another important note is that I used the value of “A1” and not a decimal number. The Arduino.h header defines A1 as a constant integer. On 328p-based boards, like the Uno, A1 is assigned the value of 15. Since A1 is a constant integer, you can treat A1 just like the number 15.

I strongly advise against trying to be clever and define your analog pins by their decimal value. Stick to the constants for two reasons:

  1. It is immediately apparent Ax refers to pins on the analog header, and
  2. If you move to a board with more I/O pins, you won’t collide your “digital” pin with an “analog” pin.

One exception is when dealing with loops. The code in analogRead knows if you pass the values of 0 through 5, you don’t mean the digital pins 0 through 5. So analogRead() will use the equivalent of A0 through A5. Keep reading for an example where I use this method.

4. Floating Pins and Mixed Logic

When you’re new to microcontrollers, you might assume that a pin is LOW (or zero) with nothing connected. However, when there is nothing attached to the pin, it is called a floating pin. This mistake is so common that I have both a written and video tutorial on floating pins.

Using a pull-up or pull-down resistor will force the pin to a known state when nothing is connected. Adding external resistors can be a drag, though. (No pun intended.) Which is why using the internal pull-up resistor is so handy. By calling pinMode() with the argument “INPUT_PULLUP”, you will enable the resistor on an individual pin.

Inverted Logic

Here’s the 2nd trap that catches both new and veteran programmers. Pull-up resistors “invert” the logic of inputs. When nothing is connected, or the input is open, the reading will be “HIGH”. Human logic might expect it to be low or zero. (Silly flawed human logic.) The microcontroller doesn’t care. Bits are bits.

The trick I’ll sometimes use is a #define statement. Gasp, I know. But in this case, I find #define suitable. Here are examples when I use a button or switch with my Arduino:

//Used for push-buttons
#define NOT-PRESSED HIGH
#define PRESSED LOW

//Used for a switch
#define OFF HIGH
#define ON LOW

//… Somewhere later in my code ...
if (digitalRead(enableButton) == PRESSED)
   doAwesomeThing();

I find reading this to be far more logical than checking to see if the enable button is LOW.

5. Array Sizes

There are two aspects of arrays I want to touch on. First is defining and accessing the array elements. The length of an array vs. the number of elements bites everyone at some point. But the mistake I want to cover is using sizeof() with an Arduino array.

Number of Array Elements

When you define an array, you tell the compiler how many elements it contains. For example, to store an Uno’s six analog inputs in an array, you might define something like this:

analogInputReadings[6];

The common Arduino programming mistake is that an array’s index is zero-based. This detail means you start with element 0. If there are six items, like our analogInputReadings[6] example, then you can only access 0, 1, 2, 3, 4 and 5. The compiler will LET you read from analogInputReadings[6], even though you probably don’t want to do that.

Why? Well, the way arrays work in C and C++ is that the compiler just does some simple math. It knows the address for the first element and the length of each element. So when you tell it to access element 0, it just accesses the memory location for 0. When you tell the compiler to access element 3, it adds 3 data type lengths to the address at 0. So when you ask to access element 6, guess what it does? More math. The microcontroller will access whatever memory location is six steps away from the zeroth element.

When reading data, this isn’t so harmful. You might get some garbage data or unexpected results. The real danger is when you try writing to that location. That memory location may be some other variable or part of another variable.

Here’s an example of what this MIGHT look like.

// not real code
int analogInputReadings[6]; 
byte poorGuy = 0x55; // 85 in decimal 

setup() { 
   Serial.begin(115200); 
   Serial.println(F(“Current Values In Memory:”)); 
   for (int x=0; x<6; x++) { 
      Serial.print(x); 
      Serial.print(F(“: “)); 
      Serial.println(analogInputReads[x]); 
   }
 
   Serial.print(F(“poorGuy: “)); 
   Serial.println(poorGuy); 

   // read in some data: Analog In will be random, based on floating pins (see above) 
   for (int x=0; x < 7; x++) 
      analogInputReadings[x] = analogRead(x); 
   Serial.println(F(“\nNew Values In Memory:”)); 

   for (int x=0; x < 6; x++) { 
      Serial.print(x); 
      Serial.print(F(“: “)); 
      Serial.println(analogInputReads[x]); 
   } 
   Serial.print(F(“poorGuy: “)); 
   Serial.println(poorGuy); 
} 

loop() { 
   while(1); 
} 

This example is arbitrary. The compiler may not cooperate and place the array and variable next to each other in this case. However, let’s assume for a minute it did. Here’s what the output COULD look like

Serial.println(F(“New Values In Memory:”));
0: 292
1: 103
2: 344
3: 291
4: 61
5: 234
PoorGuy: 85

New Values in Memory:
0: 292
1: 103
2: 344
3: 291
4: 61
5: 234
PoorGuy: 0

Long story short, don’t access array elements that don’t exist!

Arduino sizeof() Array

Whenever I create an array, I create a constant byte variable that has the size of the array. That way when I do operations with for() or while() loops, I can refer to that constant. If iI change the length of the array, I just need to change the constant.

However, I see many programmers make an Arduino programming mistake when trying to determine the size of an array within the program. I’ll see something like this:

int someValues[64];
for (int x=0; x < sizeof(someValues); x++)
   someValues[x] = 0;

Here’s the problem. sizeof() returns the length of the entire array, NOT the number of elements. For 8-bit AVR-based Arduinos, an int is 2 bytes. So sizeof(someValues) would return 128. Our for()-loop is going to corrupt whatever variables are sitting “behind” it!

So how can you determine the number of elements? Well, sort of like how the compiler handles arrays. If sizeof() returns the number of total bytes, then let’s ask for the size of a single element too. Then it is just a matter of simple division.

int someValues[64];
int arraySize = sizeof(someValues) / sizeof(someValues[0]);
for (int x=0; x < arraySize; x++)
   someValues[x] = 0;

Now the variable arrarySize will contain the number of elements in the array. You can shortcut this step and do the math in the for() loop’s declaration. I made it a discrete step to, hopefully, make the sizeof() array concept clearer.

Conclusion

There are a few other more minor Arduino programming mistakes I tend to see, but these 5 usually catch a significant number of the logic errors. Leave a comment below; I’m curious what mistakes you make, I mean, you often see other people making.

Your questions, comments, and even corrections are encouraged and very much appreciated! However. I have zero-tolerance for inappropriate or harassing comments. I try to reply to everyone... -James

Leave a comment

14 thoughts on “5 Common Arduino Programming Mistakes

  1. A comment about confusing say (max = 10) and (max == 10). Train yourself to write them like this, e.g. (10 = max).

    Technically, you have an r-value on the left side of an assignment. Now, that is a logic mistake AND a code error which the compiler will catch. It saves my backside about once a year – worth it 🙂
    cheers
    Alan
    hacking C and unix since 1985

  2. I recently encountered a problem similar to your No.2 – float and integer math.
    I was using something like this:
    #define MyConstant 40000
    int a 10000;
    long b ;
    b = a * MyConstant ;

    this worked fine.

    Then I changed the program so that it started with
    #define MyConstant 20000

    Now I found that b was being calculated as if it was an int, not a long.
    I found I could fix the problem by two methods:
    Method 1:
    b = (long) a * MyConstant ;

    Method 2 (preferred I think):
    const long MyConstant = 20000 ;

    I believe that in my first version, with #define MyConstant 40000, the compiler was interpreting MyConstant as a long, because the value was over the limit for int. Multiplying that long with an int used a long for the working result.
    However when I changed the initial value to 20,000, the compiler used an int, did the multiplication with an int as the working result, and then cast that result to the final variable which is a long.
    The first method to fix this forces the use of a long for the working result.
    The second method starts off with a long for the constant, so the working result is also necessarily a long.

    This is one of several reasons why it is better to use const than #define, where possible.
    The code with the const uses the same program and variable space as the original using #define.

  3. why should I ask for sizeof[array]? I did declare it.

    A different approach would be to work only with that elements realy filled in. But then you’ve to count otherwise.

    • There are a few cases where it makes sense. One would be when converting the code to a function. You may not know ahead of time how the function will be used. Another case could be an array that changes at compile time. Like, a sequence of numbers. You wouldn’t need to setup a second constant to track the size of the array. I’m sure there are others, it’s a popular issue.

  4. I can highly recommend the command line tool “cppcheck”. It’s a very easy-to-use command-line tool to catch some of the silliest mistakes. You can install with cygwin under linux or just install the cppcheck package if you’re using Linux. To use it, just go to the directory containing your source files and run
    cppcheck *.ino

  5. Hi James, I see some that I have made and some are beyond what I am able to do. Good to know these pointers in case I try using other elements in programming.
    Bob D

  6. while (!Serial) {};

    waits for the serial port to be ready. But if you’re not connected to the serial port (usually via USB), it will wait forever. It’s better just to insert a fixed delay or wait for the serial port for a few seconds and then give up, if the serial port is not vital to your program (e.g., just used for logging).

    • It’s also important to note this condition applies to the 32u4 based boards (Leonardo and Micro). If while (!Serial) is called on a 328p board like the Uno, it will always return true if called after Serial.begin().