Difference between revisions of "Smarter Arduino Programming - Tips and Tricks"

From OpenCircuits
Jump to navigation Jump to search
 
(22 intermediate revisions by the same user not shown)
Line 3: Line 3:
 
= Introduction =
 
= Introduction =
  
This draft is for an instructable not yet written.  Note that this is an article started by Russ Hensel, see "http://www.opencircuits.com/index.php?title=Russ_hensel#About My Articles" '''About My Articles'''
+
This draft is for an instructable now published with pictures at: https://www.instructables.com/id/Smarter-Arduino-Programming-Tips-and-Tricks/Updates will be made here, probably before anything at instructables, but look at it just for the graphics. Note that this is an article started by Russ Hensel, see "http://www.opencircuits.com/index.php?title=Russ_hensel#About My Articles" '''About My Articles'''
  
 
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.
 
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.
  
 
= Sample Program =  
 
= Sample Program =  
Line 17: Line 19:
 
= Easier Search =
 
= 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.
+
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 ) find 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.
 
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.
Line 37: Line 39:
 
</pre>
 
</pre>
  
Then at the top of BlinkInstruct.h put the line
+
Then at the top of BlinkInstruct.ino put the line
  
 
<pre>
 
<pre>
Line 54: Line 56:
 
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.
 
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.
  
= A better IDE  =
+
= Add Some Documentation =
  
I have not looked hard but there may be more powerful IDE's for the ArduinoThey may take some effort to find and setup, I have not done this, but if you do not like arduino.exe look aroundLet me know what you like in the comments.
+
Many program become difficult to use and modify because of inadequate documentation.  This can even trip up the author of the programSo add some documentationBut 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.
  
= Add Some Documentation =
+
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.
 
 
I put another .h file in my projects called readme.h.  It is not included in any other file but 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.  
 
Comments in this file do not need // to mark them as comments because the file is never complied.  
Line 69: Line 72:
 
= Serial Monitor =
 
