ESP32-C6 / Rust

Khối A · GĐ 0 · phiên chính

Phiên 1 — Toolchain và dòng log đầu tiên

Phiên đầu tiên, chưa đụng breadboard. Mục tiêu thật không phải "nạp được firmware" mà là "khi có gì sai, chip nói cho anh biết".

Thời lượng2h
Cầnboard, cáp USB-C có dây data
Chưa cầnbreadboard, linh kiện

0. Trạng thái đầu vào

Phiên đầu tiên, không có tiến độ trước. Máy dev cần có rustup với toolchain stable từ 1.86 trở lên — đó là MSRV của esp-generate Nguồn source · ví dụ esp-rs/esp-generate Sinh project no_std đúng bộ Cargo.toml + .cargo/config.toml + build.rs; đọc mục Quickstart và list-options. Mở trong tab mới · Esc để đóng .

Board dùng trong lộ trình này là loại theo bố cục DevKitC-1, và ba đặc điểm của nó ảnh hưởng trực tiếp đến phiên này Nguồn source · ví dụ wuxx/nanoESP32-C6 — schematic mã nguồn mở Board dùng trong lộ trình này: LED RGB nối GPIO nào, chân 3V3 lấy nguồn từ đâu, LDO là loại gì. Mở trong tab mới · Esc để đóng :

  • Hai cổng USB-C, có silkscreen ghi rõ CH343ESP32C6 cạnh từng cổng.
  • Chip bridge là CH343P, driver có sẵn trong kernel Linux — không phải cài gì.
  • Nút BOOTRST rời, dùng được khi cần ép vào bootloader.

1. Mục tiêu

  1. espflash board-info chạy không cần sudo, in ra đúng ESP32-C6, địa chỉ MAC, và xác nhận đúng dung lượng flash của bản đã đặt.
  2. cargo run nạp được firmware và serial monitor hiện một dòng log do chính anh viết — không phải dòng có sẵn của template.
  3. panic!() cố ý làm monitor in ra thông điệp panic kèm backtrace, thay vì reset im lặng.

2. Đọc trước

Khoảng 20–30 phút, lý tưởng là làm ở phiên phụ trước đó.

  • The Rust on ESP Book → phần Writing Your Own Application Nguồn tài liệu chính chủ The Rust on ESP Book Tài liệu chính chủ cho Rust trên chip Espressif — có cả phần OTA, debugging và troubleshooting. Mở trong tab mới · Esc để đóng . Đọc mục esp-generate và mục về cấu trúc project sinh ra.
  • README của esp-rs/esp-generate — phần Quickstart và cách dùng list-options / explain Nguồn source · ví dụ esp-rs/esp-generate Sinh project no_std đúng bộ Cargo.toml + .cargo/config.toml + build.rs; đọc mục Quickstart và list-options. Mở trong tab mới · Esc để đóng .

Hai câu hỏi cần tự trả lời trong lúc đọc:

  1. Vì sao project no_std cần ghim target trong rust-toolchain.toml, thay vì tự chạy rustup target add một lần rồi thôi? (Gợi ý: nghĩ về chuyện repo này 6 tháng nữa mở trên máy khác.)
  2. Vì sao cargo run — vốn là lệnh chạy binary trên máy host — lại nạp được firmware lên một con chip khác kiến trúc? Cái gì đứng giữa?

3. Nội dung chính

Block 1 — Cho máy tính nhìn thấy board

Khoảng 20 phút.

Vì sao khối này đứng trước: mọi thứ sau đó đều đi qua một file thiết bị /dev/tty*. Linux mặc định chỉ cho root mở nó. Bỏ qua bước này thì triệu chứng gặp phải là espflash báo permission denied — và người mới gần như luôn đi tìm lỗi ở toolchain thay vì ở quyền truy cập.

