I2C Battery BMS Status Display

hi,

i think you have to call the serial communication with the bms in the main programm!

i'll send you the complete code of my project:

  • LCD, keys and BMS with I2C
  • speed and PAS signals in interrupts
    it isn't really finished, but already working like this:
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>

// Konstanten-Deklaration
const float umfang = 2070; // Radumfang 26" 
const int PAS_magnet = 5;      // Anzahl der Magneten im PAS

// Variablen-Deklaration
// LCD 
LiquidCrystal_I2C lcd(0x21,20,4);  // set the LCD address to 0x21, for a 20 chars and 4 line display
int spos = 0;    // spalte 0-20
int zpos = 0;    // zeile 0-3
char str1[20] = "E-bike Control V1.1";
char str2[20] = " ";
char z1 = 192;  //ascii
char z2 = 192; 

// Tastatur (4 Tasten)
boolean T0 = 0;
boolean T1 = 0;
boolean T2 = 0;
boolean T3 = 0;

// IO
int Gas_in_ch = 0;  // Ain0 für Gasgriff
int Gas_in = 0;
int Gas_out_ch = 0;  // Ain0 für Gasgriff
int Gas_out = 0;
int PAS_in_ch = 2;   // Din2 (IRQ0) für PAS-Signal
boolean PAS_in = 0;
int spd_in_ch = 3;   // Din3 (IRQ1) für speed-Signal

boolean PAS_alt = 0; // für flanke
unsigned long PAS_1 = 0;       // msec 
unsigned long PAS_2 = 0;       // msec 
float PAS = 0;         // U/min
float PAS_MW = 0;         // gleitender MW 
unsigned long PAS_h_time = 0;    //Altwert PAS_H millis
unsigned long PAS_l_time = 0;    //Altwert PAS_L millis
boolean PAS_dir = 0;     // Trittrichtung

unsigned long spd_1 = 0;       // msec 
float spd = 0;
float spd_MW = 0;      // gleitender MW 
unsigned long spd_time = 0;       

// allgemein
int count = 0;    // zähler
int tmp_data = 0;
unsigned long tmp_long = 0;
boolean test = 1;  // Testbetrieb
unsigned long time;
boolean temp_bool = 0;
int n1 = 0;
int n2 = 0;
byte val = 0;

// OZ
byte val1 = 0;
byte val2 = 0;
byte adr = 0;
int zelle = 0;
int i2cadr = 0;
byte stat1 = 0;
byte stat2 = 0;
float volt = 0;
float volt1 = 0;
float volt2 = 0;
float volt3 = 0;
float volt4 = 0;
float volt5 = 0;
float volt6 = 0;
float volt7 = 0;
float volt8 = 0;
float voltMIN = 0;
float voltMAX = 0;
float amp = 0;
float watt = 0;

