桌面宠物机器人全栈设计
在快节奏的生活中,一只可爱的桌面宠物或许是解压的最佳伙伴。市面上的电子宠物大多功能单一、不可编程,而专业机器人平台又价格高昂、门槛过高。本项目基于深圳大夏龙雀科技 BT36 蓝牙模块与 STM32F103C6T6A 主控,仅用面包板 + 杜邦线 + 4个SG90舵机 + 一块OLED屏 + 一节18650电池,就打造出一只可蓝牙遥控、会做表情、能动作卖萌的桌面宠物机器人。配套开发了微信小程序作为控制端,实现了从硬件到软件的全栈设计。
一、项目背景

1.1 创作初衷
一直想做一个既有技术含量又充满趣味的DIY项目。工业类项目虽然硬核但缺乏传播力,纯玩具类又显得技术深度不足。最终选择了"桌面宠物机器人"这个方向——
-
趣味性强:可爱的外形、多种表情、跳舞卖萌,天然适合展示分享;
-
技术覆盖全:涉及嵌入式开发、蓝牙通信、PWM舵机控制、I2C显示驱动、微信小程序开发,技术栈完整;
-
门槛低可复制:面包板搭建、耗材便宜、结构简单,读者跟着教程就能复刻;
1.2 核心功能
|
功能模块 |
具体功能 |
|---|---|
|
运动控制 |
前进 / 后退 / 左转 / 右转 / 停止 |
|
表情系统 |
开心 / 困困 / 生气 / 惊讶 四种表情 |
|
炫彩灯效 |
呼吸灯 / 彩虹灯 / 闪烁灯 / 常亮灯 |
|
组合动作 |
跳舞 / 打招呼 / 卖萌 |
|
控制方式 |
微信小程序 + 蓝牙遥控器双模式 |
|
供电方式 |
单节18650锂电池,移动便携 |
二、整体架构
桌面宠物机器人采用 "应用层(小程序、遥控)— 通信层(BT36蓝牙)— 控制层(STM32)— 执行层(舵机、OLED、LED)" 四层架构:

2.1 工作原理

-
蓝牙连接:大夏龙雀 BT36 模块上电后自动广播,手机微信小程序或蓝牙遥控器搜索并建立 BLE 连接;
-
指令下发:用户在小程序端点击方向键、表情按钮或灯效按钮,指令通过 BLE 透传至 BT36;
-
UART通信:BT36 通过 UART 将指令发送给 STM32F103C6T6A 主控;
-
指令解析:STM32 解析指令格式,执行对应的舵机运动、OLED表情切换或RGB灯效变换;
-
状态回复:执行完成后通过蓝牙回复 ACK,小程序端更新状态显示。
2.2 硬件选型
|
模块 |
型号 |
接口 |
数量 |
说明 |
|---|---|---|---|---|
|
主控 |
STM32F103C6T6A |
- |
×1 |
LQFP-48封装,资源充足 |
|
蓝牙 |
大夏龙雀 BT36 |
UART |
×1 |
BLE5.3,主从一体,AT指令配置 |
|
显示 |
0.96" OLED |
I2C |
×1 |
128×64分辨率,SSD1306驱动 |
|
舵机 |
SG90 微型舵机 |
PWM |
×4 |
180°旋转,重量轻,适合桌面级 |
|
灯光 |
RGB LED(共阴) |
PWM |
×1 |
三原色混合,实现炫彩效果 |
|
电源 |
18650 锂电池 |
- |
×1 |
3.7V/2000mAh以上,续航持久 |
|
搭建 |
面包板 + 杜邦线 |
- |
若干 |
免焊接,快速原型验证 |
三、硬件设计
3.1 硬件连接总览
全部电路通过面包板 + 杜邦线搭建,无需PCB,零基础也能快速上手。连接示意图如下

3.2 引脚分配
// STM32F103C6T6A 引脚分配
#define SERVO_LEFT_LEG_PIN GPIO_Pin_0 // PA0 - 左腿
#define SERVO_RIGHT_LEG_PIN GPIO_Pin_1 // PA1 - 右腿
#define SERVO_LEFT_FOOT_PIN GPIO_Pin_2 // PA2 - 左前足
#define SERVO_RIGHT_FOOT_PIN GPIO_Pin_3 // PA3 - 右前足
#define OLED_SDA_PIN GPIO_Pin_7 // PB7 - I2C SDA
#define OLED_SCL_PIN GPIO_Pin_6 // PB6 - I2C SCL
#define BT_TX_PIN GPIO_Pin_9 // PA9 - USART1 TX
#define BT_RX_PIN GPIO_Pin_10 // PA10 - USART1 RX
#define LED_R_PIN GPIO_Pin_8 // PA8 - TIM1 CH1
#define LED_G_PIN GPIO_Pin_9 // PB9 - TIM4 CH4
#define LED_B_PIN GPIO_Pin_8 // PB8 - TIM4 CH3
3.3 舵机驱动原理
SG90舵机的驱动信号是20ms周期的PWM波,高电平宽度从0.5ms到2.5ms对应0°到180°:
|
高电平宽度 |
对应角度 |
|---|---|
|
0.5ms |
0° |
|
1.0ms |
45° |
|
1.5ms |
90°(中位) |
|
2.0ms |
135° |
|
2.5ms |
180° |
通过STM32定时器PWM输出,动态调整CCR寄存器即可控制舵机角度。四只舵机分别控制两条后腿和两只前足,通过协调不同的角度组合实现前进、后退、转向等步态。
3.4 电源方案

单节18650锂电池(3.7V标称电压)直接为整个系统供电:
-
STM32F103C6T6A:工作电压2.0~3.6V,核心板自带的LDO即可实现MCU供电
-
SG90舵机:工作电压4.8V典型,3.7V也能工作但扭矩稍小,桌面宠物重量轻完全够用
-
BT36蓝牙模块:支持3.3~5V宽电压,3.7V直接供电
-
OLED显示屏:3.3V供电,由STM32的3.3V引脚
我们使用的锂电池充放电方案是TP4056,直接粘贴在面包板上,方便使用Type-C接口直接对18650锂电池进行充电。
四、机械结构设计
桌面宠物采用4舵机方案,结构简单、动作灵活:

4.1 四舵机布局
-
后腿 ×2:左右各一只,负责主要的行走动力输出,摆动范围约60°
-
前足 ×2:左右各一只,负责辅助支撑和姿态调整,使动作更自然可爱
4.2 步态原理
通过四只舵机的角度协调变化实现不同运动:
前进步态:
-
左前足抬起 → 右腿后蹬 → 身体前倾
-
右前足抬起 → 左腿后蹬 → 身体继续前进
-
交替循环,实现"蹒跚学步"的可爱效果
转向原理:
-
左转:右腿加速前摆,左腿减速或小幅后摆,身体向左偏转
-
右转:左腿加速前摆,右腿减速或小幅后摆,身体向右偏转
4.3 结构材质
我们使用的刚好是面包板,无需任何焊接即可实现电路的快速搭建,可以用:
建议先用简单材料验证步态,满意后再做精致外壳。
五、OLED表情系统
小小的OLED屏幕是宠物的"灵魂之窗",通过绘制不同表情赋予宠物性格:

5.1 四种基础表情
|
表情 |
触发场景 |
视觉效果 |
|---|---|---|
|
开心 |
正常待机 / 收到指令执行成功 |
弯弯笑眼 + 上扬嘴角 |
|
困困 |
长时间无操作 / 夜晚模式 |
眯成一条缝 + Zzz |
|
生气 |
连续撞墙 / 被频繁打扰 |
倒八字眉 + 嘴角下撇 |
|
惊讶 |
首次连接 / 收到新指令 |
圆眼睛 + O型嘴 |
5.2 表情绘制实现

使用SSD1306驱动的0.96寸OLED(128×64),通过I2C通信:
// 表情数据结构
typedef struct {
const uint8_t *face_data; // 表情位图数据
uint8_t width; // 表情宽度
uint8_t height; // 表情高度
} Expression_t;
// 表情枚举
typedef enum {
EXP_HAPPY = 0, // 开心
EXP_SLEEPY = 1, // 困困
EXP_ANGRY = 2, // 生气
EXP_SURPRISE = 3 // 惊讶
} Expression_Type;
// 切换表情函数
void OLED_ShowExpression(Expression_Type exp)
{
// 清屏
OLED_Clear();
// 在屏幕中央绘制表情
uint8_t x = (128 - expressions[exp].width) / 2;
uint8_t y = (64 - expressions[exp].height) / 2;
OLED_DrawBMP(x, y, expressions[exp].width, expressions[exp].height,
expressions[exp].face_data);
// 可附加装饰:脸颊红晕等
if(exp == EXP_HAPPY) {
OLED_DrawCircle(30, 40, 5, 1); // 左脸颊
OLED_DrawCircle(98, 40, 5, 1); // 右脸颊
}
OLED_Refresh();
}
表情图案可以用 LCD Image Converter 或类似工具将图片转换为C数组,直接嵌入代码中。
六、RGB炫彩灯效
RGB LED是宠物的"氛围担当",四种灯效模式随心切换:

6.1 四种灯效模式
|
模式 |
效果描述 |
适用场景 |
|---|---|---|
|
呼吸灯 |
PWM渐变亮灭,温柔如呼吸 |
待机状态,安静优雅 |
|
彩虹灯 |
RGB三色循环渐变,炫彩夺目 |
跳舞时,活泼热闹 |
|
闪烁灯 |
快速闪烁,活泼俏皮 |
开心/惊讶时 |
|
常亮灯 |
稳定单色光,柔和护眼 |
夜灯模式,温暖陪伴 |
6.2 PWM混色原理

三路独立PWM,每路8位分辨率(0~255),可混合出约1677万种颜色:
// RGB颜色设置函数
void RGB_SetColor(uint8_t r, uint8_t g, uint8_t b)
{
// 直接修改CCR寄存器,0~255映射到0~ARR
TIM_SetCompare1(TIM1, r); // R通道
TIM_SetCompare3(TIM4, g); // G通道
TIM_SetCompare4(TIM4, b); // B通道
}
// 呼吸灯效果
void LED_Breathing_Effect(void)
{
static uint8_t brightness = 0;
static uint8_t direction = 1;
if(direction) {
brightness++;
if(brightness >= 200) direction = 0;
} else {
brightness--;
if(brightness == 0) direction = 1;
}
RGB_SetColor(brightness, brightness / 2, 0); // 暖橙色呼吸
}
七、蓝牙通信协议
通信核心是深圳大夏龙雀科技 BT36 蓝牙模块,这是整个项目的无线桥梁。

7.1 BT36 模块特性
BT36 是大夏龙雀出品的高性价比 BLE 5.3 模块,采用 DIALOG DA14535 芯片,主从一体设计。在本项目中,它的优势体现在:
-
BLE 5.3 协议:连接速度快、功耗低、抗干扰能力强
-
主从一体:既可作从机被手机连接,也可作主机连接遥控器
-
AT指令丰富:支持修改设备名、波特率、UUID、发射功率等参数,响应迅速(<10ms)
-
宽电压工作:3.3V~5V,直接接锂电池,无需额外电平转换
-
UART透传:透明传输模式,开发简单,像操作串口一样使用蓝牙
-
通信距离远:空旷环境下可达数十米,桌面场景绰绰有余
在实际开发中,BT36的稳定性令人印象深刻——电机PWM信号产生的电磁干扰对蓝牙通信几乎没有影响,连接稳定不掉线,这对于面包板搭建的原型项目来说非常关键。
7.2 通信协议设计
采用简洁易解析的文本指令协议,便于调试和扩展:

7.3 串口解析实现
// 蓝牙数据接收缓冲区
#define BT_RX_BUF_SIZE 64
uint8_t bt_rx_buf[BT_RX_BUF_SIZE];
uint8_t bt_rx_index = 0;
// USART1 中断接收(BT36连接)
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
uint8_t ch = USART_ReceiveData(USART1);
if(ch == '\n' || bt_rx_index >= BT_RX_BUF_SIZE - 1) {
bt_rx_buf[bt_rx_index] = '\0';
// 完整指令,解析处理
BT_Parse_Command((char*)bt_rx_buf);
bt_rx_index = 0;
} else if(ch != '\r') {
bt_rx_buf[bt_rx_index++] = ch;
}
}
}
// 指令解析函数
void BT_Parse_Command(char *cmd)
{
if(strncmp(cmd, "FWD", 3) == 0) {
Servo_WalkForward();
OLED_ShowExpression(EXP_HAPPY);
BT_SendString("OK\r\n");
}
else if(strncmp(cmd, "BACK", 4) == 0) {
Servo_WalkBackward();
BT_SendString("OK\r\n");
}
else if(strncmp(cmd, "LEFT", 4) == 0) {
Servo_TurnLeft();
BT_SendString("OK\r\n");
}
else if(strncmp(cmd, "RIGHT", 5) == 0) {
Servo_TurnRight();
BT_SendString("OK\r\n");
}
else if(strncmp(cmd, "STOP", 4) == 0) {
Servo_Stop();
BT_SendString("OK\r\n");
}
else if(strncmp(cmd, "EXP:", 4) == 0) {
uint8_t exp_num = cmd[4] - '0';
if(exp_num <= 3) {
OLED_ShowExpression(exp_num);
BT_SendString("OK\r\n");
}
}
else if(strncmp(cmd, "LED:", 4) == 0) {
uint8_t led_num = cmd[4] - '0';
if(led_num <= 3) {
LED_SetMode(led_num);
BT_SendString("OK\r\n");
}
}
else if(strncmp(cmd, "ACT:", 4) == 0) {
Action_Execute(cmd + 4);
BT_SendString("OK\r\n");
}
else {
BT_SendString("ERR\r\n");
}
}
八、系统控制流程

8.1 主循环结构
int main(void)
{
// 系统初始化
SystemInit();
Servo_Init(); // 舵机PWM初始化
OLED_Init(); // OLED初始化
RGB_Init(); // RGB灯PWM初始化
USART1_Init(9600); // 蓝牙串口初始化(BT36默认9600)
// 开机动画
OLED_ShowExpression(EXP_HAPPY);
LED_SetMode(LED_BREATHING);
Servo_WaveHello(); // 开机打招呼
while(1)
{
// 检查是否收到蓝牙指令
if(bt_cmd_ready) {
BT_Parse_Command(bt_cmd_buffer);
bt_cmd_ready = 0;
}
// LED灯效任务
LED_Task_Handler();
// 待机检测
if(HAL_GetTick() - last_cmd_time > 10000) {
if(current_expression != EXP_SLEEPY) {
OLED_ShowExpression(EXP_SLEEPY);
LED_SetMode(LED_BREATHING); // 柔和呼吸
}
}
}
}
8.2 组合动作实现
组合动作是让宠物"活起来"的关键,通过编排一系列舵机角度序列实现:
// 跳舞动作序列
void Action_Dance(void)
{
OLED_ShowExpression(EXP_HAPPY);
LED_SetMode(LED_RAINBOW); // 彩虹灯效
// 左右摇摆 + 前足摆动
for(int i = 0; i < 4; i++) {
Servo_SetAngle(SERVO_LEFT_LEG, 60 + i * 15);
Servo_SetAngle(SERVO_RIGHT_LEG, 120 - i * 15);
Delay_Ms(200);
}
for(int i = 0; i < 4; i++) {
Servo_SetAngle(SERVO_LEFT_LEG, 120 - i * 15);
Servo_SetAngle(SERVO_RIGHT_LEG, 60 + i * 15);
Delay_Ms(200);
}
// 前足上下摆动
for(int i = 0; i < 3; i++) {
Servo_SetAngle(SERVO_LEFT_FOOT, 45);
Servo_SetAngle(SERVO_RIGHT_FOOT, 45);
Delay_Ms(150);
Servo_SetAngle(SERVO_LEFT_FOOT, 135);
Servo_SetAngle(SERVO_RIGHT_FOOT, 135);
Delay_Ms(150);
}
Servo_Stop();
LED_SetMode(LED_BREATHING);
}
// 打招呼动作
void Action_Hello(void)
{
OLED_ShowExpression(EXP_HAPPY);
// 右前足抬起挥动
for(int i = 0; i < 3; i++) {
Servo_SetAngle(SERVO_RIGHT_FOOT, 30);
Delay_Ms(200);
Servo_SetAngle(SERVO_RIGHT_FOOT, 60);
Delay_Ms(200);
}
Servo_Stop();
}
九、微信小程序控制端
为了让宠物的控制更便捷、作品更完整,开发了配套的微信小程序控制端,通过 BLE 直接连接 BT36 模块:

