zhangyisong 27ae21bbea Add UID management, LED strip debug, and MCUboot signing updates
- Add UID encoding/decoding with flash storage and CRC16 validation
- Add write UID (0x07) and LED strip color (0x08) protocol commands
- Add debug LED strip configuration option
- Switch MCUboot signing from RSA-2048 to ECDSA-P256
- Add SMP serial client for firmware upload over UART
- Add firmware version output on boot
- Update upgrade documentation with ECDSA key generation steps
2026-08-20 21:16:30 +08:00

152 lines
4.9 KiB
C++

#ifndef __THER_COM_HPP__
#define __THER_COM_HPP__
#include "led.hpp"
#include <etl/delegate.h>
#include <etl/signal.h>
#include <led_strip_indicator/led_strip_indicator.hpp>
#include <stdio.h>
#include <uart_com/simple_protocal.hpp>
#include <uid.hpp>
#include <zephyr/app_version.h>
#include <zephyr/drivers/hwinfo.h>
#include <zephyr/drivers/sensor.h>
#include <zpp/driver.hpp>
#include <zpp/error.hpp>
#ifdef CONFIG_APP_DFU
#include <zephyr/sys/reboot.h>
#endif
namespace ther {
class Com {
public:
enum HostStatus : uint8_t { STANDBY, RUNNING, PAUSE, ERROR };
using RunningStateSignal = etl::signal<void(HostStatus), 10>;
static auto Init() -> zpp::error {
s_proto->SetRxCallbackTable(kRxCallbackTable);
// 优先使用生产写入的自定义 UID,否则回退芯片 UID
Uid::Init();
s_id_buff = Uid::Get();
return zpp::ok();
}
static auto SendTemp(sensor_value val) -> void {
const uint8_t temp_data[] = {(uint8_t)val.val1, (uint8_t)val.val2};
s_proto->Send(R_TEMP, temp_data);
}
static auto AddRunningState(RunningStateSignal::slot_type slot) -> bool {
return s_running_state_sig.connect(slot);
}
#ifdef CONFIG_APP_DEBUG_LED_STRIP
/// 调试灯带控制(0x08):仅调试固件(CONFIG_APP_DEBUG_LED_STRIP=y)支持,
/// 生产固件不编译此命令。订阅后收到命令回调 (r, g, b)。
using LedColorSignal = etl::signal<void(uint8_t, uint8_t, uint8_t), 4>;
static auto AddLedColor(LedColorSignal::slot_type slot) -> bool {
return s_led_color_sig.connect(slot);
}
#endif
private:
enum Addr : uint8_t {
R_TEMP = 0,
R_GET_ID,
W_RUNNING_STATE,
W_UID = 0x07,
R_VERSION = 0x06,
W_LED = 0x08,
};
inline static RunningStateSignal s_running_state_sig{};
#ifdef CONFIG_APP_DEBUG_LED_STRIP
inline static LedColorSignal s_led_color_sig{};
#endif
static auto RunningState(uart_com::DataType data) -> void {
if (data.size() != 1) {
return;
}
s_running_state_sig(HostStatus(data[0]));
}
static auto GetId(uart_com::DataType data) -> void {
printk("handle get id");
s_proto->Send(R_GET_ID, uart_com::DataType(s_id_buff));
};
static auto GetVersion(uart_com::DataType data) -> void {
// Send version string: major.minor.patchlevel
static char ver_buf[16];
snprintf(ver_buf, sizeof(ver_buf), "%d.%d.%d", APP_VERSION_MAJOR,
APP_VERSION_MINOR, APP_PATCHLEVEL);
printk("[com] GetVersion: %s\n", ver_buf);
s_proto->Send(R_VERSION, uart_com::DataType((const uint8_t *)ver_buf,
strlen(ver_buf)));
};
/// 写 UID(0x07):data 为 10 字节生产 UID → 写入 flash;data 为空 →
/// 清除记录(恢复芯片 UID)。 回 ACK:0x00=成功 0x01=写入失败 0x02=长度错误
static auto WriteUid(uart_com::DataType data) -> void {
uint8_t status = 0x02;
if (data.size() == Uid::kUidLen) {
status = (Uid::Write(data) == 0) ? 0x00 : 0x01;
} else if (data.empty()) {
status = (Uid::Write({}) == 0) ? 0x00 : 0x01;
}
if (status == 0x00) {
// 立即生效:后续 GET_ID(0x01)返回新 UID,无需等重启
s_id_buff = Uid::Get();
}
printk("[com] WriteUid len=%zu status=0x%02x\n", data.size(), status);
s_proto->Send(W_UID, uart_com::DataType(&status, 1));
};
#ifdef CONFIG_APP_DEBUG_LED_STRIP
/// 调试灯带(0x08):data = [R][G][B] → 灯带整体改色,用于开发/产线检查
/// 灯光颜色是否满足需求。(0,0,0) 即熄灭。生产固件不含此命令。
static auto SetLed(uart_com::DataType data) -> void {
if (data.size() != 3) {
printk("[com] SetLed: expect 3 bytes (R G B), got %zu\n", data.size());
return;
}
printk("[com] SetLed R=%u G=%u B=%u\n", data[0], data[1], data[2]);
s_led_color_sig(data[0], data[1], data[2]);
}
#endif
#ifdef CONFIG_APP_DFU
/// 升级命令(0xFF):data[0]=0x01 进入升级模式,data[0]=0x02 查询状态
static auto DfuCommand(uart_com::DataType data) -> void {
if (data.size() < 1)
return;
switch (data[0]) {
case 0x01:
printk("[com] entering DFU mode, rebooting...\n");
k_msleep(100);
sys_reboot(SYS_REBOOT_COLD);
break;
case 0x02: {
uint8_t status = 0x00;
s_proto->Send(0xFF, uart_com::DataType(&status, 1));
break;
}
}
}
#endif
constexpr static std::pair<const uint8_t,
uart_com::SimpleProtocal::CallbackType>
kRxCallbackTable[] = {
{R_GET_ID, GetId}, {W_RUNNING_STATE, RunningState},
{R_VERSION, GetVersion}, {W_UID, WriteUid},
#ifdef CONFIG_APP_DEBUG_LED_STRIP
{W_LED, SetLed},
#endif
#ifdef CONFIG_APP_DFU
{0xFF, DfuCommand},
#endif
};
inline static auto s_proto = (uart_com::SimpleProtocal *)DEVICE_DT_GET(
DT_COMPAT_GET_ANY_STATUS_OKAY(uart_com_simple_protocal));
inline static etl::span<const uint8_t> s_id_buff;
inline static auto s_heating_state = false;
};
} // namespace ther
#endif