Imported from GitHub: trailcurrentoss/TrailCurrentPicket · commit 64f89e3 · license MIT
Description
Reed switch sensor module for monitoring cabinet and door states over CAN bus
README
TrailCurrent Picket
Sensor module that monitors cabinet and door open/closed states using reed switches and reports status over a CAN bus interface. Part of the TrailCurrent open-source vehicle platform.
Hardware Overview
- Board: Waveshare ESP32-S3-RS485-CAN
- Microcontroller: ESP32-S3
- Framework: ESP-IDF
- Function: Cabinet and door state monitoring with CAN bus reporting
- Key Features:
- 12 reed switch inputs for open/closed detection
- CAN bus communication at 500 kbps (onboard TJA1051 transceiver)
- Firmware-configurable CAN address via NVS (up to 8 modules per bus)
- Over-the-air (OTA) firmware updates via WiFi (triggered over CAN)
- Onboard status LED
- Custom flash partition layout with dual OTA slots
Design Specifications
- Operating Temperature: -20°C to +70°C
- Power: 7-36V DC input via onboard buck converter
- Data Rate: 5 transmissions per second
CAN Bus Addressing
The CAN message ID is set at compile time via the PICKET_ADDRESS build flag. Valid values are 0 through 7, giving 8 possible modules on the same bus. The CAN ID is computed as 0x0A + PICKET_ADDRESS. Default is 0 if not specified. The build will fail if a value outside 0-7 is used.
# Build with default address 0 (CAN ID 0x0A)
idf.py build
# Build for address 3 (CAN ID 0x0D)
idf.py build -DPICKET_ADDRESS=3
PICKET_ADDRESS | CAN ID | DBC Message Name |
|---|---|---|
| 0 (default) | 0x0A | PicketStatus0 |
| 1 | 0x0B | PicketStatus1 |
| 2 | 0x0C | PicketStatus2 |
| 3 | 0x0D | PicketStatus3 |
| 4 | 0x0E | PicketStatus4 |
| 5 | 0x0F | PicketStatus5 |
| 6 | 0x10 | PicketStatus6 |
| 7 | 0x11 | PicketStatus7 |
Building All Variants
Use build-all.sh to build all 8 address variants in a single run:
./build-all.sh
This produces:
build/picket_addr0.bin # Address 0 (CAN ID 0x0A)
build/picket_addr1.bin # Address 1 (CAN ID 0x0B)
...
build/picket_addr7.bin # Address 7 (CAN ID 0x11)
Creating a GitHub Release
After building all variants, upload all 8 binaries as release assets:
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/picket_addr0.bin \
build/picket_addr1.bin \
build/picket_addr2.bin \
build/picket_addr3.bin \
build/picket_addr4.bin \
build/picket_addr5.bin \
build/picket_addr6.bin \
build/picket_addr7.bin \
--repo trailcurrentoss/TrailCurrentPicket \
--title "v1.0.0" \
--notes "Firmware release v1.0.0"
The Headwaters deployment system and the web-based firmware installer both expect these 8 files per release. The naming convention {type}_addr{N}.bin is required.
CAN Message Format
Each module transmits a 2-byte message at 5 Hz (200 ms interval):
| Byte | Bits | Description |
|---|---|---|
| 0 | 0-7 | Door status 1-8 (RSW01-RSW08) |
| 1 | 0-3 | Door status 9-12 (RSW09-RSW12) |
| 1 | 4-7 | Reserved |
Each bit represents one reed switch: 1 = door open, 0 = door closed.
CAN Control Messages
The module also listens for control messages from other nodes:
- CAN ID 0x00 - OTA Update Notification: Contains a 3-byte MAC address suffix. If it matches this module's hostname, the module connects to WiFi using stored credentials and enters OTA update mode.
- CAN ID 0x01 - WiFi Credential Configuration: Multi-message protocol to receive and store WiFi SSID and password in NVS flash for future OTA updates.
- CAN ID 0x02 - Discovery Trigger: Broadcast with no payload. Unconfigured modules respond by joining WiFi and advertising via mDNS for Headwaters to discover and register them.
- CAN ID 0x03 - Discovery Reset: Targeted by MAC address. Clears the configured flag so the module responds to the next discovery trigger.
- CAN ID 0x04 - Firmware Version Report: Sent once on boot. Payload:
[mac3, mac4, mac5, major, minor, patch]. Reports running firmware version to Headwaters.
Module Discovery
New modules are automatically discovered by Headwaters using a one-at-a-time registration protocol:
- Headwaters sends CAN ID 0x02 (broadcast, no payload)
- Unconfigured modules join WiFi and advertise
_trailcurrent._tcpvia mDNS with TXT records:type,addr,canid,fw - Headwaters browses mDNS, reads the metadata, and sends
GET /discovery/confirm - Module marks itself configured in NVS and tears down WiFi
The discovery window is 30 seconds. The status LED blinks at 4 Hz during discovery. Already-configured modules ignore the trigger. To re-discover a module, send CAN ID 0x03 with its MAC bytes to clear the configured flag.
Firmware Version
The firmware version is set in CMakeLists.txt via PROJECT_VER and reported during discovery via the fw mDNS TXT record. It is also available at runtime via esp_app_get_description().
GPIO Pin Assignments
Onboard (not on pin header)
| GPIO | Function | Notes |
|---|---|---|
| 0 | Status LED | Onboard LED |
| 15 | CAN TX | Onboard TJA1051 transceiver |
| 16 | CAN RX | Onboard TJA1051 transceiver |
| 19 | USB D_N | USB Serial/JTAG (flash + monitor) |
| 20 | USB D_P | USB Serial/JTAG (flash + monitor) |
Pin Header (reed switch inputs)

| GPIO | Function | Notes |
|---|---|---|
| 4 | RSW01 | Internal pull-up, no ext. resistor |
| 5 | RSW02 | Internal pull-up, no ext. resistor |
| 6 | RSW03 | Internal pull-up, no ext. resistor |
| 7 | RSW04 | Internal pull-up, no ext. resistor |
| 8 | RSW05 | Internal pull-up, no ext. resistor |
| 9 | RSW06 | Internal pull-up, no ext. resistor |
| 10 | RSW07 | Internal pull-up, no ext. resistor |
| 11 | RSW08 | Internal pull-up, no ext. resistor |
| 12 | RSW09 | Internal pull-up, no ext. resistor |
| 13 | RSW10 | Internal pull-up, no ext. resistor |
| 14 | RSW11 | Internal pull-up, no ext. resistor |
| 43 | RSW12 | Internal pull-up, no ext. resistor |
Ribbon Cable Pinout
When connecting via a 20-pin IDC ribbon cable, the red stripe aligns with pin 1 (3.3V/5V end). The reed switch wires are interleaved due to standard odd/even row numbering.

Hardware Requirements
Components
- Board: Waveshare ESP32-S3-RS485-CAN — industrial-grade board with onboard CAN transceiver, buck converter (7-36V input), and pin header for sensor connections
- Sensors: Normally Open (NO) reed switches
- Connectors: Wire to pin header (2x10, 2.54mm pitch)
KiCAD Library Dependencies
This project uses the consolidated TrailCurrentKiCADLibraries.
Setup:
# Clone the library alongside this project
git clone [email protected]:trailcurrentoss/TrailCurrentKiCADLibraries.git
# Set environment variables (add to ~/.bashrc or ~/.zshrc)
# Adjust paths to where you cloned the library
export TRAILCURRENT_SYMBOL_DIR="../TrailCurrentKiCADLibraries/symbols"
export TRAILCURRENT_FOOTPRINT_DIR="../TrailCurrentKiCADLibraries/footprints"
export TRAILCURRENT_3DMODEL_DIR="../TrailCurrentKiCADLibraries/3d_models"
See KICAD_ENVIRONMENT_SETUP.md in the library repository for detailed setup instructions.
Firmware
The firmware is built with ESP-IDF (not PlatformIO/Arduino).
Build & Flash
# Source ESP-IDF environment
source ~/esp/v5.5.2/esp-idf/export.sh
# Set target (first time only)
idf.py set-target esp32s3
# Build (default address 0, CAN ID 0x0A)
idf.py build
# Build with a specific module address (0-7)
idf.py build -DPICKET_ADDRESS=3
# Flash via USB
idf.py -p /dev/ttyACM0 flash
# Monitor serial output
idf.py -p /dev/ttyACM0 monitor
OTA Update
# Use the binary matching the module's address
curl -X POST http://esp32-XXYYZZ.local/ota --data-binary @build/picket_addr0.bin
Where XXYYZZ is the last 3 bytes of the device's WiFi MAC address (printed at boot).
Opening the Project
- Set up environment variables (see Library Dependencies above)
- Open KiCAD:
kicad EDA/TrailCurrentPicketModule/TrailCurrentPicketModule.kicad_pro - Verify libraries load - All symbol and footprint libraries should resolve without errors
- View 3D models - Open PCB and press
Alt+3to view the 3D visualization
Manufacturing
- PCB Files: Ready for fabrication via standard PCB services (JLCPCB, OSH Park, etc.)
- BOM Generation: Export BOM from KiCAD schematic (Tools > Generate BOM)
- Enclosure: FreeCAD design included in
CAD/directory - JLCPCB Assembly: See BOM_ASSEMBLY_WORKFLOW.md for detailed assembly workflow
Documentation
- Requirements: See
DOCS/Requirements/high-level-requirements.mdfor detailed specifications
Project Structure
├── CAD/ # FreeCAD enclosure design
├── DOCS/ # Requirements documentation
│ └── Requirements/
│ └── high-level-requirements.md
├── EDA/ # KiCAD hardware design files
│ └── TrailCurrentPicketModule/
│ ├── *.kicad_pro # KiCAD project
│ ├── *.kicad_sch # Schematic
│ └── *.kicad_pcb # PCB layout
├── main/ # ESP-IDF firmware source
│ ├── main.c # Main application, CAN task, reed switch reading
│ ├── discovery.h / discovery.c # mDNS self-discovery and Headwaters registration
│ ├── ota.h / ota.c # OTA firmware updates via WiFi
│ ├── wifi_config.h / .c # WiFi credential storage and CAN provisioning
│ ├── CMakeLists.txt # Component build config
│ └── idf_component.yml # Component dependencies
├── CMakeLists.txt # ESP-IDF project root
├── partitions.csv # Flash partition layout
├── build-all.sh # Build all 8 address variants (addr 0-7)
└── sdkconfig.defaults # Default build configuration
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.
Support
For questions about:
- KiCAD setup: See KICAD_ENVIRONMENT_SETUP.md
- Assembly workflow: See BOM_ASSEMBLY_WORKFLOW.md
No comments yet. Be the first to ask about this board.