arduino-libraries / ArduinoECCX08

76 stars 49 forks source link

Interferences with other I2C components #22

Closed gpfister closed 3 years ago

gpfister commented 4 years ago

Hi,

I have a rather strange behaviour: as soon as it communicates with the ECC508 chip, the rest of the I2C devices stops.

It is running on an Arduino MKR WiFI 1010. I used several I2C components (a 4x20 LCD device, a couple of PCF8574 chip, a PN532 NFC reader and a EEPROM.

Many thanks in advance for your help.

facchinm commented 4 years ago

Hi @gpfister , the ECC508 goes into sleep mode frequently and the only way to wake it up is by asserting the SDA line for quite a few milliseconds; to do this, we set the I2C frequency to 100khz and send a 0x00 on the line (https://github.com/arduino-libraries/ArduinoECCX08/blob/master/src/ECCX08.cpp#L437-L454). Every function calls wakeup() implicitly but this may not be a great idea (it's probably better to ask for the signature and only wake it up in case there's no answer). Moreover, the library sets I2C speed at 1MHz, which could be unsupported by other sensors. @ubidefeo

ubidefeo commented 4 years ago

thank you @facchinm for looping me in @gpfister I'm looking for workarounds to get a few things running at peace with the ECCx08, and I had success in the past, but I'm rooting for a way to get it solved at the library level. I will squeeze our firmware engineers in the following days to help me put an end to this 🗡️

for now let me try and help you with a workflow that will get you out of this. can you share more details such as sequence of use of the sensors, maybe a sample of your code?

For instance when I have to use i2c devices while using IoT Cloud (which authenticates using the ECCx08) I use the IoT Cloud library's onConnect/onDisconnect events with callbacks in order to enable or disable i2c peripherals and avoid using them until a reconnection happens.

let's see if we can do this together while I bribe @facchinm and @aentinger with promises of baked good :D

gpfister commented 4 years ago

@ubidefeo,

Sharing code will be difficult, this is on a private repo for now, as I'm prototyping for a project. My app doesn't have sensor per say, more input/output devices. I've plugged a PN532 NFC reader, and 4x4 keypad (which is a on PCF8574 chip), a LCD device and a EEPROM. The Cloud IoT side is for passing on to the device configuration, receive back activity logs and monitoring various info. I'm using I2C because the MRK WiFi 1010 is limited on pins.

Now to be fair, it is the first time I'm doing this, so I don't have years of experience. Please pardon my inexperience.

When the device starts, it setups the various components, each time calling the corresponding begin() methods of the various libraries. Then it enters the loop() and from there a task scheduler checks up at different interval (e.g. every 10ms for the keypad, every 100ms for the PN532 NFC reader, then interacts with the various EEPROM, the LCD, ... . I'm adding now the Cloud IoT layer which would run every 5 to 15 minutes (TBD) to checks for configuration to process, and would send activity.

I use Platformio.org as IDE (on VS Code).

I hope it helps.

ubidefeo commented 4 years ago

@gpfister I use a PCF8574 as LCD controller quite often and pair it with IoT Cloud with no issue at all. You might need to rework your code "a little". Unless you have to continuously talk to the ECC you won't have any problem with other i2c peripherals if you properly orchestrate how they run within your program.

I've been meaning to create a tutorial for advanced IoT Cloud use cases but haven't exactly got a lot of time on my hands :(

Try the following, I know it might look like a lot but it's an excellent workaround :) Let me know if it works out. at the top of your sketch create a bool to tell your code wether or not it's "safe" to use i2c peripherals

bool i2cPeripheralsEnabled = false

in your setup add

void setup(){
  // arduino IoT setup code and anything else
  // init properties
  // Arduino IoT Cloud begin

  ArduinoCloud.addCallback(ArduinoIoTCloudEvent::CONNECT, onIoTConnect);
  ArduinoCloud.addCallback(ArduinoIoTCloudEvent::DISCONNECT, onIoTDisconnect);
  // you can also subscribe to a SYNC event which will be triggered when data is synced between board and cloud
  // any other setup code
  // do NOT initiate begin for your other i2c peripherals
  // at this point the ECCx08 library might scramble the bus to wake the device up
}

create these two functions in your sketch

void onIoTConnect(){
  // enable your other i2c devices
  Serial.println(">>> connected to Arduino IoT Cloud");
  Serial.println("enabling other i2c devices");
  // run the setup code (begin or else) for your other i2c devices
  i2cPeripheralsEnabled = true;
}
void onIoTDisconnect(){
  // disable your other i2c devices
  Serial.println(">>> disconnected to Arduino IoT Cloud");
  Serial.println("disabling other i2c devices");
  // if necessary call the end() method on your i2c device library or even get rid of them.
  // really it's on a peripheral basis
  i2cPeripheralsEnabled = false;
}

essentially you can add conditions to only communicate with your devices when a bool is set to true

void loop(){
  ArduinoCloud.update()
  if(i2cPeripheralsEnabled){
    // talk to your display
    // talk to your IO Expander
  }
  // any other loop operation
}
gpfister commented 4 years ago

This is what I had in mind... I'll rework my code and get back to you in a couple of days. Note: I'm using Google Cloud IoT Core.

ubidefeo commented 4 years ago

ok, never used that I am part of the IoT Cloud team and personally made sure we had connection/disconnection events in order to better manage workflows (our Arduino_ConnectionHandler has the same features), but can't speak for other libraries :)

gpfister commented 4 years ago

@ubidefeo, thanks I ended up using this approach, with a twist:I built a class that controls each I2C devices, separating the exclusive one (ECC508 in this case) and the non exclusive one. Then before using the ECC508 (mostly once an hour to sign a new JWT), it calls the begin(). Once done, it call the begin() on all other I2C device. Not really nice but for now it works.

ubidefeo commented 4 years ago

Happy to have been of help, @gpfister :)