Smarter Arduino Programming - Tips and Tricks

1,820

37

5

Posted in TechnologyArduino

Introduction: Smarter Arduino Programming - Tips and Tricks

About: For now see me at: http://www.opencircuits.com/User:Russ_hensel

The following are some tips for better quicker programming of Arduinos. Much of the advice applies to other platforms so you may want to take a quick look even if you do not use the Arduino. This advice applies to the standard computer arduino ide, but applies to other environments as well. Some of the tips are fairly well known/standard but are often not used in the code I see published.

If you are a rank beginner writing 50 line programs you may not find this useful. However, my typical arduino programs are more like 500 lines plus and these methods really begin to pay off.

Thanks Wikipedia for some of the graphics.

Step 1: Sample Program

I have written a simple program that illustrates some of these techniques. You can download it from github https://github.com/russ-hensel/BlinkInstruct If you download it and unzip into a directory called BlinkInstruct then you can open it with arduino.exe. And here is a tip: the download consists of several files, .ino, .h, .cpp; when you open the .ino file arduino.exe will open all of them in separate tabs of the arduino ide. Having several files offers advantages some of which will be discussed below. If you do not want to download the files you can still read them directly on github. If you do not want read along with it you can still get something out of just reading the tips. In the topics that follow I will use as examples the code in these files.

Step 2: Easier Reading

Studies show programmers spend more time reading their code than writing it. Much of coding of all but the smallest programs involves scrolling up and down in the code looking for subroutine, names, signatures, code snippets... This scrolling makes you loose your place where you are actually writing the code. Simple solution: Open the file in another editor for read only access. You can still copy and paste between the two copies. From time to time refresh the read only copy.

Step 3: Easier Search

Most programs consist of a bunch of subroutines. If you have a list near the top of the program ( perhaps as comments ) it is easy to highlight them ( perhaps with a double click ) it ctrl-f ( for find ) and then zip down to the routine ( or calls to it ) in a flash.

In the example I have a help routine rptHelp() with the relevant subroutine names as comments that serves this purpose, and in the loop() is a small command interpreter that dispatches to different subroutines. Each can serve double duty as an assistant to finding the code. Both are near the top of the program so you do not have to scroll a long distance to find them.

Step 4: Use Header Files

This is a very standard practice by C programmers, but I rarely see it in instructable projects.

Header files or .h files are commonly used for any settings that are used in the code that you might want to change without searching through the code. Very often this is done with pound defines. For example if you have a program foo.ino make a text file foo.h. In my example BlinkInstruct.h I have:

#define  VERSION_ID       F( "BlinkInstruct 2018 02 03.2" )
          
#define  BAUD_RATE        19200      // including 9600 19200 38400 .......   set the terminal to match 


#define  MY_LED_PIN       13         // 13 is the builtin led but you can connect leds to other pins 
#define  DEFAULT_DELAY    100        // delay used in the blink

Then at the top of BlinkInstruct.ino put the line

#include "BlinkInstruct.h"  

Finally use the #defines in the code something like:

digitalWrite( MY_LED_PIN, LOW );  

You will see more examples in the files. The example has another .h file, which is similar but I will not cover it here.

Dot H file can be use for many other useful things, but this is not an instructable on them or macro expansion. Google for a lot more useful info.

I am not sure what the official way is to get the arduino.exe to open the .h files ( and .cpp files.... ). My method is just to put the files in the directory with the code ( when the code is not open ). The next time you open the file the .h and other files will be show in the project, each in its own tab.

Step 5: Add Some Documentation

Many program become difficult to use and modify because of inadequate documentation. This can even trip up the author of the program. So add some documentation. But where:

  • Start with good naming, often if names are good you are done.
  • Add comments. Do not tell what the code is doing, for that someone can read the code. Instead tell why the code is doing something: what is the purpose, if it obvious leave the comment out.
  • Write some additional material but where to put it: use another .h file, not for code but just comments.

I use a file called readme.h. It is not "included" in any other file so it is not compiled. I is a good location for scratch work, example code, and longer notes that you might not want to put in the code. Comments in this file do not need // to mark them as comments because the file is never complied. Arduino.exe will automatically open the file so it is always at hand when you are working on your code.

Step 6: Serial Monitor

