SevSeg Library + i2c = freeze

I'm using the SevSeg Library with an ATmega328p and trying to use an INA226 shunt monitor(i2c) as well. I noticed that when I would try to read from the i2c bus, the board seems to freeze. This is a custom PCB that works great other wise.

I confirmed that the PCB is OK by soldering an I2c SSD1306 OLED display for diag, as the board has no bootloader or UART. Both the INA226 and OLED display work great without the SevSeg library.

Here's a simple test sketch that freezes every time.

Any idea how to fix this?

#include <Wire.h>
#include <INA226.h>

INA226 ina;


#include "SevSeg.h"

//Create an instance of the object.
SevSeg myDisplay;

//Create global variables
unsigned long timer;
int deciSecond = 0;

void setup()
{

// Configure INA226
  ina.configure(INA226_AVERAGES_1, INA226_BUS_CONV_TIME_1100US, INA226_SHUNT_CONV_TIME_1100US, INA226_MODE_SHUNT_BUS_CONT);
// Calibrate INA226. Rshunt = 0.01 ohm, Max excepted current = 4A
  ina.calibrate(0.01, 4);

  int displayType = COMMON_CATHODE; //Your display is either common cathode or common anode

  //This pinout is for OpenSegment PCB layout
  //Declare what pins are connected to the digits
  int digit1 = 14; //Pin 12 on my 4 digit display
  int digit2 = 17; //Pin 9 on my 4 digit display
  int digit3 = 1; //Pin 8 on my 4 digit display
  int digit4 = 0; //Pin 6 on my 4 digit display

  //Declare what pins are connected to the segments
  int segA = 15; //Pin 11 on my 4 digit display
  int segB = 0; //Pin 7 on my 4 digit display
  int segC = 6; //Pin 4 on my 4 digit display
  int segD = 4; //Pin 2 on my 4 digit display
  int segE = 3; //Pin 1 on my 4 digit display
  int segF = 16; //Pin 10 on my 4 digit display
  int segG = 7; //Pin 5 on my 4 digit display
  int segDP= 5; //Pin 3 on my 4 digit display

  int numberOfDigits = 3; //Do you have a 1, 2 or 4 digit display?

  myDisplay.Begin(displayType, numberOfDigits, digit1, digit2, digit3, digit4, segA, segB, segC, segD, segE, segF, segG, segDP);
  
  myDisplay.SetBrightness(100); //Set the display to 100% brightness level

  timer = millis();
}

void loop()
{
  //Example ways of displaying a decimal number
  char tempString[10]; //Used for sprintf
  sprintf(tempString, "%4d", deciSecond); //Convert deciSecond into a string that is right adjusted
  //Produce an output on the display
  myDisplay.DisplayString(tempString, 3); //(numberToDisplay, decimal point location)

  //Check if 10ms has elapsed
  if (millis() - timer >= 100)
  {
    timer = millis();
    deciSecond++;
    }
if (deciSecond == 50) {
  //stops here, but only with SevSeg library
  float busVoltage = ina.readBusVoltage();
}

  delay(5);
}

Any idea how to fix this?

Here's some code that uses a library that I haven't posted a link to. What's wrong?

My bad on that. Here's the link to the libraries.

INA226:

SevSeg:

I can read/write the registers without the INA226 library and it's working, so I'll just do that. If I figure out what's wrong with the library I'll post back up with the fix.

It looks to me like segB and digit4 use the same Arduino pin.

Using the hardware serial pins for the 7 segment display may not be a good idea.

Good eye, but that's not the issue. I changed it to -1 after I noticed with no difference. I'm going to get some snippets ready that can hopefully help.

Here's the code that works like it's supposed to. Still using the library, but not to read the bus voltage register.

#include <Wire.h>
#include <INA226.h>

INA226 ina;


#include "SevSeg.h"

//Create an instance of the object.
SevSeg myDisplay;

//Create global variables
unsigned long timer;
int busVoltage;

