
一只“有表情”的机器狗!STM32+NFR24L01遥控宠物
简介
基于STM32F103C8T6单片机与NRF24L01射频模块设计的遥控机器狗以及遥控器,可以实现遥控机器狗的运动与表情显示
简介:基于STM32F103C8T6单片机与NRF24L01射频模块设计的遥控机器狗以及遥控器,可以实现遥控机器狗的运动与表情显示开源协议
:MIT License
(未经作者授权,禁止转载)描述
项目简介
本项目设计并实现了一套完整的无线遥控器狗系统。STM32F103C8T6为核心,结合NRF24L01 2.4GHz无线射频模块设计了机器狗的控制板与遥控器
项目功能
本项目设计并实现了一款基于STM32F103C8T6单片机与NRF24L01射频模块的智能遥控机器狗系统。系统采用主从式架构,手持遥控器通过2.4GHz无线通信向机器狗发送控制指令,机器狗接收指令后实时驱动多路舵机实现前进、后退、转向等多种仿生步态;同时,机器狗面部搭载的OLED屏可根据运行状态或遥控指令动态显示预设表情(如高兴、疑惑、睡眠等),实现直观的人机交互。
项目参数
本项目硬件系统主要由主控芯片、无线通信模块、运动执行模块及显示交互模块构成:
-
核心控制采用两片STM32F103C8T6单片机,分别负责机器狗的运动与表情驱动、遥控器的指令采集与协议处理;
-
无线通信选用NRF24L01 2.4G无线模块,确保稳定传输;

- 运动执行部分由4个舵机(SG90)构成,通过PWM信号精确控制机器狗的关节动作;
- 显示交互方面,机器狗面部搭载0.96英寸OLED显示屏,用于动态显示多种表情
- 遥控器可选配0.96英寸OLED显示屏实时显示控制参数与机器狗状态;此外,系统还包含电源管理模块、摇杆、按键、电池等基础元器件,共同构成完整的遥控机器狗系统。

遥控器图片

摇杆图片
软件代码
遥控器代码
#include "delay.h"
#include "oled.h"
#include "stm32f10x.h" // Device header
#include "nrf24l01.h"
#include "stdio.h"
// 初始化ADC1,用于PB0(ADC1_CH8)和PB1(ADC1_CH9)
void ADC1_Init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
// 使能GPIO和ADC时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_ADC1, ENABLE);
// 配置PB0和PB1为模拟输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; // 模拟输入模式
GPIO_Init(GPIOB, &GPIO_InitStructure);
// ADC配置
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; // 独立模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE; // 禁止扫描模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // 单次转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 软件触发
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // 数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; // 转换通道数量
ADC_Init(ADC1, &ADC_InitStructure);
// 使能ADC1
ADC_Cmd(ADC1, ENABLE);
// 校准ADC
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
}
void tim2_init(void)
{
TIM_TimeBaseInitTypeDef tim;
NVIC_InitTypeDef nvic;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
tim.TIM_ClockDivision=TIM_CKD_DIV1;
tim.TIM_CounterMode=TIM_CounterMode_Up;
tim.TIM_Period=10000-1;
tim.TIM_Prescaler=7200-1;
tim.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM2,&tim);
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
nvic.NVIC_IRQChannel=TIM2_IRQn;
nvic.NVIC_IRQChannelCmd=ENABLE;
nvic.NVIC_IRQChannelPreemptionPriority=0;
nvic.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&nvic);
TIM_Cmd(TIM2,ENABLE);
}
// 读取指定通道的ADC值
uint16_t ADC1_ReadChannel(uint8_t channel) {
// 配置ADC通道转换顺序和采样时间
ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_55Cycles5);
// 启动ADC转换
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
// 等待转换完成
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
// 返回转换结果
return ADC_GetConversionValue(ADC1);
}
// 将ADC值转换为电压值(mV)
uint16_t ADC_ConvertToVoltage(uint16_t adcValue) {
// 假设参考电压为3.3V,ADC为12位
return (uint16_t)((adcValue * 3300) / 4095);
}
int main(void) {
uint16_t pb0AdcValue, pb1AdcValue;
uint16_t pb0Voltage, pb1Voltage;
uint16_t status,last_status;
last_status=0xff;
NRF24L01_TxPacket[0]=0xff;
NRF24L01_TxPacket[1]=0x01;
NRF24L01_TxPacket[2]=0x02;
NRF24L01_TxPacket[3]=0x03;
// 初始化ADC
ADC1_Init();
OLED_Init();
NRF24L01_Init(); // 初始化NRF24L01
NRF24L01_Tx(); //发送模式
while(1)
{
// 读取PB0(ADC1_CH8)的电压
pb0AdcValue = ADC1_ReadChannel(ADC_Channel_8);
pb0Voltage = ADC_ConvertToVoltage(pb0AdcValue);
// 读取PB1(ADC1_CH9)的电压
pb1AdcValue = ADC1_ReadChannel(ADC_Channel_9);
pb1Voltage = ADC_ConvertToVoltage(pb1AdcValue);
OLED_ShowString(1,1,"x:");
OLED_ShowString(1,8,"y:");
OLED_ShowNum(1,3,pb0Voltage,4);
OLED_ShowNum(1,10,pb1Voltage,4);
if(pb0Voltage<1500)
{
status=0x00;//后退
}
else if(pb0Voltage>1750)
{
status=0x01;//前进
}
else if((pb1Voltage<1500)&(pb0Voltage<1750)&(pb0Voltage>1500))
{
status=0x02;//左转
}
else if((pb1Voltage>1750)&(pb0Voltage<1750)&(pb0Voltage>1500))
{
status=0x03;//右转
}
else if((pb1Voltage>1500)&(pb1Voltage<1750)&(pb0Voltage<1750)&(pb0Voltage>1500))
{
status=0x04;//原地
}
NRF24L01_TxPacket[0]=status;
NRF24L01_Send();
if(status!=last_status)
{
last_status=status;
if(status==0x00)
{
OLED_Clear();
OLED_ShowString(3,4,"GO GO GO!");
}
else if(status==0x01)
{
OLED_Clear();
OLED_ShowString(3,7,"back");
}
else if(status==0x02)
{
OLED_Clear();
OLED_ShowString(3,3,"turn right");
}
else if(status==0x03)
{
OLED_Clear();
OLED_ShowString(3,3,"turn left");
}
else if(status==0x04)
{
OLED_Clear();
OLED_ShowString(3,7,"rest");
}
}
}
}
这段代码主要实现以下功能:
-
由于摇杆的本质是两个滑动变阻器对Vcc分压,因此通过ADC1采集PB0(通道8)和PB1(通道9)的模拟电压值即可以侧面反映摇杆的方位。
-
将ADC值转换为电压值(单位mV)并通过OLED显示屏实时显示。
-
根据两个电压值的范围(即摇杆的状态)判断当前状态(前进、后退、左转、右转、原地),并通过NRF24L01无线模块发送状态码。
-
OLED上会显示当前状态对应的文本提示。
控制板代码
#include "delay.h"
#include "oled.h"
#include "stm32f10x.h" // Device header
#include "nrf24l01.h"
#include "Dynamics.h"
#include "stdio.h"
#include "emoj.h"
int main(void)
{
SystemInit();
delay_init();
OLED_Init();
Dynamics_Init();
NRF24L01_Init();
tyq_init();
while(1)
{
NRF24L01_Receive();
if(NRF24L01_ReadStatus())
{
switch(NRF24L01_RxPacket[0])
{
case 0x04:
stand();
OLED_ShowFullImage(13);
;break;
case 0x01:
back();
OLED_ShowFullImage(14);
;break;
case 0x00:
go();
OLED_ShowFullImage(15);
;break;
case 0x02:
turn_right();
OLED_ShowFullImage(16);
;break;
case 0x03:
turn_left();
OLED_ShowFullImage(16);
;break;
default:
stand();
}
}
该代码主要实现以下功能:
-
初始化系统时钟、延时、OLED显示屏、动力学控制模块(Dynamics_Init())、NRF24L01无线模块等等。
-
在主循环中,通过NRF24L01不断接收无线数据。当接收到有效数据时,根据数据包的第一个字(NRF24L01_RxPacket[0])的值,执行相应的动作函数(如前进、后退、转向、停止),并在OLED上显示对应的全屏表情图像。
实物图
机器狗的图片(未装OLED)

其他
完整代码详见附件
设计图
未生成预览图,请在编辑器重新保存一次BOM
暂无BOM
克隆工程知识产权声明&复刻说明
本项目为开源硬件项目,其相关的知识产权归创作者所有。创作者在本平台上传该硬件项目仅供平台用户用于学习交流及研究,不包括任何商业性使用,请勿用于商业售卖或其他盈利性的用途;如您认为本项目涉嫌侵犯了您的相关权益,请点击上方“侵权投诉”按钮,我们将按照嘉立创《侵权投诉与申诉规则》进行处理。
请在进行项目复刻时自行验证电路的可行性,并自行辨别该项目是否对您适用。您对复刻项目的任何后果负责,无论何种情况,本平台将不对您在复刻项目时,遇到的任何因开源项目电路设计问题所导致的直接、间接等损害负责。









