项目总结报告
Funpack第二季第三期:FireBeetle 2 ESP32-E IoT 开发板
本次项目有多个任务可以选择,我选择的是任务三:遥控小车
用FireBeetle ESP32-E开发板作为控制单元,制作一台可以遥控的小车,遥控方式可以是蓝牙、红外、espnow,wifi等方式实现车辆前进后退转向等功能。
实际完成图
一、项目介绍
本次项目是基于上海智位机器人股份有限公司(简称:DFRobot)推出的FireBeetle 2 ESP32-E IoT 开发板,是一款基于ESP-WROOM-32E双核芯片的主控板,它专为IoT设计。
开发版造型小巧,结构紧凑,同时引出绝大多数的IO口以供使用。
可以看出来板载了许多的外设,包括锂电池充放电管理,可编程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板,再免费打烊,从立创商城购买元器件,一套下来真挺不错的。
四、软件设计
首先需要确定几点基础需求:
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()
{
//不做处理
}
五、功能展示
请原谅外形不好看。。
使用ComAssistant软件将数据进行可视化显示出来,可以非常直观地看见数据的变化情况,方便调试。
摇杆数据变化
温度
湿度
气压
六、遇到的问题及对本活动的心得体会
首先非常高兴能参加此次的活动,总体上本次的任务比较简单,也没有遇到太多的问题,主要也是因为互联网上对ESP32这系列产品有着非常丰富且详尽的资料及例程,让我们迅速上手,当然问题也还是有过,就在写文档的前一天,发现控制板上的ESP32开发板竟然不能用了,然后测量了一下3v3端口的电压,发现竟然高达5V,就推测是供电芯片坏了,然后测量了一下该芯片的引脚,也是输出5V的电压,还以为这时候的ESP32已经被烧坏,后来把那个电源芯片拆掉后,由于没有同样型号的LDO,就飞线焊接了一块DC-DC的电源模块,通电后还能继续使用,没有坏掉。
还有就是快递原因,在购买了两个月之后,才收到快递开始制作,时间也比较紧迫。
这次的活动感觉尚有发挥的余地,以后可以继续完善,正在考虑加入陀螺仪来进行姿态的控制,以及重新将PCB板重新完善一下,也有考虑加入一个外壳,这样能更好看些
七、参考资料
1.乐鑫官网技术文档