Battery voltage calculation for 18650 batteries



  • I have built a Dallas Temperature sensor around a Pro Mini 8Mhz.

    I am powering it with two li-ion 18650 cells connected in series (~8.4V maximum voltage when fully charged, 5.4V minimum voltage before cutoff) and powering the Pro Mini via the RAW port.

    I would like to send the current battery voltage info to the controller. I connected the wiring according to the "Battery Powering" page (1Mohm + 470kohm resistors, the resistor join point also connected to A0). The +V is the RAW port of the arduino (where the battery + is connected)

    The sketch works but seems to report wrong voltages.

    When there is no actual voltage (battery disconnected and arduino powered by the serial programmer), it reports 0.17V.
    When the battery is connected, it should report ~8V (SOC on the li-ion pack) but it reports 3.44V.

    Maybe the example sketch was specifically created for two AA batteries in series?

    My node sketch:

    // Enable debug prints to serial monitor
    #define MY_DEBUG 
    
    #define MY_NODE_ID 78
    
    // Enable and select radio type attached
    #define MY_RADIO_NRF24
    //#define MY_RADIO_RFM69
    
    #include <SPI.h>
    #include <MySensors.h>  
    #include <DallasTemperature.h>
    #include <OneWire.h>
    
    // Define sensor node childs
    #define CHILD_ID_BATT 1
    #define CHILD_ID_TEMP 0
    
    
    #define COMPARE_TEMP 0 // Send temperature only if changed? 1 = Yes 0 = No
    
    #define ONE_WIRE_BUS 7 // Pin where dallase sensor is connected 
    
    
    #define MAX_ATTACHED_DS18B20 16
    unsigned long SLEEP_TIME = 1000; // Sleep time between reads (in milliseconds)
    
    // Battery related init
    int BATTERY_SENSE_PIN = A0;  // select the input pin for the battery sense point
    float oldBatteryV = 0;
    MyMessage msgBatt(CHILD_ID_BATT, V_VOLTAGE);
    
    
    // Dallas Temperature related init
    OneWire oneWire(ONE_WIRE_BUS); // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
    DallasTemperature sensors(&oneWire); // Pass the oneWire reference to Dallas Temperature. 
    float lastTemperature[MAX_ATTACHED_DS18B20];
    int numSensors=0;
    bool receivedConfig = false;
    bool metric = true;
    MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP);
    
    
    
    void before()
    {
      // Startup up the OneWire library
      sensors.begin();
    }
    
    void setup()  
    { 
      // requestTemperatures() will not block current thread
      sensors.setWaitForConversion(false);
    
    
      // needed for battery soc
      // use the 1.1 V internal reference
      #if defined(__AVR_ATmega2560__)
          analogReference(INTERNAL1V1);
      #else
          analogReference(INTERNAL);
      #endif
    
    }
    
    void presentation() {
      // Send the sketch version information to the gateway and Controller
      sendSketchInfo("Temperature Sensor", "1.1");
    
      // Fetch the number of attached temperature sensors  
      numSensors = sensors.getDeviceCount();
    
      // Present all sensors to controller
      for (int i=0; i<numSensors && i<MAX_ATTACHED_DS18B20; i++) {   
         present(i, S_TEMP);
      }
    }
    
    void loop()     
    {     
    
      // get the battery Voltage
      int battSensorValue = analogRead(BATTERY_SENSE_PIN);
      Serial.println(battSensorValue);
      float batteryV  = battSensorValue * 0.003363075;
      Serial.print("Battery Voltage: ");
      Serial.print(batteryV);
      Serial.println(" V");
    //  if (oldBatteryV != batteryV) {
        // Power up radio after sleep
        send(msgBatt.set(batteryV, 2));
        oldBatteryV = batteryV;
    //  }  
    
      // Fetch temperatures from Dallas sensors
      sensors.requestTemperatures();
    
      // query conversion time and sleep until conversion completed
      int16_t conversionTime = sensors.millisToWaitForConversion(sensors.getResolution());
      // sleep() call can be replaced by wait() call if node need to process incoming messages (or if node is repeater)
      sleep(conversionTime);
    
      // Read temperatures and send them to controller 
      for (int i=0; i<numSensors && i<MAX_ATTACHED_DS18B20; i++) {
    
        // Fetch and round temperature to one decimal
        float temperature = static_cast<float>(static_cast<int>((getControllerConfig().isMetric?sensors.getTempCByIndex(i):sensors.getTempFByIndex(i)) * 10.)) / 10.;
    
        // Only send data if temperature has changed and no error
        #if COMPARE_TEMP == 1
        if (lastTemperature[i] != temperature && temperature != -127.00 && temperature != 85.00) {
        #else
        if (temperature != -127.00 && temperature != 85.00) {
        #endif
    
          // Send in the new temperature
          send(msgTemp.setSensor(i).set(temperature,1));
          // Save new temperatures for next compare
          lastTemperature[i]=temperature;
        }
      }
      sleep(SLEEP_TIME);
    }
    

    Any help is appreciated.


  • Mod

    @sola yes, you are correct that the calculation is form2xAA. See the comment inside loop() on the example at https://www.mysensors.org/build/battery for the required calculation, and this part for the voltage divider:

    The ADC is set to use the internal reference value of 1.1V - so Vmax at ADCmax = 1.1*(16+4703)/4703 = 3.44V

    You'll need to change the resistors in the voltage divider to handle voltages as high as 8V.


Log in to reply
 

Looks like your connection to MySensors Forum was lost, please wait while we try to reconnect.