Table of Contents

Wireless Bike Computer

Introduction

This project aims to build a simple but versatile wireless bike computer based on two microcontroller modules.

  • It measures wheel cadence and computes basic cycling metrics such as speed, trip distance, trip average speed, trip time and total distance.
  • It provides a touchscreen LCD interface for viewing metrics and changing settings.
  • It can connect to an Android phone over Bluetooth in order to receive location data and display maps.
  • It is battery-powered and rechargeable through USB-C.

The idea behind this project came from the desire to build an open-source bike computer that is more affordable than commercial solutions such as Garmin devices, while still providing the main features needed in practice.

This project is useful both as an educational embedded systems project and as a proof of concept for a customizable and low-cost bike computer. For the author, it is also an opportunity to work on low-power firmware, wireless communication, UI rendering, persistent storage, and sensor integration in a single system.

General Description

The system is split into two main modules:

  • Wheel module - mounted on the bike fork; measures wheel revolutions using a Hall effect sensor and a magnet attached to a spoke.
  • Main module - mounted on the handlebars; receives the wheel data, computes user-visible metrics, manages the touchscreen UI, and communicates with the Android phone.

The two modules communicate wirelessly using ESP-NOW. The main module communicates with the phone using Bluetooth Low Energy (BLE).

System Overview

The project contains the following hardware and software components:

1. Wheel Module

The wheel module is designed to be low power and is responsible for measuring how long each wheel revolution takes.

Hardware components:

Software responsibilities:

2. Main Module

The main module is the central processing and UI unit.

Hardware components:

Software responsibilities:

3. Android Phone

The Android phone acts as an external data source for the maps feature.

Responsibilities:

Module Interaction

The high-level interaction flow is the following:

  1. The wheel module detects wheel revolutions using the Hall effect sensor.
  2. It buffers timing data locally.
  3. At predefined moments, the wheel module transmits the buffered data to the main module over ESP-NOW.
  4. The main module receives the packet, computes user-facing metrics and updates the LCD interface.
  5. When the user opens the maps tab, the main module connects to the Android phone over BLE, receives location data, loads the necessary map tiles from the SD card, and displays the current position.

Block Diagram

Hardware Design

Bill of Materials

1. Wheel Module

2. Main Module

Hardware Architecture

Wheel Module

The wheel module is mounted on the bike fork. A magnet attached to a wheel spoke passes near the Hall effect sensor once per wheel revolution. The sensor output is connected to the microcontroller, which measures the time between triggers.

Because the US5881 operates at a voltage higher than the 3.3V rail used by the ESP32-C6, a DC-DC boost converter is used to provide 5V to the sensor. This is not ideal from a power-consumption perspective, but it was chosen because the sensor is easy to source and integrates well in this proof-of-concept design.

The ESP32-C6 uses both its high-performance core and its ultra-low-power core in order to reduce overall energy consumption.

A 10uF bulk capacitor is used for stabilizing the 3.3V rail. A 100nF decoupling capacitor is used for the US5881 sensor, as per its datasheet.

Main Module

The main module is mounted on the handlebars. It contains the display, touch interface, SD card access, wireless communication, and persistent metric storage using ESP-IDF NVS.

The LCD, touch controller, and SD card reader all use the SPI bus. To save pins, they share the same SPI clock, MOSI, and MISO lines, while each device has its own chip-select line.

The display module is powered via the 3.3V rail, which is stabilized using a 10uF bulk capacitor. A 100nF decoupling capacitor is placed between the display module's VCC and GND pins.

Pinout

Wheel Module Pinout

ESP32-C6 Pin Function
GPIO0 Hall effect sensor OUT

Main Module Pinout

ESP32-S3 Pin Function
GPIO1 Capacitive touch wake pin
GPIO2 Touch CS
GPIO3 LCD backlight PWM
GPIO4 LCD D/C
GPIO5 LCD Reset
GPIO6 LCD CS
GPIO7 SPI CLK (shared)
GPIO8 SPI MISO (shared)
GPIO9 SPI MOSI (shared)
GPIO43 SD Card CS
GPIO44 Free

Pinout Design Notes

Electrical Schematic

Software Design

Development Environment

The firmware is developed using:

Third-Party Libraries / Components

Wheel Module Firmware

The wheel module uses both the HP core and the ULP core of the ESP32-C6.

Workflow

  1. The HP core boots and configures the wake-up sources for the ULP core, then goes to deep sleep.
  2. The ULP core can wake up either:
    1. periodically, from a timer
    2. from the Hall sensor GPIO trigger
  3. If the wake-up source is the Hall sensor:
    1. the ULP core measures the interval since the previous wheel revolution
    2. it stores the value in RTC memory
  4. If the wake-up source is the timer:
    1. the ULP core updates a timeout counter
    2. if several timer wake-ups occur without wheel movement, the wheel is assumed to have stopped
    3. in that state, periodic transmission can be reduced and the timer source can be disabled until motion is detected again
  5. After enough intervals have been buffered, or after a predefined timeout, the ULP core signals the HP core.
  6. The HP core wakes up, reads the shared data from RTC memory, builds a packet, and sends it to the main module using ESP-NOW.
  7. The HP core then returns to deep sleep.

Data Sharing

The ULP and HP cores communicate through shared variables placed in RTC memory. These variables contain:

Advantages of this approach

Main Module Firmware

The main module firmware is responsible for computation, rendering, storage, and communication.

Responsibilities

UI Design

The user interface is implemented with LVGL and designed in Squareline Studio. The UI contains the following main screens:

Data integrity

The ESP-NOW packets contain a sequence number and cumulative wheel-module counters. The main module uses the sequence number to discard duplicate or stale packets. The cumulative counters make the main module logic simpler: once a packet is accepted, the firmware can compute the delta in wheel rotations and moving time relative to the previous accepted packet.

This approach keeps the main module tolerant of occasional packet loss and avoids complex packet reordering logic. Distance and moving time are derived from accepted wheel-module data, while speed uses the most recent valid wheel periods.

Storage Strategy

Some values, such as total distance or trip statistics, need to survive resets and power cycles. These values are stored in NVS.

To avoid excessive write frequency and premature flash wear:

Maps Feature

The maps feature is designed as follows:

The tile system is based on the slippy map format.

To reduce CPU overhead:

The map viewport is implemented as an LVGL canvas placed inside the Squareline placeholder container. The canvas size is read at runtime from the UI object, so the viewport can be resized later in Squareline Studio without changing the rendering code. The current position marker is drawn in the center of the viewport, while the map tiles move underneath it.

The firmware uses a fixed zoom level. The SD card path is organized as:

The RGB565 files are generated offline by a Python CLI tool. The tool receives an input slippy-tile directory, the original image format, and an output directory, then preserves the tile hierarchy while converting each tile to raw RGB565.

To avoid redundant rendering work, the map is redrawn only when the projected location changes by at least a small pixel threshold. The canvas buffer is allocated from PSRAM because the full RGB565 viewport requires a relatively large contiguous buffer.

Wireless Coexistence

The XIAO ESP32-S3 has a single 2.4 GHz radio, so BLE and ESP-NOW must share the same radio hardware. The firmware relies on radio coexistence support in order to allow both features to operate in the same system.

Power Management

After a predefined period of inactivity the main module enters deep sleep, which in turn powers off most of the components, including the GPIO (which power the display).

The timeout is reset by wheel packets, BLE location activity, and user interaction. The device can be woken up through the dedicated touch input.

Android Application

The Android application is intentionally simple. Its role is not to display the ride metrics, but to act as a bridge between the phone location provider and the ESP32-S3.

The app:

The Android UI exposes a single state-dependent action button:

The app also shows the current connection state, the last phone coordinates, and a short log useful during development and testing.

Algorithms and Data Structures

The main algorithms and data structures used by the project are:

Source Structure / Implementation Notes

The repository is split by module and by responsibility:

The main module firmware is divided into smaller layers:

The firmware follows an event-driven structure. ESP-NOW receive callbacks, BLE callbacks, timers, and UI callbacks post events to the main application event loop. The application task then updates ride metrics, persistence state, UI state, and map rendering from one place. This avoids doing heavy work directly inside interrupt-like or stack callback contexts.

For the main module, the UI presenter converts internal data to user-visible labels. Ride metrics remain unit-system agnostic internally and are stored using metric units such as millimetres, microseconds, and kilometres per hour. Unit conversion and formatting are handled when values are presented in the UI.

The map renderer avoids runtime PNG/JPG decoding on the ESP32-S3. Raster tiles are prepared on the computer, converted to raw RGB565, copied to the SD card, and streamed by the firmware. Missing tiles are rendered as a neutral background color so the screen remains usable even if the SD card does not contain every tile around the current position.

The Android application uses a foreground service because Android may stop ordinary background work when the phone screen is off. The service owns scanning, GATT connection state, location updates, write retry logic, and the persistent notification, while the activity only displays state and exposes the action button.

Results and Conclusions

The final system implements the main goals of the project:

The project also demonstrates several important embedded-system trade-offs:

The result is a functional proof-of-concept wireless bike computer. It is not yet a polished commercial device, but it validates the complete data path: wheel sensing, low-power buffering, wireless transfer, metric computation, persistent state, phone-assisted location, and local map display.

Future improvements could include:

Images

Main Module

Assembled

assembled.jpg

Board

front.jpg back.jpg

Case

front.jpg back.jpg

UI

Metrics Screen

metrics_screen.jpg

Maps Screen

maps_screen.jpg

Settings Screen

settings_screen.jpg

Wheel Module

Assembled

assembled.jpg

Board

front.jpg back.jpg

Case

front.jpg back.jpg

Download

Github Link

Bibliography / Resources

Below is a list of the main resources used for the project.

Hardware Resources

Software Resources

Export to PDF