Low power interrupt on I2C

Hello everybody, please help me

I have a simple project where once per minute an ESP8266 requests some info from an Arduino NANO through I2C using Wire.h library on both.

The problem is, everything has to be powered from the battery. Normally, the Arduino would consume around 10mA (confirmed by me), which is unreasonable when my goal is for it to last around 1/2 of a year (6 months) on a 1600mAh lipo battery with a 5V boost converter. I tried using lowPower.h, that brought down the power consumption down enough, but then the interrupt set using Wire.onRequest on the Arduino slave completely stops working. I literally have 0 free pins on the ESP, with no easy way to free them up, so no external interrupts on D2/3. Is there a simple way around this? I need the Arduino power consumption to be => 0.2mA (200uA)

Cheers,
Adam Sindelar, CZ

Not enough information. Please post the code and a wiring diagram, following the forum rules.

OK, I am sorry for not posting the code.


Schematic (how I2C is connected):
GPIO1 ----- A4
GPIO3 ----- A5

Here is the Arduino code:

#include <Wire.h>
#include "LowPower.h"

void setup() {
 Wire.begin(8);                /* join i2c bus with address 8 */
 Wire.onReceive(receiveEvent); /* register receive event */
 Wire.onRequest(requestEvent); /* register request event */         /* start serial for debug */
}

void loop() {
    LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); //Line that does the incabability of the bus to respond
}

// function that executes whenever data is received from master
void receiveEvent(int howMany) {
delay(5);         /* to newline */
}

// function that executes whenever data is requested from master
void requestEvent() {
 Wire.write("6.4,73,92,0");  /*just a test of example variables */
 delay(5);
}

And the snippet of the ESP8266 receiving code (The problem is with the Arduino not sure why you would need it but fine)

Void updateTime gets called automatically.

#include <GxEPD.h>
#include <Fonts/FreeSans9pt7b.h>
#include "Fonts/digits69pt7b.h"
#include <ThreeWire.h>
#include <RtcDS1302.h>
#include <ESP8266WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <JSON_Decoder.h>
#include <OpenWeather.h>
#include <WiFiClientSecure.h>
#include <Time.h>
#include <Wire.h>

void Setup(){
Wire.begin(1, 3); //I do not use serial, so I repurposed the pins
}

void updateTime() {
      String final = "";
        Wire.requestFrom(8, 32); /* request & read data of size 32 from slave */
        //Serial.println("Reading");
        char all[32];
        int i = 0;
        while (!Wire.available())
        {
          
          }
        while (Wire.available()) {
          all[i] = Wire.read();
          if (all[i] == 255) {
            all[i] = '\0';
          } else {
            //Serial.println((char)all[i]);
            final = final + (char)all[i];
          }
          i++;
  
        }
        //Serial.println(final);
        currentTemp = getValue(final, ',', 0);
        currentHum = getValue(final, ',', 1);
        currentBat = getValue(final, ',', 2);
        currentBatState = getValue(final, ',', 3);
        final = "";
int in = currentBat.toInt();

if(in <= 5){
  batIcon = "f";
}else if (in > 5 and in <= 15){
  batIcon = "g";
}else if (in > 15 and in <= 40){
  batIcon = "h";
}else if (in > 40 and in <= 70){
  batIcon = "i";
}else if (in > 70 and in <= 90){
  batIcon = "j";
}else if (in > 90){
  batIcon = "k";
}else{
  batIcon = "G";
}

  displayTime = printTime(Rtc.GetDateTime());
  display.eraseDisplay();
  displayTime = printTime(Rtc.GetDateTime());
  display.drawPaged(drawAll);
}

I can't remember which pins on the nano can be used as a wake-up interrupt but I suspect if you were to connect the SCL pin (on the nano) to the relevant wake-up pin it would wake the nano.

That would almost certainly mean that the first I2C message would be lost while the nano was waking up - but it would not be a big deal to send the message again a short while later.

You would need to disable the wake-up interrupt when the nano wakes up so it would not be triggered by the other SCL pulses. Then re-enable the wake-up interrupt just before going to sleep.

...R

First off make sure to remove the delays from the receiveEvent and requestEvent functions. They are executing in an interrupt context and should be kept as short as possible.

The MCU on the Nano board has an I2C address match interrupt and the datasheet states that it is active during all sleep modes. Glancing at the source of the Wire library it also seems like it should be enabled and working.

What could be happening is that the MCU wakes up on the address match, the requestEvent function places the data in the output buffer. After the interrupt routine completes and returns the CPU continues execution from where it went to sleep and immediately enters sleep mode again. Since the only I2C interrupt that can be counts as a wakeup source is the address match, the interrupts required to empty the output buffer is never called, which could make the MCU appear to be non responsive.

As for how to perhaps fix the problem.

The Wire library doesn't appear to have any built in functionality to handle this scenario. What you could do was implement the flush function stubbed in the source. You could add a function to the Wire library to probe the output buffer.

But the easiest would probably be to just allow for "enough" time to pass before the MCU goes back to sleep.

@nicolajna YOU ARE A GENIUS!!!!
I figured out thanks to your advice let some time pass that 100 Microseconds = ~1char, so delayMicroseconds(1500); is around the minimum for my application, but I would recommend to anyone if he has the same problem, just use delay(500) and you will certainly be fine.

#include <Wire.h>
#include "LowPower.h"

void setup() {
  Wire.begin(8);                /* join i2c bus with address 8 */
  Wire.onReceive(receiveEvent); /* register receive event */
  Wire.onRequest(requestEvent); /* register request event */ 
}

void loop() {

  delayMicroseconds(1500); //ONE LINE THAT SOLVES IT ALL
//delay(500); //I would reccomend using this, but for me it is important that it is awake as short as possible, the delay 1500 Mics is just for my case.

  LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); //Now it can sleep forever

}

// just so that in a edge case it dosent crash
void receiveEvent(int howMany) {      
}

// function that executes whenever data is requested from master
void requestEvent() {

  Wire.write("6.4,73,92,0");  /*send string on request */

}

I don't know about that :p. But I'm glad I could help.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.