Siddhant Kumar
Project 088 Β· Automotive

EV Range & Trip Logger.

Logs an electric vehicle's real energy use trip by trip and turns it into an honest, condition-aware range estimate you can actually trust.

Intermediate 12–18 hours 27 min read EVAnalyticsApp
Jump to source Bill of materials
EV Range & Trip Logger β€” reference build illustration MCU VCC Β· GND Β· SIG Β· NC
Difficulty
Intermediate
Build time
12–18 hours
Indicative cost
β‚Ή2,500 – β‚Ή4,000
Platform
ESP32 DevKit V1 (ESP-WROOM-32)
Category
Automotive
Last updated
28 July 2026
Contents β€” 26 sections

Project Overview

Logs an electric vehicle's real energy use trip by trip and turns it into an honest, condition-aware range estimate you can actually trust.

Range anxiety is the defining worry of electric driving, and it is made worse by range numbers you cannot trust: the dashboard's guess-o-meter swings wildly, the official range assumes gentle conditions, and neither tells you what your car will really do on this route, in this weather, driven the way you drive. This project builds a logger that measures an EV's real energy consumption trip by trip and uses that history to produce an honest, condition-aware range estimate β€” replacing an optimistic single number with evidence from how the car is actually used.

The system reads the vehicle's energy and driving data (via OBD-II/CAN where available, plus GPS for distance and speed) and logs each trip: energy used (kWh), distance, average speed, and the conditions that dominate EV consumption β€” chiefly temperature (cold weather and cabin heating slash range), speed (aerodynamic drag rises sharply with speed), and terrain (climbs cost energy, regen returns some). From this it computes real-world efficiency (Wh/km or km/kWh) and, crucially, how that efficiency varies with conditions.

The payoff is a range estimate grounded in reality: given the current battery state of charge and the conditions and driving style at hand, how far can this car actually go β€” with the honesty to say the estimate is lower in the cold, at motorway speed, or up a long climb. It is honest that OBD/CAN access and signals vary by EV (some expose rich data, some little), that this reads and logs rather than interfering with the vehicle, and that it complements β€” not replaces β€” the car's own systems. But as a real-consumption trip logger that turns measured history into a trustworthy, condition-aware range estimate, it attacks range anxiety at its root: not with a more optimistic number, but with an honest one.

An electric car connected to a charging station
An EV range logger turns measured, condition-aware consumption into a range number you can actually trust. Photograph sourced from Wikimedia Commons β€” Electric car charging.jpg. Reused under the licence stated on that page; please check it before republishing.

What this project does

  • Logs each trip: energy used, distance, speed, conditions
  • Computes real-world efficiency (Wh/km, km/kWh)
  • Learns how consumption varies with temperature, speed and terrain
  • Produces an honest, condition-aware range estimate from state of charge
  • Records regen and climb/descent effects on energy
  • Replaces an optimistic single number with measured evidence
  • Reads/logs without interfering with the vehicle

Real-World Applications

SettingHow it is used
Range confidence / planningTrustworthy range for a specific route, weather and driving style.
Efficiency coachingSeeing how speed and heating affect real consumption.
Fleet EV managementReal efficiency and range across an EV fleet for scheduling and charging.
Battery/health insightLong-term efficiency trends that hint at battery condition.

Deployment contexts where a build of this kind earns its keep.

Features & Capabilities

  • Trip-by-trip energy/distance/condition logging
  • Real-world efficiency computation
  • Condition-aware (temperature/speed/terrain) range model
  • Honest range estimate vs current state of charge
  • Regen and elevation awareness
  • History and trends for driving/planning
  • Honest about OBD/CAN variability and read-only scope

Difficulty, Time & Required Skills

AttributeValue
Difficulty levelIntermediate
Estimated completion time12–18 hours
Indicative build costβ‚Ή2,500 – β‚Ή4,000
Primary disciplineAutomotive
Reference platformESP32 DevKit V1 (ESP-WROOM-32)

Skills you should have (or will pick up)

  • OBD-II/CAN reading of EV energy/SoC signals (where available)
  • Energy/efficiency computation (kWh, Wh/km)
  • Condition modelling (temperature/speed/terrain vs consumption)
  • Range estimation from state of charge and conditions
  • Trip logging and trend analysis

Bill of Materials

Every part below is commonly available from Indian and international hobby-electronics suppliers. Prices are indicative 2026 retail figures in Indian rupees and will drift β€” treat them as a budgeting guide, not a quotation.

