In a project involving a serial communication and an Arduino I would like to use the serial interface to run multiple routines on the board.
The idea is to send a unique string with tags and values in order to execute several istructions at the same time. Let's say that we want to set the heading of an aircraft to 180°, the altitude to 3 meters and 20 cm from the ground and to maintain a horizontal profile with roll and pitch angle of 0°. The string would be:
X,heading,180,roll,0,pitch,0,altitude,3.20,X
For simplicity's sake I'll suppose to send a less complex string such as:
X,tag1,tag2,tag3,val3,X
X,Roll,Con,Kp,1.12,X
In order to receive and elaborate the string I've tried using 3 arrays of chars and a few counters. Here is the code:
byte byteRead;
// Store decimal numbers, determine decimal point
double num1, num2;
double complNum,counter;
int numOfDec;
boolean mySwitch=false;
// Use a boolean var to enter in the command receiving mode.
// If you are interpreting several commands type this could be a way
boolean cmplx = false;
// arrays to store tags
char opt1[3];
char opt2[3];
char opt3[2];
// Counters to determine tags
int optCount=0,letterCount=0;
void setup()
{
Serial.begin(9600);
num1=0;
num2=0;
complNum=0;
counter=1;
numOfDec=0;
}
void loop()
{
/* check if data has been sent from the computer: */
while (Serial.available())
{
/* read the most recent byte */
byteRead = Serial.read();
if (byteRead == 'X')
{
if (!cmplx)
{
// begin of the string
cmplx = true;
}
else
{
// end of the string - reset values
cmplx=false;
optCount=0;
/* Create the double from num1 and num2 */
complNum=num1+(num2/(counter));
/* Reset the variables for the next round */
// Debug Stuff ignore it
Serial.println();
Serial.print(" opt1: ");
Serial.print(opt1);
Serial.print(" opt2: ");
Serial.print(opt2);
Serial.print(" opt3: ");
Serial.print(opt3);
Serial.print(" NUMBER: ");
Serial.print(complNum);
Serial.print(" letterCount: ");
Serial.print(letterCount);
Serial.print(" optCount: ");
Serial.print(optCount);
// How to reset arrays?
opt1[0] = (char)0;
opt2[0] = (char)0;
opt3[0] = (char)0;
num1=0;
num2=0;
complNum=0;
counter=1;
mySwitch=false;
numOfDec=0;
}
}
if (byteRead==44)
{
// Comma
optCount++;
// Debug stuff
Serial.println();
Serial.print("Virgola numero: ");
Serial.println(optCount);
letterCount = 0;
}
// Listen for a capital letter or a normal one
if ((byteRead>=65 && byteRead<=90) || (byteRead>=97 && byteRead<=122))
{
// Debug stuff
Serial.println();
Serial.print("lettera (Ascii value): ");
Serial.print(byteRead);
Serial.print(" ");
if (cmplx)
{
if (optCount==1 && letterCount<=3)
{
opt1[letterCount] = byteRead;
// Debug stuff
Serial.print("letterCount: ");
Serial.print(letterCount);
Serial.print("opt1: ");
Serial.print(opt1);
}
else if (optCount==2 && letterCount<=3)
{
opt2[letterCount] = byteRead;
// Debug stuff
Serial.print("letterCount: ");
Serial.print(letterCount);
Serial.print("opt2: ");
Serial.print(opt2);
}
else if (optCount==3 && letterCount<=2)
{
opt3[letterCount] = byteRead;
// Debug stuff
Serial.print("letterCount: ");
Serial.print(letterCount);
Serial.print("opt3: ");
Serial.print(opt3);
}
letterCount++;
}
}
//listen for numbers between 0-9
if(byteRead>47 && byteRead<58)
{
//number found
if (cmplx)
{
/* If mySwitch is true, then populate the num1 variable
otherwise populate the num2 variable*/
if(!mySwitch)
{
num1=(num1*10)+(byteRead-48);
}
else
{
num2=(num2*10)+(byteRead-48);
// Counters used to correctly store decimal numbers
counter=counter*10;
numOfDec++;
}
}
}
// Looks for decimal points
if (byteRead==46)
{
mySwitch=true;
}
}
}
Once the opt1, opt2 and opt3 arrays are correctly populated I can compare them with tags and then call the respective routine.
The problem
I'm pretty close, the code stores the decimal numbers correctly but it doesn't with the arrays. The output I get inserting this string
X,Rol,Con,Kd,1.12,X
is the following:
lettera: 88
virgola numero: 1
lettera: 82 letterCount: 0 opt1: R
lettera: 111 letterCount: 1 opt1: Ro
lettera: 108 letterCount: 2 opt1: Rol
virgola numero: 2
lettera: 67 letterCount: 0 opt2: C
lettera: 111 letterCount: 1 opt2: Co
lettera: 110 letterCount: 2 opt2: Con
virgola numero: 3
lettera: 75 letterCount: 0 opt3: K
lettera: 100 letterCount: 1 opt3: KdX,Rol,Con,Kd,1.12,X <- WTF?
virgola numero: 4
virgola numero: 5
opt1: RolConKdX,Rol,Con,Kd,1.12,X <- WTF?
opt2: ConKdX,Rol,Con,Kd,1.12,X <- WTF?
opt3: KdX,Rol,Con,Kd,1.12,X <- WTF?
NUMBER: 1.12
lettera: 88
How to reset arrays of chars quickly and why do I get arrays completely messed up?
Best Answer
I really think you ought to step back and re think the idea of doing this with strings, and consider designing a binary protocol. In addition to better organizing your code, you will end up with something that is less prone to error, results in shorter transmissions (and more compact code), and will better lend itself to future enhancements. Backward compatibility will also be hugely easier! For example, you could create a some simple structures like the below, which then could be easily placed or recovered from a send or receive buffer, through the use of structure pointers.
Again the above is just to illustrate an idea. By defining a header to start every message, you can now add things like source and destination addresses. That one little addition could allow your project to easily send and receive from multiple senders, or at least differentiate between them. The header would be placed first in the communication buffer, and filled using the pCtrlHdr pointer, as easily as this...
Then you could place your commands in the buffer (2 in this case) like this
Finally, you might want to add a CRC of the entire message, back in the header. This of course would require a separate CRC calculation function.
Now again this is just a simple example of what you might do with a binary protocol, and here are some of the advantages.
On that last point, in my previous life I wrote a ton of communication drivers over the years to recover data and perform control over many devices, over serial, radio, and network links, with several other coders. When there was a device with a binary protocol to be dealt with, it was always a pleasure to write the driver. But when it was an ASCII protocol, consisting of strings and decimal numbers, we would all cringe. The lengths we would have to go through to account for every possible error would consume much too much time, be difficult to fully test, and in the end would often fall short and require endless fixes. And just as bad, since it was ASCII (presumably to make it easier for a human to read) any manufacturer upgrade or change to the device would always cause unforeseen bugs, breaking not only our own driver code but the manufacturer's as well! :-) A binary protocol, with a little planning, will make your project much more friendly to maintain, and you will definitely thank yourself for going the extra mile when the project expands, or goes to a radio link! :-)