Introduction

My car tends to eat batteries! Sometimes, particularly during pandemic summers many weeks pass without the car being used, and during this time sometimes the battery discharges. I suspect the underlying problem is that somewhere current is leaking to ground, but that seems hard to fix. Instead, I’d be happy to just monitor the situation and charge the battery if needed.

The car is usually parked in range of the home WiFi network, so a convenient solution would be to have the car send me email about its battery every once in while. This article describes how to do just that.

[Car Battery Monitor]

All the files for this project are on GitHub1. The code is written in CircuitPython, the PCB was designed in KiCad, and the 3D-printable case was designed in OpenSCAD.

Design constraints

The only real constraint is that the device mustn’t draw too much current. To get a feel for the numbers say the car battery has a capacity of 100 amp hours, and in the absence of other drains we’d like a lifetime of about ten years (so that a month is roughly 1% of the capacity).

There are about 8,766 hours in a year, call it 10,000, and so about 100,000 hours in a decade. Our 100 amp hour battery can supply about 1mA for that time, so we should make sure that our gadget draws less than this.

Having satisfied the current constraint, I was keen to optimize for easy of implementation rather than anything. Rather than put components on a PCB, I was quite happy to use modules. Doubtless much smaller and cheaper designs exist, but I wasn’t fussed about that here.

Basic hardware

These days I think the default choice for random WiFi gadgets is something from the ESP32 family. I recently experimented with the ESP32S2 on the TinyS2 board, using a (slightly different) Pololu DC-DC converter to drop a 12V supply to 5V. You can read the details2 but the key numbers are:

StateCurrent consumption
Deep sleep90µA
WiFi active45mA

So, provided the WiFi isn’t on very often the current consumption might be an order of magnitude below the target.

I wanted to hear from the car every day but I don’t trust the accuracy of the clock, so I thought it best to send email every six hours. That way I can be reasonably confident of getting an email at night even if the car is away during the day.

It takes about 10s to connect to WiFi and send an email, so the duty cycle is 10s in 6 hours, or about 1 in 2000. This means that the average current for doing things is about 25µA, which when added to the sleep current of 90µA brings us to 115µA.

Schematic

[Schematic]3

Bill of Materials

DesignatorPartFootprint
U1Pololu D36V6F3 3.3V buck converter4
U2TinyS25
RV1V18AUMLA1210NH6 MLV1210
F1NANOASMDC010F-27 100mA polyfuse1206
D1SS248 Schottky diodeDO-214AA (SMB)
R1180k resistor0805
R233k resistor0805
C1100nF X7R capacitor0805

Getting power

The gadget just needs a 12V power supply which is on even when the car is stopped and the engine’s off. I powered it from a cigarette lighter socket.

I bought a plug from Amazon which had both a fuse and LED. I replaced the fuse with one with a much lower current rating (500mA). The LED I removed completely: it drew a few mA!

Protection

I am no expert but I understand that the car’s 12V supply can be rather noisy so I made some attempts to protect the electronics. The DC-DC converter claims to support input voltages of 50V, so I just wanted something to handle spikes.

The delightfully named Littelfuse company make a wonderful range of varistors for precisely this task. These devices start to conduct if the voltage across them (in either direction) exceeds some threshold. If this went on for very long the magic smoke would escape, so I put a polyfuse in series with the supply.

Finally there’s a Schottky diode for reverse polarity protection.

Voltage sensing

The ESP32S2 has inbuilt ADCs, so all that’s needed is an external potential divider. The total resistance is about 200kΩ, which implies a current of about 60µA when fed with 12V.

Empirically a simple linear model was a good map from ADC count to input voltage. Fitting over the range 10–15V gives:

V = 4.0418 * (ADC / 10000) + 0.8294

which makes these predictions

True voltage / VADC count / 10,000Model value / V
6.01.23925.929
7.01.51327.021
8.01.75757.996
9.02.01969.041
10.02.26399.996
11.02.522111.015
12.02.764312.011
13.03.014613.010
14.03.252913.959
15.03.507114.973

Construction

Although the circuit is simple, I made a PCB to make things neat and tidy. I’d not realised that GPIO pin 0 is used by the bootloader, so had to bodge things a little. The files in the GitHub repo9 have been updated to fix this problem.

[PCB]

Software

All the software was written in CircuitPython. It is straightforward, but a few points are worth making:

  1. We take all the ADC readings before turning on the WiFi because the wireless part draws lots of current which fluctuates rapidly.

  2. Results are sent by SMTP to a mail server on the local network. I’ve written about this10 before.

There are three Python files:

Installation

There are four layers of software to consider.

  1. The hardware bootloader in the ESP32. You need special software e.g. esptool14 to communicate with this.

  2. A UF215 bootloader, which provides a USB mass-storage device to which firmware can be uploaded.

  3. The CircuitPython binary, which is available as a UF2 image. When installed it provides a USB mass-storage device to which your Python code can be uploaded.

  4. The Python files which contain your application.

The CircuitPython website has instructions16 for installing all this, and both the UF2 bootloader and CircuitPython UF2 images.

These are the steps:

  1. Reset the board holding the BOOT button down.

  2. Use esptool.py to install the UF2 boot loader (combined.bin).

  3. Reset the board.

  4. Install the CircuitPython UF2 image.

  5. Reset the board.

  6. Copy the three .py files to the board.

  7. Reset the board.

Case

I designed a simple 3D-printable case in OpenSCAD. It is rather larger than necessary, but gives space to knot the incoming power cable to stop it being pulled out.

Conclusions

The device basically works as desired. The measured sleep current is about 130µA, so adding in the extra current used when active I expect the average to be about 155µA. This is a bit lower than I’d expected, probably because the DC-DC converter is a bit more efficient than in my earlier tests.

If I had time, it would be nice to fit all the components inside the cigarette-lighter plug but I doubt I’ll bother.

I am struck by just how easy it is to build something like this from pre-existing modules and Python, and still only sip power. I think we should all be thankful for the work that people have donated to the free commons which makes this possible.