gutierrezps / ESP32_I2C_Slave

I2C slave library for ESP32
GNU Lesser General Public License v2.1
81 stars 20 forks source link

WireSlave.onRequest(requestEvent); never run #12

Closed MkLHX closed 2 years ago

MkLHX commented 3 years ago

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...

gutierrezps commented 3 years ago

Hi @MkLHX . Starting at version 0.3.0, you must send an empty packet to trigger the onRequest callback. So it's just 4 bytes: [start = 0x02][length = 0x04][crc = 0x61][end = 0x04]. Check the triggerUpdate() method:

https://github.com/gutierrezps/ESP32_I2C_Slave/blob/9086f4a40f7fef9fcbf32d138e5d3dacfc46bd01/src/WireSlaveRequest.cpp#L114-L124

By the way, I think your CRC may be incorrect. From your example, your payload is 0xF4 = 244, so the CRC input is [0x05 0xF4]. Putting this on an online CRC8 calculator yields 0xEA = 234 (MAXIM algorithm), not 21 as it's on your debug output.

aldas commented 3 years ago

@gutierrezps is CRC8 calculation correct Maxim CRC8? I'm trying to communicate with raspberry but CRC seems way off to me and therefore i2c will not send responses as it discards packets that it thinks have wrong CRC.

  WirePacker packer;
  packer.write(1);
  packer.end();
  packer.printToSerial();

Output: length: 5, closed, buffer str: '...^.', buffer hex: 02 05 01 5E 04

https://crccalc.com/ gives me different results image

  WirePacker packer2;
  packer2.end();
  packer2.printToSerial();

Output: length: 4, closed, buffer str: '....', buffer hex: 02 04 00 04

image

I am using master branch. so 0.3.0

aldas commented 3 years ago

@gutierrezps I think another problem is that seed in never updated/set to new value at the end of method and therefore CRC is always calculated for last update call data and previous call CRC are disregarded

    uint8_t update(uint8_t *data, unsigned int length) {
        uint8_t crc = seed;
        uint8_t extract;
        uint8_t sum;

        for (unsigned int i = 0; i < length; i++) {
            extract = *data;

            for (char j = 8; j; j--) {
                sum = (crc ^ extract) & 0x01;
                crc >>= 1;
                if (sum) {
                    crc ^= 0x8C;
                }
                extract >>= 1;
            }

            data++;
        }
        seed = crc; // should be something like that
        return crc;
    }
gutierrezps commented 2 years ago

Unfortunately, I couldn't address this issue in a timely manner because I haven't been working with the ESP32 ever since. In this meantime, I2C Slave support has been added to the official ESP32 framework as you can see in the updated README of this repo. Therefore this library is no longer needed and its development will be ceased.