ComponentKey specificationQtyApprox. cost
ESP32 DevKit V1 (ESP-WROOM-32)
Wi-Fi transmit bursts peak near 500 mA β€” size the regulator accordingly.
Dual-core Xtensa LX6 @ 240 MHz, 520 KB SRAM, 4 MB flash, Wi-Fi 802.11 b/g/n + BLE 4.2, 34 GPIO, 18Γ— 12-bit ADC, 2Γ— 8-bit DAC1β‚Ή450
u-blox NEO-6M GPS module + ceramic patch antenna
Needs clear sky view; indoors it will never get a first fix.
50 channels, βˆ’161 dBm tracking, 2.5 m CEP, 1–5 Hz update, cold start 27 s1β‚Ή550
microSD card 32 GB A1 class
For 24/7 loggers buy a high-endurance card β€” normal cards die in months.
A1 rated, 10 MB/s random write, UHS-I, endurance-grade recommended1β‚Ή450
0.96β€³ SSD1306 OLED display
Static images burn in β€” invert or scroll the screen periodically.
128 Γ— 64 monochrome, 1.3–3.3 V logic, 100 kHz–400 kHz IΒ²C1β‚Ή250
DS18B20 waterproof temperature probe
Dozens can share one GPIO β€” you address them by ROM code.
βˆ’55 to +125 Β°C, Β±0.5 Β°C from βˆ’10 to +85 Β°C, 9–12-bit resolution, unique 64-bit ROM ID1β‚Ή160
OBD-II / CAN interface
Signals vary by EV; read-only
ELM327 or CAN transceiver for EV energy/SoC signals1β‚Ή600
GPS moduleDistance, speed and elevation backstop1β‚Ή400
Temperature sensorAmbient temperature (dominant range factor)1β‚Ή100
Vehicle power tap12 V accessory β†’ 5 V, ignition-switched1β‚Ή200

Estimated total: β‚Ή3,160, excluding tools, shipping and consumables.

Tools and consumables

  • Soldering iron (temperature controlled, 350 Β°C) with 0.8 mm 60/40 or lead-free solder
  • Digital multimeter β€” continuity, DC volts and current ranges
  • Wire strippers, flush cutters and a small set of precision screwdrivers
  • Heat-shrink tubing and a heat gun (or a lighter, carefully)
  • A laptop with a USB port and the toolchain listed above

Hardware Specifications

PartSpecificationSupplyInterfaceReference
ESP32 DevKit V1 (ESP-WROOM-32)Dual-core Xtensa LX6 @ 240 MHz, 520 KB SRAM, 4 MB flash, Wi-Fi 802.11 b/g/n + BLE 4.2, 34 GPIO, 18Γ— 12-bit ADC, 2Γ— 8-bit DAC3.3 V logic / 5 V USBUART, SPI, IΒ²C, IΒ²S, CAN, PWMDatasheet
u-blox NEO-6M GPS module + ceramic patch antenna50 channels, βˆ’161 dBm tracking, 2.5 m CEP, 1–5 Hz update, cold start 27 s3.3–5 VUART NMEA 9600Datasheet
microSD card 32 GB A1 classA1 rated, 10 MB/s random write, UHS-I, endurance-grade recommended3.3 VSDIO / SPIDatasheet
0.96β€³ SSD1306 OLED display128 Γ— 64 monochrome, 1.3–3.3 V logic, 100 kHz–400 kHz IΒ²C3.3–5 VIΒ²C (0x3C)Datasheet
DS18B20 waterproof temperature probeβˆ’55 to +125 Β°C, Β±0.5 Β°C from βˆ’10 to +85 Β°C, 9–12-bit resolution, unique 64-bit ROM ID3.0–5.5 V1-Wire (multi-drop)Datasheet

Consolidated electrical and interface specifications for every active part in the build.

Power Budget & Supply Sizing

Add up the typical active current of every part, then size the supply with at least 50 % headroom so transmit bursts and motor inrush never brown out the controller.

LoadSupply railTypical current (mA)Notes
ESP32 DevKit V1 (ESP-WROOM-32)3.3 V logic / 5 V USB160Wi-Fi transmit bursts peak near 500 mA β€” size the regulator accordingly.
u-blox NEO-6M GPS module + ceramic patch antenna3.3–5 V45Needs clear sky view; indoors it will never get a first fix.
microSD card 32 GB A1 class3.3 V100For 24/7 loggers buy a high-endurance card β€” normal cards die in months.
0.96β€³ SSD1306 OLED display3.3–5 V20Static images burn in β€” invert or scroll the screen periodically.
DS18B20 waterproof temperature probe3.0–5.5 V1.5Dozens can share one GPIO β€” you address them by ROM code.

Summed typical draw is 326.5 mA. With a 1.5Γ— design margin the supply should deliver at least 500 mA continuously at the stated rail voltage.

Software Requirements & Development Environment

Reference toolchain: Arduino IDE 2.3.x with the ESP32 board package 3.x (or PlatformIO on VS Code). Anything newer normally works; anything older may lack the board definitions used here.

  • Install the Arduino IDE 2.3.x (or PlatformIO if you prefer a real editor and dependency locking).
  • Add https://espressif.github.io/arduino-esp32/package_esp32_index.json under File β†’ Preferences β†’ Additional Board Manager URLs, then install esp32 from the Boards Manager.
  • Set the correct port under Tools β†’ Port. On Linux add yourself to the dialout group: sudo usermod -aG dialout $USER and log out and back in.
  • Open the Serial Monitor at 115200 baud β€” every sketch here logs its state there.
  • Keep File β†’ Preferences β†’ Show verbose output during: compilation switched on while you are debugging build errors.

Required libraries

LibraryWhy it is neededInstall
WiFi (ESP32 core) bundledStation/AP connection management for the ESP32.Bundled with the ESP32 Arduino core
TinyGPSPlus 1.0.3Streaming NMEA parser producing latitude, longitude, speed and time.Library Manager β†’ "TinyGPSPlus" by Mikal Hart
OneWire + DallasTemperature 2.3.x / 3.9.xBus enumeration and conversion commands for DS18B20 probes.Library Manager β†’ "DallasTemperature" (pulls OneWire)
Adafruit SSD1306 + GFX 2.5.xFramebuffer and text/graphics primitives for the OLED.Library Manager β†’ "Adafruit SSD1306"
SQLite 3.45+Zero-configuration embedded database for local logs.Bundled with Python (`import sqlite3`)
ArduinoJson 7.xZero-allocation JSON serialisation and parsing.Library Manager β†’ "ArduinoJson" by Benoit Blanchon

Block Diagram

The block diagram shows the functional decomposition of the system β€” what senses, what decides, what acts, and where the data ends up.

EV Range & Trip Logger β€” system block diagramFunctional block diagram of the EV Range & Trip Logger system. MeasureOBD/CANenergy/SoCGPSdist/speed/elevTempambientLog tripkWh, kmper tripConditionstemp/speed/terrainModelEfficiencyWh/kmvs conditionslearnedEstimateHonest rangefrom SoCCondition-awarecold/fast/climbrightrightnone
EV Range & Trip Logger β€” system block diagram

Circuit Diagram & Wiring

Every signal line in the build is shown below, followed by a pin-by-pin connection table you can work through with a multimeter in hand.

EV Range & Trip Logger β€” wiring schematicConnection schematic showing which controller pin drives each peripheral. Sensors / InputsControllerActuators / OutputsESP32 DevKit V1(ESP-WROOM-32)3.3 V logic / 5 V USBOBD/CANGPIO 16/17Energy, SoC, speedGPSGPIO 26/25Distance/speed/elevTemp sensorGPIO 4Ambient tempSD cardGPIO 18/23/5Trip logOLEDGPIO 21/22Range/efficiencyVehicle 5V5VIgnition power
EV Range & Trip Logger β€” wiring schematic
PeripheralPeripheral pinController pinSignal
OBD/CANUART/CANGPIO 16/17Energy, SoC, speed
GPSTX/RXGPIO 26/25Distance/speed/elev
Temp sensorDATAGPIO 4Ambient temp
SD cardSPIGPIO 18/23/5Trip log
OLEDSDA/SCLGPIO 21/22Range/efficiency
Vehicle 5VVIN5VIgnition power

Wire one row at a time and tick it off β€” most "it does not work" reports trace back to a single swapped pair.

Wiring explanation

  • Interface OBD-II/CAN read-only for energy, state of charge and speed where the EV exposes them.
  • Add GPS for distance/speed/elevation as a backstop and for terrain awareness.
  • Add an ambient temperature sensor β€” temperature is the dominant range factor.
  • Log trips to SD and show live range/efficiency on the OLED.
  • Read and log only; never write to or interfere with the vehicle.
A GPS navigation satellite in orbit
GPS and temperature capture the conditions β€” speed, cold, terrain β€” that dominate real EV consumption. Photograph sourced from Wikimedia Commons β€” GPS satellite.jpg. Reused under the licence stated on that page; please check it before republishing.

System Architecture

Read the stack from the bottom up: physical hardware, the firmware that drives it, the transport that moves data off the device, and the software a human actually looks at.

EV Range & Trip Logger β€” architecture stackLayered architecture from hardware to user interface. Hardware layerESP32 DevKit V1 (ESP-WROOM-32) Β· u-blox NEO-6M GPS module + ceramic patchantenna Β· DS18B20 waterproof temperature probeDriver layerwifi Β· tinygps Β· onewire Β· ssd1306Application logicsampling loop Β· filtering Β· thresholds Β· state machineTransport layerIn-vehicle logging; optional Wi-Fi/cellular sync Β· TLS Β· retry and backoffPresentation layerdashboard Β· mobile notifications Β· historical charts
EV Range & Trip Logger β€” architecture stack

Working Principle

Range anxiety is really trust anxiety. An EV usually has plenty of range for the journey; what it lacks is a number the driver believes. The dashboard estimate is jumpy and optimistic, the rated range assumes benign conditions, and neither reflects this car, route, weather and driver. The cure is not a cleverer guess from the same thin inputs β€” it is grounding the estimate in measured real consumption, so the number comes from evidence rather than optimism.

That starts with honest per-trip measurement. For each trip the logger records the energy actually used (kWh drawn from the battery), the distance covered, and the conditions in force. Dividing energy by distance gives real-world efficiency β€” Wh/km or km/kWh β€” which is the true, personal consumption of this vehicle as driven, not a laboratory figure. Accumulating this across many trips builds a picture far more trustworthy than any single dashboard reading.

The insight that makes the estimate useful is that EV consumption is dominated by a few conditions, and the model must be aware of them. Temperature is the biggest: cold weather raises battery internal resistance and, more importantly, cabin heating draws heavily from the same battery, so winter range can fall dramatically. Speed is next: aerodynamic drag rises with the square of speed, so motorway cruising consumes far more per kilometre than town driving. Terrain matters too: climbs cost energy while descents return some through regenerative braking. A logger that records efficiency alongside these conditions can learn how consumption varies with them β€” cold vs warm, fast vs slow, hilly vs flat β€” instead of pretending consumption is constant.

From that model comes the deliverable: an honest, condition-aware range estimate. Given the current state of charge and the conditions and driving at hand, the logger estimates how far the car can really go β€” and, crucially, has the honesty to say the number is lower in the cold, at speed, or up a climb, rather than flattering the driver. That honesty is the point: a range estimate you can trust because it is built from what your car actually does. The design is candid about its constraints β€” OBD/CAN signal availability varies widely between EVs (some expose rich battery data, others almost none), GPS provides a backstop for distance/speed/elevation, everything is read-only and non-interfering, and it complements rather than replaces the vehicle's own systems. Within that frame it does the one thing that actually reduces range anxiety: turn measured history into a range number worth believing.

The maths behind it

Real-world efficiency

plainReal-world efficiency
Per trip:

  efficiency (Wh/km) = energy_used_Wh / distance_km
  range-equiv (km/kWh) = distance_km / energy_used_kWh

This is the car's TRUE consumption as driven β€” not a rated
lab figure.

Condition-aware consumption

plainCondition-aware consumption
Consumption depends on conditions:

  E(v,T,grade) β‰ˆ E_base
    Γ— f_speed(v)     (drag ~ vΒ²  β†’ higher at speed)
    Γ— f_temp(T)      (cold + heating β†’ much higher)
    Γ— f_grade(grade) (climb costs; regen returns some)

Learned from logged trips, not assumed constant.

Honest range estimate

plainHonest range estimate
range_km = (SoC Γ— usable_kWh) / efficiency(conditions)

Use the efficiency for the CURRENT conditions, so the estimate
falls honestly in cold / at speed / uphill instead of
flattering the driver.

Program Flowchart

The firmware is a single cooperative loop. Nothing blocks for long, so networking, sensing and the user interface all stay responsive.

EV Range & Trip Logger β€” firmware flowchartControl flow through the main program loop. Read energy/SoC, GPS,temperatureTrip in progress?Accumulate energy/distance/conditionsClose + log tripAccumulateenergy/distance/conditionsClose + log tripUpdate efficiency model (bycondition)Estimate range from SoC +conditionsShow honest, condition-awarerange
EV Range & Trip Logger β€” firmware flowchart

Assembly Instructions

Build on a breadboard first and only commit to solder once the whole system has run for an hour without a fault.

  1. Interface energy and conditions (read-only)

    Read EV energy/SoC/speed via OBD-II/CAN where available; add GPS for distance/speed/elevation and an ambient temperature sensor.

    Handle EVs that expose little data by leaning on GPS/temperature.

  2. Log trips and compute efficiency

    Accumulate energy, distance and conditions per trip; compute real-world efficiency and store the trip.

  3. Build the condition-aware estimate

    Learn how efficiency varies with temperature, speed and terrain, and estimate range from the current state of charge and conditions β€” honestly.

Step-by-Step Implementation Guide

Work through these in order. Each step ends in something you can observe, so a failure is always localised to the step you just finished.

  1. Accumulate a trip and its conditions

    While driving, accumulate energy used and distance, and record the dominant conditions (temperature, speed, elevation change).

    pythontrip.py
    class Trip:
        def __init__(self):
            self.kwh = 0.0; self.km = 0.0
            self.temp_sum = 0.0; self.samples = 0
            self.climb_m = 0.0; self.v_sum = 0.0
    
        def sample(self, power_kw, dt_h, dist_km, temp_c, dv_elev_m, speed):
            self.kwh += power_kw * dt_h          # energy actually drawn
            self.km  += dist_km                  # distance covered
            self.temp_sum += temp_c; self.samples += 1
            if dv_elev_m > 0: self.climb_m += dv_elev_m   # climbs cost energy
            self.v_sum += speed
    
        def summary(self):
            n = max(self.samples, 1)
            eff = (self.kwh * 1000.0) / self.km if self.km else 0   # Wh/km
            return {
                "kwh": round(self.kwh, 2), "km": round(self.km, 1),
                "wh_per_km": round(eff, 0),
                "avg_temp": round(self.temp_sum / n, 1),
                "avg_speed": round(self.v_sum / n, 1),
                "climb_m": round(self.climb_m, 0),
            }
    self.kwh += power_kw * dt_h # energy actually drawnEnergy is the real quantity used β€” integrated from battery power over time β€” the basis of trustworthy efficiency, not a rated figure.
    eff = (self.kwh * 1000.0) / self.km if self.km else 0 # Wh/kmReal-world efficiency is energy over distance β€” the car's true consumption as actually driven.
    self.temp_sum += temp_c; self.samples += 1Temperature is logged with the trip because it is the single biggest driver of EV consumption.
    if dv_elev_m > 0: self.climb_m += dv_elev_m # climbs cost energyClimbs are recorded because terrain materially changes energy use β€” the estimate must be terrain-aware.
  2. Estimate range honestly for the conditions

    From the current state of charge and the efficiency for the present conditions, estimate range β€” and let it fall honestly in the cold, at speed, or uphill.

Complete Source Code

The listing below is complete and compiles as written β€” there are no elided sections. Read the annotations under each block before you upload it.

pythonev_range_logger.py
#!/usr/bin/env python3
"""
EV Range & Trip Logger

Logs real per-trip energy/distance/conditions, computes real-world
efficiency, learns how consumption varies with temperature/speed/terrain,
and produces an HONEST, condition-aware range estimate from state of charge.
Read-only: reads and logs, never interferes with the vehicle.
"""
import statistics, json

USABLE_KWH = 40.0                        # battery usable capacity

class EfficiencyModel:
    """Learns Wh/km bucketed by the dominant conditions."""
    def __init__(self):
        self.buckets = {}                # (temp_band, speed_band) -> [wh/km,...]

    @staticmethod
    def bands(temp_c, speed):
        tb = "cold" if temp_c < 10 else "mild" if temp_c < 25 else "warm"
        sb = "town" if speed < 45 else "mixed" if speed < 80 else "motorway"
        return (tb, sb)

    def learn(self, trip):
        key = self.bands(trip["avg_temp"], trip["avg_speed"])
        self.buckets.setdefault(key, []).append(trip["wh_per_km"])

    def wh_per_km(self, temp_c, speed):
        key = self.bands(temp_c, speed)
        hist = self.buckets.get(key)
        if hist:
            return statistics.median(hist)          # this car, these conditions
        # fall back to any history, then a sane default
        allv = [v for h in self.buckets.values() for v in h]
        return statistics.median(allv) if allv else 160.0

