Skip to content

Commit bd037cc

Browse files
authored
feat(wire): std::functional Wire slave callback functions (#668)
1 parent 081f8ae commit bd037cc

File tree

4 files changed

+171
-22
lines changed

4 files changed

+171
-22
lines changed

cores/esp32/HardwareI2C.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include <inttypes.h>
2222
#include "Stream.h"
23+
#include <functional>
2324

2425
class HardwareI2C : public Stream {
2526
public:
@@ -36,6 +37,7 @@ class HardwareI2C : public Stream {
3637
virtual size_t requestFrom(uint8_t address, size_t len, bool stopBit) = 0;
3738
virtual size_t requestFrom(uint8_t address, size_t len) = 0;
3839

39-
virtual void onReceive(void (*)(int)) = 0;
40-
virtual void onRequest(void (*)(void)) = 0;
40+
// Update base class to use std::function
41+
virtual void onReceive(const std::function<void(int)> &) = 0;
42+
virtual void onRequest(const std::function<void()> &) = 0;
4143
};

docs/en/api/i2c.rst

Lines changed: 131 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -347,20 +347,147 @@ This function will return ``true`` if the peripheral was initialized correctly.
347347
onReceive
348348
^^^^^^^^^
349349

350-
The ``onReceive`` function is used to define the callback for the data received from the master.
350+
The ``onReceive`` function is used to define the callback for data received from the master device.
351351

352352
.. code-block:: arduino
353353
354-
void onReceive( void (*)(int) );
354+
void onReceive(const std::function<void(int)>& callback);
355+
356+
**Function Signature:**
357+
358+
The callback function must have the signature ``void(int numBytes)`` where ``numBytes`` indicates how many bytes were received from the master.
359+
360+
**Usage Examples:**
361+
362+
.. code-block:: arduino
363+
364+
// Method 1: Regular function
365+
void handleReceive(int numBytes) {
366+
Serial.printf("Received %d bytes: ", numBytes);
367+
while (Wire.available()) {
368+
char c = Wire.read();
369+
Serial.print(c);
370+
}
371+
Serial.println();
372+
}
373+
Wire.onReceive(handleReceive);
374+
375+
// Method 2: Lambda function
376+
Wire.onReceive([](int numBytes) {
377+
Serial.printf("Master sent %d bytes\n", numBytes);
378+
while (Wire.available()) {
379+
uint8_t data = Wire.read();
380+
// Process received data
381+
Serial.printf("Data: 0x%02X\n", data);
382+
}
383+
});
384+
385+
// Method 3: Lambda with capture (for accessing variables)
386+
int deviceId = 42;
387+
Wire.onReceive([deviceId](int numBytes) {
388+
Serial.printf("Device %d received %d bytes\n", deviceId, numBytes);
389+
// Process data...
390+
});
391+
392+
// Method 4: Using std::function variable
393+
std::function<void(int)> receiveHandler = [](int bytes) {
394+
Serial.printf("Handling %d received bytes\n", bytes);
395+
};
396+
Wire.onReceive(receiveHandler);
397+
398+
// Method 5: Class member function (using lambda wrapper)
399+
class I2CDevice {
400+
private:
401+
int deviceAddress;
402+
public:
403+
I2CDevice(int addr) : deviceAddress(addr) {}
404+
405+
void handleReceive(int numBytes) {
406+
Serial.printf("Device 0x%02X received %d bytes\n", deviceAddress, numBytes);
407+
}
408+
409+
void setup() {
410+
Wire.onReceive([this](int bytes) {
411+
this->handleReceive(bytes);
412+
});
413+
}
414+
};
415+
416+
.. note::
417+
The ``onReceive`` callback is triggered when the I2C master sends data to this slave device.
418+
Use ``Wire.available()`` and ``Wire.read()`` inside the callback to retrieve the received data.
355419

356420
onRequest
357421
^^^^^^^^^
358422

359-
The ``onRequest`` function is used to define the callback for the data to be send to the master.
423+
The ``onRequest`` function is used to define the callback for responding to master read requests.
424+
425+
.. code-block:: arduino
426+
427+
void onRequest(const std::function<void()>& callback);
428+
429+
**Function Signature:**
430+
431+
The callback function must have the signature ``void()`` with no parameters. This callback is triggered when the master requests data from this slave device.
432+
433+
**Usage Examples:**
360434

361435
.. code-block:: arduino
362436
363-
void onRequest( void (*)(void) );
437+
// Method 1: Regular function
438+
void handleRequest() {
439+
static int counter = 0;
440+
Wire.printf("Response #%d", counter++);
441+
}
442+
Wire.onRequest(handleRequest);
443+
444+
// Method 2: Lambda function
445+
Wire.onRequest([]() {
446+
// Send sensor data to master
447+
int sensorValue = analogRead(A0);
448+
Wire.write(sensorValue >> 8); // High byte
449+
Wire.write(sensorValue & 0xFF); // Low byte
450+
});
451+
452+
// Method 3: Lambda with capture (for accessing variables)
453+
int deviceStatus = 1;
454+
String deviceName = "Sensor1";
455+
Wire.onRequest([&deviceStatus, &deviceName]() {
456+
Wire.write(deviceStatus);
457+
Wire.write(deviceName.c_str(), deviceName.length());
458+
});
459+
460+
// Method 4: Using std::function variable
461+
std::function<void()> requestHandler = []() {
462+
Wire.write("Hello Master!");
463+
};
464+
Wire.onRequest(requestHandler);
465+
466+
// Method 5: Class member function (using lambda wrapper)
467+
class TemperatureSensor {
468+
private:
469+
float temperature;
470+
public:
471+
void updateTemperature() {
472+
temperature = 25.5; // Read from actual sensor
473+
}
474+
475+
void sendTemperature() {
476+
// Convert float to bytes and send
477+
uint8_t* tempBytes = (uint8_t*)&temperature;
478+
Wire.write(tempBytes, sizeof(float));
479+
}
480+
481+
void setup() {
482+
Wire.onRequest([this]() {
483+
this->sendTemperature();
484+
});
485+
}
486+
};
487+
488+
.. note::
489+
The ``onRequest`` callback is triggered when the I2C master requests data from this slave device.
490+
Use ``Wire.write()`` inside the callback to send response data back to the master.
364491

365492
slaveWrite
366493
^^^^^^^^^^

libraries/Wire/src/Wire.cpp

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ extern "C" {
3939
#include "Arduino.h"
4040

4141
TwoWire::TwoWire(uint8_t bus_num)
42-
: num(bus_num & 1), sda(-1), scl(-1), bufferSize(I2C_BUFFER_LENGTH) // default Wire Buffer Size
42+
: num(bus_num), sda(-1), scl(-1), bufferSize(I2C_BUFFER_LENGTH) // default Wire Buffer Size
4343
,
4444
rxBuffer(NULL), rxIndex(0), rxLength(0), txBuffer(NULL), txLength(0), txAddress(0), _timeOutMillis(50), nonStop(false)
4545
#if !CONFIG_DISABLE_HAL_LOCKS
@@ -48,7 +48,7 @@ TwoWire::TwoWire(uint8_t bus_num)
4848
#endif
4949
#if SOC_I2C_SUPPORT_SLAVE
5050
,
51-
is_slave(false), user_onRequest(NULL), user_onReceive(NULL)
51+
is_slave(false), user_onRequest(nullptr), user_onReceive(nullptr)
5252
#endif /* SOC_I2C_SUPPORT_SLAVE */
5353
{
5454
}
@@ -62,6 +62,10 @@ TwoWire::~TwoWire() {
6262
#endif
6363
}
6464

65+
uint8_t TwoWire::getBusNum() {
66+
return num;
67+
}
68+
6569
bool TwoWire::initPins(int sdaPin, int sclPin) {
6670
if (sdaPin < 0) { // default param passed
6771
if (num == 0) {
@@ -462,10 +466,11 @@ uint8_t TwoWire::endTransmission(bool sendStop) {
462466
nonStop = true;
463467
}
464468
switch (err) {
465-
case ESP_OK: return 0;
466-
case ESP_FAIL: return 2;
467-
case ESP_ERR_TIMEOUT: return 5;
468-
default: break;
469+
case ESP_OK: return 0;
470+
case ESP_FAIL: return 2;
471+
case ESP_ERR_NOT_FOUND: return 2;
472+
case ESP_ERR_TIMEOUT: return 5;
473+
default: break;
469474
}
470475
return 4;
471476
}
@@ -591,14 +596,14 @@ void TwoWire::flush() {
591596
//i2cFlush(num); // cleanup
592597
}
593598

594-
void TwoWire::onReceive(void (*function)(int)) {
599+
void TwoWire::onReceive(const std::function<void(int)> &function) {
595600
#if SOC_I2C_SUPPORT_SLAVE
596601
user_onReceive = function;
597602
#endif
598603
}
599604

600605
// sets function called on slave read
601-
void TwoWire::onRequest(void (*function)(void)) {
606+
void TwoWire::onRequest(const std::function<void()> &function) {
602607
#if SOC_I2C_SUPPORT_SLAVE
603608
user_onRequest = function;
604609
#endif
@@ -646,8 +651,16 @@ void TwoWire::onRequestService(uint8_t num, void *arg) {
646651
#endif /* SOC_I2C_SUPPORT_SLAVE */
647652

648653
TwoWire Wire = TwoWire(0);
654+
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)
655+
#if SOC_I2C_NUM > 1
656+
TwoWire Wire1 = TwoWire(1);
657+
#elif SOC_I2C_NUM > 2
658+
TwoWire Wire2 = TwoWire(2);
659+
#endif /* SOC_I2C_NUM */
660+
#else
649661
#if SOC_HP_I2C_NUM > 1
650662
TwoWire Wire1 = TwoWire(1);
651663
#endif /* SOC_HP_I2C_NUM */
664+
#endif
652665

653666
#endif /* SOC_I2C_SUPPORTED */

libraries/Wire/src/Wire.h

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
#include "soc/soc_caps.h"
3030
#if SOC_I2C_SUPPORTED
31+
#include "esp_idf_version.h"
3132

3233
#include <esp32-hal.h>
3334
#include <esp32-hal-log.h>
@@ -47,10 +48,6 @@
4748
#ifndef I2C_BUFFER_LENGTH
4849
#define I2C_BUFFER_LENGTH 128 // Default size, if none is set using Wire::setBuffersize(size_t)
4950
#endif
50-
#if SOC_I2C_SUPPORT_SLAVE
51-
typedef void (*user_onRequest)(void);
52-
typedef void (*user_onReceive)(uint8_t *, int);
53-
#endif /* SOC_I2C_SUPPORT_SLAVE */
5451

5552
class TwoWire : public HardwareI2C {
5653
protected:
@@ -76,8 +73,8 @@ class TwoWire : public HardwareI2C {
7673
private:
7774
#if SOC_I2C_SUPPORT_SLAVE
7875
bool is_slave;
79-
void (*user_onRequest)(void);
80-
void (*user_onReceive)(int);
76+
std::function<void()> user_onRequest;
77+
std::function<void(int)> user_onReceive;
8178
static void onRequestService(uint8_t, void *);
8279
static void onReceiveService(uint8_t, uint8_t *, size_t, bool, void *);
8380
#endif /* SOC_I2C_SUPPORT_SLAVE */
@@ -104,6 +101,8 @@ class TwoWire : public HardwareI2C {
104101

105102
bool end() override;
106103

104+
uint8_t getBusNum();
105+
107106
bool setClock(uint32_t freq) override;
108107

109108
void beginTransmission(uint8_t address) override;
@@ -113,8 +112,8 @@ class TwoWire : public HardwareI2C {
113112
size_t requestFrom(uint8_t address, size_t len, bool stopBit) override;
114113
size_t requestFrom(uint8_t address, size_t len) override;
115114

116-
void onReceive(void (*)(int)) override;
117-
void onRequest(void (*)(void)) override;
115+
void onReceive(const std::function<void(int)> &) override;
116+
void onRequest(const std::function<void()> &) override;
118117

119118
//call setPins() first, so that begin() can be called without arguments from libraries
120119
bool setPins(int sda, int scl);
@@ -144,9 +143,17 @@ class TwoWire : public HardwareI2C {
144143
};
145144

146145
extern TwoWire Wire;
146+
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)
147+
#if SOC_I2C_NUM > 1
148+
extern TwoWire Wire1;
149+
#elif SOC_I2C_NUM > 2
150+
extern TwoWire Wire2;
151+
#endif /* SOC_I2C_NUM */
152+
#else
147153
#if SOC_HP_I2C_NUM > 1
148154
extern TwoWire Wire1;
149155
#endif /* SOC_HP_I2C_NUM */
156+
#endif
150157

151158
#endif /* SOC_I2C_SUPPORTED */
152159
#endif /* TwoWire_h */

0 commit comments

Comments
 (0)