ATtiny84: felias-fogg / SoftI2CMaster to control BQ24292i over i2c "issues"

Go To Last Post
4 posts / 0 new
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi everyone, 


I'm just getting started with AVR microcontrollers and have been building a game console (powered by a Raspberry Pi CM4, dubbed the Retro Lite - see pictures attached below). I am still very much a beginner and just a hobbyist at best, so please try not to berate me for making mistakes or calling me an idiot lol - I am here to learn and improve! 


With that being said, I have been trying to use the bit-banged i2c library provided by felias-fogg ( with the below wrapper functions to communicate to the BQ24292i chip from TI. I am having an issue whereby I upload the code and it basically breaks everything - the basic button debouncing doesn't work, and none of the pinMode() functions seem to be working either - i.e the power_btn on pin 8 does not get pulled high. As soon as I remove the BQ related functions, the code starts to work just fine again. 


Unfortunately, I don't have access to a logic analyser at the moment so I can't hook up the SDA/SCL lines to see what is happening, so I'm basically debugging in the dark right now. For what it's worth, I'll post my code below. Perhaps someone who has used the same library with the ATtiny84 will immediate recognize where I'm going wrong. I'm still not very good at debugging, but perhaps someone could help me get started on the best way to debug (perhaps using the serial monitor?)


I'm using the Arduino IDE (latest version 1.8.13), have the ATtiny84 running on the 8Mhz internal clock (have burned the fuses) and flashed the code using an Arduino UNO board (Arduino as ISP). The code compiles perfectly fine and I am able to flash the ATtiny. I am using the QFN package and have the SDA and SCL pins on pin 6 and pin 4 respectively. I am using external 4.7k pullups on both lines to the system voltage of the BQ output. 

#define BQ24292i_ADDRESS 0xD6 // 8 bit
#define SDA_PIN 6
#define SCL_PIN 4
#include <SoftI2CMaster.h>

const int power_btn = 8; //Power button connected to this pin. Low Active
const int sys_on = 1; //Regulator power. Active High
const int sht_dwn = 2; //Signal to start Pi Shutdown. Active High

byte powerBtnState;
byte systemState = 0; //Low Power Off
bool shutdownInit = false;

unsigned long powerBtnTimer;
unsigned long shutDown;
long powerOnDelay = 1000;
long powerOffDelay = 3000;
long shutDownDelay = 10000;
bool btnTimerStarted = false;
bool shutDownTimerStarted = false;

void setup() {
  pinMode(power_btn, INPUT_PULLUP);
  pinMode(sys_on, OUTPUT);
  pinMode(sht_dwn, OUTPUT);

void loop() {
    } else {
      btnTimerStarted = false;
  } else {

//Button State Machine

void powerTimerCheck(){
    btnTimerStarted = true;
    powerBtnTimer = millis();
    //Serial.println("Timer Restart");
  } else {
    if(systemState == 0){
      if(powerBtnTimer + powerOnDelay < millis()){
        //Serial.print("System State: ");
        systemState = 1;
        digitalWrite(sys_on, HIGH);
        btnTimerStarted = false;
    } else {
      if(powerBtnTimer + powerOffDelay < millis()){
        //Serial.print("System State: ");
        systemState = 0;
        digitalWrite(sht_dwn, HIGH);
        btnTimerStarted = false;
        shutdownInit = true;

void shutdownTimer(){
    shutDown = millis();
    shutDownTimerStarted = true;
    //Serial.println("Shutdown Timer Started");
  } else {
    if(shutDown + shutDownDelay < millis()){
      //Serial.println("Shutdown Timer Expired");
      digitalWrite(sys_on, LOW);
      digitalWrite(sht_dwn, LOW);
      shutDownTimerStarted = false;
      shutdownInit = false;

void powerButtonDebounce(){
  int input = !digitalRead(power_btn);
  if (input != powerBtnState){
    powerBtnState = input;

void BQ_Write(uint8_t address, uint8_t reg, const uint8_t message) {
  i2c_start_wait(address | I2C_WRITE);

 uint8_t BQ_Read(uint8_t address, uint8_t reg){
  i2c_start_wait(address | I2C_WRITE);
  i2c_rep_start(address | I2C_READ);
  uint8_t data = i2c_read(true);

/* BQ24292i Register Configuration

// REG00
const uint8_t EN_HIZ = 0;
const uint8_t EN_ILIM = 1;
const uint8_t INILIM = 0b111;
const uint8_t INILIM = 0b111;   // 3A

// REG01
const uint8_t REG_RST = 0;
const uint8_t WD_RST = 0;
const uint8_t CHG_CONFIG = 0b01;
const uint8_t SYS_MIN = 0b000;   // 3.0V
const uint8_t BOOST_LIM = 1;

// REG02
const uint8_t ICHG = 0b111111; // 4.54A
const uint8_t FORCE_20PCT = 0;

// REG03 default values are OK

// REG04
const uint8_t VREG = 0b101100; // 4.208V
const uint8_t BATLOWV = 0;
const uint8_t VRECHG = 0;

// REG05
const uint8_t EN_TERM = 1;
const uint8_t TERM_STAT = 0;
const uint8_t WATCHDOG = 0b00; // Disable I2C WD
const uint8_t EN_TIMER = 1;
const uint8_t CHG_TIMER = 0b01;

const uint8_t BAT_COMP = 0;
const uint8_t VCLAMP = 0;
const uint8_t TREG = 0b01;   // 80C


const uint8_t REG00_config = 0b01111111;
const uint8_t REG01_config = 0b00010001;
const uint8_t REG02_config = 0b11111100;
const uint8_t REG04_config = 0b10110000;
const uint8_t REG05_config = 0b10001010;
const uint8_t REG06_config = 0b00000001;
const uint8_t REG07_config = 0b01101011;

void BQ_INIT() {
    BQ_Write(BQ24292i_ADDRESS, 0x00, REG00_config);
    BQ_Write(BQ24292i_ADDRESS, 0x01, REG01_config);
    BQ_Write(BQ24292i_ADDRESS, 0x02, REG02_config);
    BQ_Write(BQ24292i_ADDRESS, 0x04, REG04_config);
    BQ_Write(BQ24292i_ADDRESS, 0x05, REG05_config);
    BQ_Write(BQ24292i_ADDRESS, 0x06, REG06_config);
    BQ_Write(BQ24292i_ADDRESS, 0x07, REG07_config);


Cheers and thanks for your help guys, looking forward to participating and learning more about microcontrollers (as a hobbyist). 

Perhaps someone might even be able to suggest some optimizations to the code (such as using an interrupt instead of polling the power button constantly). 


If anyone has any questions about the console I'd be happy to answer also. We are enjoying this project and the case is looking great I think laugh




Last Edited: Sun. May 2, 2021 - 03:26 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The implementation of ATtiny84 on the ARduino seems to have a bit of SNAFU madness called "damellis core" vs. "SpenceKonde core".  From all that I can tell, it appears to have something to do with the pin numbering for Ardrey's D pins being either clockwise or counterclockwise.


  Possibly the SDA and SCL signals are going to the wrong pins and are turning the wrong things on at the wrong times.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Welcome to AVRFreaks!


The nice thing about I2C is it will tell you what is wrong, but you must be listening!   You listen by checking the return status of the functions you call, never blindly run ahead with out checking the status.

In the example provided on the git hub link you will find:

    if (!i2c_init()) // Initialize everything and check for bus lockup
        Serial.println("I2C init failed");

Once that is done, then you need to see if the slave device is there:

    if (!i2c_start((I2C_7BITADDR<<1)|I2C_WRITE)) { // start transfer
        Serial.println("I2C device busy");

Note: this checks to see if the slave device is on the bus and addressed correctly and responds with an ACK to it's address.  Until this works, nothing will!

I2C uses an odd addressing scheme, it uses a 7-bit device address with an appended R/W bit, some device data sheets state the 7 bit address, while others state the two 8-bit addresses, one for read, one for write.

I2C lib's likewise will use either a 7 bit address, or an 8 bit address, you need to figure out which method is used.  I found this on the lib's website:

Note that this library uses the 8-bit addressing scheme different from the 7-bit scheme in the Arduino Wire library.   So if needed, convert the slave device's address to 8 bit form if needed!

When reading data from an I2C slave, all data read is ACK'd but the last byte (or only byte read) must be NAK'd!  In the example only one byte is read:

byte val = i2c_read(true); // read one byte and send NAK to terminate

If this is not done, then the following I2C_Stop() will fail and the bus will hang, as the slave believes the master wants more data and will not release the bus for the master to send stop.


Hope that helps.




(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"


  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ki0bk wrote:
The nice thing about I2C is it will tell you what is wrong

Well, it won't actually tell you what is wrong - but it can tell you when something has not worked.


Specifically, it can tell you that it didn't get an ACK


some device data sheets state the 7 bit address, while others state the two 8-bit addresses, one for read, one for write.

I2C lib's likewise will use either a 7 bit address, or an 8 bit address, you need to figure out which method is used.  






Top Tips:

  1. How to properly post source code - see: - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see:
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillator
  5. When your question is resolved, mark the solution:
  6. Beginner's "Getting Started" tips: