采用ESP-NOW实现遥杆控制小车
这篇教程会向你展示如何在家中用基础电子零件制作一辆用摇杆控制小车的示例。
废话不多说,先上效果
车子可以通过遥杆来控制,而且不只限于4个方向。这是一个对于初学者来说非常有趣的项目,占用的完成时间不会很多,通过它不仅可以学习到如何搭建车辆,还可以了解电机对车辆工作的作用,以及一些基本的电子知识、编程实践,甚至一些物理知识。
本篇文章采用ESP-NOW,如果又不懂的小伙伴可以看我的另外一篇文章,传送门:采用ESP-NOW实现ESP32/ESP8266之间的通信
整辆机器人车的电路图如教程所示,没有使用面包板,而是直接使用跳线在组件之间进行连接。
建立连接之前,确保将电池的地线连接到马达驱动器和ESP8266的GND。电池盒的电池正极连接到马达驱动器的9v插槽,马达驱动器的5v连接到ESP8266板的Vin现在,马达驱动器和ESP8266之间的通信连接如下:
ENA 14 // Enable/speed motors Right GPIO14(D5)
ENB 12 // Enable/speed motors Left GPIO12(D6)
IN_1 15 // L298N in1 motors Right GPIO15(D8)
IN_2 13 // L298N in2 motors Right GPIO13(D7)
IN_3 2 // L298N in3 motors Left GPIO2(D4)
IN_4 0 // L298N in4 motors Left GPIO0(D3)
最后,我们把BO电动机的连线的接口连接到马达驱动器上的电动机连接插槽。这样,马达驱动器就可以控制BO马达的运转了。
esp32代码
#include <esp_now.h>
#include <WiFi.h>
#define X_AXIS_PIN 32
#define Y_AXIS_PIN 33
#define SWITCH_PIN 15
// REPLACE WITH YOUR RECEIVER MAC Address
uint8_t broadcastAddress[] = {0x2C,0xF4,0x32,0x13,0x38,0x50};
// Structure example to send data
// Must match the receiver structure
typedef struct PacketData
{
byte xAxisValue;
byte yAxisValue;
byte switchPressed;
};
PacketData data;
esp_now_peer_info_t peerInfo;
/*此功能用于将0-4095操纵手柄值映射到0-254。因此127是我们发送的中心值。
*它还可以调整操纵手柄中的死区。
*Jostick值的范围为0-4095。但它的中心值并不总是2047。这没什么不同。
*所以我们需要在中心值上加一些死区。在我们的案例中是1800-2200。在这个死区范围内的任何值都被映射到中心127。
*/
int mapAndAdjustJoystickDeadBandValues(int value, bool reverse)
{
if (value >= 2200)
{
value = map(value, 2200, 4095, 127, 254);
}
else if (value <= 1800)
{
value = map(value, 1800, 0, 127, 0);
}
else
{
value = 127;
}
if (reverse)
{
value = 254 - value;
}
return value;
}
// callback when data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status)
{
Serial.print("\r\nLast Packet Send Status:\t ");
Serial.println(status);
Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Message sent" : "Message failed");
}
void setup() {
// Init Serial Monitor
Serial.begin(115200);
// Set device as a Wi-Fi Station
WiFi.mode(WIFI_STA);
// Init ESP-NOW
if (esp_now_init() != ESP_OK) {
Serial.println("Error initializing ESP-NOW");
return;
}
else
{
Serial.println("Succes: Initialized ESP-NOW");
}
//一旦ESPNow成功初始化,我们将注册发送CB到
//获取Trasnmitted数据包的状态
esp_now_register_send_cb(OnDataSent);
// Register peer
memcpy(peerInfo.peer_addr, broadcastAddress, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
// Add peer
if (esp_now_add_peer(&peerInfo) != ESP_OK){
Serial.println("Failed to add peer");
return;
}
else
{
Serial.println("Succes: Added peer");
}
pinMode(SWITCH_PIN, INPUT_PULLUP);
}
void loop() {
// Set values to send
data.xAxisValue = mapAndAdjustJoystickDeadBandValues(analogRead(Y_AXIS_PIN), false);
data.yAxisValue = mapAndAdjustJoystickDeadBandValues(analogRead(X_AXIS_PIN), false);
data.switchPressed = false;
if (digitalRead(SWITCH_PIN) == LOW)
{
data.switchPressed = true;
}
Serial.println((String)"x:" + data.xAxisValue + " y:"+ data.yAxisValue + " btn:"+ data.switchPressed);
// Send message via ESP-NOW
esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &data, sizeof(data));
if (result == ESP_OK) {
Serial.println("Sent with success");
}
else {
Serial.println("Error sending the data");
}
if (data.switchPressed == true)
{
delay(500);
}
else
{
delay(50);
}
}
烧录之后会自动打印摇杆值,当拨动摇杆,x、y值会变化,按下摇杆,btn置为1
注意,笔者这里Message failed是应为笔者关闭了小车,如果开启了小车电源,则提示Success
esp8266代码
#include <ESP8266WiFi.h>
#include <espnow.h>
//Right motor
int enableRightMotor=14;
int rightMotorPin1=15;
int rightMotorPin2=13;
//Left motorvv
int enableLeftMotor=12;
int leftMotorPin1=2;
int leftMotorPin2=0;
#define MAX_MOTOR_SPEED 1023 // 400 - 1023.
const int PWMFreq = 1000; /* 1 KHz */
const int PWMResolution = 8;
const int rightMotorPWMSpeedChannel = 4;
const int leftMotorPWMSpeedChannel = 5;
#define SIGNAL_TIMEOUT 1000 // This is signal timeout in milli seconds. We will reset the data if no signal
unsigned long lastRecvTime = 0;
int speed_Coeff = 3;
struct PacketData
{
byte xAxisValue;
byte yAxisValue;
byte switchPressed;
};
PacketData receiverData;
bool throttleAndSteeringMode = false;
// callback function that will be executed when data is received
void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len)
{
if (len == 0)
{
return;
}
memcpy(&receiverData, incomingData, sizeof(receiverData));
String inputData ;
inputData = inputData + "values " + receiverData.xAxisValue + " " + receiverData.yAxisValue + " " + receiverData.switchPressed;
Serial.println(inputData);
if (receiverData.switchPressed == true)
{
if (throttleAndSteeringMode == false)
{
throttleAndSteeringMode = true;
}
else
{
throttleAndSteeringMode = false;
}
}
if (throttleAndSteeringMode)
{
throttleAndSteeringMovements();
}
else
{
simpleMovements();
}
lastRecvTime = millis();
}
void simpleMovements()
{
if (receiverData.yAxisValue <= 75) //Move car Forward
{
rotateMotor(MAX_MOTOR_SPEED, MAX_MOTOR_SPEED);
}
else if (receiverData.yAxisValue >= 175) //Move car Backward
{
rotateMotor(-MAX_MOTOR_SPEED, -MAX_MOTOR_SPEED);
}
else if (receiverData.xAxisValue >= 175) //Move car Right
{
rotateMotor(-MAX_MOTOR_SPEED, MAX_MOTOR_SPEED);
}
else if (receiverData.xAxisValue <= 75) //Move car Left
{
rotateMotor(MAX_MOTOR_SPEED, -MAX_MOTOR_SPEED);
}
else //Stop the car
{
rotateMotor(0, 0);
}
}
void throttleAndSteeringMovements()
{
int throttle = map( receiverData.yAxisValue, 254, 0, -255, 255);
int steering = map( receiverData.xAxisValue, 0, 254, -255, 255);
int motorDirection = 1;
if (throttle < 0) //Move car backward
{
motorDirection = -1;
}
int rightMotorSpeed, leftMotorSpeed;
rightMotorSpeed = abs(throttle) - steering;
leftMotorSpeed = abs(throttle) + steering;
rightMotorSpeed = constrain(rightMotorSpeed, 0, 255);
leftMotorSpeed = constrain(leftMotorSpeed, 0, 255);
rotateMotor(rightMotorSpeed * motorDirection, leftMotorSpeed * motorDirection);
}
void rotateMotor(int rightMotorSpeed, int leftMotorSpeed)
{
if (rightMotorSpeed < 0)
{
digitalWrite(rightMotorPin1,LOW);
digitalWrite(rightMotorPin2,HIGH);
}
else if (rightMotorSpeed > 0)
{
digitalWrite(rightMotorPin1,HIGH);
digitalWrite(rightMotorPin2,LOW);
}
else
{
digitalWrite(rightMotorPin1,LOW);
digitalWrite(rightMotorPin2,LOW);
}
if (leftMotorSpeed < 0)
{
digitalWrite(leftMotorPin1,LOW);
digitalWrite(leftMotorPin2,HIGH);
}
else if (leftMotorSpeed > 0)
{
digitalWrite(leftMotorPin1,HIGH);
digitalWrite(leftMotorPin2,LOW);
}
else
{
digitalWrite(leftMotorPin1,LOW);
digitalWrite(leftMotorPin2,LOW);
}
analogWrite(rightMotorPWMSpeedChannel, rightMotorSpeed/speed_Coeff);
analogWrite(leftMotorPWMSpeedChannel, leftMotorSpeed/speed_Coeff);
}
void setUpPinModes()
{
pinMode(enableRightMotor,OUTPUT);
pinMode(rightMotorPin1,OUTPUT);
pinMode(rightMotorPin2,OUTPUT);
pinMode(enableLeftMotor,OUTPUT);
pinMode(leftMotorPin1,OUTPUT);
pinMode(leftMotorPin2,OUTPUT);
digitalWrite(rightMotorPin1, LOW);
digitalWrite(rightMotorPin2, HIGH);
analogWrite(enableRightMotor, MAX_MOTOR_SPEED);
digitalWrite(leftMotorPin1, LOW);
digitalWrite(leftMotorPin2, HIGH);
analogWrite(enableLeftMotor, MAX_MOTOR_SPEED);
rotateMotor(0, 0);
}
void setup()
{
setUpPinModes();
Serial.begin(115200);
WiFi.mode(WIFI_STA);
// Init ESP-NOW
if (esp_now_init() != 0)
{
Serial.println("Error initializing ESP-NOW");
return;
}
esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);
esp_now_register_recv_cb(OnDataRecv);
}
void loop()
{
//Check Signal lost.
unsigned long now = millis();
if ( now - lastRecvTime > SIGNAL_TIMEOUT )
{
rotateMotor(0, 0);
}
}