Hiện chuỗi lệnh đầy đủ của Block 1
cài công cụ và tìm device nodemáy dev
$ cargo install espflash
$ cargo install esp-generate --locked
#
$ ls /dev/tty* > /tmp/truoc.txt      # chạy TRƯỚC khi cắm board
# … cắm board vào …
$ ls /dev/tty* > /tmp/sau.txt
$ diff /tmp/truoc.txt /tmp/sau.txt   # cái mới xuất hiện chính là board
#
$ sudo usermod -aG uucp "$USER"      # Arch/EndeavourOS; Ubuntu là dialout
# … đăng xuất/đăng nhập lại, hoặc newgrp uucp …
$ groups                             # xác nhận group đã có hiệu lực
$ espflash board-info
Group serial khác nhau theo distro

uucp trên Arch và EndeavourOS, dialout trên Ubuntu Nguồn tài liệu chính chủ Arch Wiki — Users and groups Bảng group hệ thống: uucp là group cổng serial trên Arch, không phải dialout. Mở trong tab mới · Esc để đóng . Thêm group xong phải đăng nhập lại hoặc newgrp — nếu không, groups vẫn chưa hiện và anh sẽ tưởng lệnh không ăn.

Hai cổng USB-C — làm dạng đoán trước rồi kiểm chứng. Board đã silkscreen sẵn nên không phải mò. Thay vào đó:

  1. Viết ra dự đoán trước khi cắm: cổng CH343 sẽ tạo ra device node tên gì, cổng ESP32C6 tên gì? Lý do?
  2. Cắm từng cổng, ghi lại node thật.
  3. Đối chiếu. Sai chỗ nào thì chỗ đó chính là thứ đáng ghi vào sổ.

Manh mối để dự đoán: một cổng đi qua chip bridge rời nên hệ điều hành thấy một thiết bị USB-serial của hãng khác; cổng kia nối thẳng vào USB controller nằm trong chính con C6, và class USB mà nó khai báo quyết định driver nào bắt nó.

Đây không phải bài tập cho vui — GĐ 3 sẽ phải chọn đúng cổng để log không rớt khi chip ngủ, và câu trả lời nằm ở đây.

Block 2 — Sinh project và nạp lần đầu

Khoảng 35 phút.

Vì sao dùng esp-generate thay vì tự viết Cargo.toml: một project no_std trên ESP cần khớp đúng bộ Cargo.toml + .cargo/config.toml (runner, rustflags) + rust-toolchain.toml

  • build.rs + linker script. Bộ này đổi giữa các version esp-hal. Chép từ blog là cách chắc chắn nhất để mất một buổi tối vào lỗi link không liên quan gì đến việc anh định học.

Ba quyết định phải ra trong phiên này, và giữ nguyên cả dự án:

Quyết địnhHai phíaĐiều cần cân nhắc
espflash hay probe-rsespflash đi qua cổng CH343; probe-rs cần cổng ESP32C6 (USB-JTAG native)Board có cả hai cổng nên chọn gì cũng chạy được hôm nay. Nhưng ở GĐ 3 chip sẽ deep sleep — lúc đó cổng nào còn sống, cổng nào biến mất khỏi /dev?
log hay defmtlog đọc thẳng bằng mắt; defmt nhẹ hơn nhiều nhưng cần công cụ decodeFirmware này sẽ chạy 72h liên tục ở GĐ 5
Panic handleresp-backtrace hay handler tối giảnMục tiêu 3 của phiên phụ thuộc trực tiếp vào ô này

Bật unstable-hal (GĐ 3 sẽ cần), embassy (GĐ 1 sẽ cần), alloc.

Đừng bật wifi ở phiên này

P1 không dùng tới, mà nó kéo theo cả radio stack và RTOS layer — build nặng hơn và thêm hai lớp có thể hỏng Nguồn tài liệu chính chủ esp-radio (tên mới của esp-wifi) Còn ở 1.0-beta, API vẫn đổi — ghim cứng version bằng dấu =. Mở trong tab mới · Esc để đóng . GĐ 4 mới bật.

Việc cần làm, theo thứ tự:

  1. Chạy esp-generate list-options, đọc qua danh sách, rồi chạy TUI và tự chọn.
  2. cargo run. Xem template chạy.
  3. Sửa để in dòng của chính anh — đây mới là mục tiêu 2. Dòng template không tính.
  4. git init, commit đầu tiên. Ghim version bằng dấu = trong Cargo.toml.

Trước khi chạy cargo run, mở .cargo/config.toml và tìm dòng runner. Đó là câu trả lời cho câu hỏi thứ hai ở mục 2 — đọc nó rồi mới chạy, để lúc chạy anh biết chuyện gì đang xảy ra.

Block 3 — Panic phải kêu

Khoảng 25 phút.

Vì sao đây là mục tiêu ngang hàng với "nạp được firmware": trên máy tính, chương trình sai thì có stack trace. Trên chip, panic mặc định là một vòng lặp vô hạn hoặc một cú reset — không có gì được in ra. Anh sẽ thấy board "tự khởi động lại" và không biết tại sao.

16 phiên còn lại sẽ dễ hơn rất nhiều nếu ngay từ bây giờ mọi panic đều tự khai báo Nguồn tài liệu chính chủ esp-backtrace Panic handler in ra stack unwind; feature flag quyết định backtrace có hay không. Mở trong tab mới · Esc để đóng .

  1. Thêm một panic!("...") cố ý ở đâu đó chạy tới được. Nạp. Xem monitor.
  2. Nếu chỉ thấy thông điệp mà không thấy backtrace: quay lại đối chiếu ô "Panic handler" ở Block 2. Cũng mở .cargo/config.toml xem phần rustflagscó một flag ở đó tồn tại chính vì backtrace trên RISC-V, tìm xem là flag nào và đọc comment kèm nó.
  3. Backtrace in ra là một danh sách địa chỉ, không phải tên hàm. Câu hỏi tự trả lời: làm sao đổi những địa chỉ đó thành tên hàm và số dòng? (Manh mối: file ELF trong target/ giữ bảng symbol; espflash monitor có tham số nhận nó Nguồn source · ví dụ esp-rs/espflash Công cụ nạp và serial monitor; danh sách tính năng chip in ra bởi board-info nằm hardcode trong source. Mở trong tab mới · Esc để đóng .)

4. Definition of Done

  • espflash board-info chạy không có sudo, in đúng ESP32-C6 và MAC.
  • Ghi vào sổ: tên device node của từng cổng USB-C trên board, và cổng nào đi qua đường nào.
  • cargo run nạp thành công, monitor hiện dòng log do anh viết.
  • panic!() cố ý → monitor in ra thông điệp panic backtrace, dù mới ở dạng địa chỉ.
  • Ghi vào sổ ba quyết định của Block 2 kèm lý do mỗi cái — phiên sau sẽ đọc lại chỗ này.
  • git init và commit đầu tiên; esp-hal đã ghim version bằng =.
  • 3 dòng đóng phiên: đang ở đâu / lỗi đang mở / việc đầu tiên phiên sau.

5. Nếu kẹt, kiểm tra gì trước

Theo đúng thứ tự này — đi từ lớp vật lý lên, đừng đọc code trước.

  1. Board có xuất hiện trong /dev không? So ls /dev/tty* trước và sau khi cắm. Không có gì mới → gần như chắc chắn là cáp sạc chứ không phải cáp data. Đổi cáp trước khi nghĩ đến bất cứ thứ gì khác. Vẫn không thì thử cổng USB-C còn lại trên board.
  2. Có device node nhưng permission denied? Kiểm groups. Có trong /etc/groupgroups chưa hiện → chưa đăng nhập lại.
  3. Nạp được nhưng monitor trắng trơn? Đang cắm cổng nào, và cổng đó có khớp với công cụ đã chọn ở Block 2 không? Kiểm tiếp baud rate.
  4. Không vào được bootloader? Giữ nút BOOT, nhấn nhả RESET, rồi thả BOOT. Nếu phải làm thủ công mỗi lần thì ghi lại — đó là dữ kiện về board, không phải lỗi của anh.

6. Bẫy của riêng phần này

Hai cổng USB-C nằm sát nhau