= 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 it.  I have another instructable that give a quite powereful way to do this that is also simple to implement.  If no serial monitor ( terminal program ) is connected there is generally no harm, the bits just go right in the bit bucket for recycling.
+
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 ]''' )
  
= F...ing Strings =
+
== Commands ==
 +
 
 +
Many programs boot up, set up, and run a loop forever doing one thing over and over.
  
I use lots of messages which means lots of string literals like:
+
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.
  
Serial.println( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" )
+
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:
  
this can use a lot of dynamic memory ( which you have less of than program memory ).  Change your code to:
+
* 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.
  
Serial.println( F( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ) )
+
Now you can make your arduino respond to commands, behavior can change without coding, reflashing......
  
and the string goes in program memory ( which you normally have lots of )
+
Read the code it is pretty clear.
  
 +
== Version ==
  
= Version =
+
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" ).
  
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.
 
 
In the header file:
 
In the header file:
  
#define   VERSION_ID         F( "SerialCmdMaster with Blink Ver4 2017 11 17.777" )
+
<pre>
In the program:
+
#define VERSION_ID       F( "BlinkInstruct 2018 02 03.2" )
 +
</pre>
  
 +
In the program: ( where there is a subroutine rptVersion( ) )
  
Serial.println( VERSION_ID  );
+
<pre>
 +
Serial.println( VERSION_ID  );  
 +
</pre>
  
 +
== Help ==
  
= Blink a pin - Time It =
+
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.
  
 +
= F...ing Strings =
  
 +
I use lots of messages which means lots of string literals like:
  
 +
<pre>
 +
Serial.println( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" )
 +
</pre>
  
 +
this can use a lot of dynamic memory ( which you have less of than program memory ).  Change your code to:
  
= Draft material =
+
<pre>
 +
Serial.println( F( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ) )
 +
</pre>
  
 +
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.
  
 +
= Blink a Pin - Time It =
  
Better Arduino Programs with Serial Commands
+
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.
  
A lot of programs for the arduino are very simple and limited.  Some only repeat what is on the arduino.cc site.  Lets take blink. You compile it, Run it. And ( with luck ) the led blinks.  Want to change how many times it blinks or how often and you change the code, recompile, upload, and run again.  A better way is to alter the program so you can tell it what to do at run time with a serial monitor, or with another terminal program ( I recommend my …... ).
+
= A better IDE =
  
Here is how.
+
I have not looked hard but there may be more powerful IDE's for the ArduinoThey may take some effort to find and setup, I have not done this, but if you do not like arduino.exe look aroundLet me know what you like in the comments.
 
 
A good way to see how this is done is to look at a program that implements this approach.  Download the attached zip file, unzip and put the results in a directory called SerialCmdMaster wherever you keep your arduino programs Then open the location in arduino.exeYou will see 4 files each in its own tab.  Or go to github () and you can look at the files in your browser.
 
 
 
You will find 4 different files, one for each of the two parts of the program.  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.  SerialCmdMaster.ino and SerialCmdMaster.h are where the type of programming I am recommending live.  I have used the strategy of using a header file SerialCmdMaster.h for the important constants in the code, neither of the .h files have executable content.
 
 
 
So most of the action is in SerialCmdMaster.ino which is what I will discuss now.
 
 
 
The whole program is divided into a bunch of subroutines including the familiar setup and loop.  I put a comment section with some documentation at the top.  Then, pretty much as usual in a C program are some includes and variable declarations.  All straight forward. 
 
 
 
The next subroutine is my tiny little help system.  It is just a bunch of serial print statements that print one line for each command in the system. 
 
 
 
So let me digress from how the internals of the program work to how it works when you connect it to a serial terminal ( of the serial monitor )
 
 
 
It boots up, starts its serial port and prints the help text.
 
 
 
One of the commands that it then available is h  followed by a newline, the newline is automatically appended if you turn …......  What does h  show help do? It just prints the same help text shown at startup.
 
 
 
The command v version of software prints the version of the software: something like "SerialCmdMaster with Blink Ver4 2017 11 17.2"
 
 
 
In general all commands are just a single letterTo add a bit more functionality to the commands you can also stick a number on the command:    "unn      set micro seconds"  might look like u22 which would cause a variable in the program to be set to 22 microseconds.  "bnn      blink_1 n times uses micros only " which might be given as b2000 calls the blink_1 subroutine and causes it to blink 2000 times using the microsecend value set with the u command.
 
 
 
So the help command prints out a reminder of the commands.  On each line of the help printouts there may be a comment that help you search for the subroutine that supports the command.
 
 
 
Often the loop subroutine is found at the bottom of the program.  I put it right after the help because it implements the help by getting the command letter ( and number if any ) and then using a switch statement  calls the proper subroutine.  The call statements in it are also useful in searching the rest of the code. 
 
 
 
The rest of the code is just a series of pretty simple subroutines that implement the commands the help system names.
 
 
 
This code has little practical purpose except to show how the serial command system works ( and as my test framework for SerialCmd ) and to run a few basic blink routines.  The idea is that you make a copy, rename the bits of pieces, and transform it into your own code with a command interface.
 
  
This code has a connection to another instructable because it is an example of some of the techniques discussed there:
+
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]].
  
  
 
  [[Category:Arduino/RaspberryPi]]
 
  [[Category:Arduino/RaspberryPi]]

Latest revision as of 07:00, 6 February 2018

Smarter Arduino Programming

Introduction[edit]

This draft is for an instructable now published with pictures at: https://www.instructables.com/id/Smarter-Arduino-Programming-Tips-and-Tricks/. Updates will be made here, probably before anything at instructables, but look at it just for the graphics. Note that this is an article started by Russ Hensel, see "http://www.opencircuits.com/index.php?title=Russ_hensel#About My Articles" About My Articles

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.

Sample Program[edit]

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.

Easier Reading[edit]

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.

Easier Search[edit]

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 ) find 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.

Use Header Files[edit]

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.

Add Some Documentation[edit]

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.

Serial Monitor[edit]

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: Python Smart Terminal - OpenCircuits and Python Terminal for Cheap Pi Arduino Connection Try it if you are a Python user as well ( and then you might also like: Arduino and Python and Perhaps a Rasberry Pi )

Commands[edit]

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.

Version[edit]

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  ); 

Help[edit]

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.

F...ing Strings[edit]

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.

Blink a Pin - Time It[edit]

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.

A better IDE[edit]

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 [Code Folding].