I've built an RC battery discharge for 1s and 2s Lipo batteries. It has been an extremely fun project, but there have been slight issues along the way.
The current issue I am working on is the voltage under load. Reading the battery voltage with no load returns decently accurate results, but once I put a load on the battery, which is the idea, the reading is incorrect. How incorrect the reading is, depends on how big the load it. At 10A, the reading is off by about 0.04V. At 30A it is off by about 0.10V. Most of this seems to be in cell 2.
The voltage readings are done through a voltage divider of 2 5k1 resistors. I also added a filter cap of 1uF. The readings are averaged a lot, lol. But I have tried no average and high averages. I have tried to turn off the serial function, and have it on. I've tried without the filter cap.
In all honesty, I'm not sure if this is my code or hardware... But I'll put my code here, it is about 900 lines.
/*
Code Version 1.0
WMH Racing Battery Wizard
Written by Andrew Sarratore
Date: 10/28/2023
Code Version 1.1
Add getCurrentAverage()
add getFVoltageAverage()
add getCalculations()
add Program()
Changes to the "Run Program" Menu
Date:12/11/2023
Code Version 1.2
add getPVoltageAverage
add more readings to the dishcharge display
Code Version 1.3
Date:12/27/2023
add self calibrate VCC
add mAH reading, untested
add PID_v1.h library, still need to write PID code for the PWM value and replace current feedback loop
Code Version 1.4
Date:12/30/2023
Add IR struct - Untested
Add getIR function - still need to write the menu and page function
Code Version 1.4.2
Date:1/4/2024
Calibrate initial QOV based on VCC during setup
Fixed averages to be floats instead of int...
Adding IR Reading menus
Code Version 1.5
PID added, Kp=0.25, Ki=10, Kd=0
Added page 6 for displaying voltage data
Other items still needed
Data Return page
Voltage values every 15 seconds - look into graphing this, would be cool.
*/
#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"
#include "XPT2046_Touchscreen.h"
#include "Math.h"
#include "PID_v1.h"
#include "movingAvg.h"
// Define SPI pins for both display and touch
#define TFT_CS 10
#define TFT_DC 9
#define TFT_MOSI 11
#define TFT_CLK 13
#define TFT_RST 8
#define TFT_MISO 12
#define TS_CS 7
#define ROTATION 1
#define Isens A0
#define VFsens A1
#define VPsens A2
#define PWM 3
#define K (1.0/30)
char currentPage;
// Use hardware SPI (on Uno, #13, #12, #11) and the above for CS/DC/RST
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
XPT2046_Touchscreen ts(TS_CS);
// calibration values
float xCalM = -0.09, yCalM = -0.06; // gradients
float xCalC = 329.36, yCalC = 248.13; // y axis crossing points
int8_t blockWidth = 20; // block size
int8_t blockHeight = 20;
int16_t blockX = 0, blockY = 0; // block position (pixels)
class ScreenPoint {
public:
int16_t x;
int16_t y;
// default constructor
ScreenPoint(){
}
ScreenPoint(int16_t xIn, int16_t yIn){
x = xIn;
y = yIn;
}
};
class Button {
public:
int x;
int y;
int width;
int height;
char *text;
Button(){
}
void initButtonG(int xPos, int yPos, int butWidth, int butHeight, char *butText){
x = xPos;
y = yPos;
width = butWidth;
height = butHeight;
text = butText;
renderG();
}
void initButtonR(int xPos, int yPos, int butWidth, int butHeight, char *butText){
x = xPos;
y = yPos;
width = butWidth;
height = butHeight;
text = butText;
renderR();
}
void initButtonB(int xPos, int yPos, int butWidth, int butHeight, char *butText){
x = xPos;
y = yPos;
width = butWidth;
height = butHeight;
text = butText;
renderB();
}
void initButtonY(int xPos, int yPos, int butWidth, int butHeight, char *butText){
x = xPos;
y = yPos;
width = butWidth;
height = butHeight;
text = butText;
renderY();
}
void renderG(){
tft.fillRect(x,y,width,height,ILI9341_GREEN);
tft.setCursor(x+5,y+5);
tft.setTextSize(2);
tft.setTextColor(ILI9341_WHITE);
tft.print(text);
}
void renderR(){
tft.fillRect(x,y,width,height,ILI9341_RED);
tft.setCursor(x+5,y+5);
tft.setTextSize(2);
tft.setTextColor(ILI9341_WHITE);
tft.print(text);
}
void renderB(){
tft.fillRect(x,y,width,height,ILI9341_BLUE);
tft.setCursor(x+5,y+5);
tft.setTextSize(2);
tft.setTextColor(ILI9341_WHITE);
tft.print(text);
}
void renderY(){
tft.fillRect(x,y,width,height,ILI9341_YELLOW);
tft.setCursor(x+5,y+5);
tft.setTextSize(2);
tft.setTextColor(ILI9341_WHITE);
tft.print(text);
}
bool isClicked(ScreenPoint sp) {
if ((sp.x >= x) && (sp.x <= (x+width)) && (sp.y >= y) && (sp.y <= (y+height))){
return true;
}
else {
return false;
}
}
};
unsigned const int numChan = 25;
unsigned int chan;
struct ChannelData {
float startVoltage;
float endVoltage;
float voltageDrop;
float internalResistance;
};
ChannelData Channels[numChan];
//Touch Screen
ScreenPoint getScreenCoords(int16_t x, int16_t y){
int16_t xCoord = round((x * xCalM) + xCalC);
int16_t yCoord = round((y * yCalM) + yCalC);
if(xCoord < 0) xCoord = 0;
if(xCoord >= tft.width()) xCoord = tft.width() - 1;
if(yCoord < 0) yCoord = 0;
if(yCoord >= tft.height()) yCoord = tft.height() - 1;
return(ScreenPoint(xCoord, yCoord));
}
// I can probably make an array of the following button variables...
Button btnIP;// Current Selection +
Button btnIM;// Current Selection -
Button btnVP;// Cutoff V +
Button btnVM;// Cutoff V -
Button btnMI;// Main - Discharge Rate
Button btnMV;// Main - Voltage Cutoff
Button btnMM;// Main Menu on other pages
Button btnCT;// Main Calibrate Touch
Button btnRP;// Main Menu - Run Program
Button btnPN;// Run/Running
Button btnMN;// Discharge - Main Menu
Button btnRN;// STOP
Button btnIR;// Main - IR Reading
Button btnRC;// IR Reading - Check IR
Button btnDD;// Discharge - dischargeData
double IV = 30; // Initial rate of discharge in ampres. User changable in code
float VV = 350.00; // Cutoff voltage*100 so ++ works to change the value
unsigned long StartTime; // Start time to measure time of discharge
unsigned long CurrentTime; // Will give how long the discharge is running
//PID
double Input;
double Output;
//Specify the links and initial tuning parameters
double Kp=.01, Ki=15, Kd=0;
PID myPID(&Input, &Output, &IV, Kp, Ki, Kd, DIRECT);
// Current Variables
float VCC=0;// 4.967 measure the value(Measure value not needed with the VCC calibration code)
float VCCC=0;
float QOV=0; // Calibration happens in setup
float sens=0.04; //sensitivity of the current sensor
float iavg; // analogRead(Isens)/IN
float Icurrent=0; // Current Reading in Amps
float Icorrected=0; //iavg-IOFF
float voltageI=0; // iavg translated to voltage
float IOFF=0; // Offset to the raw read to zero current
int IN=75; // Sample number to average current
int mAH;
int rTmAH;
unsigned long runTime;
// Battery Voltage Variables (may be able to make a struct or class here as well)
int ValueR1F=5094;//Measured value
int ValueR2F=5104;//Measured value
int ValueR1P=5106;//Measured value
int ValueR2P=5082;//Measured value
int VFN=1000; // Sample number for average of analogRead(VFsens) Full voltage
int VPN=1000;// Sample number for average of analogRead(VPsens) Partial voltage
float vfavg; // Average of the analogRead(VFsens)/VFN -Full Voltage
float vpavg; // Average of the analogRead(VPsens)/VPN -Partial Voltage
float Fvoltage=0; // Calculation of FV seen by MCU
float Pvoltage=0; // Calculation of PV seen by MCU
float voltageFull=0;// Calculation of Full Voltage
float voltagePartial=0;// Calculation of Partial Voltage
float cell1=0; // Voltage of Cell 1
float cell2=0; // Voltage of Cell 2
//float avgLFV; // Leaky Average Full Voltage
//float LFvoltage;//Leaky Full Voltage
//float voltageLFull;// Leaky after divider
//float avgLPV;
//float LPvoltage;
//float voltageLPartial;
boolean isrunning=false;
boolean irRunning=false;
int zz=0;// variable used for analogWrite(PWM,zz)
float internalResistanceAVG=0;
//===================================================================SETUP=========================================================================================
void setup() {
Serial.begin(115200);
// avoid chip select contention
pinMode(TS_CS, OUTPUT);
digitalWrite(TS_CS, HIGH);
pinMode(TFT_CS, OUTPUT);
pinMode(Isens, INPUT);
pinMode(VFsens, INPUT);
pinMode(VPsens, INPUT);
pinMode(PWM, OUTPUT);
digitalWrite(TFT_CS, HIGH);
tft.begin();
tft.setRotation(ROTATION);
tft.fillScreen(ILI9341_BLACK);
ts.begin();
ts.setRotation(ROTATION);
//calibrateTouchScreen(); Leaving here for first run of the dispaly to get coordinates to add
getAccurateVoltage();
getCurrentAverage();
getFVoltageAverage();
getPVoltageAverage();
getCalculations();
calibrateQOV();
//initialize the variables we're linked to
Input = Icurrent;
//turn the PID on
myPID.SetMode(AUTOMATIC);
currentPage = '0'; // Indicates that we are at Home Screen
Home();
}
//unsigned long lastFrame = millis();
//==================================================================END SETUP=====================================================================
//======================================================================LOOP======================================================================
void loop() {
ScreenPoint sp;
// limit frame rate
//while((millis() - lastFrame) < 20); //(caused issues with millis function to get mAH, may need to revisit as this helped some flickering)
//lastFrame = millis();
getAccurateVoltage();
getCalculations();
//SerialData();
// Home Screen
if(currentPage=='0'){
if (ts.touched()) {
TS_Point p = ts.getPoint();
sp = getScreenCoords(p.x, p.y);
if(btnMV.isClicked(sp)){
currentPage='1'; // Go to Discharge Current
tft.fillScreen(ILI9341_BLACK);
discharge();
}
else if(btnMI.isClicked(sp)){
currentPage='2'; // Go to Cutoff Voltage
tft.fillScreen(ILI9341_BLACK);
cutoff();
}
else if(btnCT.isClicked(sp)){
currentPage='3'; // Go to Calibrate Screen
tft.fillScreen(ILI9341_BLACK);
calibrateTouchScreen();
}
else if(btnRP.isClicked(sp)){
currentPage='4'; // Go to Run Program
tft.fillScreen(ILI9341_BLACK);
program();
}
else if(btnIR.isClicked(sp)){
currentPage='5'; // Go to irReading
tft.fillScreen(ILI9341_BLACK);
irReading();
}
}
}
// Discharge Current
if(currentPage=='1'){
tft.setCursor(100,150);
tft.print(IV, 0);
tft.setCursor(190,150);
tft.print("A");
if (ts.touched()) {
TS_Point p = ts.getPoint();
sp = getScreenCoords(p.x, p.y);
if(btnIP.isClicked(sp)){
if (IV<45){
IV++;
}
tft.fillRect(100,150,125,50,ILI9341_BLACK);
delay(100);
}
else if (btnIM.isClicked(sp)){
IV--;
tft.fillRect(100,150,125,50,ILI9341_BLACK);
delay(100);
}
else if (btnMM.isClicked(sp)){
currentPage='0';
Home();
}
}
}// End Page 1
// Cutoff Voltage
if(currentPage=='2'){
tft.setCursor(100,150);
tft.print(VV/100);
tft.setCursor(210,150);
tft.print("V");
if (ts.touched()) {
TS_Point p = ts.getPoint();
sp = getScreenCoords(p.x, p.y);
if(btnIP.isClicked(sp)){
VV++;
tft.fillRect(100,150,100,50,ILI9341_BLACK);
delay(50);
}
else if (btnIM.isClicked(sp)){
if(VV>320){
VV--;
}
tft.fillRect(100,150,100,50,ILI9341_BLACK);
delay(50);
}
else if (btnMM.isClicked(sp)){
currentPage='0';
Home();
}
}
}// End Page 2
// Run Discharge (Surely I can move some of this to the page function, but when moved it caused the screen to refresh every loop)
if(currentPage=='4'){
tft.setCursor(0,50);
tft.setTextSize(2);
tft.setTextColor(ILI9341_WHITE,ILI9341_BLACK);
tft.print("Battery:");
tft.setCursor(0,75);
tft.print("Cutoff ");
tft.print(VV/100);
tft.print("V");
tft.setTextSize(2);
tft.setCursor(0,100);
tft.print("Actual ");
tft.print(voltageFull,2);
tft.setCursor(145,100);
tft.print("V");
tft.setCursor(160,50);
tft.print("Current:");
tft.setCursor(160,75);
tft.print("Rate ");
tft.print(IV, 0);
tft.print("A");
tft.setCursor(160,100);
tft.print("Actual ");
tft.print(Icurrent,1);
tft.setCursor(305,100);
tft.print("A");
tft.setTextSize(1);
tft.setCursor(0,120);
tft.print("Cell 1 ");
tft.setCursor(45,120);
tft.print(cell1,3);
tft.setCursor(80,120);
tft.print("V");
tft.setCursor(0,130);
tft.print("Cell 2 ");
tft.setCursor(45,130);
tft.print(cell2,3);
tft.setCursor(80,130);
tft.print("V");
tft.setCursor(160,120);
tft.print("Time");
tft.setCursor(160,130);
tft.fillRect(160,130,50,10,ILI9341_BLACK);
tft.print(runTime);
tft.setCursor(240,120);
tft.print("mAH");
tft.setCursor(240,130);
tft.print(rTmAH);
if (isrunning==true) {
btnPN.initButtonR(0,15,140,25,"DISCHARGE");
RunDischarge();
}
else
{
tft.setTextColor(ILI9341_WHITE,ILI9341_GREEN);
btnPN.initButtonG(0,15,140,25," RUN");
}
if (ts.touched()) {
TS_Point p = ts.getPoint();
sp = getScreenCoords(p.x, p.y);
if (btnMN.isClicked(sp)){
currentPage='0';
Home();
}
else if (btnRN.isClicked(sp)){
zz=0;
analogWrite(PWM, zz);
isrunning=false;
}
else if (btnPN.isClicked(sp)){
tft.fillRect(160,130,320,10,ILI9341_BLACK);
rTmAH=0;
runTime=0;
StartTime=millis();
isrunning=true;
}
else if (btnDD.isClicked(sp)){
tft.fillScreen(ILI9341_BLACK);
currentPage='6';
dischargeData();
}
}
}// End Page 4
// IR Reading
if(currentPage=='5'){
tft.setCursor(0,120);
tft.setTextSize(4);
tft.setTextColor(ILI9341_WHITE,ILI9341_BLACK);
tft.print(internalResistanceAVG);
tft.setCursor(130,120);
tft.print("m");
tft.drawChar(155, 120, 233, ILI9341_WHITE,4,4);
if (irRunning==true) {
btnRC.initButtonR(90,15,140,25,"Reading IR");
getIR();
}
else
{
tft.setTextColor(ILI9341_WHITE,ILI9341_GREEN);
btnRC.initButtonG(90,15,140,25," Check IR ");
}
if (ts.touched()) {
TS_Point p = ts.getPoint();
sp = getScreenCoords(p.x, p.y);
if (btnMM.isClicked(sp)){
currentPage='0';
Home();
}
else if (btnRC.isClicked(sp)){
irRunning=true;
}
}
}// End Page 5
if(currentPage=='6'){
//dischargeData();
if (ts.touched()) {
TS_Point p = ts.getPoint();
sp = getScreenCoords(p.x, p.y);
if (btnMM.isClicked(sp)){
currentPage='0';
Home();
}
}
}// End Page 6
delay(0);
}
//===========================================END LOOP=============================================
// Home Screen CurrentPage=0
void Home(){
tft.fillScreen(ILI9341_BLACK);
tft.setCursor(50,0);
tft.setTextColor(0x07FF);
tft.setTextSize(4);
tft.print("MAIN MENU");
tft.setTextColor( 0x07FF);
tft.setTextSize(3);
tft.setCursor(0,40);
tft.print("1.");
tft.setCursor(0,80);
tft.print("2.");
tft.setCursor(0,120);
tft.print("3.");
tft.setCursor(0,160);
tft.print("4.");
tft.setCursor(0,200);
tft.print("5.");
btnMV.initButtonG(50,40,200,25,"Discharge Rate");
btnMI.initButtonG(50,80,200,25,"Voltage Cutoff");
btnCT.initButtonG(50,120,200,25,"Calibrate Screen");
btnRP.initButtonG(50,160,200,25,"Run Discharge");
btnIR.initButtonG(50,200,200,25,"IR Reading");
// add button for getIR function page, adjust button heights to fit, or make 2 colums and adjust width...
}
// Discharge Current Selection CurrentPage=1
void discharge(){
btnIM.initButtonR(80,75,80,60,"-");
btnIP.initButtonG(160,75,80,60,"+");
btnMM.initButtonB(90,200,140,25," Main Menu");
tft.setCursor(50,0);
tft.setTextSize(4);
tft.setTextColor(ILI9341_YELLOW);
tft.print("DISCHARGE");
tft.setCursor(70,40);
tft.print("CURRENT");
tft.setCursor(100,150);
tft.print(IV, 0);
tft.setCursor(190,150);
tft.print("A");
}// End discharge
// Cutoff Voltage Selection CurrentPage=2
void cutoff(){
btnIM.initButtonR(80,75,80,60,"-");
btnIP.initButtonG(160,75,80,60,"+");
btnMM.initButtonB(90,200,140,25," Main Menu");
tft.setCursor(80,0);
tft.setTextSize(4);
tft.setTextColor(ILI9341_YELLOW);
tft.print("VOLTAGE");
tft.setCursor(95,40);
tft.print("CUTOFF");
}// End Cutoff
// Run Program CurrentPage=4
void program(){
btnPN.initButtonG(0,15,140,25," RUN");
btnMN.initButtonB(90,200,140,25," Main Menu");
btnRN.initButtonR(160,15,140,25," STOP ");
btnDD.initButtonY(90,150,140,25," DATA ");
}// End Program
// IR Reading CurrentPage=5
void irReading(){
btnRC.initButtonG(90,15,140,25," Check IR ");
btnMM.initButtonB(90,200,140,25," Main Menu");
}// End IR Reading
void dischargeData(){
btnMM.initButtonB(90,200,140,25," Main Menu");
}// End dischargeData
void calibrateTouchScreen(){
TS_Point p;
int16_t x1,y1,x2,y2;
tft.fillScreen(ILI9341_BLACK);
// wait for no touch
while(ts.touched());
tft.drawFastHLine(10,20,20,ILI9341_RED);
tft.drawFastVLine(20,10,20,ILI9341_RED);
while(!ts.touched());
delay(50);
p = ts.getPoint();
x1 = p.x;
y1 = p.y;
tft.drawFastHLine(10,20,20,ILI9341_BLACK);
tft.drawFastVLine(20,10,20,ILI9341_BLACK);
delay(500);
while(ts.touched());
tft.drawFastHLine(tft.width() - 30,tft.height() - 20,20,ILI9341_RED);
tft.drawFastVLine(tft.width() - 20,tft.height() - 30,20,ILI9341_RED);
while(!ts.touched());
delay(50);
p = ts.getPoint();
x2 = p.x;
y2 = p.y;
tft.drawFastHLine(tft.width() - 30,tft.height() - 20,20,ILI9341_BLACK);
tft.drawFastVLine(tft.width() - 20,tft.height() - 30,20,ILI9341_BLACK);
int16_t xDist = tft.width() - 40;
int16_t yDist = tft.height() - 40;
// translate in form pos = m x val + c
// x
xCalM = (float)xDist / (float)(x2 - x1);
xCalC = 20.0 - ((float)x1 * xCalM);
// y
yCalM = (float)yDist / (float)(y2 - y1);
yCalC = 20.0 - ((float)y1 * yCalM);
currentPage='0';
Home();
/* // Serial print the actual coordinates from the touch calibrate, enter into the global variables, for first run of the screen
Serial.print("x1 = ");Serial.print(x1);
Serial.print(", y1 = ");Serial.print(y1);
Serial.print("x2 = ");Serial.print(x2);
Serial.print(", y2 = ");Serial.println(y2);
Serial.print("xCalM = ");Serial.print(xCalM);
Serial.print(", xCalC = ");Serial.print(xCalC);
Serial.print("yCalM = ");Serial.print(yCalM);
Serial.print(", yCalC = ");Serial.println(yCalC);
*/
}// END Calibrate
// Current Average
void getCurrentAverage() {
int ii; double rawIRead;
for(int ii=0; ii<IN; ii++)
{
//analogRead(Isens);
rawIRead+=analogRead(Isens);
}
iavg=rawIRead/IN;
Icorrected=(iavg-IOFF);
}// end getCurrent
// Full voltage average (2s voltage if a 2s battery or 1s voltage for a 1s battery)
void getFVoltageAverage() {
int jj; float rawVFRead;
for(int jj=0; jj<VFN; jj++)
{
//analogRead(VFsens);
rawVFRead+=analogRead(VFsens)+1;
}
vfavg=rawVFRead/VFN;
}// end getFVoltage
/*
void getLeakyFVoltage(){
float sampF=analogRead(VFsens);
avgLFV += K*(sampF-avgLFV);
}
*/
/*
void getLeakyPVoltage(){
float sampP=analogRead(VPsens);
avgLPV =+ K*(sampP-avgLPV);
}
*/
// Partial Voltage average (only applies for 2s, this is cell 2 voltage)
void getPVoltageAverage() {
int kk; float rawVPRead;
for(int kk=0; kk<VPN; kk++)
{
// analogRead(VPsens);
rawVPRead+=analogRead(VPsens)+2;
}
vpavg=rawVPRead/VPN;
}// end Partial Voltage
// Runs the main discharge function
void RunDischarge() {
if (isrunning==true) {
if (cell1>VV/100 && cell2>VV/100)
{
Input = Icurrent;
myPID.Compute();
analogWrite(PWM, Output);
}
else if (cell1<VV/100 || cell2<VV/100)
{
zz=0;
analogWrite(PWM, zz);
isrunning=false;
}
}
} // end runDischarge
void getCalculations() {
getAccurateVoltage();
getCurrentAverage();
voltageI=Icorrected*(VCC/1023);
Icurrent=abs((voltageI-QOV)/sens);
getFVoltageAverage();
Fvoltage=vfavg*(VCC/1023);
voltageFull=((Fvoltage*(ValueR1F+ValueR2F))/ValueR2F);
getPVoltageAverage();
Pvoltage=vpavg*(VCC/1023);
voltagePartial=((Pvoltage*(ValueR1P+ValueR2P))/ValueR2P);
cell1=voltageFull-voltagePartial;
if (isrunning==true) {
cell2=voltagePartial;
}
else
{
cell2=voltagePartial;
}
VCC=VCCC/1000;
if (isrunning==true) {
CurrentTime=(millis()-StartTime)/1000;
}
if (CurrentTime>runTime) {
runTime=CurrentTime;
}
else if (isrunning==false) {
CurrentTime=0;// Need to change to keep the previous runtime around, but have a reset data button to zero time
}
mAH=((Icurrent/3.6)*CurrentTime);
if (mAH>rTmAH) {
rTmAH=mAH;
}
}// end Calculations
// calibrates the VCC using the bandgap ref - Something is amiss though, resoltion is 30mV instead of 3mV - need to work on this
void getAccurateVoltage() {
int vv; float rawgetVoltage=0;
for (vv=0; vv<10; vv++)
{
getVoltage();
rawgetVoltage+=getVoltage();
}
VCCC = rawgetVoltage/10;
}
// Read the voltage of the battery the Arduino is currently running on (in millivolts)
float getVoltage() {
const long InternalReferenceVoltage = 1080; // Adjust this value to your boards specific internal BG voltage x1000
ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<ADLAR) | (1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (0<<MUX0);
ADCSRA |= _BV( ADSC ); // Start a conversion
while( ( (ADCSRA & (1<<ADSC)) != 0 ) ); // Wait for it to complete
float results = (((InternalReferenceVoltage * 1024) / ADC) + 5); // Scale the value; calculates for straight line value
return results;
}// end VCC calibration
//Internal Resistance Measurement (This is untested and need menu and page written)
void getIR() {
if (isrunning==false) {
irRunning=true;
for (int chan=0; chan<(numChan-1); chan++)
{
tft.fillRect(0,120,130,40,ILI9341_BLACK);
getCalculations();
Channels[chan].startVoltage=voltageFull; // record unloaded voltage of battery
zz=50; // Will be appx 20A with 2s, may split this into if statements based on current battery voltage
analogWrite(PWM, zz);//Turn Mosfets on
delay(0);//delay to stabalize - may need to fine tune this delay, but the shorter the better
getCalculations();
Channels[chan].endVoltage=voltageFull; // record voltage of battery under load
zz=0;//
analogWrite(PWM, zz);//Turn off the mosfets
Channels[chan].voltageDrop=Channels[chan].startVoltage-Channels[chan].endVoltage;// Voltage drop from the load
Channels[chan].internalResistance=(Channels[chan].voltageDrop/Icurrent)*1000;//Ohms Law V=IR, R=V/I, Readings in mΩ
delay(0);// Allow for stablization between readings - may need to fine tune this delay, but start on the high side
}
for (int chan=0; chan<(numChan-1); chan++) {
internalResistanceAVG=+Channels[chan].internalResistance;// run the readings numChan times and average the IR
}
}//End if
irRunning=false;
}//end getIR
void calibrateQOV() {
analogRead(Isens);
QOV=(analogRead(Isens)*VCC)/1023;
}
void SerialData() {
getCalculations();
Serial.println("----------------------------------------");
Serial.println();
Serial.print(VCC);
Serial.print("V VCC ");
Serial.print(QOV);
Serial.print(" QOV ");
Serial.println();
Serial.print(analogRead(Isens));
Serial.print(" RAW Read ");
Serial.print(Icorrected);
Serial.print(" Corrected ");
Serial.print(voltageI);
Serial.print("V ");
Serial.print(Icurrent);
Serial.print(" Amps ");
Serial.print(iavg);
Serial.print(" AVG ");
Serial.print(Output);
Serial.println();
Serial.print(voltageFull);
Serial.print(" V Full ");
Serial.print(vfavg);
Serial.print(" ACT ");
Serial.print(analogRead(VFsens));
Serial.print(" ");
Serial.println();
Serial.print(voltagePartial);
Serial.print(" ");
Serial.print(" V Partial ");
Serial.print(vpavg);
Serial.print(" ACT ");
Serial.print(analogRead(VPsens));
Serial.println();
Serial.print("Cell 1 ");
Serial.print(cell1,3);
Serial.print("V Cell 2 ");
Serial.print(cell2,3);
Serial.print("V");
Serial.println();
}





