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".
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õ
CH343vàESP32C6cạ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 BOOT và RST rời, dùng được khi cần ép vào bootloader.
1. Mục tiêu
espflash board-infochạy không cầnsudo, in ra đúngESP32-C6, địa chỉ MAC, và xác nhận đúng dung lượng flash của bản đã đặt.cargo runnạ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.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-generatevà mục về cấu trúc project sinh ra. - README của
esp-rs/esp-generate— phần Quickstart và cách dùnglist-options/explainNguồ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:
- Vì sao project no_std cần ghim target trong
rust-toolchain.toml, thay vì tự chạyrustup target addmộ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.) - 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
$ 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-infouucp 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 đó:
- Viết ra dự đoán trước khi cắm: cổng
CH343sẽ tạo ra device node tên gì, cổngESP32C6tên gì? Lý do? - Cắm từng cổng, ghi lại node thật.
- Đố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 định | Hai phía | Điều cần cân nhắc |
|---|---|---|
espflash hay probe-rs | espflash đ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 defmt | log đọc thẳng bằng mắt; defmt nhẹ hơn nhiều nhưng cần công cụ decode | Firmware này sẽ chạy 72h liên tục ở GĐ 5 |
| Panic handler | esp-backtrace hay handler tối giản | Mụ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.
wifi ở phiên nàyP1 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ự:
- Chạy
esp-generate list-options, đọc qua danh sách, rồi chạy TUI và tự chọn. cargo run. Xem template chạy.- 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.
git init, commit đầu tiên. Ghim version bằng dấu=trongCargo.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 .
- Thêm một
panic!("...")cố ý ở đâu đó chạy tới được. Nạp. Xem monitor. - 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.tomlxem phầnrustflags— có 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ó. - 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 monitorcó 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-infochạy không cósudo, in đúngESP32-C6và 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 runnạ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 và 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 initvà 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.
- Board có xuất hiện trong
/devkhông? Sols /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. - Có device node nhưng permission denied? Kiểm
groups. Có trong/etc/groupmàgroupschưa hiện → chưa đăng nhập lại. - 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.
- 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
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
rustflagstrong.cargo/config.tomldù 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.
no_stdbỏ đi những gì so vớistd? Vì sao firmware này không dùng đượcprintln!, và thứ anh vừa dùng để in log khác nó ở chỗ nào?cargo runtrong 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?- Board có hai cổng USB-C. Cắm cổng nào thì
probe-rslà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
3V3trê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 buildvàcargo 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
- 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 =.