A few random tips for Arduino programmers. We cover saving RAM by storing text in program memory, making your programs easier to read, a technique to avoid being held up in delays, the C++ ternary operator and the watchdog timer.
Putting text strings in program memory saves ram
Write Serial.println(F("Hello World!"));
instead of Serial.println("Hello World");
When you write:
Serial.println("Hello World!");
the text ‘Hello World!’ is stored in two places:
- Program memory
- Random access memory (RAM)
The contents of RAM are lost every time the Arduino is reset, or the power is lost. Program memory is retained through reset and power loss, so at the start of each program, normal strings are copied from program memory into RAM so they can be used by your program.
Many Arduino boards don’t have much RAM though: there’s only 2 kb on the 328 chip used in the Uno, for example. So it is easy to run out of RAM if your program uses many strings or other variables.
When you write:
Serial.println(F("Hello World!"));
the println
function gets the text from program memory directly, using a temporary buffer that doesn’t eat up lots of precious RAM.
Constants make programs easier to read
Let’s say you want to annoy your little brother: attach a buzzer to one of the digital outputs of your Arduino and have it beep every few seconds. If the buzzer is attached to port 6, you’ll need to enter the 6 in three places:
- setting the pin mode as an output,
- turning on the buzzer, and
- turning off the buzzer.
Instead, define a constant at the top of your program: const int BuzzerPort = 6;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
/* Beeper Makes a noise every few seconds. */ // A constant for the port that the buzzer is connected to. const int BuzzerPort = 6; // These constants define how often the beeper sounds, and how long it sounds for. const int BuzzerPeriod = 5000; // [milliseconds] const int BuzzerOnTime = 100; // [milliseconds] void setup() { pinMode(BuzzerPort, OUTPUT); } // the loop routine runs over and over again forever: void loop() { digitalWrite(BuzzerPort, HIGH); delay(BuzzerPeriod); digitalWrite(BuzzerPort, LOW); delay(BuzzerOnTime); } |
Now, when you plug the buzzer back into port 7 (after your little brother has ripped the wires out to shut the thing up), you have to change the port in only one place.
Even better, the code is clearer. Straight away we know that digitalWrite(BuzzerPort, HIGH)
should turn on a buzzer; with digitalWrite(6, HIGH)
, we need to remember what is connected to port 6.
So why use a constant instead of just a variable? Wouldn’t int BuzzerPort=6;
work just as well?
Sort of: you still only have one number to change when the port changes. But when the ‘variable’ is declared as a constant, the compiler, the tool that turns your program into something the Arduino can use, won’t let you change it accidentally. So if you have const int BuzzerPort=6
and you try to change is value:
1 2 3 4 5 |
void setup() { BuzzerPort = 7; pinMode(BuzzerPort, OUTPUT); } |
The compiler will report an error. So with constants, you can rely on the value never being changed after it is set.
No little brothers were harmed in the writing of this tip.
Delays are boring
The delay function stops your program dead. The classic blink program spends most of its time doing absolutely nothing:
1 2 3 4 5 6 7 |
void loop() { digitalWrite(LedPin, HIGH); delay(1000); // yawn. Nothing is happening here. digitalWrite(LedPin, LOW); delay(1000); // zzz. Or here! } |
Think of all the great things you could do in that dead time: reading sensors, making noise, driving motors. Delays can be useful, but often they are just a waste of time.
There’s an easy way around it, but we’ll need a few variables:
1 2 3 4 5 6 7 8 9 10 11 |
// Time stamp when the LED was last turned on or off. unsigned long LastLEDToggleTime = 0; // Current state of the LED; true => on; false => off. bool IsLEDOn = false; // Time between turning the LED on or off const int LEDTogglePeriod = 1000; // [milliseconds] // The pin the LED is connected to const int LEDPin = 13; |
And the loop becomes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
void loop() { // This will toggle the LED on and off, with no wasteful delays if ((millis() - LastLEDToggleTime) > LEDTogglePeriod) { LastLEDToggleTime = millis(); if (IsLEDOn) { IsLEDOn = false; digitalWrite(LEDPin, LOW); } else { IsLEDOn = true; digitalWrite(LEDPin, HIGH); } } // Do something else here. } |
Use the ArduinoTimer
in our library and the ternary operator (see below) and this gets even simpler:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#include "ArduinoTimer.h" // A variable to keep track of time ArduinoTimer BlinkTimer; // Current state of the LED; true => on; false => off. bool IsLEDOn = false; // Time between turning the LED on or off const int LEDTogglePeriod = 1000; // [milliseconds] // The pin the LED is connected to const int LEDPin = 13; void loop() { // This will toggle the LED on and off, with no wasteful delays if (BlinkTimer.TimePassed_Milliseconds(LEDTogglePeriod)) { IsLEDOn = !IsLEDOn; // toggle status digitalWrite(LEDPin, IsLEDOn ? HIGH : LOW); } // Do something else here. } |
Ternary Tricks
C++, the language the Arduino platform uses, includes a handy expression for selecting between two values:
(expression) ? (true-value) : (false-value)
This is called a ternary operator…because it has 3 arguments:
- (expression), a test which is true or false
- (true-value), the result if (expression) is true
- (false-value), the result if (expression) is false
It lets you write things like:
1 2 |
IsLEDOn = !IsLEDOn; // toggle value of IsLEDOn digitalWrite(LEDPin, IsLEDOn ? HIGH : LOW) |
Instead of:
1 2 3 4 5 6 7 8 9 10 |
if (IsLEDOn) { IsLEDOn = false; digitalWrite(LEDPin, LOW); } else { IsLEDOn = true; digitalWrite(LEDPin, HIGH); } |
It is easy to create code that is hard to follow using this tip. Use it wisely!
Taming runaway programs with the watch-dog timer
The Arduino’s microprocessor includes a watch-dog timer. The watch-dog timer is a sort of dead-man’s switch: if your program doesn’t keep telling the timer all is well, it will blow up the Arduino.
Actually, it just resets the micro: what you do on reset is your own responsibility.
To use the watch-dog timer, you’ll need to:
- include a library
- enable the watch-dog
- tell the watch-dog that all is well
This example should work on any Arduino that uses a microcontroller from Atmel, including the Uno, Mini and Mega. Note, older Mega’s had a problem in their bootloader. If you set the watch-dog timeout too low, you won’t be able to reprogram the Arduino using the serial bootloader. Update to the latest Mega bootloader to fix that.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
// Include the watch-dog library functions #include unsigned CountDown = 10; void setup() { Serial.begin(9600); // enable the watchdog timer and set a timeout. // if we don’t call wdt_reset() before the time is up, the program will reset automatically. wdt_enable(WDTO_1S); // WDTO_1S => 1 second timeout. } void loop() { // the program is alive...for now. wdt_reset(); while (CountDown >= 0) { Serial.println(CountDown); CountDown = CountDown - 1; delay(30); } Serial.println(F("Kaboom!")); } |
This program contains a bug which means the countdown will never finish. Can you spot it?
The program will get stuck in the while
loop. Because that means wdt_reset()
is only called the first time into loop
the watch-dog timer will eventually timeout and the program will get reset.
Got a thorny question on Arduino development? Post your question below and we’ll try to cover it in our next tip.
-
[…] 5 Tips for Arduino Programs | Articles | MegunoLink. ESP8266 seconde partie : Le mode Standalone | Nicolas C. – Blog. Si vous avez suivi la première partie, vous avez dû comprendre que le mode que je m’apprête à vous présenter ici est mon petit préféré. […]
Leave a Comment
you never define >
It was a mistake; should have been a greater than symbol. Thanks for spotting that!
>= and unsigned?
Yep nicely spotted!
Will running a very long Delay cause an Arduino to ignore an attempt to re-program it? My Arduino wouldn’t even respond to the reset button. Should I just toss it out?
Hi Jake, no it should be fine. The bootloader should run independently when reset and allow programming. It sounds like either the bootloader is not working or the arduino has been fried?