Monitoring charge state of a 12V battery

In my project I am using a solar panel to charge a 12V battery to power a small pump and then to a 5V usb converter to power the photon. One of the variables I want to keep track of is the state of the battery, so that it does not get too low. I’m waking the photon twice a day to run the other sensors I have and perform the tasks i need and I want to see the voltage of the battery after each of these wakeups. I have already tried creating a voltage divider to drop it down to a safe 3-3.3V into an analog pin but I cannot get anything. Can someone please help me configure this? code examples are greatly appreciated because that is my weakest skill for these sort of projects.

This is my voltage divider

int testpin = A4;
void loop(){
    float battvolts;
    battvolts = analogRead(testpin);
    battvolts = float (battvolts)*3.3/4096*(404.7/102.2);
    Particle.publish("batterytest",String(battvolts), 60);
    delay(5000);
}

This is the working code that I have with the voltage divider i uploaded above. Seeing accuracy withing about .05V which should be good enough for what I’m doing.
Thanks

1 Like

What do you mean by “I cannot get anything”? You should show us your code, and your wiring of the voltage divider.

I uploaded a pic of my voltage divider. Im trying to use pieces of code from other projects and i think that is most of my issue. One of my professors did a project that had a piece of this that I wanted to use but is different from what I need and the other stuff that I have been testing comes from the other “monitor a 12V battery” on the getting started forum.

This is my professors project, I’m wanting to use the batterycheck he has implemented towards the end

/*This sketch experiments with low power for battery/solar operation and thingspeak.
 to transmit a notification to your device when the mail is received. (mailbox door opened)
 a magnetic reed switch is connected to D0.  The particle sleeps and wakes on a falling edge interrupt at D0.
 if triggered it publishes a notification that can be detected by IFTTT and sends a notification
 to your device.
 on every interrupt and on wakeup a update is published to thingspeak with voltage, RSSI and # of triggers.
                                       +-----+
 *                          +----------| USB |----------+
 *                          |          +-----+       *  |
 *       /------------------| [ ] VIN           3V3 [ ] |
 *      |                   | [ ] GND           RST [ ] |
 *    10K                   | [ ] TX           VBAT [ ] |
 *      |                   | [ ] RX  [S]   [R] GND [*] |-------
 *      |                   | [ ] WKP            D7 [ ] |       \
 *       \                  | [ ] DAC +-------+  D6 [ ] |       |
 *       /------batt sense->| [*] A5  |   *   |  D5 [ ] |       |
 *    10K batt sense gnd  ->| [*] A4  |Photon |  D4 [ ] |     10K pull down to ground
 *                          | [ ] A3  |       |  D3 [ ] |       |
 *                          | [ ] A2  +-------+  D2 [ ] |       |
 *                          | [ ] A1             D1 [ ] |       |
 *                          | [ ] A0             D0 [*] |<-magnetic reed (door) switch
 *                          |                           |
 *                           \    []         [______]  /
 *                            \_______________________/
 *
 *
 */

/*  begin particle webhook code snippit to add to particle webhook interface on console
{
    "event": "thingSpeakWrite_",
    "url": "https://api.thingspeak.com/update",
    "requestType": "POST",
    "form": {
        "api_key": "{{k}}",
        "field1": "{{1}}",
        "field2": "{{2}}",
        "field3": "{{3}}",
        "field4": "{{4}}",
        "field5": "{{5}}",
        "field6": "{{6}}",
        "field7": "{{7}}",
        "field8": "{{8}}",
        "lat": "{{a}}",
        "long": "{{o}}",
        "elevation": "{{e}}",
        "status": "{{s}}"
    },
    "mydevices": true,
    "noDefaults": true
}
*/

char publishString[40];
int mailpin = D0;
int volttestpin = A5;
int voltpowerpin = A4;
const int sleepytime = 1800; //seconds to sleep 3600=1hr
const String key = "YOUR_THINGSPEAK_KEY_HERE";


int rssival = 0;
volatile bool mailsend = FALSE;
volatile bool rssisend = FALSE;
int mailtriggers = 0;
long waketime = 0;
SYSTEM_THREAD(ENABLED); //enable multitasking.  allows the ability to catch the mailpin trigger and send the message
SYSTEM_MODE(AUTOMATIC);  //run code. then connect     
     
void rssi_update()
{
    //delay (2000);
    static int count = 0;
    Serial.println(count++);
    rssival = WiFi.RSSI();
    sprintf(publishString,"%d",rssival);
    bool success = Particle.publish("RSSI",publishString);
    
    sprintf(publishString, "%1.4f", checkbattery());

       Particle.publish("thingSpeakWrite_All", "{ \"1\": \"" + String(rssival) + "\"," +
       "\"2\": \"" + String(checkbattery()) + "\"," +
       "\"3\": \"" + String(mailtriggers) + "\"," +
     //"\"4\": \"" + String(var3) + "\"," +
     //"\"5\": \"" + String(var4) + "\"," +
       "\"k\": \"" + key + "\" }", 60, PRIVATE);
    if (success == TRUE) {
        //delay(10000);
        rssisend = FALSE; //if sent, then turn of the send flag, otherwise let it try again.
    }
}


