Skip to the content
PCBKitNew board
2 layers134.7 × 91.9 mmTrailCurrentTorrent, the printed circuit board in 3D

CAN bus controlled power distribution module for vehicle electrical circuits

Size
134.7 × 91.9 mm
Copper layers
2
Thickness
1.6 mm
Parts
68
Nets
78
Pads
258
Tracks, vias
642, 376
Pours
4
Routed
Yes
KiCad
KiCad 9.0 (file 20241229)
Created
13 Feb 2026
Updated
2 Oct 2026

Overview

TrailCurrentTorrent is an open-source printed circuit board by trailcurrentoss, from the trailcurrentoss/TrailCurrentTorrent repository on GitHub, published under the MIT licence. It is a 2-layer board of 134.7 × 91.9 mm with 68 parts on 78 nets, routed with 642 tracks and 376 vias.

It is built around the ESP32-WROOM-32, SN65HVD230D and IRF4905STRLPBF, with the SS54 among its other parts. Its parts include 19 resistors, 14 connectors, 11 ICs, 8 diodes and 8 transistors. It belongs with the interfaces and adapters and home automation and IoT boards and the ESP32 boards of this library.

Main components

4

README

README.md

TrailCurrent Torrent

TrailCurrent Torrent

CAN-controlled 8-channel PWM power distribution module for vehicle lighting and accessory control with OTA firmware update capability and mDNS self-discovery. Up to 3 Torrent modules can operate on the same CAN bus, each with a unique address and CAN IDs. Part of the TrailCurrent open-source vehicle platform.

Hardware Overview

  • Microcontroller: ESP32 (WROOM32)
  • Function: 8-channel PWM lighting/accessory controller with CAN bus interface
  • Key Features:
    • 8 independent MOSFET-driven PWM outputs (0-255 brightness)
    • CAN bus communication at 500 kbps
    • Up to 3 modules on the same bus via compile-time TORRENT_ADDRESS (0-2)
    • Individual and master on/off/brightness control
    • Animated light sequences (startup, interior, exterior)
    • Over-the-air (OTA) firmware updates via WiFi
    • mDNS self-discovery for automatic registration with Headwaters
    • WiFi credentials provisioned dynamically over CAN bus
    • Hierarchical PCB schematic design (5 sheets)

Hardware Requirements

Components

  • Microcontroller: ESP32 development board
  • CAN Transceiver: Vehicle CAN bus interface (TX: GPIO 15, RX: GPIO 13)
  • MOSFET Drivers: 8 channels for PWM output switching
  • DIP Switches: Configuration switches

Pin Connections

PWM Outputs:

GPIOFunction
32Output 1
33Output 2
26Output 3
14Output 4
4Output 5
17Output 6
19Output 7
23Output 8

KiCAD Library Dependencies

This project uses the consolidated TrailCurrentKiCADLibraries.

Setup:

bash
# Clone the library
git clone git@github.com: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 in the library repository for detailed setup instructions.

Opening the Project

  1. Set up environment variables (see Library Dependencies above)
  2. Open KiCAD:
    bash
    kicad EDA/trailcurrent-torrent.kicad_pro
  3. Verify libraries load - All symbol and footprint libraries should resolve without errors
  4. View 3D models - Open PCB and press Alt+3 to view the 3D visualization

Schematic Sheets

The design uses a hierarchical schematic with dedicated sheets:

  • Root - Top-level connections
  • Power - Power distribution and regulation
  • CAN - CAN bus transceiver interface
  • MCU - ESP32 microcontroller and support circuits
  • MOSFETs - 8-channel MOSFET driver outputs
  • DIP Switch - Configuration switches

Firmware

Built with ESP-IDF (Espressif IoT Development Framework) targeting the ESP32.

Prerequisites

Install ESP-IDF v5.x following the official guide.

Build and Flash

bash
# Set up ESP-IDF environment
source ~/esp/v5.5.2/esp-idf/export.sh

# First time: set chip target
idf.py set-target esp32

# Build firmware (default address 0)
idf.py build

# Build for a specific module address (0-2)
idf.py build -DTORRENT_ADDRESS=1

# Flash and monitor (serial)
idf.py -p /dev/ttyUSB0 flash monitor

Multi-Instance Addressing

Up to 3 Torrent modules can share the same CAN bus. Each module is built with a unique address that determines its CAN IDs:

bash
idf.py build                      # Address 0 (default)
idf.py build -DTORRENT_ADDRESS=1  # Address 1
idf.py build -DTORRENT_ADDRESS=2  # Address 2

Each address produces a firmware binary with unique CAN IDs (see CAN Bus Protocol below). Flash each binary to a different ESP32. At boot, the module logs its address and CAN IDs for verification.

Building All Variants

Use build-all.sh to build all 3 address variants in a single run:

bash
./build-all.sh

This produces two binaries per address — one for OTA updates, one for the web flasher:

FileContentsUsed By
build/torrent_addr{N}.binApplication image onlyHeadwaters OTA (deploy.sh, ota.js), direct curl uploads
build/torrent_addr{N}_merged.binBootloader + partition table + OTA data + applicationWeb flasher (full flash at 0x0)

The two binary types exist because OTA and the web flasher write to different targets. Headwaters OTA sends the binary to the device's /ota HTTP endpoint, which calls esp_ota_write to write it to a single app partition. That function validates the image as an application — a merged binary starts with the bootloader instead of an app header, so it would fail validation. The web flasher writes the entire flash contents starting at offset 0x0, so it needs all partitions combined into one file.

Creating a GitHub Release

After building all variants, attach all 6 binaries (3 app-only + 3 merged) as release assets:

bash
git tag -a v1.0.0 -m "Firmware release v1.0.0"
git push origin v1.0.0

gh release create v1.0.0 \
  build/torrent_addr0.bin \
  build/torrent_addr1.bin \
  build/torrent_addr2.bin \
  build/torrent_addr0_merged.bin \
  build/torrent_addr1_merged.bin \
  build/torrent_addr2_merged.bin \
  --repo trailcurrentoss/TrailCurrentTorrent \
  --title "v1.0.0" \
  --notes "Firmware release v1.0.0"

Both the Headwaters deployment system and the web flasher pull from GitHub releases. The web flasher matches _merged.bin files by name for full-flash use. The Headwaters deployment system (fetch-firmware.sh) downloads the app-only torrent_addr{N}.bin files for OTA delivery.

OTA Firmware Update

After initial serial flash, firmware can be updated over WiFi:

  1. Provision WiFi credentials via CAN bus (message ID 0x01)
  2. Trigger OTA mode via CAN bus (message ID 0x00 with device MAC bytes)
  3. Upload firmware via HTTP — always use the app-only binary (torrent_addr{N}.bin), never the merged binary:
    bash
    curl -X POST http://esp32-XXYYZZ.local/ota --data-binary @build/torrent_addr0.bin

The device enters OTA mode for 3 minutes, starts an HTTP server, and advertises via mDNS. After a successful upload it reboots into the new firmware. Dual OTA partitions provide rollback safety.

mDNS Self-Discovery

Modules register themselves with the Headwaters controller:

  1. Provision WiFi credentials via CAN bus (message ID 0x01)
  2. Send discovery trigger via CAN bus (message ID 0x02, broadcast)
  3. Module joins WiFi and advertises a _trailcurrent._tcp mDNS service with metadata:
    • type=torrent — module type
    • addr=0 — module address (0-2, from TORRENT_ADDRESS build flag)
    • canid=0x1B — CAN status message ID (varies by address)
    • fw=1.0.0 — firmware version
  4. Headwaters confirms registration by calling GET /discovery/confirm
  5. Module tears down WiFi and returns to normal CAN operation

Discovery can be triggered again on subsequent CAN 0x02 broadcasts. A module that is already running a discovery session ignores additional triggers until the current session completes or times out (3 minutes).

Logging

Firmware uses the ESP-IDF logging system (ESP_LOGI, ESP_LOGW, ESP_LOGE). Log output is available on the serial monitor at 115200 baud.

Log verbosity can be configured at build time via idf.py menuconfig under Component config > Log output.

WiFi Credentials:

  • WiFi credentials are provisioned dynamically via CAN bus (Message ID 0x01)
  • Credentials are stored in NVS (non-volatile storage) and persist across reboots
  • For standalone testing, credentials can be set manually via NVS

CAN Bus Protocol

CAN IDs are computed as BASE + TORRENT_ADDRESS. The table below shows base IDs and per-instance values.

Receive (Bus → Module):

Base IDAddr 0Addr 1Addr 2Description
—0x000x000x00OTA update trigger (3 bytes: last 3 MAC bytes for device targeting)
—0x010x010x01WiFi credential provisioning (chunked SSID/password protocol)
—0x020x020x02Discovery trigger (broadcast, no payload)
—0x040x040x04Version report on boot: [mac3, mac4, mac5, major, minor, patch]
0x150x150x160x17Set brightness (byte 0 = channel 0-7, byte 1 = PWM value 0-255)
0x180x180x190x1AToggle channel on/off (byte 0 = channel 0-7, 8=all off/on, 9=all on)
0x330x330x340x35Trigger light sequence (byte 0: 0=interior, 1=exterior)

Transmit (Module → Bus):

Base IDAddr 0Addr 1Addr 2IntervalDescription
0x1B0x1B0x1C0x1D33 ms (30 Hz)Status report — current PWM values for all 8 channels (8 bytes)

WiFi Credential Provisioning Protocol

WiFi credentials are sent over CAN in chunks (max 6 payload bytes per CAN frame):

Stepdata[0]PayloadDescription
10x01ssid_len, pass_len, ssid_chunks, pass_chunksStart message
20x02chunk_index, up to 6 bytesSSID chunk
30x03chunk_index, up to 6 bytesPassword chunk
40x04XOR checksumEnd/commit message

All messages are sent on CAN ID 0x01. A 5-second timeout resets the state machine if the sequence is interrupted.

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 for detailed assembly workflow

Project Structure

text
├── EDA/                          # KiCAD hardware design files
│   ├── trailcurrent-torrent.kicad_pro
│   ├── trailcurrent-torrent.kicad_sch  # Root schematic
│   ├── can.kicad_sch             # CAN subsystem
│   ├── mcu.kicad_sch             # MCU subsystem
│   ├── power.kicad_sch           # Power subsystem
│   ├── mosfets.kicad_sch         # 8-channel MOSFET drivers
│   ├── dip_switch.kicad_sch      # Configuration switches
│   └── trailcurrent-torrent.kicad_pcb  # PCB layout
├── CAD/                          # FreeCAD case design and 3D models
│   ├── trailcurrent-torrent-case.FCStd
│   └── TrailCurrentTorrent.3mf
├── main/                         # ESP-IDF firmware source
│   ├── main.c                    # App entry, LEDC PWM, TWAI CAN task, light sequences
│   ├── ota.h / ota.c             # OTA updates, WiFi management, credential provisioning
│   ├── discovery.h / discovery.c # mDNS self-discovery and Headwaters registration
│   ├── CMakeLists.txt            # Component build config
│   └── idf_component.yml         # ESP-IDF component dependencies
├── partitions.csv                # ESP32 flash partition layout (dual OTA)
├── sdkconfig.defaults            # ESP-IDF build defaults
├── build-all.sh                  # Build all 3 address variants (addr 0-2)
└── CMakeLists.txt                # Root ESP-IDF project config

License

MIT License - See LICENSE file for details.

This is open source hardware. You are free to use, modify, and distribute these designs under the terms of the MIT license.

Contributing

Improvements and contributions are welcome! Please submit issues or pull requests.

Support

For questions about:

Parts

68
QtyValueFootprintReferences
1010kResistor_SMD:R_1206_3216Metric_Pad1.30x1.75mm_HandSolderR1, R4, R6, R9, R11, R13, R15, R17, R19, R21
8470Resistor_SMD:R_1206_3216Metric_Pad1.30x1.75mm_HandSolderR7, R8, R10, R12, R14, R16, R18, R20
8Conn_01x04Fuse:Automotive_Fuse_HolderJ4, J5, J6, J7, J9, J10, J11, J12
8IRF4905STRLPBFTransistor_Power:IRF4905STRLPBFQ1, Q2, Q3, Q4, Q5, Q6, Q7, Q8
8PC817OptoDevice:PC817X3CSP9FU4, U5, U6, U7, U8, U9, U10, U11
8SS54Diode_SMD:D_SMBD1, D2, D3, D4, D5, D6, D7, D8
322uFCapacitor_SMD:C_1206_3216Metric_Pad1.33x1.80mm_HandSolderC1, C5, C6
2Conn_01x01TestPoint:TestPoint_7.77mmX7.77mmJ13, J14
2Conn_01x04Connector:Screw_Terminal_1x04J3, J8
10.1uFCapacitor_SMD:C_1206_3216Metric_Pad1.33x1.80mm_HandSolderC2
1100nFCapacitor_SMD:C_1206_3216MetricC4
110uFCapacitor_SMD:C_1206_3216MetricC3
1120Resistor_SMD:R_1206_3216Metric_Pad1.30x1.75mm_HandSolderR5
1AP63203WU-7CustomLibraries:AP63203WU7IC1
1Conn_01x04CustomLibraries:CAN_PLUS_POWERJ2
1Conn_01x06Connector_PinHeader_2.54mm:PinHeader_1x06_P2.54mm_HorizontalJ1
1ESP32-WROOM-32RF_Module:ESP32-WROOM-32UU1
1NLV32T-3R9J-EF (3.9uH)Inductor_SMD:NLV32T-3R9J-EFL1
1SN65HVD230Package_SO:SOIC-8_3.9x4.9mm_P1.27mmU2
1SW_DIP_x04Button_Switch_SMD:SW_DIP_219-4LPSTSW1

