Skip to content

Latest commit

Β 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

CGO600 Smart Bridge

Standalone ESP32 firmware that reads live telemetry from a Tenways CGO600 e-bike over Bluetooth Low Energy and forwards it to Home Assistant via MQTT. Includes a built-in, multilingual web dashboard (installable as a PWA) for live monitoring, light control, and a local ride history β€” no computer needed after initial setup.

"TENWAYS" and "CGO600" are trademarks of TENWAYS. This is an unofficial community project, not affiliated with or endorsed by TENWAYS β€” use at your own risk.

Motivation

I own a Tenways CGO600 and run a smart home built around Home Assistant. I like to integrate as many devices as possible into Home Assistant, including devices that do not expose a ready-made integration.

For this project I reverse engineered the bike's Bluetooth protocol and built ESP32 firmware that acts as a small BLE-to-MQTT gateway. In my setup the ESP32 is mounted in the shed where the bike is parked. When the bike connects on departure or arrival, the gateway reads the latest data and publishes it to Home Assistant via MQTT.

Screenshots

Dashboard Rides Configuration
Dashboard Rides Config

Home Assistant entities

Sensor Unit MQTT topic
Battery % cgo600/sensor/battery/state
Speed km/h cgo600/sensor/speed/state
Max speed km/h cgo600/sensor/max_speed/state
Average speed km/h cgo600/sensor/avg_speed/state
Trip distance km cgo600/sensor/trip_distance/state
Trip duration s cgo600/sensor/trip_duration/state
Remaining range km cgo600/sensor/remaining_range/state
Total range km cgo600/sensor/range/state
Serial number β€” cgo600/sensor/serial_number/state
Last connected ISO 8601 UTC cgo600/sensor/last_connected/state
Light on/off cgo600/light/state

The light can also be controlled from Home Assistant by publishing on or off to cgo600/light/set.

All entities are registered in Home Assistant automatically via MQTT discovery. The topic prefix (cgo600) is configurable.

Ride history

The bridge only sees the bike when it reconnects (typically on departure or arrival), so it infers rides from the time and distance between two check-ins. Each inferred ride records its window, distance, duration, battery delta, and average/max speed. Rides are stored locally on the ESP32 and shown on the dashboard's Rides tab.


Requirements

  • ESP32 dev board β€” recommended: ESP32-WROOM-32U (external antenna connector for better BLE range). This is what the project runs on in practice; other ESP32 boards will work but WROOM-32U is the tested configuration.
  • USB cable for initial flashing

Installation

Pre-built firmware

Download the four .bin files from the latest release and flash with esptool:

esptool.py --chip esp32 --baud 460800 \
  write_flash \
  0x1000   cgo600-bootloader.bin \
  0x8000   cgo600-partitions.bin \
  0x10000  cgo600-firmware.bin \
  0x3D0000 cgo600-littlefs.bin

Build from source

Install PlatformIO, then from the repo root:

pio run -d firmware -t upload
pio run -d firmware -t uploadfs

Add --upload-port <port> if auto-detection fails:

pio run -d firmware -t upload --upload-port <port>

First setup

  1. After flashing, the ESP32 creates a WiFi access point called CGO600-Setup.
  2. Connect your phone or laptop to that network.
  3. A captive portal opens β€” select your WiFi and enter the password.
  4. The device connects to your network and is usually reachable at http://cgo600.local/ on networks that support mDNS. Otherwise, use the ESP32 IP address shown by your router.

Updating

After initial USB flashing, updates can be done over WiFi. Use cgo600.local when mDNS works, or the ESP32 IP address otherwise:

pio run -d firmware -t upload --upload-port cgo600.local

If you changed the web interface:

pio run -d firmware -t buildfs
pio run -d firmware -t uploadfs --upload-port cgo600.local

BLE protocol

The CGO600 exposes a Nordic UART Service (NUS) over BLE. Commands are written to the RX characteristic; telemetry replies arrive as notifications on the TX characteristic. The protocol details below come from reverse engineering and may differ across bike/display firmware versions.

Frame format

Read  0x52 [len] [field_ids...] [CRC16_lo] [CRC16_hi] 0x45
Write 0x57 [len] [payload...]   [CRC16_lo] [CRC16_hi] 0x45

CRC16 is computed over all bytes from the start byte up to (but not including) the CRC bytes, using the same lookup table as the Python reference implementation.

Field IDs

ID Field Decoding
0x07 Serial number ASCII string
0x11 Speed uint16 BE Γ· 10 β†’ km/h
0x12 Max speed uint16 BE Γ· 10 β†’ km/h
0x13 Average speed uint16 BE Γ· 10 β†’ km/h
0x14 Trip distance uint16 BE Γ· 10 β†’ km
0x15 Trip duration uint16 BE β†’ seconds
0x16 Remaining range uint16 BE Γ· 10 β†’ km
0x17 Total range uint16 BE Γ· 10 β†’ km
0x1A Battery uint8 β†’ %
0x21 Light uint8 (0 = off, 1 = on)

Repository layout

firmware/
  platformio.ini        Build configuration
  partitions.csv        Custom partition layout
  src/
    main.cpp            WiFi, mDNS, OTA, orchestration
    config.h/.cpp       Persistent configuration (NVS)
    protocol.h/.cpp     Frame building, parsing, CRC16
    bike_state.h/.cpp   Telemetry data model and field decoding
    ble_client.h/.cpp   BLE scan, connect, handshake, polling
    mqtt_ha.h/.cpp      MQTT publishing and Home Assistant discovery
    ride_log.h/.cpp     Ride inference and local history storage
    web_server.h/.cpp   HTTP API and web interface serving
    logger.h/.cpp       Logging helpers
    reset_reason.h      Boot/reset reason decoding
  data/
    index.html          Dashboard shell (single-page app)
    app.js              Dashboard logic and rendering
    i18n.js             UI translations
    style.css           Shared styling
    config.html         Redirect shim (/config.html β†’ /#settings)
    manifest.json       PWA manifest
    sw.js               Service worker (offline shell cache)

Partition layout

Partition Size Purpose
app0 1.9 MB Firmware (active)
app1 1.9 MB Firmware (OTA staging)
spiffs 192 KB Web interface (LittleFS)
nvs 20 KB Configuration storage

The partition label is spiffs for ESP32 compatibility, but PlatformIO builds a LittleFS image for it.

About

🚲 Standalone ESP32 bridge that streams Tenways CGO600 telemetry to Home Assistant over MQTT β€” built-in web dashboard and ride history

Topics

Resources

Contributing

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages