CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Battery capacity monitor for ROV
Goto page Previous  1, 2
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
rovtech



Joined: 24 Sep 2006
Posts: 288

View user's profile Send private message AIM Address

PostPosted: Thu Aug 06, 2015 1:55 pm     Reply with quote

My original question and concern is for a program to tell me when my batteries are running low. Some of you brought up some good points about temperature and Li batteries, enough to convince me to do some tests. Careful measurements were made to compare Li and Pb batteries.
Both batteries were bought in the last 6 months and have had only one cycle each, so they are essentially new. Both are 12 volt, 12AH batteries. They were discharged while in a ‘fridge at 11 °C into a 5 ohm 250 watt resistor sitting in a pan with a few ice cubes.
The test conditions favor a lead acid battery because the approximately 2.5 amp current is low enough to reduce the effects of Peukerts Law which can be almost ignored for the LiFePO4 battery. A test at 12 amps would certainly favour the LiFePO4 battery and the results would be disastrous for the lead-acid battery, a situation I have seen.
The Li battery was fully charged at room temperature a week before the test and stored in a hot room. They don’t self discharge much and actually prefer to be stored partly discharged.
The AGM was charged immediately before being placed in the ‘fridge, it would self-discharge somewhat if stored for a week in a hot room. Several weeks could see the start of sulphation. A few months stored discharged would destroy it.
If the minimum acceptable voltage is 10.5 volts then the LiFePO4 battery delivered 10.15 AH and the AGM (Advanced Glass Matt, Lead-acid) delivered 8.76 AH.
If the minimum acceptable voltage is raised to 12.2 volts then the LiFePO4 delivered 7.8 AH but the AGM only lasted 30 seconds with the 5 ohm load (2.5 amps).
In practice my motors are driven by a 24 volt inverter powered by the battery. If the battery voltage sags the inverter still puts out 24 volts and draws more current to do so. Since the loads on the battery are often 18 amps, Peukerts law simply kills the AGM battery life and I see 8 volts or less as the inverter senses over-current and shuts down. The Li battery does not do this.
What is more important than AH is watt-hours and the LiFePO4 delivered 121.34 compared to the AGM which delivered 103.1 watt-hours. Also note that because the Li battery held a higher voltage with little sag it was delivering higher current compared to the AGM battery. A constant current load would have favoured the Li battery even more (because of Peukerts Law).
Why do I need a battery when power is supplied from the surface? I need it because of high transient loads and because I won’t send 200 volts down the cable with swimmers, or myself, in the water. I have been operating without the charging power because I have not needed it and it makes me nervous.
My software is being tested and I will post it soon.
temtronic



Joined: 01 Jul 2010
Posts: 9633
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Thu Aug 06, 2015 3:58 pm     Reply with quote

Though you might have already figured this out a 12 V 12AH battery really can't deliver 12A for 1 hour, let alone 6A for 2 hrs. The stated AH rating is usually some convoluted nonsense about using the battery at 1/20th of the 'rated' capacity. While some things have changed over the years I wager the REAL ability of a battery is NOT what 'they' say it is.
If I needed 12 Amps I'd be looking at 24AH batteries or greater. Yes, they'll take up more space and there is the weight issue but you seem you be doing a LOT of R&D, perhaps a 'bigger' battery may help?
I may have suggested 'supercaps' as a way to 'smooth' the current demands of your motors and inverters. Maybe it'll help ?

Just thinking out loud here.

Jay
rovtech



Joined: 24 Sep 2006
Posts: 288

View user's profile Send private message AIM Address

PostPosted: Thu Aug 06, 2015 5:39 pm     Reply with quote

Jay; I really appreciate the advice and most points are valid. However you are quoting lead acid features.
These new 12AH LiFePO4 batteries really are spec'd at 12 amps for 1 hour at 25 deg C. This should take you to the correct place:
http://www.lithiumion-batteries.com/uploads/files/14990/SB12.pdf
for discharge curves, and this for other data like amp-hours:
http://www.lithiumion-batteries.com/knowledge-base.php
I don't trust everything I read and certainly don't expect to get 92% capacity at 10 deg C because they fail to specify the current. However I did get 85% or more depending on how I calculate it. I also don't expect precision for my monitor but with a few correction factors for temperature and other factors like experience I expect it to be useful.
The proof is in the pudding as they say and the battery change solved my problems.
I cannot fit a different size of battery, I like the weight reduction because I will need it for the addition of a manipulator arm and other features. This is a hobby. I can see a use for measuring any battery to see how bad it is.
I also appreciate the comments from others and have looked up the references.
dyeatman, you say you have used the DS2438. Do you have any software examples on this forum?
Does anyone else have software to share on the use of battery monitor chips?
I modify/fix/change what I can and live with the rest.
temtronic



