-
Notifications
You must be signed in to change notification settings - Fork 5
Arduino in Target mode
Not all Arduino boards support the I2C Slave mode. The Arduino boards and compatible boards that support the I2C Slave mode might have issues. Not every combination of Master and Slave might work.
The I2C bus is not the best bus to transfer data between processors. Serial/UART communication is easier and often better.
In Slave mode the Arduino board can receive data or respond to a request.
If the Master sends data:
Wire.beginTransmission( i2c_slave_address);
Wire.write( data);
Wire.endTransmission();
then Slave can receive that data with the 'onReceive' handler.
void setup()
{
Wire.begin( i2c_slave_address);
Wire.onReceive( receiveEvent);
}
void receiveEvent(int howMany)
{
byte data = Wire.read();
}
The receiveEvent() function is called from a interrupt routine, therefor it should be as short and as fast as possible.
Since the I2C bus uses packages of data, the code is easier when the packages have a fixed length, for example a 'struct'. In the receiveEvent() function a check can be done if the right amount of data was received.
// Suppose the Master has send 4 bytes
void receiveEvent(int howMany)
{
byte data[4];
if( howMany == 4)
{
for( int i=0; i<4; i++)
{
data[i] = Wire.read();
}
}
}
To keep the receiveEvent() function as short as possible, the received data is often processed in the loop(). The data can be passed on to the loop() with a global buffer for the data and a 'bool' flag. Those variables should be 'volatile' to tell the compiler that the variables can be changed in a interrupt. The compiler will then adapt the optimizations for the code in the loop().
If the Master requests data:
Wire.requestFrom( i2c_slave_address, number_of_bytes);
then the Slave runs the 'onRequest' handler.
void setup()
{
Wire.begin( i2c_slave_address);
Wire.onRequest( requestEvent);
}
void requestEvent()
{
byte data = 0x55;
Wire.write( data);
}
The requestEvent() function is called from a interrupt routine, therefor it should be as short and as fast as possible.
While the requestEvent() function runs, the Slave keeps the SCL line low to put the Master on hold. That is called: clock pulse stretching.
The Master calls Wire.requestFrom() to request a certain amount of bytes. The Slave can return a number of bytes, but that does not have to be the same amount. It can be more or less than requested. The Master can not even tell if it did receive the right amount of bytes. That is the result of the I2C protocol. The return value of Wire.requestFrom() is not the number of bytes that the Slave has sent. The number is zero when there was a bus error.
I prefer to check if everything was okay, and then read all the data. The Master still does not know that the Slave has sent the right amount of bytes.
int n = Wire.requestFrom( i2c_slave_address, number_of_bytes);
if( n == number_of_bytes)
{
// read the bytes
}
The Arduino as a I2C Slave should respond as fast as possible. There are however libraries that turn off the interrupts for some time (Neopixel, FastLED, DHT, OneWire, and more) or use interrupts intensively themself (SoftwareSerial, VirtualWire, RadioHead in RH_ASK mode, and more). That will cause a problem if the there is a lot I2C communication.
When a Arduino board is set in Slave mode, it can be a Master as well. All the functions for the Master can be used. However, two Masters on the I2C bus is bound to go wrong. It is better to stay out of trouble and have only one Master on the I2C bus.
It is demanding when a basic 16MHz Arduino board is set as a I2C Slave. Even if the bus speed is only 100kHz and the onRequest and onReceive handlers are short and fast. When the Master continuously pounds the Slave with I2C sessions, then the code in the Slave Arduino will not run properly anymore. A delay of a few milliseconds between the I2C sessions is enough for the Slave to keep up.
When an Arduino is set in Slave mode, it could emulate a sensor or a EEPROM. It would even be possible to insert an Arduino board between a Master and a Slave and be a man-in-the-middle. In almost all cases this will not work. The existing Master must allow clock pulse stretching and there can be timing issues or many other problems.