基于Arduino Nano33 BLE和Wio Terminal的遥控小车设计
本文设计了一个蓝牙遥控小车控制系统。系统主要包含车身和遥控器两部分。系统实现了小车的前进、后退、转向、加减速等控制功能,结构简单,具有一定的趣味性。
标签
Arduino
BLE
Funpack参赛
遥控车
WioTerminal
chinaking
更新2022-03-31
上海大学
1586

一、概述

本文设计了一个蓝牙遥控小车控制系统,系统主要包含车身和遥控器两部分。

车体的控制板为Arduino Nano33 BLE,作为BLE Server,用来接收BLE信号和驱动电机;遥控采用的Wio Terminal,作为BLE Client,主要是采集手柄的模拟量信号,进行显示并通过BLE发送。

系统实现了小车的前进、后退、转向、加减速等控制功能,结构简单,具有一定的趣味性。

FhXZSo-zsp0uFkM8Mnb1iTylAu4K

二、硬件设计

系统硬件包括车身和遥控器两部分。

2.1车身硬件设计

车身硬件主要元件如下表所示

序号

名称

数量

1

Arduino Nano33 BLE开发板

1

2

TB6612FNG电机驱动板

1

3

直流电机

4

车身的硬件连接图如下图所示:

Fkfv6Yk03sJo9KcvjlytXomvy_lC

车身的电源为DC5V,由充电宝供电。Arduino Nano33 BLE接收到遥控器的控制信号,通过IO端口输出,分成两路到电机驱动板的输入。

电机驱动板的AIN1,AIN2,PWMA为第1路输入;BIN1,BIN2,PWMB为第2路输入。AO1,AO2为第1路输出;BO1,BO2为第2路输出。

第1路输出接左侧两个电机,第2路输出接右侧两个电机。同侧的两个电机并联在一起。

以第1路为例,电机方向由AIN1和AIN2控制。当AIN1=1,AIN2=0,电机正转;当AIN1=0,AIN2=1,电机反转;当AIN1=0,AIN2=0,电机停止。通过PWMA端口接收的数值,可以控制电机转速。

因此,当左侧和右侧电机方向和速度都相同时,小车前进或后退;当速度不同时,通过差速可以实现车身转向控制。

2.2遥控硬件设计

遥控主要包括Wio Terminal和双轴手柄各1个。硬件连接图如下:

FqvlyOx7ZnCeiB6_EFecKtN7tvYP

双轴手柄的GND、VCC、X、Y分别连接Wio Terminal的GND、3.3V、A0、A1。

Wio Terminal对手柄模拟量信号进行AD转换,将0-3.3V电压信号,转换成0-1024。经过程序线性处理后,由BLE发送给车身的控制器。

三、软件设计

车身主要代码

#include <ArduinoBLE.h>
#include <math.h>


BLEService MyService("181A");
BLEUnsignedIntCharacteristic Character_X("2A88", BLEWrite | BLENotify);
BLEUnsignedIntCharacteristic Character_Y("2A89", BLEWrite | BLENotify);

//byte iCarState=0;//小车状态 0-停止 1-前进 2-后退 3-左 4-右   BLE通信接口由 接受一个变量 前后左右,改成接受两个变量 X和Y手柄值,0-255变化
int16_t iBle_X=120;//零位
int16_t iBle_Y=120;
int Speed;

void setup() {
Serial.begin(9600); 
//while (!Serial);

pinMode(LED_BUILTIN, OUTPUT);

pinMode(2, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);

pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);


if (!BLE.begin()) {
Serial.println("BLE failed to Initiate");
delay(500);
while (1);
}


BLE.setLocalName("Arduino Nano 33 BLE Sensors");
BLE.setAdvertisedService(MyService);
//MyService.addCharacteristic(temperatureChar);
MyService.addCharacteristic(Character_X);
MyService.addCharacteristic(Character_Y);


BLE.addService(MyService);
Character_X.readValue(iBle_X);
Character_Y.readValue(iBle_Y);

BLE.advertise();

Serial.println("Bluetooth device is now active, waiting for connections...");
}


void loop() {

BLEDevice central = BLE.central();
if (central) {
Serial.print("Connected to central: ");
Serial.println(central.address());
digitalWrite(LED_BUILTIN, HIGH);


while (central.connected()) {
delay(10);
 
 Character_X.readValue(iBle_X);
 Character_Y.readValue(iBle_Y);

 Serial.println("X: ");
 Serial.println(iBle_X);
 Serial.println("Y: ");
 Serial.println(iBle_Y);
 

//中间死区  静止
if ( (iBle_Y>=100) && (iBle_Y<=130) && (iBle_X>=100) && (iBle_X<=130)) {
  Speed=0;
  digitalWrite(2, LOW);
  digitalWrite(3, LOW);
  analogWrite(4, Speed);//Speed值0-255对应占空比 0-100
  
  digitalWrite(5, LOW); 
  digitalWrite(6, LOW);
  analogWrite(7, Speed);
  }

//向前直行 比例控制
else if ((iBle_Y>0) && (iBle_Y<100) && (iBle_X>=100) && (iBle_X<=130)) {
  Speed=230-iBle_Y;
  digitalWrite(2, HIGH);
  digitalWrite(3, LOW);
  analogWrite(4,Speed);
  
  digitalWrite(5, HIGH); 
  digitalWrite(6, LOW);
  analogWrite(7, Speed);
  }

  //向后直行 比例控制
else if ((iBle_Y>=130)  && (iBle_X>=100) && (iBle_X<=130)) {
  Speed=iBle_Y;
  digitalWrite(2, LOW);
  digitalWrite(3, HIGH);
  analogWrite(4,Speed);
  
  digitalWrite(5, LOW); 
  digitalWrite(6, HIGH);
  analogWrite(7, Speed);
  }

    //向左原地打转 
else if ((iBle_Y>=100)  && (iBle_Y<=130) && (iBle_X>0) && (iBle_X<100)) {
  //Speed=230-iBle_X; 可以不用比例
  digitalWrite(2, LOW);
  digitalWrite(3, HIGH);
  analogWrite(4,120);
  
  digitalWrite(5, HIGH); 
  digitalWrite(6, LOW);
  analogWrite(7, 120);
  }

    //向右原地打转 比例控制
else if ((iBle_Y>=100)  && (iBle_Y<=130) && (iBle_X>130)) {
 // Speed=iBle_X;
  digitalWrite(2, HIGH);
  digitalWrite(3, LOW);
  analogWrite(4,120);
  
  digitalWrite(5, LOW); 
  digitalWrite(6, HIGH);
  analogWrite(7, 120);
  }

//前进中左拐 低速 向左慢拐
else if ((iBle_Y>=50) && (iBle_Y<100) && (iBle_X>=50) && (iBle_X<100)) {
  Speed=0;
  digitalWrite(2, HIGH);
  digitalWrite(3, LOW);
  analogWrite(4,150);
  
  digitalWrite(5, HIGH); 
  digitalWrite(6, LOW);
  analogWrite(7, 180);
  }
//前进中左拐 低速 向左快拐
else if ((iBle_Y>=50) && (iBle_Y<100) && (iBle_X>=0) && (iBle_X<50)) {
  Speed=0;
  digitalWrite(2, HIGH);
  digitalWrite(3, LOW);
  analogWrite(4,120);
  
  digitalWrite(5, HIGH); 
  digitalWrite(6, LOW);
  analogWrite(7, 180);
  }
//前进中左拐 高速 向左慢拐
else if ((iBle_Y>0) && (iBle_Y<50) && (iBle_X>=50) && (iBle_X<100)) {
  Speed=0;
  digitalWrite(2, HIGH);
  digitalWrite(3, LOW);
  analogWrite(4,150);
  
  digitalWrite(5, HIGH); 
  digitalWrite(6, LOW);
  analogWrite(7, 200);
  }
//前进中左拐 高速 向左快拐
else if ((iBle_Y>0) && (iBle_Y<50) && (iBle_X>0) && (iBle_X<50) ) {
  Speed=0;
  digitalWrite(2, HIGH);
  digitalWrite(3, LOW);
  analogWrite(4,120);
  
  digitalWrite(5, HIGH); 
  digitalWrite(6, LOW);
  analogWrite(7, 200);
  }


//前进中右拐 低速 慢拐
else if ((iBle_Y>=50) && (iBle_Y<100) && (iBle_X>130) && (iBle_X<180)) {
  Speed=0;
  digitalWrite(2, HIGH);
  digitalWrite(3, LOW);
  analogWrite(4,180);
  
  digitalWrite(5, HIGH); 
  digitalWrite(6, LOW);
  analogWrite(7, 150);
  }
//前进中右拐 低速 快拐
else if ((iBle_Y>=50) && (iBle_Y<100) && (iBle_X>=180) ) {
  Speed=0;
  digitalWrite(2, HIGH);
  digitalWrite(3, LOW);
  analogWrite(4,180);
  
  digitalWrite(5, HIGH); 
  digitalWrite(6, LOW);
  analogWrite(7, 120);
  }
//前进中右拐 高速 慢拐
else if ((iBle_Y>0) && (iBle_Y<50) && (iBle_X>130) && (iBle_X<180)) {
  Speed=0;
  digitalWrite(2, HIGH);
  digitalWrite(3, LOW);
  analogWrite(4,200);
  
  digitalWrite(5, HIGH); 
  digitalWrite(6, LOW);
  analogWrite(7, 150);
  }
//前进中右拐 高速 快拐
else if ((iBle_Y>0) && (iBle_Y<50) && (iBle_X>=180)) {
  Speed=0;
  digitalWrite(2, HIGH);
  digitalWrite(3, LOW);
  analogWrite(4,200);
  
  digitalWrite(5, HIGH); 
  digitalWrite(6, LOW);
  analogWrite(7, 120);
  }
else {
  Speed=0;
  digitalWrite(2, LOW);
  digitalWrite(3, LOW);
  analogWrite(4, Speed);
  
  digitalWrite(5, LOW); 
  digitalWrite(6, LOW);
  analogWrite(7, Speed);
  }


}
}
digitalWrite(LED_BUILTIN, LOW);
Serial.print("Disconnected from central: ");
Serial.println(central.address());
}

遥控器主要代码

#include "rpcBLEDevice.h" //BLE库
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>

#include"TFT_eSPI.h" //LCD库
TFT_eSPI tft;
TFT_eSprite spr=TFT_eSprite(&tft);

#define IWIDTH  320
#define IHEIGHT 180

uint8_t ble_x;
uint8_t ble_y;

static BLEUUID serviceUUID(0x181A); //CAR service UUID
static BLEUUID characteristicUUID_x(0x2A88);//x characteristic UUID 手柄输出值(0-255)
static BLEUUID characteristicUUID_y(0x2A89);//y characteristic UUID

static boolean doConnect = false;//执行连接
static boolean isConnected = false;//已连接
static boolean doScan = false;//执行搜索Server

static BLERemoteService* pRemoteService;
static BLERemoteCharacteristic *pRemoteCharacteristic1;
static BLERemoteCharacteristic *pRemoteCharacteristic2;
static BLEClient*  pClient  = BLEDevice::createClient();

static BLEAdvertisedDevice* myDevice;
uint8_t bd_addr[6] = {0xB2, 0xAA, 0x9B, 0xDF, 0xD7, 0x60};//BLE Server的MAC地址,注意Aruduino里和手机APP搜到的MAC顺序是反的(60:D7:DF:9B:AA:98)
BLEAddress BattServer(bd_addr);

static void notifyCallback(
  BLERemoteCharacteristic* pBLERemoteCharacteristic,
  uint8_t* pData,
  size_t length,
  bool isNotify) {
    Serial.print("Notify callback for characteristic ");
    Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
    Serial.print(" of data length ");
    Serial.println(length);
    Serial.print("data: ");
    Serial.print(*(uint8_t *)pData);
}
 
class MyClientCallback : public BLEClientCallbacks {
  void onConnect(BLEClient* pclient) {
  }
 
  void onDisconnect(BLEClient* pclient) {
    isConnected = false;
    Serial.println("onDisconnect");
  }
};
 
 
bool connectToServer() {
    Serial.print("Forming a connection to ");
    Serial.println(myDevice->getAddress().toString().c_str());
 
    pClient->connect(myDevice);  // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private) 
    Serial.println(" - Connected to server");
 
    isConnected = true;
    return true;
}
bool disconnectToServer() {
   
    pClient->disconnect();  // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private)
    Serial.println(" - DisConnected to server");
    isConnected = false;  
}