void setup()
{

// Configure INA226
  ina.configure(INA226_AVERAGES_4, INA226_BUS_CONV_TIME_1100US, INA226_SHUNT_CONV_TIME_1100US, INA226_MODE_SHUNT_BUS_CONT);
// Calibrate INA226. Rshunt = 0.01 ohm, Max excepted current = 4A
  ina.calibrate(0.01, 4);

  int displayType = COMMON_CATHODE; //Your display is either common cathode or common anode

  //This pinout is for OpenSegment PCB layout
  //Declare what pins are connected to the digits
  int digit1 = 14; //Pin 12 on my 4 digit display
  int digit2 = 17; //Pin 9 on my 4 digit display
  int digit3 = 1; //Pin 8 on my 4 digit display
  int digit4 = -1; //Pin 6 on my 4 digit display

  //Declare what pins are connected to the segments
  int segA = 15; //Pin 11 on my 4 digit display
  int segB = 0; //Pin 7 on my 4 digit display
  int segC = 6; //Pin 4 on my 4 digit display
  int segD = 4; //Pin 2 on my 4 digit display
  int segE = 3; //Pin 1 on my 4 digit display
  int segF = 16; //Pin 10 on my 4 digit display
  int segG = 7; //Pin 5 on my 4 digit display
  int segDP= 5; //Pin 3 on my 4 digit display

  int numberOfDigits = 3; //Do you have a 1, 2 or 4 digit display?

  myDisplay.Begin(displayType, numberOfDigits, digit1, digit2, digit3, digit4, segA, segB, segC, segD, segE, segF, segG, segDP);
  
  myDisplay.SetBrightness(100); //Set the display to 100% brightness level

  timer = millis();


Wire.begin();
}

void loop()
{
  byte DP;
  if (busVoltage > 100) DP = 2;
  else DP = 1;
  //Example ways of displaying a decimal number
  char tempString[10]; //Used for sprintf
  sprintf(tempString, "%2d", busVoltage); //Convert busVoltage into a string that is right adjusted
  //Produce an output on the display
  myDisplay.DisplayString(tempString, DP); //(numberToDisplay, decimal point location)

    int16_t value;

    //this totally works
    Wire.beginTransmission(0x40);
    #if ARDUINO >= 100
        Wire.write(0x02);
    #else
        Wire.send(0x02);
    #endif
    Wire.endTransmission();

    delay(1);

    Wire.beginTransmission(0x40);
    Wire.requestFrom(0x40, 2);
    while(!Wire.available()) {};
    #if ARDUINO >= 100
        uint8_t vha = Wire.read();
        uint8_t vla = Wire.read();
    #else
        uint8_t vha = Wire.receive();
        uint8_t vla = Wire.receive();
    #endif;
    Wire.endTransmission();

    value = vha << 8 | vla;
    busVoltage = (value * 0.00125)* 10;
}

Now, here's the read bus voltage function from the INA226 library.

float INA226::readBusVoltage(void)
{
    int16_t voltage;

    voltage = readRegister16(INA226_REG_BUSVOLTAGE);

    return (voltage * 0.00125);
}

I copied the actual read register function into my working code to read the bus voltage and it works, but freezes when I use the ina.readBusVoltage() function. I don't see what the difference is. Maybe someone else can notice something.

int16_t INA226::readRegister16(uint8_t reg)
{
    int16_t value;

    Wire.beginTransmission(inaAddress);
    #if ARDUINO >= 100
        Wire.write(reg);
    #else
        Wire.send(reg);
    #endif
    Wire.endTransmission();

    delay(1);

    Wire.beginTransmission(inaAddress);
    Wire.requestFrom(inaAddress, 2);
    while(!Wire.available()) {};
    #if ARDUINO >= 100
        uint8_t vha = Wire.read();
        uint8_t vla = Wire.read();
    #else
        uint8_t vha = Wire.receive();
        uint8_t vla = Wire.receive();
    #endif;
    Wire.endTransmission();

    value = vha << 8 | vla;

    return value;
}

You do not use beginTransmission()/endTransmission() to request data.

Thanks for that. I fixed it in the library and my sketch. Library function still doesn't work, but my sketch does.

EDIT:I'm going to end up just writing my own functions for my sketch. :slight_smile:

I appreciate your help, Paul S.

#include <Wire.h>
#include <INA226.h>
#include "SevSeg.h"
INA226 ina;


//Create an instance of the object.
SevSeg myDisplay;

//Create global variables
unsigned long timer;

int busVoltage;

void setup()
{

// Configure INA226
  ina.configure(INA226_AVERAGES_4, INA226_BUS_CONV_TIME_1100US, INA226_SHUNT_CONV_TIME_1100US, INA226_MODE_SHUNT_BUS_CONT);
// Calibrate INA226. Rshunt = 0.01 ohm, Max excepted current = 4A
  ina.calibrate(0.01, 4);

  int displayType = COMMON_CATHODE; //Your display is either common cathode or common anode

  //This pinout is for OpenSegment PCB layout
  //Declare what pins are connected to the digits
  int digit1 = 14; //Pin 12 on my 4 digit display
  int digit2 = 17; //Pin 9 on my 4 digit display
  int digit3 = 1; //Pin 8 on my 4 digit display
  int digit4 = -1; //Pin 6 on my 4 digit display

  //Declare what pins are connected to the segments
  int segA = 15; //Pin 11 on my 4 digit display
  int segB = 0; //Pin 7 on my 4 digit display
  int segC = 6; //Pin 4 on my 4 digit display
  int segD = 4; //Pin 2 on my 4 digit display
  int segE = 3; //Pin 1 on my 4 digit display
  int segF = 16; //Pin 10 on my 4 digit display
  int segG = 7; //Pin 5 on my 4 digit display
  int segDP= 5; //Pin 3 on my 4 digit display

  int numberOfDigits = 3; //Do you have a 1, 2 or 4 digit display?

  myDisplay.Begin(displayType, numberOfDigits, digit1, digit2, digit3, digit4, segA, segB, segC, segD, segE, segF, segG, segDP);
  
  myDisplay.SetBrightness(100); //Set the display to 100% brightness level

  timer = millis();


Wire.begin();
}

void loop()
{
  byte DP;
  if (busVoltage > 100) DP = 2;
  else DP = 1;
  //Example ways of displaying a decimal number
  char tempString[10]; //Used for sprintf
  sprintf(tempString, "%2d", busVoltage); //Convert busVoltage into a string that is right adjusted
  //Produce an output on the display
  myDisplay.DisplayString(tempString, DP); //(numberToDisplay, decimal point location)

    Wire.beginTransmission(0x40);
    Wire.write(0x02);//bus voltage register
    Wire.endTransmission();

    delay(1);
    int16_t value;

    Wire.requestFrom(0x40, 2);
    while(!Wire.available()) {};
    
        uint8_t vha = Wire.read();
        uint8_t vla = Wire.read();
        value = vha << 8 | vla;
    
    busVoltage = (value * 0.00125)* 10;
    
    
    
   // busVoltage = ina.readBusVoltage(); 

}

Got her done. Totally working. Going to clean it up and add into the original sketch. Need to add more functions, but I'm happy for now. Still not sure why the original library isn't working.

//INA226V.h
 

//INA226 registers
#define INA226_ADDRESS          (0x40)
#define INA226_REG_CONFIG       (0x00)
#define INA226_REG_SHUNTVOLTAGE (0x01)
#define INA226_REG_BUSVOLTAGE   (0x02)
#define INA226_REG_POWER        (0x03)
#define INA226_REG_CURRENT      (0x04)
#define INA226_REG_CALIBRATION  (0x05)
#define INA226_REG_MASKENABLE   (0x06)
#define INA226_REG_ALERTLIMIT   (0x07)
//Not sure WTF this is yet
#define INA226_BIT_SOL              (0x8000)
#define INA226_BIT_SUL              (0x4000)
#define INA226_BIT_BOL              (0x2000)
#define INA226_BIT_BUL              (0x1000)
#define INA226_BIT_POL              (0x0800)
#define INA226_BIT_CNVR             (0x0400)
#define INA226_BIT_AFF              (0x0010)
#define INA226_BIT_CVRF             (0x0008)
#define INA226_BIT_OVF              (0x0004)
#define INA226_BIT_APOL             (0x0002)
#define INA226_BIT_LEN              (0x0001)
typedef enum
{
    INA226_AVERAGES_1             = 0b000,
    INA226_AVERAGES_4             = 0b001,
    INA226_AVERAGES_16            = 0b010,
    INA226_AVERAGES_64            = 0b011,
    INA226_AVERAGES_128           = 0b100,
    INA226_AVERAGES_256           = 0b101,
    INA226_AVERAGES_512           = 0b110,
    INA226_AVERAGES_1024          = 0b111
} ina226_averages_t;
typedef enum
{
    INA226_BUS_CONV_TIME_140US    = 0b000,
    INA226_BUS_CONV_TIME_204US    = 0b001,
    INA226_BUS_CONV_TIME_332US    = 0b010,
    INA226_BUS_CONV_TIME_588US    = 0b011,
    INA226_BUS_CONV_TIME_1100US   = 0b100,
    INA226_BUS_CONV_TIME_2116US   = 0b101,
    INA226_BUS_CONV_TIME_4156US   = 0b110,
    INA226_BUS_CONV_TIME_8244US   = 0b111
} ina226_busConvTime_t;
typedef enum
{
    INA226_SHUNT_CONV_TIME_140US   = 0b000,
    INA226_SHUNT_CONV_TIME_204US   = 0b001,
    INA226_SHUNT_CONV_TIME_332US   = 0b010,
    INA226_SHUNT_CONV_TIME_588US   = 0b011,
    INA226_SHUNT_CONV_TIME_1100US  = 0b100,
    INA226_SHUNT_CONV_TIME_2116US  = 0b101,
    INA226_SHUNT_CONV_TIME_4156US  = 0b110,
    INA226_SHUNT_CONV_TIME_8244US  = 0b111
} ina226_shuntConvTime_t;
typedef enum
{
    INA226_MODE_POWER_DOWN      = 0b000,
    INA226_MODE_SHUNT_TRIG      = 0b001,
    INA226_MODE_BUS_TRIG        = 0b010,
    INA226_MODE_SHUNT_BUS_TRIG  = 0b011,
    INA226_MODE_ADC_OFF         = 0b100,
    INA226_MODE_SHUNT_CONT      = 0b101,
    INA226_MODE_BUS_CONT        = 0b110,
    INA226_MODE_SHUNT_BUS_CONT  = 0b111,
} ina226_mode_t;
#include <Wire.h>
#include "INA226V.h"
#include "SevSeg.h"


//variables
float currentLSB, powerLSB;
float vShuntMax, vBusMax, rShunt;
int voltageInt;
//Create an instance of the object.
SevSeg myDisplay;

//Create global variables
unsigned long timer;



void setup()
{

// Configure INA226
  configureINA226(INA226_AVERAGES_4, INA226_BUS_CONV_TIME_140US, INA226_SHUNT_CONV_TIME_140US, INA226_MODE_SHUNT_BUS_CONT);
// Calibrate INA226. Rshunt = 0.01 ohm, Max excepted current = 4A
  calibrateINA226(0.001, 80);

  int displayType = COMMON_CATHODE; //Your display is either common cathode or common anode

  //This pinout is for OpenSegment PCB layout
  //Declare what pins are connected to the digits
  int digit1 = 14; //Pin 12 on my 4 digit display
  int digit2 = 17; //Pin 9 on my 4 digit display
  int digit3 = 1; //Pin 8 on my 4 digit display
  int digit4 = -1; //Pin 6 on my 4 digit display

  //Declare what pins are connected to the segments
  int segA = 15; //Pin 11 on my 4 digit display
  int segB = 0; //Pin 7 on my 4 digit display
  int segC = 6; //Pin 4 on my 4 digit display
  int segD = 4; //Pin 2 on my 4 digit display
  int segE = 3; //Pin 1 on my 4 digit display
  int segF = 16; //Pin 10 on my 4 digit display
  int segG = 7; //Pin 5 on my 4 digit display
  int segDP= 5; //Pin 3 on my 4 digit display

  int numberOfDigits = 3; //Do you have a 1, 2 or 4 digit display?

  myDisplay.Begin(displayType, numberOfDigits, digit1, digit2, digit3, digit4, segA, segB, segC, segD, segE, segF, segG, segDP);
  
  myDisplay.SetBrightness(100); //Set the display to 100% brightness level

  timer = millis();


Wire.begin();
}

void loop()
{
  
  byte DP;
  if (voltageInt > 100) DP = 2;
  else DP = 1;
  voltageInt = readBusVoltage() * 10;
  
  //Example ways of displaying a decimal number
  char tempString[10]; //Used for sprintf
  sprintf(tempString, "%2d", voltageInt); //Convert busVoltage into a string that is right adjusted
  //Produce an output on the display
  myDisplay.DisplayString(tempString, DP); //(numberToDisplay, decimal point location)

  
 

}
  

int16_t readRegister16(uint8_t reg) {
int16_t value;

    Wire.beginTransmission(INA226_ADDRESS);
    Wire.write(reg);
    Wire.endTransmission();
    
    delay(1);//Is this needed?

    
    Wire.requestFrom(INA226_ADDRESS, 2);
    Wire.beginTransmission(INA226_ADDRESS);
    while(!Wire.available()) {};
    uint8_t vha = Wire.read();
    uint8_t vla = Wire.read();
    Wire.endTransmission();

    value = vha << 8 | vla;

    return value;
}

//Read Shunt voltage

float readShuntVoltage(void)
{
    float voltage;

    voltage = readRegister16(INA226_REG_SHUNTVOLTAGE);

    return (voltage * 0.0000025);
}

float readBusVoltage(void)
{
    int16_t voltage;

    voltage = readRegister16(INA226_REG_BUSVOLTAGE);

    return (voltage * 0.00125);
}

void writeRegister16(uint8_t reg, uint16_t val)
{
    uint8_t vla;
    vla = (uint8_t)val;
    val >>= 8;

    Wire.beginTransmission(INA226_ADDRESS);
    Wire.write(reg);
    Wire.write((uint8_t)val);
    Wire.write(vla);
    Wire.endTransmission();
}

//INA226 CONFIG
bool configureINA226(ina226_averages_t avg, ina226_busConvTime_t busConvTime, ina226_shuntConvTime_t shuntConvTime, ina226_mode_t mode){
uint16_t config = 0;
config |= (avg << 9 | busConvTime << 6 | shuntConvTime << 3 | mode);
    vBusMax = 36; //probably not needed
    vShuntMax = 0.08192f;//probably not needed
writeRegister16(INA226_REG_CONFIG, config);
return true;
}

//INA226 Calibration 
bool calibrateINA226(float rShuntValue, float iMaxExpected) {
uint16_t calibrationValue;
    rShunt = rShuntValue;
    float iMaxPossible, minimumLSB;
    
    iMaxPossible = vShuntMax / rShunt;

    minimumLSB = iMaxExpected / 32767;

    currentLSB = (uint16_t)(minimumLSB * 100000000);
    currentLSB /= 100000000;
    currentLSB /= 0.0001;
    currentLSB = ceil(currentLSB);
    currentLSB *= 0.0001;

    powerLSB = currentLSB * 25;

    calibrationValue = (uint16_t)((0.00512) / (currentLSB * rShunt));

    writeRegister16(INA226_REG_CALIBRATION, calibrationValue);

   return true;
}