My final try to convince you that you might be able to use ADC channel 39 to monitor the teensy's supply voltage. The sketch won't monitor your battery voltage or Vin, but it will monitor the teensy's supply voltage (e.g., what you would measure from the teensy's 3.3v pin). So with > 3.7 volts at Vin, the channel-39 sketch should report about 3300 mv. As Vin voltage drops, the channel-39 sketch should report a lower voltage. As Paul notes, if Vin falls below 3.7v the internal voltage regulator might go non-linear near Vin of 2.7v.
Some time ago, I did some experiments using this technique of measuring analogRead(39) to get an indication of the input voltage. Today I spotted another thread about this (
LiPo Battery Monitoring Circuit & Code). In this post I share the findings of my experiments in the hope that others find it useful.
Test setup & Method
I used a Teensy 3.0, the Vusb trace is not cut. No external components are attached and the following code is uploaded:
Code:
analogReference(EXTERNAL);
analogReadResolution(12);
analogReadAveraging(32);
Serial1.begin(9600);
Serial1.println("Boot!");
while (1) {
delay(1000);
Serial1.print("analogRead: ");Serial1.println(analogRead(39));
}
Next, I connected a power supply to either the Vin or 3v3 pin, I used an serial->usb converter to obtain the values returned by analogRead(39). The measurements were performed by varying the voltage, writing down the analogRead value and measuring the applied input voltage with a cheap multimeter (readings up to 0.01V, but I'm not so certain about its accuracy).
Measurements
For both applying the input voltage to the Vin pin and 3v3, about 40 measurements are taken. These can be seen in
View attachment to_Vin.txt and
View attachment to_3v3.txt respectively.
Analysis
The goal is to obtain a function which returns the input voltage applied to the Teensy as a function of the measured analogRead value. To obtain this function I resampled the measurements such that they were equidistant, then I fitted a polynomial through the resampled data points. For the Vin input I also converted the quadratic polynomial to an integer-only version. I used a Python script to analyse the measurements. It uses matplotlib and numpy, the entire script can be downloaded:
View attachment analyse.py.txt (.py is not allowed

) or
Gist.
Input to 3v3 pin:
The polynomial coefficients found are: (leftmost is x^2 and x^3 respectively)
Coefficients P_2: [ 6.24435071e-04 -3.86562880e+00 7.70557029e+03]
Coefficients P_3: [ -3.43172539e-07 2.78848110e-03 -8.24887282e+00 1.05447294e+04]
Input to Vin pin:
Coefficients P_2: [ 4.79305738e-04 -3.18084349e+00 7.22111948e+03]
Coefficients P_3: [ -2.31118140e-07 2.03935318e-03 -6.60300979e+00 9.65583352e+03]
I wanted a funtcion without floating point computation so I tried to convert the quadratic polynomial to an integer only version by scaling the positive terms to the maximum allowed in an unsigned 32 bit integer. I assumed the maximum value returned by analogRead is about 3000, this means that a scale factor can be calculated with the positive coefficients of the polynomial. Then the coefficients of the polynomial can be multiplied by this factor, dividing by it after the addition allows computation without floating points:
Code:
Scale: 372346.361704
C0: 178
C1: 1184375
C2: 2688757565
Calculation in C:
uint32_t output = (178*x*x + 2688757565 - 1184375 * x) / 372346; // order is important, negative values need to be prevented.
This integer representation of the polynomial is also plotted in the graph, where it can be seen that this hardly deviates from the floating point values.
Evaluation
I used the formula derived above to create a function to run on the Teensy:
Code:
/*
// be sure to set the ADC to use the external reference, put the following in your setup():
analogReference(EXTERNAL);
analogReadResolution(12);
analogReadAveraging(32); // this one is optional.
*/
uint32_t getInputVoltage(){ // for Teensy 3.0, only valid between 2.0V and 3.5V. Returns in millivolts.
uint32_t x = analogRead(39);
return (178*x*x + 2688757565 - 1184375 * x) / 372346;
}
This function returns the input voltage applied to Vin in mV without the use of any external components. I compared the values provided by this function with the readings provided by my multimeter, they were in close agreement. I would be confident in using this to get an indication of the battery level.
The files with measurements and analyse.py script can also be downloaded from a gist: [Teensy] Script used to relate analogRead(39) with input voltage.