Funpack2-3基于ESP32-E的ESPNOW遥控小汽车
使用两片ESP32处理器,基于ESPNOW协议制作遥控小汽车,实现基本的前进后退,两轮差动转向功能,以及温湿度气压音量探测功能
标签
嵌入式系统
ESP32
遥控小车
ESPNOW
six
更新2023-01-04
河南科技大学
1093

项目总结报告

Funpack第二季第三期:FireBeetle 2 ESP32-E IoT 开发板

本次项目有多个任务可以选择,我选择的是任务三:遥控小车

用FireBeetle ESP32-E开发板作为控制单元,制作一台可以遥控的小车,遥控方式可以是蓝牙、红外、espnow,wifi等方式实现车辆前进后退转向等功能。

 

实际完成图

Fo6M4VNDlyDctoYvE_t7wGFMUYj7

 

一、项目介绍

      本次项目是基于上海智位机器人股份有限公司(简称:DFRobot)推出的FireBeetle 2 ESP32-E IoT 开发板,是一款基于ESP-WROOM-32E双核芯片的主控板,它专为IoT设计。

      开发版造型小巧,结构紧凑,同时引出绝大多数的IO口以供使用。

FlFpcrU7BBaiAuRezegVlQwnhkDw

FiQNfg5-QpehKtFfmlfHcpAYXS_6

      可以看出来板载了许多的外设,包括锂电池充放电管理,可编程RGB的LED灯,普通led灯,复位和用户按键,PH2.0和GDI接口,以及多达22个物理GPIO端口。

      使用这一块开发版,再搭配相应的电源及电机驱动等外设,即可拥有一台遥控车!

 

二、开发环境准备

1.立创EDA,用于PCB硬件设计

2.Visual Studio Code,用于编写代码及下载程序

3.ComAssistant,用于串口调试

 

 

三、硬件设计

      由于只有开发板是不能完成任务的,还需要搭配其他的外设方能使用。

      设计遥控器和电机控制端,就需要两块ESP32开发板。

      在遥控器中,使用两个摇杆分别控制行走和转向,OLED屏用于显示信息,同时板载了9轴姿态传感器,后续再据此进行开发。 由于目前还未使用到其他按键,于是就未焊接。

      在电机控制板中,使用TB6612FNG芯片作为电机的驱动,使用3片4.2V锂电池串联,经DCDC电源模块降压为5v后给ESP32-E开发板供电,电机驱动直接使用电池电源供电。

      使用嘉立创一条龙服务,立创EDA绘制原理图及PCB板,再免费打烊,从立创商城购买元器件,一套下来真挺不错的。

FozRWcWQlqtawy_d1EJpY1dqs-Wx

FmGc-_ALQzpFkwPoVhDujuKLcIXX

FpO6epKIj4ecdxJPKaz4xbuX5Cyh

Fs6mTyx-xeW3ihIPR4Xbn_vpPAJx

 

 

四、软件设计

      首先需要确定几点基础需求:

      1:需要设计遥控端及受控端。

      2:遥控端与受控端之间使用ESP-NOW协议进行双向通信。

      3:遥控端使用两个摇杆作为输入设备,同时需要软件校正摇杆中点。

      4:遥控端需要OLED屏幕显示从受控端回传的信息。

      5:受控端需要产生2路高频PWM信号+4路高低电平信号提供给电机驱动芯片。

      6:受控端需要有丢信号保护措施。

      

      使用Visual Studio Code中的PlatformIO编写Arduino程序并下载,PlatformIO 是一个跨平台、跨架构、多框架、专业的嵌入式系统工程师和编写应用程序的软件开发人员的工具。Arduino 是一个基于易于使用的硬件和软件的开源电子平台。

      大体的设计思路就是在遥控器端读取两个摇杆的倾斜度,通过ESP-NOW协议发送到电机控制板上,电机控制板根据该数值进行处理,转为相应的PWM占空比数值,再根据该占空比数值,输出相应的PWM波形即可

 

      设计遥控端的程序

#include <Arduino.h>
#include <WiFi.h>
#include <esp_now.h>
#include <EEPROM.h>
#include <Ticker.h>
#include <U8g2lib.h>
#include <Wire.h>

//*******宏定义按键引脚**********//
#define EEPROM_SIZE 9

// LEFT-JOYSTICK
#define LX 35
#define LY 34
#define LK0 18
// RIGHT-JOYSTICK
#define RX 39
#define RY 36
#define RK0 15

// TRIGGER BUTTONS
//6个按键
#define LK1 23
#define LK2 19
#define LK3 2

#define RK1 12
#define RK2 4
#define RK3 16

//4个led灯
#define LED0 13
#define LED1 14
#define LED2 0
#define LED3 26

//*******宏定义按键数据**********//
// #define BK 23 //拨动开关
int buttons[8] = {LK0, LK1, LK2, LK3, RK0, RK1, RK2, RK3};

#define numberOfPotSamples 5  // Number of pot samples to take (to smooth the values)
#define delayBetweenSamples 2 // Delay in milliseconds between pot samples

uint16_t LX_read = 0;
uint16_t LY_read = 0;
uint16_t RX_read = 0;
uint16_t RY_read = 0;

uint8_t LX_zero = 127;
uint8_t LY_zero = 127;
uint8_t RX_zero = 127;
uint8_t RY_zero = 127;

uint16_t LX_to_send = 0;
uint16_t LY_to_send = 0;
uint16_t RX_to_send = 0;
uint16_t RY_to_send = 0;

bool LY_inverted = false;
bool LX_inverted = false;
bool RY_inverted = false;
bool RX_inverted = false;

uint16_t counter = 0;
uint16_t invert_counter = 0;

float voltage = 3.00;
int percentage = 0;

uint16_t potValue = 0;

//***********espnow*******************//
//全0xFF的Mac地址时广播到附近所有ESPNOW设备
uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
esp_now_peer_info_t peerInfo = {};

Ticker ticker1;                                                                                                //定时器对象
U8G2_SSD1306_128X32_UNIVISION_F_SW_I2C u8g2(U8G2_R1, /* clock=*/SCL, /* data=*/SDA, /* reset=*/U8X8_PIN_NONE); // Adafruit Feather ESP8266/32u4 Boards + FeatherWing OLED

// 设置数据结构体
typedef struct
{
  uint8_t LPotX; //左右摇杆
  uint8_t LPotY;
  uint8_t RPotX;
  uint8_t RPotY;
  bool buttonL0; //左右按键
  bool buttonL1;
  bool buttonL2;
  bool buttonL3;
  bool buttonR0;
  bool buttonR1;
  bool buttonR2;
  bool buttonR3;

  // uint8_t roll; //陀螺仪功能
  // uint8_t pitch;
  // uint8_t yaw;
} Data_Package;

Data_Package data; //Create a variable with the above structure

typedef struct
{
  int PWML; //左右电机pwm
  int PWMR; //左右电机pwm
  uint8_t LPotX;
  uint8_t LPotY;
  uint8_t RPotX;
  uint8_t RPotY;
  float Temp;
  float Humidity;
  float Pressure;
  uint16_t Voice;

} Data_Package_Recv;

Data_Package_Recv data_recv; //Create a variable with the above structure

void resetData()
{ //数据重置
  // Reset the values when there is no radio connection - Set initial default values
  data.LPotX = 127;
  data.LPotY = 127;
  data.RPotX = 127;
  data.RPotY = 127;
  data.buttonL0 = 1;
  data.buttonL1 = 1;
  data.buttonL2 = 1;
  data.buttonL3 = 1;
  data.buttonR0 = 1;
  data.buttonR1 = 1;
  data.buttonR2 = 1;
  data.buttonR3 = 1;
  // data.roll = 127;
  // data.pitch = 127;
  // data.yaw = 127;

data_recv.PWML = 0; //左右电机pwm
data_recv.PWMR= 0; //左右电机pwm
data_recv.LPotX= 0;
data_recv.LPotY= 0;
data_recv.RPotX= 0;
data_recv.RPotY= 0;
data_recv.Temp= 0;
data_recv.Humidity= 0;
data_recv.Pressure= 0;
 data_recv.Voice= 0;

}

// 数据发送回调函数
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status)
{
  char macStr[18];
  snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
           mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
  Serial.print("send_ok");
  //  Serial.print("Last Packet Sent to: "); Serial.println(macStr);
  //  Serial.print("Last Packet Send Status: "); Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}

// 数据接收回调函数
void OnDataRecv(const uint8_t *mac_addr, const uint8_t *incomingData, int data_len)
{
  char macStr[18];
  snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
           mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
  memcpy(&data_recv, incomingData, sizeof(data_recv)); //接收的数据
  Serial.println("recv_ok");

  Serial.print("{PWM}");
  Serial.print(data_recv.PWML);
  Serial.print(",");
  Serial.print(data_recv.PWMR);
  Serial.print(",");
  Serial.print(data_recv.LPotX);
  Serial.print(",");
  Serial.print(data_recv.LPotY);
  Serial.print(",");
  Serial.print(data_recv.RPotX);
  Serial.print(",");
  Serial.println(data_recv.RPotY);
  Serial.print(",");
  Serial.print(data_recv.Temp);
  Serial.print(",");
  Serial.print(data_recv.Humidity);
  Serial.print(",");
  Serial.println(data_recv.Pressure);
  Serial.print(",");
  Serial.println(data_recv.Voice);

  //OLED显示
  u8g2.clearBuffer();
  u8g2.setCursor(0, 20);
  u8g2.print(data_recv.PWML);
  u8g2.setCursor(0, 30);
  u8g2.print(data_recv.PWMR);
  u8g2.setCursor(0, 40);
  u8g2.print(data_recv.LPotX);
  u8g2.setCursor(0, 50);
  u8g2.print(data_recv.LPotY);
  u8g2.setCursor(0, 60);
  u8g2.print(data_recv.RPotX);
  u8g2.setCursor(0, 70);
  u8g2.print(data_recv.RPotY);
  u8g2.setCursor(0, 80);
  u8g2.print(data_recv.Temp);
  u8g2.setCursor(0, 90);
  u8g2.print(data_recv.Humidity);
  u8g2.setCursor(0, 100);
  u8g2.print((data_recv.Pressure)/1000);
  u8g2.setCursor(0, 110);
  u8g2.print(data_recv.Voice);
  u8g2.sendBuffer();
}

//初始化espnow
void InitESPNow()
{
  WiFi.mode(WIFI_STA); //打开wifi_sta模式
  WiFi.disconnect(); //断开WIFI连接
  if (esp_now_init() == ESP_OK)
  {
    Serial.println("ESPNow Init Success");
  }
  else
  {
    Serial.println("ESPNow Init Failed");
    ESP.restart();
  }
}

//配对连接
void pair_device()
{
  memcpy(&peerInfo.peer_addr, broadcastAddress, 6);
  if (!esp_now_is_peer_exist(broadcastAddress))
  {
    esp_now_add_peer(&peerInfo);
  }
}

// 发送数据
void sendData()
{ 
  esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *)&data, sizeof(data));
}

//*********data处理**********//
//设置引脚输入上拉
void pinmode_pullup()
{
  for (int i = 0; i < 8; i++)
  {
    pinMode(buttons[i], INPUT_PULLUP); //输入上拉
  }
}

//值,低-中-高,反向
//数据归一化
/**************************************************/
int map_normal(int val, int lower, int middle, int upper, bool reverse)
{
  val = constrain(val, lower, upper); //限幅
  if (val < middle)
    val = map(val, lower, middle, 0, 127); //等比例缩放
  else
    val = map(val, middle, upper, 127, 255);
  return (reverse ? 255 - val : val);
}

/**************************************************/
//使用ADC读取摇杆按键数据
void read_data()
{
  uint8_t i, j = 0;
  uint16_t potValues[4] = {0, 0, 0, 0};    //四个通道的累加值
  for (i = 0; i < numberOfPotSamples; i++) //循环采样的次数
  {
    potValues[0] += analogRead(LX);
    delay(delayBetweenSamples);

    potValues[1] += analogRead(LY);
    delay(delayBetweenSamples);
    potValues[2] += analogRead(RX);
    delay(delayBetweenSamples);
    potValues[3] += analogRead(RY);
    delay(delayBetweenSamples);
  }
  potValue = potValues[0] / numberOfPotSamples;
  LX_read = map(potValue, 0, 4095, 0, 255); //转换0
  potValue = potValues[1] / numberOfPotSamples;
  LY_read = map(potValue, 0, 4095, 0, 255); //转换1
  potValue = potValues[2] / numberOfPotSamples;
  RX_read = map(potValue, 0, 4095, 0, 255); //转换2
  potValue = potValues[3] / numberOfPotSamples;
  RY_read = map(potValue, 0, 4095, 0, 255); //转换3

  //归一化处理
  LX_to_send = map_normal(LX_read, 0, LX_zero, 255, LX_inverted);
  LY_to_send = map_normal(LY_read, 0, LY_zero, 255, LY_inverted);
  RX_to_send = map_normal(RX_read, 0, RX_zero, 255, RX_inverted);
  RY_to_send = map_normal(RY_read, 0, RY_zero, 255, RY_inverted);

  /*********************************************************************************************/
  //限制在0-255
  LX_to_send = constrain(LX_to_send, 0, 255);
  LY_to_send = constrain(LY_to_send, 0, 255);
  RX_to_send = constrain(RX_to_send, 0, 255);
  RY_to_send = constrain(RY_to_send, 0, 255);

  //数据赋给结构体
  data.LPotX = LX_to_send;
  data.LPotY = LY_to_send;
  data.RPotX = RX_to_send;
  data.RPotY = RY_to_send;

  //读按键值
  data.buttonL0 = digitalRead(LK0);
  data.buttonL1 = digitalRead(LK1);
  data.buttonL2 = digitalRead(LK2);
  data.buttonL3 = digitalRead(LK3);
  data.buttonR0 = digitalRead(RK0);
  data.buttonR1 = digitalRead(RK1);
  data.buttonR2 = digitalRead(RK2);
  data.buttonR3 = digitalRead(RK3);
}

//摇杆原点纠偏程序
void zero_test()
{
  Serial.println(" joy_zero_testing... ");
  read_data();
  delay(300);

  LX_to_send = map_normal(LX_read, 0, 127, 255, 0);
  LY_to_send = map_normal(LY_read, 0, 127, 255, 0);
  RX_to_send = map_normal(RX_read, 0, 127, 255, 0);
  RY_to_send = map_normal(RY_read, 0, 127, 255, 0);

  LX_to_send = constrain(LX_to_send, 0, 255);
  LY_to_send = constrain(LY_to_send, 0, 255);
  RX_to_send = constrain(RX_to_send, 0, 255);
  RY_to_send = constrain(RY_to_send, 0, 255);

  LX_zero = LX_to_send;
  LY_zero = LY_to_send;
  RX_zero = RX_to_send;
  RY_zero = RY_to_send;

  Serial.println(" Writing in EEPROM... ");
  delay(300);

  if (EEPROM.read(1) != LX_zero)
    EEPROM.write(1, LX_zero);
  if (EEPROM.read(2) != LY_zero)
    EEPROM.write(2, LY_zero);
  if (EEPROM.read(3) != RX_zero)
    EEPROM.write(3, RX_zero);
  if (EEPROM.read(4) != RY_zero)
    EEPROM.write(4, RY_zero);
  EEPROM.commit();

  Serial.println(" Done... ");
  Serial.print(" LX_zero: ");
  Serial.print(EEPROM.read(1));
  Serial.print(" LY_zero: ");
  Serial.print(EEPROM.read(2));
  Serial.print(" RX_zero: ");
  Serial.print(EEPROM.read(3));
  Serial.print(" RY_zero: ");
  Serial.println(EEPROM.read(4));
}

void eeprom_ini()
{
  EEPROM.begin(EEPROM_SIZE);

  if (EEPROM.read(0) != 56)
  { //判断是否首次使用(新的所有地址貌似是255的值)
    zero_test();
    EEPROM.write(0, 56);
    EEPROM.commit();
  }

  LX_zero = EEPROM.read(1);
  LY_zero = EEPROM.read(2);
  RX_zero = EEPROM.read(3);
  RY_zero = EEPROM.read(4);
}

//*************************//
//定时处理
void update_100ms()
{
  read_data(); //读取遥感按键值
  sendData(); //发送数据
}

//*************************//
void setup()
{
  Serial.begin(115200);
  delay(100);
  pinmode_pullup(); //引脚设置输入上拉
  eeprom_ini();     //EEPROM初始化
  //打印eeprom的值
  Serial.print(" LX_zero: ");
  Serial.print(EEPROM.read(1));
  Serial.print(" LY_zero: ");
  Serial.print(EEPROM.read(2));
  Serial.print(" RX_zero: ");
  Serial.print(EEPROM.read(3));
  Serial.print(" RY_zero: ");
  Serial.println(EEPROM.read(4));

  Serial.println("Initializing ESP-NOW...");
  // 初始化espnow
  InitESPNow();
  // 设置发送数据回调函数
  esp_now_register_send_cb(OnDataSent);
  // 设置接收数据回调函数
  esp_now_register_recv_cb(OnDataRecv);
  //配对连接另一个设备
  pair_device();
  //重置数据
  resetData();
  //发送初始数据
  read_data();

  Serial.print("Data Size: ");
  Serial.print(sizeof(data));
  Serial.println(" Bytes");
  Serial.print(" LX: ");
  Serial.print(data.LPotX);
  Serial.print(" LY: ");
  Serial.print(data.LPotY);
  Serial.print(" RX: ");
  Serial.print(data.RPotX);
  Serial.print(" RY: ");
  Serial.print(data.RPotY);
  Serial.print(" // L0: ");
  Serial.print(data.buttonL0);
  Serial.print(" L1: ");
  Serial.print(data.buttonL1);
  Serial.print(" L2: ");
  Serial.print(data.buttonL2);
  Serial.print(" L3: ");
  Serial.print(data.buttonL3);
  Serial.print(" R0: ");
  Serial.print(data.buttonR0);
  Serial.print(" R1: ");
  Serial.print(data.buttonR1);
  Serial.print(" R2: ");
  Serial.println(data.buttonR2);
  Serial.print(" R3: ");
  Serial.println(data.buttonR3);

  u8g2.begin();
  u8g2.clearBuffer();                  // clear the internal memory
  u8g2.setFont(u8g2_font_ncenB08_tr);  // choose a suitable font
  u8g2.drawStr(0, 20, "Hello World!"); // write something to the internal memory
  u8g2.sendBuffer(); // transfer internal memory to the display
  ticker1.attach_ms(100, update_100ms); //Ticker定时调用
}

void loop()
{
  //不做处理
}

 

 

设计电机控制板的程序

#include <Arduino.h>

#include <WiFi.h>
#include <esp_now.h>
#include <Ticker.h>
#include <Adafruit_AHTX0.h>
#include <Adafruit_BMP280.h>

//TB6612引脚控制
#define MotorA1 16
#define MotorA2 14
#define PWMA 17
#define MotorB1 4
#define MotorB2 12
#define PWMB 15
#define Motor_STBY 26

//速度限制
#define Speed_Min -1023
#define Speed_Max 1023
//转向限制
#define Turn_Min -511
#define Turn_Max 511

#define CHANNEL 1

Adafruit_AHTX0 aht;  //温湿度
Adafruit_BMP280 bmp; // 气压
Ticker ticker1;      //定时器
#define ANALOG_PIN 35

const uint16_t PWMFreq = 10000; /* 10k Hz */
const uint8_t PWMChanne0 = 0;   //PWM通道
const uint8_t PWMChanne1 = 1;

const uint8_t PWMResolution = 12; //PWM占空比位数//0-4095

int spd_motor1, spd_motor2;                                 //电机速度值
int lpad_x = 127, lpad_y = 127, rpad_x = 127, rpad_y = 127; //摇杆初始值

uint8_t tick_num = 0;

//***********espnow*******************//
//全0xFF的Mac地址时广播到附近所有ESPNOW设备
uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
esp_now_peer_info_t peerInfo = {};

// 设置数据结构体
typedef struct
{
  uint8_t LPotX; //左右摇杆
  uint8_t LPotY;
  uint8_t RPotX;
  uint8_t RPotY;
  bool buttonL0; //左右按键
  bool buttonL1;
  bool buttonL2;
  bool buttonL3;
  bool buttonR0;
  bool buttonR1;
  bool buttonR2;
  bool buttonR3;

} Data_Package;
Data_Package data; //遥控传递的数据结构

typedef struct
{
  int PWML; //左右电机pwm
  int PWMR; //左右电机pwm
  uint8_t LPotX;
  uint8_t LPotY;
  uint8_t RPotX;
  uint8_t RPotY;
  float Temp;
  float Humidity;
  float Pressure;
  uint16_t Voice;

} Data_Package_Send;
Data_Package_Send data_send; //Create a variable with the above structure

//配对连接pair with another ESP-NOW device
void pair_device()
{
  memcpy(&peerInfo.peer_addr, broadcastAddress, 6);
  if (!esp_now_is_peer_exist(broadcastAddress))
  {
    esp_now_add_peer(&peerInfo);
  }
}

void resetData()
{ //数据重置
  data.LPotX = 127;
  data.LPotY = 127;
  data.RPotX = 127;
  data.RPotY = 127;
  data.buttonL0 = 1;
  data.buttonL1 = 1;
  data.buttonL2 = 1;
  data.buttonL3 = 1;
  data.buttonR0 = 1;
  data.buttonR1 = 1;
  data.buttonR2 = 1;
  data.buttonR3 = 1;
}

//发送消息回调函数
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status)
{
  char macStr[18];
  snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
           mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
  Serial.print("send_ok");

  //  Serial.print("Last Packet Sent to: "); Serial.println(macStr);
  //  Serial.print("Last Packet Send Status: "); Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}

//收到消息回调函数
void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len)
{
  memcpy(&data, incomingData, sizeof(data));
  tick_num = 0;
  Serial.println("recv_ok");

  Serial.print("RECV_CAR: ");
  Serial.print(data.LPotX);
  Serial.print(",");
  Serial.print(data.LPotY);
  Serial.print(",");
  Serial.print(data.RPotX);
  Serial.print(",");
  Serial.print(data.RPotY);
  Serial.print(",");
  Serial.print(data.buttonL0);
  Serial.print(",");
  Serial.print(data.buttonL1);
  Serial.print(",");
  Serial.print(data.buttonL2);
  Serial.print(",");
  Serial.print(data.buttonL3);
  Serial.print(",");
  Serial.print(data.buttonR0);
  Serial.print(",");
  Serial.print(data.buttonR1);
  Serial.print(",");
  Serial.print(data.buttonR2);
  Serial.print(",");
  Serial.print(data.buttonR3);

  Serial.print(";     Data_size: ");
  Serial.println(sizeof(data));
}

// espnow发送数据
void sendData()
{
  esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *)&data_send, sizeof(data_send));
}

// config AP SSID
void configDeviceAP()
{
  String Prefix = "Slave:";
  String Mac = WiFi.macAddress();
  String SSID = Prefix + Mac;
  String Password = "12345678";
  bool result = WiFi.softAP(SSID.c_str(), Password.c_str(), CHANNEL, 0);
  if (!result)
  {
    Serial.println("AP Config failed.");
  }
  else
  {
    Serial.println("AP Config Success. Broadcasting with AP: " + String(SSID));
  }
}

//初始化espnow
void InitESPNow()
{
  //打开wifiSTA模式
  WiFi.mode(WIFI_STA);
  Serial.print("AP MAC: ");
  Serial.println(WiFi.softAPmacAddress());

  WiFi.disconnect();
  if (esp_now_init() == ESP_OK)
  {
    Serial.println("ESPNow Init Success");
  }
  else
  {
    Serial.println("ESPNow Init Failed");
    ESP.restart();
  }
}