Questions

How big is the TrailCurrentTorrent PCB?

It measures 134.7 × 91.9 mm and has 2 copper layers, 1.6 mm thick.

What parts does the TrailCurrentTorrent use?

68 parts, built around the ESP32-WROOM-32, SN65HVD230D, IRF4905STRLPBF and SS54. The full list of its parts is on this page, with links to each part's symbol and footprint where PCBKit's library has them.

Where can I download the TrailCurrentTorrent KiCad files?

From the trailcurrentoss/TrailCurrentTorrent repository on GitHub, at the commit this page was made from: the board is EDA/trailcurrent-torrent.kicad_pcb and its schematic EDA/Test/Test.kicad_sch. PCBKit opens the board in the browser, with nothing to install.

Can I use the TrailCurrentTorrent design in my own project?

Its repository publishes it under the MIT licence: you may reuse it on that licence's terms (keep the author's notice, and for a copyleft licence share your changes alike).

Similar boards

  • 2 layers70.9 × 38.1 mmRwled ctrl, the printed circuit board in 3D

    Rwled ctrl

    cliffanet/rwled

    RW-led factory

    53 parts
  • 2 layers184 × 94 mmFIC Board, the printed circuit board in 3D

    FIC Board

    DanielRHoefer/satoshi-emblem

    Toyota backlit grill emblem with sequential LEDs for 3rd gen 4runner

    193 parts1
  • 4 layers100 × 80 mmEsp32stepper, the printed circuit board in 3D

    Esp32stepper

    kulichbulich/ieap_motor

    IEAP Motor driver

    127 parts
  • 6 layers98.9 × 81.4 mmEthernetSwitch, the printed circuit board in 3D

    EthernetSwitch

    SamuelCicuitStudio/LAN9645-STM32-Managed-Ethernet-Switch

    Managed Ethernet switch platform built around the LAN9645 and STM32H743, featuring copper and SerDes ports, SPI management, RMII, LwIP, and a USB diagnostic CLI.

    231 parts
  • 2 layersBMU switch mosfet, the printed circuit board in 3D

    BMU switch mosfet

    KomplexKapharnaum/KXKM_Batterie_Parallelator

    Battery Management Unit for parallel 24-30V packs Up to 32 batteries ESP32-S3 BLE + MQTT + InfluxDB iOS companion app

    18 partsGPL-3.0
  • 2 layers43 × 43 mmTurboCAN, the printed circuit board in 3D

    TurboCAN

    henrikssn/TurboCAN

    This is the official repository for the TurboCAN controller board, designed specifically for the Voron AfterBurner toolhead but can also be used on similar toolheads using NEMA17 steppers.

    88 parts82
  • 4 layers144.2 × 82.8 mmSmartFuseBox, the printed circuit board in 3D

    SmartFuseBox

    k3ldar/PowerControlHub

    PowerControlHub is a flexible automation controller for 12V systems. It combines relay switching, sensor monitoring, and smart‑home connectivity (MQTT, Home Assistant, REST, BLE) into one device.

    93 parts5GPL-3.0
  • 2 layers82 × 72 mmSRSE2, the printed circuit board in 3D

    SRSE2

    GBert/misc

    miscellaneous stuff / unsorted

    90 parts99