Skip to content

Custom Bootloader via CAN - #22

Open
rickydamaraju wants to merge 5 commits into
mainfrom
feature/stm32f103-can-bootloader
Open

rickydamaraju wants to merge 5 commits into
mainfrom
feature/stm32f103-can-bootloader

Conversation

@rickydamaraju

Copy link
Copy Markdown

Addresses ticket #19. Adds a CAN 2.0 bootloader for the STM32F103T8U6, supporting board-specific firmware transfer, flash erase, CRC verification, retries, application startup, and remote reset. It also includes a Python flashing tool, automated tests, and the memory-layout changes required for the temperature-board application.

@rickydamaraju rickydamaraju added the enhancement New feature or request label Sep 9, 2026

@jeevanshah07 jeevanshah07 left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found several issues that should be addressed before this is used on the vehicle:

  1. Boot handoff leaves SysTick active. jump_to_application() calls HAL_DeInit(), but this HAL implementation does not disable SysTick. It then changes VTOR/MSP and re-enables interrupts before entering the application reset handler. A pending tick can execute application interrupt code before .data and .bss initialization. Disable SysTick and clear pending/enabled NVIC state before changing the stack and branching.

  2. The temperature application now interrupts on every CAN frame. CAN_Init_Filter() still uses an all-zero ID/mask, while this branch enables the FIFO0 pending interrupt. Every frame on a busy vehicle bus will invoke the ISR just to discard it. Configure a filter for standard ID 0x600 + node instead.

  3. Every normal build silently uses node 1. Both BL_NODE_ID and BOOTLOADER_NODE_ID default to 1, and neither build exposes a documented node parameter. Multiple boards will reset together and can produce colliding bootloader responses. Make the node explicit in both builds and fail compilation when it is omitted.

  4. The application vector relocation edit is inactive. VECT_TAB_OFFSET is 0x4000, but USER_VECT_TAB_ADDRESS remains undefined. Bootloader handoff sets VTOR, but direct debug/application startup will use the bootloader vector table. Enable the relocation in the application build.

  5. The host receive-queue drain can run forever. enter_bootloader() loops until nonblocking recv() returns None; a saturated bus can keep that loop alive indefinitely. Bound the drain or set a receive filter for the selected response ID.

  6. Response parsing needs validation. _response() unpacks any matching arbitration ID as exactly eight bytes and does not verify the returned command. A malformed or delayed response can abort or satisfy the wrong operation. Validate standard/data frame type, DLC, and expected command.

Also, CRC provides corruption detection but not firmware authenticity. If the CAN bus is not explicitly treated as trusted, any participant can erase and replace the application, so the update protocol needs authentication or signature verification (low prio).

/* Change per PCB (valid range: 1..7). */
#ifndef BL_NODE_ID
#define BL_NODE_ID 1U
#endif

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in main.c you use BOOTLOADER_NODE_ID and here it is BL_NODE_ID - are these supposed to be the same?

@rickydamaraju

Copy link
Copy Markdown
Author

The bootloader handoff, CAN filtering, vector relocation, and node-ID configuration were fixed to make updates safer and prevent multiple boards from responding incorrectly. The flashing tool now limits queue draining and validates every response’s CAN frame type, length, and command before accepting it. Both oscillator versions compile successfully, all tests pass, and firmware authentication is documented as a future improvement.

@jeevanshah07 jeevanshah07 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review written by GPT-6 Astra on behalf of Jeevan, based on the supplied review findings.

The shared vector-table relocation prevents reliable bootloader operation. The update completion handshake and application node-selection builds also contain functional defects that should be fixed before merging.

  • [P1] Keep the bootloader vector table at its own flash origin — rfr26-tempSensor/Core/Src/system_stm32f1xx.c:110

    The bootloader Makefile also compiles this system_stm32f1xx.c, so its SystemInit() now sets VTOR to the application at 0x08004000. Once HAL_Init() enables SysTick, interrupts use application vectors instead of the bootloader's handlers; on a freshly installed board these vectors are erased, causing a fault before flashing can work. Make the offset build-specific so the bootloader uses zero and only the application uses 0x4000.

  • [P2] Wait for START after completing an update — stm32f103-can-bootloader/src/main.c:140-142

    When an update takes longer than the 1.5-second startup window, END sets rx_state to RX_IDLE and the same main-loop iteration jumps into the application before processing another frame. However, can_flash.py always sends START after receiving COMPLETE and waits for its acknowledgement. The application does not handle START, so a successful update ends with a host timeout. Keep completed updates in a state that awaits START, or explicitly align the host protocol with automatic startup.

  • [P2] Rebuild application objects when NODE changes — rfr26-tempSensor/Debug/Core/Src/subdir.mk:31

    Building with NODE=1 and then NODE=2 reuses the same application objects because make does not track changes to command-line compiler definitions. The second binary therefore still listens for node 1's reset command, despite being built for node 2, preventing correctly addressed updates and potentially resetting another board. Track NODE as a build dependency or use per-node output directories, as the bootloader already does; apply this to both Debug and Release.

@rickydamaraju

Copy link
Copy Markdown
Author

The bootloader now keeps its vector table at its own flash origin, while application builds relocate theirs to 0x08004000. After completing an update, the bootloader waits for and acknowledges the host’s START command before launching the application. Debug and Release builds now recompile the node-dependent code every time, preventing stale node IDs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants