Files
ESP32S3_vt/reference/armor/main/air.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

223 lines
7.4 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.
#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;
}
}