Bỏ qua, đến nội dung chính
Bài viếtFirmwareMới

OTA Firmware Updates: Thiết kế và triển khai cập nhật firmware từ xa

Hướng dẫn thiết kế OTA an toàn cho ESP32 và STM32, từ partition, image có chữ ký, rollback, rollout theo nhóm đến delta update.

Chia sẻ

LinkedInFacebookX
ESP32 development board beside a laptop showing a firmware deployment dashboard on an electronics workbench

OTA update nhìn thì đơn giản, nhưng để chạy ổn ngoài thực tế lại có khá nhiều góc phải nghĩ. Demo thì chỉ cần tải một file, reboot và thấy firmware mới. Còn thiết bị đặt ngoài hiện trường thì phải chịu được mất điện giữa chừng, firmware mới boot được nhưng không kết nối được, hoặc nằm offline vài tháng.

Trong bài này, mình sẽ đi qua một kiến trúc thực tế cho ESP32 và STM32. Mục tiêu không phải chọn sẵn một cloud service, mà là hiểu những mảnh ghép giúp một lần update có thể phục hồi: layout flash, manifest, chữ ký số, kiểm tra ở lần boot đầu, rollback và chính sách rollout.

OTA không chỉ là tải file qua HTTP

Hãy xem OTA là một quy trình có ba bên:

  1. Application đang chạy tải firmware mới vào một vùng an toàn.
  2. Bootloader kiểm tra image và quyết định slot nào sẽ được boot.
  3. Application mới phải chứng minh rằng nó hoạt động ổn trước khi được xác nhận vĩnh viễn.

Nếu application ghi đè lên bản duy nhất đang chạy, một lần mất điện có thể biến update có thể khôi phục thành một ca phải mang thiết bị về sửa. Vì vậy, nên quyết định layout flash trước khi viết phần network.

Board phát triển ESP32 cạnh sổ tay có phác thảo partition flash

Trước khi viết code OTA, hãy vẽ layout flash và quyết định nơi giữ lại firmware đang chạy ổn định.

Bắt đầu bằng chiến lược partition

Với ESP32, layout an toàn phổ biến sẽ có bootloader, partition table, OTA metadata và ít nhất hai app slot:

flash
├── bootloader
├── partition table
├── otadata          # slot OTA nào sẽ boot
├── ota_0            # application hiện tại hoặc bản trước
├── ota_1            # nơi tải candidate firmware
└── nvs, littlefs…   # dữ liệu thiết bị, tách khỏi app image

Firmware đang chạy ghi image mới vào slot không được chọn. Khi file đã tải đủ và qua bước verify, firmware mới đổi lựa chọn boot. Bản cũ vẫn còn đó cho đến khi candidate vượt qua các kiểm tra ở lần boot đầu.

Trên STM32, tên gọi có thể khác nhưng ý tưởng tương tự. Bootloader có thể dùng download slot và execution slot, hoặc dùng hai app slot với cơ chế swap/copy. Ví dụ SBSFU của ST có cả cấu hình một slot và hai slot; cấu hình hai slot cho nhiều khoảng trống hơn để xử lý mất điện khi cài đặt.

Đổi lại, bạn phải trả bằng dung lượng flash. Hai app slot đầy đủ tốn flash hơn một slot, còn delta package vẫn cần vùng tạm hoặc một cách patch streaming được thiết kế rất cẩn thận. Hãy tính kích thước firmware tối đa từ sớm. Image vừa đủ hôm nay có thể không còn vừa sau khi thêm driver màn hình ở phiên bản sau.

Định nghĩa manifest trước khi định nghĩa endpoint

Đừng để thiết bị phải đoán mọi thứ từ tên file như firmware-v12.bin. Hãy đưa các thông tin quan trọng vào manifest hoặc image header có chữ ký:

{
  "product": "sensor-node",
  "hardware": ["rev-a", "rev-b"],
  "version": "1.8.0",
  "security_version": 4,
  "size": 786432,
  "sha256": "…",
  "image_url": "https://updates.example.com/node-1.8.0.bin",
  "minimum_bootloader": "2.1.0"
}

Hash giúp phát hiện dữ liệu hỏng. Chữ ký trả lời một câu hỏi khác: image này có thật sự được tạo bởi quy trình release được phép hay không? Trước khi đánh dấu slot là bootable, hãy kiểm tra cả chữ ký lẫn product, hardware revision và bootloader tối thiểu.

