Jump to content

Well. It has been a while, but I am back into Arduino. And I need help.

iamdarkyoshi

I am making a battery capacity tester that tests lithium cells directly via a discharge resistor.

Pins 0-10 are connected to a 3 digit 7 segment display. Below is some code that can be used to drive it.

Pin 11 is connected to a button, pressed is high, released is low. This button should start the discharge process.

Pin 12 is connected to the discharge circuitry, when it is high, the battery will be discharged, when low, it is not being discharged.

Pin 13 is connected to an LED that will indicate if it is done testing, high is on and low is off. This should basically just be the opposite of pin 12.

Analog pin 0 is the current sense resistor. A read value of .1 equals 1A. I hope that the arduino has a high enough resolution ADC for accurate measurements...

Analog pin 1 is the battery voltage plus the current sense resistor. Subtract the value read off of analog pin 0 to get true battery voltage.

 

Here is the code I have right now, literally all it does is drive the 7 segment display with "1.23"

/*
testWholeDisplay.ino
-test each segment in the display
-a simple example using Dean Reading's SevSeg library to light up all 4 digits plus the 4 decimal points on a 4 digit display
-the purpose of this example is to ensure you have the wires all hooked up right for every segment and digit, and to troubleshoot the display and wiring
 to ensure *every* segment and period lights up

By Gabriel Staples 
Website: http://www.ElectricRCAircraftGuy.com
My contact info is available by clicking the "Contact Me" tab at the top of my website.
Written: 1 Oct. 2015
Last Updated: 1 Oct. 2015
*/

/*
LICENSING:
-this *example file* only is modified from Dean Reading's original example, and is in the public domain.

Dean Reading's SevSeg library is as follows:
Copyright 2014 Dean Reading

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at 
http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include <SevSeg.h>

SevSeg sevseg; //Instantiate a seven segment controller object

void setup()
{
  byte numDigits = 3;   
  byte digitPins[] = {0, 1, 2}; //Digits: 1,2,3,
  byte segmentPins[] = {3, 4, 5, 6, 7, 8, 9, 10}; //Segments: A,B,C,D,E,F,G,Period

  sevseg.begin(COMMON_CATHODE, numDigits, digitPins, segmentPins);
  sevseg.setBrightness(10);
}

void loop()
{
  //local vars
  static byte decPlace = 0;
  
  sevseg.setNumber(123,decPlace);
  decPlace = 2; 

  sevseg.refreshDisplay(); // Must run repeatedly; don't use blocking code (ex: delay()) in the loop() function or this won't work right
}

Here is what I want it to do:

 

When turned on, do nothing other than light up the LED on pin 13 to show that it is in "standby"

 

After the button is pressed, reset the amp-hour rating and start discharging by setting pin 12 high and turn off pin 13's LED.

 

Every second, add the read value of analog pin 0 to an integer. Divide this integer by 360 to get the amp hour rating. Show it on the LED display (with the leftmost digit being the "ones" place)

 

Once the battery voltage (analog pin 1 minus analog pin 0) reaches 3v, stop discharging by setting pin 12 low and then set pin 13 high. Leave the amp hour rating on the display.

 

Wait for the user to press the button again and repeat the process.

 

 

I hope this all made sense, and thank you in advance to anyone helping me on this. I suck at software and programming.

Link to comment
Share on other sites

Link to post
Share on other sites

 

This is not a correct solution (I don't think), but it will get you started in the right direction. Give it a try on your Arduino (I compiled it in the Arduino IDE and it at least is syntactically correct), and let me know how it goes, or if you have any questions about the approach used here.

#include <SevSeg.h>

SevSeg sevseg; //Instantiate a seven segment controller object

void initializeIO();
void doStandby(bool activeOrCancel);
void doVoltageTest();
void initializeIO();
void activateStandby();
void cancelStandby();
void clearSevenSegmentDisplay();
void changeSevenSegmentDisplay(int numberToDisplay, int decimalPosition);
void doVoltageTest();

const int USER_INPUT_BUTTON_PIN = 11;
const int DISCHARGE_PIN = 12;
const int STANDBY_LED_PIN = 13;
const int CURRENT_SENSE_RESISTOR_PIN = A0;
const int BATTERY_VOLTAGE_PIN = A1;
byte NUMBER_OF_DIGITS = 3;
byte DIGIT_PINS[] = {0, 1, 2}; //Digits: 1,2,3,
byte SEGMENT_PINS[] = {3, 4, 5, 6, 7, 8, 9, 10}; //Segments: A,B,C,D,E,F,G,Period

/*A double is used to force floating point division*/
const double SECONDS_PER_HOUR = 3600;
const double MILLISECONDS_PER_SECOND = 1000;

const bool CANCEL_STANDBY = false;
const bool ACTIVATE_STANDBY = true;

void setup()
{
    initializeIO();
    activateStandby();
}

void loop()
{
    if (digitalRead(USER_INPUT_BUTTON_PIN)) {
        doVoltageTest();
    }
    sevseg.refreshDisplay(); // Must run repeatedly; don't use blocking code (ex: delay()) in the loop() function or this won't work right
}

void initializeIO()
{

    sevseg.begin(COMMON_CATHODE, NUMBER_OF_DIGITS, DIGIT_PINS, SEGMENT_PINS);
    sevseg.setBrightness(10);

    pinMode(USER_INPUT_BUTTON_PIN, INPUT);
    pinMode(DISCHARGE_PIN, OUTPUT);
    pinMode(STANDBY_LED_PIN, OUTPUT);
    pinMode(CURRENT_SENSE_RESISTOR_PIN, INPUT);
    pinMode(BATTERY_VOLTAGE_PIN, INPUT);

    digitalWrite(DISCHARGE_PIN, LOW);
    digitalWrite(STANDBY_LED_PIN, LOW);
}

void activateStandby()
{
    digitalWrite(STANDBY_LED_PIN, HIGH);
}

void cancelStandby()
{
    digitalWrite(STANDBY_LED_PIN, LOW);
}

void clearSevenSegmentDisplay()
{
    changeSevenSegmentDisplay(0, 0);
}

void changeSevenSegmentDisplay(int numberToDisplay, int decimalPosition)
{
    sevseg.setNumber(numberToDisplay, decimalPosition);
    sevseg.refreshDisplay();
}

void changeSevenSegmentDisplay(float numberToDisplay, int numberOfDecimals)
{
    sevseg.setNumber(numberToDisplay, numberOfDecimals);
    sevseg.refreshDisplay();
}

void doVoltageTest()
{
    unsigned long startTimeMs = 0;
    unsigned long endTimeMs = 0;
    float ampHourRating = 0;
    cancelStandby();
    clearSevenSegmentDisplay();
    digitalWrite(DISCHARGE_PIN, HIGH);
    while ((analogRead(A1) - analogRead(A0)) > 3) {
        startTimeMs = millis();
        //What do you want the seven segment to display during the first second?
        do {
            sevseg.refreshDisplay();
            endTimeMs = millis();
        } while ((endTimeMs - startTimeMs) < MILLISECONDS_PER_SECOND);

        /*I assume you meant 3600 instead of 360?*/
        ampHourRating += (analogRead(CURRENT_SENSE_RESISTOR_PIN) / (SECONDS_PER_HOUR));
        changeSevenSegmentDisplay(ampHourRating, (NUMBER_OF_DIGITS - 1));
        sevseg.refreshDisplay();
    }
    activateStandby();
}

  

 

Link to comment
Share on other sites

Link to post
Share on other sites

4 hours ago, Pinguinsan said:

 

This is not a correct solution (I don't think), but it will get you started in the right direction. Give it a try on your Arduino (I compiled it in the Arduino IDE and it at least is syntactically correct), and let me know how it goes, or if you have any questions about the approach used here.


#include <SevSeg.h>

SevSeg sevseg; //Instantiate a seven segment controller object

void initializeIO();
void doStandby(bool activeOrCancel);
void doVoltageTest();
void initializeIO();
void activateStandby();
void cancelStandby();
void clearSevenSegmentDisplay();
void changeSevenSegmentDisplay(int numberToDisplay, int decimalPosition);
void doVoltageTest();

const int USER_INPUT_BUTTON_PIN = 11;
const int DISCHARGE_PIN = 12;
const int STANDBY_LED_PIN = 13;
const int CURRENT_SENSE_RESISTOR_PIN = A0;
const int BATTERY_VOLTAGE_PIN = A1;
byte NUMBER_OF_DIGITS = 3;
byte DIGIT_PINS[] = {0, 1, 2}; //Digits: 1,2,3,
byte SEGMENT_PINS[] = {3, 4, 5, 6, 7, 8, 9, 10}; //Segments: A,B,C,D,E,F,G,Period

/*A double is used to force floating point division*/
const double SECONDS_PER_HOUR = 3600;
const double MILLISECONDS_PER_SECOND = 1000;

const bool CANCEL_STANDBY = false;
const bool ACTIVATE_STANDBY = true;

void setup()
{
    initializeIO();
    activateStandby();
}

void loop()
{
    if (digitalRead(USER_INPUT_BUTTON_PIN)) {
        doVoltageTest();
    }
    sevseg.refreshDisplay(); // Must run repeatedly; don't use blocking code (ex: delay()) in the loop() function or this won't work right
}

void initializeIO()
{

    sevseg.begin(COMMON_CATHODE, NUMBER_OF_DIGITS, DIGIT_PINS, SEGMENT_PINS);
    sevseg.setBrightness(10);

    pinMode(USER_INPUT_BUTTON_PIN, INPUT);
    pinMode(DISCHARGE_PIN, OUTPUT);
    pinMode(STANDBY_LED_PIN, OUTPUT);
    pinMode(CURRENT_SENSE_RESISTOR_PIN, INPUT);
    pinMode(BATTERY_VOLTAGE_PIN, INPUT);

    digitalWrite(DISCHARGE_PIN, LOW);
    digitalWrite(STANDBY_LED_PIN, LOW);
}

void activateStandby()
{
    digitalWrite(STANDBY_LED_PIN, HIGH);
}

void cancelStandby()
{
    digitalWrite(STANDBY_LED_PIN, LOW);
}

void clearSevenSegmentDisplay()
{
    changeSevenSegmentDisplay(0, 0);
}

void changeSevenSegmentDisplay(int numberToDisplay, int decimalPosition)
{
    sevseg.setNumber(numberToDisplay, decimalPosition);
    sevseg.refreshDisplay();
}

void changeSevenSegmentDisplay(float numberToDisplay, int numberOfDecimals)
{
    sevseg.setNumber(numberToDisplay, numberOfDecimals);
    sevseg.refreshDisplay();
}

void doVoltageTest()
{
    unsigned long startTimeMs = 0;
    unsigned long endTimeMs = 0;
    float ampHourRating = 0;
    cancelStandby();
    clearSevenSegmentDisplay();
    digitalWrite(DISCHARGE_PIN, HIGH);
    while ((analogRead(A1) - analogRead(A0)) > 3) {
        startTimeMs = millis();
        //What do you want the seven segment to display during the first second?
        do {
            sevseg.refreshDisplay();
            endTimeMs = millis();
        } while ((endTimeMs - startTimeMs) < MILLISECONDS_PER_SECOND);

        /*I assume you meant 3600 instead of 360?*/
        ampHourRating += (analogRead(CURRENT_SENSE_RESISTOR_PIN) / (SECONDS_PER_HOUR));
        changeSevenSegmentDisplay(ampHourRating, (NUMBER_OF_DIGITS - 1));
        sevseg.refreshDisplay();
    }
    activateStandby();
}

  

 

Wow. We are getting somewhere! I have no idea what I meant when I said that the battery voltage needed to be obtained by subtracting the value of the a0 pin. The battery voltage is literally just what a1 reads. I was tired...

 

As for the current sensing, I brought out my "trusty" 40 year old multimeter and measured the exact voltage drop on the current sense resistor. It measures 0.1063v when there is 1.000 amp across it (I used a .1 ohm resistor)

 

When connected, the display just reads "  0." and no power is drawn. This is fine.

 

Once the button is pressed, it will start discharging the battery (I have it connected to my bench supply instead)

 

Once the voltage on the battery reaches 3v, it continues discharging. Oops. Once it reaches 0v, it goes into standby (pin 13's LED) but still has the pin 12 mosfet enabled, so it is still discharging. I cannot seem to stop it discharging without resetting the processor.

 

The Ah counter is also weird (maybe because 1A is equal to a reading of 0.1063v on pin a0?)

 

Another reason I have it on my bench supply is because my bench supply counts mAh. Once the arduino reads 1.00 Ah, the bench supply reads 0.055 Ah. Not sure whats going on here....?

 

Anyway, thanks so much for the help so far, I really appreciate it!

Link to comment
Share on other sites

Link to post
Share on other sites

Hey, no problem man. I was rushed this morning but I wanted to at least write a base program according to the logic you wrote down. I haven't taken any time to think about the hardware, so give me a little bit to think through the circuit and I'll see if I can correct some stuff.

Link to comment
Share on other sites

Link to post
Share on other sites

12 minutes ago, Pinguinsan said:

Hey, no problem man. I was rushed this morning but I wanted to at least write a base program according to the logic you wrote down. I haven't taken any time to think about the hardware, so give me a little bit to think through the circuit and I'll see if I can correct some stuff.

I am making major overhauls to the circuitry, so don't get too far :P

 

Resolution of the ADC was far too bad for sensing current. So I am going to use a 4 ohm resistor as both the load and current sensing.

Link to comment
Share on other sites

Link to post
Share on other sites

Update: It works!

#include <SevSeg.h>

SevSeg sevseg; //Instantiate a seven segment controller object

void initializeIO();
void doStandby(bool activeOrCancel);
void doVoltageTest();
void initializeIO();
void activateStandby();
void cancelStandby();
void clearSevenSegmentDisplay();
void changeSevenSegmentDisplay(int numberToDisplay, int decimalPosition);
void doVoltageTest();

const int USER_INPUT_BUTTON_PIN = 11;
const int DISCHARGE_PIN = 12;
const int STANDBY_LED_PIN = 13;
const int CURRENT_SENSE_RESISTOR_PIN = A0;
const int BATTERY_VOLTAGE_PIN = A1;
byte NUMBER_OF_DIGITS = 3;
byte DIGIT_PINS[] = {0, 1, 2}; //Digits: 1,2,3,
byte SEGMENT_PINS[] = {3, 4, 5, 6, 7, 8, 9, 10}; //Segments: A,B,C,D,E,F,G,Period

/*A double is used to force floating point division*/
const double SECONDS_PER_HOUR = 3600;
const double MILLISECONDS_PER_SECOND = 1000;

const bool CANCEL_STANDBY = false;
const bool ACTIVATE_STANDBY = true;

void setup()
{
    initializeIO();
    activateStandby();
}

void loop()
{
    if (digitalRead(USER_INPUT_BUTTON_PIN)) {
        doVoltageTest();
        
    }
    sevseg.refreshDisplay(); // Must run repeatedly; don't use blocking code (ex: delay()) in the loop() function or this won't work right
  

}

void initializeIO()
{

    sevseg.begin(COMMON_CATHODE, NUMBER_OF_DIGITS, DIGIT_PINS, SEGMENT_PINS);
    sevseg.setBrightness(10);

    pinMode(USER_INPUT_BUTTON_PIN, INPUT);
    pinMode(DISCHARGE_PIN, OUTPUT);
    pinMode(STANDBY_LED_PIN, OUTPUT);
    pinMode(CURRENT_SENSE_RESISTOR_PIN, INPUT);
    pinMode(BATTERY_VOLTAGE_PIN, INPUT);

    digitalWrite(DISCHARGE_PIN, LOW);
    digitalWrite(STANDBY_LED_PIN, LOW);
}

void activateStandby()
{
    digitalWrite(STANDBY_LED_PIN, HIGH);
    digitalWrite(DISCHARGE_PIN, LOW);
}

void cancelStandby()
{
    digitalWrite(STANDBY_LED_PIN, LOW);
}

void clearSevenSegmentDisplay()
{
    changeSevenSegmentDisplay(0, 0);
}

void changeSevenSegmentDisplay(int numberToDisplay, int decimalPosition)
{
    sevseg.setNumber(numberToDisplay, decimalPosition);
    sevseg.refreshDisplay();
}

void changeSevenSegmentDisplay(float numberToDisplay, int numberOfDecimals)
{
    sevseg.setNumber(numberToDisplay, numberOfDecimals);
    sevseg.refreshDisplay();
}

void doVoltageTest()
{
    unsigned long startTimeMs = 0;
    unsigned long endTimeMs = 0;
    long measuredCurrent = 0;
    long measuredVoltage = 0;
    float ampHourRating = 0;
    cancelStandby();
    clearSevenSegmentDisplay();
    digitalWrite(DISCHARGE_PIN, HIGH);
    while ((analogRead(A1)) > 620) { //without the voltage conversion running, we have to do a raw data readback here...
        startTimeMs = millis();
        //What do you want the seven segment to display during the first second?
        do {
            sevseg.refreshDisplay();
            endTimeMs = millis();
        } while ((endTimeMs - startTimeMs) < MILLISECONDS_PER_SECOND);

      
        measuredCurrent = ((analogRead(A1)-(analogRead(A0)))*1.183); //convert raw data to mA
        measuredVoltage = ((analogRead(A1)*4.834));                  //convert raw data to V
        ampHourRating += ((measuredCurrent)/ (SECONDS_PER_HOUR)/1000);
        changeSevenSegmentDisplay(ampHourRating, (NUMBER_OF_DIGITS - 1));
        sevseg.refreshDisplay();
  Serial.print("Current: ");
  Serial.print(measuredCurrent);
  Serial.print("  Voltage: ");
  Serial.print(measuredVoltage);
  Serial.print("   Amp-Hours: ");
  Serial.print(ampHourRating);
  Serial.println("");
    }
    activateStandby();
}

The circuit has changed a bit. I will do my best to do a schematic later.

Link to comment
Share on other sites

Link to post
Share on other sites

Hey, great job man! I'm glad to hear it works! Good work on the updates, they look great.

Link to comment
Share on other sites

Link to post
Share on other sites

1 hour ago, Pinguinsan said:

Hey, great job man! I'm glad to hear it works! Good work on the updates, they look great.

Yeah, thanks for the starting code! Now, I am not asking you to actually design anything of the sort, but if I were to use only the serial monitor (removing the LED display to get more IO), could the code be easily modified to run 6 batteries? The thing I worry about is the arduino stopping each cell when they need to be. Hmm...

Link to comment
Share on other sites

Link to post
Share on other sites

10 hours ago, iamdarkyoshi said:

Yeah, thanks for the starting code! Now, I am not asking you to actually design anything of the sort, but if I were to use only the serial monitor (removing the LED display to get more IO), could the code be easily modified to run 6 batteries? The thing I worry about is the arduino stopping each cell when they need to be. Hmm...

I like where your head is at. Going from 1 to 6 batteries is possible, but the equivalent of an "event scheduler" would need to be implemented to make them all work concurrently, since the AVR chips don't support real hardware multithreading. It's not a small task, but I have done it before. I could actually probably use my old event scheduler and modify it for this application. The biggest issue in my mind would be readability of the results, especially if it's over serial. The serial monitor would be flooded with a lot of information.

Link to comment
Share on other sites

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×