After 10 years of PIC assembly language (and Z80 etc back in the 1980s), I've now got started writing short applications for the Arduino in C.
There are some program routines that you use over and over, and so I thought I would have a go at writing some of them in C, so that I have them available in my programming "toolkit". One of the most useful, is a means of getting serial data in and out of the microcontroller, and more specifically the means for the micro to accept serial commands and data from a terminal programme. This is a fundamental building block of any system and has been implemented on just about every computing device since the year dot.
So I set about writing some code for the Arduino to allow it to interpret and act upon commands sent over the serial link. These serial commands could come from a terminal emulator running on a laptop, from an ethernet to serial gateway, or any other device, such as another Arduino that can generate serial data.
There are many small applications in control and monitoring which require some form of human/machine interface so that the program actions can be modified according to some numerical input. It occurred to me that this functional block is something which could be used from one project to another, so I decided to start from scratch and write something simple which would allow me to achieve basic control.
One example of how it could be used, is for the setting of the current time into a real time clock programme. If you could just enter the hours, minutes and seconds from a terminal emulator, which would set the appropriate variables in the code to those values, then it would be very handy. Or if you had a sketch which counted pulses from a gas or electricity meter, being able to set the initial reading from the meter, so that the pulse counter follows the actual readings on the meter - again a useful function.
As the Arduino can be made to act on serial data, this could come from any source, not just a laptop USB connection. For example it might come from a master Arduino which sends commands over a RS485 bus to a series of slaves, or from an ethernet to serial gateway which receives commands from a remote server or browser.
This week I have been working on an application which reads values from the Arduino ADC channels displays them on the terminal, and requires PWM data to be sent to three output pins either under manual or automatic control.
Realising that this was going to be a useful building block, and my C skills need all the exercise they can get, I set about writing the command interpreter.
Initially I used a single alpha character a-z, followed by a 3 digit number. The alpha character acted as a mnemonic so that you could easily remember the various commands. This was fine for setting the PWM channels - such that f100 would set the PWM for the cooling fan speed, and b123 would set the brightness of some LEDs.
However, following a discussion with some friends it was deemed that this might be a little too simple and that the command format should be extended to allow a more elaborate scheme. At the same time, the command interpreter is an overhead to the rest of the sketch, so it needs to remain fairly compact and not too unwieldly.
So I extended the format to allow the use of sub-addressing. The parameter immediately following the alpha character would be in the range of 0 - 255 and would allow you to direct the command to any one of 255 slave devices, with zero being reserved as the local device.
I then followed the sub-address with a pair of comma separated arguments. These were to be unsigned integers with the range 0 to 65535, with the intention that they could be used for 16-bit addressing of memory devices. More arguments could be added, but for most purposes two were deemed to be sufficient.
The basis of the command interpreter is to hold the serial input in a suitable sized array, and then parse through it to strip off the various paramenters and arguments as dictated by the commas which are used as separators. This string parser is the basis of the command interpreter. Reading in the string of characters is fairly simple, just put them into an array until you come across a carriage return character (13 decimal).
Then you strip of the initial command alpha character and the digits which form the sub address. You keep copying these numerical characters into a smaller sub-address array, until you encounter the first comma, at which point you know you have all the digits.
Then you form arrays for the first and second arguments, knowing that they are also separated by a comma and will end with a carriage return.
Once you have formed separate strings for the sub-address and arguments, you just need to enumerate them using the atoi (ascii to integer) C function.
So the string parser has stripped out the various fields and stored them into arrays, which have been enumerated. At this point you can print them out to the terminal to check that your parser has got them correctly - and not made any errors!
We then need to form a set of action routines, which do the real work when a command is executed. For this we need to decode the alpha character and use it plus the arguments to control functions within the code. For simplicity I chose to use a switch-case statement method to look for certain ascii character values and perform certain code blocks.
The other tasks performed by the Arduino were arranged to be executed once per second. This included reading the various ADC channels, averaging them out and converting to meaningful values - like a thermistor reading in degrees centigrade. As these tasks were fairly quickly performed, it left a lot of spare time for the Arduino to loop around the command interpreter - so that the response to new commands was fairly rapid, and didn't appear to distract from the once per second operations.