The gc9a01 display SPI initialization causes a ~50mA current transient
that trips the brownout detector, causing a boot loop. The bd-66hx power
supply needs decoupling improvement; disabling brownout is the software
workaround until hardware is reworked.
Also discovered the previous sdkconfig was manually corrupted (wrong
partition table, USB JTAG console instead of UART, Memprot lock enabled).
Deleting sdkconfig and regenerating from sdkconfig.defaults restores the
correct OTA partition table, UART0 console, and proper rollback config.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Display was black because gc9a01_init() was never called — driver compiled
but never invoked. Init before vesc_can_init so SPI/register init completes
before TWAI claims GPIO2 (BL pin); TWAI idle=recessive=high keeps BL on.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Without this file, idf.py build fails after fullclean with 'Failed to resolve
component cJSON' because the component manager has no manifest to fetch from.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CMakeLists.txt REQUIRES 'cJSON' fails on IDF v5.2 — component manager
installs it as 'espressif__cjson'. Update the require name to match.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
gc9a01.c and gc9a01.h were never committed to main despite ota_display.c
depending on their display_fill_rect/draw_string/draw_arc functions.
Also:
- CMakeLists.txt: add gc9a01.c to SRCS
- config.h: restore DISP_* GPIO defs (DC=8 CS=9 SCK=10 MOSI=11 RST=14 BL=2)
for Waveshare ESP32-S3-Touch-LCD-1.28
- ota_display.c: fix snprintf buffer too small (verline[32]→[48]) which
GCC 13.2.0 rejects as -Werror=format-truncation
Confirmed builds clean and boots on bd-66hx hardware (mbpi5 /dev/ttyACM0).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extends the bd-66hx serial protocol with two new Orin→ESP32 commands:
CMD_OTA_CHECK (0x10): triggers gitea_ota_check_now(), responds with
TELEM_VERSION_INFO (0x84) for Balance and IO (current + available ver).
CMD_OTA_UPDATE (0x11): uint8 target (0=balance, 1=io, 2=both) — triggers
uart_ota_trigger() for IO or ota_self_trigger() for Balance.
NACK with ERR_OTA_BUSY or ERR_OTA_NO_UPDATE on failure.
New telemetry: TELEM_OTA_STATUS (0x83, target+state+progress+err),
TELEM_VERSION_INFO (0x84, target+current[16]+available[16]).
Wires OTA stack into app_main: ota_self_health_check on boot,
gitea_ota_init + ota_display_init after peripherals ready.
CMakeLists updated with all OTA component dependencies.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds ota_display_task (5 Hz) on GC9A01 240×240 round LCD:
- Idle: orange dot badge at top-right when update available, version text
- Progress: arc sweeping 0→360° around display perimeter with phase label
- States: Downloading/Verifying/Applying/Rebooting (Balance) and
Downloading/Sending/Done (IO via UART)
- Error: red arc + "FAILED RETRY?" prompt
Display primitives (fill_rect, draw_string, draw_arc) are stubs called
from the GC9A01 SPI driver layer (separate driver bead).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Balance side (uart_ota.c): downloads io-firmware.bin from Gitea to RAM,
computes SHA256, then streams to IO over UART1 (GPIO17/18, 460800 baud)
as OTA_BEGIN/OTA_DATA/OTA_END frames with CRC8 + per-chunk ACK/retry (×3).
IO side (uart_ota_recv.c): receives frames, writes to inactive OTA partition
via esp_ota_write, verifies SHA256 on OTA_END, sets boot partition, reboots.
IO board main.c + CMakeLists.txt scaffold included.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Downloads balance-firmware.bin from Gitea release URL to inactive OTA
partition, streams SHA256 verification via mbedTLS, sets boot partition
and reboots. Auto-rollback via CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE if
ota_self_health_check() not called within 30 s of boot. Progress 0-100%
in g_ota_self_progress for display task.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds gitea_ota_check_task on Balance board: fetches Gitea releases API
every 30 min and on boot, filters by esp32-balance/ and esp32-io/ tag
prefixes, compares semver against embedded FW version, stores update info
(version string, download URL, SHA256) in g_balance_update / g_io_update.
WiFi credentials read from NVS namespace "wifi"; falls back to compile-time
DEFAULT_WIFI_SSID/PASS if NVS is empty.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add dual OTA partitions (ota_0/ota_1 × 1.75 MB each) and otadata to
both esp32s3/balance/ and esp32s3/io/ on 4 MB flash layouts.
Enable CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE and OTA HTTP on Balance.
Create esp32s3/io/ project scaffold with config.h pin assignments.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements full boot-time auto-start for the SaltyBot ROS2 stack on
Jetson Orin. Everything comes up automatically after power-on with
correct dependency ordering and restart-on-failure for each service.
New systemd services:
saltybot-ros2.service full_stack.launch.py (perception + SLAM + Nav2)
saltybot-esp32-serial.service ESP32-S3 BALANCE UART bridge (bd-wim1, PR #727)
saltybot-here4.service Here4 DroneCAN GPS bridge (bd-p47c, PR #728)
saltybot-dashboard.service Web dashboard on port 8080
Updated:
saltybot.target now Wants all four new services with
boot-order comments
can-bringup.service bitrate 500 kbps → 1 Mbps (DroneCAN for Here4)
70-canable.rules remove bitrate from udev RUN+=; let service
own the bitrate, add TAG+=systemd for device unit
install_systemd.sh installs all services + udev rules, colcon
build, enables mosquitto, usermod dialout
full_stack.launch.py resolve 8 merge conflict markers (ESP32-S3
rename) and fix missing indent on
enable_mission_logging_arg — file was
un-launchable with SyntaxError
New:
scripts/ros2-launch.sh sources ROS2 Humble + workspace overlay,
then exec ros2 launch — used by all
ROS2 service units via ExecStart=
udev/80-esp32.rules /dev/esp32-balance (CH343) and
/dev/esp32-io (ESP32-S3 native USB CDC)
Resolves bd-1hyn
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds .gitea/workflows/ota-release.yml: triggered on esp32-balance/vX.Y.Z
or esp32-io/vX.Y.Z tags, builds the corresponding ESP32-S3 project with
espressif/idf:v5.2.2, and attaches <app>_<version>.bin + .sha256 to the
Gitea release for OTA download.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces Orin↔ESP32-S3 BALANCE CAN comms (0x300-0x303 / 0x400-0x401)
with binary serial framing over CH343 USB-CDC at 460800 baud.
Protocol matches bd-wim1 (sl-perception) exactly:
Frame: [0xAA][LEN][TYPE][PAYLOAD][CRC8-SMBUS]
CRC covers LEN+TYPE+PAYLOAD, big-endian multi-byte fields.
Commands (Orin→ESP32): HEARTBEAT/DRIVE/ESTOP/ARM/PID
Telemetry (ESP32→Orin): TELEM_STATUS, TELEM_VESC_LEFT (ID 56),
TELEM_VESC_RIGHT (ID 68), ACK/NACK
VESC CAN TWAI kept for motor control; drive commands from Orin
forwarded to VESCs via SET_RPM. Hardware note: SN65HVD230
rewired from IO43/44 to IO2/IO1 to free IO43/44 for CH343.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
rclpy RcutilsLogger.info/warning/debug() do not accept printf-style
positional format args. Also fix p["use_phone_timestamp"] → p["use_phone_ts"]
key mismatch in __init__ log line.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add _TOPIC_IOS_GPS = 'saltybot/ios/gps' constant
- Subscribe to saltybot/ios/gps in _on_mqtt_connect
- Dispatch to _handle_ios_gps() in _dispatch()
- _handle_ios_gps(): same logic as _handle_gps(), frame_id='ios_gps',
publishes to /saltybot/ios/gps via self._ios_gps_pub
- Add rx/pub/err/last_rx_ts counters for the new topic
- Add /saltybot/ios/gps to rosbridge_params.yaml topics_glob
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add /saltybot/phone/gps, /saltybot/phone/imu, /saltybot/phone/battery,
/saltybot/phone/bridge/status, /gps/fix, /gps/vel to topics_glob so
the browser GPS dashboard can receive phone-bridged GPS data.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously showed only phone GPS. Now also subscribes via ROSLIB to
saltybot/gps/fix + saltybot/gps/vel on the same rosbridge URL for
robot (SAUL-TEE) position. Blue marker+trail for phone (raw WS
{type:gps}), orange marker+trail for robot (ROS topics). Sidebar shows
phone speed/alt/heading/accuracy + robot lat/lon/speed + distance
between the two. FIT ALL button auto-zooms to show both. Status bar
badges for phone staleness and robot fix/vel freshness.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>