reference: 早先那版 C 写的装甲板固件(ESP-IDF 工程)

`armor/` 是它的 Rust 改写版,这一份留着当参考:ADC 阈值的 2000 就是从它标定出来的
(`.env.example` 里那句 "same as the reference firmware" 指的就是它),而且它的 README
记着实测设备与 MAC,换硬件时是有用的现场记录。

只收了 20 个源文件。它自带的 `.gitignore` 已经排掉 `build/`(211 MB 的 ESP-IDF 构建产物)
与生成的 `sdkconfig`,所以仓库里不会多出这些东西。

两处顺带处理:
- `reference/armor/.git` 是一个**空的**仓库壳(`git init` 过,零提交、零 remote、零 ref),
  而外层 git 正因为它的存在而拒绝索引 `reference/` 下的任何文件
  (`error: 'reference/armor/' does not have a commit checked out`)。它被挪到
  `desktop/.dev/backup/reference-armor-git`(可逆,且该目录已被忽略),不是删除。
- 它的 README 指着 `../armor-platformio-backup/`,那份备份并不存在。改成明说"没有进仓库",
  免得以后有人照着找一个不存在的目录。

另外把 `/docs/` 加进 .gitignore:那是工作区里另起的一个 Astro 文档站,与本仓库无关。
This commit is contained in:
2026-10-14 19:45:15 +08:00
parent a30eb3200a
commit fd71b83e3a
22 changed files with 1886 additions and 0 deletions
+215
View File
@@ -0,0 +1,215 @@
/*
* ESP-IDF entry point for the "armor" hit-detection node (ESP32-C3).
*
* This is the ESP-IDF counterpart of the original Arduino `main.cpp`:
* setup() -> app_main()
* loop() -> hit_task() (only ran Hit::onLoop())
* loop2() -> loop2_task() (button, ESP-NOW and serial bridging)
* Serial -> USB Serial/JTAG console + driver (CDC protocol)
* Serial0 -> UART0 on GPIO20/21 (DBUS bridge)
*/
#include <cstdio>
#include <cstring>
#include "driver/uart.h"
#include "driver/usb_serial_jtag.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "nvs_flash.h"
#include "air.hpp"
#include "armor_time.hpp"
#include "button.hpp"
#include "hit.hpp"
#include "storage.hpp"
namespace {
constexpr const char *TAG = "armor";
constexpr uint32_t TIMEOUT_MS = 100;
// GPIO9 is the on-board BOOT button of the ESP32-C3 DevKitM-1, active low.
// Original: `OneButton button(9, true);`
Button button(9, true);
volatile bool g_click = false;
volatile bool g_long_click = false;
volatile uint32_t g_last_cdc_receive_ms = 0;
void handleClick() {
g_click = true;
}
void handleLongPressStart() {
g_long_click = true;
}
// --------------------------------------------------------------- UART0 -----
// Original: `Serial0.begin(115200, SERIAL_8N1, 20, 21);` (rx=20, tx=21)
constexpr uart_port_t DBUS_UART = UART_NUM_0;
constexpr int DBUS_UART_RX = 20;
constexpr int DBUS_UART_TX = 21;
void serial0_begin() {
uart_config_t cfg = {};
cfg.baud_rate = 115200;
cfg.data_bits = UART_DATA_8_BITS;
cfg.parity = UART_PARITY_DISABLE;
cfg.stop_bits = UART_STOP_BITS_1;
cfg.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
cfg.source_clk = UART_SCLK_DEFAULT;
ESP_ERROR_CHECK(uart_driver_install(DBUS_UART, 256, 256, 0, nullptr, 0));
ESP_ERROR_CHECK(uart_param_config(DBUS_UART, &cfg));
ESP_ERROR_CHECK(uart_set_pin(DBUS_UART, DBUS_UART_TX, DBUS_UART_RX, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
}
// ----------------------------------------------------------- USB CDC -------
// Replaces Arduino's `serialEvent()`: 10-byte DBUS frames arrive over the USB
// CDC port, and the original truncated/committed them in 10-byte units.
void cdc_task(void *) {
uint8_t chunk[32];
uint8_t frame[10];
size_t frame_len = 0;
while (true) {
const int n = usb_serial_jtag_read_bytes(chunk, sizeof(chunk), pdMS_TO_TICKS(20));
for (int i = 0; i < n; i++) {
frame[frame_len++] = chunk[i];
if (frame_len == sizeof(frame)) {
Air::dbusSet(frame, sizeof(frame));
g_last_cdc_receive_ms = millis();
frame_len = 0;
}
}
}
}
// ------------------------------------------------------------ business -----
void handle_air() {
static Air::packet_t peer_packet_last;
// 处理收到的数据包
// 颜色变化
if (Air::peer_packet.color != peer_packet_last.color && Air::peer_packet.color != 0) {
Hit::color = Air::peer_packet.color;
Storage::putUInt("color", Hit::color);
}
// 击打次数变化
if (Air::peer_packet.hit_cnt != peer_packet_last.hit_cnt) {
Hit::last_hit_ms = millis();
}
peer_packet_last = Air::peer_packet;
// 发送自己的数据包
Air::my_packet.color = Hit::color;
Air::my_packet.hit_cnt = Hit::hit_cnt;
if (millis() - g_last_cdc_receive_ms > TIMEOUT_MS) { // 超时数据清零
Air::dbusClear();
}
}
void handle_serial() {
// 串口发送 DBUS 数据
if (millis() - Air::last_air_ms > TIMEOUT_MS) { // 超时数据清零
memset(Air::peer_packet.dbus, 0, sizeof(Air::peer_packet.dbus));
}
uart_write_bytes(DBUS_UART, Air::peer_packet.dbus, sizeof(Air::peer_packet.dbus));
uart_wait_tx_done(DBUS_UART, pdMS_TO_TICKS(20)); // Serial0.flush()
// CDC发送:颜色、击打次数、信号强度(tx, rx)、距离最后一次收到消息的毫秒数
const uint32_t latency_ms = millis() - Air::last_air_ms;
printf("%d,%d,%d,%d,%d\n",
static_cast<int>(Air::my_packet.color),
static_cast<int>(Air::peer_packet.hit_cnt),
static_cast<int>(Air::peer_packet.rssi),
static_cast<int>(Air::my_packet.rssi),
static_cast<int>(latency_ms));
}
// ------------------------------------------------------------- tasks -------
// Equivalent of the original Arduino `loop2()`.
void loop2_task(void *) {
while (true) {
button.tick();
Air::onLoop();
if (!Air::is_pairing) { // 不在配对中
if (g_long_click) { // 长按配对
Air::entryPairing();
Hit::detect_hit = false;
} else if (g_click) { // 短按切换颜色
Hit::color = (Hit::color == Hit::RED) ? Hit::BLUE : Hit::RED;
Storage::putUInt("color", Hit::color);
} else { // 正常运行业务逻辑
handle_air();
handle_serial();
}
} else { // 在配对中
if (g_click) { // 短按保存配对
Air::savePairing();
Hit::color = Hit::RED;
Storage::putUInt("color", Hit::color);
Hit::detect_hit = true;
} else {
Hit::color = Air::pairing_found ? Hit::GREEN : Hit::YELLOW;
}
}
g_click = false;
g_long_click = false;
vTaskDelay(pdMS_TO_TICKS(10));
}
}
// Equivalent of the original Arduino `loop()`.
void hit_task(void *) {
while (true) {
Hit::onLoop();
}
}
} // namespace
extern "C" void app_main(void) {
// 初始化 NVS(原 Preferences 底层就是 NVS)
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_ERROR_CHECK(err);
ESP_ERROR_CHECK(Storage::begin("armor"));
// With CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG the console initialisation has
// already installed the USB Serial/JTAG driver, so only install it when
// that did not happen; reading raw CDC bytes then works either way.
if (!usb_serial_jtag_is_driver_installed()) {
usb_serial_jtag_driver_config_t usb_cfg = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT();
usb_cfg.rx_buffer_size = 256;
if (usb_serial_jtag_driver_install(&usb_cfg) != ESP_OK) {
ESP_LOGW(TAG, "USB Serial/JTAG driver unavailable, CDC protocol disabled");
}
}
serial0_begin();
// 绑定回调函数
button.attachClick(handleClick);
button.attachLongPressStart(handleLongPressStart);
button.setClickMs(50); // 短按时间(毫秒)
button.setPressMs(500); // 长按时间(毫秒)
Hit::begin();
Air::begin();
ESP_LOGI(TAG, "armor node started");
xTaskCreate(cdc_task, "cdc", 3072, nullptr, 5, nullptr);
xTaskCreate(loop2_task, "loop2", 8192, nullptr, 5, nullptr);
xTaskCreate(hit_task, "hit", 4096, nullptr, 5, nullptr);
}