基于stm32库函数开发的蓝牙遥控机械臂小车
基于stm32f103rct6制作的蓝牙遥控小车,刚接触stm32单片机和openmv时做的第一个小车,网上关于蓝牙遥控的小车资料有很多,但也希望这篇文章能提供给别人不同的代码和硬件设计思路。
目录:
1.前言

这个是自己在刚接触stm32单片机和openmv时做的第一个小车,网上关于蓝牙遥控的小车资料有很多,但也希望这篇文章能提供给别人不同的代码和硬件设计思路。
1.所需硬件模块

主控板可以根据自己项目的实际需要进行更改,开发方式这里选择的是stm32标准库函数,相应数量的杜邦线,面包板,12V电池组,大家可以根据自己的需要进行准备。
2.引脚图设计及模块原理介绍
1.引脚图设计
1.L298N驱动
前A:PA6---------AENA (TIM3_CH1)
PA7---------AENB (TIM3_CH2)
PB3 PB4 PB5 PB6 控制IN1 IN2 IN3 IN4
后B:PB0--------BENA (TIM3_CH3)
PB1--------BENB (TIM3_CH4)
PC0 PC1 PC2 PC3 控制IN1 IN2 IN3 IN4
2.蜂鸣器
PA12------I/O
3.OLED模块 ---- SPI
PB13 ------------- D0 (时钟线)
PB15 ------------- D1 (数据线)
PB12 ------------- RES(复位线)
PB10 ------------- DC (数据/命令控制线)
PB11 ------------- CS (片选线 低电平有效 如果不想用必须接地)
4.机械臂四个舵机
PA0(TIM2_CH1)------------正面左舵机
PA1(TIM2_CH2)------------正面右舵机
PA2(TIM2_CH3)------------机械夹舵机
PA3(TIM2_CH4)------------底部转身舵机
5.蓝牙模块
USART1
PA9(TX) -------- 蓝牙RX
PA10(RX)-------- 蓝牙TX
2.驱动模块L298N介绍

电源输入7~12V电压时,5v的位置不用接电源,该位置可输出一个5v,用于给单片机供电,L298N的GND接单片机的GND,一定要共地,( 否则没有参考电压,不能进行正常控制 )。给对应的ENA和ENB引脚使能后,通过给IN1和IN2引脚赋不同的高低电平,实现对应直流减速电机的转动。
举例:(IN1 IN2指的是对应引脚的高低电平)
IN1 IN2 电机状态
0 0 停止
1 1 停止 (同电平时,电机不转动)
1 0 正转
0 1 反转
3.机械臂舵机模块介绍