def honest_range_km(model, soc_frac, temp_c, speed):
    wh_km = model.wh_per_km(temp_c, speed)          # condition-aware
    available_wh = soc_frac * USABLE_KWH * 1000.0
    est = available_wh / wh_km
    return round(est), round(wh_km)                 # km, Wh/km used

def log_trip(model, trip, path="trips.jsonl"):
    model.learn(trip)                               # update the model
    with open(path, "a") as f:
        f.write(json.dumps(trip) + "\n")           # keep the evidence

if __name__ == "__main__":
    model = EfficiencyModel()
    # ... each completed trip:
    #   trip = Trip(...).summary(); log_trip(model, trip)
    # live estimate for current conditions:
    km, wh = honest_range_km(model, soc_frac=0.62, temp_c=4, speed=95)
    print(f"Honest range ~{km} km at {wh} Wh/km "
          f"(cold + motorway β†’ lower, and we say so)")
self.buckets = {} # (temp_band, speed_band) -> [wh/km,...]Efficiency is stored bucketed by conditions, so the model knows this car's consumption cold-and-fast vs warm-and-slow β€” the key to a useful estimate.
return statistics.median(hist) # this car, these conditionsThe estimate uses the measured efficiency for the current conditions from this car's own history β€” evidence, not optimism.
available_wh = soc_frac * USABLE_KWH * 1000.0Range is energy available (state of charge Γ— usable capacity) divided by real consumption for the conditions.
f.write(json.dumps(trip) + "\n") # keep the evidenceEvery trip is logged, so the model keeps improving and the range number stays grounded in real history.
f"(cold + motorway β†’ lower, and we say so)")The estimate is honest: it falls in the cold and at speed rather than flattering the driver β€” the honesty that builds trust and cures range anxiety.

Configuration & Calibration

Configuration steps

  • Configure the OBD/CAN signals available on your EV and the usable battery capacity.
  • Configure GPS and temperature inputs; set condition bands (temp/speed) for the model.
  • Configure trip start/stop detection and logging.
  • Configure conservative rounding for the range estimate.

Calibration procedure

An uncalibrated sensor produces confident, precise, wrong numbers. Do this once per physical unit and record the constants.

  1. Energy accuracy

    Cross-check logged trip energy against the car's own trip energy or a full charge's known kWh.

  2. Condition bands

    Verify efficiency really does separate across temperature and speed bands after a few varied trips.

  3. Range honesty

    Compare estimated vs actual range achieved; tune to be honest/slightly conservative, not optimistic.

Network Architecture & Connectivity

EV Range & Trip Logger β€” network topologyPath taken by telemetry from field node to end user. Edge nodesGatewayCloudClientsEV loggerESP32Other EVsfleetWi-Fi/cellularPhone/routerwhen parkedHTTPSTrip/efficiency storehistory + modelDriverrange/efficiencyFleetEV efficiency
EV Range & Trip Logger β€” network topology

Communication protocol

Trips are logged locally and can sync to a store for history/trends; the condition-aware model improves as trips accumulate.

Topic / endpointDirectionPayload
ev/<veh>/triplogger β†’ storeenergy, distance, efficiency, conditions
ev/<veh>/rangelogger β†’ appcondition-aware range estimate

Message contract between the device and the broker.

Cloud platform configuration

A store of real trips per vehicle powers honest range, efficiency trends and (for fleets) charging/scheduling insight.

Dashboard setup

Trip history, real efficiency vs conditions, and a range estimate you can trust.

Mobile app integration

Live condition-aware range and pre-trip range for a planned route/weather.

Security considerations

  • Read-only vehicle access; never interfere.
  • Trip/location data is personal β€” secure and minimise it.
  • Local logging works offline; sync is optional.

Testing Procedure & Expected Output

Test from the bottom up. Confirm power, then each sensor in isolation, then the integrated loop β€” the first failing step tells you exactly where to look.

TestWhat you should see
Log a warm town tripLow Wh/km recorded in warm/town bucket
Log a cold motorway tripMuch higher Wh/km in cold/motorway bucket
Estimate range in the cold at speedHonestly lower range than warm/town
Drive a climb then descentClimb energy up; regen recovers some on descent
EV exposes little OBD dataFalls back to GPS/temperature gracefully
Compare estimate to realityEstimate honest / slightly conservative

