ESP8266 remotely controls ESP8266 through MQTT protocol

ESP8266 button handle remote control ESP8266 remote control car (little yellow car version)
ESP8266 remotely controls ESP8266 through MQTT protocol
ESP8266 button handle code

The MQTT server I built is EMQ. Tencent Cloud or Alibaba Cloud can be used. I have tutorials. If you need CSDN, leave your information and I will contact you.

The button handle and the receiving board PCB will also be released on CSDN! ! Home page view

The entire program has been personally tested and reliable, guaranteed by everyone, I have used it! Station B posted a video

B station video: Feiyu Intelligent Control 4G remote control car has the same button handle inside
Now no longer for sale, hereby open source!

Install the library

PubSubClient Arduino IDE installation
WiFiManager can be downloaded from the official website of Baidu Tai Chi Maker
ESP8266 3.0 or above Arduino IDE installation

Below is the button handle made by ESP8266

#include <PubSubClient.h> //MQTT_TEST
#include <Ticker.h>

#include <WiFiManager.h> // Reference WiFi manager library
#include <EEPROM.h> //Use EEPROM to save data
#include <String.h>
#ifdef ESP8266 // If the chip type is ESP8266
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <ESP8266WebServer.h> // ESP8266 website server library
ESP8266WebServer server(80); // Create a website server
#elif defined(ESP32) //If the chip type is ESP32
#include "WiFi.h"
#include <ESPmDNS.h>
#include <WebServer.h> // ESP32 website server library
WebServer server(80); // Create a website server
#endif
/********************** parameter settings *****************/
char publishTopic[] = "fff123" ;
//Set wifi access information (please modify it according to your WiFi information)
//const char* mqttServer = "mqtt-cn-i7m2omxjh0b.mqtt.aliyuncs.com";
const char* mqttServer = "114.132.158.66";//ip address
//MQTT server connection username and password
//const char* mqttUserName = "Signature|LTAI5t7qtbYjGYBcpci2mjs9|mqtt-cn-i7m2omxjh0b";
//const char* mqttPassword = "AyjfUDaezp72ZxChHnYKAT4npwc=";

#define BUTTON_forward 4
#define BUTTON_RIGHT 12
#define BUTTON_BACK 5
#define BUTTON_LEFT 14
#define BUTTON_accelerate 13
#define BUTTON_decelerate 2
#define BUTTON_oil 16

byte forward_flag = 0;
byte back_flag = 0;
byte right_flag = 0;
byte left_flag =0;
byte accelerate_flag =0;
byte decelerate_flag =0;
byte oil_flag =0;

byte forward_foward = 0;
byte back_back = 0;
byte right_right = 0;
byte left_left = 0;
byte accelerate_accelerate =0;
byte decelerate_decelerate =0;
byte oil_oil =0;

byte pwm_ac_flag=0;
byte pwm_de_flag=0;
 
// If the above MQTT server cannot connect normally, please go to the following page to find a solution.
// http://www.taichi-maker.com/public-mqtt-broker/

Ticker ticker;
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

int count; //Variable for ticker counting

#define AP_SSID "Spaceship Remote Control" // Customized ESP device AP name
#define AP_PWD "12345678" // Customized AP password
#define TRIGGER_PIN 0 // Enable the button pin of "Wi-Fi Settings Portal"
unsigned int timeout = 180; // Set the timeout seconds for the entrance
unsigned int startTime = millis(); // Record the start time of the setting entry
bool portalRunning = false; // Set whether the portal is running, the default is "No"


WiFiManager wifiManager; // Create WiFiManager object


bool shouldSaveConfig = false; //Flag bit whether to save data
//Callback function, used to notify us that we need to save the configuration
void saveConfigCallback () {<!-- -->
  Serial.println("Should save config");
  shouldSaveConfig = true;
}

/************************ Start programming memory section *****************/
#define EEPROM_SALT 12664 //Used to verify whether the read data is valid
typedef struct
{<!-- -->
  int salt = EEPROM_SALT;
  char Secret_Key[15] = "";
}WMSettings;//Structure, used to save secret key data

WMSettings blynk;

//EEPROM reading function
void eeprom_read()
{<!-- -->
  EEPROM.begin(3000);
  EEPROM.get(2500, blynk);
  EEPROM.end();
  
  Serial.print("The secret key read from EEPROM is:");
  Serial.println(blynk.Secret_Key);
}

//EEPROM storage function
void eeprom_saveconfig()
{<!-- -->
  EEPROM.begin(3000);
  EEPROM.put(2500, blynk);
  EEPROM.commit();
  EEPROM.end();
}


/************************ WIFI administrator parameter configuration and reset function *****************/
WiFiManagerParameter custom_Secret_Key("KEY", "Secret Key", blynk.Secret_Key, 15);

//Used to reset WIFI settings and secret keys
void doWiFiManager()
{<!-- --> // Perform the work of WiFi administrator
  if (portalRunning)
  {<!-- --> // If "Set Entry" is being executed...
    wifiManager.process(); // Process the client connection request of "Setting Portal"
    // If "Settings Portal" is enabled for longer than the default time (180 seconds)...
    if ((millis() - startTime) > (timeout * 1000))
    {<!-- -->
      Serial.println(""Wi-Fi Settings Entry" operation timed out...");
      portalRunning = false; // Set to "not executing"
      if(strlen(custom_Secret_Key.getValue()) == 12)//Simply determine whether the filled in secret key is valid
      {<!-- -->
        strcpy(blynk.Secret_Key, custom_Secret_Key.getValue());
        Serial.print("The secret key written to EEPROM is:");
        Serial.println(blynk.Secret_Key);
        eeprom_saveconfig();
      }
      wifiManager.stopConfigPortal(); // Stop "Setting Portal"
      Serial.println("Restarting your device");
      delay(1000);
      ESP.restart();//Restart the ESP device
// Blinker.begin(blynk.Secret_Key, WiFi.SSID().c_str(), WiFi.psk().c_str());
// Blinker.attachData(dataRead);
// Button1.attach(button1_callback);
     }
    }
    
    // If the pin that enables "Settings Entry" is clicked
    // And the value of portalRunning is not true...
    if (digitalRead(TRIGGER_PIN) == LOW & amp; & amp; (!portalRunning))
    {<!-- -->
      Serial.println("The button has been pressed, starting the setting portal.");
      wifiManager.setConfigPortalBlocking(false); // Set to "non-shelved" mode
      wifiManager.startConfigPortal(AP_SSID, AP_PWD); // Enable Wi-Fi AP
  
      portalRunning = true; // Set to "Setting Portal" and executing
      startTime = millis(); // Record the current time
    }
}

