Serveur Web de robot de voiture télécommandé ESP32-CAM

Serveur Web de robot de voiture télécommandé ESP32-CAM

Construisez un robot de voiture télécommandé Wi-Fi avec l’ESP32-CAM. Vous pourrez contrôler le robot à l’aide d’un serveur Web qui affiche un streaming vidéo de ce que le robot « voit ». Vous pouvez contrôler votre robot à distance même s’il est hors de votre vue. L’ESP32-CAM sera programmé à l’aide d’Arduino IDE.

Serveur Web de robot de voiture télécommandé ESP32-CAM Arduino IDE

Compatibilité des cartes: ce projet nécessite 4 GPIO pour contrôler les moteurs à courant continu. Ainsi, vous pouvez utiliser n’importe quelle carte de caméra ESP32 avec 4 GPIO disponibles comme le Carte ESP32-CAM Ai-Thinker ou la TTGO T-Journal.

Aperçu du projet

Avant de commencer le projet, nous mettrons en évidence les fonctionnalités et les composants les plus importants utilisés pour construire le robot.

Wifi

Le robot sera contrôlé via Wi-Fi à l’aide de votre ESP32-CAM. Nous allons créer une interface Web pour contrôler le robot, accessible depuis n’importe quel appareil de votre réseau local.

La page Web montre également un streaming vidéo de ce que le robot « voit ». Pour de bons résultats avec le streaming vidéo, nous vous recommandons d’utiliser un ESP32-CAM avec antenne externe.

Antenne externe de la carte ESP32-CAM

Important: sans antenne externe, le flux vidéo est à la traîne et le serveur Web est extrêmement lent à contrôler le robot.

Commandes de robots

Le serveur web dispose de 5 contrôles : Effronté, En arrière, À gauche, À droite, et Arrêter.

Serveur Web Robot Télécommandé ESP32-CAM Arduino IDE

Le robot bouge tant que vous appuyez sur les boutons. Lorsque vous relâchez un bouton, le robot s’arrête. Cependant, nous avons inclus le Arrêter bouton qui peut être utile dans le cas où l’ESP32 ne reçoit pas la commande d’arrêt lorsque vous relâchez un bouton.

Kit de châssis de robot intelligent

Nous allons utiliser le Kit de châssis de robot intelligent. Vous pouvez le trouver dans la plupart des magasins en ligne. Le kit coûte environ 10 $ et il est facile à assembler – regardez cette vidéo pour voir comment assembler le kit de châssis de robot.

Vous pouvez utiliser n’importe quel autre kit de châssis à condition qu’il soit livré avec deux moteurs à courant continu.

Kit de châssis de robot intelligent Kit Arduino ESP32-CAM

Pilote de moteur L298N

Il existe de nombreuses façons de contrôler les moteurs à courant continu. Nous utiliserons le pilote de moteur L298N qui permet de contrôler facilement la vitesse et la direction de 2 moteurs à courant continu.

Pilote de moteur L298N DC ESP32-CAM

Nous n’expliquerons pas comment fonctionne le pilote de moteur L298N. Vous pouvez lire l’article suivant pour un didacticiel détaillé sur le pilote de moteur L298N :

Pouvoir

Pour que le circuit reste simple, nous alimenterons le robot (les moteurs) et l’ESP32 en utilisant la même source d’alimentation. Nous avons utilisé une banque d’alimentation/un chargeur portable (comme ceux utilisés pour charger votre smartphone) et cela a bien fonctionné.

Banque d'alimentation portable pour alimenter le robot ESP32-CAM

Noter: les moteurs consomment beaucoup de courant, donc si vous sentez que votre robot ne bouge pas correctement, vous devrez peut-être utiliser une alimentation externe pour les moteurs. Cela signifie que vous avez besoin de deux sources d’alimentation différentes. L’un pour alimenter les moteurs à courant continu et l’autre pour alimenter l’ESP32.

Pièces requises

Pour ce projet, nous utiliserons les pièces suivantes :

Vous pouvez utiliser les liens précédents ou aller directement sur MakerAdvisor.com/tools pour trouver toutes les pièces pour vos projets au meilleur prix !

1640970008 832 Serveur Web de robot de voiture telecommande ESP32 CAM

Code

Copiez le code suivant dans votre IDE Arduino.

/*********
  Rui Santos
  Complete instructions at https://Raspberryme.com/esp32-cam-projects-ebook/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*********/

#include "esp_camera.h"
#include <WiFi.h>
#include "esp_timer.h"
#include "img_converters.h"
#include "Arduino.h"
#include "fb_gfx.h"
#include "soc/soc.h"             // disable brownout problems
#include "soc/rtc_cntl_reg.h"    // disable brownout problems
#include "esp_http_server.h"

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

#define PART_BOUNDARY "123456789000000000000987654321"

#define CAMERA_MODEL_AI_THINKER
//#define CAMERA_MODEL_M5STACK_PSRAM
//#define CAMERA_MODEL_M5STACK_WITHOUT_PSRAM
//#define CAMERA_MODEL_M5STACK_PSRAM_B
//#define CAMERA_MODEL_WROVER_KIT

#if defined(CAMERA_MODEL_WROVER_KIT)
  #define PWDN_GPIO_NUM    -1
  #define RESET_GPIO_NUM   -1
  #define XCLK_GPIO_NUM    21
  #define SIOD_GPIO_NUM    26
  #define SIOC_GPIO_NUM    27
  
  #define Y9_GPIO_NUM      35
  #define Y8_GPIO_NUM      34
  #define Y7_GPIO_NUM      39
  #define Y6_GPIO_NUM      36
  #define Y5_GPIO_NUM      19
  #define Y4_GPIO_NUM      18
  #define Y3_GPIO_NUM       5
  #define Y2_GPIO_NUM       4
  #define VSYNC_GPIO_NUM   25
  #define HREF_GPIO_NUM    23
  #define PCLK_GPIO_NUM    22

#elif defined(CAMERA_MODEL_M5STACK_PSRAM)
  #define PWDN_GPIO_NUM     -1
  #define RESET_GPIO_NUM    15
  #define XCLK_GPIO_NUM     27
  #define SIOD_GPIO_NUM     25
  #define SIOC_GPIO_NUM     23
  
  #define Y9_GPIO_NUM       19
  #define Y8_GPIO_NUM       36
  #define Y7_GPIO_NUM       18
  #define Y6_GPIO_NUM       39
  #define Y5_GPIO_NUM        5
  #define Y4_GPIO_NUM       34
  #define Y3_GPIO_NUM       35
  #define Y2_GPIO_NUM       32
  #define VSYNC_GPIO_NUM    22
  #define HREF_GPIO_NUM     26
  #define PCLK_GPIO_NUM     21

#elif defined(CAMERA_MODEL_M5STACK_WITHOUT_PSRAM)
  #define PWDN_GPIO_NUM     -1
  #define RESET_GPIO_NUM    15
  #define XCLK_GPIO_NUM     27
  #define SIOD_GPIO_NUM     25
  #define SIOC_GPIO_NUM     23
  
  #define Y9_GPIO_NUM       19
  #define Y8_GPIO_NUM       36
  #define Y7_GPIO_NUM       18
  #define Y6_GPIO_NUM       39
  #define Y5_GPIO_NUM        5
  #define Y4_GPIO_NUM       34
  #define Y3_GPIO_NUM       35
  #define Y2_GPIO_NUM       17
  #define VSYNC_GPIO_NUM    22
  #define HREF_GPIO_NUM     26
  #define PCLK_GPIO_NUM     21