void enablemailsend(void){
    mailsend=TRUE;
    digitalWrite(D7,1);
}

void setup() {
    //setADCSampleTime(ADC_SampleTime_239Cycles5);  //sets adc to longest time for low noise
    Time.zone(-4);  //set eastern time zone
    pinMode(mailpin,INPUT);
    pinMode(voltpowerpin, INPUT);
    pinMode(D7, OUTPUT);
    attachInterrupt(mailpin, enablemailsend, FALLING);
    Particle.variable("mailtrig", mailtriggers);
    rssisend = TRUE;
}


void loop() {
    if ((Particle.connected())&&(mailsend == TRUE)&&WiFi.ready()) 
    {
        mailnotification();
    }
    
    if ((Particle.connected())&&(rssisend == TRUE)&&WiFi.ready()) 
    {
        rssi_update();
    }
    

    if ((millis()-waketime > 20000) && (mailsend == FALSE) && (rssisend == FALSE)) 
    {
        Particle.disconnect();
        
        //System.sleep(mailpin,RISING,30); 
        System.sleep(mailpin,FALLING,sleepytime);
        pinMode(mailpin,INPUT); // this may need resetting after sleep.
        waketime = millis();  //millis haults when sleeping.  find out the time on boot and compare from there.
        rssisend = TRUE;  //on wakeup report rssi but do not report mail trigger
        mailsend = FALSE;
        
    }

    if (digitalRead(mailpin) == LOW) {
        enablemailsend();
    }
    
    if (Particle.connected() == false) 
    {
        Particle.connect();
    }
    /*if (waitFor(Particle.connected, 20000))
    {
    }
    */
}

float checkbattery(){
    float battvolts;
    pinMode(volttestpin, INPUT);  
    pinMode(voltpowerpin, OUTPUT); //sent power pin as output
    digitalWrite(voltpowerpin, 0); //send a 0, or LOW to turn on voltage test
    delay(1); //wait for volts to settle good measure
    battvolts = analogRead(volttestpin); //sample voltage
    battvolts = float(battvolts)*3.3/4096.0*2.0*3.85/3.827;  //calibrate voltage reading
    pinMode(voltpowerpin, INPUT); //disable powerpin and set as high Z input
    return(battvolts); //return calibrated battery voltage
}

void mailnotification(void){
    digitalWrite(D7,1);
    rssi_update();
    sprintf(publishString, "%1.4f", checkbattery());
    Particle.publish("mail/volts", publishString,60,PRIVATE);
    
    //sprintf(publishString, "%d", mailtriggers);
    bool success = Particle.publish("youve-got-mail", Time.format(Time.now(), "%I:%M%p"), 60, PRIVATE);
    delay(1000);
    if (success == TRUE) {
        mailtriggers++;
        mailsend = FALSE; //if sent, then turn off the send flag, otherwise let it try again.
        digitalWrite(D7,0);
        }
}

I have added some code at the top that I have working, could you take a look and spot any pitfalls? I would greatly appreciate it.

300K and 100K ohm are really large as a voltage divider for these purposes. The reason is that the ADC pin draws some current too, to do its reading which will be in the range of the 32 microamps that you are using to drop the voltage through your divider. I’d recommend using a 3K and 1K resistor instead.

Also, I’m not sure how stable your 12v source is, but if it went up to say 15v, your divider would divide down to 3.75v, which would exceed the max on those input pins. So for that reason, maybe you should use a larger divider, like maybe 5k and 1k. The ADC has plenty of resolution even within a somewhat more limited range.

my powersource is a sealed lead acid battery (12V, 5000mAh for a brake controller). Thanks for the help

12V is the nominal value, but fully charged batteries are likely to go a bit beyond that voltage.
So you may want to fully charge and take a proper reading and then add some margin.

I’ve changed my voltage divider to 4.7kohm and 1kohm. I have plugged the actual values into the calibration in my code and it is back to running within about .04-.05 V of the measured value so everything looks good. Also with this configuration I should be able to handle +15V

You can use one of these to read voltages up to 26v very accurately.

This would also allow you to measure current if you desire but you would need to change the resistor size to meet your max current in our out based on what you have hooked up to the battery. You can just measure voltage with it also and forget the current readings to keep things simple.

There is a Particle library for these and they work great. Think this chip does not waste power like the Resistor Divider setup does but I’m not sure.