2

I am using Python smbus write_word_data method to send int values from a Raspberry Pi to an Arduino using I²C.

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);
}
  • 2
    Aren'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 Jul 22 '14 at 17:45
2
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() and Wire.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 Jul 22 '14 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 Jul 23 '14 at 15:00
  • This likely won't work as op expects. Each write(byte) transmits in separate transactions. If the master calls this with requestFrom(), it'll only receive the first byte, and never receive the second. To send both bytes, you need to pass a byte array to write(). – Cerin Sep 20 '16 at 20:19
1

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 Jul 23 '14 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 Jul 23 '14 at 18:43
1

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.