How to use an UNO as i2c slave so that it is able to send different messages

Hey guys,

sry if that topic is already open somewhere but if so then I haven’t found it…

I am a little confused on how to programm my UNO as an i2c slave.

My setup is that I have a Raspberry Pi (Master) and some sensors and actors which are all connected via the i2c bus and so should be the UNO. My major problem is that I want the UNO to do different things on different messages and also read some calculated values from the UNO but only on request.

The normal message setup for reading out a sensor is “ADD, REG” and the slave responses with its “DATA” but in my understanding the UNO is just able to respond to its address but I cant give it any additional information on what “register” I want to read.

Here is my code so far as I wanted to test the mechanism but it doesn’t work out the way I want it to:

#include <Wire.h>

#define I2C_ADDRESS 0x08

void setup() {
  Wire.begin(I2C_ADDRESS);  // i2c Bus started as Slave
  Serial.begin(9600);
  Wire.onRequest(requestEvent);
}

void loop() {
  delay(100);
}

void requestEvent() {
  char c = Wire.read();
  Serial.print(c);
  Serial.println("test");
  switch(c){
    case 1:
      Wire.write(0x05);
      break;
    case 0x02:
      Wire.write(0xA0);
      break;
    default:
      Wire.write(0xAA);
      break;
  }
}

Thank you all for your time and I hope somebody can help me out here :slight_smile:
Cheers
Thomas

Ok I guess I have found a working solution on my own by trying a little longer and clicking even more links on google :slight_smile:

Looks like every i2c transaction with the UNO is handled with Wire.onReceive(receiveEvent) and just the reading actions are also handled by Wire.onRequest(requestEvent). In this case you can send the address and a few more bytes and read them out in your receiveEvent subroutine and react on it in your requestEvent subroutine if any information should be sent to the i2c master.

Here is a little piece of code that works and should pretty much explain what I was trying to tell above:

#include <Wire.h>

#define I2C_ADDRESS 0x08

char c;

void setup() {
  Wire.begin(I2C_ADDRESS);  // i2c Bus started as Slave
  Serial.begin(9600);
  Wire.onRequest(requestEvent);
  Wire.onReceive(receiveEvent);
}

void loop() {
  //delay(100);
  if (c != 0) {
    Serial.println("awesome");
    
  }
}

void receiveEvent(int howMany){
  c = Wire.read();
  Serial.println("test");
}

void requestEvent() {
  switch(c){
    case 1:
      Wire.write(0x05);
      c = 0;
      break;
    case 0x02:
      Wire.write(0xA0);
      c = 0;
      break;
    default:
      Wire.write(0xAA);
      c = 0;
      break;
  c = 0;
  }
}

My suggestion would be after you debug this code, remove any Serial output from the I2C interrupt functions or, if absolutely necessary to have Serial output on the slave, kick your Serial speed up a lot, say 115200.