Joined: 01 Jul 2010
Posts: 9633
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Thu Aug 06, 2015 6:09 pm     Reply with quote

Wow specs that are really real !!! Kinda refreshing to me. Pricey batteries but if they perform as stated, well worth it.
You certainly don't want to have a cheaper product 'die' and have to pull the ROV back up by hand !!
Guess I'm 'old school' for designing stuff. Over kill, redundancy, etc. If done right it'll do the job right from day one.

Jay
rovtech



Joined: 24 Sep 2006
Posts: 288

View user's profile Send private message AIM Address

PostPosted: Mon Aug 24, 2015 6:15 pm     Reply with quote

temtronic wrote:
Wow specs that are really real !!!

Well that is to be seen. I have the software written for a hardware test setup that connects the 5 ohm resistor by means of a relay. The display is a 64x128 graphics and my description of how I got to draw graphics on it can be found by searching on my handle, another story.
Here is the software. It runs until the battery voltage drops to 10.5 volts then disconnects the load and stops. It subtracts or adds AH depending on load and any charging current.
The Ampere-Hours is stored in the non-volatile memory so the remaining AH is retained when the ROV is shut off. The AH can be reset to 12 by means of a switch.

Question: Can anyone suggest a better way to time the one second? It does not seem as stable with the EEPROM read/write working. A pin is toggled every pass of the while loop which gives a 2 seconds period, but each pass is 1 second. I am checking it on a frequency/period counter.
Code:

////////////////////////////////////////////////////////////////////////////////////////////
// File  Graphics AH Meter.c                                                           //
// Digole Serial Adapter for 128x64 Graphic LCD. Jumper I2C on the adapter                 //
// See Graphics AH Meter schematic.                                                       //
// Port B is not used except for programming, Port C drives the display                   //
// AN1 reads the battery voltage from a resistor divider, 20v max gives 5v at the ADC     //
//     pin, =19.53mv/bit or 51.2 bits/volt                                                //
// AN2 reads the current from the shunt amplifier, +/- 30 amps gives +/-50mv on the       //
//     shunt, = +/- 2.5v at U1 O/P and 0 to 5v at the U2 O/P (0v = -30amps, 2.5v = 0amps, //
//     and 5v = +30amps) = .235 amps/bit (30/127)                                         //
// RA3 is a switch input to reset the AmpHours to an initial full battery value,          //
// RA4 drives the load relay,                                                             //
// RA5 has a 2 second timing signal (1sec/pass)                                           //
// 24 Aug 2015                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////

/* Pre-processor directives */
 #include <16F1938.H>
 #include <math.h>
 #fuses INTRC_IO, NOWDT, PUT, NOPROTECT, BROWNOUT, MCLR
 #use delay (clock=1000000)
 #use I2C (master, SCL=PIN_C3, SDA=PIN_C4)
 #byte portc = getenv("SFR:PORTC")
 

// define I2C address
#define LCD_WRT_ADDR 0X4E               // LCD display
#define buff_size 22                    // characters per line plus one
   
// Function prototypes
    void clear_LCD (void);                      // Clear LCD
    void cursor(short ctrl);                    // Turn cursor ON(1) or OFF(0)
    void ctrl_start (int ctrl);                 // Turn Start Screen ON or OFF
    void control_addr (short ctrl);             // Turn on mode
    void text_position (int line, int column);  // set start for next text
    void set_font (char size);                  // set font size
    void set_DM(char cmd);                      // set Display Mode for next command
    void send_str(char buff[buff_size]);        // Send string to LCD
    void draw_line(int x, int y, int x1, int y1);  // draw a line from x,y to x1,y1
    float read_float_eeprom(int EEPROM_address);
    void write_float_eeprom(int EEPROM_address, float data);
   
