0
\$\begingroup\$

I'm looking for a way to prevent my Arduino Nano from freezing when my I2C device (TBP-H08) loses power.

I have error handling built into the program and if the SDA or SCL lines are disconnected the Arduino functions as expected and reports "check i2c connection" and carries on with its other duties. The issue I'm experiencing is that if the power or ground for the TBP-H08 is disconnected the Arduino totally freezes and needs a manual reset to continue. I've tried adding a reset function to the I2C "wire" to no avail.

\$\endgroup\$
7
  • \$\begingroup\$ You forgot to ask an answerable question. And if your Arduino works like that, then it does. There are so many different Arduino hardware platforms and I2C libraries for each platform, and they may not handle unexpected things happening on the bus, as I2C is not hot pluggable bus. Those are the downsides of using ready made libraries, they don't necessarily work like you want. You may have better luck on Arduino.SE, but remember to ask a question. \$\endgroup\$
    – Justme
    Commented Mar 7, 2023 at 21:37
  • \$\begingroup\$ Is your I2C device powered by the Arduino, or might it have is own power supply? Try adding a small resistor on the power line, and capacitors at the Arduino as well as the TBP-H08. You might want to add a link to a datasheet or app note. \$\endgroup\$
    – PStechPaul
    Commented Mar 7, 2023 at 21:37
  • \$\begingroup\$ @justme i guess my point/question was that it seems odd to me that if the SDA/SCL lines are compromised/totally disconnected the Arduino handles it fine. Why would the 5V or GND (from the arduino @PStechPaul) to the sensor cause the arduino to totally freeze? \$\endgroup\$
    – bdmcnamara
    Commented Mar 7, 2023 at 21:53
  • \$\begingroup\$ The answer to that is likely in the I2C library source code, and the driver handles a normally working bus where no chips are present, but it can't handle a bus where communication is impossible due to an unpowered chip. It's not a least bit odd for a simple driver to expect that the bus is not jammed by an unpowered chip. \$\endgroup\$
    – Justme
    Commented Mar 7, 2023 at 21:59
  • 2
    \$\begingroup\$ @PStechPaul I2C is an open drain bus, typical maximum via pull-up resistor is 3 or 6 mA depending on standard. There is no way even a dead short could pull too much current, as that's how chips communicate on bus too. \$\endgroup\$
    – Justme
    Commented Mar 7, 2023 at 23:43

1 Answer 1

2
\$\begingroup\$

You will need to know how the arduino I2C function works in order to understand where the problem is coming from.

As someone who has written the I2C implementation using registers (via Atmel studio) I can tell you than most probably, the issue is that the arduino sends a signal on the I2C line and awaits response inside a while() function. It never received the response so it is stuck there waiting forever. You will have to find a way to reach the registers using Arduino IDE, I dont know how easy of feasable that is, you might need to change your I2C library as well.

For example a simple function to read data from TWI (which probably arduino uses) is:

//Read from the TWI, Read result is in TWDR.
uint8_t TWI_Read(uint8_t CheckReadStatus)
{
    //Enable TWINT(make it 0) to execute command, Data byte will be received and ACK will be returned
    TWCR = (1 << TWINT) | (1 << TWEA)| (1 << TWEN);
    //waiting for all data to be read
    //while TWINT =0 do nothing. the TWINT will be 1 when start is done.
    while((TWCR&(0B10000000))==0); //Arduino's code probably stucks here
}

My solution to this, is to utilize one of arduino's timers. For example I set the timer (timer2 for example here) and then on timer's interrupt routine I just count to a number, say 100 here, and it will reset itself as well (basically use a variable to count time):

/*
For TWI timer check
*/
ISR  (TIMER2_OVF_vect)
{
    //static uint8_t t2ovf = 0;
    if(twiTimerStart)//if we want to start its timer
    {
        twiTimerStart++;//increase it
        
        if(twiTimerStart >= 100)//if it reaches 100
        {
            twiTimerStart = 0;//make it zero again
        }
    }
}

And then I modified the read function to also check on that variable and do ot wait forever:

//Read from the TWI, Read result is in TWDR.
uint8_t TWI_Read(uint8_t CheckReadStatus)
{
    //Enable TWINT(make it 0) to execute command, Data byte will be received and ACK will be returned
    TWCR = (1 << TWINT) | (1 << TWEA)| (1 << TWEN);
    //waiting for all data to be read
    twiTimerStart = 1;
    //while TWINT =0 AND while timer has not ended, do nothing. the TWINT will be 1 when start is done.
    while((TWCR&(0B10000000))==0  && (twiTimerStart != 0) );
    
    if(twiTimerStart)//timer to apply command is >0
    {
        twiTimerStart = 0;//reset the timer
    }
    else//TWI ended with time exceeded error.
    {
        return 1;//timer to apply command exceeded
    }
}

It will be hard(er) to do this modifications via Arduino IDE, as it does not give you so direct access to the registers.

regarding the I2C registers, they are described in ATMEGA328p's datasheet page 199

\$\endgroup\$
5
  • \$\begingroup\$ Let me know if you need more, detailed code. :) \$\endgroup\$ Commented Mar 7, 2023 at 22:05
  • 1
    \$\begingroup\$ I think I followed that for the most part. Thank you very much for the explanations an example. I was hoping for a relatively easy/uninvolved fix, but that does not seem like it will be the case. \$\endgroup\$
    – bdmcnamara
    Commented Mar 7, 2023 at 22:30
  • \$\begingroup\$ There was a library presented about a year ago as a solution for a similar problem. It times and returns an error if the target does not respond. You could modify the wire library to maybe solve this. \$\endgroup\$
    – Gil
    Commented Mar 8, 2023 at 1:32
  • \$\begingroup\$ A timer is indeed the solution. A compliant I2C solution is specified to DC (no timeout), \$\endgroup\$ Commented Mar 8, 2023 at 14:46
  • \$\begingroup\$ Or instead of timer you could use a 16-bit or 32-bit variable that increases by 1 in the while() loop, and escape the loop when this variable reaches a really large value (depending on how much you want to wait). Example: while((TWCR&(0B10000000))==0 && (twiCounter < 65.000 ) ) { twiCounter ++ }; \$\endgroup\$ Commented Mar 8, 2023 at 14:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.