Thursday, December 4, 2008

Passing large values between the Arduino to the TouchShield

(I've gotten a couple of questions about this, especially since I posted the Processing on a TouchShield post, so here's a little tutorial on how to pass large values, like integers between the Arduino and TouchShield ... it's a slightly modified version of what I posted on the arduino.cc forum.)

Communicating from the Arduino to the TouchShield is a bit of a manual process, largely because there are so many different ways someone might want to communicate that it's tough to plan for all of them ahead of time. It's easy to pass unsigned chars one at a time, because the serial.read() function grabs one char at a time, which is one byte. The catch is that integers (ints) are 2 bytes long, so if you try to just use serial.print() and serial.read(), it only sends one byte, or 8 bits, and then you lose the other part of your number!

So in order to send a full int from the arduino, you have to send each byte of the 2-byte integer separately, and then reconstruct it back into the full two-byte value. Chris figured out how to get this working in the Pin Visualizer project, and I've adapted it for the Processing graphics library code too.

On the TouchShield, I use this code:

int getValue = Serial.read();
getValue = (getValue < < 8) + Serial.read();

This first calls Serial.read() to get the high bit. The " < < 8" means bit shift the byte up (to the left) 8 bits. Then, it calls Serial.read() again to get the low byte. And finally it adds both together. It kind of looks like this:

First get byte 1, the high byte:
0000000010101010
Shift it left 8 bits
1010101000000000
Then get byte 2, the low byte:
11111111
Add that to the left-shifted byte:
1010101011111111

And on the Arduino, I'll use this code:

unsigned char lowByte, highByte;
unsigned int val;
//set val to something
lowByte = (unsigned char)val;
highByte = (unsigned char)(val > > 8);
mySerial.print(highByte);
delay(1);
mySerial.print(lowByte);
delay(1);

That kind of does the inverse behavior. First it take val, and sets lowByte to only the lower or rightmost 8 bits. Then, it sets highByte to the upper or leftmost 8 bits (by bitshifting it down 8 bits first). Then, it prints the high byte, followed by the low byte. I throw a delay(1) in between just to give the Arduino and TouchShield time to separate the two sends from each other.

So if val looks like:
1010101011111111
It sets lowByte to:
11111111
It then shifts val to the right by 8 bits to get:
0000000010101010
Which it then grabs only the lower 8 bits of:
10101010
Which get stored as highByte

Because I send highByte first, and the send lowByte, that means the TouchShield has to know that the first thing it gets will be the highByte. So that's why the first byte the TouchShield gets is shifted left by 8 bits... the "circle of life" is now complete :-)

Phew. That was really long... On the plus side, this also happens to be a convenient way to pass large values between two Arduinos over a two-wire Rx/Tx serial connection, or between the Arduino and a custom program on the PC. I know they tend not to be used in too many apps or sketches, but bit shifts really do come in handy sometimes. And if you still want some more help, just send me some code to look at, and I'll try my best!

5 comments:

Matt said...

Dave writes:

C doesn't guarantee left-to-right order
of operand evaluation. So even though
the getValue assignment --

..
On the TouchShield, I use this code:

int getValue = (Serial.read() < < 8) + Serial.read();

This first calls Serial.read() to
get the high bit. The " < < 8" means
bit shift the byte up (to the left)
..

-- will usually work, doing something
like changing optimization levels could
lead to the second Serial.read() being
evaluated before the first one. So it
has to be

int getValue = Serial.read();
getValue = (getValue<<8) + Serial.read();

or whatever, to be safe.


Thanks a lot - I've just updated the post!

Dan Thompson said...

why are you using mySerial.print(highByte);
instead of
Serial.print(highByte);

Is there a reason for this? Are you using some special library not shown in this example?

www.grozeaion.com said...

i have some problems transferring some integers from arduino to touch shield. I've put a problem here
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?board=swtrouble

"Serial comunication problem" by gvi70000

One more question. Can we use EEPROM.h to store some values on the slide? If yes, what are the restrictions ?

www.grozeaion.com said...

i have some problems transferring some integers from arduino to touch shield. I've put a problem on arduino forum

"Serial comunication problem" by gvi70000

One more question. Can we use EEPROM.h to store some values on the slide? If yes, what are the restrictions ?

www.grozeaion.com said...

i have some problems transferring some integers from arduino to touch shield. I've put a problem on arduino forum

"Serial comunication problem" by gvi70000

One more question. Can we use EEPROM.h to store some values on the slide? If yes, what are the restrictions ?