Silkscreen ở mặt trên, nên khi board cắm trên breadboard với dây cáp che ngang thì chữ CH343/ESP32C6 rất dễ khuất — mà cắm nhầm cổng cho ra triệu chứng "board không phản hồi" y hệt cáp hỏng. Dán một mẩu băng dính màu lên cổng dùng chính.

  • Cáp sạc không có dây data — lỗi phổ biến nhất của người mới, và nó nguỵ trang thành lỗi board.
  • Không chọn panic handler có backtrace thì mục tiêu 3 không bao giờ đạt, mà thông điệp lỗi vẫn hiện ra — nên rất dễ tưởng là đã xong.
  • Đừng sửa rustflags trong .cargo/config.toml dù thấy lạ. Có flag ở đó phục vụ backtrace.

7. Câu hỏi kiểm tra cuối phiên

Trả lời vào sổ, không mở code hay tài liệu.

  1. no_std bỏ đi những gì so với std? Vì sao firmware này không dùng được println!, và thứ anh vừa dùng để in log khác nó ở chỗ nào?
  2. cargo run trong project này thực chất gọi lệnh gì? Cấu hình đó nằm ở file nào, dòng nào?
  3. Board có hai cổng USB-C. Cắm cổng nào thì probe-rs làm việc được, cổng nào thì không — và vì sao? (Câu này quyết định lựa chọn ở GĐ 3, nên nếu chưa chắc thì ghi rõ là chưa chắc.)

8. Stretch

Không tính vào Definition of Done.

  • Nạp thử lên board thứ hai. Cùng firmware, xem có chạy y hệt không. Board thứ hai hỏng thì biết bây giờ tốt hơn biết ở GĐ 4 lúc cần hai con để chạy ESP-NOW.
  • Clone esp-rs/esp-hal, checkout đúng tag đang dùng, mở examples/ xem có gì Nguồn source · ví dụ esp-rs/esp-hal — examples/ và qa-test/ Tài liệu thật của dự án. README nói thẳng: xem examples ở đúng tag của bản esp-hal đang dùng, đừng đọc nhánh main. Mở trong tab mới · Esc để đóng . Biết đường đi trong đó sớm sẽ tiết kiệm nhiều phiên sau.
  • Mở schematic của board và tìm hai thứ: chân 3V3 trên header nối vào đâu, và con LDO là loại gì Nguồn source · ví dụ wuxx/nanoESP32-C6 — schematic mã nguồn mở Board dùng trong lộ trình này: LED RGB nối GPIO nào, chân 3V3 lấy nguồn từ đâu, LDO là loại gì. Mở trong tab mới · Esc để đóng . GĐ 3 sẽ cần cả hai.
  • So kích thước binary cargo buildcargo build --release. Chênh bao nhiêu lần? Ghi lại con số — GĐ 6 làm OTA sẽ cần biết firmware to cỡ nào so với slot flash.

Phiên này không có phần code phải tự nghĩ nhiều, nhưng ba quyết định ở Block 2 sẽ theo suốt 16 phiên còn lại. Ghi lý do, không chỉ ghi lựa chọn.

Đọc trước hoặc trong phiên

  • tài liệu chính chủThe Rust on ESP Book

    Tài liệu chính chủ cho Rust trên chip Espressif — có cả phần OTA, debugging và troubleshooting.

  • source · ví dụesp-rs/esp-generate

    Sinh project no_std đúng bộ Cargo.toml + .cargo/config.toml + build.rs; đọc mục Quickstart và list-options.

  • source · ví dụwuxx/nanoESP32-C6 — schematic mã nguồn mở

    Board dùng trong lộ trình này: LED RGB nối GPIO nào, chân 3V3 lấy nguồn từ đâu, LDO là loại gì.

  • source · ví dụesp-rs/esp-hal — examples/ và qa-test/

    Tài liệu thật của dự án. README nói thẳng: xem examples ở đúng tag của bản esp-hal đang dùng, đừng đọc nhánh main.

Tra khi cần, không đọc tuyến tính

  • source · ví dụesp-rs/espflash

    Công cụ nạp và serial monitor; danh sách tính năng chip in ra bởi board-info nằm hardcode trong source.

  • tài liệu chính chủesp-backtrace

    Panic handler in ra stack unwind; feature flag quyết định backtrace có hay không.

  • tài liệu chính chủArch Wiki — Users and groups

    Bảng group hệ thống: uucp là group cổng serial trên Arch, không phải dialout.

Để dành cho giai đoạn sau