Potato Controller

本文介绍如何使用Potato Controller对MP0进行遥控。

原理

手机开启热点,MP0连接热点,手机通过UDP协议发送控制指令,MP0接收指令并执行。

ESP32代码

#include <WiFi.h>
#include <WiFiUdp.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <MPU6050_tockn.h>

MPU6050 mpu6050(Wire);

/**
 * @brief 巡线传感器引脚定义
 *
 */
#define IO_X1 35
#define IO_X2 34
#define IO_X3 39
#define IO_X4 36

/**
 * @brief 电机接口定义
 *
 */
#define IO_M1PWM 32
#define IO_M2PWM 18
#define IO_M3PWM 33
#define IO_M4PWM 19
#define IO_M1IN1 14
#define IO_M1IN2 13
#define IO_M2IN1 17
#define IO_M2IN2 5
#define IO_M3IN1 26
#define IO_M3IN2 27
#define IO_M4IN1 16
#define IO_M4IN2 4
/**
 * @brief 超声波接口定义
 */
#define IO_TRIG 23
#define IO_ECHO 25

/*-----OLED Definition-------*/
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

const char *sta_ssid = "LunarLobster";
const char *sta_password = "12345678";

#define MOTOR_DISTANCE_TO_CENTER 0.1

/*--------Keyboard Definition--------*/
int index1;
int index2;
int index3;
int index4;
int index5;
int index6;
int index7;

String joy_x;
String joy_y;
String but_a;
String but_b;
String but_x;
String but_y;
String but_l;
String but_r;

uint8_t send_buff[20];

WiFiUDP Udp;

int open_flag = 0;

/*------函数定义--------*/
/**
 * @brief 电机引脚初始化
 */
void MotorInit(void);
/**
 * @brief 电机速度设置
 */
void SetDirectionAndSpeed(int speed1, int speed2, int speed3, int speed4);
void SetSpeed(float vx_set, float vy_set, float wz_set);
/**
 * @brief 超声波引脚初始化
 */
void UltrasonicInit(void);
/**
 * @brief 超声波测距
 *
 * @return int
 */
int UltrasonicDistence(void);
/**
 * @brief 灰度巡线模块数据 x1 x2 x3 x4
 *
 * @return uint8_t
 */
uint8_t GetLine(void);


long duration;
float distance;
void setup()
{
    Serial.begin(9600);
    Wire.begin();

    delay(500);
    UltrasonicInit();

    /**各种外设初始化**/
    MotorInit();
    LineInit();

    pinMode(2, OUTPUT);
    digitalWrite(2, LOW);
    if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C))
    {
        Serial.println(F("SSD1306 allocation failed"));
        for (;;)
            ;
    }
    Serial.println("oled done");
    display.display();
    delay(2000);

    //    mpu6050.begin();
    //    mpu6050.calcGyroOffsets(true);
    //    Serial.println("mpu6050 done");

    display.clearDisplay();
    /**---------------------TCP Server Init---------------------------**/
    WiFi.mode(WIFI_STA);
    /*------------------connect to the internet-------------------*/
    WiFi.begin(sta_ssid, sta_password);

    display.clearDisplay();
    display.setTextSize(2);
    display.setTextColor(SSD1306_WHITE);
    display.setCursor(0, 0);
    display.println(sta_password);
    display.println(sta_ssid);
    display.print("Connecting");

    while (WiFi.status() != WL_CONNECTED)
    {
        Serial.print(".");
        display.display();
        delay(500);
    }
    Serial.println("Connected");
    Serial.print("IP Address:");
    Serial.println(WiFi.localIP());

    display.clearDisplay();

    display.setTextSize(2);
    display.setTextColor(SSD1306_WHITE);
    display.setCursor(0, 30);
    // display.println("shl-001");
    display.println(WiFi.localIP());
    display.display();

    Udp.begin(3000);
    digitalWrite(2, HIGH);
    delay(500);
    digitalWrite(2, LOW);
    delay(500);
}

void loop()
{
    static int err_cnt = 0;
    delay(2);
#if 1
    int packetSize = Udp.parsePacket();
    if (packetSize)
    {
        err_cnt = 0;
        digitalWrite(2, HIGH);
        char buf[packetSize + 1];
        String rx;
        Udp.read(buf, packetSize);
        rx = buf;

        index1 = rx.indexOf(':', 0);
        index2 = rx.indexOf(':', index1 + 1);
        index3 = rx.indexOf(':', index2 + 1);
        index4 = rx.indexOf(':', index3 + 1);
        index5 = rx.indexOf(':', index4 + 1);
        index6 = rx.indexOf(':', index5 + 1);
        index7 = rx.indexOf(':', index6 + 1);

        joy_x = rx.substring(0, index1);
        joy_y = rx.substring(index1 + 1, index2);
        but_a = rx.substring(index2 + 1, index3);
        but_b = rx.substring(index3 + 1, index4);
        but_x = rx.substring(index4 + 1, index5);
        but_y = rx.substring(index5 + 1, index6);
        but_l = rx.substring(index6 + 1, index7);
        but_r = rx.substring(index7 + 1, packetSize + 1);

        if (but_y.toInt())
        {
            SetDirectionAndSpeed(100, 100, 100, 100);
        }
        else if (but_a.toInt())
        {
            SetDirectionAndSpeed(-100, -100, -100, -100);
        }
        else if (but_x.toInt())
        {
            SetDirectionAndSpeed(-100, 100, 100, -100);
        }
        else if (but_b.toInt())
        {
            SetDirectionAndSpeed(100, -100, -100, 100);
        }
        else if (but_l.toInt())
        {
            SetDirectionAndSpeed(-100, 100, -100, 100);
        }
        else if (but_r.toInt())
        {
            SetDirectionAndSpeed(100, -100, 100, -100);
        }
        else
        {
            SetDirectionAndSpeed(0, 0, 0, 0);
        }
    }
    else
    {
        err_cnt++;
        if (err_cnt > 200)
            digitalWrite(2, LOW);
    }
#endif
}