// The main function
void main(void)
{
// setup ports
  set_tris_c (0x00);                 // all outputs
  set_tris_a (0xCF);               // Time(5) & Ctrl(4) outputs
// setup ADC
   setup_adc (ADC_CLOCK_DIV_32);       // configures ADC, 2-6 us reqd in .h
   setup_adc_ports (sAN1|sAN2);         // volts, amps
   delay_us(10);                        // wait
// declare variables
  int i, EEPROM_address = 0;            // start of non-volatile memory
  int ADCbits = 127;                    // 0 current
  char buff[buff_size];
  float BatVolts = 12;                  // BatVolts to 12
  float BatAmps = 0;                    // BatAmps to 0
  float TotalAmps = 0;                  // TotalAmps to 0
  float AmpHours;                       // AmpHours remaining

// Setup start screen. Only do these one at a time and only once
// then comment out and re-program to get rid of Digole welcome screen.
//  control_addr(OFF);                  // control startup display
//  ctrl_start(OFF);                    // turn Digole screen ON/OFF
//  cursor(ON);                         // turn cursor ON
//  delay_ms(2000);                     // for 2 seconds

// reset AmpHours if switch is set
    if (!input(PIN_A3))
    {
    AmpHours = 12;                      // set to full battery
    EEPROM_address = 0x00;                          // Start of 4 bytes for float data
    write_float_eeprom(EEPROM_address, AmpHours);   // store AmpHours in EEPROM
    delay_ms (5);
    }
// read current AmpHours from non-volatile memory
    AmpHours = read_float_eeprom(EEPROM_address);
// setup display with welcome screen
  clear_LCD();                          // clear the LCD
  set_font(10);                         // change text to size 10
  sprintf(buff, "Battery Monitor");     // place text string in buffer
  text_position(4,0);                   // start second line, center
  send_str(buff);                       // display text array
// Turn on load
    output_low(PIN_A4);                 // LOW = relay ON

// START Endless loop
// any changes in the while loop will require checking the timing
   while (BatVolts>10.4)
 {
// check for AH reset here in final version
// clear the variables
    TotalAmps=0;
// select ADC to read VOLTS                     // not used yet
    set_adc_channel (1);                   // points a/d at channel 2, get volts
    delay_us(10);                         // wait
    BatVolts = read_adc();                    // starts conversion, reads ADC into volts
    delay_us(100);                         // wait 100 us
   BatVolts = BatVolts*0.078;                // convert ADC reading to volts
    sprintf (buff, "Battery %3.1fv", BatVolts); // send battery voltage to buffer
    text_position(0,1);                         // start second line
    send_str(buff);                             // display text array

// loop to integrate the current for 1 sec
// select ADC to read AMPS
    set_adc_channel (2);               // points a/d at channel 3
    delay_us(10);                     // wait

    for (i=0; i<100; i++)
    {
    ADCbits = read_adc();               // starts conversion, reads ADC into amps
    delay_us(4090);                     // wait 4 ms, to make up time to 1 sec
// test, .235 amps per bit, 30/127
//    ADCbits=119;                            // -2.109 amps to test, 119 is 9 bits below 128
    if(ADCbits>127)
    {
    ADCbits=ADCbits-127;                    // + current above 2.5 volts
    BatAmps=ADCbits*0.235;                  // 0.234 amps/bit, convert to amps
    }
    else
    {
    ADCbits=127-ADCbits;                    // - current below 2.5 volts
    BatAmps=-(ADCbits*0.235);               // convert to negative amps
    }
    TotalAmps=TotalAmps+BatAmps;            // integrate
    }                                       // end for loop

// pulse for timing check
    output_toggle(PIN_A5);                  // to check timing is 1 sec
// add the AmpHours
    AmpHours=AmpHours+(TotalAmps/360000);   // AH=TotalAmps/(100*time)
    if(AmpHours>12)
      AmpHours=12;                          // don't exceed 12 AmpHours
// Display AH
    text_position(0,2);                             // start second line
    sprintf (buff, "AH remaining %4.2f", AmpHours); // send AmpHours to buffer
    send_str(buff);                                 // display text array
// Store AmpHours in non-volatile memory
    EEPROM_address = 0x00;                          // Start of 4 bytes for float data
    write_float_eeprom(EEPROM_address, AmpHours);   // store AmpHours in EEPROM
    delay_ms (5);
 
 }                       // end of while loop, exit if BatVolts < 10.5

// BatVolts has reached 10.5 volts so turn off load
    output_high(PIN_A4); // Shut down and stop
}                        // end of main function

