I am using the following code to print an array of 4 values to the serial port as raw bytes.
unsigned int valOut; // print out the results from a strike
unsigned char cOut;
for (char i=0; i<MAXPADS; i++) {
valOut=(unsigned int)piezoSum[i];
// now put the output values into the lower 7 bits of 2 bytes, high part first.
cOut=((unsigned char)(valOut >> 7)) & 0x7f;
Serial.write(cOut);
//Serial.print(" ");
cOut=(unsigned char)valOut & 0x7f;
if (i==3) {
cOut|=0x80; // last character has the high bit set as a flag.
}
Serial.write(cOut);
//Serial.print(" ");
}
The idea is that each of the (integer) values in piezoSum[] is converted to two 7 bit bytes, with the top bit of the last byte in the sequence set as a marker. The problem I'm having is that sometimes the arduino program seems to output less bytes than it should - when the marked byte is detected by the program reading the port (in linux), there are less bytes in the buffer than there should be.
The problem is almost certainly with this bit of code - when I put it into debug mode which just prints the numbers as strings, it works fine. I've also tried just catting the port through the hexdump utility to see if there's a fault with the program that receives the bytes, and there is still the same problem.
That snippet prints two bytes every time through the loop. If you go through the loop four times, it prints a total of eight bytes. All of the bytes will have '0' as the most significant bit except for the last byte, which will have '1' as the MSB.
Make a test program that uses Serial.print and shows hex values to see if it is doing what you think it should.
#define MAXPADS 4
unsigned int piezoSum[MAXPADS] = {0x1234, 0x5678, 0x9abc, 0xdef0};
void setup()
{
Serial.begin(9600);
unsigned int valOut; // print out the results from a strike
unsigned char cOut;
for (int i = 0; i < MAXPADS; i++) {
valOut = piezoSum[i];
Serial.print("valOut = 0x");
Serial.print(valOut,HEX);Serial.print(" ==> 0x");
cOut = (valOut >> 7) & 0x7f;
Serial.print(cOut, HEX);
Serial.print(" 0x");
cOut = valOut & 0x7f;
if (i == MAXPADS-1) {
cOut|=0x80; // last character has the high bit set as a flag.
}
Serial.println(cOut, HEX);
}
}
void loop(){}
When you are sure that it gives the correct number of bytes (and the bytes have the correct values) then go back and eliminate the extra print stuff and use Serial.write(cOut) (or Serial.print(cOut,BYTE) or whatever) to put the binary values out to the port.
Regards,
Dave
Footnote:
I know that sometimes you have to use casts in C++ to keep the compiler happy, but when I see a bunch of superfluous casts sprinkled throughout the landscape, it makes me fraught. I'm wondering why you thought you needed them? Did the compiler give some kind of annoying messages that you wanted to suppress? Or what?
davekw7x:
That snippet prints two bytes every time through the loop. If you go through the loop four times, it prints a total of eight bytes. All of the bytes will have '0' as the most significant bit except for the last byte, which will have '1' as the MSB.
Make a test program that uses Serial.print and shows hex values to see if it is doing what you think it should.
davekw7x: Footnote:
I know that sometimes you have to use casts in C++ to keep the compiler happy, but when I see a bunch of superfluous casts sprinkled throughout the landscape, it makes me fraught. I'm wondering why you thought you needed them? Did the compiler give some kind of annoying messages that you wanted to suppress? Or what?
The only reason I put them in was because I wasn't sure how the C++ compiler deals with type conversion between signed and unsigned, and short and long, variables. I wondered if there might be some kind of overflow error going on. They don't seem to make any difference, so maybe I should take them out.
I've written some test code, and there is still a problem. This is the code I wrote:
#define MAXPADS 4
#define MINVAL 0
#define MAXVAL 127
#define MAXDEL 250
#define MINDEL 250
unsigned long piezoSum[MAXPADS] = {0x1234, 0x5678, 0x9abc, 0xdef0};
void setup() {
Serial.begin(19200);
}
void loop() {
unsigned int valOut; // print out the results from a strike
unsigned char cOut;
for (char i=0; i<MAXPADS; i++) {
piezoSum[i] = random(MINVAL,MAXVAL);
}
for (char i=0; i<MAXPADS; i++) {
valOut=piezoSum[i];
// now put the output values into the lower 7 bits of 2 bytes, high part first.
cOut=(valOut >> 7) & 0x7f;
Serial.write(cOut);
//Serial.print(" ");
cOut=valOut & 0x7f;
if (i==3) {
cOut|=0x80; // last character has the high bit set as a flag.
}
Serial.write(cOut);
//Serial.print(" ");
}
delay(random(MINDEL,MAXDEL));
}
I've tried various values for MAXVAL, MINVAL, MAXDEL, and MINDEL. The timing doesn't seem to make much difference, but the numbers being sent do make a difference - if MINVAL and MAXVAL are both zero then it runs fine, but with MINVAL=0 and MAXVAL=127, the number of bytes received before a mark byte (i.e. one with the high bit set) changes unpredictably. Below is a hexdump of the output with MINVAL=0, MAXVAL=127, and MINDEL=MAXDEL=250:
You can see from this that it is in a stable pattern with the mark byte second from the left of each block, and then shifts at around 0200 to another pattern with the mark byte at the left of the block.
I still can't work out why this isn't working - it's just meant to split the lower 14 bits of each value in piezoSum across two bytes, and send them out with a marker byte at the end of each block so the program interpreting it knows where it is in the sequence.
highfellow:
I've cracked the problem, and it seems to be a bug in the arduino Serial library. For some reason, serial.write() seems to fail on the number 3.
How are you reading the bytes on your PC? Is the port set up in "raw" mode? (0x03 is Ctrl-c and maybe your PC port does something special with Ctrl-C)
I tested the program with the usb serial set to raw mode (stty -F /dev/ttyUSB0 19200 raw), and it's working fine, so it was the ctrl-C thing that was causing the problem. Thanks to davekw7x for pointing this out!