Bench-test checklist. If a row fails, stop and fix it before moving on.

Expected output

A live, condition-aware range estimate plus a trip log of real energy, distance, efficiency and conditions.

jsontrip-summary.json
{
  "km": 42.6,
  "kwh": 9.1,
  "wh_per_km": 214,
  "avg_temp": 4,
  "avg_speed": 95,
  "climb_m": 180,
  "range_estimate_km": 116,
  "note": "cold + motorway -> honest lower range"
}

A cold motorway trip logged at 214 Wh/km β€” far above a gentle-day figure β€” and the range estimate honestly reflects it rather than promising the rated number.

An 18650 lithium-ion cell
Real per-trip energy history replaces an optimistic guess-o-meter with honest evidence. Photograph sourced from Wikimedia Commons β€” 18650 Li-ion battery.jpg. Reused under the licence stated on that page; please check it before republishing.

Troubleshooting: Common Errors & Fixes

Range estimate too optimistic

Likely cause. Ignoring conditions

Fix. Use condition-aware efficiency; prefer conservative rounding

Little/no OBD data

Likely cause. EV exposes few signals

Fix. Fall back to GPS distance/speed and temperature; use battery kWh from charging

Efficiency looks wrong

Likely cause. Energy vs distance mismatch

Fix. Cross-check energy against a known charge; verify distance from GPS/odometer

No condition sensitivity

Likely cause. Not bucketing by condition

Fix. Bucket efficiency by temperature/speed; log enough varied trips

Interfering with the car

Likely cause. Writing to the bus

Fix. Read-only; never write to or interfere with the vehicle

The sketch will not upload β€” "Failed to connect" or "avrdude: stk500_recv()"

Likely cause. The bootloader is not being reached: wrong port, wrong board, a serial monitor holding the port open, or a USB cable that only carries power.

Fix. Close every serial monitor, confirm Tools β†’ Board and Port, and swap to a known data-capable USB cable. On an ESP32 hold BOOT while the IDE prints "Connecting…", then release. If a peripheral is wired to the UART pins (GPIO 1/3 on ESP32, D0/D1 on Uno) unplug it β€” it fights the programmer.

The board resets in a loop, or the serial monitor prints "Brownout detector was triggered"

Likely cause. The supply cannot deliver peak current. Wi-Fi transmit bursts, relay coils and servos all pull far more than their average draw.

Fix. Power peripherals from a separate regulated supply with a common ground rather than from the board 5 V pin. Add a 470–1000 Β΅F electrolytic capacitor across the supply near the load, and use a real power adapter rather than a laptop USB port.

Serial monitor shows garbage characters

Likely cause. Baud rate mismatch between Serial.begin() and the monitor, or a floating/shared UART line.

Fix. Set the monitor to 115200 to match the sketch. If it still garbles, the crystal or the USB bridge is being confused by noise β€” shorten the cable and keep motor wiring away from the USB lead.

An IΒ²C device is not detected

Likely cause. Wrong address, missing pull-ups, swapped SDA/SCL, or a bus too long for the pull-up value.

Fix. Run an IΒ²C scanner sketch first β€” it should print the device address. Most breakout boards include 4.7 kΞ© pull-ups, but if you have chained four of them the parallel resistance is too low; remove the pull-ups from all but one board. Keep the bus under 30 cm at 100 kHz.

Wi-Fi connects but MQTT never does (state -2)

Likely cause. Wrong broker address or port, a firewall in the way, or the broker requiring credentials the sketch is not sending.

Fix. Test from a laptop on the same network first: mosquitto_sub -h <broker> -t "#" -v. If that works, the problem is on the device β€” check the IP literal, port 1883 (or 8883 for TLS), and that client.setServer() runs before connect(). PubSubClient state codes are documented in its header.

Readings arrive for a while and then stop

Likely cause. The Wi-Fi or MQTT session dropped and the sketch never reconnects, or the broker dropped the client on keep-alive timeout.

Fix. Never assume the link stays up. Check WiFi.status() and client.connected() at the top of every loop and reconnect with exponential backoff. Add a watchdog so a wedged network stack reboots the device instead of going silent.

Performance Optimisation

  • Log real energy/distance per trip; compute true efficiency.
  • Bucket efficiency by condition for an accurate estimate.
  • Estimate range from state of charge honestly, not optimistically.
  • Degrade gracefully when OBD signals are sparse.
  • Replace every delay() with a millis() comparison β€” blocking delays are the single most common cause of dropped readings.
  • Sample sensors on a fixed cadence and publish on a slower one; you almost never need to transmit at the sampling rate.
  • Move networking into its own FreeRTOS task so a slow DNS lookup cannot stall the control loop.
  • Use uint8_t / uint16_t where the range allows; on an 8-bit AVR a 32-bit add costs four times as much.
  • Batch several samples into one MQTT publish. Radio time, not CPU time, dominates the energy budget.
  • Set the MQTT keep-alive to a value that matches your reporting interval so the broker does not churn reconnections.
  • For battery builds use deep sleep between samples: an ESP32 drops from ~160 mA awake to about 10 Β΅A asleep, which is the difference between days and months of runtime.

Safety Precautions

  • Read and log only β€” never write to or interfere with the vehicle.
  • Do not let the display distract driving; keep it glanceable.
  • Trip and location data are personal β€” secure and minimise them.
  • This complements the vehicle's own systems; do not rely on it as a sole safety-critical gauge.
  • Wear eye protection when soldering or cutting, and solder in a ventilated space β€” rosin flux fumes are a respiratory irritant.
  • Power the circuit through a bench supply with a current limit while you are testing. A 300 mA limit turns a wiring mistake into a beep instead of a dead board.
  • Disconnect power before changing any wiring. Hot-plugging a sensor onto a live bus is the fastest way to lose a controller.

Maintenance

  • Keep logging trips so the condition model stays current.
  • Re-check energy accuracy against known charges periodically.
  • Update usable capacity as the battery ages (efficiency trends hint at health).
  • Verify OBD/CAN signals after vehicle software updates.
  • Re-check every screw terminal and header after the first week β€” thermal cycling loosens connections that felt tight on day one.
  • Rotate the microSD card annually and keep an image of the working system. Cards used as loggers wear out silently.
  • Keep the broker and dashboard containers patched, and rotate device credentials at least once a year.
  • Recalibrate at the interval given in the calibration section, and keep the constants in a text file next to the firmware β€” not only in flash.
  • Keep a short logbook of firmware versions and what changed. Six months later you will not remember why that constant is 1.083.

Future Improvements & Upgrades

A working v1 is a platform, not a finish line. These are the upgrades that add the most capability for the least rework.

  • Add route-based range (elevation/weather along a planned route).
  • Add battery-health estimation from long-term efficiency/capacity trends.
  • Add charging-cost and eco-driving coaching.
  • Add fleet scheduling from real per-vehicle efficiency.
  • Design a proper PCB. Once the breadboard version has run for a month, moving to a two-layer board removes the intermittent-contact failures that dominate prototype faults.
  • Add over-the-air firmware updates so you never have to physically reach a deployed node again.
  • Add persistent local storage (microSD or the on-chip flash) so a network outage does not create a hole in your data.
  • Move configuration out of the source: a captive-portal setup page or a JSON config file makes the build reusable without a recompile.
  • Add a battery and solar option so the unit survives a power cut and can be sited away from a socket.
  • Write a small test harness that feeds synthetic sensor values through the decision logic, so you can validate thresholds without physically triggering the event.

Frequently Asked Questions

Why is the car's own range estimate not enough?

It is jumpy and tends to be optimistic, and it does not reflect this car, route, weather and driving. Grounding the estimate in your own measured trips makes it trustworthy.

What makes EV range vary so much?

Mainly temperature (cold weather and cabin heating cut range sharply), speed (drag rises with the square of speed), and terrain (climbs cost energy, regen returns some). A useful estimate must be aware of these.

Where does the energy figure come from?

From OBD-II/CAN where the EV exposes battery power/state of charge, with GPS for distance/speed/elevation as a backstop. Energy over distance gives real efficiency.

Why prefer a conservative estimate?

Because trust is the goal. A range number that under-promises and over-delivers cures range anxiety; an optimistic one that strands you destroys confidence.

Does it work on any EV?

Partly β€” OBD/CAN signals vary widely. Where the car exposes rich data it is precise; where it exposes little, the logger leans on GPS and temperature and is honest about the limits. It is read-only and never interferes with the vehicle.

References & Learning Resources

These are the primary sources worth reading in full. Manufacturer datasheets always outrank forum posts when the two disagree.

  1. Electric vehicle rangeReference
  2. Range anxietyReference
  3. Regenerative brakingReference
  4. Aerodynamic drag and speedReference
  5. OBD-II / EV dataReference