gutierrezps / esp32_i2c_slave Goto Github PK
View Code? Open in Web Editor NEWI2C slave library for ESP32
License: GNU Lesser General Public License v2.1
I2C slave library for ESP32
License: GNU Lesser General Public License v2.1
Hi, I have a code with communication between two ESP-WROOM 32U 240MHz / 4MB Flash. During sending first 4-5 times everything is ok, but next I'm getting error "Max attempts" or "Slave not found". Master restart doesn't fix a problem, so I think It must be problem with slave (his restart fix problem).
It's my code: (a little chaotic, because I'm trying different things to repair this error)
Master:
`
void I2CFun(void * param) {
vTaskDelay(2000);
String message = "";
Serial.println("Wywoluje x.begin();");
x.setClock(10000);
bool status = (x.begin(16, 4));
vTaskDelay(2000);
scanI2C();
for (;;){
if (!status) {
Serial.println("Wywoluje x.begin();");
status = (x.begin(16, 4));
}
WireSlaveRequest slaveReq(x, 0x04, MAX_SLAVE_RESPONSE_LENGTH);
slaveReq.setRetryDelay(100);
slaveReq.setAttempts(255);
bool success = slaveReq.request();
String temp = "";
if (success) {
while (1 < slaveReq.available()) { // pętla przez wszystkie bajty oprócz ostatniego
char c = slaveReq.read(); // otrzymaj bajt jako znak
temp += c;
// wypisuje znak
}
Serial.println(temp);
saveData(temp);
deserializeJson(sensorsData, temp);
}
else {
Serial.println(slaveReq.lastStatusToString());
Serial.println("Wywoluje x.begin();");
I2C_ClearBus();
vTaskDelay(2000);
status = (x.begin(16, 4));
vTaskDelay(2000);
}
//deserializeJson(sensorsData, message);
vTaskDelay(1000*60*3);
}
I2CTask = NULL;
vTaskDelete(NULL);
}
`
and Slave:
`
void I2CMasterFun(void * param) {
unsigned long timeToBeginAgain = millis() + (1000 * 60 * 5);
vTaskDelay(1000);
bool status = (WireSlave.begin(13, 27, 0x04));
vTaskDelay(10);
if (!status) {
Serial.println("I2C Slave init failed");
while (1) delay(100);
}
else {
Serial.println("I2C Slave init OK");
}
WireSlave.onRequest(requestEvent);
for (;;) {
if (millis() >= timeToBeginAgain) {
I2C_ClearBus();
status = (WireSlave.begin(13, 27, 0x04));
WireSlave.onRequest(requestEvent);
WireSlave.clearWriteError();
WireSlave.flush();
Serial.print("PEEK: ");
Serial.println(WireSlave.peek());
timeToBeginAgain = millis() + (1000 * 60 * 2);
}
WireSlave.update();
vTaskDelay(5);
}
vTaskDelete(NULL);
}
`
Thank you for your library.
I used it in a project and stumbled upon an issue on high data rates.
I found two problems in the WireSlaveRequest.cpp
where memory is accessed in an array without range checking the index.
I solved the problem by changing the line while (unpacker.available())
in the bool WireSlaveRequest::request(uint8_t address)
function to while ((unpacker.available()) && (rxIndex_ < UNPACKER_BUFFER_LENGTH))
and line if (lastStatus_ == PACKET_READ && rxIndex_ < rxLength_)
in the int WireSlaveRequest::read()
function to if (lastStatus_ == PACKET_READ && rxIndex_ < rxLength_ && (rxIndex_ < UNPACKER_BUFFER_LENGTH))
I will create a pull request with this fix.
Hi folks,
I am trying to use this library to communicate between two esp32, one as master and one as slave, particularly master needs to write to slave here. I have verified the following items, but still no receive event is detected by the code, and the function that is assigned by "WireSlave.onReceive(receiveEvent);" does not get called at all.
I appreciate any help on this
I checked where the thread travels within the WireSlave.cpp code once the "update" function is called, and the result always goes through the condition of "nothing received" or "inputLen == 0".
I have verified with Saleae logic hardware and probing the i2c signal and it shows that master successfully writes the bytes, and also slave acknowledges the receipt of write setup and each byte of packet.
I used the unpacker in master when sending
This print never shows up, meaning that nothing is detected to be received by the code
void receiveEvent(int howMany)
{
#ifdef debug_prints
Serial.print(" receiveEvent ");
#endif
}
I am looking at how the WireSlave.onRequest() method is implemented. The function accepts a callback and I was hoping to pass some context variable to the (say an opaque pointer) callback. As the callback has no argument, the only way to pass a variable to it is through a global variable. It can be done, but this would be bad design practice, particularly in a multi-threaded environment.
There are three ways to fix this:
`
int readSlaveBuffer( uint8_t *data, size_t max_size, TickType_t ticks_to_wait=1 )
{
return i2c_slave_read_buffer(portNum, data, max_size, ticks_to_wait );
}
int writeSlaveBuffer( const uint8_t *data, size_t size, TickType_t ticks_to_wait=0, bool reset_fifo=true )
{
if( reset_fifo )
i2c_reset_tx_fifo(portNum);
return i2c_slave_write_buffer(portNum, data, size, ticks_to_wait);
}
`
Hello;
Thanks for the library, it works really well. In one of my projects, I need to communicate with stm32 to ı2c. I think I'm having problems with frequencies. I could not find a frequency arrangement in the library. Which frequency does it use for communication or can they change the frequency?
Thanks for your great library!
I am just curious how to send a large amount of data? Say 3~4k bytes?
Of course, I can split the 3~4k bytes payload into several 128-byte blocks, but not sure how to send the 2nd block..
Using WireSlave.write()
function, but it returns 0 if the buffer is full with 128 bytes.
Thanks!
TL;DR go to the end of issue
Hello,
Back on my project with raspberry pi as master and esp32 as slave.
In the last months everythings works well and was so great. I doing a break from this part of the project.
Today i back on it and i'm facing a strange behavior with the method WireSlave.onRequest(requestEvent);
When i send i2c request from master with the good data format the WireSlave.onReceive(receiveEvent); get the data but the
WireSlave.onRequest(requestEvent); never run and send nothing to the master....
This is my test sketch on esp32 side directly copied from this repo:
// WireSlave Sender
// by Gutierrez PS <https://github.com/gutierrezps>
// ESP32 I2C slave library: <https://github.com/gutierrezps/ESP32_I2C_Slave>
// based on the example by Nicholas Zambetti <http://www.zambetti.com>
// Demonstrates use of the WireSlave library for ESP32.
// Sends data as an I2C/TWI slave device; data is packed using WirePacker.
// In order to the slave send the data, an empty packet must
// be received first. This is internally done by the WireSlaveRequest class.
// The data is sent using WirePacker, also done internally by WireSlave.
// Refer to the "master_reader" example for use with this
#include <Arduino.h>
#include <Wire.h>
#include <WireSlave.h>
#define SDA_PIN 21
#define SCL_PIN 22
#define I2C_SLAVE_ADDR 0x60
byte reg;
int howManyBytesReceived;
void requestEvent();
void receiveEvent(int howMany);
// function that runs whenever the master sends an empty packet.
// this function is registered as an event, see setup().
// do not perform time-consuming tasks inside this function,
// do them elsewhere and simply read the data you wish to
// send inside here.
void requestEvent()
{
Serial.println(F("request event --> send to master"));
WireSlave.write(127);
}
// function that executes whenever a complete and valid packet
// is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany)
{
Serial.println(F("receive event --> receive from master"));
howManyBytesReceived = howMany;
Serial.print(howManyBytesReceived);
Serial.println(F(" bytes"));
reg = WireSlave.read();
Serial.println(reg);
}
void setup()
{
Serial.begin(115200);
bool res = WireSlave.begin(SDA_PIN, SCL_PIN, I2C_SLAVE_ADDR);
if (!res)
{
Serial.println("I2C slave init failed");
while (1)
delay(100);
}
WireSlave.onRequest(requestEvent);
WireSlave.onReceive(receiveEvent);
Serial.printf("Slave joined I2C bus with addr #%d\n", I2C_SLAVE_ADDR);
}
void loop()
{
// the slave response time is directly related to how often
// this update() method is called, so avoid using long delays
// inside loop(), and be careful with time-consuming tasks
WireSlave.update();
// let I2C and other ESP32 peripherals interrupts work
delay(1);
}
}
this is my raspberry pi side python code (including python classes from https://github.com/MkLHX/Raspberry_Pi_Master_for_ESP32_I2C_SLAVE for more readable i remove Packer, Unpacker and Crc8 classes from snippet:
from time import sleep
from Adafruit_PureIO.smbus import SMBus
from adafruit_extended_bus import ExtendedI2C as I2C
if "__main__" == __name__:
try:
i2c = I2C(1)
scan = i2c.scan()
print("I2c devices found: ", scan)
with SMBus(1) as smbus:
address = scan[0]
register = [244, 128, 2, 3]
num_of_bytes = 5
unpacked = None
with Packer() as packer:
packer.debug = True
packer.write(register[0])
packer.end()
packed = packer.read()
print("packed: ", packed)
smbus.write_bytes(address, bytearray(packed))
sleep(0.8) # let the bus process first write
raw = smbus.read_bytes(address, num_of_bytes)
with Unpacker() as unpacker:
unpacker.debug = True
unpacker.write(list(raw))
unpacked = unpacker.read()
print(unpacked)
except Exception as e:
print("ERROR: {}".format(e))
the anwser on esp32 is:
pio device monitor
--- Available filters and text transformations: colorize, debug, default,
direct, esp32_exception_decoder, hexlify, log2file, nocontrol, printable,
send_on_enter, time
--- More details at http://bit.ly/pio-monitor-filters
--- Miniterm on COM3 115200,8,N,1 ---
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
ets Jun 8 2016 00:22:57
rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:1044
load:0x40078000,len:10124
load:0x40080400,len:5828
entry 0x400806a8
�Slave joined I2C bus with addr #96
receive event --> receive from master
1 bytes
244
the answer on raspberry pi:
I2c devices found: [96]
Data to pack: 244
packed: [2, 5, 244, 21, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Data to unpack: [255, 255, 255, 255, 255]
ERROR: ERROR: Unpacker invalid start byte
We can see the device is found 0x60 = 96
the data to pack as waiting esp32 slave is 244 and formated bytearray is good with first, length, data, crc8, last parameters.
But the ESP32 doesn't return anything to the rpi so the unpacked instance crash.
So my question is why WireSlave.onRequest(requestEvent); seems to do nothing when i2c event is triggered?
And how to debug it ASAP? because my project need to be deliver next week and now nothing works.
Don't change anything juste take the code i wrote few months ago and re run it.
I see i used the latest release 0.0.3 seems to fix update blocking but maybe not (in my case)
how can i rollback to the v0.2.0 to test it?
EDIT: i download the archive of v0.2.0, put it in lib/ folder on pio project and my project rollback to works!
i think there is a bug between python raspberry and v0.3.0...
Hi, in the Wire library its possible to set the i2c frequency. I couldnt find it in yours, could you help me out?
I have been trying to get this library working in ESP32-Arduino Mega communication but no luck. The Arduino is sending few simple chars over I2C while the ESP32 should receive and display the chars but it is not working. I am using 0x04 as an address in both devices and ESP32 is acting as a master.
What could be the issue here?
Hi,
I am a long-time user of this library, so first I want to thank you for all your work to have I2C slave work on ESP32 all this time.
I understand that the ESP32-Arduino core has finally fixed the issue with native I2C slave support. I am not very sure how would I transition to it.
The fix isn't included in any release yet, should I wait for the next release? Once I have the latest Arduino core, do I just use the Wire library the same as on an Uno?
Thanks,
Hello, i'm working on python class to permit Raspberry as i2c Master with ESP32 slave. look #2
I have to convert the parcker and unpacker class into python code and i'm facing about this operation =>
ESP32_I2C_Slave/src/WirePacker.cpp
Line 63 in 1224da3
What is the goal of >buffer_ + 2< ? bytearray + 2 🤓
Why using packer and unpacker data workflow? is it the esp-idf layer who need this data format?
Hi,
I am trying to set up a network where an ESP32 development board will be the master & there will be 2 ESP32 slaves (dev modules) connected via SDA & SCL.
I uploaded the master_reveiver to the ESP32 dev board & slave_sender to the ESP32 dev module. Somehow, the master is not able to identify slave & keeps printing "slave not found".
Now I tried the same code, I uploaded the master_receiver to one ESP32 dev module & slave_sender to one ESP32 dev module, it worked. Is there any reason as to why the master ESP32 dev kit would not detect the slave?
Set the SLA pin as 23 & SDA as 22. Pull-up resistors connected on both slave boards (I am assuming the main esp32 dev kit doesn't require additional ones?)
Thanks! Any help would be appreciated!
Hi there!
I'm trying to use this library on ESP32 has i2c slave with a raspberry pi 3A+ has master.
I'm working on platformio (core: 5.0.1 / Home 3.3.0) framework arduino
here my platformio.ini:
[env:mhetesp32minikit]
platform = espressif32
board = mhetesp32minikit
framework = arduino
monitor_speed = 115200
upload_speed = 921600
[env]
lib_deps =
# Using a library name
ArduinoUniqueID
Board Identify
Adafruit NeoPixel
ESP32 I2C Slave
Bus connectivity is ok i can see ESP32 slave address when rpi is scanning. (tested with i2cdetect -y 1 and python I2C() =>scan() method).
But nothing happens when MASTER send i2c orders to the SLAVE.
My code is your examples code mixed.
#include <Arduino.h>
#include <Wire.h>
#include <WireSlave.h>
#define SDA_PIN 21
#define SCL_PIN 22
#define I2C_SLAVE_ADDR 0x14
void receiveEvent(int howMany);
void requestEvent();
void setup()
{
Serial.begin(115200);
bool success = WireSlave1.begin(SDA_PIN, SCL_PIN, I2C_SLAVE_ADDR);
if (!success) {
Serial.println("I2C slave init failed");
while(1) delay(100);
}
WireSlave1.onReceive(receiveEvent);
WireSlave1.onRequest(requestEvent);
}
void loop()
{
// the slave response time is directly related to how often
// this update() method is called, so avoid using long delays
// inside loop()
WireSlave1.update();
// let I2C and other ESP32 peripherals interrupts work
delay(1);
}
// function that executes whenever a complete and valid packet
// is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany)
{
while (1 < WireSlave1.available()) // loop through all but the last byte
{
char c = WireSlave1.read(); // receive byte as a character
Serial.print(c); // print the character
}
int x = WireSlave1.read(); // receive byte as an integer
Serial.println(x); // print the integer
}
void requestEvent()
{
static byte y = 0;
WireSlave1.print("y is ");
WireSlave1.write(y++);
}
My python script:
import time
from adafruit_extended_bus import ExtendedI2C as I2C
from Adafruit_PureIO import smbus
# scan i2c devices
i2c = I2C(bus_id=1)
print(i2c.scan())
address = 0x14
with smbus.SMBus(1) as smbus:
for i in range(0x00, 0x0A):
print("register read: %s" % i)
r = smbus.read_byte_data(address, i)
print(hex(r))
time.sleep(.3)
So what can i do to help? i need use esp32 has i2c slave ;-)
I have 2 UNO's talking with eachother with I2C. Both are standard in Slave_Receiver. But when one of them has data it will change to Master_Writer, send the 6 bytes data and the turning back to Slave_Receiver. This works great on 2 UNO's. But because I run out of memory on one UNO I exchanged it for an ESP32. And then came the problems. Compared both WIRE libraries and there is indeed some lack of programming. As for now I go testing with the library unless there is another solution for onRequest()/onReceive.
Hi Gabriel,
I'm using your library in my BBC Microbit & ESP32 board. I took your advice and have the BBC Microbit signalling a pin on the ESP32 to act as an interrupt. Everything is working well with sending text smaller than 16 characters in length. So my string
"G,192.168.1.170" works fine. However if the string is longer such as "G1,192.168.1.170", then the following error occurs.
**sent: G1,192.168.1.170
assertion "heap != NULL && "free() target pointer is outside heap areas"" failed: file "/home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/heap/heap_caps.c", line 267, function: heap_caps_free
abort() was called at PC 0x40103957 on core 1
Backtrace: 0x40093a90:0x3ffdf8c0 0x40093cc1:0x3ffdf8e0 0x40103957:0x3ffdf900 0x4008593f:0x3ffdf930 0x400867a9:0x3ffdf950 0x4000bec7:0x3ffdf970 0x40138b3d:0x3ffdf990 0x4013902e:0x3ffdf9b0 0x400d2ff7:0x3ffdf9d0 0x400d66d9:0x3ffdfa20 0x400d2e3f:0x3ffdfad0 0x4008ff0d:0x3ffdfb00
Rebooting...
ets Jun 8 2016 00:22:57**
Any thoughts on how to resolve this?
Thanks John in York (UK)
Describe the bug
Requesting some bytes from a slave is blocking the main loop for 3..6ms
In the implementation of slaveReq.request() is the delay function used in a while loop:
WireslaveRequest.cpp in Line 32:
// wait until slave fills its output buffer
delay(retryDelay_ * (attempts + 1));
The request is sent and then it stucks for some ms while waiting for the response.
Wouldn't it be possible to get things separated, sending the request, then going back to the main loop, doing a poll in every loop cycle and then read the response if available?
The actual implementation is killing my application which requires a fast CPU cycle.
To Reproduce
Use the sample code for master_reader and slave_sender
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.