Hackster is hosting Hackster Holidays, Finale: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Tuesday!Stream Hackster Holidays, Finale on Tuesday!
Marco Zonca
Published © GPL3+

Li.Po. Battery charger with BT Telemetry

For 2s1p (7.4V) battery packs, it does balancing, ready for a load circuit.

IntermediateFull instructions provided3,132
Li.Po. Battery charger with BT Telemetry

Things used in this project

Story

Read more

Schematics

PCB bottom

PCB top

PCB silk (components)

PCB bottom (small LED board)

PCB top (small LED board)

PCB silk, components (small LED board)

Charger schematic diagram (Fritzing)

Charger schematic diagram (PNG)

Code

Arduino .INO Charger sketch

Arduino
/*
  CHARGER for 2S Lipo battery
  by Marco Zonca, 2022
  
  This sketch works as 2S LiPo Charger, originally made for Autopilot2 for small sailing boats
  Arduino Nano as MCU, 3 relais, LM317, ua7812, 3 Thermistors TMP36, DAC module, 2 fuses, 4 BC337 NPN, 5W ceramic resistor, 2 RGB LED,
  resistors and capacitors, Buzzer, JST connectors, Cooling Fan, Bluetooth HC-05, etc.;

/*  
  WARNING: before switching Relais2 ON or OFF,
  switch OFF Relais1 first, then Relais2, then Relais1 ON again if necessary;
  This circuit KEEPS SEPARATED the battery ground from any other ground because during balanced charging
  the pins Cells, (-) and (+), are necessarily switched from one CELL to another, and inverted polarity at common wire!
 
  LETS CONNECT the load only by the way of X1-LOAD connector; the X1-LOAD power is automatically full disconnected when charging (both wires (-) and (+))
   
*/

#include <Wire.h>
#include <Adafruit_MCP4725.h>

Adafruit_MCP4725 DAC;

//--------------------------------------------------Charging constant Values
const float CBatt = 2.600;  // put here the battery power A/h
const float VCmin = 3.20;  // absolute minimum cell Voltage
const float VCMax = 4.22;  // absolute max cell voltage
const float VCfinalize = 4.18;  // finalize charging cell voltage (after this it will charge at fixed voltage and limited current)
const float VCchgd = 4.18;  // already charged cell voltage (skip charging)
const float ETAdd = 60;  // additionally time in minutes for charging (finalize)
const float ETFact = 1.20;  // coefficent estimated charging time (plus additionally ETAdd)
const float ETFactAlarm = 2.00;  // coefficent estimated charging time sets Alarm too (plus additionally ETAdd)
const float CellChgCurrent = CBatt/5; // charging current 1/5 of battery (cell) A/h i.e. 2.600 / 5 = 0.520 A
const float CellFinalizeCurrent = CellChgCurrent / 100 * 20; // % of charging current in finalizing mode, if below then stop charging (see VCfinalize)
const float CellSlowDownCurrent = CellChgCurrent / 100 * 50; // % of charging current in slow down mode (LM317 or uA7812 or Battery high temperature)
const float VPower = 5.0;  // max DAC voltage
const byte HighTRegH = 60; // LM317 or uA7812 high (threshold H) temperature C
const byte HighTRegL = 50; // LM317 or uA7812 high (threshold L) temperature C
const byte HighTBatH = 40; // battery high (threshold H) temperature C
const byte HighTBatL = 30; // battery high (threshold L) temperature C
const byte FanTRHigh = 55;  // Fan (threshold H) LM317 or uA7812 temperature C
const byte FanTRLow = 45;  // Fan (threshold L) LM317 or uA7812 temperature C
const byte FanTBHigh = 36;  // Fan (threshold H) battery temperature C
const byte FanTBLow = 32;  // Fan (threshold L) battery temperature C
const byte RegulatorTAlarm = 70;  // regulators TC alarm (STOP charging)
const byte BatteryTAlarm = 45;  // battery TC alarm (STOP charging)
//--------------------------------------------------Charging constant Values

