Ic2 error after lots of inactivity

I’m using circuit python to send data to notehub every 6 hours on a pico w connected over i2c to a notecarrier B. I seem to always get the following i2c lock error messages after I send my request. This does not happen if I send notes more frequently in say hour increments. Does anyone know what could be the cause? I tried another breadboard with new wires and different note carrier and pico and same result. Very confused…

i2c Error message

[Errno 5] Input/output error
Resetting Notecard I2C communications.
Failed to acquire I2C lock.

Code to reproduce

import time
import board
import digitalio
import notecard
import busio
from analogio import AnalogIn
import microcontroller
import watchdog
import os

report_INTERVAL_SEC = (6*60*60)
last_REPORT_SEND_TIME  = 0
# Boot timmer
bootTime = time.time()
port = busio.I2C(board.GP21, board.GP20)
card = notecard.OpenI2C(port, 0, 0, debug=True)
# NoteCard settings:
req = {"req": "hub.set"}
req["mode"] = "periodic"
req["outbound"] = 1
req["inbound"] = 1200
rsp = card.Transaction(req)


while True:  # main loop
    wdt.mode = watchdog.WatchDogMode.RESET
    wdt.feed()

    if (time.time()-last_REPORT_SEND_TIME > report_INTERVAL_SEC or last_REPORT_SEND_TIME == 0):
        try:
            uptime = time.time() - bootTime
            logOutput("publishing data...")
            req = {"req": "note.add"}
            req["file"] = "data.qo"
            req["sync"] = True
            req["body"] = {"uptime": uptime}
            rsp = card.Transaction(req)
            wdt.feed()
            if (rsp):
                      last_REPORT_SEND_TIME = time.time()  # update last time data sync was done
               
        except Exception as error:
            logOutput(error)

Doing some more testing now but this might have been caused by calling the following right after the rsp = card.Transaction(req) my original post. I remove it because I though I had tested without that before. If thats true it means you’d need to space out requests to the note card otherwise you’d overwhelm the i2c bus.

 req = {"req": "env.get"}
 rsp = card.Transaction(req)
1 Like

Never-mind the problem still occurs. Looks like I can reproduce it after just an hour of inactivity 100% of the time. If I send notes every 30 minutes things work without issue.
Using the Pico w I have tied both I2C buses and same result. I’ve changed wires, tried alternative picos and note carrier Bs - nothing resolves the I2C locking issue. Anyone have any ideas why the I2C bus would lock after a certain amount of time on the notecard?

import time
import board
import digitalio
import notecard
import busio
import microcontroller
import watchdog
import os
# Watchdog settings
wdt = microcontroller.watchdog
wdt.timeout = 8  # Set a timeout of the watchdog , max 8 seconds


# Boot timmer
bootTime = time.time()

# Note Card Settings
productUID = "com.xxxxx"
port = busio.I2C(board.GP21, board.GP20)
card = notecard.OpenI2C(port, 0, 0, debug=True)


# LED Light Settings
led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT

# Pin to detect USB Power
powerPin = digitalio.DigitalInOut(board.GP24)

# Sync outbound notes to Notehub a max of every 1 minutes if notes are waiting to go out.
outbound_SYNC_MINS = 1
# Read the sensor every 80 seconds.
sensor_READ_INTERVAL_SEC = (80)
# Publish outbound notes every 6 hours 
report_INTERVAL_SEC = (60*60)
# reboot if havent sent report in 7 hours
missing_INTERVAL_SEC = (7*60*60)
# Sync inbound notes from Notehub every 20 hours
inbound_SYNC_MINS = 1200
last_REPORT_SEND_TIME = 0

# initialize notecard
req = {"req": "hub.set"}
req["product"] = productUID
req["mode"] = "periodic"
req["outbound"] = outbound_SYNC_MINS
req["inbound"] = inbound_SYNC_MINS
rsp = card.Transaction(req)


#logging function
def logOutput(log):
    wdt.feed()
    try:
        #delete crash log if > 99KB
        fileTup=os.stat('_crash.log')
        if fileTup[6]>599000:
            os.remove("_crash.log")
        #write new log message
        file = open("_crash.log", "a")
        #file.write(log+ "..."+str(time.time())+"\n")
        print(log+ "..."+str(time.time())+"\n")
        file.close()
    except Exception as error:
        print('Cannot log message', error)



def publishSystemData(reason, action):
    global last_REPORT_SEND_TIME
    last_REPORT_SEND_TIME = time.time()  # update last time data sync was done


    try:
        logOutput("publishSystemData started")
        # Calculate Uptime
        uptime = time.time() - bootTime
        # add in other data to send
        req = {"req": "note.add"}
        req["file"] = "data.qo"
        req["sync"] = True
        req["body"] = {
                       "uptime": uptime, "reason": reason,}
        rsp = card.Transaction(req)
  
           
    except Exception as error:
        logOutput('Cannot Connect to HUB'+error)
    finally:
        
        logOutput("publishSystemData finished")



