Pages: 1 [2]   Go Down
Author Topic: Attiny85 Lipo battery cutoff/monitor  (Read 1770 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 24
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
/*
  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 7
const int switchPin = 0; //physical pin 5
const 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 input
int index = 0;                  // the index of the current reading
int total = 0;                  // the running total
int average = 0;                // the average





void 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

Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 24
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
https://www.youtube.com/watch?v=9kuDbNBoO2w&feature=c4-overview&list=UU10XXrQW0CE6wFZmbBi8duQ
Logged

Global Moderator
Dallas
Offline Offline
Shannon Member
*****
Karma: 209
Posts: 13027
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset


Thank you for the follow-up.

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

Offline Offline
Newbie
*
Karma: 0
Posts: 24
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
      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           
      }

Logged

United Kingdom
Offline Offline
Tesla Member
***
Karma: 227
Posts: 6637
Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

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.

Pages: 1 [2]   Go Up
Jump to: