how to debug code in arduino IDE

Hi,

Can anyone tell me how to debug the code written in arduino IDE.

Just write bug free code.

Serial.print() is your friend for debugging Arduino. Place them to print variables or see if code is getting executed.

Consistent coding style helps a lot.

1 Like

Jimmy60:
...
Serial.print() is your friend for debugging Arduino. Place them to print variables or see if code is getting executed.
...

as a newbie myself, this is the absolute bare minimum.
sprinkle it around your code to say "i'm here now", or "the variable 'x' is now : "

Debugging Arduino code isn't quite the same as debugging code for a PC because the Arduino code is usually controlling physical outputs or receiving physical inputs to/from the real world and the debugging process has to take those into account.

As others have said, using Serial.println() is the usual tool to monitor what's happening. Another option is to have LEDs switch on and off to signify progress through your code, but then you have to remember what they signify!

A very important element is to develop your project in small chunks and make sure each works and that you understand it before adding further features. That way if you have a question for the forum it can be specific and not "Help, my code doesn't work, what's wrong" followed by 200 lines of complex code where the error could be anywhere.

I also find it very useful to develop my code as small functions that clearly separate the functionality and make the code easy to follow. That is a great help with separating sometimes complex decision logic from the activities that the decisions control, or from which they get their input. You can also start off with dummy functions that allow the rest of the program to be compiled and tested.

And, when starting on something new, ask yourself "if this doesn't work how will I know" and maybe add extra code specially for debugging.

...R

Scaffold code is another technique to use. Scaffolding involves placing the following line at the top of your code:

#define DEBUG 1

If your code doesn't already use the Serial object, then add this to setup():

#ifdef DEBUG
Serial.begin(9600);
#endif

Now suppose you need to inspect an array value in a for loop. You might place the following inside the for loop:

#ifdef DEBUG
Serial.print("array[");
Serial.print(i);
Serial.print("]" = ");
Serial.println(array[i]);
#endif

Now, after you no longer need the debug code, you can do one of two things: 1) comment out the #define DEBUG 1 line, which silences all debug print statement in the program, or 2) change the line in the for loop to #ifdef DEBUG1. Because DEBUG1 is not defined, that debug sequence is removed, but any others in the program remain active.

Scaffolding allows you to leave all of the debug statements in the code if you wish, but comment out the #define and you can see the actual code size, less the debug statements. When you're finally convinced your code has no debugs...have a cup of coffee! While you could go back and remove all of your debug statements, which does make the code easier to read, I tend to leave them in for several weeks until I have more confidence that there are no more bugs. (Even then, some still show up.)

2 Likes
Serial.print("array[");

Just be careful your scaffolding doesn't cause the rest of your building to collapse :wink:

Serial.print(F("array[")); etc.

AWOL make s good point. String literals are copied into SRAM and byte away (pun intended) at that memory space. The F() macro prevents them from being copied into SRAM memory space. You can also get an idea of the amount of space you have left by using the following function:

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

and then call it using:

Serial.println(freeRam());
1 Like

If you write code to flash a LED and the LED never goes off because things are happening too fast, you can place a temporary break point :wink: after the digitalWrite(led,LOW); to stop the code for a short time i.e. delay(500);
If the sketch does get to that point the delay will help confirm the LED does go off.
Remove the delay after you prove the point.

You can modify the Scaffold technique considerably by using Variadic Macros.
This saves typing and is a bit more readable.
Two simple examples:

#define DEBUG   //If you comment this line, the DPRINT & DPRINTLN lines are defined as blank.
#ifdef DEBUG    //Macros are usually in all capital letters.
   #define DPRINT(...)    Serial.print(__VA_ARGS__)     //DPRINT is a macro, debug print
   #define DPRINTLN(...)  Serial.println(__VA_ARGS__)   //DPRINTLN is a macro, debug print with new line
#else
   #define DPRINT(...)     //now defines a blank line
   #define DPRINTLN(...)   //now defines a blank line
#endif

