Files
ESP32S3_vt/reference/armor/main/armor_main.cpp
T
Horldsence fd71b83e3a 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 文档站,与本仓库无关。
2026-10-14 19:45:15 +08:00

216 lines
7.0 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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);
}