1. Totem Library 概述Totem Library 是一个面向教育机器人开发的轻量级嵌入式通信中间件核心目标是为 Totem 系列机器人如 Totem Robot、RoboBoard提供跨平台、低延迟、高可靠性的控制通道。其设计严格遵循“硬件抽象—协议解耦—应用聚焦”三层架构思想不依赖特定操作系统内核可在裸机Bare Metal、FreeRTOS 或 Arduino Core 环境下无缝运行。项目由 TotemMaker 团队主导维护开源协议为 MIT源码托管于 GitHub支持 ESP32主控、Arduino AVR如 Uno/Nano、STM32通过 HAL/LL 移植层等主流 MCU 平台。与通用 BLE 协议栈如 NimBLE、Bluedroid不同Totem Library 并非 BLE 协议实现者而是 BLE 应用层协议的语义封装器它将机器人运动控制、传感器读取、LED 状态设置等操作映射为标准化的 BLE Characteristic 值变更并在串口端统一为 ASCII 编码的指令帧。这种设计使上层应用开发者无需理解 GATT 服务发现、MTU 协商或串口帧校验等底层细节仅需调用totem_move_forward(50)或totem_set_led(0, 255, 0, 1)即可完成硬件交互。项目关键词中 “totem, totemmaker, esp32, robot, roboboard” 明确界定了其技术边界totem / totemmaker指代硬件生态体系包括 Totem Robot 主控板基于 ESP32-WROVER、RoboBoard 扩展板集成电机驱动、IMU、RGB LED、超声波等、LabBoard 教学实验板带按键、OLED、蜂鸣器esp32作为 BLE 主控芯片承担 GATT Server 角色响应手机 App 或 PC 端的 BLE 连接请求robot / roboboard定义功能范畴——运动控制双轮差速、舵机角度、环境感知超声测距、加速度计、人机交互LED、蜂鸣器、按钮arduino指代串口通信端的兼容性设计允许 LabBoard 等设备通过 USB-TTL 串口连接 Arduino 开发板接收并执行 Totem 指令集。该库的本质是协议翻译器 硬件驱动聚合器一端对接 BLE GATT 层ESP32另一端对接外设驱动层电机 PWM、I²C 传感器、GPIO LED中间通过状态机与环形缓冲区保障实时性与数据一致性。2. 系统架构与通信模型2.1 分层架构设计Totem Library 采用清晰的四层结构层级名称职责典型实现L4Application Layer应用层实现用户逻辑路径规划、避障算法、灯光秀编排main.c中的loop()函数L3Totem API LayerTotem 接口层提供统一 C 函数接口屏蔽底层差异totem_move(),totem_read_ultrasonic()L2Transport Abstraction Layer传输抽象层封装 BLE 和 Serial 两种物理通道自动路由指令totem_transport_send()内部判断当前模式L1Hardware Driver Layer硬件驱动层直接操作寄存器或调用 HAL/LL API 驱动外设motor_driver_set_speed(),i2c_read_accel()该分层确保了关键工程特性可移植性更换主控芯片时仅需重写 L1 层驱动如将 ESP32 的ledc_write()替换为 STM32 的HAL_TIM_PWM_Start()可测试性L3 层函数可通过 Mock 驱动进行单元测试无需真实硬件可裁剪性若项目无需超声波则在编译时关闭TOTEM_FEATURE_ULTRASONIC宏自动剔除相关代码与内存占用。2.2 双模通信机制Totem Library 支持两种独立但语义一致的通信模式由编译时宏TOTEM_TRANSPORT_MODE控制BLE 模式默认TOTEM_TRANSPORT_BLE适用于 Totem Robot 主控ESP32。ESP32 启动后广播名为TotemRobot_XXXX的 BLE 设备XXXX为 MAC 地址后 4 字节提供标准 GATT ServiceService UUID:0x12345678-90AB-CDEF-1234-567890ABCDEFControl Characteristic (Write):0x12345678-90AB-CDEF-1234-567890ABCDEF—— 接收 8 字节二进制指令见 2.3 节Status Characteristic (Notify):0x12345678-90AB-CDEF-1234-567890ABCDEF—— 主动推送传感器状态如电池电压、超声距离Serial 模式TOTEM_TRANSPORT_SERIAL适用于 LabBoard 与 Arduino 串口直连场景。使用 115200bps、8N1 标准 UART指令格式为 ASCII 行协议M:50,0 // Move: 左轮50%右轮0%即原地右转 L:0,255,0,1 // LED: 第0号灯RGB(255,0,0)亮度10-3 U: // Ultrasonic: 请求一次超声测距响应为 U:23单位 cm两种模式共享同一套 Totem API开发者无需条件编译即可切换部署形态。例如// 无论 BLE 或 SerialAPI 调用完全一致 totem_move(60, -30); // 左轮60%右轮-30%倒车 totem_set_led(1, 0, 0, 255); // 第1号灯亮纯红全亮度 uint16_t dist totem_read_ultrasonic(); // 返回 cm 值内部自动处理协议差异2.3 指令编码规范所有控制指令均按固定二进制格式打包长度恒为 8 字节确保 BLE ATT MTU通常 23 字节内单包传输避免分片开销。格式定义如下字节偏移字段长度说明示例值十六进制0Command ID1 byte指令类型标识0x01 Move,0x02 LED,0x03 Ultrasonic1-2Param12 bytes参数1有符号整数0x0032 50速度百分比3-4Param22 bytes参数2有符号整数0xFFE2 -30负值表示反转5-6Param32 bytes参数3无符号整数0x0001 LED 编号 17Reserved1 byte保留位置 00x00以totem_move(50, -30)为例生成指令为0x01 0x00 0x32 0xFF 0xE2 0x00 0x00 0x00该设计带来三大优势确定性延迟固定长度指令使解析时间恒定满足实时控制需求抗干扰性强无起始/结束符依赖 BLE 协议栈的包完整性校验比 ASCII 协议更鲁棒扩展性好新增指令只需分配新 Command ID不破坏现有协议。3. 核心 API 详解与使用范式3.1 运动控制 APITotem Robot 采用双轮差速驱动API 设计直接映射物理含义避免抽象层级过多导致的语义失真。函数原型功能说明参数约束典型用法void totem_move(int8_t left_percent, int8_t right_percent)设置左右轮 PWM 占空比-100 ~ 100left/right ∈ [-100, 100]0 为停止totem_move(100, 100); // 全速前进void totem_move_forward(uint8_t speed)前进左右轮同向speed ∈ [0, 100]totem_move_forward(75);void totem_move_backward(uint8_t speed)后退speed ∈ [0, 100]totem_move_backward(30);void totem_turn_left(uint8_t speed)原地左转左轮反向右轮正向speed ∈ [0, 100]totem_turn_left(50);void totem_stop(void)紧急停止左右轮立即归零无参数totem_stop(); // 硬件看门狗触发时调用底层实现逻辑ESP32调用totem_move(60, -40)时库内部执行将 60% →ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 60 * 8191 / 100)8191 为 LEDC 13-bit 分辨率最大值将 -40% → 设置 GPIO 方向引脚为低电平反转电机极性再调用ledc_set_duty(..., 40%)最终通过ledc_update_duty()原子更新 PWM 输出确保左右轮同步变化避免打滑。3.2 传感器与外设 API超声波测距HC-SR04 兼容uint16_t totem_read_ultrasonic(void);返回值距离cm范围 2~400超时返回 0硬件依赖Trig 引脚GPIO18、Echo 引脚GPIO19实现要点使用 ESP32 RMT 外设精确测量 Echo 高电平脉宽规避pulseIn()的阻塞缺陷。RMT channel 配置为 10MHz 时钟1us 分辨率实测误差 1cm。RGB LED 控制WS2812Bvoid totem_set_led(uint8_t index, uint8_t r, uint8_t g, uint8_t b); void totem_set_all_leds(uint8_t r, uint8_t g, uint8_t b);indexLED 编号0-basedRoboBoard 默认 4 颗r/g/b0~255线性亮度值驱动层调用 ESP32 的 RMT 发送 WS2812B 信号T0H350ns, T1H700ns经rmt_write_sample()输出。IMU 数据读取MPU6050I²C 接口typedef struct { int16_t ax; int16_t ay; int16_t az; int16_t gx; int16_t gy; int16_t gz; } totem_imu_data_t; bool totem_read_imu(totem_imu_data_t *data);返回值true 表示读取成功I²C ACK校准要求首次使用需调用totem_imu_calibrate()进行 10 秒静止采样计算零偏数据来源直接读取 MPU6050 的ACCEL_XOUT_H/L和GYRO_XOUT_H/L寄存器地址 0x3B~0x40未启用 DMP保证低延迟。3.3 系统状态与调试 APIuint16_t totem_get_battery_mv(void); // ADC 读取分压电阻精度 ±20mV uint8_t totem_get_button_state(void); // 返回 0x01~0x0F每位代表一个按键 void totem_buzzer_beep(uint16_t freq_hz, uint16_t duration_ms); // 使用 LEDC 生成方波关键设计考量totem_get_battery_mv()内部执行 16 次 ADC 采样并取中值滤波消除电源纹波干扰按键状态采用硬件消抖GPIO 配置为上拉输入库内定时器每 10ms 扫描一次连续 3 次相同值才更新状态蜂鸣器驱动复用 LEDC 通道避免额外 Timer 资源占用。4. ESP32 平台深度集成实践4.1 BLE GATT Server 初始化流程Totem Library 在 ESP32 上的 BLE 初始化严格遵循 ESP-IDF v4.4 规范关键步骤如下// 1. 初始化蓝牙控制器 esp_bt_controller_config_t bt_cfg BT_CONTROLLER_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_bt_controller_init(bt_cfg)); ESP_ERROR_CHECK(esp_bt_controller_enable(ESP_BT_MODE_BLE)); // 2. 初始化 Bluedroid 协议栈 ESP_ERROR_CHECK(esp_bluedroid_init()); ESP_ERROR_CHECK(esp_bluedroid_enable()); // 3. 创建 GATT 服务Totem Service gl_profile_tab[PROFILE_APP_IDX].gatt_if esp_ble_gatts_create_app(totem_gatt_service_id); ESP_ERROR_CHECK(esp_ble_gatts_start_service(gl_profile_tab[PROFILE_APP_IDX].service_handle)); // 4. 注册回调函数处理 Write/Notify 事件 esp_ble_gatts_register_callback(gatts_event_handler);其中gatts_event_handler()是核心事件分发器ESP_GATTS_WRITE_EVT解析 8 字节指令调用对应totem_xxx()函数ESP_GATTS_EXEC_WRITE_EVT批量写入时的确认ESP_GATTS_MTU_EVT动态调整 ATT MTU 至 200 字节需手机 App 支持提升吞吐量。4.2 低功耗优化策略针对电池供电的机器人场景Totem Library 提供三级功耗管理策略实现方式效果BLE 广播优化默认广播间隔 100ms连接后自动停止广播断连 30 秒无活动则进入 Light-sleep待机电流从 10mA 降至 1.2mA外设时钟门控未使用超声波时禁用 RMT 时钟IMU 闲置时写入PWR_MGMT_10x40进入睡眠IMU 功耗从 3.9mA 降至 0.5μACPU 频率动态调节空闲时调用rtc_clk_cpu_freq_set(RTC_CPU_FREQ_XTAL)切换至 40MHz 晶振主频CPU 功耗降低 40%所有功耗操作均封装为totem_enter_lowpower_mode()开发者可一键启用。4.3 FreeRTOS 协同开发模式在复杂任务场景如同时处理 BLE 通信、PID 电机控制、视觉识别推荐使用 FreeRTOS 任务分离// 任务1BLE 通信高优先级防止指令积压 void ble_task(void *pvParameters) { while(1) { if (ble_rx_buffer_has_data()) { parse_ble_command(ble_rx_buffer); // 调用 totem_xxx() } vTaskDelay(1); // 1ms 轮询 } } // 任务2电机闭环控制最高优先级硬实时 void motor_control_task(void *pvParameters) { while(1) { read_encoder_ticks(); compute_pid_output(); totem_move(left_target, right_target); // 直接驱动 vTaskDelay(2); // 2ms 控制周期 } } // 启动任务 xTaskCreate(ble_task, BLE, 2048, NULL, 5, NULL); xTaskCreate(motor_control_task, MOTOR, 4096, NULL, 10, NULL);Totem Library 的所有 API 均为可重入reentrant内部无全局静态变量完全兼容 RTOS 环境。5. Arduino 串口模式开发指南5.1 LabBoard 与 Arduino 连接配置LabBoard 通过 6-pin ISP 接口与 Arduino Nano 连接引脚映射如下LabBoard PinArduino Nano Pin功能电平TXDD2串口发送LabBoard → Arduino3.3V TTLRXDD3串口接收Arduino → LabBoard3.3V TTLVCC5V供电需注意电平匹配5VGNDGND公共地—关键注意事项Arduino Nano 的 5V 输出可直接为 LabBoard 供电LabBoard 支持 4.5~5.5V 输入由于 LabBoard UART 为 3.3V 电平而 Arduino Nano 的 UART 为 5V必须在 TXD/RXD 线路间串联 1kΩ 限流电阻或使用电平转换芯片如 TXB0104Arduino 端需使用SoftwareSerial库因硬件串口被 USB 占用初始化为SoftwareSerial labSerial(2, 3);。5.2 Arduino 端 Totem 指令解析器实现#include TotemLibrary.h // 此头文件包含串口模式专用实现 void setup() { Serial.begin(115200); // 用于调试输出 labSerial.begin(115200); // 连接 LabBoard totem_init(TOTEM_TRANSPORT_SERIAL, labSerial); // 注册串口对象 } void loop() { // Totem Library 自动解析串口数据调用对应驱动 // 开发者只需关注业务逻辑 if (millis() % 2000 0) { uint16_t dist totem_read_ultrasonic(); Serial.printf(Distance: %d cm\n, dist); if (dist 10) { totem_buzzer_beep(1000, 200); totem_set_all_leds(255, 0, 0); // 红灯告警 } } }totem_init()内部注册labSerial对象并启动一个while(labSerial.available())循环解析 ASCII 指令。其解析器采用状态机设计支持指令流水线处理即使在loop()中未及时调用指令也会被缓存在 64 字节环形缓冲区中。5.3 串口协议调试技巧当遇到指令无响应时按以下顺序排查硬件层用万用表测量 TXD/RXD 电压确认空闲态为高电平3.3V发送时有电平翻转协议层用逻辑分析仪捕获 UART 波形验证波特率是否为 115200数据位/停止位是否为 8N1软件层在totem_parse_serial_line()函数入口添加Serial.println(Parsing...)确认解析器是否被触发驱动层检查totem_set_led()是否正确调用了FastLED.show()确认 LED 数据线连接无误。6. 实际项目案例基于 Totem Library 的巡线小车6.1 硬件选型与接线主控ESP32-WROVER内置 BLE传感器TCRT5000 红外巡线模块4 路接 GPIO32~35执行器TB6612FNG 双 H 桥IN1/IN2 接 GPIO12/13PWMA/PWMB 接 GPIO14/27电源7.4V 锂电池经 AMS1117-5.0 降压至 5V 供电机AMS1117-3.3 供 ESP326.2 核心控制逻辑FreeRTOS 任务// 巡线任务优先级 8 void line_follow_task(void *pvParameters) { uint8_t sensor_state 0; // 4-bit 状态bit0left1, bit1left2, bit2right1, bit3right2 int8_t left_spd 0, right_spd 0; while(1) { sensor_state read_infrared_sensors(); // 返回 0x0F 表示全黑0x00 表示全白 switch(sensor_state) { case 0b0000: // 全白 - 直行 left_spd right_spd 60; break; case 0b1100: // 左侧黑 - 左转 left_spd 30; right_spd 70; break; case 0b0011: // 右侧黑 - 右转 left_spd 70; right_spd 30; break; default: // 其他状态如 0b1010- 停止 left_spd right_spd 0; } totem_move(left_spd, right_spd); vTaskDelay(20); // 20ms 控制周期 } } // 启动任务 xTaskCreate(line_follow_task, LINE_FOLLOW, 2048, NULL, 8, NULL);6.3 BLE 远程监控集成在手机 App 中通过 BLE 连接后订阅 Status Characteristic实时显示sensor_state和battery_mv发送 Control Characteristic 指令0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00Command ID0x04 为自定义巡线启停指令ESP32 端gatts_event_handler()解析后调用xTaskNotifyGive(line_follow_task_handle)唤醒巡线任务。此案例完整展现了 Totem Library 的工程价值将复杂的多任务协同、硬件驱动、协议解析全部封装开发者仅需 30 行核心逻辑代码即可构建一个具备远程监控能力的智能小车系统。