#elif defined(CAMERA_MODEL_AI_THINKER)
  #define PWDN_GPIO_NUM     32
  #define RESET_GPIO_NUM    -1
  #define XCLK_GPIO_NUM      0
  #define SIOD_GPIO_NUM     26
  #define SIOC_GPIO_NUM     27
  
  #define Y9_GPIO_NUM       35
  #define Y8_GPIO_NUM       34
  #define Y7_GPIO_NUM       39
  #define Y6_GPIO_NUM       36
  #define Y5_GPIO_NUM       21
  #define Y4_GPIO_NUM       19
  #define Y3_GPIO_NUM       18
  #define Y2_GPIO_NUM        5
  #define VSYNC_GPIO_NUM    25
  #define HREF_GPIO_NUM     23
  #define PCLK_GPIO_NUM     22

#elif defined(CAMERA_MODEL_M5STACK_PSRAM_B)
  #define PWDN_GPIO_NUM     -1
  #define RESET_GPIO_NUM    15
  #define XCLK_GPIO_NUM     27
  #define SIOD_GPIO_NUM     22
  #define SIOC_GPIO_NUM     23
  
  #define Y9_GPIO_NUM       19
  #define Y8_GPIO_NUM       36
  #define Y7_GPIO_NUM       18
  #define Y6_GPIO_NUM       39
  #define Y5_GPIO_NUM        5
  #define Y4_GPIO_NUM       34
  #define Y3_GPIO_NUM       35
  #define Y2_GPIO_NUM       32
  #define VSYNC_GPIO_NUM    25
  #define HREF_GPIO_NUM     26
  #define PCLK_GPIO_NUM     21

#else
  #error "Camera model not selected"
#endif

#define MOTOR_1_PIN_1    14
#define MOTOR_1_PIN_2    15
#define MOTOR_2_PIN_1    13
#define MOTOR_2_PIN_2    12

static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
static const char* _STREAM_BOUNDARY = "rn--" PART_BOUNDARY "rn";
static const char* _STREAM_PART = "Content-Type: image/jpegrnContent-Length: %urnrn";

httpd_handle_t camera_httpd = NULL;
httpd_handle_t stream_httpd = NULL;

static const char PROGMEM INDEX_HTML[] = R"rawliteral(
<html>
  <head>
    <title>ESP32-CAM Robot</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
      body { font-family: Arial; text-align: center; margin:0px auto; padding-top: 30px;}
      table { margin-left: auto; margin-right: auto; }
      td { padding: 8 px; }
      .button {
        background-color: #2f4468;
        border: none;
        color: white;
        padding: 10px 20px;
        text-align: center;
        text-decoration: none;
        display: inline-block;
        font-size: 18px;
        margin: 6px 3px;
        cursor: pointer;
        -webkit-touch-callout: none;
        -webkit-user-select: none;
        -khtml-user-select: none;
        -moz-user-select: none;
        -ms-user-select: none;
        user-select: none;
        -webkit-tap-highlight-color: rgba(0,0,0,0);
      }
      img {  width: auto ;
        max-width: 100% ;
        height: auto ; 
      }
    </style>
  </head>
  <body>
    <h1>ESP32-CAM Robot</h1>
    <img src="" id="photo" >
    <table>
      <tr><td colspan="3" align="center"><button class="button" onmousedown="toggleCheckbox('forward');" ontouchstart="toggleCheckbox('forward');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Forward</button></td></tr>
      <tr><td align="center"><button class="button" onmousedown="toggleCheckbox('left');" ontouchstart="toggleCheckbox('left');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Left</button></td><td align="center"><button class="button" onmousedown="toggleCheckbox('stop');" ontouchstart="toggleCheckbox('stop');">Stop</button></td><td align="center"><button class="button" onmousedown="toggleCheckbox('right');" ontouchstart="toggleCheckbox('right');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Right</button></td></tr>
      <tr><td colspan="3" align="center"><button class="button" onmousedown="toggleCheckbox('backward');" ontouchstart="toggleCheckbox('backward');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Backward</button></td></tr>                   
    </table>
   <script>
   function toggleCheckbox(x) {
     var xhr = new XMLHttpRequest();
     xhr.open("GET", "/action?go=" + x, true);
     xhr.send();
   }
   window.onload = document.getElementById("photo").src = window.location.href.slice(0, -1) + ":81/stream";
  </script>
  </body>
</html>
)rawliteral";

static esp_err_t index_handler(httpd_req_t *req){
  httpd_resp_set_type(req, "text/html");
  return httpd_resp_send(req, (const char *)INDEX_HTML, strlen(INDEX_HTML));
}

static esp_err_t stream_handler(httpd_req_t *req){
  camera_fb_t * fb = NULL;
  esp_err_t res = ESP_OK;
  size_t _jpg_buf_len = 0;
  uint8_t * _jpg_buf = NULL;
  char * part_buf[64];

  res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
  if(res != ESP_OK){
    return res;
  }

  while(true){
    fb = esp_camera_fb_get();
    if (!fb) {
      Serial.println("Camera capture failed");
      res = ESP_FAIL;
    } else {
      if(fb->width > 400){
        if(fb->format != PIXFORMAT_JPEG){
          bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len);
          esp_camera_fb_return(fb);
          fb = NULL;
          if(!jpeg_converted){
            Serial.println("JPEG compression failed");
            res = ESP_FAIL;
          }
        } else {
          _jpg_buf_len = fb->len;
          _jpg_buf = fb->buf;
        }
      }
    }
    if(res == ESP_OK){
      size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);
      res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
    }
    if(res == ESP_OK){
      res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
    }
    if(res == ESP_OK){
      res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
    }
    if(fb){
      esp_camera_fb_return(fb);
      fb = NULL;
      _jpg_buf = NULL;
    } else if(_jpg_buf){
      free(_jpg_buf);
      _jpg_buf = NULL;
    }
    if(res != ESP_OK){
      break;
    }
    //Serial.printf("MJPG: %uBn",(uint32_t)(_jpg_buf_len));
  }
  return res;
}

static esp_err_t cmd_handler(httpd_req_t *req){
  char*  buf;
  size_t buf_len;
  char variable[32] = {0,};
  
  buf_len = httpd_req_get_url_query_len(req) + 1;
  if (buf_len > 1) {
    buf = (char*)malloc(buf_len);
    if(!buf){
      httpd_resp_send_500(req);
      return ESP_FAIL;
    }
    if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
      if (httpd_query_key_value(buf, "go", variable, sizeof(variable)) == ESP_OK) {
      } else {
        free(buf);
        httpd_resp_send_404(req);
        return ESP_FAIL;
      }
    } else {
      free(buf);
      httpd_resp_send_404(req);
      return ESP_FAIL;
    }
    free(buf);
  } else {
    httpd_resp_send_404(req);
    return ESP_FAIL;
  }

  sensor_t * s = esp_camera_sensor_get();
  int res = 0;
  
  if(!strcmp(variable, "forward")) {
    Serial.println("Forward");
    digitalWrite(MOTOR_1_PIN_1, 1);
    digitalWrite(MOTOR_1_PIN_2, 0);
    digitalWrite(MOTOR_2_PIN_1, 1);
    digitalWrite(MOTOR_2_PIN_2, 0);
  }
  else if(!strcmp(variable, "left")) {
    Serial.println("Left");
    digitalWrite(MOTOR_1_PIN_1, 0);
    digitalWrite(MOTOR_1_PIN_2, 1);
    digitalWrite(MOTOR_2_PIN_1, 1);
    digitalWrite(MOTOR_2_PIN_2, 0);
  }
  else if(!strcmp(variable, "right")) {
    Serial.println("Right");
    digitalWrite(MOTOR_1_PIN_1, 1);
    digitalWrite(MOTOR_1_PIN_2, 0);
    digitalWrite(MOTOR_2_PIN_1, 0);
    digitalWrite(MOTOR_2_PIN_2, 1);
  }
  else if(!strcmp(variable, "backward")) {
    Serial.println("Backward");
    digitalWrite(MOTOR_1_PIN_1, 0);
    digitalWrite(MOTOR_1_PIN_2, 1);
    digitalWrite(MOTOR_2_PIN_1, 0);
    digitalWrite(MOTOR_2_PIN_2, 1);
  }
  else if(!strcmp(variable, "stop")) {
    Serial.println("Stop");
    digitalWrite(MOTOR_1_PIN_1, 0);
    digitalWrite(MOTOR_1_PIN_2, 0);
    digitalWrite(MOTOR_2_PIN_1, 0);
    digitalWrite(MOTOR_2_PIN_2, 0);
  }
  else {
    res = -1;
  }

  if(res){
    return httpd_resp_send_500(req);
  }

  httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
  return httpd_resp_send(req, NULL, 0);
}