const byte Relais1ConnectCellsPin = 2; 
const byte Relais2SwitchCellsPin = 3;
const byte RGBledBluePin[3] = {0,4,6};
const byte RGBledRedPin[3] = {0,5,7};
const byte FanPWMPin = 9;
const byte VcellPin = 14;
const byte VoutPin = 15;
const byte BuzzerPin = 16;
const byte TMP36_BatteryPin = 17;
const byte TMP36_7812Pin = 20;
const byte TMP36_317Pin = 21;
const byte Set_OFF = LOW;
const byte Set_ON = HIGH;
const int PrintDebugInterval = 4000;
const int CheckChargeInterval = 10000;
const byte DAC_Address = 0x60;
const float DAC_InitialV = 3.0;  // charger Volt to cell (it is an initial/parking value, should not charge at this value!)
const boolean IsDEBUG = true;  // set this as TRUE for Serial Monitor debug data output
const float Rload = 0.47;  // shunt resistor

boolean IsCharging = false;
boolean IsOverTime = false;
boolean IsAlreadyCharged = false;
boolean IsSlowDown = false;
boolean IsALARM = false;

byte PhaseNr = 0;
byte WhichCellIsOn = 0;
byte WhichFanSpeedIsOn = 0;
int FanSpeed[3] = {255*0/100, 255*90/100, 255*100/100};  // % of max speed, 0% normal TC, 90% high TC, 100% ALARM

float TempBattery = 0;
float Temp7812 = 0;
float Temp317 = 0;
float Vcell = 0;
float Voutput = 0;
float Vload = 0;
float Aload = 0;
float SetVOutput = 0;
float SetDAC = 0;
float TFull = 0;  // estimated charge time in minutes
float TRemain[3] = {0,0,0};  //  estimated remaining time of charging in minutes
float TScale = 0;  // time ETAdd reduction factor 
float C_OverTime[3] = {0,0,0};  // estimated time of charging in minutes
float C_AlarmTime[3] = {0,0,0};  // estimated time of charging in minutes sets Alarm
float SoC[3] = {0,0,0}; // state of charge in %
float VInitCell[3] = {0,0,0};  // initial charging cell voltage
float VFinalCell[3] = {0,0,0};  // final charging cell voltage

unsigned long lastPrintDebugMillis = 0;
unsigned long lastCheckChargeMillis = 0;
unsigned long ChTimeStart[3] = {0,0,0};  // charging start time, time elapsed as minutes from power-up
unsigned long ChTimeEnd[3] = {0,0,0};  // charging start time, time elapsed as minutes from power-up
unsigned long ChTimeNow = 0;  // now time, time elapsed as minutes from power-up

void setup() {
  if (IsDEBUG) Serial.begin(38400);  // output both serial monitor & bluetooth HC-05
  DAC.begin(DAC_Address);
  pinMode(RGBledBluePin[1], OUTPUT);
  pinMode(RGBledRedPin[1], OUTPUT);
  pinMode(RGBledBluePin[2], OUTPUT);
  pinMode(RGBledRedPin[2], OUTPUT);
  pinMode(TMP36_BatteryPin, INPUT);
  pinMode(TMP36_7812Pin, INPUT);
  pinMode(TMP36_317Pin, INPUT);
  pinMode(VcellPin, INPUT);
  pinMode(VoutPin, INPUT);
  pinMode(Relais1ConnectCellsPin,OUTPUT);
  pinMode(Relais2SwitchCellsPin,OUTPUT);
  pinMode(BuzzerPin,OUTPUT);
  pinMode(FanPWMPin,OUTPUT);
  DisconnectAllCells();
  WriteDACVOutput(DAC_InitialV);
  buzzerBips(1);
  ledShow();
}//setup()