Always have a serial monitor as part of your code. This lets you more easily see what is going on in the program and optionally control and debug it. If no serial monitor ( terminal program ) is connected there is generally no harm, the bits just go right in the bit bucket for recycling. Arduino.exe has its own serial monitor built in. I wanted more so I have written a serial monitor for the PC end of the conversations: [http://www.opencircuits.com/Python_Smart_Terminal Python Smart Terminal - OpenCircuits ] and [https://www.instructables.com/id/Python-Terminal-for-Cheap-Pi-Arduino-Connection/ Python Terminal for Cheap Pi Arduino Connection] Try it if you are a Python user as well ( and then you might also like: '''[https://www.instructables.com/id/Arduino-and-Python-and-perhaps-a-Rasberry-Pi/ Arduino and Python and Perhaps a Rasberry Pi ]''' )

Step 7: Commands (serial Monitor )

Many programs boot up, set up, and run a loop forever doing one thing over and over.

I like to make my programs more interesting by letting a user control them using the serial monitor ( or other terminal program, see especially: [[Python Smart Terminal]] ). I have written a library like object SerialCmd to support this. SerialCmd.cpp and SerialCmd.h are used over and over in my different programs, they are essentially library programs and you can just use them as is without every looking at them.

So here I will not discuss there internals but just how they are used in the sample program.

The whole program is divided into a bunch of subroutines including the familiar setup and loop. Setup gets SerialCmd, yes, setup. It is used in loop(). Subroutine loop() works this way:

* It checks the UART to receive any characters that might be waiting. ( serialCmd.tryRecCmd( ); )

* It check for the end of a command by seeing if a carriage return character has been received. ( if ( serialCmd.gotCmd ) )

* If it has a new command it separates the single character for the command, and gets any associated number into local variables. ( cmdChar = serialCmd.cmdPrior[0]; cmdNbr = SerialCmd.parseCmdNbr( serialCmd.cmdPrior ); ) Else it just loops

* The new command letter is used in a case statement to call the correct subroutine, optionaly with the associated number.

Now you can make your arduino respond to commands, behavior can change without coding, reflashing......

Read the code it is pretty clear.

Step 8: Version ( Serial Monitor )

I do a lot of programs and a lot of revisions. Sometimes I am not working with the version that I want. So my code always includes a version that it sends to the serial port when the program starts and any time I ask the program for it ( with the command "v" ).

In the header file:

#define  VERSION_ID       F( "BlinkInstruct 2018 02 03.2" )

In the program: ( where there is a subroutine rptVersion( ) )

Serial.println( VERSION_ID  ); 

Step 9: Help ( Serial Monitor )

One of the commands that is made available using the serial monitor is "?" to ask for Help. It is implemented by rptHelp(). It is really simple it just prints a little reminder of the commands the program understands. Really useful after switching between different programs or after not using a program for a period of time.

Step 10: F...ing Strings

I use lots of messages which means lots of string literals like:

Serial.println( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" )

this can use a lot of dynamic memory ( which you have less of than program memory ). Change your code to:

Serial.println( F( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ) )

and the string goes in program memory ( which you normally have lots of ). This explains the "F(" which those of you with sharp eyes will have already noticed in the code.

Step 11: Blink a Pin - Time It

It is usually fairly easy to add a blink to most programs. You can use it to indicate that power is on and that the program is ( maybe ) functioning.

In some cases you may want to know how fast ( usually a part of the program that is supposed to be as fast as possible ). One way to do this is to look at the assembled code and use the timing for each instruction ( and path ). The other way is to make it blink a pin and measure it with an oscilloscope of logic analyzer. Much quicker and easier.

The picture above shows about the fasted blink you can get without getting tricky.

Step 12: A Better IDE

I have not looked hard but there may be more powerful IDE's for the Arduino. They may take some effort to find and setup, I have not done this, but if you do not like arduino.exe look around. Let me know what you like in the comments.

Notepad ++ is not an IDE but it is a better editor than the one in arduino.exe. If you set it to treat .ino files as c++ it will do a lot of code highlighting and clever features like code folding https://en.wikipedia.org/wiki/Code_folding Code Folding

Share

Recommendations

  • Microcontroller Contest

    Microcontroller Contest
  • Casting Contest

    Casting Contest
  • Make it Move Contest

    Make it Move Contest
user

We have a be nice policy.
Please be positive and constructive.

Tips

Questions

5 Comments

Hi. I made my ewer first program in C++ for Arduino a couple of month’s ago.
One of them commands: delay(), got me to trouble… untill I found out that the ”program” actually ”halts” during delay(), it won’t do anything else during that time???.
Take a look at my site www.teksel.net and there the Bilgepump to see where I need them delays in the functions, (alarm delay, them blinking’s etc.)++
Then I thought about how to get rid of that annoying behavour of delay().
OK. I built a TIME-generator of my own. It’s actually a kind of a ”stop-watch”, not a real-time clock, (keep that in mind all the way). I’ll ”catch” the millis() that’s generated by the processor, ( guess it’s as a part of in the ”bootloader”?), You could as well ”catch” the micros(), but I don’t need that accuaricy in my schedule here now.
Of course You can use the delay(), but I recommend to use it with short delays only.
Ewen my time-generator has to be considered in the whole program timewise.
It’s good with the generator when the timeframes are in seconds and up…. Actually you can build it up to allmost infinity of time.
True! Just get familiar to it and build on to …weeks..months.. of delay.
Just keep in mind: WHENEWER THE POWER IS CUT OFF the timers are reset ,
(you could take a backup ewery now and then to the flash memory), but thats another story.

The accuarity of my time-generator isn’t ”top-exact”. I tested it with a delay of 25 minutes, and the ”off” was less than 1second.
You can easyly calibrate your generator. I’ll get to that sometimes later on.
1’st make your whole program to run, (consider them serialPrint’s() allso, because they allso ”halt” the program). When done, test your time-generator by putting there a ”looo..ng” delay and time it with a clock.

I have written many programs in Assambler, for another type of processor with it’s own architechture). In that assam. program I built up a time-generator of my own, and now I transcripted it to C++. In part 1. I’ll discribe the function in the assam. in some details.
You can jump right on to part 2. if you don’t wanna know this assambler part,
(actually you don’t need it).

Part1
Here are the some parts for the in assam. written program, Them ”labels” and comments are mostly in Finnish, but don’t mind them, I’ll explane. The processor generates an dynamic interrupt in it’s software, (depending on the prescalers/ - counters set by you in the program). That is: where ewer you are running in your program there will be a dynamic timer IRQ .
The processor jumps to the timer interrupt routine executing that and then return and continue from where it stopped. In the setup I load the basic times for 10mS; 100mS etc.
I didnt have a need of 1mS or less in this application

;***************************************************************
; THE MAIN PROGRAM
;***************************************************************
ldi time10,0ah ; Here I load variables for the timer
ldi time100,0ah
ldi time1s,07h
ldi time10s,06h
ldi time1m,0ah
ldi aputime,06h
ldi pa_aika,pa ;Switch debounce-time
.
.
.
Here’s the timer interrup itself

;===============================================================
; TIMER interrupt
;===============================================================

tim_int ld save_a,a ;Save the present acku value
call aika ;CALL THE TIMER SUBROUTINE!!!!!
ldi wdr,11111110b ;Reload the prescaler
ldi tcr,pre_tcr ;Preset the register
res 7,tscr ;Zero the TMZ bit, new count
ld a,save_a ;restore accu value
reti ;return from interrupt



Here’s a part of the time-generator subroutine that is called from the timer iterrupt

;===============================================================
; TIMER subroutine
;===============================================================
aika dec time10 ;YOU COME HERE EWERY 1mS
jrz sata ;Check if time10 counted to zero
jp timeend ;Not yet, jump to end
sata ldi time10,0ah ;10ms reached
dec tahti1_a ;Some routines to do in the time window
jrnz sata_a ;within the 10mS, like here I use
ld a,tahti1 ;a FLIP-FLOP function with a
com a ;200mS frequence
ld tahti1,a ;Flop
ldi tahti1_a,ta1 ;200ms
sata_a dec tahti2_a ;Another FLIP-FLOP in the window
jrnz sata_b ; but this time a 500mS
ld a,tahti2 ;Flip
com a ;
ld tahti2,a ;Flop
ldi tahti2_a,ta2 ;500ms
.
.
sekunti ldi time100,0ah ;1second reached reload the 100mS
dec time1s
jrz sek_a
jp timeend
.
.
sek10 ldi time1s,06h ;10seconds reached reload the 1second
dec time10s ;Note 1second is loaded with 6, because
jrz sek10_a ;in 1minute there are 6 of 10seconds
jp timeend
sek10_a nop
;
yksimin ldi time10s,06h ;1minute reached reload the 10seconds
timeend ret

You can load within your program where ewer a variable with some value and do the decrementing of it to zero, (or to some pre-defined value), within the time-generator routine.
To decrement is much more powerful to do instead of increment.. because it’s easyer to check if a count is zero than to do a comperasion to a given value.

Part 2.
Making a TIME-Generator in C++ for ex. Arduino. (I have to explane some same things again here that was in part 1. because some people jumps ower the part 1.).
Again: this generator acts like a ”count down” clock. It seems to act wery ”odd” because it actually ”counts UP”. I’ll come to that later on. Don’t copy from my example ”right off” because I wrote the program in Finnish and then translated these parts shown here, so there may be errors? I just wanna show the ”princibles” of the concept.
The basic idea is that I ”grab” the millisecond generated by the processor,
at ewery ”loop around” thus giving it a name ”actual_time”.

Here are the variables

//------------ VARIABLES (will change)---------------------

unsigned long vanha_milli; // old_time in timegen.
unsigned long nyky_milli; // actual_time = millis()
unsigned long aika_1ms = 1; // time for 1ms
unsigned long tahti_1 = 0; // Count of 1ms
unsigned long tahti_10= 0 ; // Count of 10ms
unsigned long tahti_100= 0 ;// Count of 100ms
int tahti_1s = 0; // Count of 1second
int tahti_10s = 0 ; // Count of 10seconds
int tahti_1m ; // Count of 1minute
int tahti_10m ; // Count of 10minutes

Note: because the millis() is a unsigned long, so must the actual_time, old_time and time_1ms allso to be. In the mainprogram I call the timegenerator in ewery loop.
There I ”Grab” the millis() to be nyky_milli and use it in the generator

//=============================================================

void loop()
{ nyky_milli = millis(); // Grab the actual time to nyky_milli

// Call subroutines
timeout_tila(); // call timeout
reset_painike(); // call reset
aikagen(); // call the timegenerator
}
//======================== END of LOOP ===========================

Here is an example of using the timer. I want the reset_painike, (it’s a switch),
to be read with a conciderition of a debounce time.
Here I use a debounce time of 100mS, (reset_tahti),
At first I complement the input (nyky_reset, LOW to HIGH and vice vers).
Because I use in the setup them switch inputs to be with ”pull-up”, and therefore
an active input is LOW, howewer it’s more logical to work with logical HIGH when active.

//================================================================

void reset_painike() {
// Debounce
nyky_reset = digitalRead(sw_reset); //Read the switch
nyky_reset =! nyky_reset ; // Because active is = 0, so invert it
if ((nyky_reset == HIGH ) && (reset_tahti <=1))
{reset_apu1 = HIGH ;
} else { reset_apu1 = LOW ;}
if (nyky_reset == LOW) // If NOT active then
reset_tahti = 100 ; // DEBOUNCE TIME
//================================================================

Here is the actual TIME-GENERATOR

//================================================================

void aikagen() {

// The base count 1ms
if (nyky_milli - vanha_milli > (aika_1ms /100))//New time – Old Time
{ ++tahti_1 ; } // Increment until 1ms is reached

// Count 10ms
vanha_milli = nyky_milli; // The old_time becomes the new_time
if (tahti_1 >= 10 ) // Reached 1ms?
{ tahti_1 = 0 ;
++tahti_10 ; }

// HERE I RUN A DEBOUNCE CHECK OF A SWITCH (NYKY_RESET)
//
if (reset_tahti >= 1) //Note: it will still decrement when 1
--reset_tahti; //Note: the reset_tahti is decremented

// Count 100ms
if (tahti_10 >= 10) // Reached 10ms?
{ (tahti_10 = 0);
++tahti_100 ;

//HERE WE DO THE TASKS WE WANT TO DELAY IN 100mS WINDOW
//I HAVE THIS ”TIMEOUT” FUNCTION IN MY PROGRAM, AND HERE I
//”TRY” TO COUNT IT DOWN TO ZERO, AND IF IT REACHES ZERO,
//I NOTICE THAT IN THE ”TIMEOUT” ROUTINE.
//THE VARIABLE timeout_1 WILL BE RELOADED WITH A VALUE
//(WHAT SO EWER IT IS)AGAIN and AGAIN IN THE TIMEOUT ROUTINE
//UNTIL THE PROGRAM SAYS TO ”START” THE COUNT DOWN

if (timeout_1 >= 1) // Test: value setted: 25,5sek.
-- timeout_1 ;} // Measured time: 25,6sek.
//Note: the timeout_1 is decremented
// Count 1sek
if (tahti_100 >= 10) // Reached 100ms?
{ (tahti_100 = 0);
++tahti_1s ;}

// Count 10sek
// if (tahti_1s >= 10) // Reached 1sek?
// { (tahti_1s = 0);
// ++tahti_10s; }

// Count 1min
if (tahti_1s >= 60) // Reached 10sek?
{ (tahti_1s = 0);
++tahti_1m;}

// Count 10min
if (tahti_1m >= 10) // Reached 1minute?
{ (tahti_1m = 0);
++tahti_10m ;}
} // Return
//============================================================


The subroutine TIMEOUT

//================================================================

void timeout_tila() {

// timeout_1 "is loaded" again when MANU is NOT "active"
if (timeout_1 >= 1 ) //Has the "Count down" in timegen.
//reached zero?
{digitalWrite (led_time, LOW);No
} else {
time_alarm = HIGH ; // Hälytys
bitSet ((alarm_rek),0); // Alarm 0= TIME,blinkroutine
digitalWrite (led_time, HIGH); }
}
//================================================================

I’ll attach the complete scetch Bilge_X.ino , but notice that it’s in FINNISH, hope it gives you some ideas anyway.

Great instructable!
Definitely one to keep close.

Thanks a lot!

Hey russ_hense some nice tips you have gathered here! Thank you for sharing them. I use the sloeber ide It is more powerfull than the arduino ide. In my opinion it is the optimal solution to work with .h files and multiple taps.

Thanks. I may take a look at sloeber, but it is an Eclipse extension and years ago I found Eclipse to be a bit opaque. It is by all reports very powerful and may be worth climbing the learning curve.