void setup() {<!-- -->
    pinMode(BUTTON_forward, INPUT);
    digitalWrite(BUTTON_forward, HIGH);
    pinMode(BUTTON_RIGHT, INPUT);
    digitalWrite(BUTTON_RIGHT, HIGH);
    pinMode(BUTTON_BACK, INPUT);
    digitalWrite(BUTTON_BACK, HIGH);
    pinMode(BUTTON_LEFT, INPUT);
    digitalWrite(BUTTON_LEFT, HIGH);
     pinMode(BUTTON_accelerate, INPUT);
    digitalWrite(BUTTON_accelerate, HIGH);
    pinMode(BUTTON_decelerate, INPUT);
    digitalWrite(BUTTON_decelerate, HIGH);
    pinMode(BUTTON_oil, INPUT);
    digitalWrite(BUTTON_oil, HIGH);

 
    pinMode(TRIGGER_PIN, INPUT_PULLUP); //Configure the pin that triggers the reset function //Configure the pin that triggers the reset function
    
    Serial.begin(115200);

    WiFi.mode(WIFI_STA); //Wi-Fi is set to STA mode; the default mode is STA + AP
    /****************************************************** ***************/
    delay(500);
    eeprom_read(); //Read the secret key data in EEPROM
    //Determine whether the secret key data read in EEPROM is contaminated
    if (blynk.salt != EEPROM_SALT)
    {<!-- -->
      Serial.println("Invalid settings read from EEPROM, tried to use default value");
      WMSettings defaults;
      blynk = defaults;
    }
  
    
    wifiManager.addParameter( & amp;custom_Secret_Key); //Add secret key parameters in the WiFi administrator interface
    wifiManager.setSaveConfigCallback(saveConfigCallback);
    
    /********************** For testing only *******************/
    //Reset settings - test
// wifiManager.resetSettings();
    //Set the minimum quality of the signal so that it ignores APs at this quality
    //Default is 8%
// wifiManager.setMinimumSignalQuality(30);
    //Set the connection timeout
// wifiManager.setConnectTimeout(CONNECT_TIMEOUT);
// wifiManager.setTimeout(AP_TIMEOUT);
  
    //Then enter a blocking loop, waiting for configuration
    if (!wifiManager.autoConnect(AP_SSID,AP_PWD))
    {<!-- -->
      Serial.println("Failed to connect or timed out");
      Serial.println("Restarting your device");
      delay(1000);
      ESP.restart();
    }

    //Going here means you are connected to WiFi
    Serial.println("Already connected to WiFi:)");

    //Simply determine whether the secret key is valid
    if(strlen(custom_Secret_Key.getValue()) == 12)
    {<!-- -->
      strcpy(blynk.Secret_Key, custom_Secret_Key.getValue());
      Serial.print("The secret key is:");
      Serial.println(blynk.Secret_Key);
      eeprom_saveconfig();
    }//At this point, data saving ends

  
    // Automatically connect to WiFi. The parameter of the following statement is the WiFi name when connecting to ESP8266
// wifiManager.autoConnect("AutoConnectAP");
    
    // If you want to add a password to the WiFi, you can use the following statement:
    // wifiManager.autoConnect("AutoConnectAP", "12345678");
    // 12345678 in the above statement is the password to connect to AutoConnectAP
    // After the WiFi connection is successful, the connection success information will be output through the serial monitor
    Serial.println("");
    Serial.print("ESP8266 Connected to ");
    Serial.println(WiFi.SSID()); // Serial port prints the currently connected WIFI name
    Serial.print("IP address:\t");
    Serial.println(WiFi.localIP()); // Serial port prints the IP of the currently connected WIFI
    Serial.printf("password: %s\\
", WiFi.psk().c_str());//Serial port prints the currently connected WIFI password

     //Set the MQTT server and port number
    mqttClient.setServer(mqttServer, 1883);
  
    // Connect to MQTT server
    connectMQTTServer();
  
    // Ticker timing object
    ticker.attach(1, tickerCount);

}

void loop() {<!-- -->
    doWiFiManager();
    if(!portalRunning)
    {<!-- --> if (mqttClient.connected())
  {<!-- --> // If the development board successfully connects to the server
    // Publish information every 3 seconds
      pubMQTTmsg();
    // keep heartbeat
    mqttClient.loop();
  } else
   {<!-- --> // If the development board fails to successfully connect to the server
    connectMQTTServer(); // Try to connect to the server
   }}

}
void tickerCount(){<!-- -->
  count + + ;
}

void connectMQTTServer(){<!-- -->
  // Generate a client ID based on the MAC address of ESP8266 (to avoid having the same name as the client ID of other ESP8266)
  String clientId = "esp8266-" + WiFi.macAddress();

  // Connect to MQTT server
// if (mqttClient.connect(clientId.c_str(),mqttUserName,mqttPassword)) {<!-- -->
  if (mqttClient.connect(clientId.c_str())) {<!-- -->
    Serial.println("MQTT Server Connected.");
    Serial.println("Server Address: ");
    Serial.println(mqttServer);
    Serial.println("ClientId:");
    Serial.println(clientId);
  } else {<!-- -->
    Serial.print("MQTT Server Connect Failed. Client State:");
    Serial.println(mqttClient.state());
    delay(3000);
  }
}

// release news
void pubMQTTmsg(){<!-- -->
  byte fac = 0;
  byte bac = 0;
  byte lac = 0;
  byterac = 0;
  byte fde = 0;
  byte bde = 0;
  byte lde = 0;
  byte rde = 0;
  
if(digitalRead(BUTTON_accelerate) == LOW & amp; & amp; pwm_ac_flag == 0){<!-- -->
  pwm_ac_flag = 1;
  if(digitalRead(BUTTON_forward) == LOW){<!-- -->mqttClient.publish(publishTopic, "fd_ac");fac=1;}
  if(digitalRead(BUTTON_BACK) == LOW){<!-- -->mqttClient.publish(publishTopic, "bk_ac");bac=1;}
  if(digitalRead(BUTTON_LEFT)== LOW){<!-- -->mqttClient.publish(publishTopic, "left_ac");lac=1;}
  if(digitalRead(BUTTON_RIGHT) == LOW){<!-- -->mqttClient.publish(publishTopic, "right_ac");rac=1;}
  if(fac==0 & amp; & amp;bac==0 & amp; & amp;lac==0 & amp; & amp;rac==0){<!-- -->mqttClient.publish(publishTopic , "ac");}
}
else if(digitalRead(BUTTON_accelerate) == HIGH){<!-- -->pwm_ac_flag = 0;fac=0;bac=0;lac=0;rac=0;}


if(digitalRead(BUTTON_decelerate) == LOW & amp; & amp; pwm_de_flag == 0){<!-- -->
  pwm_de_flag = 1;
  if(digitalRead(BUTTON_forward) == LOW){<!-- -->mqttClient.publish(publishTopic, "fd_de");fde=1;}
  if(digitalRead(BUTTON_BACK) == LOW){<!-- -->mqttClient.publish(publishTopic, "bk_de");bde=1;}
  if(digitalRead(BUTTON_LEFT)== LOW){<!-- -->mqttClient.publish(publishTopic, "left_de");lde=1;}
  if(digitalRead(BUTTON_RIGHT) == LOW){<!-- -->mqttClient.publish(publishTopic, "bk_de");rde=1;}
  if(fde==0 & amp; & amp;bde==0 & amp; & amp;lde==0 & amp; & amp;rde==0){<!-- -->mqttClient.publish(publishTopic , "de");}
}
else if(digitalRead(BUTTON_decelerate) == HIGH){<!-- -->pwm_de_flag = 0;fde=0;bde=0;lde=0;rde=0;}


if(digitalRead(BUTTON_oil) == LOW & amp; & amp; oil_oil == 0) {<!-- -->
   mqttClient.publish(publishTopic, "oil");
   oil_flag = 0;
   oil_oil = 1;
    Serial.println("Throttle");
 }
else if(digitalRead(BUTTON_oil) == HIGH & amp; & amp; oil_flag==0)
{<!-- -->
  mqttClient.publish(publishTopic, "oil_");
   oil_flag = 1;
   oil_oil = 0;
}

if(digitalRead(BUTTON_forward) == LOW & amp; & amp; forward_foward == 0 & amp; & amp; fac==0 & amp; & amp;fde==0 ) {<!-- -->
   mqttClient.publish(publishTopic, "fd");
   forward_flag = 0;
   forward_forward = 1;
    Serial.println("forward");
 }
else if(digitalRead(BUTTON_forward) == HIGH & amp; & amp; forward_flag==0)
{<!-- -->
  mqttClient.publish(publishTopic, "fd_");
   forward_flag = 1;
   forward_forward = 0;
}

if(digitalRead(BUTTON_BACK) == LOW & amp; & amp; back_back == 0 & amp; & amp; bac==0 & amp; & amp;bde==0 ) {<!-- -->
   mqttClient.publish(publishTopic, "bk");
   back_flag = 0;
   back_back = 1;
   Serial.println("Back");
 }
else if(digitalRead(BUTTON_BACK) == HIGH & amp; & amp; back_flag==0)
{<!-- -->
  mqttClient.publish(publishTopic, "bk_");
   back_flag = 1;
   back_back = 0;
}

if(digitalRead(BUTTON_LEFT) == LOW & amp; & amp; left_left==0 & amp; & amp; lac==0 & amp; & amp;lde==0 ) {<!-- -->
   mqttClient.publish(publishTopic, "left");
   left_flag = 0;
   left_left = 1;
   Serial.println("Turn left");
 }
else if(digitalRead(BUTTON_LEFT) == HIGH & amp; & amp; left_flag==0)
{<!-- -->
  mqttClient.publish(publishTopic, "left_");
  left_flag = 1;
  left_left = 0;
}
 
if(digitalRead(BUTTON_RIGHT) == LOW & amp; & amp; right_right==0 & amp; & amp; rac==0 & amp; & amp;lde==0 ) {<!-- -->
   mqttClient.publish(publishTopic, "right");
   right_flag = 0;
   right_right = 1;
   Serial.println("Turn right");
 }
else if(digitalRead(BUTTON_RIGHT) == HIGH & amp; & amp; right_flag==0)
{<!-- -->
  mqttClient.publish(publishTopic, "right_");
  right_flag = 1;
  right_right = 0;
}
}

The following is the code of the car receiving end, which is connected to the toy wheel with the L298N driver wiring.

#include <PubSubClient.h>

#include <WiFiManager.h> // Reference WiFi manager library
#include <EEPROM.h> //Use EEPROM to save data
#include <String.h>
#ifdef ESP8266 // If the chip type is ESP8266
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <ESP8266WebServer.h> // ESP8266 website server library
ESP8266WebServer server(80); // Create a website server
#elif defined(ESP32) //If the chip type is ESP32
#include "WiFi.h"
#include <ESPmDNS.h>
#include <WebServer.h> // ESP32 website server library
WebServer server(80); // Create a website server
#endif
/********************** parameter settings *****************/
//const char* mqttServer = "mqtt-cn-i7m2omxjh0b.mqtt.aliyuncs.com";
const char* mqttServer = "114.132.158.66";
#define AP_SSID "Smart Internet of Things-Car" // Customized ESP device AP name
#define AP_PWD "12345678" // Customized AP password
#define TRIGGER_PIN 0 // Enable the button pin of "Wi-Fi Settings Portal"

#define fd_bk1 12
#define fd_bk2 14
#define lt_rt1 5
#define lt_rt2 4

unsigned int timeout = 180; // Set the timeout seconds for the entrance
unsigned int startTime = millis(); // Record the start time of the setting entry
bool portalRunning = false; // Set whether the portal is running, the default is "No"
//MQTT server connection username and password
//const char* mqttUserName = "Signature|LTAI5t9XAHpNLn4LUaEKETez|mqtt-cn-i7m2omxjh0b";
//const char* mqttPassword = "0uNk + aVm2W7guPrEfo6iSr6tBow=";



WiFiManager wifiManager; // Create WiFiManager object
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

bool shouldSaveConfig = false; //Flag bit whether to save data
//Callback function, used to notify us that we need to save the configuration
void saveConfigCallback () {<!-- -->
  Serial.println("Should save config");
  shouldSaveConfig = true;
}

int pwm = 100;
int pwm_temp = 0;
byte flag = 0;
byte fd_flag = 1;
byte bk_flag = 2;
byte left_flag = 3;
byte right_flag = 4;
/************************ Start programming memory section *****************/
#define EEPROM_SALT 12664 //Used to verify whether the read data is valid
typedef struct
{<!-- -->
  int salt = EEPROM_SALT;
  char Secret_Key[15] = "";
}WMSettings;//Structure, used to save secret key data

WMSettings blynk;

//EEPROM reading function
void eeprom_read()
{<!-- -->
  EEPROM.begin(3000);
  EEPROM.get(2500, blynk);
  EEPROM.end();
  
  Serial.print("The secret key read from EEPROM is:");
  Serial.println(blynk.Secret_Key);
}

//EEPROM storage function
void eeprom_saveconfig()
{<!-- -->
  EEPROM.begin(3000);
  EEPROM.put(2500, blynk);
  EEPROM.commit();
  EEPROM.end();
}

/************************ Configuration part of lighting Blinker *****************/

/************************ WIFI administrator parameter configuration and reset function *****************/
WiFiManagerParameter custom_Secret_Key("KEY", "Secret Key", blynk.Secret_Key, 15);

//Used to reset WIFI settings and secret keys
void doWiFiManager()
{<!-- --> // Perform the work of WiFi administrator
  if (portalRunning)
  {<!-- --> // If "Set Entry" is being executed...
    wifiManager.process(); // Process the client connection request of "Setting Portal"
    // If "Settings Portal" is enabled for longer than the default time (180 seconds)...
    if ((millis() - startTime) > (timeout * 1000))
    {<!-- -->
      Serial.println(""Wi-Fi Settings Entry" operation timed out...");
      portalRunning = false; // Set to "not executing"
      if(strlen(custom_Secret_Key.getValue()) == 12)//Simply determine whether the filled in secret key is valid
      {<!-- -->
        strcpy(blynk.Secret_Key, custom_Secret_Key.getValue());
        Serial.print("The secret key written to EEPROM is:");
        Serial.println(blynk.Secret_Key);
        eeprom_saveconfig();
      }
      wifiManager.stopConfigPortal(); // Stop "Setting Portal"
      Serial.println("Restarting your device");
      delay(1000);
      ESP.restart();//Restart the ESP device
// Blinker.begin(blynk.Secret_Key, WiFi.SSID().c_str(), WiFi.psk().c_str());
// Blinker.attachData(dataRead);
// Button1.attach(button1_callback);
     }
    }
    
    // If the pin that enables "Settings Entry" is clicked
    // And the value of portalRunning is not true...
    if (digitalRead(TRIGGER_PIN) == LOW & amp; & amp; (!portalRunning))
    {<!-- -->
      Serial.println("The button has been pressed, starting the setting portal.");
      wifiManager.setConfigPortalBlocking(false); // Set to "non-shelved" mode
      wifiManager.startConfigPortal(AP_SSID, AP_PWD); // Enable Wi-Fi AP
  
      portalRunning = true; // Set to "Setting Portal" and executing
      startTime = millis(); // Record the current time
    }
}

void setup() {<!-- -->

    pinMode(fd_bk1, OUTPUT);//
    pinMode(fd_bk2, OUTPUT);//
    pinMode(lt_rt1, OUTPUT);//
    pinMode(lt_rt2, OUTPUT);//
    digitalWrite(fd_bk1, 0);
    digitalWrite(fd_bk2, 0);
    digitalWrite(lt_rt1, 0);
    digitalWrite(lt_rt2, 0);
// pinMode(LED_BUILTIN, OUTPUT); // Set the LED pin on the board to output mode
// digitalWrite(LED_BUILTIN, HIGH); // Turn off the LED on the board after startup
    WiFi.mode(WIFI_STA); //Wi-Fi is set to STA mode; the default mode is STA + AP
    Serial.begin(115200);
    /****************************************************** ***************/
    delay(500);
    pinMode(TRIGGER_PIN, INPUT_PULLUP); //Configure the pin that triggers the reset function
    eeprom_read(); //Read the secret key data in EEPROM
    //Determine whether the secret key data read in EEPROM is contaminated
    if (blynk.salt != EEPROM_SALT)
    {<!-- -->
      Serial.println("Invalid settings read from EEPROM, tried to use default value");
      WMSettings defaults;
      blynk = defaults;
    }
  
    
    wifiManager.addParameter( & amp;custom_Secret_Key); //Add secret key parameters in the WiFi administrator interface
    wifiManager.setSaveConfigCallback(saveConfigCallback);
    
    /********************** For testing only *******************/
    //Reset settings - test
// wifiManager.resetSettings();
    //Set the minimum quality of the signal so that it ignores APs at this quality
    //Default is 8%
// wifiManager.setMinimumSignalQuality(30);
    //Set the connection timeout
// wifiManager.setConnectTimeout(CONNECT_TIMEOUT);
// wifiManager.setTimeout(AP_TIMEOUT);
  
    //Then enter a blocking loop, waiting for configuration
    if (!wifiManager.autoConnect(AP_SSID,AP_PWD))
    {<!-- -->
      Serial.println("Failed to connect or timed out");
      Serial.println("Restarting your device");
      delay(1000);
      ESP.restart();
    }

    //Going here means you are connected to WiFi
    Serial.println("Already connected to WiFi:)");

    //Simply determine whether the secret key is valid
    if(strlen(custom_Secret_Key.getValue()) == 12)
    {<!-- -->
      strcpy(blynk.Secret_Key, custom_Secret_Key.getValue());
      Serial.print("The secret key is:");
      Serial.println(blynk.Secret_Key);
      eeprom_saveconfig();
    }//At this point, data saving ends

  
    // Automatically connect to WiFi. The parameter of the following statement is the WiFi name when connecting to ESP8266
// wifiManager.autoConnect("AutoConnectAP");
    
    // If you want to add a password to the WiFi, you can use the following statement:
    // wifiManager.autoConnect("AutoConnectAP", "12345678");
    // 12345678 in the above statement is the password to connect to AutoConnectAP
    // After the WiFi connection is successful, the connection success information will be output through the serial monitor
    Serial.println("");
    Serial.print("ESP8266 Connected to ");
    Serial.println(WiFi.SSID()); // Serial port prints the currently connected WIFI name
    Serial.print("IP address:\t");
    Serial.println(WiFi.localIP()); // Serial port prints the IP of the currently connected WIFI
    Serial.printf("password: %s\\
", WiFi.psk().c_str());//Serial port prints the currently connected WIFI password

      //Set the MQTT server and port number
    mqttClient.setServer(mqttServer, 1883);
      //Set the MQTT subscription callback function
    mqttClient.setCallback(receiveCallback);
 
    // Connect to MQTT server
    connectMQTTserver();

}

void loop() {<!-- -->
    doWiFiManager();
    if(!portalRunning){<!-- -->if (mqttClient.connected()) {<!-- --> // If the development board successfully connects to the server
    mqttClient.loop(); // Process information and heartbeat
  } else {<!-- --> // If the development board fails to successfully connect to the server
    connectMQTTserver(); // Then try to connect to the server
  }}
}

// Connect to the MQTT server and subscribe to information
void connectMQTTserver(){<!-- -->
  // Generate a client ID based on the MAC address of ESP8266 (to avoid having the same name as the client ID of other ESP8266)
 String clientId = "esp8266-" + WiFi.macAddress();
 
  // Connect to MQTT server
// if (mqttClient.connect(clientId.c_str(),mqttUserName,mqttPassword)) {<!-- -->
  if (mqttClient.connect(clientId.c_str())) {<!-- -->
    Serial.println("MQTT Server Connected.");
    Serial.println("Server Address:");
    Serial.println(mqttServer);
    Serial.println("ClientId: ");
    Serial.println(clientId);
    subscribeTopic(); // Subscribe to the specified topic
  } else {<!-- -->
    Serial.print("MQTT Server Connect Failed. Client State:");
    Serial.println(mqttClient.state());
    delay(5000);
  }
}
 
//Callback function after receiving information
void receiveCallback(char* topic, byte* payload, unsigned int length) {<!-- -->
  char recode[20]={<!-- -->0};
  Serial.print("Message Received [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i + + ) {<!-- -->
     Serial.println(length);
    Serial.print((char)payload[i]);
    recode[i] = payload[i];
  }
// Serial.println("");
// Serial.print("Message Length(Bytes) ");
// Serial.println(length);
//
// if ((char)payload[0] == '1') { // If the received message starts with "1"
// digitalWrite(BUILTIN_LED, LOW); // Then light up the LED.
// Serial.println("Received 1");
// }

  if( !strcmp(recode,"ac") || !strcmp(recode,"fd_ac")|| !strcmp(recode,"bk_ac")|| !strcmp(recode,"left_ac\ ")||!strcmp(recode,"right_ac") & amp; & amp; pwm<1000){
    if(pwm<250){ pwm + = 10; } //Every time you press the accelerator key, the acceleration is 50, adjustable
     Serial.println("Accelerate__");
  }
  if( !strcmp(recode,"de") || !strcmp(recode,"fd_de")|| !strcmp(recode,"bk_de")|| !strcmp(recode,"left_de\ ")||!strcmp(recode,"bk_de") & amp; & amp; pwm>0){
     if(pwm>0){pwm -= 10; } //Every time you press the accelerator key, decelerate by 50, adjustable
     Serial.println("Slow down__");
  }
  if( !strcmp(recode,"oil")){<!-- --> //Press the accelerator button and move forward at full speed
    digitalWrite(fd_bk1, 0);
    digitalWrite(fd_bk2, 1);
    Serial.println("Throttle");
  } else if(!strcmp(recode,"oil_")){<!-- --> //Stop after releasing the accelerator button
    digitalWrite(fd_bk1, 0);
    digitalWrite(fd_bk2, 0);
   }

  if( !strcmp(recode,"fd")||!strcmp(recode,"fd_ac")|| !strcmp(recode,"fd_de")){<!-- --> // Press the forward button and drive forward at the current speed
    digitalWrite(fd_bk1, 0);
    analogWrite(fd_bk2, pwm);
// Serial.println("forward");
  } else if(!strcmp(recode,"fd_")){<!-- --> //Let go of the forward button and the car will stop
    digitalWrite(fd_bk1, 0);
    digitalWrite(fd_bk2, 0);
// Serial.println("Stop moving forward");
   }
   
  if( !strcmp(recode,"bk")|| !strcmp(recode,"bk_ac")|| !strcmp(recode,"bk_de")){<!-- --> // Press the back button to go back at the current speed
    analogWrite(fd_bk1, pwm);
    digitalWrite(fd_bk2, 0);
// Serial.println("Back");
}else if(!strcmp(recode,"bk_")){<!-- --> //Stop retreating after letting go
    digitalWrite(fd_bk1, 0);
    digitalWrite(fd_bk2, 0);
// Serial.println("Stop backing");
   }
   
  if( !strcmp(recode,"left")|| !strcmp(recode,"left_ac")|| !strcmp(recode,"left_de")){<!-- --> // Press the left turn key and turn left at the current speed
    analogWrite(lt_rt1, 150);
    digitalWrite(lt_rt2, 0);
// Serial.println("Turn left");
}else if(!strcmp(recode,"left_")){<!-- --> //Stop after letting go
    digitalWrite(lt_rt1, 0);
    digitalWrite(lt_rt2, 0);
// Serial.println("Stop turning left");
   }
   
  if( !strcmp(recode,"right")||!strcmp(recode,"right_ac")||!strcmp(recode,"bk_de")){<!-- --> // Press the right turn key and turn right at the current speed
    digitalWrite(lt_rt1, 0);
    analogWrite(lt_rt2, 150);
// Serial.println("Turn right");
}else if(!strcmp(recode,"right_")){<!-- -->//Stop after letting go
    digitalWrite(lt_rt1, 0);
    digitalWrite(lt_rt2, 0);
// Serial.println("Stop turning right");
   }

}
 
// Subscribe to the specified topic
void subscribeTopic(){<!-- -->
 
  //Create a subscription topic. The topic name is prefixed with Taichi-Maker-Sub, followed by the MAC address of the device.
  // This is done to ensure that when different devices use the same MQTT server to test message subscriptions, the subscribed topic names are different.
  String topicString = "fff123" ;
  char subTopic[topicString.length() + 1];
  strcpy(subTopic, topicString.c_str());
  
  // Output through the serial port monitor whether the topic is successfully subscribed and the subscribed topic name
  if(mqttClient.subscribe(subTopic)){<!-- -->
    Serial.println("Subscribe Topic:");
    Serial.println(subTopic);
  } else {<!-- -->
    Serial.print("Subscribe Fail...");
  }
}