I2C Invalid reads

Creating a custom “communication protocol” between 2 particle devices via I2C, one acting as a slave(photon) and one as a master(argon), I was lead many times reading invalid values (value 0x31). Specifically, when I try to read from the master, and the slave lags behind I usually get this value (which is not present in the data that I send, but its not random as well). In conclussion I read invalid bytes when it seems that master can read, but actually the slave doesn’t send anything or writes slowly. Does anybody observed something similar?

Update: I did some more digging around. The slave address I was using is 0x18, which, as I understand, is shifted to the left (x2) and the last bit is (0 for write 1 for read). Thus when I try to read from the slave I send the 0x18 << 2 | 1 = 0x31. Tried with different address (0x19) and I read 0x33. So now it seams that the master reads back the read command from the I2C Bus instead of reading the data sent by the slave.

It would be good to see your codes (master/slave).

Or you could try this sample implementation

Argon as the I2C master, photon I2C slave , both devices have v1.1.0 firmware. This is not the actual code, but it seems to recreate the issue. The first call of the cloud function, would result in a valid output (“456789”) and the subsequent ones would be shifted by the address byte, thus printing (“145678”).

#include "application.h"
#include "stdarg.h"

#define MASTER 0
#define speed 100000
#define SLAVE_ADDRESS 0x18

#if MASTER

void setup() {
    
    Particle.function("sendData", sendData);
    Wire.setSpeed(speed);
    Wire.begin();
    
    Serial.begin(9600);
}


int sendData(String cmd) {
   for (int i = 0; i < 1; ++i) {
        uint8_t count = Wire.requestFrom(SLAVE_ADDRESS, 6);
        
        if (count == 0) {
            Serial.println("Timeout");
            delay(1000);
        }

        while(Wire.available()) {
            char c = Wire.read();
            Serial.print(c);
        }
        Serial.println();
    }
    return 0;
}


void loop() {
    Serial.println("Waiting in loop, master");
    delay(1000);
}


#else 


void requestEvent() {
    char * data = "456789";
    Wire.print(data);
}

void setup() {
    
    Wire.setSpeed(speed);
    Wire.begin(SLAVE_ADDRESS);
    
    Wire.onRequest(requestEvent);
    Serial.begin(9600);
}

void loop() {
    Serial.println("Executing loop");
    delay(1000);
}

#endif

Just a shot in the blue, how about replacing Wire.print(data) with Wire.write((uint8_t*)data, strlen(data))?
Does that change anything?

Nope, if I request to read 7 bytes the output would be "1456789". The send part is ok I guess, but where that 1 comes from?

Were you able to recreate it ?

I'm not talking about requesting seven bytes.

What I was thinking along (without actually testing or pondering long about) was that when you request six bytes but get seven then the "lingering" byte may be messing up your following transmission.

I haven't yet pulled out my devices to test it hands-on.

I mentioned (requesting 7 bytes) to emphasize on input shifting, from what is seems to be the i2c read cmd byte. The bug is reproducible using the code above (6 bytes send and read).

Now I have tested all options I could think of and it really seems to be a bug in the Gen3 device OS. When inverting the roles (Photon as master, Argon as slave) things work as expected.

I have filed a GitHub issue about this

2 Likes

Thanks