void startCameraServer(){
  httpd_config_t config = HTTPD_DEFAULT_CONFIG();
  config.server_port = 80;
  httpd_uri_t index_uri = {
    .uri       = "/",
    .method    = HTTP_GET,
    .handler   = index_handler,
    .user_ctx  = NULL
  };

  httpd_uri_t cmd_uri = {
    .uri       = "/action",
    .method    = HTTP_GET,
    .handler   = cmd_handler,
    .user_ctx  = NULL
  };
  httpd_uri_t stream_uri = {
    .uri       = "/stream",
    .method    = HTTP_GET,
    .handler   = stream_handler,
    .user_ctx  = NULL
  };
  if (httpd_start(&camera_httpd, &config) == ESP_OK) {
    httpd_register_uri_handler(camera_httpd, &index_uri);
    httpd_register_uri_handler(camera_httpd, &cmd_uri);
  }
  config.server_port += 1;
  config.ctrl_port += 1;
  if (httpd_start(&stream_httpd, &config) == ESP_OK) {
    httpd_register_uri_handler(stream_httpd, &stream_uri);
  }
}

void setup() {
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
  
  pinMode(MOTOR_1_PIN_1, OUTPUT);
  pinMode(MOTOR_1_PIN_2, OUTPUT);
  pinMode(MOTOR_2_PIN_1, OUTPUT);
  pinMode(MOTOR_2_PIN_2, OUTPUT);
  
  Serial.begin(115200);
  Serial.setDebugOutput(false);
  
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG; 
  
  if(psramFound()){
    config.frame_size = FRAMESIZE_VGA;
    config.jpeg_quality = 10;
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }
  
  // Camera init
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;
  }
  // Wi-Fi connection
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  
  Serial.print("Camera Stream Ready! Go to: http://");
  Serial.println(WiFi.localIP());
  
  // Start streaming web server
  startCameraServer();
}

void loop() {
  
}

Afficher le code brut

Insérez vos informations d’identification réseau et le code devrait fonctionner immédiatement.

const char* ssid = "REPLACE_WITH_YOUR_SSID"; 
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Comment fonctionne le code

Jetons un coup d’œil aux pièces pertinentes pour contrôler le robot. Définissez les GPIO qui contrôleront les moteurs. Chaque moteur est contrôlé par deux broches.

#define MOTOR_1_PIN_1 14
#define MOTOR_1_PIN_2 15
#define MOTOR_2_PIN_1 13
#define MOTOR_2_PIN_2 12

Lorsque vous cliquez sur les boutons, vous effectuez une demande sur une URL différente.