// Functions
// Clear Display
void clear_LCD (void)
   {
   I2C_START ();                // start I2C
   I2C_WRITE (LCD_WRT_ADDR);    // addr of LCD
   I2C_WRITE ('C');             // C CL to clear display
   I2C_WRITE ('L');             // L
   I2C_STOP ();                 // stop I2C
   }
// Turn cursor ON or OFF
void cursor(short ctrl)
   {
   I2C_START ();                // start I2C
   I2C_WRITE (LCD_WRT_ADDR);    // addr of LCD
   I2C_WRITE ('C');             // C, CS0 to control cursor
   I2C_WRITE ('S');             // S
   I2C_WRITE (0);            // 1=ON, 0=OFF
   I2C_STOP ();                 // stop I2C
   }
// Control Start Screen
void ctrl_start (short ctrl)
   {
   I2C_START ();                // start I2C
   I2C_WRITE (LCD_WRT_ADDR);    // addr of LCD
   I2C_WRITE ('D');             // D DSSb to ccontrol Digole screen
   I2C_WRITE ('S');             // S
   I2C_WRITE ('S');             // S
   I2C_WRITE (ctrl);            // 1=ON, 0=OFF
   I2C_STOP ();                 // stop I2C
   }
// Display Configuration: Control address display
void control_addr (short ctrl)
   {
   I2C_START ();                // start I2C
   I2C_WRITE (LCD_WRT_ADDR);    // addr of LCD
   I2C_WRITE (0x44);            // D, to display I2C address
   I2C_WRITE (0x43);            // C
   I2C_WRITE (ctrl);            // 1=on, 0 to turn off
   I2C_STOP ();                 // stop I2C
   }
// set position of next text
void text_position(int line, int column)
   {
   I2C_START ();                // start I2C
   I2C_WRITE (LCD_WRT_ADDR);    // addr of LCD
   I2C_WRITE ('T');            // T, TP set text position
   I2C_WRITE ('P');            // P
   I2C_WRITE (line);            // line position
   I2C_WRITE (column);          // column position
   I2C_STOP ();                 // stop I2C
   }
// Set Font Size
void set_font (char size)
   {
   I2C_START ();                // start I2C
   I2C_WRITE (LCD_WRT_ADDR);    // addr of LCD
   I2C_WRITE ('S');
   I2C_WRITE ('F');
   I2C_WRITE (size);
   I2C_STOP ();                 // stop I2C
   }
// Set Display Mode for next command to inverse
void set_DM(char cmd)
   {
   I2C_START ();                // start I2C
   I2C_WRITE (LCD_WRT_ADDR);    // addr of LCD
   I2C_WRITE ('D');
   I2C_WRITE ('M');
   I2C_WRITE (cmd);
   I2C_STOP ();                 // stop I2C
   }
// send string to LCD
void send_str(char buff[buff_size])
   {
   int i;
   I2C_START ();                // start I2C
   I2C_WRITE (LCD_WRT_ADDR);    // addr of LCD
   I2C_WRITE('T');              // send TT for text coming
   I2C_WRITE('T');
    for (i=0; i<buff_size; i++)
   {
   I2C_WRITE(buff[i]);          // start with a Z
   }
   I2C_WRITE(0);
   I2C_STOP ();                 // stop I2C   
   }
// draw a line from x,y to x1,y1   
void draw_line(int x, int y, int x1, int y1)
   {
   I2C_START ();                // start I2C
   I2C_WRITE (LCD_WRT_ADDR);    // addr of LCD
   I2C_WRITE ('L');
   I2C_WRITE ('N');
   I2C_WRITE (x);
   I2C_WRITE (y);   
   I2C_WRITE (x1);
   I2C_WRITE (y1);
   I2C_STOP ();                 // stop I2C
   }
// Read a 32 bit floating point number from internal eeprom
// Inputs an eeprom address, Outputs the floating point number read from the internal eeprom
float read_float_eeprom(int EEPROM_address)
 {
    float data;
    int i;
   for(i = 0; i < 4; i++)
     *((int8 *)(&data) + i) = read_eeprom(EEPROM_address + i);
   return(data);
 }
// Write a 32 bit floating point number to internal eeprom
// Inputs:     1) An eeprom address. Four eeprom locations will be used.
//             2) The floating point number to write to internal eeprom
void write_float_eeprom(int EEPROM_address, float data)
 {
   int i;
   for(i = 0; i < 4; i++)
     write_eeprom(EEPROM_address + i, *((int8 *)(&data) + i));
}

// end
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page Previous  1, 2
Page 2 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group