//将遥杆数据转换成电机运行的数据
void Mapdata()
{
  //分别读左右摇杆数据
  int valLX = map(lpad_x, 0, 255, Turn_Min, Turn_Max);
  int valLY = map(lpad_y, 0, 255, Speed_Min, Speed_Max);
  int valRX = map(rpad_x, 0, 255, Turn_Min, Turn_Max);
  int valRY = map(rpad_y, 0, 255, Speed_Min, Speed_Max);
  //差速转向
  spd_motor1 = valLY + valRX;
  spd_motor2 = valLY - valRX;

  //限制电机的静止启动范围
  if (abs(spd_motor1) < 100)
    spd_motor1 = 0;
  if (abs(spd_motor2) < 100)
    spd_motor2 = 0;
  spd_motor1 = constrain(spd_motor1, -4095, 4095); //限幅
  spd_motor2 = constrain(spd_motor2, -4095, 4095);
}

//根据方向及PWM值运行电机
void M1Forward() //前进
{
  digitalWrite(MotorA1, LOW);
  digitalWrite(MotorA2, HIGH);
  ledcWrite(PWMChanne0, abs(spd_motor1));
}
void M1Backward() //后退
{
  digitalWrite(MotorA1, HIGH);
  digitalWrite(MotorA2, LOW);
  ledcWrite(PWMChanne0, abs(spd_motor1));
}
void M1Release() //停
{
  digitalWrite(MotorA1, LOW);
  digitalWrite(MotorA2, LOW);
  ledcWrite(PWMChanne0, 0);
}

void M2Forward()
{
  digitalWrite(MotorB1, LOW);
  digitalWrite(MotorB2, HIGH);
  ledcWrite(PWMChanne1, abs(spd_motor2));
}
void M2Backward()
{
  digitalWrite(MotorB1, HIGH);
  digitalWrite(MotorB2, LOW);
  ledcWrite(PWMChanne1, abs(spd_motor2));
}
void M2Release()
{
  digitalWrite(MotorB1, LOW);
  digitalWrite(MotorB2, LOW);
  ledcWrite(PWMChanne1, 0);
}

//根据速度值判断方向运行电机
void run_motors()
{
  if (spd_motor1 > 0)
  {
    M1Forward();
  }
  else if (spd_motor1 < 0)
  {
    M1Backward();
  }
  else
    M1Release(); //制动或自由滑行

  if (spd_motor2 > 0)
  {
    M2Forward();
  }
  else if (spd_motor2 < 0)
  {
    M2Backward();
  }
  else
    M2Release();
}

//定时调用
void update_100ms()
{

  sensors_event_t humidity, temp;
  tick_num++;
  if (tick_num > 10)
  {
    lpad_x = 127;
    lpad_y = 127;
    rpad_x = 127;
    rpad_y = 127;
    Serial.println("signal timeout");
  }
  else
  {
    lpad_x = data.LPotX;
    lpad_y = data.LPotY;
    rpad_x = data.RPotX;
    rpad_y = data.RPotY;
  }

  Mapdata();    //处理数据
  run_motors(); //运行电机

  aht.getEvent(&humidity, &temp); //温度湿度
  bmp.takeForcedMeasurement();    //气压

  Serial.print("Temperature: ");
  Serial.print(temp.temperature);
  Serial.println(" degrees C");
  Serial.print("Humidity: ");
  Serial.print(humidity.relative_humidity);
  Serial.println("% rH");

  data_send.PWML = spd_motor1;
  data_send.PWMR = spd_motor2;

  data_send.LPotX = lpad_x;
  data_send.LPotY = lpad_y;
  data_send.RPotX = rpad_x;
  data_send.RPotY = rpad_y;

  data_send.Temp = temp.temperature;
  data_send.Humidity = humidity.relative_humidity;
  data_send.Pressure = bmp.readPressure();
  data_send.Voice = analogRead(ANALOG_PIN);

  sendData(); //发送数据
}

//*******************************//
void setup()
{
  Serial.begin(115200); // 启动硬件串口(用于输出调试信息和接收控制指令)
  delay(100);

  /**********************************************/
  // 初始化 ESP-NOW
  Serial.println("Initializing ESP-NOW...");
  delay(100);

  //初始化ESPNOW
  InitESPNow();

  esp_now_register_send_cb(OnDataSent); // 注册发送数据回调函数
  esp_now_register_recv_cb(OnDataRecv); //注册接受数据回调函数
  //配对连接另一个设备
  pair_device();
  //初始化数据
  resetData();

  /**********************************************/
  pinMode(MotorA1, OUTPUT);
  pinMode(MotorA2, OUTPUT);
  pinMode(PWMA, OUTPUT);

  pinMode(MotorB1, OUTPUT);
  pinMode(MotorB2, OUTPUT);
  pinMode(PWMB, OUTPUT);
  pinMode(Motor_STBY, OUTPUT);

  digitalWrite(Motor_STBY, HIGH);

  ledcSetup(PWMChanne0, PWMFreq, PWMResolution); //通道,频率,占空比
  ledcSetup(PWMChanne1, PWMFreq, PWMResolution);
  ledcWrite(PWMChanne0, 0); //设置初始值
  ledcWrite(PWMChanne1, 0);

  ledcAttachPin(PWMA, PWMChanne0); //将通道绑定到引脚
  ledcAttachPin(PWMB, PWMChanne1);

  if (!aht.begin())
  {
    Serial.println("Could not find AHT? Check wiring");
    while (1)
      delay(10);
  }
  Serial.println("AHT10 or AHT20 found");
  if (!bmp.begin())
  {
    Serial.println(F("Could not find a valid BMP280 sensor, check wiring or "
                     "try a different address!"));
    while (1)
      delay(10);
  }
  bmp.setSampling(Adafruit_BMP280::MODE_FORCED,     /* Operating Mode. */
                  Adafruit_BMP280::SAMPLING_X2,     /* Temp. oversampling */
                  Adafruit_BMP280::SAMPLING_X16,    /* Pressure oversampling */
                  Adafruit_BMP280::FILTER_X16,      /* Filtering. */
                  Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */

  ticker1.attach_ms(100, update_100ms); //Ticker定时调用
}

void loop()
{
  //不做处理
}

 

 

五、功能展示

      

 

      请原谅外形不好看。。

Fs99g9KYRtKvRHot_ZflLXErTf3a

 

Fl6HBaSR7RSQWdiBA4VJedj0WIL0

      使用ComAssistant软件将数据进行可视化显示出来,可以非常直观地看见数据的变化情况,方便调试。

摇杆数据变化

FlWyP5oW9pMAp1asAVPxJSRoOgYX

温度

FhCT6sN4M5llwlM3qY_I059VX_Po

湿度

FmPfjRusj24xn7i6WQOw5vJ--YuD

气压

FjUa39w8wEMM0UrV9Tmp-Y8cZLO8

 

六、遇到的问题及对本活动的心得体会

      首先非常高兴能参加此次的活动,总体上本次的任务比较简单,也没有遇到太多的问题,主要也是因为互联网上对ESP32这系列产品有着非常丰富且详尽的资料及例程,让我们迅速上手,当然问题也还是有过,就在写文档的前一天,发现控制板上的ESP32开发板竟然不能用了,然后测量了一下3v3端口的电压,发现竟然高达5V,就推测是供电芯片坏了,然后测量了一下该芯片的引脚,也是输出5V的电压,还以为这时候的ESP32已经被烧坏,后来把那个电源芯片拆掉后,由于没有同样型号的LDO,就飞线焊接了一块DC-DC的电源模块,通电后还能继续使用,没有坏掉。

      还有就是快递原因,在购买了两个月之后,才收到快递开始制作,时间也比较紧迫。

      这次的活动感觉尚有发挥的余地,以后可以继续完善,正在考虑加入陀螺仪来进行姿态的控制,以及重新将PCB板重新完善一下,也有考虑加入一个外壳,这样能更好看些

 

七、参考资料

1.乐鑫官网技术文档

2.Arduino core for the ESP32

3.DFRobot产品资料库

4.很有趣的ESP32-NOW协议无线应用小车

 

 

 

附件下载
遥控器原理图+pcb.zip
驱动板原理图+pcb.zip
VsCodePIO平台Arduino代码.rar
ESP32RemoteControl中U8g2库源文件太大,删掉了,在库管理器直接安装上就可以
团队介绍
一普通大学生
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号