Imported from GitHub: trailcurrentoss/TrailCurrentBearing · commit 9119abe · license MIT
Description
GNSS positioning module providing location and timing data over CAN bus
README
TrailCurrent Bearing

GNSS (Global Navigation Satellite System) module for precise positioning, timing, and navigation. Part of the TrailCurrent open-source vehicle platform.
Project Overview
TrailCurrent Bearing is a standalone GNSS module that reads data from a DFRobot Gravity GNSS receiver via I2C and distributes it over the CAN bus for other modules to consume.
Example Use Cases:
- Digital clocks with precise time synchronization
- Navigation and geolocation devices
- Geospatial macros that control devices based on pinned locations
- Vehicle tracking and positioning systems
Hardware Overview
- Microcontroller: ESP32-S3 (Waveshare ESP32-S3-RS485-CAN board)
- GNSS Receiver: DFRobot Gravity GNSS (I2C, multi-constellation)
- CAN Bus: 500 kbps via TWAI driver (GPIO 15 TX, GPIO 16 RX)
- I2C: GPIO 1 SDA, GPIO 2 SCL
- Flash: 4 MB with dual OTA partitions
Components
- DFRobot Gravity GNSS module (GPS + BeiDou + GLONASS)
- Waveshare ESP32-S3-RS485-CAN module
- JST XH connectors for power and signal distribution
KiCAD Library Dependencies
This project uses the consolidated TrailCurrentKiCADLibraries.
Setup:
# Clone the library
git clone [email protected]:trailcurrentoss/TrailCurrentKiCADLibraries.git
# Set environment variables (add to ~/.bashrc or ~/.zshrc)
export TRAILCURRENT_SYMBOL_DIR="/path/to/TrailCurrentKiCADLibraries/symbols"
export TRAILCURRENT_FOOTPRINT_DIR="/path/to/TrailCurrentKiCADLibraries/footprints"
export TRAILCURRENT_3DMODEL_DIR="/path/to/TrailCurrentKiCADLibraries/3d_models"
See KICAD_ENVIRONMENT_SETUP.md for detailed setup instructions.
Firmware
Built with ESP-IDF (v5.5.x). Pure C, no Arduino framework.
Prerequisites
# Install ESP-IDF (one-time setup)
# See https://docs.espressif.com/projects/esp-idf/en/stable/esp32/get-started/
# Source the ESP-IDF environment
source ~/esp/v5.5.2/esp-idf/export.sh
Build and Flash
idf.py set-target esp32s3
idf.py build
idf.py -p /dev/ttyACM0 flash monitor
Release Binaries
The firmware produces two binary types for different deployment methods:
| Binary | Purpose | How it's used |
|---|---|---|
bearing.bin | App-only image | OTA updates via Headwaters (esp_ota_write validates the app header) |
bearing_merged.bin | Full flash image (bootloader + partition table + OTA data + app) | Web flasher (writes entire flash at offset 0x0) |
After building, run the merge script to create both:
idf.py build
./merge.sh
Attach both files to each GitHub release. The merged binary cannot be used for OTA — esp_ota_end() validates an app image header, and the merged binary starts with the bootloader, which would fail validation.
OTA Firmware Update
Firmware can be updated over-the-air via the CAN bus trigger protocol:
- Send CAN ID
0x00with the target device's last 3 MAC bytes - The device joins WiFi (credentials must be provisioned first) and starts an HTTP server
- Upload the app-only firmware binary:
curl -X POST http://esp32-XXYYZZ.local/ota --data-binary @build/bearing.bin
The device writes to the inactive OTA partition and reboots with the new firmware. If the upload fails or times out (3 minutes), the device returns to normal operation.
mDNS Self-Discovery
When CAN ID 0x02 is broadcast (typically by the Headwaters controller), all modules with WiFi credentials join the network and advertise themselves via mDNS:
- Service:
_trailcurrent._tcp - TXT Records:
type=bearing— module typecanid=0x06— primary CAN status message IDfw=<version>— firmware version
The Headwaters controller queries mDNS to discover all online modules, then confirms each via GET /discovery/confirm. Modules disconnect from WiFi and return to CAN-only operation after confirmation or a 3-minute timeout.
CAN Bus Protocol
All messages use standard (11-bit) identifiers at 500 kbps.
Graceful Node Management
The CAN bus uses a TX_ACTIVE / TX_PROBING state machine that allows nodes to enter and leave the bus without impacting other nodes:
- TX_ACTIVE: Normal operation at 30 Hz. If 3 consecutive TX failures occur (no ACK from peers), transitions to TX_PROBING.
- TX_PROBING: Sends a single status message every 2 seconds. When a peer ACKs a transmission or incoming RX data is detected, transitions back to TX_ACTIVE.
This means unplugging a module causes it to gracefully back off, and plugging it back in resumes full-rate communication automatically with zero configuration.
TX Messages (GNSS Data)
| ID | Name | DLC | Format |
|---|---|---|---|
0x06 | DateTime | 7 | [year_H, year_L, month, day, hour, minute, second] |
0x07 | SatSpeedCourse | 6 | [satellites, speed_H, speed_L, course_H, course_L, gnss_mode] |
0x08 | Altitude | 4 | [alt_3, alt_2, alt_1, alt_0] — altitude in meters * 100 |
0x09 | LatLon | 8 | [lat_sign, lat_2, lat_1, lat_0, lon_sign, lon_2, lon_1, lon_0] — degrees * 10000 |
Encoding details:
- Speed: knots * 100 (uint16, big-endian)
- Course: degrees * 10 (uint16, big-endian, rounded)
- Altitude: meters * 100 (uint32, big-endian)
- Lat/Lon: sign byte (0=positive, 1=negative) + 24-bit scaled value (degrees * 10000)
RX Messages (Control)
| ID | Name | Purpose |
|---|---|---|
0x00 | OTA Trigger | [mac0, mac1, mac2] — target device by last 3 MAC bytes |
0x01 | WiFi Config | Chunked credential provisioning (see below) |
0x02 | Discovery | Broadcast trigger for mDNS self-discovery |
0x04 | Version Report | Sent on boot: [mac3, mac4, mac5, major, minor, patch] — reports running firmware version to Headwaters |
WiFi Credential Provisioning (CAN ID 0x01)
WiFi credentials are sent in 6-byte chunks over CAN:
- Start:
[0x01, ssid_len, pass_len, ssid_chunks, pass_chunks] - SSID chunks:
[0x02, chunk_index, byte0..byte5] - Password chunks:
[0x03, chunk_index, byte0..byte5] - End:
[0x04, xor_checksum]
Each chunk carries up to 6 bytes. A 5-second timeout resets reception if a chunk is missed.
GNSS Modes
The DFRobot Gravity receiver supports multiple satellite constellation configurations:
| Mode | Constellations |
|---|---|
| 1 | GPS only |
| 2 | BeiDou only |
| 3 | GPS + BeiDou |
| 4 | GLONASS only |
| 5 | GPS + GLONASS |
| 6 | BeiDou + GLONASS |
| 7 | GPS + BeiDou + GLONASS (default) |
Project Structure
main/ # ESP-IDF firmware
├── main.c # GNSS polling, CAN TX/RX with state machine
├── gnss.h / gnss.c # DFRobot GNSS I2C driver
├── ota.h / ota.c # OTA updates & WiFi provisioning
├── discovery.h / discovery.c # mDNS auto-discovery
├── CMakeLists.txt # Build config
└── idf_component.yml # Managed dependencies (mdns)
EDA/ # KiCAD hardware design
├── trailcurrent-gps-module.kicad_pro
├── trailcurrent-gps-module.kicad_sch # Root schematic (5 sheets)
├── trailcurrent-gps-module.kicad_pcb
├── mcu.kicad_sch # MCU subsystem
├── power.kicad_sch # Power management
├── can.kicad_sch # CAN interface
└── connectivity.kicad_sch # GNSS interface
CAD/ # FreeCAD housing / 3D prints
partitions.csv # Dual OTA partition table
sdkconfig.defaults # Build defaults
Manufacturing
- PCB Files: Ready for fabrication via standard PCB services (JLCPCB, OSH Park, etc.)
- BOM Generation: Export BOM from KiCAD schematic (Tools > Generate BOM)
- JLCPCB Assembly: See BOM_ASSEMBLY_WORKFLOW.md
License
MIT License - See LICENSE file for details
This is open source hardware. You are free to use, modify, and distribute these designs for personal or commercial purposes.
Contributing
Improvements and contributions are welcome! Please submit issues or pull requests to the main repository.
Support
For questions about:
- KiCAD setup: See KICAD_ENVIRONMENT_SETUP.md
- Library consolidation: See CONNECTOR_CONSOLIDATION_SUMMARY.md
- Assembly workflow: See BOM_ASSEMBLY_WORKFLOW.md
No comments yet. Be the first to ask about this board.