Skip to content

Add JetFleet BMS targets, C6 support, and ESP-IDF v6 migration - #92

Closed
Fajdiga wants to merge 14 commits into
vedderb:mainfrom
Fajdiga:cleanup/upstream-ready
Closed

Add JetFleet BMS targets, C6 support, and ESP-IDF v6 migration#92
Fajdiga wants to merge 14 commits into
vedderb:mainfrom
Fajdiga:cleanup/upstream-ready

Conversation

@Fajdiga

@Fajdiga Fajdiga commented May 14, 2026

Copy link
Copy Markdown

This PR brings the upstream-ready JetFleet/VESC Express work from Fajdiga:cleanup/upstream-ready onto vedderb:main.

Changes

  • Adds JetFleet JFBMS32 v1/v2 hardware targets, including BQ769x2 support, config defaults, config parser/XML metadata, and VESC Tool settings.
  • Adds JetFleet JFBMS slave hardware target and documents the private master/slave BMS CAN protocol.
  • Adds JetFleet JF Link hardware target for receiving JFBMS slave data, aggregating BMS values, and coordinating balancing.
  • Adds generic hardware CAN hooks and compile-time toggles:
    • HW_CAN_PING_SCAN_ENABLED
    • HW_CAN_NO_ACK_MODE
    • hardware CAN filter/RX hooks
    • HW_IS_SLAVE
  • Fixes BMS aggregate values reply capacity and clamps received/transmitted cell and temperature counts to local array sizes.
  • Fixes uint32_t wraparound in Lisp sleep timer wakeups.
  • Splits light/deep sleep radio teardown and deinitializes radios before deep sleep.
  • Adds an LBM evaluator heartbeat and watchdog feeder so Lisp evaluator stalls can trip the task watchdog instead of leaving units frozen.
  • Adds ESP32-C6 NimBLE BLE support and splits C6 devkit targets into 4 MB and 8 MB flash variants.
  • Migrates the project to ESP-IDF v6.0.1 API paths, including ADC, I2C, TWAI/CAN, BLE, WiFi, USB, display/touch, sdkconfig defaults, and bootloader component updates.

Motivation

The branch makes the JetFleet BMS hardware usable from the upstream tree and includes the generic infrastructure needed for headless/private-CAN BMS variants. It also carries reliability fixes found while running long sleep/wake BMS cycles and the ESP32-C6/ESP-IDF v6 updates used by the current hardware work.

Compatibility

Existing hardware targets keep their default behavior unless they opt into the new hardware macros. The ESP-IDF v6 migration is included in this branch, so this PR intentionally updates the project baseline rather than only adding hardware files.

Tested

  • Built locally from cleanup/upstream-ready.
  • Runtime-tested on JFBMS32 hardware.
  • Runtime-tested on JFBMS slave hardware with master-side receivers.
  • Runtime-tested repeated BMS sleep/wake cycles.
  • Tested JF Link aggregation with JFBMS slave broadcasts.
  • Tested ESP32-C6 BLE communication with the NimBLE backend.

Jerne Fajdiga and others added 14 commits May 14, 2026 17:24
Adds the JetFleet JFBMS32 hwconf for ESP32-C3. The target has v1 and v2 entry-point headers with common definitions in hw_jfbms32_core.h and the shared BQ769x2 implementation in hw_jfbms32.c.

The shared implementation handles dual BQ76952 initialization, I2C address recovery, cell and temperature reporting, charge control, balancing, power-switch control, and BMS sleep/wakeup support.

The hwconf ships its own bq769x2_defs.h copy, following the convention used by the existing VESC BMS hwconfs, and includes a generated config parser/XML/defaults set for JFBMS32 settings. External NTC resistance and beta are configurable per unit through that settings XML.

CMake already finds the hw_*.h target files; this commit also adds the JetFleet/JFBMS32 include directories needed for the override config and hardware source files to compile.
Adds an optional hwconf macro that lets a board opt out of the
COMM_PING_CAN broadcast scan. The default in main/hwconf/hw.h is 1
to preserve the existing behavior; a hwconf header can override it
to 0 to skip the scan loop in commands.c.

Useful for headless variants that share a CAN bus with a master and
should never participate in VESC ping discovery.
Adds an optional hwconf macro that switches the TWAI driver into
TWAI_MODE_NO_ACK at init. The default in main/hwconf/hw.h is 0, so
the existing TWAI_MODE_NORMAL behavior is preserved unless a hwconf
header opts in.

Useful for bench / diagnostic builds that need to observe a live CAN
bus without generating ACKs.
Adds HW_CAN_FILTER_CONFIG in main/hwconf/hw.h so a hwconf header can override the default TWAI acceptance filter. The default expands to false, so behavior remains TWAI_FILTER_CONFIG_ACCEPT_ALL() when no hardware provides an override.

The filter is re-applied on both initial CAN bring-up and after comm_can_update_baudrate() so a hwconf override survives baud changes. f_config is made non-const so update_filter() can replace it.

Also dispatches an optional weak hw_can_rx_hook() from the CAN process task after Lisp CAN processing and before the normal VESC/BMS decoder path. Hardware variants such as JFBMS_SLAVE can use this to consume custom 11-bit protocol frames without changing generic CAN decoding.

Matches the existing HW_INIT_HOOK() / HW_POST_LISPIF_HOOK() pattern: a hwconf header declares its hardware-specific function and wires it through a small macro or weak-symbol implementation.
Adds a hwconf-defined HW_IS_SLAVE flag that suppresses two pieces of
boot-time behavior in main.c:

- BLE and WiFi initialization. A slave variant has no user-facing radios;
  running these only wastes RAM and adds attack surface.
- main_store_backup_data() copying controller_id and can_baud_rate from
  the live config into the persisted backup. Headless slave builds use
  their own slave-specific config and should not mirror these shared
  VESC Express compatibility fields back into backup storage.

When HW_IS_SLAVE is not defined, behavior is unchanged. The flag is
expected to be set in a hwconf header alongside HW_NAME and the other
HW_* macros.
Adds a headless JFBMS slave hwconf for ESP32-C3 that uses the hwconf
hooks introduced by the preceding commits:

- HW_IS_SLAVE skips BLE and WiFi init at boot.
- HW_CAN_PING_SCAN_ENABLED 0 suppresses the COMM_PING_CAN scan loop so
  the slave does not participate in VESC ping discovery.
- HW_CAN_NO_ACK_MODE 0 keeps normal ACK behavior; it is declared
  explicitly so the slave CAN mode is visible alongside the other CAN
  options.
- HW_CAN_FILTER_CONFIG forwards to hw_can_get_filter_config(), which
  installs a TWAI acceptance filter for the balance-command ID derived
  from the configured slave_id.

The slave drives up to two BQ76952 ICs over I2C with per-IC enable
pins, runs an active-low buzzer on GPIO3, and persists a small
slave-only config: slave_id, can_baud_rate, cells_ic1/cells_ic2, NTC
resistance, and NTC beta.

It also documents the private 11-bit JFBMS master/slave CAN protocol
and adds the JFBMS slave include directory to main/CMakeLists.txt so the
override config and hardware files are visible to the build.
esp_sleep_enable_timer_wakeup expects a uint64_t microsecond count, but
ext_sleep_deep / ext_sleep_light cast the float-seconds product to
uint32_t first. That wraps for sleep_time > ~4294 s, so a
customer-configured 2 h wake cycle silently aliased to ~48.5 min and
the unit woke up far too often. Use uint64_t for the cast.
Leaving Bluedroid and the BT controller enabled across esp_deep_sleep_start
has been observed to corrupt BLE state over many sleep cycles - the BMS
freezes after weeks of 2 h-cycle wakes. Tear them down before sleeping.

Light sleep returns to the caller after wake, so the stacks must still be
usable. Split the helper into:

- sleep_disable_radios() - reversible (wifi_stop, bluedroid_disable,
  bt_controller_disable). Used by ext_sleep_light.
- sleep_deinit_radios()  - full teardown, calls disable + bluedroid_deinit
  + bt_controller_deinit. Used by ext_sleep_deep, where the chip reboots
  on wake so deinit is free.
CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0 only monitors the idle task, so
an LBM eval thread stuck on a mutex can leave idle running while the unit
appears frozen. In that state the old configuration never tripped the
task WDT, so recovery required a power cycle.

Enable task WDT panic/reset on C3, C6, and S3 defaults and add a
low-priority monitor task in main.c. The monitor registers itself with
the WDT and feeds it while the LispBM evaluator heartbeat advances.