Place the above section at the top of your sketch.
So now when you enter the following in your sketch/code to debug something,
#define DEBUG
DPRINT("Testing123"); after compiling becomes Serial.print ("Testing123");
DPRINTLN(0xC0ffeeul,DEC); after compiling becomes Serial.println (0xC0ffeeul,DEC);
DPRINTLN(12648430ul,HEX); after compiling becomes Serial.println( 12648430ul,HEX);
So if you comment out the line,
//#define DEBUG
DPRINTLN("Testing123"); after compiling becomes a BLANK line.
DPRINTLN(0xC0ffeeul,DEC); after compiling becomes a BLANK line.
DPRINTLN(12648430ul,HEX); after compiling becomes a BLANK line.
Now, obviously you can create whatever macro you need to aid in de-bugging, be aware that when you add debug code to your sketches the timing and memory usage does change and you may see different results or problems.
Be careful when you use macros because if not written properly it may be very difficult to find the problems they can create.

Note: you are not limited to Serial.print, you can do things like:
#define TRACE(…) I2Cdebug.print(VA_ARGS)
Ex: TRACE(x,y); becomes I2Cdebug.print(x,y);
#define LCDPRINT(...) lcd.print(VA_ARGS)
Ex: LCDPRINT(minute()); becomes lcd.print(minute());

One more example:

#define DEBUG   
#ifdef DEBUG      
   #define DELAY(...)    delay(__VA_ARGS__) 
#else
   #define DELAY(...)    //a blank line
#endif

Now you have a DELAY macro you can turn on and off by commenting the #define DEBUG line.
Hope this helps.

Edit: this May help too. Gammon Forum : Electronics : Microprocessors : Arduino programming traps, tips and style guide

1 Like

Just to add my 6 cents worth... two things that I find useful...

  1. A Say() method that saves typing out 'Serial.println("blah")' each time. I add this to my code like this:
void Say(char x[]){
  Serial.println(x);
}

void Say(float x){
  Serial.println(x);
}

void Say(char x){
  Serial.println(x);
}

void Say(int x){
  Serial.println(x);
}

So now I can just call it like this:

Say("a..");

float f = 1.23;
Say(f);

char c = 'z';
Say(c);

int i = 99;
Say(i);

It also works for the other types. If not, it's easy enough to create another overloaded version.
You could just name it "S" if you liked to save even more keystrokes... :slight_smile:

  1. A little routine for displaying HEX. Like this:
  byte b = 0x21;

  printHEX(b);
  printHEX(0xaf);



void printHEX(byte b) {
  char A[17] = "0123456789ABCDEF";
  byte v = b / 16;
  byte w = b % 16;
  Serial.print(A[v]);
  Serial.println(A[w]);
}

This will print out:

21
AF

@LarryD Thank you for the variadic macros...exactly what I needed!

Related: how to open library files from arduino application - Programming Questions - Arduino Forum

I have found this side, where you can simulate your circuit and also debug your arduino code.

1 Like

Hello all,
this is a vital reminder that must be repeated because it cost wasted hours of debugging.
It is valid for every type of sketch, but it becomes vital when you start having a complex sketch with variables and so on.

SO HERE IT COMES!

The serial monitor is much slower than the processor itself and it has a buffer, so what you see when it stops writing is actually something that could be set much before the error came.

Even at speeds like 115k, we are talking of 1/115k*8 seconds, or 70 microseconds per character, so a sentence with 15 characters is a millisecond, ages for a CPU.

So before entering in complex ideas about stack and memory and variables, which could very well be the cause anyhow, YOU SHOULD CHECK FOR YOUR MISTAKE MUCH AFTER THE SERIAL MESSAGE, exp in the case of loops or recurring subs, the error could be in the next sub altogether.

I had a sketch restarting in the middle of nowhere and consuming heap at each restart, without any other information than regular serial.print messages.
So I would look at the messages and try to figure out what could go wrong.
I actually disassembled the whole libraries and built several sketches to reconstruct the problem, only to find the mistake was in a library which will be invoked later in the setup!

Hope this saves someone the same time I wasted, although one never knows how much time he saved, does he?

One can call Serial.flush() after each Serial.print() if is vital to see the output before the code moves on.

1 Like

This my useful macro:

#define DEBUG

#ifdef DEBUG
 #define DEBUG_PRINT(x)  Serial.print(F(#x" = ")); Serial.print (x);Serial.print (F(" ")); 
#else
 #define DEBUG_PRINT(x)
#endif
#ifdef DEBUG
 #define DEBUG_PRINTLN(x)  DEBUG_PRINT(x); Serial.println (x);
#else
 #define DEBUG_PRINTLN(x)
#endif

it prints the variable name, then " = " and variable name.

example:

a=6;
b=0;
DEBUG_PRINT(a);
DEBUG_PRINT(b);

will produce the output:
a = 6 b = 0

the DEBUG_PRINTLN(x) additionally adds new line at the end. Variable labels are stored in program memory, so it does not add up to RAM usage.

1 Like

Unicornis:
This my useful macro:

Works very nicely. Thank you.

...R

Robin2:
Works very nicely. Thank you.

...R

Thanks from me too.

There is a mistake in the definition of DEBUG_PRINTLN
It should be

 #define DEBUG_PRINTLN(x)  DEBUG_PRINT(x); Serial.println ();

instead of

 #define DEBUG_PRINTLN(x)  DEBUG_PRINT(x); Serial.println (x);

otherwise the value of the variable is printed twice

UKHeliBob:
There is a mistake in the definition of DEBUG_PRINTLN

Thanks for mentioning that. I had noticed it and corrected it but forgot to mention it.

...R

Thanks.

Removed the ; from the end of the macros.

#define DEBUG_PRINT(...)   Serial.print(F(#__VA_ARGS__" = ")); Serial.print (__VA_ARGS__);Serial.print(F(" ")) 
#define DEBUG_PRINTLN(...) DEBUG_PRINT(__VA_ARGS__); Serial.println()

//and

#define DEBUG_PRINT(...)    
#define DEBUG_PRINTLN(...)

Add these to DebugMacros.h file:

//***************************************************************
//   Example of use: 
//   #define DEBUG  //                              <---<<< this line must appear before the include line
//
//   #include <DebugMacros.h>
// 
//If you comment the line:    #define DEBUG
//the Macro lines are defined as blank, thus would be ignored by the compiler
//#define DEBUG  // if this line is NOT commented, these macros will be included in the sketch
//examples:
//  This  converts to >>>>----------------------->  This OR a Blank Line.  
// DPRINTLN("Testing123");                          Serial.println("Testing123");  
// DPRINTLN(0xC0FFEEul,DEC);                        Serial.println(0xC0FFEEul,DEC); 
// DPRINTLN(12648430ul,HEX);                        Serial.println(12648430ul,HEX); 
// DPRINTLNF("This message came from your flash");  Serial.println(F("This message came from your flash"));
// DPRINT(myVariable);                              Serial.print(myVariable);
// DELAY(100);                                      delay(100);
// PINMODE(9600);                                   pinMode(9600);
// TOGGLEd13;                                       PINB = 0x20;  // D13 Toggle,for UNO ONLY
//
// Also, this works  #define INFO(...)  { Console->printf("INFO: "); Console->printf(__VA_ARGS__); }   >>>--->   where {} allows multiple lines of code.
// See: http://forum.arduino.cc/index.php?topic=511393.msg3485833#new

#ifdef DEBUG
//examples:
//#define DPRINT(args...)  Serial.print(args)  OR use the following syntax:
#define SERIALBEGIN(...)   Serial.begin(__VA_ARGS__)
#define DPRINT(...)        Serial.print(__VA_ARGS__)
#define DPRINTLN(...)      Serial.println(__VA_ARGS__)
#define DRINTF(...)        Serial.print(F(__VA_ARGS__))
#define DPRINTLNF(...)     Serial.println(F(__VA_ARGS__)) //printing text using the F macro
#define DELAY(...)         delay(__VA_ARGS__)
#define PINMODE(...)       pinMode(__VA_ARGS__)
#define TOGGLEd13          PINB = 0x20                    //UNO's pin D13

#define DEBUG_PRINT(...)   Serial.print(F(#__VA_ARGS__" = ")); Serial.print(__VA_ARGS__); Serial.print(F(" ")) 
#define DEBUG_PRINTLN(...) DEBUG_PRINT(__VA_ARGS__); Serial.println()

//***************************************************************
#else
#define SERIALBEGIN(...)  
#define DPRINT(...)       
#define DPRINTLN(...)     
#define DPRINTF(...)      
#define DPRINTLNF(...)    
#define DELAY(...)        
#define PINMODE(...)      
#define TOGGLEd13      

#define DEBUG_PRINT(...)    
#define DEBUG_PRINTLN(...)  

#endif
//***************************************************************
#define DEBUG
#include <DebugMacros.h>

void setup()
{
  Serial.begin(9600);
}
void loop()
{
  for (byte x = 0; x < 100; x++)
  {
    delay(100);
    DEBUG_PRINTLN(x,HEX);
    DEBUG_PRINTLN(x,BIN);
  }
  
}