void loop() {
  readTemperatures();
  readVAvalues();
  if ((IsDEBUG) && (lastPrintDebugMillis + PrintDebugInterval) < millis()) serialPrintDebug();
  if (IsALARM==false) {
    if ((lastCheckChargeMillis + CheckChargeInterval) < millis()) checkCharge();
    if (WhichCellIsOn) display();
   } else {  // ALARM
      if (WhichCellIsOn) DisconnectAllCells();
      display();
      buzzerBips(5);
  }//IsALARM
}//loop()

void checkCharge() {  // ----------------------------------------------------------------------------------- start, charge or skip, finish
  float ri = 0;
  float current = 0;
  float c = 0;
  TFull = roundZeroDec(CBatt / CellChgCurrent) * 60;
  current=CellChgCurrent;
  if (IsSlowDown == true) {  // limiting current during high TC
    TFull = roundZeroDec(CBatt / CellSlowDownCurrent) * 60;
    current=CellSlowDownCurrent;
  }
  if (Vcell > VCfinalize) {
    TFull = roundZeroDec(CBatt / CellFinalizeCurrent) * 60;
    TScale=(((Aload - CellFinalizeCurrent) * 100) / (current - CellFinalizeCurrent)) / 100;
   } else {
    TScale=1;
  }
  
  if (IsCharging == true) { // ----------------------------------------------------- charging
    if (Vcell > VCMax) {  // ======== stop charging, over VCMax...
      ChTimeEnd[WhichCellIsOn]=roundZeroDec(((millis()/1000)/60)+1);
      VFinalCell[WhichCellIsOn]=Vcell;  // final cell voltage
      DisconnectAllCells();
      WriteDACVOutput(DAC_InitialV);
      IsCharging=false;
      if (PhaseNr==1) buzzerBips(2);
      if (PhaseNr==2) buzzerBips(3);
      if (IsDEBUG) Serial.println("STOP, over VCMax...");
     } else {
      if (Vcell <= VCfinalize) {  // ========== tuning, fixed current
        for (ri=(SetVOutput - 0.06);ri=ri+0.02;ri<VPower) {  // increment DAC voltage till reaching "current"
          WriteDACVOutput(ri);
          readVAvalues();
          if (IsDEBUG) serialPrintChg("TUNE");
          if ((Aload >= current) || (Vcell>=(VCMax - 0.02))) break;
        }
      }//IfVcell<=Finalize
      if (Vcell > VCfinalize) {  // ============ finalize, fixed voltage and limited current
        for ((ri=SetVOutput - 0.03);ri=ri+0.01;ri<=VCMax) {  // increment DAC voltage till reaching "VCMax-0.02"
          WriteDACVOutput(ri);
          readVAvalues();
          if (IsDEBUG) serialPrintChg("FINAL");
          if ((Aload >= current) || (Vcell>=(VCMax - 0.02))) break;
        }
        if (Aload < CellFinalizeCurrent) {  // === stop charging, below finalizing current, perfect
          ChTimeEnd[WhichCellIsOn]=roundZeroDec(((millis()/1000)/60)+1);
          VFinalCell[WhichCellIsOn]=Vcell;  // final cell voltage
          DisconnectAllCells();
          WriteDACVOutput(DAC_InitialV);
          IsCharging=false;
          if (PhaseNr==1) buzzerBips(2);
          if (PhaseNr==2) buzzerBips(3);
          if (IsDEBUG) Serial.println("END, current finalized");
        }
      }//IfVcell>=Finalize
    }//ifVCell>=VCMax
  }//IfIsCharging==true
  
  if (IsCharging == false) {  // ----------------------------------------------------- phase and start
    if (PhaseNr < 2) {  // =============== switch Cell -> 1 -> 2 
      PhaseNr++;
      ConnectCell_N(PhaseNr);
      readVAvalues();
      if (Vcell >= VCchgd) {
        IsAlreadyCharged=true;
       } else {
        IsAlreadyCharged=false;
      }
      IsOverTime=false;
      if (IsAlreadyCharged == false) {  // ================ start charging, fixed current
        ChTimeStart[WhichCellIsOn]=roundZeroDec(((millis()/1000)/60)+1);
        VInitCell[WhichCellIsOn]=Vcell;  // initial cell voltage
        C_OverTime[WhichCellIsOn] = roundZeroDec(((((CBatt / CellChgCurrent) * 60) * (VCMax - 0.02 - VInitCell[WhichCellIsOn])) * ETFact) + ETAdd); // estimated maximum time of charging
        C_AlarmTime[WhichCellIsOn] = roundZeroDec(((((CBatt / CellChgCurrent) * 60) * (VCMax - 0.02 - VInitCell[WhichCellIsOn])) * ETFactAlarm) + ETAdd); // estimated maximum time of charging then setes Alarm
        for (ri=Vcell;ri=ri+0.02;ri<VPower) {  // increment DAC voltage till reaching "current"
          WriteDACVOutput(ri);
          readVAvalues();
          if (IsDEBUG) serialPrintChg("START");
          if ((Aload >= current) || (Vcell>=(VCMax-0.02))) break;
        }
        IsCharging=true;
       } else {
        ChTimeStart[WhichCellIsOn]=roundZeroDec(((millis()/1000)/60)+1);  // ======== charging not necessary, already charged...
        VInitCell[WhichCellIsOn]=Vcell;  // initial cell voltage
        C_OverTime[WhichCellIsOn] = roundZeroDec(((((CBatt / CellChgCurrent) * 60) * (VCMax - 0.02 - VInitCell[WhichCellIsOn])) * ETFact) + ETAdd); // estimated maximum time of charging
        C_AlarmTime[WhichCellIsOn] = roundZeroDec(((((CBatt / CellChgCurrent) * 60) * (VCMax - 0.02 - VInitCell[WhichCellIsOn])) * ETFactAlarm) + ETAdd); // estimated maximum time of charging then sets Alarm
        ChTimeEnd[WhichCellIsOn]=roundZeroDec(((millis()/1000)/60)+1);
        VFinalCell[WhichCellIsOn]=Vcell;  // final cell voltage
        display();
        DisconnectAllCells();
        WriteDACVOutput(DAC_InitialV);
        if (PhaseNr==1) buzzerBips(2);
        if (PhaseNr==2) buzzerBips(3);
        if (IsDEBUG) Serial.println("END, already charged");
      }//IsAlreadyCharged==false
    }//IfPhase<2
  }//IsCharging==false

  if (WhichCellIsOn) {
    SoC[WhichCellIsOn] = roundZeroDec(100 - ((VCMax - 0.02 - Vcell) * 100)); // state of charge in %
    ChTimeNow=roundZeroDec(((millis()/1000)/60)+1);  // now time as minutes from power-up +1
    if ((ChTimeNow-ChTimeStart[WhichCellIsOn]) > C_OverTime[WhichCellIsOn]) IsOverTime=true;
    if ((ChTimeNow-ChTimeStart[WhichCellIsOn]) > C_AlarmTime[WhichCellIsOn]) IsALARM=true;
    TRemain[WhichCellIsOn] = roundZeroDec(((TFull * (VCMax - 0.02 - Vcell)) * ETFact) + (ETAdd * TScale));  //  estimated remaining time of charging in minutes
  }
  lastCheckChargeMillis=millis();
}//checkCharge()