/**
 * @brief 电机引脚初始化
 */
void MotorInit(void)
{
    pinMode(IO_M1PWM, OUTPUT);
    pinMode(IO_M2PWM, OUTPUT);
    pinMode(IO_M3PWM, OUTPUT);
    pinMode(IO_M4PWM, OUTPUT);

    pinMode(IO_M1IN1, OUTPUT);
    pinMode(IO_M1IN2, OUTPUT);
    pinMode(IO_M2IN1, OUTPUT);
    pinMode(IO_M2IN2, OUTPUT);
    pinMode(IO_M3IN1, OUTPUT);
    pinMode(IO_M3IN2, OUTPUT);
    pinMode(IO_M4IN1, OUTPUT);
    pinMode(IO_M4IN2, OUTPUT);
}

/**
 * @brief 电机速度设置
 */
void SetDirectionAndSpeed(int speed1, int speed2, int speed3, int speed4)
{

    /*不同电机接线方向可能不同,改IN1 和 IN2的逻辑*/
    if (speed1 < 0)
    {
        speed1 *= -1;
        digitalWrite(IO_M1IN1, HIGH);
        digitalWrite(IO_M1IN2, LOW);
        analogWrite(IO_M1PWM, speed1);
    }
    else
    {
        digitalWrite(IO_M1IN1, LOW);
        digitalWrite(IO_M1IN2, HIGH);
        analogWrite(IO_M1PWM, speed1);
    }
    if (speed2 < 0)
    {
        speed2 *= -1;
        digitalWrite(IO_M2IN1, LOW);
        digitalWrite(IO_M2IN2, HIGH);
        analogWrite(IO_M2PWM, speed2);
    }
    else
    {
        digitalWrite(IO_M2IN1, HIGH);
        digitalWrite(IO_M2IN2, LOW);
        analogWrite(IO_M2PWM, speed2);
    }
    if (speed3 < 0)
    {
        speed3 *= -1;
        digitalWrite(IO_M3IN1, HIGH);
        digitalWrite(IO_M3IN2, LOW);
        analogWrite(IO_M3PWM, speed3);
    }
    else
    {
        digitalWrite(IO_M3IN1, LOW);
        digitalWrite(IO_M3IN2, HIGH);
        analogWrite(IO_M3PWM, speed3);
    }
    if (speed4 < 0)
    {
        speed4 *= -1;
        digitalWrite(IO_M4IN2, HIGH);
        digitalWrite(IO_M4IN1, LOW);
        analogWrite(IO_M4PWM, speed4);
    }
    else
    {
        digitalWrite(IO_M4IN2, LOW);
        digitalWrite(IO_M4IN1, HIGH);
        analogWrite(IO_M4PWM, speed4);
    }
}

void UltrasonicInit(void)
{
    pinMode(IO_TRIG, OUTPUT);
    pinMode(IO_ECHO, INPUT);
}
int UltrasonicDistence(void)
{
    digitalWrite(IO_TRIG, HIGH);
    delayMicroseconds(10);
    digitalWrite(IO_TRIG, LOW);
    return pulseIn(IO_ECHO, HIGH);
}
void LineInit(void)
{
    pinMode(IO_X1, INPUT);
    pinMode(IO_X2, INPUT);
    pinMode(IO_X3, INPUT);
    pinMode(IO_X4, INPUT);
}
uint8_t GetLine(void)
{
    uint8_t x1, x2, x3, x4;
    uint8_t tmp = 0;
    x1 = digitalRead(IO_X1);
    x2 = digitalRead(IO_X2);
    x3 = digitalRead(IO_X3);
    x4 = digitalRead(IO_X4);
    tmp = x2 | (x1 << 1) | (x3 << 2) | (x4 << 3);
    return tmp;
}

void SetSpeed(float vx_set, float vy_set, float wz_set)
{
    int speed1, speed2, speed3, speed4;
    speed1 = vx_set - vy_set - MOTOR_DISTANCE_TO_CENTER * wz_set;
    speed2 = vx_set + vy_set + MOTOR_DISTANCE_TO_CENTER * wz_set;
    speed3 = vx_set + vy_set - MOTOR_DISTANCE_TO_CENTER * wz_set;
    speed4 = vx_set - vy_set + MOTOR_DISTANCE_TO_CENTER * wz_set;

    SetDirectionAndSpeed(speed1, speed2, speed3, speed4);
}

使用方法

首先将ESP32代码烧录到单片机中,在代码中需要更改WiFi的名称和密码和后面手机开的热点匹配。

单片机上电后打开手机热点,等待单片机连接成功后显示IP地址,然后打开手机APP,输入目标IP,点击设定。

此时手机APP上的对应按键按下时就会发送对应的指令到单片机。解析到joy_x,joy_y,but_a等变量中,注意此变量是字符串类型,映射成速度需要做一步转换。