<table>
    <tr><td colspan="3" align="center"><button class="button" onmousedown="toggleCheckbox('forward');" ontouchstart="toggleCheckbox('forward');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Forward</button></td></tr>
    <tr><td align="center"><button class="button" onmousedown="toggleCheckbox('left');" ontouchstart="toggleCheckbox('left');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Left</button></td><td align="center"><button class="button" onmousedown="toggleCheckbox('stop');" ontouchstart="toggleCheckbox('stop');">Stop</button></td><td align="center"><button class="button" onmousedown="toggleCheckbox('right');" ontouchstart="toggleCheckbox('right');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Right</button></td></tr>
    <tr><td colspan="3" align="center"><button class="button" onmousedown="toggleCheckbox('backward');" ontouchstart="toggleCheckbox('backward');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Backward</button></td></tr>                   
</table>
<script>
    function toggleCheckbox(x) {
      var xhr = new XMLHttpRequest();
      xhr.open("GET", "/action?go=" + x, true);
      xhr.send();
    }
   window.onload = document.getElementById("photo").src =     window.location.href.slice(0, -1) + ":81/stream";
</script>

Voici les requêtes effectuées en fonction du bouton sur lequel on appuie :

Effronté:

<ESP_IP_ADDRESS>/action?go=forward

En arrière :

/action?go=backward

À gauche:

/action?go=left

À droite:

/action?go=right

Arrêter:

/action?go=stop

Lorsque vous relâchez le bouton, une demande est faite sur le /action?go=stop URL. Le robot ne bouge que tant que vous appuyez sur les boutons.

Traiter les demandes

Pour gérer ce qui se passe lorsque nous recevons des requêtes sur ces URL, nous utilisons ces instructions if… else :

if(!strcmp(variable, "forward")) {
    Serial.println("Forward");
    digitalWrite(MOTOR_1_PIN_1, 1);
    digitalWrite(MOTOR_1_PIN_2, 0);
    digitalWrite(MOTOR_2_PIN_1, 1);
    digitalWrite(MOTOR_2_PIN_2, 0);
 }
 else if(!strcmp(variable, "left")) {
   Serial.println("Left");
   digitalWrite(MOTOR_1_PIN_1, 0);
   digitalWrite(MOTOR_1_PIN_2, 1);
   digitalWrite(MOTOR_2_PIN_1, 1);
   digitalWrite(MOTOR_2_PIN_2, 0);
}
else if(!strcmp(variable, "right")) {
   Serial.println("Right");
   digitalWrite(MOTOR_1_PIN_1, 1);
   digitalWrite(MOTOR_1_PIN_2, 0);
   digitalWrite(MOTOR_2_PIN_1, 0);
   digitalWrite(MOTOR_2_PIN_2, 1);
}
else if(!strcmp(variable, "backward")) {
   Serial.println("Backward");
   digitalWrite(MOTOR_1_PIN_1, 0);
   digitalWrite(MOTOR_1_PIN_2, 1);
   digitalWrite(MOTOR_2_PIN_1, 0);
   digitalWrite(MOTOR_2_PIN_2, 1);
}
else if(!strcmp(variable, "stop")) {
   Serial.println("Stop");
   digitalWrite(MOTOR_1_PIN_1, 0);
   digitalWrite(MOTOR_1_PIN_2, 0);
   digitalWrite(MOTOR_2_PIN_1, 0);
   digitalWrite(MOTOR_2_PIN_2, 0);
}

Tester le code

Après avoir inséré vos informations d’identification réseau, vous pouvez télécharger le code sur votre carte ESP32-CAM. Si vous ne savez pas comment télécharger du code sur le tableau, suivez le tutoriel suivant :

Après le téléchargement, ouvrez le moniteur série pour obtenir son adresse IP.

ESP32-CAM Obtention de l'adresse IP Moniteur série

Ouvrez un navigateur et saisissez l’adresse IP ESP. Une page Web similaire devrait se charger :

Démonstration du robot 2 télécommandé du serveur Web ESP32-CAM

Appuyez sur les boutons et jetez un œil au Serial Monitor pour voir s’il diffuse sans décalage et s’il reçoit les commandes sans planter.