while True:  # main loop
    wdt.mode = watchdog.WatchDogMode.RESET
    wdt.feed()
    time.sleep(6)
    wdt.feed()
    time.sleep(6)
    wdt.feed()


    if (time.time()-last_REPORT_SEND_TIME > report_INTERVAL_SEC or last_REPORT_SEND_TIME == 0):
         publishSystemData("normal publish, lastsend: " +str(last_REPORT_SEND_TIME), "")
       

         
         
        
    
  



Hi @bkauf,

Can you please try this code without the watchdog code and see if you can still replicate the error? Our suspicion is that’s the root cause but I’d like to have some validation of that.

Thanks,
Rob

Hi Rob,

I’ve since updated my notecard firmware from 5.3.1.16292 to 6.1.1.16332 and it appears the I2C blocking error is gone. I’ve had the IoT device running now for over 24 hours with updates every 6 hours without issue. Do the developer versions of the notecard firmware have issues with the watchdog timers? How would you best implement a watchdog timer on the MCU when you only need to send notes out every 6 hours 99% of the time?

Thank you!

Brian

Hi @bkauf,

It’s a good question, but there are no differences in underlying functionality between the developer and LTS editions of the firmware. A watchdog timer with an 8 second expiration is probably too short when using the Notecard. The Notecard transaction timeout in note-python is 30 seconds. That means a transaction can take up to 30 seconds to complete (or fail). Your watchdog might be expiring in the middle of that, and whatever happens from there isn’t unlocking the I2C bus, and then it’s locked “forever” from the perspective of the firmware.

Rob

Hi Rob,

Thank you for jumping in here. 8 seconds is unfortunately the max watchdog timer for circuit python as I understand it.

Printing the timestamps before and after I send a note I see the python code returns in a second or less from both sending a note and getting the env vars. This is done via a main “publishSystemData” function that if successful calls the “genEnvFromHub” function so I’d assume the watchdog is not firing before the lockup occurs however it is indeed firing which makes this difficult to track down. I’d absolutely need a watchdog timer in my implementation.

req = {"req": "note.add"}
rsp = card.Transaction(req)

and issuing a

 req = {"req": "env.get"}
 rsp = card.Transaction(req)

Console log below

publishSystemData started…1704747488

Using transaction timeout of 30 seconds.
{“req”:“note.add”,“body”:{},“sync”:true,“file”:“data.qo”,“crc”:“0001:3084cd9b”}
{‘crc’: ‘0001:15CB2833’, ‘total’: 1}
getEnvFromHub started…1704747489

Using transaction timeout of 30 seconds.
{“req”:“env.get”,“crc”:“0002:a0700fd4”}
{‘time’: 1704765060, ‘body’: {}, ‘crc’: ‘0002:5685162F’}

getEnvFromHub finished…1704747489

publishSystemData finished…1704747489

Hi @bkauf,

I’m still thinking of possible solutions for you. I’m curious if you’ve ever measured the wireless connection with a call to card.wireless? Also which Notecard are you using specifically?

My theory is that if you’re working on a poor connection (e.g. “1 bar”) you may have inconsistent connectivity and every once in a while you’ll have a significant delay on ascertaining a connection.

Rob

Hi Rob, thank you for your time on this. Bars are between 2-3. I haven’t seen the issue since upgrading the notecards to 6.1 on 2 devices now running for the last week without issue, very strange. I’m using notecard cellular and notecarrier B. On this topic, what would be the best way to determine if there is connectivity to notehub from the MCU? If I check

req = {"req": "card.wireless"}
   rsp = card.Transaction(req)

I see the status sign when I first turn the notecard on is "cell-registration-wait

{‘crc’: ‘0009:BAF00351’, ‘status’: ‘{cell-registration-wait}’, ’ …}}

Then after it connects I can see a bars count and “network-up”

{'crc': '000C:27B332AB', , 'bars': 3, 'status': '{network-up}'...}

The status seems to only change when the modem is on then goes back to “modem off”. Is there a good status to key off to determine if the last sync to the notehub was successful? I’m using periodic mode and only calling note hub every few hours, I’d want to know if a call to notehub failed so I can flash an LED but not sure of the best way to do that if the device doesn’t throw an error like I saw with the I2C bus originally.

Thank you!

Brian

Thank you!

If you look at the hub.sync.status API, you’ll see there is a completed field that shows you the number of seconds since the last successful sync. There are some other fields in that request that may help as well.

Rob

Thank you this is exactly what I needed!

1 Like