脉冲宽度调制(PWM):用于测量,通信功率控制与变换的许多领域,是一种数字信号,最常用于控制电路。该信号在预定义的时间和速度中设置为高(5v或3.3v)和低(0v)。通常,我们将PWM的高电平称为1,低电平为0。PWM 信号是通过比较定时器计数器CK_CNT的当前值与 CCR 中的参考值来生成的。
PWM占空比:在一串理想的脉冲序列(如方波)中,正脉冲的持续时间与脉冲总周期的比值,PWM信号保持高电平的时间百分比称为占空比。如果信号始终为高电平,则它处于100%占空比,如果它始终处于低电平,则占空比为0%。如图1所示,T1为占空比,T为一个PWM周期。
而SG90舵机的控制一般需要一个20ms左右的时基脉冲,该脉冲的高电平部分一般为0.5ms~2.5ms范围内的角度控制脉冲部分。以180度角度伺服为例,那么对应的控制关系是这样的:
0.5ms--------------0度;
1.0ms------------45度;
1.5ms------------90度;
2.0ms-----------135度;
2.5ms-----------180度;
注意:平时学校中很多用于比赛的机械臂对舵机要求较高,SG90便宜但是扭矩很小,在比赛夹取物块时很容易烧坏,大家可以选用扭矩更大,性能更稳定的金属舵机平替。
4.蓝牙模块介绍
(1)主从模式介绍
蓝牙通信的工作频段为2.54GHz的ISM频段。采用高速跳频扩展技术(Frequency Hopping Spread Spectrum,FHSS),提高其抗干扰能力,跳频速度为每秒1600跳。
蓝牙地址为符合IEEE 802标准的48位地址,每个蓝牙设备都有一个全球唯一的地址码。蓝牙以无线局域网标准IEEE802.11为基础,实现了“即连即用”。
HC-05蓝牙模块具有两种工作模式:自动连接工作模式和命令响应工作模式。
在自动连接工作模式下,HC-05模块的工作模式可分为主(Master)、从(Slave)和回环(Loopback)3种。在主模式下,HC-05蓝牙模块会主动搜索周边的蓝牙设备,并发起连接。在从模式下,HC-05模块被动响应配对请求。在回环模式下,HC-05模块被动响应配对请求,建立连接后,会将接收到的数据发回发送方。
在命令响应工作模式下,模块能响应AT命令。此时用户可以通过串口向HC-05模块发送AT命令,设定模块的工作方式,改变模块的设定参数等,控制HC-05模块的工作。
HC-05蓝牙模块上的输入引脚EN控制蓝牙模块的工作模式,引脚为高电平时进入命令响应工作模式,为低电平时进入自动连接工作模式。
模块上的按键控制EN引脚的电平,按下按键时EN引脚上拉为高电平,松开时为低电平,可以通过按键切换蓝牙模块的工作模式。
(2)AT命令
HC-05蓝牙模块出厂时默认设置为从模式,配对码为1234,波特率为38400bps,1个停止位,没有校验位。
模块进入命令响应工作模式(即AT模式)后,可以发送AT命令对模块进行配置,如查询模块状态、修改模块参数等。
AT命令是以字符“AT”开始,以回车换行结束的字符串,AT指令不区分字母大小写。发送AT命令时需要将EN引脚拉高一次,而部分AT命令需要将EN引脚一直设置为高电平才有效。

5.OLED模块介绍(简单了解原理)