Commandes de moniteur série de robot télécommandé ESP32-CAM

Si tout fonctionne correctement, il est temps d’assembler le circuit.

Circuit

Après avoir assemblé le châssis du robot, vous pouvez câbler le circuit en suivant le schéma suivant.

Schéma du robot télécommandé ESP32-CAM Circuit de câblage des moteurs à courant continu

Commencez par connecter l’ESP32-CAM au pilote de moteur comme indiqué dans le schéma de principe. Vous pouvez utiliser une mini planche à pain ou un stripboard pour placer votre ESP32-CAM et construire le circuit.

Le tableau suivant montre les connexions entre l’ESP32-CAM et le pilote de moteur L298N.

Pilote de moteur L298N ESP32-CAM
EN 1 GPIO 14
EN 2 GPIO 15
IN3 GPIO 13
IN4 GPIO 12

Nous avons assemblé toutes les connexions sur un mini stripboard comme indiqué ci-dessous.

ESP32-CAM Robot Stripboard télécommandé pour circuit

Après cela, câblez chaque moteur à son bornier.

Noter: nous suggérons de souder un condensateur céramique de 0,1 uF aux bornes positive et négative de chaque moteur, comme indiqué sur le schéma pour aider à atténuer les pics de tension. De plus, vous pouvez souder un interrupteur coulissant au fil rouge provenant de la banque d’alimentation. De cette façon, vous pouvez allumer et éteindre l’appareil.

Enfin, appliquez l’alimentation avec une banque d’alimentation comme indiqué dans le diagramme schématique. Vous devez dénuder un câble USB. Dans cet exemple, l’ESP32-CAM et les moteurs sont alimentés en utilisant la même source d’alimentation et cela fonctionne bien.

Noter: les moteurs consomment beaucoup de courant, donc si vous pensez que votre robot ne se déplace pas assez vite, vous devrez peut-être utiliser une alimentation externe pour les moteurs. Cela signifie que vous avez besoin de deux sources d’alimentation différentes. L’un pour alimenter les moteurs à courant continu et l’autre pour alimenter l’ESP32. Vous pouvez utiliser un pack de 4 piles AA pour alimenter les moteurs. Lorsque vous obtenez votre kit de châssis de robot, vous obtenez généralement un support de batterie pour 4 piles AA.

Votre robot devrait ressembler à la figure suivante :

Robot de carte télécommandé ESP32-CAM assemblé

N’oubliez pas que vous devez utiliser une antenne externe avec l’ESP32-CAM, sinon le serveur Web pourrait être extrêmement lent.

Manifestation

Ouvrez un navigateur sur l’adresse IP ESP32-CAM et vous devriez pouvoir contrôler votre robot. Le serveur Web fonctionne bien sur un ordinateur portable ou un smartphone.

Démonstration du serveur Web robotisé télécommandé ESP32-CAM

Vous ne pouvez ouvrir le serveur Web que sur un seul appareil/onglet à la fois.

Conclusion

Dans ce didacticiel, vous avez appris à construire un robot télécommandé à l’aide de l’ESP32-CAM et à le contrôler à l’aide d’un serveur Web.

Contrôler les moteurs à courant continu avec l’ESP32-CAM revient à les contrôler à l’aide d’un ESP32 « régulier ». Lisez ce didacticiel pour en savoir plus : ESP32 avec moteur à courant continu et pilote de moteur L298N – Contrôle de la vitesse et de la direction.

Si vous souhaitez contrôler votre robot en dehors de la portée de votre réseau local, vous pouvez envisager de configurer l’ESP32-CAM comme point d’accès. De cette façon, l’ESP32-CAM n’a pas besoin de se connecter à votre routeur, il crée son propre réseau wi-fi et les appareils wi-fi à proximité comme votre smartphone peuvent s’y connecter.

Pour plus de projets et de tutoriels avec l’ESP32-CAM :