How do I debug an Arduino sketch?
Modularity is your friend. Write your main loop to do its thing by calling functions, which call functions, ..., until the level at which your functions would be simple. Start with the main loop and the next level down, make stub functions; either empty:
function foo(){
;
}
or fake:
function read_temperature(){
return(95);
}
, that do nothing but return whatever the calling level needs for it to be able to continue. When that level works, move down a level and start filling in simple code that likewise calls stub functions. Gradually un-stub a function at a time until you have a working application.
To debug a function that returns a bad value, or to create one without any influence from the rest of your application, you can build scaffolding - a simple sketch that just feeds the function some example values, and within the function, print out parameter values and some intermediate values, until you gain some insight into what part of the function is failing. I've even made faking-functions that prompt me on the terminal for a value to return. (Obviously this technique can only work if the system can tolerate the relatively glacial speed of us humans! Another use for scaffolding.)
Stubbing works especially well to stand in for functions that interface to hardware, allowing you start bringing up the application before you have to dive into data-sheets, timing issues, and other minutiae (like, not having the parts!) that might otherwise stall your progress.
Speaking of timing issues, toggling an output pin at a particular point in your program, such as entry to and exit from an ISR, gives you a square wave at the Arduino pin whose frequency or duty cycle can give you some insight into the internal timing of your program. The direct port-I/O form, e.g.,
PORTC ^= 0x01;
, will distort the timing less than calling digitalWrite()
. Useful if you have a 'scope handy, or one of the DMMs with the ability to measure frequency and/or duty-cycle.
Similarly, you can use an analog output pin to output a numerical value to your meter from inside the program without disturbing the timing too much or bloating the code with the serial I/O functions. Use the direct I/O forms here, too.
I use Serial.print() and I make LEDs flash.
That's pretty much all you can do.
Also, I make sure the code is readable and easy to understand. Break things down into simple steps and create functions for each step, so you can see the exact sequence of events.
3 Other Techniques:
Build up the functionality of a program by slowly testing at each stage, that way you confront only a small set of bugs at a time.
Build the program around a command interpreter so you can work with sections at a time as in here.
Pulse out at significant times and use a scope.