Lab 07b: Time Remaining

The questions below are due on Tuesday March 20, 2018; 08:25:00 PM.

Partners: You have not yet been assigned a partner for this lab.
You are not logged in.

If you are a current student, please Log In for full access to the web site.
Note that this link will take you to an external site ( to authenticate, and then you will be redirected back to this page.

Music for this Lab



In this lab we're going to return to our system's power usage and add in some structure to provide more information about current battery capacity.


One of the downloadables is a new, updated version of the adp5350 library. Make sure to replace that folder in your libraries directory in your Arduino directory and then restart Arduino.

The microcontroller code for today, power_tracker.ino can be uploaded to your ESP32 as-is, and we'll spend lab adding on to it. Make sure your button is connected properly to Pin 15 as we have done all semester, and that your large white LED and GPS are connected to LDO3. When up and running you'll get some content on your OLED that shows some stuff, including two symbols in the upper left corner. One represents the state of the LDO3 (the shiny symbol), and one represents the state of WiFi on the ESP32. By pressing the button, you can toggle these elements on and off (there are three states: ALL ON, LED/GPS (LDO3) ON WiFi OFF, and ALL OFF).

In addition, below the symbols is a message indicating the state of your system with regards to its battery, and whether it is charging you battery or not (when hooked up to USB it will be charging, and when disconnected it will be discharging). If it gets fully charged, it will say FINISHED.

If in discharging mode (running off the battery), three more lines of text will appear:

  • Discharge Amt: Currently a dummy value which you'll implement in this lab. This should eventually report the fraction of battery discharged based on the battery voltage.
  • Battery Voltage: The current battery voltage (in Volts)
  • Time Remaining: Currently a dummy value which you'll implement in this lab. This should eventually report the remaining "life" of the device based on discharge amount and discharge rate.

By the end of the lab, your display should look like the following, but at the start you'll also see some of this stuff. The battery symbol is something you'll create at the end. The Discharge Amount and Time-Remaining are values you'll also calculate.

Ok now let's move on.

1) A Reminder of Batteries

As we saw in Lecture 2, our lithium polymer battery does not provide a flat voltage which we can use directly. Instead it yields a highly variable voltage based off of the discharge amount and dischrage rate and additional electronics (such as voltage regulators in our ADP5350 or onboard our ESP32 development board) "clean up" this voltage up so that our sensitive electronics can run.

If starting from a fully-charged state, we go ahead and record our battery's voltage over time as we draw a constant amount of power we can get very nice discharge curves. We ran our 350 mAh battery and logged its voltages over several discharge cycles (with constant consumption) in order to get the data shown below. The shape and values of this curve are surprisingly consistent, so we'll use this as a starting dataset in this lab. You'll notice the x axis is time (in hours, and the battery goes through its complete cycle from charged to discharged in about 2.5 hours when we were drawing the amount of current we were. In this case all our electronics were drawinga bout 150 mA continuously, so we can see the 350 mAhr rating is approximately correct)

Battery Discharge curve over time.

Because this data was collected while under a constant load, meaning the system's components were drawing a constant amount of current and power, we can directly relate time to the discharge amount of the battery so that we get a similarly shaped plot below except with a re-interpretted x-axis. We'll define discharge amount as a normalized value ranging from 0.0 to 1.0 where 0.0 is a fully charged battery (no amount discharged) and 1.0 is a fully discharged batter (all discharged).

The battery voltage as a function of discharge fraction (where 0.0 means a fully charged battery and 1.0 means af fully discharged battery).

While most of the time in use, we really don't need to worry about this variable battery voltage, the fact that the plot takes on the shape it does, provides a means to extract how much capacity we've got left in the battery during operation (without this we'd need some sort of timer and integration and that can be unreliable.) In fact, during normal runtime operation, we can readily take a measurement of the battery's voltage using the following command (which returns the voltage of the battery back in millivolts) since the ADP5350 has some circuitry built-in to do this for us.

int voltage = adp.batteryVoltage(); // Battery Voltage (mV)

So putting this all together, the goal of this lab will be for you to implement a few functions that will allow the reporting of the remaining charge left in our battery and ultimately estimate how much time we have left with our battery during operation (and under several different power-demand scenarios). We'll do this by:

  1. First coming up with a mathematical model to express battery voltage as a function of discharge amount based off of the data shown above and implementing that function in code.
  2. Generating a second function that uses the previous function to express discharge amount as a function of battery voltage.
  3. Use the discharge amount to render a human-understandable "battery-remaining" symbol
  4. Track the discharge amount over time to estimate a discharge rate which can be used to estimate the time remaining before the battery is expired.

2) A Polynomial

The curves shown above in Figures 2 and 3 are taken from data, but in order to use them in our code, we'd like to encapsulate the trend shown into a lower-order polynomial since that will take up significantly less space in memory. In the downloadable for this week, open up Within this simple Python script, you'll see two large Python lists which correspond to the discharge amount and voltage values of the plot in Figure 3. We'd like to use this data to create a polynomial so we'll need to run a fitting routine on it. For this we're going to use numpy and matplotlib. Make sure you have numpy installed in your Python distribution as well as matplotlib. If you don't, you can use pip or whatever to install them (or ask for help!). Once you're sure you have those installed look up how to use numpy's polynomial fitter given the data we've provided in order to calculate a fourth-order polynomial based off of the levels vs. voltages values. Store the result in the fit_np variable, and run the code as well as uncomment a few lines that will allow you to compare the data to its fitted polynomial. You should get a plot that shows the raw data as well as the resulting fitted-polynomial. Is it close? On what parts is it not?

2.1) Recreating in C++

The next part will be to create a C++ function that expresses that polynomial. This function, voltage_from_discharge should, as its name implies, take in a value of normalized discharge amount and return the voltage that corresponds to that point. Use the checker below to test your function. When it is working, replace the appropriate function in the ESP32 script for lab.

float voltage_from_discharge(float discharge){ //your code here }

Go over your function from above and what we've covered so far with a staff member.

3) Discharge from Voltage

So we have a function which gives us voltage provided we know the discharge amount. In run-time we want the opposite though! Specifically the system can get voltage readings of the battery and we'd like it to figure out what amount of the battery has been discharged. Unfortunately our polynomial is fourth order so we can't just invert it by hand and find a non-disgusting looking closed-form solution (really anything above third-order polynomials are very hard to algebraically invert, and getting a closed-form solution to above ~fifth order is generally impossible).

What we can do, howevever, is computationally invert using our original function voltage_from_discharge. The basic idea behind this is that since it is relatively easy to call voltage_from_discharge we can search through its outputs (voltages), compare to the voltage we've measured, and then return the corresponding discharge amount used to generate that voltage. In essence, we're brute-forcing our way to an inverted function. Create a function discharge_from_voltage that takes in two arguments:

  • float voltage: The voltage (in Volts) we'd like to know the corresponding discharge amount for
  • float error: The error (in Volts), we'll tolerate in our answer. If a sufficient error cannot be found, the function should return the closest value possible.

There are many ways to do this. This is essentially a search problem. You can iterate through the entire range of possible values, you can perform a binary search through the range, or try other ways. Use the checker function below to develop your code as well as your ESP32. Note a functiong version of voltage_from_discharge is provided for you in this checker.

float discharge_from_voltage(float voltage, float error){ //your code here! return 0; }

Once this second function is working, insert it (along with your earlier function) into the ESP32 code skeleton, and reupload. You should now see the Discharge amount be something reasonable when you disconnect your system from the computer.

But humans don't care about numbers. We need a symbol to show us what we have. On to the next section.

4) How Much Left

A classic symbol of our modern lives is the battery symbol with a varying fill-amount indicative of charge. Along with hunger, this symbol guides a lot of decisions in our lives. We'd like to have that for our labkit! Complete the function void drawBattery(uint8_t x, uint8_t y, float level) in the code skeleton which takes in three values:

  • uint8_t x: The upper x coordinate for drawing
  • uint8_t y: The upper y coordinate for drawing
  • float level: The fill amount of the battery

This function should render a battery of the dimensions shown below (but which can be translatable in x and y based on the coordinate input arguments) and fill a proportional amount of the battery based on the level provided (so if level is 0, the battery symbol will be empty/hollow and if level is 1.0, the battery symbol will be full.) Use the shape below for size reference. We'll ultimately want to draw it at coordinate (104,3), but we'd like you to keep the function as general as possible in terms of positioning (so you could move it around in x and y as needed). Remember the U8G2 references are your friend.

Note that the value taken in by the level variable is equivalent to 1-discharge_amt! This is implemented in how we call the function in the code!

We'd like to render a battery icon similar to the one shown above, with the fill amount proportional to the 1-discharge amount.

Show your working battery symbol to a staff member. Be prepared to go into detail code.

5) Time Remaining

Finally, wouldn't it be really great on top of all of this is if we could somehow estimate how much time we have left based on the current discharge rate AND discharge amount? Using information which you have access to in the code, update the following region of the code so that the variables discharge_rate and time_remaining properly express the dischrage rate and time remaining until fully discharged, respectively.

  //every check_period iterations, calculate a derivative of the discharge amount
  if (check_counter ==0){
    discharge_rate = 0;
    time_remaining = 0;
    old_discharge_amt = discharge_amt;

Your system should be estimating the seconds remaining until your out of power once your changes are in place! How does it change as you cycle the system loads? (turn off WiFi, LED, etc..?

Show your whole working system to a staff member