// I2C slave
#include <Wire.h>

// The address on the I2C bus that we will assign to this device
constexpr int MY_ADDRESS = 0x33;

// Buffers for sending and receiving
uint8_t slaveTxData[4] = {0x00, 0x10, 0x22, 0x84};
uint8_t slaveRxData[8] = {0};

// Function for receiving data from the I2C bus
uint8_t slaveGetData(uint8_t *buf, uint8_t bufSize)
{
  // If there are bytes available for reading, read them in a loop
  uint8_t n = 0;
  while (Wire.available()) 
  {
    // Read from the bus - from the master device
    buf[n] = Wire.read();
    // If the entire buffer is filled, stop reading
    if (++n == bufSize)
      break;
  }
  // Return the number of received bytes
  return n;
}

// Function for sending data via I2C bus
uint8_t slaveSendData(uint8_t* data, uint8_t dataLen)
{
  return Wire.write(data, dataLen);
}

// A function that will be called if the master requests data
void requestEvent() 
{
  Serial.println("------------------------");
  Serial.println("Slave | Request event");

  // Send data
  uint8_t txBytesQty = slaveSendData(slaveTxData, sizeof(slaveTxData));
  // Show result
  if (txBytesQty == sizeof(slaveTxData))
    Serial.print("Slave | Bytes quantity sent: ");
  else
    Serial.print("Slave | Send ERROR! Bytes quantity sent: ");
  Serial.println(txBytesQty);

  slaveTxData[0] += 1; // Change the first byte for variety
}

// Function that will be called if the master sends data
void receiveEvent(int bytesNum)
{
  Serial.println("------------------------");
  Serial.println("Slave | Receive event");

  // Get new data
  uint8_t rxBytesQty = slaveGetData(slaveRxData, sizeof(slaveRxData));
  // Show result
  if (rxBytesQty == 0)
    Serial.println("Slave | Receive ERROR!");
  else
  {
    for (uint8_t i = 0; i < rxBytesQty; i++)
    {
      Serial.print("Slave | Received byte: 0x");
      Serial.println(slaveRxData[i], HEX);
    }
  }
}

// ----------------------------------------- //
void setup() 
{  
  Serial.begin(57600);
  while (!Serial);
  Serial.println("I2C slave start");  

  // Start working with I2C as a slave device with the address MY_ADDRESS
  Wire.begin(MY_ADDRESS);    
  // Register an event handler that will be called when data is requested by the master device
  Wire.onRequest(requestEvent);
  // Register an event handler that will be called after receiving data from the master device
  Wire.onReceive(receiveEvent);
}

void loop() 
{
  // Don't do anything in the loop, everything is implemented in the registered event handlers
}
