feat(sys): 添加AES加密和电机OTA中继功能模块
添加了AES128加密算法实现和电机OTA中继相关文件,包括: - ./sys/aes128.h 和 ./sys/aes128.cpp - AES128加密算法头文件和实现 - ./sys/motor_ota_relay.h 和 ./sys/motor_ota_relay.cpp - 电机OTA中继功能 - 更新了项目配置文件包含这些新模块
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* @file motor_state.cpp
|
||||
* @brief 多电机状态表实现 — CAN STATUS 帧识别、内存缓存、SPI 推送序列化。
|
||||
*
|
||||
* 所有函数仅在主循环上下文调用(taskCanRxParse / taskHeartbeat / pushStep),
|
||||
* 无 ISR 竞态,不需要临界区保护。
|
||||
*/
|
||||
|
||||
#include "device/motor_state.h"
|
||||
#include "device/spi/app_spi_command_defs.h"
|
||||
#include "main.h"
|
||||
#include <cstring>
|
||||
|
||||
/* ── 全局状态表实例 ── */
|
||||
static MotorStateTable s_motorTable = {};
|
||||
|
||||
namespace MotorState {
|
||||
|
||||
MotorStateTable &table()
|
||||
{
|
||||
return s_motorTable;
|
||||
}
|
||||
|
||||
void onCanFrame(uint8_t channel, uint32_t id, const uint8_t *data, uint8_t len)
|
||||
{
|
||||
/* BOOT 心跳 0x103(固定 ID,不带节点号):电机在 Bootloader IDLE 每 ~10ms 发一次。
|
||||
* 记录最近收到时刻与通道,供网页显示独立 BOOT 状态(不归属任何 Nx)。
|
||||
* 注意:必须在 STATUS 掩码过滤之前拦截,否则 0x103 会被下面的掩码判断丢弃。 */
|
||||
if (id == kOtaBootHeartbeatId) {
|
||||
s_motorTable.boot_hb_sec = g_app_uptime_sec;
|
||||
s_motorTable.boot_channel = channel;
|
||||
s_motorTable.boot_active = 1U;
|
||||
/* 0x103 仅更新 boot_active 状态,不再串口打印(高频心跳会刷屏)。 */
|
||||
return;
|
||||
}
|
||||
|
||||
/* 掩码匹配 STATUS ID: 0x000N0100 */
|
||||
if ((id & kMotorStatusIdMask) != kMotorStatusIdMatch) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t node = static_cast<uint8_t>((id >> 16) & 0x0FU);
|
||||
if (node < 1U || node > kMotorMaxNodes) {
|
||||
return;
|
||||
}
|
||||
|
||||
MotorSlot &slot = s_motorTable.slots[node - 1U];
|
||||
|
||||
slot.node_id = node;
|
||||
slot.channel = channel;
|
||||
slot.online = 1U;
|
||||
slot.dlc = (len <= kMotorStatusDlc) ? len : kMotorStatusDlc;
|
||||
slot.rx_count++;
|
||||
slot.last_rx_sec = g_app_uptime_sec;
|
||||
slot.seq = ++s_motorTable.global_seq;
|
||||
|
||||
/* 拷贝原始 payload */
|
||||
std::memcpy(slot.data, data, slot.dlc);
|
||||
if (slot.dlc < kMotorStatusDlc) {
|
||||
std::memset(&slot.data[slot.dlc], 0, kMotorStatusDlc - slot.dlc);
|
||||
}
|
||||
}
|
||||
|
||||
void checkOffline()
|
||||
{
|
||||
const uint32_t now = g_app_uptime_sec;
|
||||
uint8_t active = 0U;
|
||||
|
||||
for (uint8_t i = 0U; i < kMotorMaxNodes; ++i) {
|
||||
MotorSlot &slot = s_motorTable.slots[i];
|
||||
if (slot.node_id == 0U) {
|
||||
continue; /* 空槽 */
|
||||
}
|
||||
if ((now - slot.last_rx_sec) >= kMotorOfflineSec) {
|
||||
slot.online = 0U;
|
||||
}
|
||||
if (slot.online != 0U) {
|
||||
active++;
|
||||
}
|
||||
}
|
||||
s_motorTable.active_count = active;
|
||||
|
||||
/* BOOT 心跳超时:>kBootHeartbeatTimeoutSec 无 0x103 → 电机已退出 BOOT。 */
|
||||
if (s_motorTable.boot_active != 0U &&
|
||||
(now - s_motorTable.boot_hb_sec) >= kBootHeartbeatTimeoutSec) {
|
||||
s_motorTable.boot_active = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t buildPayload(uint8_t page, uint8_t *buf, uint16_t maxLen)
|
||||
{
|
||||
if (page >= kMotorTotalPages || buf == nullptr) {
|
||||
return 0U;
|
||||
}
|
||||
|
||||
/* 需要的最小空间: header + 至少 1 entry */
|
||||
const uint16_t needed = kMotorPageHeaderLen + kMotorEntrySize;
|
||||
if (maxLen < needed) {
|
||||
return 0U;
|
||||
}
|
||||
|
||||
const uint8_t startIdx = static_cast<uint8_t>(page * kMotorEntriesPerPage);
|
||||
uint8_t count = 0U;
|
||||
|
||||
/* Header (4B) */
|
||||
buf[0] = page;
|
||||
buf[1] = kMotorTotalPages;
|
||||
buf[2] = 0U; /* entry_count, 稍后回填 */
|
||||
/* bit[6:0]=在线电机数,bit7=BOOT 心跳(0x103)活跃标志(供网页显示独立 BOOT 状态)。
|
||||
* active_count 上限 9,bit7 空闲可复用,旧解析方读到会多算 128,故 ESP32 侧需掩 0x7F。 */
|
||||
buf[3] = static_cast<uint8_t>((s_motorTable.active_count & 0x7FU)
|
||||
| (s_motorTable.boot_active ? kMotorActiveBootBit : 0U));
|
||||
|
||||
uint16_t offset = kMotorPageHeaderLen;
|
||||
|
||||
for (uint8_t i = 0U; i < kMotorEntriesPerPage; ++i) {
|
||||
const uint8_t slotIdx = static_cast<uint8_t>(startIdx + i);
|
||||
if (slotIdx >= kMotorMaxNodes) {
|
||||
break;
|
||||
}
|
||||
|
||||
const MotorSlot &slot = s_motorTable.slots[slotIdx];
|
||||
if (slot.node_id == 0U) {
|
||||
continue; /* 跳过空槽 */
|
||||
}
|
||||
|
||||
/* 检查剩余空间 */
|
||||
if (offset + kMotorEntrySize > maxLen) {
|
||||
break;
|
||||
}
|
||||
|
||||
buf[offset + 0U] = slot.node_id;
|
||||
buf[offset + 1U] = slot.channel;
|
||||
buf[offset + 2U] = slot.online;
|
||||
buf[offset + 3U] = slot.dlc;
|
||||
writeU32Le(&buf[offset + 4U], slot.rx_count);
|
||||
writeU32Le(&buf[offset + 8U], slot.seq);
|
||||
std::memcpy(&buf[offset + 12U], slot.data, kMotorStatusDlc);
|
||||
|
||||
offset += kMotorEntrySize;
|
||||
count++;
|
||||
}
|
||||
|
||||
buf[2] = count;
|
||||
return offset;
|
||||
}
|
||||
|
||||
} // namespace MotorState
|
||||
Reference in New Issue
Block a user