// --------------------------------
// Init
void setup()
{
  Serial.begin(9600);
// IO init
  pinMode(PAS_in_ch, INPUT_PULLUP); 
  pinMode(4, OUTPUT);
  PAS_l_time = millis();
  PAS_h_time = millis();
  
// Interrupt Din2 ist IRQ 0 (PAS), Din3 ist IRQ 1 (speed)
  attachInterrupt(0, irqprg0, CHANGE);
  attachInterrupt(1, irqprg1, RISING);
  
// LCD init
  lcd.init();               // initialize the lcd  
  lcd.backlight();
  lcd.setCursor(0,0);
  lcd.print(str1);           // Print a message to the LCD.
  delay(1000);
//    lcd.clear();

// Tastatur-PCF8574 auf input
  Wire.beginTransmission(0x38);
  Wire.write(0xff); // alle IO high
  Wire.endTransmission();
  
  // Tastatur einlesen
  Wire.requestFrom(0x38, 1);
  tmp_data = Wire.read();
  T0= (tmp_data & 0x01);  // Taste 0 beim Einschalten gedrückt
//  test = T0               // -> Testbetrieb
  Serial.println(tmp_data); 
}
// --------------------------------
// main
void loop()
{
  // Zeit
  time = millis();
  
  // test
  temp_bool = count % 2;
  digitalWrite(4,temp_bool);
  
  // Tastatur einlesen
   Wire.requestFrom(0x38, 1);
   tmp_data = Wire.read();
   
   // Tasten ausmaskieren 
   T0= (tmp_data & 0x01);
   T1= (tmp_data & 0x02) >> 1;
   T2= (tmp_data & 0x04) >> 2;
   T3= (tmp_data & 0x08) >> 3;
   
   // IOs einlesen  
   Gas_in = analogRead(Gas_in_ch);  
    
// Erfassung PAS jetzt mit IRQ0  
// Drehrichtung fehlt noch
if ((PAS_1 > 0) and (PAS_2 > 0) and ((millis() - PAS_h_time) < 500))
{
   // Trittfrequenz in U/min
   PAS = 60000/((PAS_1 + PAS_2)*PAS_magnet);
   // gleitender Mittelwert
   PAS_MW = (PAS_MW * PAS_magnet + PAS)/(1+PAS_magnet);
}
else
{
   PAS = 0;
   PAS_MW = 0;
}
//   PAS_h_time = pulseIn(PAS_in_ch, HIGH);  
//   PAS_l_time = pulseIn(PAS_in_ch, LOW);  

// Erfassung speed  mit IRQ1  
if (((spd_1 > 50) and (spd_1 < 4000)) and ((millis() - spd_time) < 4000))
{
   // Geschwindigkeit in km/h
   spd = umfang/spd_1;
   // gleitender Mittelwert über 3 Werte
   spd_MW = (spd_MW * 3 + spd)/(1+3);
}
else
{  
   spd = 0;
   spd_MW = 0;
}
   
   if (test = 1) 
   { 
    // Testprogramm
    delay(1000);
      
 // akku einlesen
  i2cadr = 0x30;   // OZ: adr. 30 statt 60h!!
  adr = 0x0;         // chip id an adr. 0h
 
  // strom lesen
   adr = 0x54;         // strom an adr. 54h
  // register adresse übermitteln
  Wire.beginTransmission(i2cadr); 
  Wire.write(adr); 
  stat1 = Wire.endTransmission(false); // 0:sucess
  // register daten lesen
  stat2 = Wire.requestFrom(i2cadr, 2); // anzahl der gelesenen bytes
  val1 = Wire.read();
  val2 = Wire.read();
  // bytes auswerten
  val2 =  (val2 & 0x7F); // VZ ausblenden, noch auswerten
  amp = 7.63 * (val2 * 256 + val1)/1000; // mV-Spannungsabfall
  
  // Leistung berechnung
  watt = amp * volt;
  
  // spannungen lesen
  adr = 0x32;         //  spannung zelle 1 an adr 32h
  zelle = 1+count % 8;   // für 8 Zellen
  adr = adr + 2*(zelle-1);
  // register adresse übermitteln
  Wire.beginTransmission(i2cadr); 
  Wire.write(adr); 
  stat1 = Wire.endTransmission(false); // 0:sucess
  // register daten lesen
  stat2 = Wire.requestFrom(i2cadr, 2); // anzahl der gelesenen bytes
  val1 = Wire.read();
  val2 = Wire.read();
  // bytes auswerten
  val1 =  val1/8; // 3 bits ausblenden
  val2 =  (val2 & 0x7F); // VZ ausblenden
  
  switch (zelle) {
    case 1:
      volt1 = 1.22 * (val2 * 32 + val1);
      break;
   case 2:
      volt2 = 1.22 * (val2 * 32 + val1);
      break;
   case 3:
      volt3 = 1.22 * (val2 * 32 + val1);
      break;
   case 4:
      volt4 = 1.22 * (val2 * 32 + val1);
      break;
   case 5:
      volt5 = 1.22 * (val2 * 32 + val1);
      break;
   case 6:
      volt6 = 1.22 * (val2 * 32 + val1);
      break;
   case 7:
      volt7 = 1.22 * (val2 * 32 + val1);
      break;
   case 8:
      volt8 = 1.22 * (val2 * 32 + val1);
      break;
  }
  // Gesamt-, MIN und MAX
  volt = (volt1+volt2+volt3+volt4+volt5+volt6+volt7+volt8)/1000;
  voltMIN = min(volt1,min(volt2,min(volt3,min(volt4,min(volt5,min(volt6,min(volt7,volt8)))))));
  voltMAX = max(volt1,max(volt2,max(volt3,max(volt4,max(volt5,max(volt6,max(volt7,volt8)))))));
  
 // Display-ASusgabe
   zpos = 1;
   spos = 0;
    
   lcd.setCursor(spos,zpos);
 //  lcd.print(tmp_data);
   lcd.print(volt);
   lcd.setCursor(5,zpos);
   lcd.print(" Tasten: ");
   lcd.print(T0);
   lcd.print(T1);
   lcd.print(T2);
   lcd.print(T3);
   
   zpos = 2;
   spos = 0;
   
   lcd.setCursor(spos,zpos);
   lcd.print(PAS,0);  
   lcd.setCursor(5,zpos);
   lcd.print(PAS_MW,0);  
   lcd.setCursor(10,zpos);
   lcd.print(spd,1);  
   lcd.setCursor(15,zpos);
   lcd.print(spd_MW,1);
 
   zpos = 3;
   spos = 0;
   
   lcd.setCursor(spos,zpos);
   lcd.print(voltMIN);  
   lcd.setCursor(10,zpos);
   lcd.print(voltMAX);  
  
 // debugging   
  Serial.println(stat1); 
  Serial.println(val); 
  Serial.println(stat2); 

 //   Serial.print(PAS_1);
 //   Serial.print(" ");
 //   Serial.print(PAS_2);
 //   Serial.print(" ");
 //   Serial.print(PAS);
 //   Serial.print(" ");
 //   Serial.print(spd);
 //   Serial.print(" ");
 //   Serial.print(" ");
 //   Serial.println(" ");
    
   count = count+1;
   }
}

// --------------------------------
// Interruptprogramme
void irqprg0()
  // Trittfrequenz PAS-Signale -> Timer 
{
   PAS_in = digitalRead(PAS_in_ch);
     if (PAS_in == 1)  //steigend
     {
       PAS_h_time = millis();
       PAS_2 = PAS_h_time - PAS_l_time;
     }
     else           // fallend
     {
       PAS_l_time = millis();
       PAS_1 = PAS_l_time - PAS_h_time;
     }
}
void irqprg1()
  // speed-Signale -> Timer 
{
    spd_1 = millis() - spd_time;
    spd_time = millis();
}