diff --git a/cores/esp32/HardwareI2C.h b/cores/esp32/HardwareI2C.h index 65b7e2036b2..c44f34e1ee7 100644 --- a/cores/esp32/HardwareI2C.h +++ b/cores/esp32/HardwareI2C.h @@ -20,6 +20,7 @@ #include #include "Stream.h" +#include class HardwareI2C : public Stream { public: @@ -36,6 +37,7 @@ class HardwareI2C : public Stream { virtual size_t requestFrom(uint8_t address, size_t len, bool stopBit) = 0; virtual size_t requestFrom(uint8_t address, size_t len) = 0; - virtual void onReceive(void (*)(int)) = 0; - virtual void onRequest(void (*)(void)) = 0; + // Update base class to use std::function + virtual void onReceive(const std::function &) = 0; + virtual void onRequest(const std::function &) = 0; }; diff --git a/docs/en/api/i2c.rst b/docs/en/api/i2c.rst index eac04b76a23..06d4d1953a6 100644 --- a/docs/en/api/i2c.rst +++ b/docs/en/api/i2c.rst @@ -347,20 +347,147 @@ This function will return ``true`` if the peripheral was initialized correctly. onReceive ^^^^^^^^^ -The ``onReceive`` function is used to define the callback for the data received from the master. +The ``onReceive`` function is used to define the callback for data received from the master device. .. code-block:: arduino - void onReceive( void (*)(int) ); + void onReceive(const std::function& callback); + +**Function Signature:** + +The callback function must have the signature ``void(int numBytes)`` where ``numBytes`` indicates how many bytes were received from the master. + +**Usage Examples:** + +.. code-block:: arduino + + // Method 1: Regular function + void handleReceive(int numBytes) { + Serial.printf("Received %d bytes: ", numBytes); + while (Wire.available()) { + char c = Wire.read(); + Serial.print(c); + } + Serial.println(); + } + Wire.onReceive(handleReceive); + + // Method 2: Lambda function + Wire.onReceive([](int numBytes) { + Serial.printf("Master sent %d bytes\n", numBytes); + while (Wire.available()) { + uint8_t data = Wire.read(); + // Process received data + Serial.printf("Data: 0x%02X\n", data); + } + }); + + // Method 3: Lambda with capture (for accessing variables) + int deviceId = 42; + Wire.onReceive([deviceId](int numBytes) { + Serial.printf("Device %d received %d bytes\n", deviceId, numBytes); + // Process data... + }); + + // Method 4: Using std::function variable + std::function receiveHandler = [](int bytes) { + Serial.printf("Handling %d received bytes\n", bytes); + }; + Wire.onReceive(receiveHandler); + + // Method 5: Class member function (using lambda wrapper) + class I2CDevice { + private: + int deviceAddress; + public: + I2CDevice(int addr) : deviceAddress(addr) {} + + void handleReceive(int numBytes) { + Serial.printf("Device 0x%02X received %d bytes\n", deviceAddress, numBytes); + } + + void setup() { + Wire.onReceive([this](int bytes) { + this->handleReceive(bytes); + }); + } + }; + +.. note:: + The ``onReceive`` callback is triggered when the I2C master sends data to this slave device. + Use ``Wire.available()`` and ``Wire.read()`` inside the callback to retrieve the received data. onRequest ^^^^^^^^^ -The ``onRequest`` function is used to define the callback for the data to be send to the master. +The ``onRequest`` function is used to define the callback for responding to master read requests. + +.. code-block:: arduino + + void onRequest(const std::function& callback); + +**Function Signature:** + +The callback function must have the signature ``void()`` with no parameters. This callback is triggered when the master requests data from this slave device. + +**Usage Examples:** .. code-block:: arduino - void onRequest( void (*)(void) ); + // Method 1: Regular function + void handleRequest() { + static int counter = 0; + Wire.printf("Response #%d", counter++); + } + Wire.onRequest(handleRequest); + + // Method 2: Lambda function + Wire.onRequest([]() { + // Send sensor data to master + int sensorValue = analogRead(A0); + Wire.write(sensorValue >> 8); // High byte + Wire.write(sensorValue & 0xFF); // Low byte + }); + + // Method 3: Lambda with capture (for accessing variables) + int deviceStatus = 1; + String deviceName = "Sensor1"; + Wire.onRequest([&deviceStatus, &deviceName]() { + Wire.write(deviceStatus); + Wire.write(deviceName.c_str(), deviceName.length()); + }); + + // Method 4: Using std::function variable + std::function requestHandler = []() { + Wire.write("Hello Master!"); + }; + Wire.onRequest(requestHandler); + + // Method 5: Class member function (using lambda wrapper) + class TemperatureSensor { + private: + float temperature; + public: + void updateTemperature() { + temperature = 25.5; // Read from actual sensor + } + + void sendTemperature() { + // Convert float to bytes and send + uint8_t* tempBytes = (uint8_t*)&temperature; + Wire.write(tempBytes, sizeof(float)); + } + + void setup() { + Wire.onRequest([this]() { + this->sendTemperature(); + }); + } + }; + +.. note:: + The ``onRequest`` callback is triggered when the I2C master requests data from this slave device. + Use ``Wire.write()`` inside the callback to send response data back to the master. slaveWrite ^^^^^^^^^^ diff --git a/libraries/Wire/examples/WireSlaveFunctionalCallback/WireSlaveFunctionalCallback.ino b/libraries/Wire/examples/WireSlaveFunctionalCallback/WireSlaveFunctionalCallback.ino new file mode 100644 index 00000000000..a18fd2f023e --- /dev/null +++ b/libraries/Wire/examples/WireSlaveFunctionalCallback/WireSlaveFunctionalCallback.ino @@ -0,0 +1,37 @@ +// This example demonstrates the use of functional callbacks with the Wire library +// for I2C slave communication. It shows how to handle requests and data reception + +#include "Wire.h" + +#define I2C_DEV_ADDR 0x55 + +uint32_t i = 0; + +void setup() { + Serial.begin(115200); + Serial.setDebugOutput(true); + + Wire.onRequest([]() { + Wire.print(i++); + Wire.print(" Packets."); + Serial.println("onRequest"); + }); + + Wire.onReceive([](int len) { + Serial.printf("onReceive[%d]: ", len); + while (Wire.available()) { + Serial.write(Wire.read()); + } + Serial.println(); + }); + + Wire.begin((uint8_t)I2C_DEV_ADDR); + +#if CONFIG_IDF_TARGET_ESP32 + char message[64]; + snprintf(message, 64, "%lu Packets.", i++); + Wire.slaveWrite((uint8_t *)message, strlen(message)); +#endif +} + +void loop() {} diff --git a/libraries/Wire/examples/WireSlaveFunctionalCallback/ci.json b/libraries/Wire/examples/WireSlaveFunctionalCallback/ci.json new file mode 100644 index 00000000000..3c877975d62 --- /dev/null +++ b/libraries/Wire/examples/WireSlaveFunctionalCallback/ci.json @@ -0,0 +1,5 @@ +{ + "requires": [ + "CONFIG_SOC_I2C_SUPPORT_SLAVE=y" + ] +} diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index 34c814b5117..cda098d2d5b 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -48,7 +48,7 @@ TwoWire::TwoWire(uint8_t bus_num) #endif #if SOC_I2C_SUPPORT_SLAVE , - is_slave(false), user_onRequest(NULL), user_onReceive(NULL) + is_slave(false), user_onRequest(nullptr), user_onReceive(nullptr) #endif /* SOC_I2C_SUPPORT_SLAVE */ { } @@ -596,14 +596,14 @@ void TwoWire::flush() { //i2cFlush(num); // cleanup } -void TwoWire::onReceive(void (*function)(int)) { +void TwoWire::onReceive(const std::function &function) { #if SOC_I2C_SUPPORT_SLAVE user_onReceive = function; #endif } // sets function called on slave read -void TwoWire::onRequest(void (*function)(void)) { +void TwoWire::onRequest(const std::function &function) { #if SOC_I2C_SUPPORT_SLAVE user_onRequest = function; #endif diff --git a/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h index b84aa5b2131..9cebdfaa304 100644 --- a/libraries/Wire/src/Wire.h +++ b/libraries/Wire/src/Wire.h @@ -48,10 +48,6 @@ #ifndef I2C_BUFFER_LENGTH #define I2C_BUFFER_LENGTH 128 // Default size, if none is set using Wire::setBuffersize(size_t) #endif -#if SOC_I2C_SUPPORT_SLAVE -typedef void (*user_onRequest)(void); -typedef void (*user_onReceive)(uint8_t *, int); -#endif /* SOC_I2C_SUPPORT_SLAVE */ class TwoWire : public HardwareI2C { protected: @@ -77,8 +73,8 @@ class TwoWire : public HardwareI2C { private: #if SOC_I2C_SUPPORT_SLAVE bool is_slave; - void (*user_onRequest)(void); - void (*user_onReceive)(int); + std::function user_onRequest; + std::function user_onReceive; static void onRequestService(uint8_t, void *); static void onReceiveService(uint8_t, uint8_t *, size_t, bool, void *); #endif /* SOC_I2C_SUPPORT_SLAVE */ @@ -116,8 +112,8 @@ class TwoWire : public HardwareI2C { size_t requestFrom(uint8_t address, size_t len, bool stopBit) override; size_t requestFrom(uint8_t address, size_t len) override; - void onReceive(void (*)(int)) override; - void onRequest(void (*)(void)) override; + void onReceive(const std::function &) override; + void onRequest(const std::function &) override; //call setPins() first, so that begin() can be called without arguments from libraries bool setPins(int sda, int scl);