void buzzerBips(byte n) {
  byte q=0;
  for (q=1;q<=n;q++) {
    digitalWrite(BuzzerPin, HIGH);
    delay(100);  
    digitalWrite(BuzzerPin, LOW);
    delay(100);  
  }
}//buzzerBips()

/*  
  WARNING: before switching Relais2 ON or OFF,
  switch OFF Relais1 first, then Relais2, then Relais1 ON again if necessary;
  This circuit KEEPS SEPARATED the battery ground from any other ground because during balanced charging
  the pins Cells, (-) and (+), are necessarely switched from one CELL to another, and inverted polarity at common wire!
*/
void DisconnectAllCells() {  // ------------------------------ disconnect Cells by Relais
  digitalWrite(Relais1ConnectCellsPin, Set_OFF);
  delay(1000);
  digitalWrite(Relais2SwitchCellsPin, Set_OFF);
  delay(1000);
  WhichCellIsOn = 0;
}//DisconnectAllCells()

void ConnectCell_N(byte c) {  // ----------------------------- connect Cell by Relais
  switch (c) {
    case 1:
      digitalWrite(Relais1ConnectCellsPin, Set_OFF);
      delay(1000);
      digitalWrite(Relais2SwitchCellsPin, Set_OFF);
      delay(1000);
      digitalWrite(Relais1ConnectCellsPin, Set_ON);
      delay(1000);
    break;
    case 2:
      digitalWrite(Relais1ConnectCellsPin, Set_OFF);
      delay(1000);
      digitalWrite(Relais2SwitchCellsPin, Set_ON);
      delay(1000);
      digitalWrite(Relais1ConnectCellsPin, Set_ON);
      delay(1000);
    break;
  }
  WhichCellIsOn = c;
}//ConnectCell_N()

