跳到主要内容

图 10 - 板级架构类图

110 个板子是怎么组织在一起的?哪些是父类哪些是子类?

如果上方图无法显示,点这里看 PNG 版本

4 层继承结构

              Board (抽象)

┌──────────┼──────────┐
│ │ │
WifiBoard Ml307Board DualNetworkBoard
▲ ▲
┌───┴───┐ │
具体板子 (内部持有上面两个)

第 1 层:Board 抽象基类

定义"所有板子都得回答的问题":

  • GetAudioCodec() — 你用什么 codec?
  • GetDisplay() — 你有屏吗?什么屏?
  • GetLed() — 你有指示灯吗?什么样的?
  • GetCamera() — 有摄像头吗?
  • GetNetwork() — 你的网络栈?

凡是**纯虚函数(带 )的必须实现*,其它有默认实现可省。

第 2 层:网络栈维度的派生

  • WifiBoard:Wi-Fi 板的网络管理(Wi-Fi 凭证 / 配网 / 重连)
  • Ml307Board:4G 板的 Modem 管理(AT 命令 / PPP / SIM 状态)
  • DualNetworkBoard:双栈板的切换逻辑

第 3 层:具体板子

继承前面任一个,根据板子的硬件做最终实现:

  • CompactWifiBoard(面包板)继承 WifiBoard
  • EspBox3Board(Espressif Box 3)继承 WifiBoard
  • CompactMl307Board(4G 面包板)继承 DualNetworkBoard

单例工厂

#define DECLARE_BOARD(BoardCls) \
void* create_board() { return new BoardCls(); }

class Board {
static Board& GetInstance() {
static Board* instance = (Board*)create_board();
return *instance;
}
};

每个 <name>_board.cc 文件末尾用 DECLARE_BOARD 宏注册一个全局 create_board() 函数。链接时只能有一个——所以 CMake 在编译期就把无关的板子排除掉,零运行时开销。

DualNetworkBoard 的"重启切换"哲学

为啥不在运行时切换网络栈?因为:

  • 切换涉及关闭 socket、关闭网络驱动、重启另一套驱动;
  • 还要重建 MQTT/WebSocket 连接、重新激活、重新建立 MCP 会话;
  • 状态非常复杂,bug 风险高。

索性重启了事——4 秒搞定,比手忙脚乱切换还稳定。

一句话讲清

"Board 是个抽象接口,4 层继承:抽象 → 网络栈分类 → 具体板。每个板子用宏注册自己,CMake 编译期选其中一个;运行时永远不需要判断"现在是哪个板子"——多态搞定。"

关联章节

  • /09-boards 整章