Trên ESP32, ESP-IDF có OTA API và secure boot để kiểm tra application image đã ký. Trên STM32, bạn có thể dùng giải pháp secure firmware update như SBSFU hoặc bootloader như MCUboot. API khác nhau, nhưng ranh giới tin cậy phải rõ: private signing key nằm trong hệ thống release, không nằm trong firmware thiết bị hay thư mục public của update server.

State machine của một lần update

Mình thường viết các state rõ ràng trước, vì cách này làm lộ ra phần lớn bug OTA:

IDLE
  → CHECKING
  → DOWNLOADING
  → DOWNLOADED
  → VERIFIED
  → PENDING_BOOT
  → FIRST_BOOT_TEST
       ├── HEALTHY  → CONFIRMED
       └── FAILED   → ROLLBACK

Thiết bị nên lưu đủ state để sau một lần reset có thể tiếp tục hoặc bắt đầu lại một cách an toàn. Download có thể retry; quyết định boot phải được ghi theo cách atomic. Dùng counter tăng dần hoặc metadata ghi lặp để mất điện không biến phần chọn slot thành dữ liệu nửa chừng.

Với ESP-IDF, partition otadata được thiết kế cho công việc này. Với MCUboot, image trailer và swap state cho biết image test đã được confirm hay cần revert. Với bootloader STM32 tự viết, đây là phần nên dành nhiều thời gian nhất cho failure-injection test.

Rollback cần health check

“Thiết bị reboot được” không đồng nghĩa với “update thành công”. Ở lần boot đầu, hãy chạy một cửa sổ kiểm tra ngắn:

  • Application có khởi tạo được các peripheral bắt buộc không?
  • Có đọc được configuration mà không lỗi migration không?
  • Có kết nối được control plane trong thời gian giới hạn không?
  • Watchdog có ổn khi application bắt đầu chạy thật không?
  • Sensor hoặc actuator quan trọng có vượt qua self-test không?

Chỉ khi qua các bước này, image mới nên được đánh dấu là hợp lệ. Nếu check fail, bootloader sẽ chọn lại slot tốt gần nhất ở lần reboot sau. Bạn nên test luôn nhánh xấu bằng cách cố tình tạo một candidate firmware fail health check.

Board vi điều khiển cạnh nguồn cấp và laptop trong lúc kiểm thử cập nhật firmware

OTA an toàn phải được thử với mất nguồn, boot lỗi và tải dở dang, không chỉ thử một lần update thành công.

Với ESP-IDF, rollback được phối hợp giữa bootloader và application. Image đang ở trạng thái chờ có thể được xác nhận bằng esp_ota_mark_app_valid_cancel_rollback(), hoặc bị từ chối bằng esp_ota_mark_app_invalid_rollback_and_reboot(). Với MCUboot, image mới chỉ được confirm sau khi nó tự đánh dấu OK; nếu không, bootloader sẽ revert test swap.

Một chi tiết dễ bị bỏ qua: bootloader đã nằm trên các thiết bị ngoài hiện trường cũng phải hỗ trợ rollback. Bật setting trong application mới không thể biến một bootloader cũ, vốn không biết theo dõi pending image, thành bootloader có rollback.

Transport bảo mật, image có chữ ký và anti-rollback

Hãy dùng TLS cho đường truyền, nhưng đừng nhầm TLS với firmware authenticity. TLS bảo vệ kết nối; chữ ký image bảo vệ firmware ngay cả khi file đã được lưu, cache hoặc đi qua một endpoint không đúng.

Với một sản phẩm nghiêm túc, mình thường cần các lớp sau:

  • HTTPS hoặc transport có xác thực tương đương.
  • Manifest và firmware image có chữ ký.
  • Secure boot hoặc root of trust ở bootloader.
  • Device identity và quyền truy cập ở update service.
  • Anti-rollback cho những bản sửa lỗi bảo mật không được phép quay về image cũ.
  • Dữ liệu thiết bị được mã hóa hoặc kiểm soát truy cập, nhất là credential trong NVS hay bộ nhớ ngoài.