void WriteDACVOutput(float v) {  // ------------------------- write DAC
  SetVOutput = v;
  SetDAC = ((SetVOutput - 1.25 + 0.75) * (4095.0 / 5.0));  // (Vdac - VdacADD + Vdiod) * (Res / Vref)
  DAC.setVoltage(SetDAC, false);
  delay(1000);
}//WriteDACVOutput()

void display() {  // ---------------------------------------- display LED status
  digitalWrite(RGBledBluePin[WhichCellIsOn],LOW);
  digitalWrite(RGBledRedPin[WhichCellIsOn],LOW);
  if (IsALARM==true) {
    blink_redALARM();
  } else {
    if (IsAlreadyCharged==false) {
      if ((IsCharging==true)) {
        if (IsOverTime==false) {
          if (SoC[WhichCellIsOn] <= 20) blink_red();
          if (SoC[WhichCellIsOn] > 20 && SoC[WhichCellIsOn] <=40) blink_red_purple();
          if (SoC[WhichCellIsOn] > 40 && SoC[WhichCellIsOn] <=60) blink_purple();
          if (SoC[WhichCellIsOn] > 60 && SoC[WhichCellIsOn] <=80) blink_purple_blue();
          if (SoC[WhichCellIsOn] > 80 && SoC[WhichCellIsOn] <=90) blink_blue();
          if (SoC[WhichCellIsOn] > 90) blink_slowblue();
         } else {
          blink_quickblue();  //overtime warning
        }//over
       } else {
        steady_red();  //deciding what to do
      }//charging
     } else {
      steady_blue();  //charged 
    }//already
  }//IsALARM
}//display()

void readVAvalues() {  // ---------------------------------- read V, A, values
  float n=0;
  float s=0;
  float VCmem=0;
  float VOmem=0;
  int x=0;
  for (x=1;x<=100;x++) {  // x nr. of readings as average filter
    n = analogRead(VcellPin);
    s = ((5.0 * n) / 1023);  // from cell
    VCmem=VCmem+s;
    n = analogRead(VoutPin);
    s = ((5.0 * n) / 1023);  // from LM317
    VOmem=VOmem+s;
  }
  s=(VCmem/(x-1));
  Vcell = (s + ((s * 1.40) /100));  // +- % arbitrary correction (not active if = 0.00)
  Vcell = roundThreeDec(Vcell);
  s=(VOmem/(x-1));
  Voutput = (s + ((s * 1.40) /100));  // +- % arbitrary correction (not active if = 0.00)
  Voutput = roundThreeDec(Voutput);
  s=(Voutput-Vcell);
  Vload = roundThreeDec(s);
  n=(Vload/Rload);
  Aload = roundThreeDec(n);
}//readVAvalues()

void readTemperatures() {  // --------------------------------- read temperatures
  int n=0;
  n = analogRead(TMP36_BatteryPin);
  TempBattery = (((5.0 * n) / 1023) - 0.500 ) * 100;  // (-500mV=offset) 0 to 500 = below zero values
  TempBattery = roundZeroDec(TempBattery);
  n = analogRead(TMP36_7812Pin);
  Temp7812 = (((5.0 * n) / 1023) - 0.500 ) * 100;
  Temp7812 = roundZeroDec(Temp7812);
  n = analogRead(TMP36_317Pin);
  Temp317 = (((5.0 * n) / 1023) - 0.500 ) * 100;
  Temp317 = roundZeroDec(Temp317);
  if ((Temp7812 < HighTRegL) && (Temp317 < HighTRegL) && (TempBattery < HighTBatL)) {  // current
    IsSlowDown = false;  // will normal chg current
  }
  if ((Temp7812 > HighTRegH) || (Temp317 > HighTRegH) || (TempBattery > HighTBatH)) {
    IsSlowDown = true;  // will decrease chg current
  }
  if (IsALARM==false) {
    if ((Temp7812 < FanTRLow) && (Temp317 < FanTRLow) && (TempBattery < FanTBLow)) {  // fan
      analogWrite(FanPWMPin, FanSpeed[0]);  // Fan normal temperatures
      WhichFanSpeedIsOn=FanSpeed[0];
    }
    if ((Temp7812 > FanTRHigh) || (Temp317 > FanTRHigh) || (TempBattery > FanTBHigh)) {
      analogWrite(FanPWMPin, FanSpeed[1]);  // Fan high temperatures
      WhichFanSpeedIsOn=FanSpeed[1];
    }
    if ((Temp7812 > RegulatorTAlarm) || (Temp317 > RegulatorTAlarm) || (TempBattery > BatteryTAlarm)) {  // ALARM
      IsALARM = true;  // will STOP charging etc.
      analogWrite(FanPWMPin, FanSpeed[2]);  // Fan ALARM temperatures
      WhichFanSpeedIsOn=FanSpeed[2];
    }
   } else {
    analogWrite(FanPWMPin, FanSpeed[2]);  // Fan ALARM
    WhichFanSpeedIsOn=FanSpeed[2];    
  }
}//readTemperatures()

void blink_red() {  // ----------------------------------------- led
  delay(500);
  digitalWrite(RGBledRedPin[WhichCellIsOn], HIGH);
}
void blink_redALARM() {
  digitalWrite(RGBledRedPin[1], LOW);
  digitalWrite(RGBledRedPin[2], LOW);
  digitalWrite(RGBledBluePin[1], LOW);
  digitalWrite(RGBledBluePin[2], LOW);
  delay(100);
  digitalWrite(RGBledRedPin[1], HIGH);
  delay(100);
  digitalWrite(RGBledRedPin[1], LOW);
  digitalWrite(RGBledRedPin[2], HIGH);
  delay(100);
}
void blink_blue() {
  delay(500);
  digitalWrite(RGBledBluePin[WhichCellIsOn], HIGH);
  delay(500);
}
void blink_purple() {
  delay(500);
  digitalWrite(RGBledRedPin[WhichCellIsOn], HIGH);
  digitalWrite(RGBledBluePin[WhichCellIsOn], HIGH);
  delay(500);
}
void blink_red_purple() {
  delay(500);
  digitalWrite(RGBledRedPin[WhichCellIsOn], HIGH);
  delay(1000);
  digitalWrite(RGBledBluePin[WhichCellIsOn], HIGH);
  delay(500);
}
void blink_purple_blue() {
  delay(500);
  digitalWrite(RGBledRedPin[WhichCellIsOn], HIGH);
  digitalWrite(RGBledBluePin[WhichCellIsOn], HIGH);
  delay(1000);
  digitalWrite(RGBledRedPin[WhichCellIsOn], LOW);
  digitalWrite(RGBledBluePin[WhichCellIsOn], HIGH);
  delay(500);
}
void blink_slowblue() {
  delay(1000);
  digitalWrite(RGBledBluePin[WhichCellIsOn], HIGH);
  delay(2000);
}
void blink_quickblue() {
  delay(100);
  digitalWrite(RGBledBluePin[WhichCellIsOn], HIGH);
  delay(100);
}
void steady_blue() {
  digitalWrite(RGBledBluePin[WhichCellIsOn], HIGH);
}
void steady_red() {
  digitalWrite(RGBledRedPin[WhichCellIsOn], HIGH);
}

