2

ok, I feel like this should be simple, but I am at a loss

Simple code mock up to send 4 bytes(mimicking a float) over I2C from Arduino to rpi. I am trying to create a state machine so that I can request certain "registers" containing 4 bytes each. I send a byte from pi to arduino indicating what "register" I want, then read back 4 bytes. I was attemping to reset the state back to NO_STATE after the end of the switch statement, but it effects the bytes received by the pi, and I dont see how.

#include <Wire.h>
#define SLAVE_ADDRESS 0x04

//States
#define NO_STATE 0x00
#define GET_BAT_TEMP 0x03

byte state = NO_STATE;
void setup() {
  Serial.begin(9600);
  pinMode(13, OUTPUT);
  Wire.begin(SLAVE_ADDRESS);
  Wire.onReceive(receiveData);
  Wire.onRequest(sendData);
}

void loop() {
  delay(100);
}

// callback for received data
void receiveData(int byteCount){

  while(Wire.available()) {
    state = Wire.read();
  }
  Serial.print("Got:");
  Serial.println(state);
}

// callback for sending data
void sendData(){
  Serial.println("Sending...");
  byte b[4]={1,1,1,1};

  switch (state){
    case NO_STATE:
      //bad state   not sure if can return only byte, may need to return 4 zeros or somthing
      Wire.write(0);
      Wire.write(0);
      Wire.write(0);
      Wire.write(0);
      break;
    case GET_BAT_TEMP:
      //send battery temp
      Wire.write(3);
      Wire.write(3);
      Wire.write(3);
      Wire.write(3);
      break;
    default:
      Wire.write(9);
      Wire.write(9);
      Wire.write(9);
      Wire.write(9);
      //send back error code   
  }
  //reset state to none
  state=NO_STATE;

} 

with this code I receive [3,0,0,0]

if I comment out the line

//state=NO_STATE;

I then receive back [3,3,3,3]

which is what I would expect. But I do not see how reseting the state "after" all the bytes are sent would cause an issue

output from arduino shows entering that function only once, and sending the 4 bytes

Got:3
Sending...
Sending...
Sending...
Sending...
  • I give you +1 for a well written code, your debugging work and a well formed question. – user31481 Feb 13 '18 at 9:44
0

I'm not an expert, but I think I found why it is not working.

The main problem is that you are using always the same byte (0, 3 or 9) for debugging, so you are not noticing that it is not sending four bytes, but only one, and the function gets called four times. This is obvious however from the reply on the serial interface (+1 for adding it in the question, good work).

What you can do is to track how many bytes you sent, and send the successive one. After the bytes are all sent you can reset back the state. The code can be something like

//States
#define NO_STATE 0x00
#define GET_BAT_TEMP 0x03

byte sentData;
byte batteryLevel[4]={3,4,5,6};

byte state = NO_STATE;
void setup() {
    Serial.begin(9600);
    pinMode(13, OUTPUT);
    Wire.begin(SLAVE_ADDRESS);
    Wire.onReceive(receiveData);
    Wire.onRequest(sendData);
}

void loop() {
  // delay(100); // Useless, let it cycle at its max speed
}

// callback for received data
void receiveData(int byteCount){
    while(Wire.available()) {
        state = Wire.read();
        sentData = 0;
    }
    Serial.print("Got:");
    Serial.println(state);
}

// callback for sending data
void sendData(){
    Serial.println("Sending...");

    switch (state){
        case NO_STATE:
            //bad state   not sure if can return only byte, may need to return 4 zeros or somthing
            Wire.write(0);
            break;
        case GET_BAT_TEMP:
            //send battery temp
            Wire.write(batteryLevel[sentData]);
            if (sentData < 3)
                sentData++;
            else
                //reset state to none
                state=NO_STATE;
            break;
        default:
            Wire.write(9);
            //send back error code   
    }
} 

I haven't tested it, so forgive any bugs. But I'm sure you understood the main point.

Just some remarks: the delay is useless (you are not saving energy or other with it, so just remove it); the serial is ok for debugging, but I think you should remove it from the code you will use (not ok to stop the interrupts for such a long time).

And most important: I hope you considered the 5V vs 3.3V voltage problem on the I2C ;)

In any case, I second Look Alterno: good way to ask a question, with all the information needed.

EDIT: as pointed out in the comments, there exist a function to send all the data together. I'm not sure how it behaves in different conditions (e.g. when less bytes are requested), but if you trust the communication the function can be simplified in

// callback for sending data
void sendData(){
    Serial.println("Sending...");

    switch (state){
        case NO_STATE:
            //bad state   not sure if can return only byte, may need to return 4 zeros or somthing
            Wire.write(0);
            break;
        case GET_BAT_TEMP:
            //send battery temp
            Wire.write(batteryLevel, 4);
            state=NO_STATE;
            break;
        default:
            Wire.write(9);
            //send back error code   
    }
}

(and the sentData variable becomes useless)

  • 1
    You can use Wire.write(data, len) to send all 4 bytes at once. – user31481 Feb 13 '18 at 13:05
  • @LookAlterno I never used it, so I didn't know it existed ;) in any case the OP was using the single byte version, so I used it too... Thank you for the info, I'll update the answer with this – frarugi87 Feb 13 '18 at 15:17
  • 1
    It was not a criticism, just a collaboration. – user31481 Feb 13 '18 at 15:50
  • If the internal pullups on the arduino side are disabled, there is no reason to worry about the voltage mismatch correct? Arduino only pulls the SDA line low, letting the pi pull it up to rail voltage at 3.3V. – Chad G Feb 13 '18 at 16:18
  • I must have been really tired last night. Didn't put it together in my head that the interrupt was being triggered again before the remaining data was sent. I have not tired any of this yet, but will mark it correct as it seems right. Will edit it with working code once I am done. – Chad G Feb 13 '18 at 16:20

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.