当前位置:   article > 正文

基于Arduino的蓝牙通信_arduino蓝牙数据包

arduino蓝牙数据包

基于Arduino的蓝牙通信

前言: 本文主要以Arduino小车中的蓝牙遥控作为案例来讲解蓝牙通信。了解相关编程和数据设计。
功能和结构上:一个舵机控制小车前排车轮转向(模拟阿克曼转向结构),另外两个舵机控制车前部两轴云台,云台上放置ESP32 CAM用来拍摄。超声波传感器用来自动避障、三路红外用来巡线。


本例中用到的硬件:
主控UNO或Nano,
扩展板1块(板载有源蜂鸣器和蓝牙接口),
TT马达4个,
舵机3个,
超声波传感器1个,
三路红外传感器1个,
ESP32 CAM1个,
电源等


1.了解蓝牙模块

1.1.介绍

BT05 低功耗4.0蓝牙

  • 超低待机功耗 90uA~400uA
  • 超远连接距离100英尺/60米
  • 超快反应速度 0.4秒
  • 兼容iOS等多平台
  • 收发无字节限制,最高可达3K Bytes/秒

AT 指令只能在模块未连接状态下才能生效,一旦蓝牙模块与设备连接上,蓝牙模块即进入数据透传模式

AT指令集指南

1.2.引脚连接方式

  • 准备TTL转USB模块和蓝牙模块:(用Arduino板CH340再通过软串口同样可以在串口调试器上调试,不需要转接器)
    在这里插入图片描述
  • 连接线路
    在这里插入图片描述

2.串口调试助手(拓展,用于了解蓝牙AT命令)

2.1.打开即用串口助手

串口调试助手sscom下载

2.2.基础介绍

  • 1.确认端口号正确,而且没有被占用(这很重要,否则打开串口失败,例如Arduino IDE选中了该COM号之后在这里选同一个就会报警告);
  • 2.勾选“加回车换行”,这样在输入指令时就不需要加回车换行;
  • 3.波特率设置与蓝牙默认的保持一致(一般默认9600);
    在这里插入图片描述
    更多获取蓝牙信息的操作请看上文AT指令集指南

3.蓝牙调试APP作为小车遥控器

3.1.安装APP(安卓)

下载安装

3.2.APP使用介绍

来自大佬的教程

4.蓝牙通信数据设计

从功能上考虑遥控时需要通信的数据结构,一种是布尔值,一种是字符值。什么情况下用布尔值,例如控制小车左转弯,当你按下按钮发送信息左转,松手就不左转,只有动作和不动作两种情况,所以用布尔值。当你遥控小车想要不同速度行驶,这时发送速度信息就是速度值,是一个过程变化的量,用一个字节-128~127作为通信。同理,舵机控制转向左右角度值也是过程变化的量,同样各占用一个字节

4.1.蓝牙助手端数据设计

在这里插入图片描述
云台和单点控制组成一字节,剩下运动模式继续排在下一字节。移动方向摇杆和速度滑杆使用单独一字节。
APP内的数据结构设置:
在这里插入图片描述
这里面的逻辑值设置先后排序与下一节中代码设计有对应关系,不能乱调换位置。关于数据传输、数据帧等更多相关详细内容欢迎查看之前博文串行通信

4.2.主控板端代码数据设计

数据位为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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

为了更好的调试蓝牙数据,采用了软串口,避免频繁拔掉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;
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76

在调试过程中遇到小问题:在Arduino IDE中,Serial.print() 函数将整数类型自动解释为有符号整数,因此当你传递负数给 Serial.print() 函数时,它会将其解释为有符号整数进行打印,而不会打印负号。导致数值不正确,折腾了好一会儿,用一个(int8_t)搞定了。

打开串口监视器得到的信息与预期一致:

在这里插入图片描述

4.3.不一定的数据设计

直接一个按钮对应一个字节,按一下发一个字符。每一个指令都有对应一个字符,接收到一个字符证明就有一个按钮按下。


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;
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

总结:
后一种通信设计功能上同样可以实现,与前一种方式相比需要增加一些停止的指令,在按钮松开时要发送停止指令。同时设置的按钮指令也会更多,对于蓝牙调试APP中有限的按钮设置来说不太现实的。将布尔值的开关量用字符值来代替在通信效率上也差了一些。对于同一字节来讲每一条指令为互斥关系,按了前进就不能同时旋转舵机,而前一种数据设计一个字节包含多个操作,在实际应用上更灵活,整体代码关系更简单。

搞完后才感慨这次又了解了不少东西,蓝牙AT命令、软串口和借助CH340在串口调试助手上调试数据、数据类型的设计转换等等。明天元宵开耍!开心!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/一键难忘520/article/detail/933186
推荐阅读
  

闽ICP备14008679号