I am using Python smbus write_word_data
method to send int values from a Raspberry Pi to an Arduino using I2C.
This seems to be the correct method.
When I send values from 0-255, I see the following in my Arduino serial monitor
0,0,0
0,1,0
0,2,0
...
0,255,0
Sending 256 I see:
0,0,1
For 257 I see:
0,1,1
Etc, etc...
My question is, how would I modify the following Arduino code to properly 'reconstitute' the int values, and then send them back to the RPi?
#include <Wire.h>
#define SLAVE_ADDRESS 0x04
int number = 0;
int state = 0;
bool newData = false;
void setup() {
pinMode(13, OUTPUT);
Serial.begin(9600);
// initialize i2c as slave
Wire.begin(SLAVE_ADDRESS);
// define callbacks for i2c communication
Wire.onReceive(receiveData);
Wire.onRequest(sendData);
Serial.println("Ready!");
}
void loop() {
Serial.println(receivedValue);
newData=false;
}
// callback for received data
void receiveData(int byteCount) {
while(Wire.available()) {
newData = true;
Wire.read();
if(Wire.available() == 2) {
receivedValue = Wire.read() | Wire.read() << 8;
}
}
}
// callback for sending data
void sendData() {
Wire.write(number);
}
-
2Aren't you really transferring these over i2c, not (asynchronous) serial as implied in the title, and using the serial more as a debug monitor to tell what is going on?Chris Stratton– Chris Stratton2014年07月22日 17:45:20 +00:00Commented Jul 22, 2014 at 17:45
3 Answers 3
void receiveData(int byteCount){
// Looks like your number is sent in a 3 byte packet
if (bytecount == 3)
{
// Discard first number as it is seemingly alway zero
Wire.read();
// Read low byte into rxnum
int rxnum = Wire.read();
// Read high byte into rxnum
rxnum += Wire.read() << 8;
// Send back
sendInt(rxnum);
}
}
void sendInt(int num){
// Uncomment below if you want so send back in same format
// Wire.write(0);
// Send low byte
Wire.write((uint8_t)num);
// Send high byte
num >>= 8;
Wire.write((uint8_t)num);
}
-
Just to clarify.
Wire.read()
andWire.write
only work of single bytes. To send a 2 byte int, you need to split the int into 2 bytes, send them individually, and then join them again at the receiving end.Gerben– Gerben2014年07月22日 20:45:58 +00:00Commented Jul 22, 2014 at 20:45 -
thanks!. I've edited my original code and it works, but how do I separate out my x,y coordinates in my main loop? Serial.println(receivedValue);Bachalo– Bachalo2014年07月23日 15:00:24 +00:00Commented Jul 23, 2014 at 15:00
-
This likely won't work as op expects. Each
write(byte)
transmits in separate transactions. If the master calls this withrequestFrom()
, it'll only receive the first byte, and never receive the second. To send both bytes, you need to pass a byte array towrite()
.Cerin– Cerin2016年09月20日 20:19:52 +00:00Commented Sep 20, 2016 at 20:19
Ir seems to me that there is something wrong with the Python Raspberry Pi code. What you show is 3 bytes of data, not 2. That's wrong.
If you're writing a 2 byte word, I would expect to see 2 bytes, not 3. You might want to post a question on the Raspberry Pi forum to get help resolving THAT problem first.
If you're stuck with the sending code you have, then here's what you would do.
Looking at your data, the first byte is always zero. You want to discard it.
The second byte is your low order byte, and your third byte of data is the high order byte. You need to do some juggling to get the data into the correct byte order, like this:
void readWordWithPadding()
{
if (Wire.available()<3)
{
newData = false;
return;
}
newData = true;
Wire.read(); //Throw away the filler byte
int lowByte = Wire.read();
int hiByte = Wire.read();
//Shift the high byte by 8 bits and add in the low byte
receivedValue = (hiByte << 8) + lowByte ;
return;
}
-
Thanks. The remaining problem is separating out my x,y values. On the sending side I call writeNumber(x) then writeNumber(y). So In my arduino code receivedValue alternates between x&y. But having a problem separating the 2.Bachalo– Bachalo2014年07月23日 15:38:24 +00:00Commented Jul 23, 2014 at 15:38
-
That is a design problem. You need to coordinate what is sent from the Pi with what you look for on the Arduino side. If you're always sending an X/Y pair, then you should write your Arduino code to look for an X/Y pair. Always read both numbers. You might need to adjust your code logic to make sure 4 bytes (or 6 bytes if you haven't been able to get rid of those spurious zero bytes) are available before starting to read.Duncan C– Duncan C2014年07月23日 18:43:46 +00:00Commented Jul 23, 2014 at 18:43
Unless (and until) your application needs high enough data rates that you need to make the most efficient use possible of your data channel (or you're talking to an inflexible hardware device that dictates the data protocol), you'll save yourself a lot of headaches by sending human-readable strings, something like:
x= 12345, y= -32
x= 432, y=-33221
You can reconstitute them at the other end with with atol()
, log them to a file, be able to understand them later, and see instantly whether the sender sent what you expected it to. Don't optimize until you have to; you may not have to.