Anti-rollback cũng cần được bật có chủ đích. Một bug fix thông thường có thể cần rollback về bản trước. Nhưng nếu firmware mới sửa một lỗ hổng nghiêm trọng, security counter nên tăng để thiết bị không quay lại build còn lỗ hổng. Hãy tách “application version” và “security version” thành hai khái niệm riêng.

Delta update: tiết kiệm bandwidth nhưng không miễn phí

Delta update chỉ gửi phần khác nhau giữa image cũ và image mới. Cách này có thể tiết kiệm bandwidth, đặc biệt với thiết bị dùng cellular hoặc fleet có chi phí kết nối cao. Nhưng nó kéo theo một số ràng buộc:

  • Thiết bị phải biết chính xác base image đang có.
  • Patch phải được xác thực giống như full image.
  • Thiết bị cần đủ vùng tạm và RAM cho thuật toán patch, hoặc một thiết kế streaming rất cẩn thận.
  • Phải có full-image fallback cho thiết bị lệch nhiều phiên bản hoặc có storage lỗi.
  • Test phải bao gồm mất điện giữa patch, không chỉ patch thành công.

Mình sẽ bắt đầu bằng full-image A/B update, trừ khi bandwidth đã là vấn đề đo được. Khi đường update ổn định rồi, mới thêm delta package ở tầng phân phối và vẫn giữ contract cuối cùng của bootloader: nhận được một image đầy đủ, đã verify và boot được.

Rollout theo từng nhóm thiết bị

OTA backend không nên phát hành một bản mới cho toàn bộ fleet cùng lúc. Một chính sách nhỏ là đủ để bắt đầu:

  1. Release cho nhóm thiết bị nội bộ.
  2. Release cho một nhóm canary chọn ngẫu nhiên.
  3. Theo dõi boot success, rollback rate, connectivity, pin và crash report.
  4. Chỉ mở rộng theo từng đợt khi metric vẫn nằm trong giới hạn.
  5. Tự động pause hoặc cancel rollout khi vượt ngưỡng.

Thiết bị không nên chỉ báo “download complete”. Hãy gửi version đang chạy, kết quả boot, lý do rollback, thời gian update và error code ở mức đủ dùng. Đừng đưa secret hay toàn bộ dữ liệu khách hàng vào telemetry. Fleet update là một hệ thống vận hành, không chỉ là một tính năng firmware.

Checklist triển khai gọn

Trước khi gọi OTA là production-ready, mình sẽ kiểm tra:

  • Inactive slot đủ lớn cho signed image lớn nhất.
  • Mất điện lúc download vẫn boot được image hiện tại.
  • Mất điện khi ghi boot metadata không làm hỏng lựa chọn slot.
  • Signature sai, hardware revision sai và package hết hạn đều bị từ chối.
  • Health check lần boot đầu có thể kích hoạt rollback.
  • Thiết bị offline lâu có thể dùng full image phù hợp để cập nhật.
  • Bootloader và rollback policy đã được triển khai trên fleet.
  • Server có staged rollout và nút dừng phát hành.
  • Có đường recovery bằng USB/UART hoặc service tool cho các ca thật sự không thể tự cứu.

Kết luận

OTA tốt thường khá “bảo thủ”: giữ lại bản đang chạy ổn, verify trước khi boot, chỉ confirm sau health check và quan sát được rollout. ESP32 có OTA path được ESP-IDF hỗ trợ rõ. STM32 có nhiều cách làm secure firmware update tùy dòng chip và bootloader. Kiến trúc có thể dùng chung dù API cụ thể khác nhau.

Mình khuyên bạn làm full-image dual-slot trước, thử bằng cách chủ động ngắt nguồn, sau đó mới thêm anti-rollback và staged rollout. Hãy xem delta update là một tối ưu hóa sau khi đường update cơ bản đã đủ nhàm chán vì nó luôn hoạt động.

Tài liệu tham khảo

Chia sẻ

LinkedInFacebookX

Tiếp tục khám phá

Đọc tiếp

Bài viết liên quan

Xem thêm trong Bài viết

Nastrotek sử dụng cookie để phân tích truy cập và cá nhân hóa quảng cáo, giúp hiểu cách website được sử dụng. Bạn có thể chấp nhận hoặc từ chối các cookie không thiết yếu. Chính sách bảo mật