Go Down

### Topic: Attiny85 Lipo battery cutoff/monitor (Read 4814 times)previous topic - next topic

#### dwhacks

#15
##### Nov 01, 2013, 02:49 am
Great! it works now! that was a silly mistake, thank you for your help!

Now I have another question. The values I had for the voltage "readings" are way off. I dont know if its just like that but my calculated voltages are about 40 below what seams to work. I cant imagine my DMM being that much off.

As I understand it, the voltage after the divider, on the pin should fit into this equation to find the byte value:
1024*(VOLTAGE/1.1) = VALUE
But like I said It seams off.

Next question, I have used the smoothing example to smooth out my readings and I have made it its own functions. As I though, it should run the amount of numReadings before getting the average. How can I make it loop that function without running the rest of my loop?

Heres the updated code:

Code: [Select]
`/*   Attiny85_batteryMonitor  Ideas borrowed from: https://github.com/unixbigot/Flat-Mate  modified for arduino tiny core by DWhacks *@@ Voltage trigger levels. * * Battery voltage is read through a voltage divider and compared to the internal voltage reference. * * If *    Vin ----+ *            R1 *            +----- Vout (BATI) *            R2 *            | *            = *            .  (gnd) * * Then Vout = Vin * ( R2 / (R1 + R2) ) * * ; Use this Emacs lisp function to calculate divisors * (defun rn2div (rup rdown) (/ (float rdown) (+ rup rdown))) * * * eg. R1=12k R2=1k => Vout = Vin * (1000 / (1000 + 12000)) *                            Vin * 0.0769 * *     R1=20k R2=10k => Vout = Vin * 0.3333   Ileak = 0.4mA @ 12v *     R1=2k2 R2=1k  => Vout = Vin * 0.3125 *     R1=3k3 R2=1k  => Vout = Vin * 0.232 *     R1=3k9 R2=1k  => Vout = Vin * 0.204    Ileak = 2.4mA @ 12v *     R1=39k R2=10k => Vout = Vin * 0.204    Ileak = 0.24mA @ 12v *     R1=4k7 R2=1k  => Vout = Vin * 0.175 *     R1=10k R2=1k  => Vout = Vin * 0.0909   Ileak = 1mA @ 12v *     R1=12k R2=1k  => Vout = Vin * 0.0769   Ileak = 0.92mA @ 12v * * Fully charged LiPo is 4.23v/cell, discharged is 2.7v/cell (nominal voltage 3.7v/cell) * For battery endurance, do not discharge below 3.0v/cell (aircraft users commonly use 2.9v/cell as limit) * * A 2-cell battery (nominally 7.46v)  varies     from 8.46v to 5.40v, with low-volt alert at 6.00v * A 3-cell battery (nominally 11.1v) thus varies from 12.9v to 8.10v, with low-volt alert at 9.00v * A 4-cell battery (nominally 14.8v) thus varies from 16.9v to 10.8v, with low-volt alert 12 12.0v *   NOTE: a 4-cell battery requires a different voltage divider than 2-and-3 cells (use 15:1 not 12:1) * * *@@ Analog read values for defined voltage levels * * For a 3-cell battery, we consider 12v+ to be "full", 11v "good", 10v "low" and 9v "critical" * (BMV_foo constants are these values in millivolts) * * In AVR-worldview, we use 12:1 voltage divider and read 10-bit ADC comparisons versus AREF (1.1v) *  * So 12v becomes 1.00V when divided.    * Compared to 1.1v reference this gives an ADC result of 1024*(1.0/1.1) == 859 * * An alternative approach is to use a smaller voltage divisor and compare * against Vcc (5.0v), but in practice a 12:1 divisor is easier to achieve * due to the standard first preference resistor value series. * * You can use these Emacs lisp defuns to calculate threshold analog values for your voltage levels * * (defun volts2int (v sf ref) (round (/ (* 1024.0 (* (float v) sf) ) (float ref)))) * (defun vlist2int (sf ref levels) (mapcar (lambda (v) (volts2int (float v) sf ref)) levels)) * eg. (volts2int 12 0.333 5.0) => 818 *     (vlist2int (rn2div 10000 1000) 1.1 '(12 11 10 9)) => (1016 931 846 762) *     (vlist2int (rn2div 20000 10000) 5.0 '(12 11 10 9)) => (819 751 683 614) *     (vlist2int (rn2div 12000 1000) 1.1 '(12 11 10 9))=> (859 788 716 644) * * for 4-cell, use a 15:1 divider *     (vlist2int (rn2div 15000 1000) 1.1 '(16 14.5 13 12)) => (931 844 756 698) * * The above lines calculate the VL_* values shown below /* Use a 12:1 voltage divider */ //#define INTERNAL (2) #define CELL_COUNT 2  // DEFINE THE NUMBER OF CELLS 2,3,4#if CELL_COUNT == 4/* Use a 15:1 voltage divider */#define BMV_FULL 16000#define VL_FULL    931#define BMV_GOOD 14500#define VL_GOOD    844#define BMV_LOW  13000#define VL_LOW     756#define BMV_CRIT 12000#define VL_CRIT    698#elif CELL_COUNT == 3/* Use a 12:1 voltage divider */#define BMV_FULL 12000#define VL_FULL    859#define BMV_GOOD 11000#define VL_GOOD    788#define BMV_LOW  10000#define VL_LOW     716#define BMV_CRIT  9000#define VL_CRIT    644#elif CELL_COUNT == 2/* Use a 12:1 voltage divider */#define VL_FULL    613  //about 7.8v#define VL_GOOD    583  //about 7.3v#define VL_LOW     510  //about 6.3v#define VL_CRIT    470#endif const int batteryPin = 1; //V+ from battery connected to analog1 physical pin 7const int switchPin = 0; //physical pin 5const int led = 3; //physical pin 2// Define the number of samples to keep track of.  The higher the number,// the more the readings will be smoothed, but the slower the output will// respond to the input.  Using a constant rather than a normal variable lets// use this value to determine the size of the readings array.const int numReadings = 3;int readings[numReadings];      // the readings from the analog inputint index = 0;                  // the index of the current readingint total = 0;                  // the running totalint average = 0;                // the averagevoid setup(){  analogReference(INTERNAL);  pinMode(batteryPin, INPUT);  pinMode(switchPin, OUTPUT);  pinMode(led, OUTPUT);   for (int thisReading = 0; thisReading < numReadings; thisReading++)      readings[thisReading] = 0; // initialize all the readings to 0      }void loop(){      averageVoltage(); //get average voltage from function      int voltage = average;        if (voltage > VL_CRIT){ //if battery is above critical, turn on transistor swtich      digitalWrite(switchPin, HIGH);    }        if (voltage >= VL_FULL){ //if the battery is full or higher      digitalWrite(led, HIGH);    }        else if (voltage >= VL_GOOD){           /*Fade Up*/          for(byte i=1; i<100; i++) {            byte on  = i;            byte off = 100-on;            for( byte a=0; a<100; a++ ) {              digitalWrite(led, HIGH);              delayMicroseconds(on);              digitalWrite(led, LOW);              delayMicroseconds(off);            }          }            /*Fade Down*/            for(byte i=1; i<100; i++) {            byte on  = 100-i;            byte off = i;            for( byte a=0; a<100; a++ ) {              digitalWrite(led, HIGH);              delayMicroseconds(on);              digitalWrite(led, LOW);              delayMicroseconds(off);            }             }    }        else if (voltage >= VL_LOW){           /*Fade Up*/          for(byte i=1; i<50; i++) {            byte on  = i;            byte off = 50-on;            for( byte a=0; a<100; a++ ) {              digitalWrite(led, HIGH);              delayMicroseconds(on);              digitalWrite(led, LOW);              delayMicroseconds(off);            }          }            /*Fade Down*/            for(byte i=1; i<50; i++) {            byte on  = 50-i;            byte off = i;            for( byte a=0; a<100; a++ ) {              digitalWrite(led, HIGH);              delayMicroseconds(on);              digitalWrite(led, LOW);              delayMicroseconds(off);            }             }                }        else if (voltage < VL_LOW){      digitalWrite(switchPin, LOW);      digitalWrite(led, HIGH);      delay(100);      digitalWrite(led, LOW);      delay(100);                     }                }      void averageVoltage() {        // subtract the last reading:        total= total - readings[index];                // read from the sensor:          readings[index] = analogRead(batteryPin);        // add the reading to the total:        total= total + readings[index];              // advance to the next position in the array:          index = index + 1;                                  // if we're at the end of the array...        if (index >= numReadings)                        // ...wrap around to the beginning:          index = 0;                                        // calculate the average:        average = total / numReadings;                  delay(1);        // delay in between reads for stability                  }//end`

#### dwhacks

#16
##### Nov 02, 2013, 07:13 pm
Well I figured out all the issues! the analogRead value is a lot closer to whats expected on the actually soldered together version. Bread board must have added odd resistances or bad connections.

All I have to to is calculate the values for 3s and 4s to my taste and then I will make a blog post. I will also make up a PCB in Eagle.

All revisions of the code are on my gists page is anyone is interested: https://gist.github.com/dwhacks/7208805

Oh, and heres a video of it working:

#17
##### Nov 02, 2013, 09:18 pm

Thank you for the follow-up.

Do you still need help with the questions in Reply #15?

#### dwhacks

#18
##### Nov 02, 2013, 09:32 pm
I think It's all working as it should now. I thought about making the averageVoltage function loop until it reaches the numReadings value but now I'm thinking that might add a funny pause in my main loop ruining the effect of the fading LED.

Do you have any suggestions or insight into that?

Code: [Select]
`      void averageVoltage() {        // subtract the last reading:        total= total - readings[index];                // read from the sensor:          readings[index] = analogRead(batteryPin);        // add the reading to the total:        total= total + readings[index];              // advance to the next position in the array:          index = index + 1;                                  // if we're at the end of the array...        if (index >= numReadings)                        // ...wrap around to the beginning:          index = 0;                                        // calculate the average:        average = total / numReadings;                  delay(1);        // delay in between reads for stability                  }`

#### dc42

#19
##### Nov 03, 2013, 10:53 am

Well I figured out all the issues! the analogRead value is a lot closer to whats expected on the actually soldered together version. Bread board must have added odd resistances or bad connections.

Bear in mind that the internal 1.1V reference is not accurate, it can be anywhere between 1.0 and 1.2V. You need to calibrate it for each chip. Standard practice would be to store the calibration constant in EEPROM so that you don't have to change the program.
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

Go Up

Please enter a valid email to subscribe