赞
踩
前言: 本文主要以Arduino小车中的蓝牙遥控作为案例来讲解蓝牙通信。了解相关编程和数据设计。
功能和结构上:一个舵机控制小车前排车轮转向(模拟阿克曼转向结构),另外两个舵机控制车前部两轴云台,云台上放置ESP32 CAM用来拍摄。超声波传感器用来自动避障、三路红外用来巡线。
本例中用到的硬件:
主控UNO或Nano,
扩展板1块(板载有源蜂鸣器和蓝牙接口),
TT马达4个,
舵机3个,
超声波传感器1个,
三路红外传感器1个,
ESP32 CAM1个,
电源等
BT05 低功耗4.0蓝牙
AT 指令只能在模块未连接状态下才能生效,一旦蓝牙模块与设备连接上,蓝牙模块即进入数据透传模式
从功能上考虑遥控时需要通信的数据结构,一种是布尔值,一种是字符值。什么情况下用布尔值,例如控制小车左转弯,当你按下按钮发送信息左转,松手就不左转,只有动作和不动作两种情况,所以用布尔值。当你遥控小车想要不同速度行驶,这时发送速度信息就是速度值,是一个过程变化的量,用一个字节-128~127作为通信。同理,舵机控制转向左右角度值也是过程变化的量,同样各占用一个字节
云台和单点控制组成一字节,剩下运动模式继续排在下一字节。移动方向摇杆和速度滑杆使用单独一字节。
APP内的数据结构设置:
这里面的逻辑值设置先后排序与下一节中代码设计有对应关系,不能乱调换位置。关于数据传输、数据帧等更多相关详细内容欢迎查看之前博文串行通信
数据位为5个字节,加上包头、校验和包尾共8个字节的蓝牙数据包。
byte RX_package[8] = {0};
typedef struct //5字节分布:
{
byte ControlMode; //遥控八位:bit0~7:原地左转0x01、原地右转0x02、云台左转0x04、云台右转0x08、云台向上0x10、云台向下0x20、方向盘回正0x40、蜂鸣器打开0x80;
byte MoveMode; //运动模式四位:bit0~3:自由控制模式0x01、避障模式0x02、循迹模式0x04、跟随模式0x08
byte x_axis = 0; //摇杆X轴
byte y_axis = 0; //摇杆Y轴
byte speed = 0; //速度控制(App端控件范围默认-128~127,要想速度从0开始,就要加上128)
}rx_buffer;
为了更好的调试蓝牙数据,采用了软串口,避免频繁拔掉rx/tx。同时避免串口阻塞,增加Serialcount清阻。下面的小样例测试代码用Nano板测试的,蓝牙的TX连接Nano的D4,RX连接D3引脚,可以获取到调试APP发来的信息。
#include<SoftwareSerial.h> /* *SoftwareSerial(rxPin, txPin, inverse_logic) *rxPin:接收串行数据的引脚。 *txPin:传输串行数据的引脚。 *inverse_logic:用于反转输入位的含义(默认为正常逻辑)。如果设置, SoftwareSerial 将 RX 引脚上的低电平(引脚上通常为 0v) 视为 1 位(空闲状态),将高电平(引脚上通常为 5V)视为 0 位 。它还会影响写入 TX 引脚的方式。默认值为 false。 */ int myvalue =0; int Serialcount = 0; SoftwareSerial softSerial(4, 3); byte RX_package[8] = {0}; typedef struct //5字节分布: { byte ControlMode; byte MoveMode; byte x_axis = 0; //摇杆X轴 byte y_axis = 0; //摇杆Y轴 byte speed = 0; //速度控制 }rx_buffer; rx_buffer RX_buffer; void setup() { Serial.begin(9600); softSerial.begin(9600); // softSerial.listen(); // Serial.println(softSerial.isListening()); } void loop() { if(softSerial.available()>0){ delay(1); if(softSerial.readBytes(RX_package,8)){ if(RX_package[0] == 0xA5 && RX_package[7] == 0x5A){ Serialcount =0; RX_buffer.ControlMode = RX_package[1]; RX_buffer.MoveMode = RX_package[2]; RX_buffer.x_axis = RX_package[3]; RX_buffer.y_axis = RX_package[4]; RX_buffer.speed = map(RX_package[5], -128, 127, 0, 255);//-128~127 }else{ Serialcount++; // Serial.println("getit"); return; } Serial.print("[1]ControlMode:"); Serial.print(RX_package[1]); Serial.print("[2]MoveMode:"); Serial.print(RX_package[2]); Serial.print("[3]X:"); //无符号byte类型在print打印时自动转成有符号类型, //如果不转为int8_t看到的数值就不是负数,而是更大的数 Serial.print((int8_t)RX_buffer.x_axis); Serial.print("[4]Y:"); Serial.print((int8_t)RX_buffer.y_axis); Serial.print("[5]speed:"); Serial.println(RX_buffer.speed); } }else { delay(1); Serialcount++; if(Serialcount > 500) { Serialcount = 0; RX_buffer.ControlMode = 0; RX_buffer.MoveMode = 0; RX_buffer.x_axis = 0; RX_buffer.y_axis = 0; } } }
在调试过程中遇到小问题:在Arduino IDE中,Serial.print() 函数将整数类型自动解释为有符号整数,因此当你传递负数给 Serial.print() 函数时,它会将其解释为有符号整数进行打印,而不会打印负号。导致数值不正确,折腾了好一会儿,用一个(int8_t)搞定了。
打开串口监视器得到的信息与预期一致:
直接一个按钮对应一个字节,按一下发一个字符。每一个指令都有对应一个字符,接收到一个字符证明就有一个按钮按下。
if (Serial1.available() > 0) { //判断接收到的数据是否大于0 char ser_val = Serial1.read(); Serial.print("ser_val="); Serial.println(ser_val); switch (ser_val) { case 'S': F_MOD = 0; break; //车轮停止转动 case 'F': F_MOD = 1; Serial.print("FFFFF");break; //前进 case 'B': F_MOD = 2; break; //后退 case 'Q': F_MOD = 3; break; //方向盘回正后 原地左转 case 'E': F_MOD = 4; break; //方向盘回正后 原地右转 case 'H': servo_mod = 0; break; //云台、方向盘停止转动 case 'Z': servo_mod = 1; break; //方向盘左转 case 'X': servo_mod = 2; break; //方向盘回正 case 'C': servo_mod = 3; break; //方向盘右转 case 'O': servo_mod = 4; break; //云台回正 case 'I': servo_mod = 5; break; //云台左转 case 'P': servo_mod = 6; break; //云台右转 case 'A': servo_mod = 7; break; //云台向上 case 'D': servo_mod = 8; break; //云台向下 case 'J': Buzzer = true; break; //蜂鸣器打开 case 'K': Buzzer = false; break; //蜂鸣器关闭 case '4': Car_MOD = 0; break; //自由控制模式 case '5': Car_MOD = 1; break; //避障模式 case '6': Car_MOD = 2; Steer_middle(); break; //默认先回正方向,切寻迹模式 case '7': Car_MOD = 3; break; //跟随模式 default: break; } }
总结:
后一种通信设计功能上同样可以实现,与前一种方式相比需要增加一些停止的指令,在按钮松开时要发送停止指令。同时设置的按钮指令也会更多,对于蓝牙调试APP中有限的按钮设置来说不太现实的。将布尔值的开关量用字符值来代替在通信效率上也差了一些。对于同一字节来讲每一条指令为互斥关系,按了前进就不能同时旋转舵机,而前一种数据设计一个字节包含多个操作,在实际应用上更灵活,整体代码关系更简单。
搞完后才感慨这次又了解了不少东西,蓝牙AT命令、软串口和借助CH340在串口调试助手上调试数据、数据类型的设计转换等等。明天元宵开耍!开心!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。