9.1 小程序功能架构
小程序包含三个主要页面:
|
页面 |
功能 |
|---|---|
|
控制页 |
方向控制 + 表情切换 + 灯效选择 + 快捷动作 |
|
设置页 |
舵机角度微调、响应速度设置、自动休眠时间 |
|
关于页 |
作品介绍、版本信息、教程链接 |
9.2 BLE连接核心代码
// 小程序端 - 蓝牙连接与控制
const DEVICE_NAME = 'DX-BT36'; // BT36默认设备名
const SERVICE_UUID = '0000FFE0-0000-1000-8000-00805F9B34FB';
const CHAR_UUID = '0000FFE1-0000-1000-8000-00805F9B34FB';
let deviceId = null;
let connected = false;
// 搜索并连接设备
function connectDevice() {
wx.startBluetoothDevicesDiscovery({
services: [SERVICE_UUID],
success: (res) => {
console.log('开始搜索设备');
wx.onBluetoothDeviceFound((devices) => {
const device = devices.devices.find(d =>
d.name === DEVICE_NAME || d.localName === DEVICE_NAME
);
if(device) {
wx.stopBluetoothDevicesDiscovery();
createConnection(device.deviceId);
}
});
}
});
}
// 建立连接
function createConnection(id) {
wx.createBLEConnection({
deviceId: id,
success: () => {
deviceId = id;
connected = true;
wx.showToast({ title: '连接成功', icon: 'success' });
// 监听特征值变化
wx.notifyBLECharacteristicValueChange({
deviceId,
serviceId: SERVICE_UUID,
characteristicId: CHAR_UUID,
success: () => {
wx.onBLECharacteristicValueChange((res) => {
const data = ab2str(res.value);
console.log('收到:', data);
});
}
});
}
});
}
// 发送指令
function sendCommand(cmd) {
if(!connected) {
wx.showToast({ title: '请先连接设备', icon: 'none' });
return;
}
const buffer = str2ab(cmd + '\r\n');
wx.writeBLECharacteristicValue({
deviceId,
serviceId: SERVICE_UUID,
characteristicId: CHAR_UUID,
value: buffer,
success: () => console.log('发送:', cmd)
});
}
// 工具函数:字符串转ArrayBuffer
function str2ab(str) {
const buffer = new ArrayBuffer(str.length);
const view = new Uint8Array(buffer);
for(let i = 0; i < str.length; i++) {
view[i] = str.charCodeAt(i);
}
return buffer;
}
function ab2str(buffer) {
return String.fromCharCode.apply(null, new Uint8Array(buffer));
}
// 方向控制按钮
const control = {
forward: () => sendCommand('FWD'),
backward: () => sendCommand('BACK'),
turnLeft: () => sendCommand('LEFT'),
turnRight: () => sendCommand('RIGHT'),
stop: () => sendCommand('STOP'),
};
// 表情切换
const expressions = {
happy: () => sendCommand('EXP:0'),
sleepy: () => sendCommand('EXP:1'),
angry: () => sendCommand('EXP:2'),
surprise: () => sendCommand('EXP:3'),
};
// 灯效切换
const ledModes = {
breathing: () => sendCommand('LED:0'),
rainbow: () => sendCommand('LED:1'),
blink: () => sendCommand('LED:2'),
solid: () => sendCommand('LED:3'),
};
// 快捷动作
const actions = {
dance: () => sendCommand('ACT:DANCE'),
hello: () => sendCommand('ACT:HELLO'),
cute: () => sendCommand('ACT:CUTE'),
};
9.3 小程序界面设计要点
-
方向控制区:D-pad方向键布局,中间键为停止,符合游戏手柄使用习惯
-
表情切换区:4个表情按钮横向排列,点击即切换,状态反馈清晰
-
灯效选择区:4种灯效彩色按钮,视觉上即传达灯光氛围
-
快捷动作区:跳舞/打招呼/卖萌三个大按钮,一键触发组合动作
-
状态栏:顶部实时显示连接状态,已连接显示绿色圆点
十、项目总结
这款桌面宠物机器人虽然只用面包板搭建,但麻雀虽小五脏俱全,从硬件到软件实现了完整的全栈设计:
10.1 项目亮点
-
全栈自主设计:从电路搭建、嵌入式程序到微信小程序,端到端完整实现,技术覆盖面广
-
BT36深度适配:充分发挥大夏龙雀 BT36 蓝牙模块的性能优势,BLE5.3稳定连接、AT指令灵活配置、宽电压适配
-
面包板入门友好:无需PCB、无需焊接,所有耗材易购,降低了复刻门槛,适合教学和推广
-
趣味性与传播性:可爱的表情、灵动的动作、微信小程序控制,天然适合短视频和社交分享
-
扩展性强:可以不断添加新表情、新动作、新传感器(触摸/声音/光线),持续迭代
10.2 成本清单
|
物料 |
单价(参考) |
数量 |
小计 |
|---|---|---|---|
|
STM32F103C6T6A 最小系统板 |
约15元 |
×1 |
15元 |
|
大夏龙雀 BT36 蓝牙模块 |
约20元 |
×1 |
20元 |
|
SG90 微型舵机 |
约5元 |
×4 |
20元 |
|
0.96" OLED 显示屏 |
约8元 |
×1 |
8元 |
|
RGB LED + 电阻 |
约1元 |
×1 |
1元 |
|
18650 锂电池 + 电池盒 |
约10元 |
×1 |
10元 |
|
面包板 + 杜邦线 |
约10元 |
若干 |
10元 |
|
总计 |
- |
- |
约84元 |
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐
所有评论(0)