Hello everyone,
I faced some challenges when configuring I2C as a slave on ESPHome to communicate with other devices using an ESP32. After several trials and debugging, I managed to resolve the issues, and I wanted to share my experience in case it helps anyone else.
Problem:
In my project, I needed to configure ESPHome so that ESP32 would act as an I2C slave and receive data through the I2C protocol. However, I encountered several obstacles:
- Including the Wire library: I had to manually include the
Wire
library since ESPHome by default uses I2C as a master. - Configuring I2C as a slave: I had to avoid declaring I2C as a master by using the standard
i2c:
configuration in the YAML file. - Handling received data: I created a custom library to receive data using the
onReceive
method, as the available solutions didn’t fit my needs perfectly.
Solution:
I created a custom library and configured my YAML file to handle data reception through the onReceive
callback. Below, you can find both the code for the library and the ESPHome YAML configuration I used.
Custom Library Code (config/esphome/deauther_i2c_receiver.h
):
#include "esphome.h"
#include <Wire.h>
class MyI2CReceiver : public Component {
public:
MyI2CReceiver(text_sensor::TextSensor *sensor) {
this->sensor_ = sensor;
}
// Static function for the callback
static void receiveEventStatic(int numBytes) {
if (instance_ != nullptr) {
instance_->receiveEvent(numBytes); // Calls the non-static method
}
}
void receiveEvent(int numBytes) {
ESP_LOGD("custom", "%s", "receiving");
char message[32]; // Buffer for the message
int index = 0;
while (Wire.available() > 0 && index < sizeof(message) - 1) {
message[index] = Wire.read(); // Reads I2C data
index++;
}
message[index] = '\0'; // String terminator
if (strlen(message) > 0) {
ESP_LOGD("custom", "%s", message);
this->sensor_->publish_state(message); // Publishes the data via MQTT
}
}
void setup() override {
// Initialize I2C in slave mode
Wire.begin(0x08); // Slave address
Wire.onReceive(receiveEventStatic); // Callback for receiving data
instance_ = this; // Assign the current instance
}
void loop() override {}
protected:
uint8_t address_;
text_sensor::TextSensor *sensor_;
static MyI2CReceiver *instance_; // Static pointer to the class instance
};
// Define the static instance
MyI2CReceiver *MyI2CReceiver::instance_ = nullptr;
ESPHome YAML Configuration:
esphome:
name: deauth-detector
friendly_name: deauth_detector
includes:
- deauther_i2c_receiver.h # Custom file to include
libraries:
- "Wire"
esp32:
board: esp32-c3-devkitm-1
framework:
type: arduino
api:
encryption:
key: "YOUR_API_KEY"
ota:
password: "YOUR_OTA_PASSWORD"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
fast_connect: true
output_power: 20dB
mqtt:
broker: !secret mqtt_broker
username: !secret mqtt_user
password: !secret mqtt_password
sensor:
- platform: uptime
name: "Uptime Sensor"
text_sensor:
- platform: template
name: "I2C Sensor Data"
id: i2c_sensor_data
custom_component:
- lambda: |-
auto my_receiver = new MyI2CReceiver(id(i2c_sensor_data));
return {my_receiver};
logger:
level: DEBUG
Solution Details:
- Initializing I2C as Slave: I used
Wire.begin(0x08)
to set up the ESP32 as an I2C slave with the address0x08
. onReceive
Callback: The static functionreceiveEventStatic
handles the interaction between theWire
library and the data receiving method in my class.- Publishing Data via MQTT: After reading the data received via I2C, I publish it as a text sensor via MQTT using the
text_sensor
component.
I hope this solution helps anyone who needs to configure a device as an I2C slave with ESPHome.