/**
 * Scan for BLE servers and find the first one that advertises the service we are looking for.
 */
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
 /**
   * Called for each advertising BLE server.
   */
  void onResult(BLEAdvertisedDevice advertisedDevice) {
    Serial.print("BLE Advertised Device found: ");
    Serial.println(advertisedDevice.toString().c_str());
 
    // We have found a device, let us now see if it contains the service we are looking for.
    if (memcmp(advertisedDevice.getAddress().getNative(),BattServer.getNative(), 6) == 0) {
      Serial.print("BATT Device found: ");
      Serial.println(advertisedDevice.toString().c_str());
      BLEDevice::getScan()->stop();
      Serial.println("new BLEAdvertisedDevice");
      myDevice = new BLEAdvertisedDevice(advertisedDevice);
      Serial.println("new BLEAdvertisedDevice done");
      doConnect = true;
      doScan = true;    
  } // onResult
  }
}; // MyAdvertisedDeviceCallbacks

 
void setup() {
  Serial.begin(115200);
  
  pinMode(A0, INPUT);
  pinMode(A1, INPUT);
  
  tft.begin();
  tft.setRotation(3);//设置屏幕方向

  //绘制欢迎页 
  tft.fillScreen(TFT_WHITE);    
  tft.setTextColor(TFT_BLUE, TFT_WHITE); 
  tft.setTextSize(2);
  tft.drawString("Connecting to",50,65);
  tft.drawString("BLE Server...",50,95);

  spr.createSprite(IWIDTH,IHEIGHT);//不知为何画布大小无法设置成 TFT_WIDTH,TFT_HEIGHT  即320,240
  
 // while(!Serial){}; //不用串口监视时可注释掉
  delay(2000);
  Serial.println("Starting Arduino BLE Client application...");
  BLEDevice::init("");
 
  // Retrieve a Scanner and set the callback we want to use to be informed when we
  // have detected a new device.  Specify that we want active scanning and start the
  // scan to run for 5 seconds.
  BLEScan* pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
  pBLEScan->setInterval(1349);
  pBLEScan->setWindow(449);
  pBLEScan->setActiveScan(true);
  pBLEScan->start(5, false);


} // End of setup.
 
 
// This is the Arduino main loop function.
void loop() {
    int x = analogRead(A0);//0-1024对应0-3.3V
    int y = analogRead(A1);

    float volt_x = x * (3.3 / 1024.0); //将返回值换算成电压 只表示换算关系,实际不用
    float volt_y = y * (3.3 / 1024.0); //将返回值换算成电压

    ble_x=x/4+1;//缩小BLE发送数据的长度,不超出unit8_t范围,控制在0-255之间
    ble_y=y/4+1;
 
  Serial.println("X = ");
  Serial.println(ble_x);
  Serial.println("Y = ");
  Serial.println(ble_y);
 
 //显示界面

   //绘制标题栏
   spr.fillSprite(TFT_WHITE);
   spr.fillRect(0,0,320,50,TFT_BLUE);
   spr.setTextColor(TFT_WHITE);
   spr.setTextSize(3);
   spr.drawString("BLE CAR",100,15);


   //X
   spr.setTextColor(TFT_BLACK);
   spr.setTextSize(2);
   spr.drawString("X",70,65);
   spr.setTextSize(3);
   spr.drawNumber(ble_x,50,95);

   //Y
   spr.setTextColor(TFT_BLACK);
   spr.setTextSize(2);
   spr.drawString("Y",220,65);
   spr.setTextSize(3);
   spr.drawNumber(ble_y,200,95);
  
   if (doConnect == true) {
    connectToServer();
    doConnect = false;
    }

    
 if (isConnected ==true)
   {
  pRemoteCharacteristic1=pClient->getService(serviceUUID)->getCharacteristic(characteristicUUID_x);
  pRemoteCharacteristic1->writeValue(ble_x);

  pRemoteCharacteristic2=pClient->getService(serviceUUID)->getCharacteristic(characteristicUUID_y);
  pRemoteCharacteristic2->writeValue(ble_y);
  
  spr.setTextSize(2);
  spr.drawString("ONLINE",200,150);
   }
   else
   {
    spr.setTextSize(2);
    spr.drawString("OFFLINE",200,150);
   }
    spr.pushSprite(0,0);
} // End of loop

 

四、心得体会

1.使用Wio Terminal进行AD转换时,需要注意AD采样的电压范围,只能0-3.3V,使用5V时会超限。

2.合理确定BLE通讯接口。系统设计时尝试使用4个characterUUID传输手柄上、下、左、右数据时,发现延时较大,控制实时性严重下降;后改为2个characterUUID传输手柄X轴和Y轴数据,控制效果有明显提升。因此,要考虑“带宽”对通讯和控制的影响。

3.因程序分为车体程序和遥控程序两部分,需注意程序的版本对应关系。

附件下载
BLE_Client_Wio3.2.rar
车身ArduinoNano33BLE代码
BLE_Server_Nano3.2.rar
遥控WioTerminal代码
团队介绍
老胡,电子爱好者。
团队成员
chinaking
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号