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
+222
View File
@@ -0,0 +1,222 @@
#include "air.hpp"
#include <cstring>
#include "armor_time.hpp"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_wifi.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "storage.hpp"
namespace {
constexpr const char *TAG = "air";
portMUX_TYPE s_dbus_lock = portMUX_INITIALIZER_UNLOCKED;
// ESP-NOW payloads are 802.11 frames; the promiscuous callback is used to keep
// track of the peer's signal strength.
typedef struct {
unsigned frame_ctrl : 16;
unsigned duration_id : 16;
uint8_t addr1[6]; /* receiver address */
uint8_t addr2[6]; /* sender address */
uint8_t addr3[6]; /* filtering address */
unsigned sequence_ctrl : 16;
uint8_t addr4[6]; /* optional */
} wifi_ieee80211_mac_hdr_t;
typedef struct {
wifi_ieee80211_mac_hdr_t hdr;
uint8_t payload[0]; /* network data ended with 4 bytes csum (CRC32) */
} wifi_ieee80211_packet_t;
} // namespace
// public
Air::packet_t Air::my_packet, Air::peer_packet;
volatile uint32_t Air::last_air_ms = 0;
volatile bool Air::is_pairing = false;
volatile bool Air::pairing_found = false;
// private
const uint8_t Air::empty_mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
const uint8_t Air::pairing_data[4] = {0x11, 0x45, 0x14, 0x00};
esp_now_peer_info_t Air::broadcast_peer;
esp_now_peer_info_t Air::peer;
volatile int8_t Air::pairing_best_rssi = -128;
void Air::begin() {
// ESP-NOW needs the WiFi stack up on the station interface.
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
wifi_init_config_t wifi_cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&wifi_cfg));
// The peer MAC lives in our own NVS namespace; don't let the WiFi driver
// write extra calibration data it does not need here.
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_start());
// 使用 WiFi 14 信道
wifi_country_t country = {};
memcpy(country.cc, "JP", sizeof(country.cc));
country.schan = 1;
country.nchan = 14;
country.policy = WIFI_COUNTRY_POLICY_MANUAL;
ESP_ERROR_CHECK(esp_wifi_set_country(&country));
ESP_ERROR_CHECK(esp_wifi_set_channel(14, WIFI_SECOND_CHAN_NONE));
// 20dBm 功率(84 = 21dBm;C3 上限为 20dBm,失败时退回 80)
esp_err_t power_err = esp_wifi_set_max_tx_power(84);
if (power_err != ESP_OK) {
ESP_LOGW(TAG, "set_max_tx_power(84) failed: %s, retrying with 80",
esp_err_to_name(power_err));
power_err = esp_wifi_set_max_tx_power(80);
if (power_err != ESP_OK) {
ESP_LOGW(TAG, "set_max_tx_power(80) failed: %s", esp_err_to_name(power_err));
}
}
// 启用混杂模式,以获取发送方 rssi
wifi_promiscuous_filter_t filter = {};
filter.filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT;
ESP_ERROR_CHECK(esp_wifi_set_promiscuous_filter(&filter));
ESP_ERROR_CHECK(esp_wifi_set_promiscuous_rx_cb(promiscuous_rx_cb));
ESP_ERROR_CHECK(esp_wifi_set_promiscuous(true));
// 初始化 ESP-NOW
ESP_ERROR_CHECK(esp_now_init());
ESP_ERROR_CHECK(esp_now_register_recv_cb(onRecv));
// 添加广播 peer
memset(&broadcast_peer, 0, sizeof(broadcast_peer));
memset(broadcast_peer.peer_addr, 0xFF, sizeof(broadcast_peer.peer_addr));
broadcast_peer.channel = 0;
broadcast_peer.ifidx = WIFI_IF_STA;
broadcast_peer.encrypt = false;
ESP_ERROR_CHECK(esp_now_add_peer(&broadcast_peer));
memset(&peer, 0, sizeof(peer));
peer.channel = 0;
peer.ifidx = WIFI_IF_STA;
peer.encrypt = false;
if (Storage::hasKey("peer_mac")) {
Storage::getBytes("peer_mac", peer.peer_addr, sizeof(peer.peer_addr));
if (memcmp(peer.peer_addr, empty_mac, sizeof(empty_mac)) == 0) {
ESP_LOGI(TAG, "stored peer MAC is empty, staying unpaired");
} else {
esp_err_t err = esp_now_add_peer(&peer);
if (err != ESP_OK) {
ESP_LOGW(TAG, "esp_now_add_peer(stored) failed: %s", esp_err_to_name(err));
}
}
} else {
memset(peer.peer_addr, 0x00, sizeof(peer.peer_addr));
}
}
void Air::entryPairing() {
// 清除之前的配对
esp_now_del_peer(peer.peer_addr);
is_pairing = true;
pairing_found = false;
// 用于只配对最强的信号
my_packet.rssi = -128;
pairing_best_rssi = -128;
}
void Air::savePairing() {
if (pairing_found) {
// 添加 peer
esp_err_t err = esp_now_add_peer(&peer);
if (err != ESP_OK) {
ESP_LOGW(TAG, "esp_now_add_peer(pairing) failed: %s", esp_err_to_name(err));
}
} else {
// 配对失败,清空设备 mac
memset(peer.peer_addr, 0x00, sizeof(peer.peer_addr));
}
// 写入 flash
Storage::putBytes("peer_mac", peer.peer_addr, sizeof(peer.peer_addr));
is_pairing = false;
}
void Air::onLoop() {
if (is_pairing) {
// 发送配对请求
esp_now_send(broadcast_peer.peer_addr, pairing_data, sizeof(pairing_data));
} else if (memcmp(peer.peer_addr, empty_mac, sizeof(empty_mac)) != 0) {
// 已有配对设备
esp_now_send(peer.peer_addr, reinterpret_cast<const uint8_t *>(&my_packet), sizeof(packet_t));
}
}
void Air::dbusSet(const uint8_t *src, size_t len) {
portENTER_CRITICAL(&s_dbus_lock);
memcpy(my_packet.dbus, src, len);
portEXIT_CRITICAL(&s_dbus_lock);
}
void Air::dbusClear() {
portENTER_CRITICAL(&s_dbus_lock);
memset(my_packet.dbus, 0, sizeof(my_packet.dbus));
portEXIT_CRITICAL(&s_dbus_lock);
}
void Air::onRecv(const esp_now_recv_info_t *info, const uint8_t *data, int data_len) {
if (info == nullptr || info->src_addr == nullptr || data == nullptr) {
return;
}
// The original relied on the promiscuous callback to have recorded the RSSI
// of this frame beforehand; rx_ctrl carries the same value synchronously
// with the packet we are handling.
if (info->rx_ctrl != nullptr) {
my_packet.rssi = info->rx_ctrl->rssi;
}
if (is_pairing) {
// 配对 magic 正确
if (data_len == static_cast<int>(sizeof(pairing_data)) &&
memcmp(pairing_data, data, sizeof(pairing_data)) == 0) {
// 只配对最强的信号
if (my_packet.rssi > pairing_best_rssi) {
pairing_best_rssi = my_packet.rssi;
memcpy(peer.peer_addr, info->src_addr, sizeof(peer.peer_addr));
pairing_found = true;
}
}
} else if (memcmp(peer.peer_addr, info->src_addr, sizeof(peer.peer_addr)) == 0) {
// 是从配对设备发过来的,且数据长度正确
if (data_len == static_cast<int>(sizeof(packet_t))) {
memcpy(&peer_packet, data, sizeof(packet_t));
last_air_ms = millis();
}
}
}
void Air::promiscuous_rx_cb(void *buf, wifi_promiscuous_pkt_type_t type) {
if (type != WIFI_PKT_MGMT) {
return;
}
const auto *ppkt = static_cast<const wifi_promiscuous_pkt_t *>(buf);
const auto *ipkt = reinterpret_cast<const wifi_ieee80211_packet_t *>(ppkt->payload);
const wifi_ieee80211_mac_hdr_t *hdr = &ipkt->hdr;
if (is_pairing) {
// 配对模式
my_packet.rssi = ppkt->rx_ctrl.rssi;
} else if (memcmp(hdr->addr2, peer.peer_addr, sizeof(peer.peer_addr)) == 0) {
my_packet.rssi = ppkt->rx_ctrl.rssi;
}
}