The heartbeat is driven by the evaluator scheduler in eval_cps.c, so
normal Lisp execution, sleeps, and pauses are treated as healthy. If the
heartbeat stops after the monitor has armed, the monitor waits for a
30-second stall window, then stops feeding the WDT; with the configured
5-second task-WDT timeout, the chip then panics/resets shortly after.

Pre-arm: feed unconditionally until the first heartbeat fires so units
where the evaluator never starts do not self-reset.

Includes lbm_get_eval_heartbeat() and heartbeat bumps in the evaluator
loop so the watchdog has a reliable, GC-independent liveness signal.
Grow the shared BMS cell array limit from 50 to 255 cells so aggregate
master/slave reporting can represent larger packs.

Clamp received and transmitted cell/temperature counts to the local
array sizes before indexing. The CAN status replies now report the
clamped counts, preventing malformed or oversized BMS frames from
driving out-of-bounds accesses.

Keep COMM_BMS_GET_VALUES large enough for the expanded payload by moving
it to a 1024-byte static buffer, protected by a reply mutex because the
buffer is no longer stack-local.
Adds the JetFleet JF Link hwconf for ESP32-C3, including its config
defaults, parser, XML, and CMake include directory.

JF Link listens to the private JFBMS slave 11-bit CAN protocol, tracks
up to eight slave BMS boards, aggregates their cell/temperature/status
data into the shared BMS value structure, and exposes that aggregate to
VESC Tool through the existing BMS command path.

The target also coordinates slave balancing: it sends balance masks,
waits for slave voltage-settled status, computes balancing candidates
from the aggregate pack state, and keeps balance commands alive on the
bus. A new BMS command hook lets JF Link intercept force-balance and
manual balance override commands before the generic BMS forwarding path.
Adds a NimBLE implementation of the VESC Express BLE protocol for
ESP32-C6 builds while keeping the existing Bluedroid implementation for
targets that still use it.

main/CMakeLists.txt now selects comm_ble_nimble.c and
ble/custom_ble_nimble.c when CONFIG_BT_NIMBLE_ENABLED is set, otherwise
it builds the original comm_ble.c and ble/custom_ble.c files.

The new ble_compat.h shim provides the Bluedroid-shaped UUID,
permission, characteristic-property, and advertising types used by the
existing custom BLE API so the LispBM BLE extensions can build against
either host stack.

The NimBLE backend implements the VESC UART-style RX/TX GATT service,
packet chunking/notifications, advertising, pairing reset handling, and
the dynamic custom BLE service API used by LispBM scripts. ESP32-C6
defaults are switched to NimBLE with matching controller/host settings.
Split the ESP32-C6 devkit target into explicit 4 MB and 8 MB hardware
configs. The default ESP32-C6 HW_NAME becomes "Devkit C6 8M", while
"Devkit C6 4M" is available for modules with smaller flash.

The 4 MB target adds sdkconfig.defaults.hw_c6_4m with the 4 MB flash
size and 4 MB OTA partition table. CMake now applies shared
sdkconfig.defaults.<target> first and then appends any
sdkconfig.defaults.<hw_file> overrides, so hardware-specific defaults
only need to contain the board-specific differences.

README.md documents the shared/default override layering and which C6
target to select for 4 MB versus 8 MB modules.
Move the build to ESP-IDF v6.0.1 and update APIs that changed in the new
toolchain.

- add an esp_driver_i2c-backed compatibility layer for legacy I2C call sites
- migrate ADC reads to the oneshot/calibration drivers
- update WiFi, BLE, sleep wakeup, bootloader, and component-manager APIs
- migrate TWAI/CAN code to the ESP-IDF v6 on-chip TWAI node API
- update BME280, IMU, display, touch, RGB LED, and hwconf call sites for
  ESP-IDF v6 API and header changes
- keep C6 4 MB builds on NimBLE and size optimization to fit the 1600 KiB
  OTA slots
- update the main and bootloader CMake files for the new toolchain layout
- update README.md from ESP-IDF 5.5.4 to the supported 6.0.1 release

Verified with ESP-IDF v6.0.1:
- Devkit C6 4M
@Fajdiga

Fajdiga commented May 14, 2026

Copy link
Copy Markdown
Author

Closing this all-in-one draft in favor of the split PRs: #93, #94, #95, #96, #97, and #98.

@Fajdiga Fajdiga closed this May 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant