# I2C reading strategy: Software timer vs millis() recipe in loop()?

**URL:** <https://community.particle.io/t/i2c-reading-strategy-software-timer-vs-millis-recipe-in-loop/62813>\
**Category:** Firmware\
**Tags:** tracker\
**Created:** [June 27, 2022, 1:47pm UTC](https://community.particle.io/t/i2c-reading-strategy-software-timer-vs-millis-recipe-in-loop/62813 "2022-06-27T13:47:37Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![gusgonnet](https://sea2.discourse-cdn.com/flex026/user_avatar/community.particle.io/gusgonnet/32/27491_2.png) [@gusgonnet](https://community.particle.io/u/gusgonnet)\
**Post date:** [June 27, 2022, 1:47pm UTC](https://community.particle.io/t/i2c-reading-strategy-software-timer-vs-millis-recipe-in-loop/62813/1 "2022-06-27T13:47:37Z")

</div>

Hello everyone!

I am planning on using this board (below) for sampling a few analog sensors and monitoring 4 dry contacts with a Tracker One:

> **[8-Channel Analog to Digital Converter 12-Bit + 8-Channel Digital IO +...](https://store.ncd.io/product/8-channel-analog-to-digital-converter-12-bit-8-channel-digital-io-8-channel-open-collector-with-i2c-interface/)**
>
> This multi-function I2C device includes a MCP23017 configured for 8-Channels of Open Collector Output (Up to 100ma per Channel) and 8-Bit General Purpose Programmable Input and Outputs. Open Collector outputs can be used to directly control...

Regarding the analog sensors, I can read them once or twice a second with [a millis recipe on the loop() function](https://community.particle.io/t/millis-and-rollover-tutorial/20429). All good, the input signals I’m looking to measure don’t change rapidly.

Regarding the dry contacts, I’m worried about what can be done in the callback of [a software timer](https://docs.particle.io/reference/device-os/api/software-timers/software-timers/#software-timers).

Imagine I want to sample (or read the I2C register) 10 times per second.

If I go with the millis recipe, since the timing of it _may_ not be accurate, I’m not sure how to go about debouncing the inputs (let’s say for 300msec).

If I go with the software timer, I’m not sure if I2C calls made from the timer callback have the potential to create trouble in my or the tracker firmware.

Have you done something similar in the past?  
Do you have any thoughts?  
Please reply below.  
Thanks!  
Gustavo.

**EDIT:** I suspect I need an I2C level shifter between the tracker and this board since this board might use 5V I2C levels. To be confirmed…

---

<div class="post-metadata">

**Author:** ![rickkas7](https://sea2.discourse-cdn.com/flex026/user_avatar/community.particle.io/rickkas7/32/8681_2.png) [@rickkas7](https://community.particle.io/u/rickkas7)\
**Post date:** [June 27, 2022, 2:01pm UTC](https://community.particle.io/t/i2c-reading-strategy-software-timer-vs-millis-recipe-in-loop/62813/2 "2022-06-27T14:01:47Z")

</div>

The Tracker itself does not use the primary I2C interface (Wire or Wire3) so it won’t conflict, but if you also access I2C devices from your main application loop you’ll need to lock the interface (Wire.lock() and Wire.unlock()) in both the main loop code and the software timer code.

10 times per second will probably be OK with I2C.

The best solution would be to use interrupt mode of the MCP23017 but it doesn’t look like that board exposes the pin. It requires a pin from the MCP23008/MCP23017 to go to a GPIO input on the MCU, which allows you to check for any interrupt-enabled pin change without having to continuously poll by I2C.

---

<div class="post-metadata">

**Author:** ![gusgonnet](https://sea2.discourse-cdn.com/flex026/user_avatar/community.particle.io/gusgonnet/32/27491_2.png) [@gusgonnet](https://community.particle.io/u/gusgonnet)\
**Post date:** [June 27, 2022, 2:08pm UTC](https://community.particle.io/t/i2c-reading-strategy-software-timer-vs-millis-recipe-in-loop/62813/3 "2022-06-27T14:08:38Z")

</div>

Thank you for the tips. I'll pay attention to the lock()/unlock() mechanism so I stay out of trouble.

> [@rickkas7](#):
>
> The best solution would be to use interrupt mode of the MCP23017 but it doesn’t look like that board exposes the pin.

Yes, I read how you did it in one app note, but the pin seems not exposed on this board.

Thanks!

---

<div class="post-metadata">

**Author:** ![rickkas7](https://sea2.discourse-cdn.com/flex026/user_avatar/community.particle.io/rickkas7/32/8681_2.png) [@rickkas7](https://community.particle.io/u/rickkas7)\
**Post date:** [June 27, 2022, 3:20pm UTC](https://community.particle.io/t/i2c-reading-strategy-software-timer-vs-millis-recipe-in-loop/62813/4 "2022-06-27T15:20:07Z")

</div>

Sometimes you can get by without level-shifting I2C as long as the 5V I2C board you are using does not have on-board terminators, or you remove them. You instead add pull-ups to 3V3 off-board on SDA and SCL.

This works because most 5V I2C devices will treat 3.3V as logic high. And because I2C never drives the bus high - it only pulls it low with an open-collector driver, the SDA and SCL pins won’t exceed 3.3V.

If you do need level-shifting, primarily because you have a long I2C bus that really needs to run at 5V, you can use something like a PCA9306, or there’s a really clever technique that uses N-channel MOSFET transistors, one each on SDA and SCL. Adafruit likes to use this technique on their boards that have I2C level shifting.

---

<div class="post-metadata">

**Author:** ![rickkas7](https://sea2.discourse-cdn.com/flex026/user_avatar/community.particle.io/rickkas7/32/8681_2.png) [@rickkas7](https://community.particle.io/u/rickkas7)\
**Post date:** [June 27, 2022, 3:37pm UTC](https://community.particle.io/t/i2c-reading-strategy-software-timer-vs-millis-recipe-in-loop/62813/5 "2022-06-27T15:37:09Z")

</div>

It appears that you can use the interrupt on change mode on the MCP23008/MCP20017 without the hardware interrupt pin. Instead of polling the actual GPIO values hoping to catch when the GPIO changes, you poll the INTF register to see if an interrupt has occurred. This has the advantage of being able to catch a small pulse without having to poll very frequently.

---

<div class="post-metadata">

**Author:** ![gusgonnet](https://sea2.discourse-cdn.com/flex026/user_avatar/community.particle.io/gusgonnet/32/27491_2.png) [@gusgonnet](https://community.particle.io/u/gusgonnet)\
**Post date:** [June 27, 2022, 4:00pm UTC](https://community.particle.io/t/i2c-reading-strategy-software-timer-vs-millis-recipe-in-loop/62813/6 "2022-06-27T16:00:29Z")

</div>

> [@rickkas7](#):
>
> It appears that you can use the interrupt on change mode on the MCP23008/MCP20017 without the hardware interrupt pin.

interesting twist, thanks for the heads up!

---

<div class="post-metadata">

**Author:** ![gusgonnet](https://sea2.discourse-cdn.com/flex026/user_avatar/community.particle.io/gusgonnet/32/27491_2.png) [@gusgonnet](https://community.particle.io/u/gusgonnet)\
**Post date:** [August 26, 2022, 1:44am UTC](https://community.particle.io/t/i2c-reading-strategy-software-timer-vs-millis-recipe-in-loop/62813/7 "2022-08-26T01:44:23Z")

</div>

> [@rickkas7](#):
>
> You instead add pull-ups to 3V3 off-board on SDA and SCL.
> 
> This works because most 5V I2C devices will treat 3.3V as logic high. And because I2C never drives the bus high - it only pulls it low with an open-collector driver, the SDA and SCL pins won’t exceed 3.3V.

This was yet another INVALUABLE tip - thank you.

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/flex026/uploads/particle/original/3X/6/4/64635adccc35238caa19e9b553ec3e98b163d026.png) [@system](https://community.particle.io/u/system)\
**Post date:** [February 24, 2023, 1:44pm UTC](https://community.particle.io/t/i2c-reading-strategy-software-timer-vs-millis-recipe-in-loop/62813/8 "2023-02-24T13:44:55Z")

</div>

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