Arduino 4x Battery Capacity Tester, Need Software Help - Page 1
Forum section: electronics / general

author: iamdarkyoshi
author_rank: Frequent Contributor
timestamp: July 01, 2016, 08:47:57 pm
content: This time, it is more complicated and I will be using EVERY standard IO pin on the arduino Micro.The purpose of this device is to discharge up to FOUR lithium batteries and count the Ah that was drained from them and display it on the screen.Here is the old one (which worked PERFECTLY) but only did one cell at a time and used a different display:https://linustechtips.com/main/topic/612434-well-it-has-been-a-while-but-i-am-back-into-arduino-and-i-need-helpHere is the pinout of the arduino I will be using:Digital Output Pin 0 - LCD EDigital Output Pin 1 - LCD RSDigital Output Pin 2 - LCD D4Digital Output Pin 3 - LCD D5Digital Output Pin 4 - LCD D6Digital Output Pin 5 - LCD D7Digital Input Pin 7 - Test ButtonDigital Output Pin 9 - Enable Battery 1Digital Output Pin 10 - Enable Battery 2Digital Output Pin 11 - Enable Battery 3Digital Output Pin 12 - Enable Battery 4Digital Output Pin 13 - BeeperPin A0 - Battery Current 1Pin A1 - Battery Voltage 1Pin A2 - Battery Current 2Pin A3 - Battery Voltage 2Pin A4 - Battery Current 3Pin A5 - Battery Voltage 3Pin A7 - Battery Current 4Pin A8 - Battery Voltage 4Note that digital IO pins 6 and 8 are the same pins as analog pins A7 and A8, respectively.Here is what I want it to do:Display counted amp hours in each section of the 1602 LCD, so it would look like this:1.234Ah 5.678Ah9.012Ah 3.456AhWith the first one being the first battery, second one being the second battery, and so on.The Test Button will reset all four counters and start all four battery slots discharging.However, if there is already a battery discharging, its display slot should NOT be reset and that battery should keep discharging.Batteries should STOP discharging once they reach a raw analog measurement on the Battery Voltage Pin of 620.Every second, the arduino should run this:measuredCurrent = ((analogRead(A1)-(analogRead(A0)))*1.183); //convert raw data to mALook below for the full code.These calculations are used to calculate mA for the respective battery.This should then go through some more math to calculate Amp Hours. Here is how the old code did it:ampHourRating += ((measuredCurrent)/ (SECONDS_PER_HOUR)/1000);Look below for the full code.One the battery finishes discharging, turn off its respective Enable Battery Pin which will stop it discharging. Do not start discharging until the Test button is pressed again.Leave its value on the screen and do not reset it until I press the test button.Sound the Beeper for 1 second by making it go high for one second to give an audible cue that a battery finished.This is the code for the old one with the help of member Pinguinsan from the LTT forums:http://pastebin.com/xUvu8jfiThat code was meant for a 3 digit 7 segment LED display, this time I am using a 1602 LCD display, which uses less pins and shows more info. It also uses a different library. The LiquidCrystal library is much easier to use.The old code was only meant for ONE battery. This project is meant to discharge four batteries.If anyone could give me some advice on how I should write the code for this, that would be appreciated. This topic will also be posted on the LinusTechTips Forums.

author: Buriedcode
author_rank: Super Contributor
timestamp: July 01, 2016, 10:19:23 pm
content: Sounds like you know what you need to do, so I'm not sure what the problem is? You have an enable for each battery, which I assume is a button/switch input, so you won't need a menu system to turn on/off discharges. If you have those 4 inputs spare you could use buttons for prev, next, enter, cancel/back. The main routine would just loop through taking measurements - the difficult part is the programmable load, and thats it.Without the seven segment display you dont' have to refresh anything, so as you pointed out, makes life easier. You won't need serial as you can see it from the display. Rather than rewrite that code, I would just start from scratch. It seems quite sequential with little user input so you just start it and it'll loop, taking measurements, turning off loads as their batteries reach the minimum voltage, until the last battery reaches that level.

author: MosherIV
author_rank: Super Contributor
timestamp: July 01, 2016, 10:40:37 pm
content: HiAs buriedcode said you know what you want it to do.I would not throw away your old code (I have not looked at it by the way).You can salvage some of it eg the code to calculte the charge.It is time to think about structured code.Think objects eg battery, switch, display.You want a main loop which calls each battery object, display object and the switch object.Each object looks after its own behaviour eg battery knows if it is discharging or not, calculates its capacityThe tircky part is object interaction, ie when the switch is pressed how does it tell the battery objects to start discharging.I suggest each battery object have a startDischarge() method and the switch object call them. Each battery object already knows if it is discharging or not so can start if not already discharging.The battery object should call the display object with the result when it is done.Hope this helps.

author: dannyf
author_rank: Super Contributor
timestamp: July 02, 2016, 01:08:06 am
content: QuoteThe easiest way to re-use your old code is to put all battery-related variables into a struct and pass that struct (pointer to that struct actually) to your routines.BTW, you should try to get your code working without resorting to floating point math.

author: iamdarkyoshi
author_rank: Frequent Contributor
timestamp: July 02, 2016, 01:10:04 am
content: The issue here is that I barely know what you guys are talking aboutI havent written arduino code in years and never properly learned it

author: retrolefty
author_rank: Super Contributor
timestamp: July 02, 2016, 01:33:36 am
content: Quote from: iamdarkyoshi on July 02, 2016, 01:10:04 amSo you wish to build it but have someone else do the programming rather then learn how to program it yourself? That's bound to not get you many useful responses except perhaps some very bored retired type.

author: iamdarkyoshi
author_rank: Frequent Contributor
timestamp: July 02, 2016, 01:42:45 am
content: Quote from: retrolefty on July 02, 2016, 01:33:36 amQuote from: iamdarkyoshi on July 01, 2016, 08:47:57 pm

author: CustomEngineerer
author_rank: Frequent Contributor
timestamp: July 02, 2016, 07:28:05 am
content: Quote from: iamdarkyoshi on July 01, 2016, 08:47:57 pmThis is exactly what people were doing, giving you advice on how to write the code. Looking at the other site you posted to, it appears the same guy that wrote the code for your first version has also already written the code for this new one. There is nothing wrong with asking for someone else to write the code for you, but thats what you should ask for if thats your intent.

author: dannyf
author_rank: Super Contributor
timestamp: July 02, 2016, 10:05:41 am
content: QuoteSounds like a perfect opportunity for you to learn.

author: Ian.M
author_rank: Super Contributor
timestamp: July 02, 2016, 12:11:55 pm
content: I dislike 'pin-bound' designs. If you don't have a couple of spare pins it makes it very difficult to extend and even debug the project as you don't even have the option to add a blinking LED.The key to freeing up pins for projects using a parallel HD44780 LCD is that when the LCD E strobe is inactive, you can freely use the LCD data and R/S lines for other purposes. e.g. with 5 Schottky diodes to provide isolation, they can be used to scan five buttons (enough for a cursor pad + a select button) into a single digital input. Add a parallel latch, and you can control five outputs using only a single extra pin to control the latch. Alternatively switch to an I2C backpack for the LCD and get back 4 pins immediately + share the I2C bus with any other peripherals you need.You *MAY* want to keep the battery enable outputs direct rather than latched so you can PWM them to maintain a constant discharge current. Also, if the beeper is a simple sounder rather than a buzzer, you may need a PWM pin for that as well.However more buttons would be worth it for a more menu driven flexible UI especially if you want to add the capability to adjust the 100% discharge cutoff voltage, or to adjust the average discharge current to suit different nominal battery capacities.On the software side of things, you'll need a non-blocking state machine for each battery, running as a task per battery in a superloop. You'll also probably need a monitor task that reads the button(s), manages the display and interfaces to each battery task. That's pretty standard stuff for a realtime control application that has multiple concurrent tasks to handle, without a RTOS to provide multi-threading.

author: Buriedcode
author_rank: Super Contributor
timestamp: July 02, 2016, 12:25:26 pm
content: I generally just leave people to it rather than write any code for them, but if you're pretty new to it all, then it can be daunting because often people open up the IDE and just stare at the blank screen.Whilst I rarely do it these days, I almost always planned software for micros with a flow chart/diagram. You start off with very basic commands like 'read battery voltage', or 'if this button do this'. A diagram is, I think, a bit nicer than just text. Then start on pseudo-code, where you convert that program flow into very basic language - providing instructions that anyone can follow, being very specific about what needs to be done, so there is no ambiguity.You then should go through each stage, and pad it out, either by translating it directly to code, or more likely, just more detailed pseudo-code. The arduino IDE gets you started with two simple routines: 'setup', and 'loop'. As you probably know, the 'setup' is run once at start-up - this is where you set IO's as inputs or outputs, set default values for variables, configure hardware peripherals etc..The loop is as basic as it gets - it will run forever unless the MCU is reset, the power is turned off (obviously) or there are certain commands like 'break', which will leave the main loop, and start back on the setup routine.I can't say I'm overly experienced in coding, but I almost always have at the very least - notepad - open making notes before and during writing the code. It helps to get idea's down and keep track of the main structure. For example, in your project I would write down 'things the MCU needs to do:- turn on active load/constant current sink for any battery that is enabled.- Measure each battery's voltage.- Measure each ones current- Check voltage is not < minimum.- display voltages on LCDperhaps not in any order, but at least these can be thought of as 'blocks' of code.Then there's variables. How many do you need? what sorts of things do we need to store? Are they variable at all or constant throughout? As you have four batteries, it is likely that each battery will need a few variables, namely, voltage, current, discharge_time, perhaps even starting_voltage.As they are the same variables for each battery, you could create a structure (http://playground.arduino.cc/Code/Struct) that encompasses all the above. Or have each variable as an array, for example:Code:unsigned int BattVoltage = {0,0,0,0};Creates an array of 16-bit variables, with 4 members.You can read/write to these by pointing to each one with BatteryVoltage where 'j' is a number 0-3 (arrays first entry is at '0'). This way you can write just one routine for measuring voltage and current, converting them into mV, or mA but each time changing the variable 'i' to select a different battery. This is why loops are so useful - if the same thing(s) must be done multiple times, perhaps for multiple things, a for-loop can go through each iteration.Similarly, you're writing a voltage to the LCD, the only variables being - print location (starting location, and line) and the number it prints. So you could have your voltages stored in global arrays/variables, and have a subroutine called whose only purpose is to read those values and print them on the LCD. It can be passed things that change such as LCD location, line, and voltage meaning you can just write a routine and call it with 'LCD_print_volts(<start_loc><line><value for voltage>)'.There are so many 'ways' of doing these things, some more complicated than others, some more efficient. I learned simply by setting myself a task of writing code that did something useful, and writing out a rough plan of what it should do, and how it should do it, and then... most likely getting stuck very easily - googling for examples on how to do specific things. I believe people get stuck because they don't clearly define what it is they want to do, that is why writing pseudo-code is a good start, if you can break everything down into very simple terms, it makes it more specific, and so you can google 'reading voltages on arduino ADC' rather than 'how to arduino battery'. This way, you may have a lots of things you'll have to research to understand, but each one is quite simple. So instead of wasting time just sitting at a blank arduino project you have things 'to do'.Apologies for the long disjointed rant. I have no idea if you've written code for anything before, so I assumed you hadn't. But this sort of project soundsperfectfor learning how. Its pretty linear/sequential, it has lots of inputs and outputs, it has a few variables to keep track of, and its 'modular' - you can easily break it down into smaller parts that you can work on one at a time, or even improve later. Anyone can copy/paste someone elses code,and sometimes using pre-written code is a good way to learn (providing you take the time to see exactly what each part is doing, why, and how) but sooner or later one should just start from scratch. There are libraries there to help you, and examples of how to use those libraries but ultimately if you want a micro to do exactly what you want - you have to tell it howEditted: of course using  with an iwhen not in code blocks makes everything after Italic - like a rant on a political blog...

author: iamdarkyoshi
author_rank: Frequent Contributor
timestamp: July 03, 2016, 02:28:33 am
content: Alright, here I am with a probably extremely un-optimized and messy code.Code:// include the library code:#include <LiquidCrystal.h>#ifndef A7#define A7 6#endif#ifndef A8#define A8 8#endif// initialize the library with the numbers of the interface pinsLiquidCrystal lcd(1, 0, 2, 3, 4, 5);const int BATTERY_1_ENABLE_PIN = 9;const int BATTERY_1_CURRENT_PIN = A0;const int BATTERY_1_VOLTAGE_PIN = A1;bool DISCHARGE1;const int BATTERY_2_ENABLE_PIN = 10;const int BATTERY_2_CURRENT_PIN = A2;const int BATTERY_2_VOLTAGE_PIN = A3;bool DISCHARGE2;const int BATTERY_3_ENABLE_PIN = 11;const int BATTERY_3_CURRENT_PIN = A4;const int BATTERY_3_VOLTAGE_PIN = A5;bool DISCHARGE3;const int BATTERY_4_ENABLE_PIN = 12;const int BATTERY_4_CURRENT_PIN = A7;const int BATTERY_4_VOLTAGE_PIN = A8;bool DISCHARGE4;const int TEST_BUTTON_PIN = 7;const int BEEPER_PIN = 13;float measuredCurrent_1;float measuredCurrent_2;float measuredCurrent_3;float measuredCurrent_4;float measuredVoltage_1;float measuredVoltage_2;float measuredVoltage_3;float measuredVoltage_4;void setup() {// put your setup code here, to run once:Serial.begin(9600); //Serial debuggingpinMode(BATTERY_1_ENABLE_PIN, OUTPUT);pinMode(BATTERY_2_ENABLE_PIN, OUTPUT);pinMode(BATTERY_3_ENABLE_PIN, OUTPUT);pinMode(BATTERY_4_ENABLE_PIN, OUTPUT);}void loop() {// put your main code here, to run repeatedly:measuredCurrent_1 = ((analogRead(BATTERY_1_VOLTAGE_PIN)-(analogRead(BATTERY_1_CURRENT_PIN)))*1.183); //convert raw data to mAmeasuredCurrent_2 = ((analogRead(BATTERY_2_VOLTAGE_PIN)-(analogRead(BATTERY_2_CURRENT_PIN)))*1.183); //convert raw data to mAmeasuredCurrent_3 = ((analogRead(BATTERY_3_VOLTAGE_PIN)-(analogRead(BATTERY_3_CURRENT_PIN)))*1.183); //convert raw data to mAmeasuredCurrent_4 = ((analogRead(BATTERY_4_VOLTAGE_PIN)-(analogRead(BATTERY_4_CURRENT_PIN)))*1.183); //convert raw data to mAmeasuredVoltage_1 = ((analogRead(BATTERY_1_VOLTAGE_PIN)*0.004834)); //convert raw data to VoltsmeasuredVoltage_2 = ((analogRead(BATTERY_2_VOLTAGE_PIN)*0.004834)); //convert raw data to VoltsmeasuredVoltage_3 = ((analogRead(BATTERY_3_VOLTAGE_PIN)*0.004834)); //convert raw data to VoltsmeasuredVoltage_4 = ((analogRead(BATTERY_4_VOLTAGE_PIN)*0.004834)); //convert raw data to VoltsSerial.print("Battery 1 Voltage: ");Serial.print(measuredVoltage_1);Serial.print(" ");Serial.print("Battery 2 Voltage: ");Serial.print(measuredVoltage_2);Serial.print(" ");Serial.print("Battery 3 Voltage: ");Serial.print(measuredVoltage_3);Serial.print(" ");Serial.print("Battery 4 Voltage: ");Serial.print(measuredVoltage_4);Serial.println("");Serial.print("Battery 1 Current: ");Serial.print(measuredCurrent_1);Serial.print(" ");Serial.print("Battery 2 Current: ");Serial.print(measuredCurrent_2);Serial.print(" ");Serial.print("Battery 3 Current: ");Serial.print(measuredCurrent_3);Serial.print(" ");Serial.print("Battery 4 Current: ");Serial.print(measuredCurrent_4);Serial.println("");Serial.println("");Serial.println("");if (DISCHARGE1 == false && digitalRead(TEST_BUTTON_PIN) == LOW) {digitalWrite(BATTERY_1_ENABLE_PIN, HIGH);DISCHARGE1 = true;}if (DISCHARGE2 == false && digitalRead(TEST_BUTTON_PIN) == LOW) {digitalWrite(BATTERY_2_ENABLE_PIN, HIGH);DISCHARGE2 = true;}if (DISCHARGE3 == false && digitalRead(TEST_BUTTON_PIN) == LOW) {digitalWrite(BATTERY_3_ENABLE_PIN, HIGH);DISCHARGE3 = true;}if (DISCHARGE4 == false && digitalRead(TEST_BUTTON_PIN) == LOW) {digitalWrite(BATTERY_4_ENABLE_PIN, HIGH);DISCHARGE4 = true;}if (measuredVoltage_1 < 3){digitalWrite(BATTERY_1_ENABLE_PIN, LOW);DISCHARGE1 = false;}if (measuredVoltage_2 < 3){digitalWrite(BATTERY_2_ENABLE_PIN, LOW);DISCHARGE2 = false;}if (measuredVoltage_3 < 3){digitalWrite(BATTERY_3_ENABLE_PIN, LOW);DISCHARGE3 = false;}if (measuredVoltage_4 < 3){digitalWrite(BATTERY_4_ENABLE_PIN, LOW);DISCHARGE4 = false;}delay(1000);}It currently just spits the values out over the serial port. It does stop at 3v. There is a weird offset on battery 4's current, it reads -1200 when there is no current flowing.

author: iamdarkyoshi
author_rank: Frequent Contributor
timestamp: July 03, 2016, 04:58:57 am
content: So it turns out that the arduino was retarded and wont measure analog values on A7 (pin 6) and after LOTS of fiddling, I got a hardware config that works:Battery 1's discharge enable pin has been changed from 9 to 7The Test button has been changed from 7 to 6Battery 4's current sense pin has been changed from 6 to 9 (A9)This would explain the weird current readings (the analogread on a7 was ALWAYS 1023)

author: iamdarkyoshi
author_rank: Frequent Contributor
timestamp: July 03, 2016, 05:26:34 am
content: Added Ah counter, still no buzzer or LCD. I also havent properly calibrated the values in the measurement lines.Code:// include the library code:#include <LiquidCrystal.h>#ifndef A9#define A9 9#endif#ifndef A8#define A8 8#endif// initialize the library with the numbers of the interface pinsLiquidCrystal lcd(1, 0, 2, 3, 4, 5);const int BATTERY_1_ENABLE_PIN = 7;const int BATTERY_1_CURRENT_PIN = A0;const int BATTERY_1_VOLTAGE_PIN = A1;bool DISCHARGE1;const int BATTERY_2_ENABLE_PIN = 10;const int BATTERY_2_CURRENT_PIN = A2;const int BATTERY_2_VOLTAGE_PIN = A3;bool DISCHARGE2;const int BATTERY_3_ENABLE_PIN = 11;const int BATTERY_3_CURRENT_PIN = A4;const int BATTERY_3_VOLTAGE_PIN = A5;bool DISCHARGE3;const int BATTERY_4_ENABLE_PIN = 12;const int BATTERY_4_CURRENT_PIN = A9;const int BATTERY_4_VOLTAGE_PIN = A8;bool DISCHARGE4;const int TEST_BUTTON_PIN = 6;const int BEEPER_PIN = 13;float measuredCurrent_1;float measuredCurrent_2;float measuredCurrent_3;float measuredCurrent_4;float measuredVoltage_1;float measuredVoltage_2;float measuredVoltage_3;float measuredVoltage_4;float AH1;float AH2;float AH3;float AH4;void setup() {// put your setup code here, to run once:Serial.begin(9600); //Serial debuggingpinMode(BATTERY_1_ENABLE_PIN, OUTPUT);pinMode(BATTERY_2_ENABLE_PIN, OUTPUT);pinMode(BATTERY_3_ENABLE_PIN, OUTPUT);pinMode(BATTERY_4_ENABLE_PIN, OUTPUT);}void loop() {// put your main code here, to run repeatedly:measuredCurrent_1 = ((analogRead(BATTERY_1_VOLTAGE_PIN)-(analogRead(BATTERY_1_CURRENT_PIN)))*1.185); //convert raw data to mAmeasuredCurrent_2 = ((analogRead(BATTERY_2_VOLTAGE_PIN)-(analogRead(BATTERY_2_CURRENT_PIN)))*1.185); //convert raw data to mAmeasuredCurrent_3 = ((analogRead(BATTERY_3_VOLTAGE_PIN)-(analogRead(BATTERY_3_CURRENT_PIN)))*1.185); //convert raw data to mAmeasuredCurrent_4 = ((analogRead(BATTERY_4_VOLTAGE_PIN)-(analogRead(BATTERY_4_CURRENT_PIN)))*1.185); //convert raw data to mAmeasuredVoltage_1 = ((analogRead(BATTERY_1_VOLTAGE_PIN)*0.00475)); //convert raw data to VoltsmeasuredVoltage_2 = ((analogRead(BATTERY_2_VOLTAGE_PIN)*0.00475)); //convert raw data to VoltsmeasuredVoltage_3 = ((analogRead(BATTERY_3_VOLTAGE_PIN)*0.00475)); //convert raw data to VoltsmeasuredVoltage_4 = ((analogRead(BATTERY_4_VOLTAGE_PIN)*0.00475)); //convert raw data to VoltsSerial.print("V1: ");Serial.print(measuredVoltage_1);Serial.print(" ");Serial.print("V2: ");Serial.print(measuredVoltage_2);Serial.print(" ");Serial.print("V3: ");Serial.print(measuredVoltage_3);Serial.print(" ");Serial.print("V4: ");Serial.print(measuredVoltage_4);Serial.println("");Serial.print("A1: ");Serial.print(measuredCurrent_1);Serial.print(" ");Serial.print("A2: ");Serial.print(measuredCurrent_2);Serial.print(" ");Serial.print("A3: ");Serial.print(measuredCurrent_3);Serial.print(" ");Serial.print("A4: ");Serial.print(measuredCurrent_4);Serial.println("");Serial.print("Ah1: ");Serial.print(AH1);Serial.print(" ");Serial.print("Ah2: ");Serial.print(AH2);Serial.print(" ");Serial.print("Ah3: ");Serial.print(AH3);Serial.print(" ");Serial.print("Ah4: ");Serial.print(AH4);Serial.println("");Serial.println("");Serial.println("");if (DISCHARGE1 == false && digitalRead(TEST_BUTTON_PIN) == LOW) {digitalWrite(BATTERY_1_ENABLE_PIN, HIGH);DISCHARGE1 = true;AH1 = 0;}if (DISCHARGE2 == false && digitalRead(TEST_BUTTON_PIN) == LOW) {digitalWrite(BATTERY_2_ENABLE_PIN, HIGH);DISCHARGE2 = true;AH2 = 0;}if (DISCHARGE3 == false && digitalRead(TEST_BUTTON_PIN) == LOW) {digitalWrite(BATTERY_3_ENABLE_PIN, HIGH);DISCHARGE3 = true;AH3 = 0;}if (DISCHARGE4 == false && digitalRead(TEST_BUTTON_PIN) == LOW) {digitalWrite(BATTERY_4_ENABLE_PIN, HIGH);DISCHARGE4 = true;AH4 = 0;}if (measuredVoltage_1 < 2.9){digitalWrite(BATTERY_1_ENABLE_PIN, LOW);DISCHARGE1 = false;}if (measuredVoltage_2 < 2.9){digitalWrite(BATTERY_2_ENABLE_PIN, LOW);DISCHARGE2 = false;}if (measuredVoltage_3 < 2.9){digitalWrite(BATTERY_3_ENABLE_PIN, LOW);DISCHARGE3 = false;}if (measuredVoltage_4 < 2.9){digitalWrite(BATTERY_4_ENABLE_PIN, LOW);DISCHARGE4 = false;}if (DISCHARGE1 == true) {AH1 += ((measuredCurrent_1)/ (3600)/1000);}if (DISCHARGE2 == true) {AH2 += ((measuredCurrent_2)/ (3600)/1000);}if (DISCHARGE3 == true) {AH3 += ((measuredCurrent_3)/ (3600)/1000);}if (DISCHARGE4 == true) {AH4 += ((measuredCurrent_4)/ (3600)/1000);}delay(1000);}