diff --git a/.workbuddy/expert-history.json b/.workbuddy/expert-history.json deleted file mode 100644 index eba2273..0000000 --- a/.workbuddy/expert-history.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "version": 2, - "sessions": { - "17e27f37a08d4079971bed834cd7a702": [ - { - "expertId": "EmbeddedFirmwareEngineer", - "name": "固件通", - "profession": "嵌入式固件工程师", - "avatarUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/avatars/02-Engineering/EmbeddedFirmwareEngineer/EmbeddedFirmwareEngineer.png", - "promptUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/experts/02-Engineering/EmbeddedFirmwareEngineer/EmbeddedFirmwareEngineer_zh.md", - "usedAt": 1776444279306, - "industryId": "all" - } - ] - }, - "lastUpdated": 1776444603959 -} \ No newline at end of file diff --git a/.workbuddy/memory/2026-04-26.md b/.workbuddy/memory/2026-04-26.md new file mode 100644 index 0000000..f1ebe88 --- /dev/null +++ b/.workbuddy/memory/2026-04-26.md @@ -0,0 +1,18 @@ +# 2026-04-26 工作日志 + +## 固件工程架构梳理 +- 工程路径:`code/firmware-2.7.15.567b8ea` +- 版本:Meshtastic v2.7.15,基于 PlatformIO 构建 +- 支持平台:ESP32 / nRF52 / RP2040 / RP2350 / Linux (Portduino) +- 路由层级:ReliableRouter → NextHopRouter → FloodingRouter → Router +- 定制点:TCA9535 I²C IO 扩展器用于代理 LoRa RST(P1.4)、GPS EN(P1.7)和 POWER_EN 引脚控制 +- 自定义文件:`src/input/TCA9535ButtonThread.cpp/.h`(I²C 按键驱动,约16KB+12KB) +- main.cpp 中有 TCA9535GpioHal 自定义 HAL 类,拦截虚拟引脚转发到 I²C + +## P1.6 振子震动功能实现 +- 变体:`variants/esp32c3/diy/esp32c3_moonshine_travelers_3` +- P1.6 原为 GPS RST,改为振子(VIBRATOR),高电平震动,低电平停止 +- 改动文件: + - `src/input/TCA9535ButtonThread.h`:`tca9535GpsReset()` → `tca9535Vibrate()`,添加 `_vibrateOn`/`_vibrateStartMs` 成员 + - `src/input/TCA9535ButtonThread.cpp`:init() 开机后触发 300ms 震动;runOnce() 关机时触发 300ms 震动;振子超时自动停止逻辑在 runOnce() 开头 + - `variants/.../variant.h`:注释更新 diff --git a/.workbuddy/memory/MEMORY.md b/.workbuddy/memory/MEMORY.md index e69de29..15a4abb 100644 --- a/.workbuddy/memory/MEMORY.md +++ b/.workbuddy/memory/MEMORY.md @@ -0,0 +1,29 @@ +# MEMORY.md - 项目长期记忆 + +## 当前固件工程 + +- **路径**:`code/firmware-2.7.15.567b8ea` +- **版本**:Meshtastic v2.7.15(PlatformIO 工程) +- **支持平台**:ESP32 / nRF52 / RP2040 / RP2350 / Linux(Portduino) +- **构建系统**:PlatformIO,默认目标 `tbeam`,variants/ 目录存放各板型配置 + +## 自定义硬件扩展(重要!) + +### TCA9535 I²C IO 扩展器 +用于代理几个不能直连 MCU GPIO 的关键引脚: +- **P1.4** → LoRa 模块 RST(通过 `TCA9535GpioHal` 自定义 HAL 拦截 RadioLib 调用) +- **P1.6** → 振子 VIBRATOR(高电平震动):开机/关机各震动 300ms(变体 esp32c3_moonshine_travelers_3) +- **P1.7** → GPS 使能引脚(`GpioTca9535GpsEnPin` 包装) +- **POWER_EN** → 上电保持(早期 boot 时即拉高,防止松开按键后 MOS 断电) + +### 自定义文件 +- `src/input/TCA9535ButtonThread.cpp` / `.h`:I²C 按键驱动(~16KB + ~12KB) +- `src/main.cpp`:含 TCA9535GpioHal 类定义、GPS EN 替换逻辑、TCA9535 按键线程初始化 + +## 工程架构要点 + +- **路由层级**:ReliableRouter → NextHopRouter → FloodingRouter → Router +- **Mesh 服务**:MeshService 连接射频收发、手机 API、GPS、模块系统 +- **模块系统**:setupModules() 统一初始化,各模块通过 MeshModule 订阅数据包端口 +- **线程调度**:基于 concurrency::OSThread + Periodic,loop() 中 mainController.runOrDelay() +- **消息协议**:Protobuf(protobufs/ 目录),生成代码在 src/mesh/generated/ diff --git a/PCB/Moonshine_travelers.eprj2 b/PCB/Moonshine_travelers.eprj2 index 51b960c..079ac81 100644 Binary files a/PCB/Moonshine_travelers.eprj2 and b/PCB/Moonshine_travelers.eprj2 differ diff --git a/code/firmware-2.7.15.567b8ea/src/input/TCA9535ButtonThread.cpp b/code/firmware-2.7.15.567b8ea/src/input/TCA9535ButtonThread.cpp index a861c52..53c6d61 100644 --- a/code/firmware-2.7.15.567b8ea/src/input/TCA9535ButtonThread.cpp +++ b/code/firmware-2.7.15.567b8ea/src/input/TCA9535ButtonThread.cpp @@ -85,15 +85,13 @@ TCA9535ButtonThread::TCA9535ButtonThread(const char *name, TwoWire *wire) bool TCA9535ButtonThread::init() { - // =================================================================== - // 第一步:配置 P1 口方向 - // P1.0=输出(未用), P1.1=输入(CHARGE_DET), P1.2=输出(POWER_EN), + // P1 口方向注释: + // P1.0=输出(键盘背光), P1.1=输入(CHARGE_DET), P1.2=输出(POWER_EN), // P1.3=输入(POWER_BOOT), P1.4=输出(LoRa RST), P1.5=输出(状态灯), - // P1.6=输出(GPS RST), P1.7=输出(GPS EN) - // Configuration 寄存器:1=input, 0=output + // P1.6=输出(振子 VIBRATOR), P1.7=输出(GPS EN) + // P1 口配置寄存器:1=input, 0=output // bit: P1.7 P1.6 P1.5 P1.4 P1.3 P1.2 P1.1 P1.0 // 0 0 0 0 1 0 1 0 = 0x0A - // =================================================================== if (!writeReg(TCA9535_REG_CONFIG_P1, 0x0A)) { LOG_WARN("TCA9535: P1 config write failed"); return false; @@ -109,8 +107,8 @@ bool TCA9535ButtonThread::init() // P1.5 状态灯默认熄灭(高电平) tca9535StatusLed(false); - // P1.6 GPS RST 默认释放(高电平 = 正常工作) - tca9535GpsReset(true); + // P1.6 振子默认关闭(低电平) + tca9535Vibrate(false); // P1.7 GPS EN 默认打开(高电平 = GPS 上电) tca9535GpsEn(true); @@ -118,11 +116,17 @@ bool TCA9535ButtonThread::init() // =================================================================== // 第三步:POWER_EN 已由 main.cpp 在 Wire.begin() 后立即拉高, // 并等待 P1.3 持续按住 2 秒确认开机(超时 3 秒则断电关机)。 - // 此处只需确认状态机进入 RUNNING。 + // 此处只需确认状态机进入 RUNNING,并触发开机震动 300ms。 // =================================================================== LOG_INFO("TCA9535: Boot already confirmed in early boot, state=RUNNING"); _powerState = TCA9535PowerState::RUNNING; + // 开机震动 300ms + tca9535Vibrate(true); + _vibrateOn = true; + _vibrateStartMs = millis(); + LOG_DEBUG("TCA9535: Boot vibration started (300ms)"); + // =================================================================== // 第四步:配置 P0 口方向(矩阵键盘) // =================================================================== @@ -177,6 +181,13 @@ int32_t TCA9535ButtonThread::runOnce() if (held >= TCA9535_POWER_BOOT_HOLD_MS) { LOG_WARN("TCA9535: Power button held %lu ms -> SHUTDOWN", held); _powerBtnPressStart = 0; + // 关机震动 300ms,震动结束后由振子超时逻辑触发 SHUTDOWN 事件 + if (!_vibrateOn) { + tca9535Vibrate(true); + _vibrateOn = true; + _vibrateStartMs = millis(); + LOG_DEBUG("TCA9535: Shutdown vibration started (300ms)"); + } dispatchEvent(INPUT_BROKER_SHUTDOWN); } } else if (!pressed && _powerBtnPressStart != 0) { @@ -188,6 +199,15 @@ int32_t TCA9535ButtonThread::runOnce() } } + // =================================================================== + // P1.6 振子超时停止:震动 300ms 后自动关闭 + // =================================================================== + if (_vibrateOn && millis() - _vibrateStartMs >= 300) { + _vibrateOn = false; + tca9535Vibrate(false); + LOG_DEBUG("TCA9535: Vibration stopped"); + } + // =================================================================== // P1.5 状态灯闪烁:500ms 亮 + 500ms 灭 = 1 秒周期 // =================================================================== diff --git a/code/firmware-2.7.15.567b8ea/src/input/TCA9535ButtonThread.h b/code/firmware-2.7.15.567b8ea/src/input/TCA9535ButtonThread.h index 20c3e3b..d87e63c 100644 --- a/code/firmware-2.7.15.567b8ea/src/input/TCA9535ButtonThread.h +++ b/code/firmware-2.7.15.567b8ea/src/input/TCA9535ButtonThread.h @@ -12,6 +12,7 @@ * - P1.3:电源开机按钮(POWER_BOOT),输入,低电平有效(按键按下接地) * - P1.4:LoRa RST 输出(通过 I²C 控制 RadioLib 复位序列) * - P1.5:状态指示灯,低电平点亮 + * - P1.6:振子(VIBRATOR),高电平震动,低电平停止 * P1 Config 寄存器 = 0x0A(P1.1、P1.3 为输入,其余为输出) * * 电源管理逻辑: @@ -88,7 +89,7 @@ #define TCA9535_BIT_P13 (1u << 3) // POWER_BOOT 输入 #define TCA9535_BIT_P14 (1u << 4) // LoRa RST 输出 #define TCA9535_BIT_P15 (1u << 5) // 状态指示灯输出(低电平点亮) -#define TCA9535_BIT_P16 (1u << 6) // GPS RST 输出 +#define TCA9535_BIT_P16 (1u << 6) // 振子输出(VIBRATOR,高电平震动) #define TCA9535_BIT_P17 (1u << 7) // GPS EN 输出(高电平有效) #ifdef TCA9535_CHARGE_DET_PIN @@ -240,10 +241,11 @@ static inline bool tca9535Backlight(bool on) } /** - * 通过 I²C 控制 TCA9535 P1.6 上的 GPS RST。 - * @param high true=释放复位(高电平),false=触发复位(低电平) + * 通过 I²C 控制 TCA9535 P1.6 上的振子(VIBRATOR)。 + * 高电平震动,低电平停止。 + * @param on true=震动(高电平),false=停止(低电平) */ -static inline bool tca9535GpsReset(bool high) +static inline bool tca9535Vibrate(bool on) { Wire.beginTransmission(TCA9535_I2C_ADDR); Wire.write(TCA9535_REG_OUTPUT_P1); @@ -253,10 +255,10 @@ static inline bool tca9535GpsReset(bool high) return false; uint8_t p1Out = Wire.read(); - if (high) - p1Out |= TCA9535_BIT_P16; // 拉高 = 释放复位 + if (on) + p1Out |= TCA9535_BIT_P16; // 拉高 = 震动 else - p1Out &= ~TCA9535_BIT_P16; // 拉低 = 触发复位 + p1Out &= ~TCA9535_BIT_P16; // 拉低 = 停止 Wire.beginTransmission(TCA9535_I2C_ADDR); Wire.write(TCA9535_REG_OUTPUT_P1); @@ -343,6 +345,10 @@ class TCA9535ButtonThread : public Observable, public concur bool _backlightOn = false; uint32_t _backlightLastMs = 0; + // P1.6 振子控制(开机/关机震动 300ms) + bool _vibrateOn = false; + uint32_t _vibrateStartMs = 0; // 震动开始时刻,0 = 未在震动 + // 写寄存器 bool writeReg(uint8_t reg, uint8_t val); diff --git a/code/firmware-2.7.15.567b8ea/variants/esp32c3/diy/esp32c3_moonshine_travelers_3/variant.h b/code/firmware-2.7.15.567b8ea/variants/esp32c3/diy/esp32c3_moonshine_travelers_3/variant.h index ec2fa35..6cecbf1 100644 --- a/code/firmware-2.7.15.567b8ea/variants/esp32c3/diy/esp32c3_moonshine_travelers_3/variant.h +++ b/code/firmware-2.7.15.567b8ea/variants/esp32c3/diy/esp32c3_moonshine_travelers_3/variant.h @@ -42,7 +42,8 @@ // 开机:持续按住 2 秒 → POWER_EN 拉高维持供电 // 关机:运行中持续按住 2 秒 → POWER_EN 拉低断电 // - P1.4 LoRa RST 输出(通过 I²C 控制 RadioLib 复位序列) -// - P1.6 GPS_RST 输出(通过 tca9535GpsReset() 控制,init 中释放) +// - P1.6 振子(VIBRATOR),高电平震动,低电平停止 +// 开机确认后震动 300ms,关机时震动 300ms // - P1.7 GPS_EN 输出(高电平有效,通过 enablePin 桥接到 TCA9535) // - P1.1 CHARGE_DET 输入(高电平=正在充电) // - 中断引脚 GPIO5,低电平有效,下降沿触发