void ledShow() {  // -------------------------------------------- initial show & Fan
  digitalWrite(RGBledBluePin[1], HIGH);
  digitalWrite(RGBledRedPin[1], LOW);
  digitalWrite(RGBledBluePin[2], LOW);
  digitalWrite(RGBledRedPin[2], LOW);
  delay(500);
  digitalWrite(RGBledBluePin[1], HIGH);
  digitalWrite(RGBledRedPin[1], HIGH);
  digitalWrite(RGBledBluePin[2], LOW);
  digitalWrite(RGBledRedPin[2], LOW);
  delay(500);
  digitalWrite(RGBledBluePin[1], LOW);
  digitalWrite(RGBledRedPin[1], HIGH);
  digitalWrite(RGBledBluePin[2], LOW);
  digitalWrite(RGBledRedPin[2], LOW);
  delay(500);
  digitalWrite(RGBledBluePin[1], LOW);
  digitalWrite(RGBledRedPin[1], LOW);
  digitalWrite(RGBledBluePin[2], HIGH);
  digitalWrite(RGBledRedPin[2], LOW);
  delay(500);
  digitalWrite(RGBledBluePin[1], LOW);
  digitalWrite(RGBledRedPin[1], LOW);
  digitalWrite(RGBledBluePin[2], HIGH);
  digitalWrite(RGBledRedPin[2], HIGH);
  delay(500);
  digitalWrite(RGBledBluePin[1], LOW);
  digitalWrite(RGBledRedPin[1], LOW);
  digitalWrite(RGBledBluePin[2], LOW);
  digitalWrite(RGBledRedPin[2], HIGH);
  delay(500);
  digitalWrite(RGBledBluePin[1], LOW);
  digitalWrite(RGBledRedPin[1], HIGH);
  digitalWrite(RGBledBluePin[2], LOW);
  digitalWrite(RGBledRedPin[2], HIGH);
  analogWrite(FanPWMPin, FanSpeed[1]);  // Fan high temperatures test
  delay(5000);
  analogWrite(FanPWMPin, FanSpeed[0]);
}//ledShow()

void serialPrintChg(const char msg[]) {  // -------------------------------- print charging
  Serial.print(msg);
  Serial.print(":");
  Serial.print(" Cell=");
  Serial.print(WhichCellIsOn);
  Serial.print(" Vdac=");
  Serial.print(SetVOutput,3);
  Serial.print(" Vcel=");
  Serial.print(Vcell,3);
  Serial.print(" Acel=");
  Serial.println(Aload,3);
}

void serialPrintDebug() {  // ----------------------------------------------- print debug serial & bluetooth
  Serial.print("TBatt=");  // temperatures
  Serial.print(TempBattery,0);
  Serial.print(" T7812=");
  Serial.print(Temp7812,0);
  Serial.print(" T317=");
  
  Serial.print(Temp317,0);
  Serial.print(" Fan=");
  Serial.print(WhichFanSpeedIsOn);
  Serial.println("/255");

  Serial.print("Cell=");  // VA values
  Serial.print(WhichCellIsOn);
  Serial.print(" VCel=");
  Serial.print(Vcell,3);
  Serial.print(" Vdac=");
  Serial.print(Voutput,3);
  Serial.print(" ACel=");
  Serial.println(Aload,3);

  Serial.print("IsAlrdy=");  // status
  Serial.print(IsAlreadyCharged);
  Serial.print(" IsCharg=");
  Serial.print(IsCharging);
  Serial.print(" IsOver=");
  Serial.print(IsOverTime);
  Serial.print(" IsALRM=");
  Serial.print(IsALARM);
  Serial.print(" IsSlow=");
  Serial.println(IsSlowDown);

  Serial.print("Start[1]=");  // charging times [1]
  Serial.print(ChTimeStart[1]);
  Serial.print(" End[1]=");
  if (ChTimeEnd[1] > 0) {
    Serial.print(ChTimeEnd[1]);
  } else {
    Serial.print("?");
  }
  Serial.print(" Time[1]=");
  if (ChTimeEnd[1] > 0) {
    Serial.print(long(ChTimeEnd[1] - ChTimeStart[1]));
  } else {
    Serial.print("?");
  }
  Serial.print(" VInit[1]=");
  Serial.print(VInitCell[1],3);
  Serial.print(" VFinal[1]=");
  Serial.print(VFinalCell[1],3);
  Serial.print(" Over[1]=");
  Serial.print(C_OverTime[1]);
  Serial.print(" Alarm[1]=");
  Serial.print(C_AlarmTime[1]);
  Serial.print(" Remain[1]=");
  Serial.print(TRemain[1]);
  Serial.print(" SoC%[1]=");
  Serial.println(SoC[1]);

  Serial.print("Start[2]=");  // charging times [2]
  Serial.print(ChTimeStart[2]);
  Serial.print(" End[2]=");
  if (ChTimeEnd[2] > 0) {
    Serial.print(ChTimeEnd[2]);
  } else {
    Serial.print("?");
  }
  Serial.print(" Time[2]=");
  if (ChTimeEnd[2] > 0) {
    Serial.print(long(ChTimeEnd[2] - ChTimeStart[2]));
  } else {
    Serial.print("?");
  }
  Serial.print(" VInit[2]=");
  Serial.print(VInitCell[2],3);
  Serial.print(" VFinal[2]=");
  Serial.print(VFinalCell[2],3);
  Serial.print(" Over[2]=");
  Serial.print(C_OverTime[2]);
  Serial.print(" Alarm[2]=");
  Serial.print(C_AlarmTime[2]);
  Serial.print(" Remain[2]=");
  Serial.print(TRemain[2]);
  Serial.print(" SoC%[2]=");
  Serial.println(SoC[2]);
  
  Serial.print("TimeNow=");
  Serial.println(ChTimeNow);

  lastPrintDebugMillis=millis();
}//serialPrintDebug()

// round zero decimal  // ----------------------------------------------- rounds
float roundZeroDec(float f) {
  float y, d;
  y = f*1;
  d = y - (int)y;
  y = (float)(int)(f*1)/1;
  if (d >= 0.5) {
    y += 1;
   } else {
    if (d < -0.5) {
      y -= 1;
    }
  }
  return y;
}
// round three decimals
float roundThreeDec(float f) {
  float y, d;
  y = f*1000;
  d = y - (int)y;
  y = (float)(int)(f*1000)/1000;
  if (d >= 0.5) {
    y += 0.001;
   } else {
    if (d < -0.5) {
      y -= 0.001;
    }
  }
  return y;
}

BTMonitor for Android (.apk)

BatchFile
Copy the file on your mobile phone or tablet and install it
No preview (download only).

BTMonitor source code (.aia)

BatchFile
To edit the .aia file import it at ai2.appinventor.mit.edu website
No preview (download only).

Credits

Marco Zonca
12 projects • 43 followers
"From an early age I learned to not use pointers"

Comments