OLED本身是没有显存的,它的显存是依赖于SSD1306提供的。而SSD1306提供一块显存,芯片具体的讲解见下文。SSD1306的显存总共为128*64bit大小,SSD1306将这些显存分为了8页。每页包含了128个字节,总共8页,这样刚好是128*64的点阵大小。
(1) 行地址
OLED 显示屏通常将其垂直方向(纵向)分为多个页,每页包含一组连续的行。在一个典型的 128x64 像素的 OLED 显示屏中,它通常被分成 8 页,每页 8 行,总共 64 行。每个页对应于显示器中的一个 8 行区域。页地址:页地址用来选择显示区域中的特定页。页地址的选择影响的是显示屏中哪一组 8 行被写入或读取数据。
如何设置:设置页地址的命令通常以 0xb0 为基地址,加上页的索引值 y。比如,页地址为 0xb0 + y,其中 y 是从 0 到 7 的整数,表示第 y 页。
(2) 列地址
列地址用于选择显示屏的水平位置,即在选定的页中显示的具体列。列地址:列地址由两个部分组成:高四位和低四位。在 OLED 控制器中,这些高低四位被分别设置,以便选择具体的列位置。
如何设置:列地址高四位:通常需要设置列地址的高四位,范围从 0 到 15(对应于 16 个可能的列地址高位值)。通常的命令是 0x10 加上列地址的高四位部分。列地址低四位:设置列地址的低四位,范围从 0 到 15(对应于 16 个可能的列地址低位值)。通常的命令是 0x01 加上列地址的低四位部分。
3.驱动部分代码
//1.驱动模块头文件部分的代码 MOTOR.h
#ifndef __MOTOR_H
#define __MOTOR_H
#include "SYS.h"
#include "LED.h"
#include "TIM.h"
#include "PWM.h"
#define PWM_SetRatio(x) (TIM3->CCR1=(x),TIM3->CCR2=(x),TIM3->CCR3=(x),TIM3->CCR4=(x))
#define Motor_Start() PWM_Start() //start motor without changing moving direction
#define Motor_Stop() PWM_Stop()
#define Motor_SetSpeed(x) PWM_SetRatio(x) //x in the range of [300, 900]
void MOTOR_Init(void);
void MOTOR_Forward(void);
void MOTOR_Backward(void);
void MOTOR_leftward(void);
void MOTOR_rightward(void);
#endif
//2.实现驱动部分的实现代码 MOTOR.c
#include "MOTOR.h"
/**
*@brief L298N模块的初始化
*@param NONE
*@retval NONE
**/
void MOTOR_Init(void)
{
LED_Init();
TIM_Init(72,1000);
PWM_Init(201);
}
/*
前A:PA6---------AENA
PA7---------AENB
PB3 PB4 PB5 PB6 控制IN1 IN2 IN3 IN4
后B:PB0--------BENA
PB1--------BENB
PC0 PC1 PC3 PC2 控制IN1 IN2 IN3 IN4
*/
void MOTOR_Forward(void)
{
//前轮部分
GPIO_WriteBit(GPIOB,GPIO_Pin_4|GPIO_Pin_5,Bit_SET);
GPIO_WriteBit(GPIOB,GPIO_Pin_3|GPIO_Pin_6,Bit_RESET);
//后轮部分
GPIO_WriteBit(GPIOC,GPIO_Pin_0|GPIO_Pin_2,Bit_SET);
GPIO_WriteBit(GPIOC,GPIO_Pin_1|GPIO_Pin_3,Bit_RESET);
}
void MOTOR_Backward(void)
{
//前轮部分
GPIO_WriteBit(GPIOB,GPIO_Pin_3|GPIO_Pin_6,Bit_SET);
GPIO_WriteBit(GPIOB,GPIO_Pin_4|GPIO_Pin_5,Bit_RESET);
//后轮部分
GPIO_WriteBit(GPIOC,GPIO_Pin_1|GPIO_Pin_3,Bit_SET);
GPIO_WriteBit(GPIOC,GPIO_Pin_0|GPIO_Pin_2,Bit_RESET);
}
void MOTOR_leftward(void)
{
//后轮部分
GPIO_WriteBit(GPIOB,GPIO_Pin_5,Bit_SET);
GPIO_WriteBit(GPIOB,GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_6,Bit_RESET);
//前轮部分
GPIO_WriteBit(GPIOC,GPIO_Pin_2,Bit_SET);
GPIO_WriteBit(GPIOC,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_3,Bit_RESET);
}
void MOTOR_rightward(void)
{
//后轮部分
GPIO_WriteBit(GPIOB,GPIO_Pin_5,Bit_SET);
GPIO_WriteBit(GPIOB,GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_6,Bit_RESET);
//前轮部分
GPIO_WriteBit(GPIOC,GPIO_Pin_0,Bit_SET);
GPIO_WriteBit(GPIOC,GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3,Bit_RESET);
}
//1.定时器时基初始化实现代码 TIM.c
#include "TIM.h"
extern uint16_t time;
/**
*@brief TIM3的时基初始化
*@param psc 精度 arr 量程
*@retval NONE
**/
void TIM_Init(uint16_t psc,uint16_t arr)
{
TIM_TimeBaseInitTypeDef timebaseinit;
TIM_TimeBaseStructInit(&timebaseinit);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
TIM_Cmd(TIM3,ENABLE);
timebaseinit.TIM_Prescaler = psc-1;
timebaseinit.TIM_CounterMode = TIM_CounterMode_Up;
timebaseinit.TIM_Period = arr-1;
timebaseinit.TIM_ClockDivision = TIM_CKD_DIV1; //不进行时钟分割
TIM_TimeBaseInit(TIM3,&timebaseinit);
TIM_ARRPreloadConfig(TIM3,ENABLE);
}
void TIM2_IRQHandler()
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
{
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1) == 1)
{
time++;
}
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
//2.驱动模块PWM实现代码 PWM.c
#include "PWM.h"
/**
*@brief PWM的初始化
*@param pulse 占空比
*@retval NONE
**/
void PWM_Init(uint16_t pulse)
{
#if MY_DRIVER
//寄存器开发
#else
//库函数开发
TIM_OCInitTypeDef pwminit;
TIM_OCStructInit(&pwminit);
pwminit.TIM_OCMode = TIM_OCMode_PWM1; //设置为PWM1模式
pwminit.TIM_Pulse = pulse-1; //设置脉冲宽度 占空比
pwminit.TIM_OutputState = TIM_OutputState_Disable;
pwminit.TIM_OCNPolarity = TIM_OCNPolarity_High; //设置高电平为有效电平
TIM_OC1Init(TIM3,&pwminit);
TIM_OC2Init(TIM3,&pwminit);
TIM_OC3Init(TIM3,&pwminit);
TIM_OC4Init(TIM3,&pwminit);
//预装载功能
TIM_OC1PreloadConfig(TIM3,TIM_OCPreload_Enable);
TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable);
TIM_OC3PreloadConfig(TIM3,TIM_OCPreload_Enable);
TIM_OC4PreloadConfig(TIM3,TIM_OCPreload_Enable);
#endif
}
void PWM_Start(void)
{
TIM_CCxCmd(TIM3, TIM_Channel_1, TIM_CCx_Enable);
TIM_CCxCmd(TIM3, TIM_Channel_2, TIM_CCx_Enable);
TIM_CCxCmd(TIM3, TIM_Channel_3, TIM_CCx_Enable);
TIM_CCxCmd(TIM3, TIM_Channel_4, TIM_CCx_Enable);
}
void PWM_Stop(void)
{
TIM_CCxCmd(TIM3, TIM_Channel_1, TIM_CCx_Disable);
TIM_CCxCmd(TIM3, TIM_Channel_2, TIM_CCx_Disable);
TIM_CCxCmd(TIM3, TIM_Channel_3, TIM_CCx_Disable);
TIM_CCxCmd(TIM3, TIM_Channel_4, TIM_CCx_Disable);
}
void PWM_ARM_Start(void)
{
TIM_CCxCmd(TIM2, TIM_Channel_1, TIM_CCx_Enable);
TIM_CCxCmd(TIM2, TIM_Channel_2, TIM_CCx_Enable);
TIM_CCxCmd(TIM2, TIM_Channel_3, TIM_CCx_Enable);
TIM_CCxCmd(TIM2, TIM_Channel_4, TIM_CCx_Enable);
}
void PWM_ARM_Stop(void)
{
TIM_CCxCmd(TIM2, TIM_Channel_1, TIM_CCx_Disable);
TIM_CCxCmd(TIM2, TIM_Channel_2, TIM_CCx_Disable);
TIM_CCxCmd(TIM2, TIM_Channel_3, TIM_CCx_Disable);
TIM_CCxCmd(TIM2, TIM_Channel_4, TIM_CCx_Disable);
}
4.机械臂控制部分代码
//1.机械臂舵机部分头文件代码 MEROBOTICARM.h
#ifndef __MEROBOTICARM__H
#define __MEROBOTICARM__H
#include "SYS.h"
#define ARM_Start() PWM_ARM_Start() //start motor without changing moving direction
#define ARM_Stop() PWM_ARM_Stop()
void MEARM_Init(void);
void PWM_SetCompare1(uint16_t compare);
void PWM_SetCompare2(uint16_t compare);
void PWM_SetCompare3(uint16_t compare);
void PWM_SetCompare4(uint16_t compare);
#endif
//2.机械臂舵机部分实现代码 MEROBOTICARM.c
#include "MEROBOTICARM.h"
//对机械臂的控制就是对四个舵机的控制 PS2手柄遥控改变的是舵机的转动角度
//机械臂四个舵机
//PA0(TIM2_CH1)------------正面左舵机
//PA1(TIM2_CH2)------------正面右舵机
//PA2(TIM2_CH3)------------机械夹舵机
//PA3(TIM2_CH4)------------底部转身舵机
/**
*@brief 对四个舵机及引脚的初始化
*@param NONE 舵机的时基配置为 72 20000 <=> 20ms
*@retval NONE
**/
void MEARM_Init(void)
{
//库函数开发
GPIO_InitTypeDef gpioinit;
TIM_TimeBaseInitTypeDef timeinit;
TIM_OCInitTypeDef pwminit;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
GPIO_StructInit(&gpioinit);
gpioinit.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
gpioinit.GPIO_Mode =GPIO_Mode_AF_PP;
gpioinit.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&gpioinit);
TIM_TimeBaseStructInit(&timeinit);
timeinit.TIM_CounterMode = TIM_CounterMode_Up;
timeinit.TIM_Prescaler = 72-1;
timeinit.TIM_Period = 20000-1;
timeinit.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM2,&timeinit);
TIM_ARRPreloadConfig(TIM2,ENABLE);
TIM_OCStructInit(&pwminit);
pwminit.TIM_OCMode = TIM_OCMode_PWM1; //对于舵机采用PWM1模式
pwminit.TIM_Pulse = 0;
pwminit.TIM_OCPolarity = TIM_OCPolarity_High;
pwminit.TIM_OutputState = TIM_OutputState_Enable;
TIM_OC1Init(TIM2,&pwminit);
TIM_OC2Init(TIM2,&pwminit);
TIM_OC3Init(TIM2,&pwminit);
TIM_OC4Init(TIM2,&pwminit);
TIM_OC1PreloadConfig(TIM2,TIM_OCPreload_Enable);
TIM_OC2PreloadConfig(TIM2,TIM_OCPreload_Enable);
TIM_OC3PreloadConfig(TIM2,TIM_OCPreload_Enable);
TIM_OC4PreloadConfig(TIM2,TIM_OCPreload_Enable);
TIM_Cmd(TIM2,ENABLE);
}
/**
*@brief 设置TIM2_CH1,2,3,4的PWM输出占空比
*@param compare
*@retval NONE
*/
void PWM_SetCompare1(uint16_t compare) //PA0 正面左舵机
{
TIM_SetCompare1(TIM2,compare);
}
void PWM_SetCompare2(uint16_t compare) //PA1 正面右舵机
{
TIM_SetCompare2(TIM2,compare);
}
void PWM_SetCompare3(uint16_t compare) //PA2 机械夹舵机
{
TIM_SetCompare3(TIM2,compare);
}
void PWM_SetCompare4(uint16_t compare) //PA3 机械臂底部舵机
{
TIM_SetCompare4(TIM2,compare);
}
重复的舵机初始化代码,大家掌握熟悉舵机的控制后,可根据自己对应定时器的通道更改自己的代码配置。
5.蓝牙遥控部分代码
//1.蓝牙遥控部分代码的头文件 Bluetooth.h
#ifndef __BLUETOOTH__H
#define __BLUETOOTH__H
#include "SYS.h"
#include "EXTI.h"
#include "MOTOR.h"
#include "MEROBOTICARM.h"
void Bluetooth_USART1_Init(void);
void Bluetooth_MOTOR(void);
#endif
//2.蓝牙遥控部分代码的实现 Bluetooth.c
#include "Bluetooth.h"
extern uint8_t rx_end;
extern uint16_t rx_data;
//USART1
//PA9(TX) -------- 蓝牙RX
//PA10(RX)-------- 蓝牙TX
/**
*@brief 蓝牙模块的NVIC初始化
*@param NONE
*@retval NONE
**/
void Bluetooth_NVIC_Init(void)
{
NVIC_InitTypeDef nvicinit;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
nvicinit.NVIC_IRQChannel = USART1_IRQn;
nvicinit.NVIC_IRQChannelPreemptionPriority = 0;
nvicinit.NVIC_IRQChannelSubPriority = 1;
nvicinit.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvicinit);
}
//USART1
//PA9(TX) -------- 蓝牙RX
//PA10(RX)-------- 蓝牙TX
/**
*@brief 蓝牙模块串口1的初始化
*@param NONE
*@retval NONE
**/
void Bluetooth_USART1_Init(void)
{
GPIO_InitTypeDef gpioinit;
USART_InitTypeDef usartinit;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
//配置TX引脚 PA10 PA10(RX)
gpioinit.GPIO_Pin = GPIO_Pin_10;
gpioinit.GPIO_Mode = GPIO_Mode_IN_FLOATING;
gpioinit.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&gpioinit);
//配置RX引脚 PA9
gpioinit.GPIO_Pin = GPIO_Pin_9;
gpioinit.GPIO_Mode = GPIO_Mode_Out_PP;
gpioinit.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&gpioinit);
//串口通信USART1
usartinit.USART_BaudRate = 115200;
usartinit.USART_WordLength =USART_WordLength_8b;
usartinit.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
usartinit.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
usartinit.USART_Parity = USART_Parity_No;
usartinit.USART_StopBits = USART_StopBits_1;
USART_Init(USART1,&usartinit);
Bluetooth_NVIC_Init();
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //使能接收中断
USART_Cmd(USART1,ENABLE);
}
void Bluetooth_MOTOR(void)
{
if(rx_end)
{
rx_end = 0;
GPIO_WriteBit(GPIOD,GPIO_Pin_2,Bit_RESET);
if(rx_data == 'F') //前进
{
Motor_Start();
MOTOR_Forward();
Motor_SetSpeed(1500);
GPIO_WriteBit(GPIOD,GPIO_Pin_2,Bit_SET);
}
else if(rx_data == 'B') //后退
{
Motor_Start();
MOTOR_Backward();
Motor_SetSpeed(1800);
GPIO_WriteBit(GPIOD,GPIO_Pin_2,Bit_RESET);
}
else if(rx_data == 'L') //左转
{
Motor_Start();
MOTOR_leftward();
Motor_SetSpeed(1800);
}
else if(rx_data == 'R') //右转
{
Motor_Start();
MOTOR_rightward();
Motor_SetSpeed(1800);
}
else if(rx_data == 'S') //刹车
{
Motor_Stop();
}
else if(rx_data == '4') //机械臂底部向左转
{
ARM_Start();
MEARM_Init();
PWM_SetCompare4(500);
}
else if(rx_data == '6') //机械臂底部向右转
{
ARM_Start();
MEARM_Init();
PWM_SetCompare4(1800);
}
else if(rx_data == '8') //夹子打开 //1800->1000 开-合
{
ARM_Start();
MEARM_Init();
PWM_SetCompare3(1200);
}
else if(rx_data == '2') //夹子闭合
{
ARM_Start();
PWM_SetCompare3(700);
}
else if(rx_data == '7') //左舵机下降(前伸) //600->1500 降-升
{
ARM_Start();
PWM_SetCompare1(600);
}
else if(rx_data == '1') //左舵机上升(后退)
{
ARM_Start();
PWM_SetCompare1(1500);
}
else if(rx_data == '9') //右舵机下降(前伸) //2500->1800 降-升
{
ARM_Start();
PWM_SetCompare2(2500);
}
else if(rx_data == '3') //右舵机上升(后退)
{
ARM_Start();
PWM_SetCompare2(1800);
}
}
}
1.初始化蓝牙串口:通过Bluetooth_USART1_Init初始化串口和GPIO,配置USART通信。
2.接收命令:通过蓝牙接收数据并存储在rx_data中,判断命令并根据命令执行不同的动作。Bluetooth_MOTOR()函数实现了对机器人运动和机械臂的控制,具体操作基于从蓝牙模块接收到的数据(rx_data),根据接收到的命令控制电机和机械臂动作。通过判断rx_end是否为1:如果接收到完整数据,则rx_end会被设置为1,表示可以处理接收到的数据并执行相应动作。
3.控制运动和机械臂:根据接收到的命令,控制电机、机械臂的运动,改变电机的速度和位置。
6.结束语
这里的蓝牙调试器选取网络上常见的安卓手机蓝牙调试器即可。有问题欢迎评论留言交流,希望大家都可以顺利做出自己的蓝牙遥控小车。
更多推荐



所有评论(0)