منو سایت

  • خانه
  • وبلاگ
  • آموزش ساخت ماشین ربات دوربین دار با ماژول ESP32-CAM

آموزش ساخت ماشین ربات دوربین دار با ماژول ESP32-CAM

 تاریخ انتشار :
/
  وبلاگ
آموزش ساخت ماشین ربات دوربین دار با ماژول ESP32-CAM

امروزه طراحی و ساخت ربات ماشینی با دوربین به یکی از جذاب ترین و پرکاربردترین آموزش ها در زمینه رباتیک تبدیل شده است و در این پروژه می توان از ماژول ESP32-CAM و آنچه ربات با استفاده از دوربین می بیند کمک گرفت. این ماژول بر روی سرور شبکه نمایش داده می شود تا بتوانیم ربات را حتی در صورت خارج شدن از دیدمان کنترل کنیم و همچنین توانایی کنترل این ربات را از راه دور و از طریق وای فای داشته باشیم.

لازم به ذکر است که در این پروژه از Arduino IDE برای برنامه ریزی ماژول ESP32-CAM استفاده می کنیم.

اجزا و ویژگی های مهم در ساخت ماشین ربات

قبل از شروع، مهم ترین ویژگی ها و اجزای مورد استفاده در این پروژه را مرور می کنیم.

1) وای فای

ربات ماشین ما با استفاده از Wi-Fi و یک ماژول ESP32-CAM کنترل می شود. برای کنترل این ربات، یک رابط وب سرور ایجاد می کنیم که از هر دستگاه متصل به شبکه محلی قابل دسترسی است. صفحه وب همچنین یک ویدیوی زنده از آنچه ربات می بیند را نشان می دهد. برای تصویر بهتر استفاده از ماژول با آنتن خارجی توصیه می شود زیرا بدون آنتن خارجی می توان فیلم را به کندی پخش کرد و همچنین کنترل ربات از طریق وب سرور نیز کند و با تاخیر انجام می شود.

ماژول ESP32-CAM برای ساخت ماشین ربات
آنتن خارجی برای کنترل از راه دور ماشین ربات

2) کیت شاسی ربات

در این پروژه از شاسی ربات دو چرخ استفاده می کنیم که شامل شاسی پلاستیکی، دو چرخ پلاستیکی، دو موتور گیربکس پلاستیکی، چرخ یدکی و سایر قطعات مورد نیاز برای ساخت ربات شامل پیچ و اسپیسر می باشد.

البته می توانید با توجه به خلاقیت خود از شاسی های فلزی، چوبی یا پلاستیکی مختلف استفاده کنید یا از موتورها و چرخ های مختلف استفاده کنید. اما برای شروع، این شاسی کار را بسیار ساده می کند.

شاسی ربات دو چرخ برای ساخت ماشین ربات

3) راننده موتور سیکلت L298N

روش های مختلفی برای کنترل موتورهای DC وجود دارد. ما در این پروژه از درایور موتور L298N استفاده می کنیم زیرا کنترل سرعت و جهت چرخش دو موتور را بسیار آسان می کند.

اجزای ماژول درایور موتور L298N

4) تغذیه ربات

برای جلوگیری از پیچیدگی مدار، ربات (موتورها) و ماژول ESP32 را به یک منبع تغذیه متصل می کنیم و برای این منظور از یک کیس مینی پاور بانک تک خروجی استفاده می کنیم.
توجه: موتورها معمولا جریان زیادی می کشند، بنابراین اگر فکر می کنید ربات شما به درستی حرکت نمی کند، به منبع تغذیه خارجی نیاز دارید. به این معنا که شما باید از دو منبع تغذیه استفاده کنید. یکی برای موتورهای DC و دیگری برای ماژول ESP32.

مینی پاور باکس تک خروجی برای ساخت ربات خودرویی با باتری

قطعات لازم

اکنون که با قطعات ساخت ماشین ربات دوربین دار آشنا شدید، نوبت به ارائه قطعات زیر می رسد که برای دسترسی سریعتر لینک آن را در اختیار شما قرار داده ایم:

نحوه رانندگی با ماشین ربات با دوربین

همانطور که می بینید، ما از وب سرور یا مرورگر تلفن همراه خود برای کنترل از راه دور این ربات کنترلی استفاده خواهیم کرد. این وب سرور دارای 5 دکمه است: رو به جلو، بازگشت، به سمت چپ، درست است، واقعی و خودت را متوقف کن. تا زمانی که دست ما روی یکی از دکمه ها باشد، ربات در جهت مربوطه حرکت می کند و با رها شدن دکمه، ربات می ایستد. با این حال، گاهی اوقات ESP32 ممکن است وقتی دست خود را از روی دکمه بردارید، دستور توقف را دریافت نکند. در این مواقع داشتن دکمه توقف به ما کمک می کند تا ربات را متوقف کنیم.

با تلفن همراه به وب سرور متصل شوید و ربات را از راه دور با دکمه های جلو، عقب، چپ، راست و توقف کنترل کنید.

کد آردوینو

پس از یادگیری نحوه کار اجزای این ربات ماشینی و مشخص کردن مسیر ارتباطی با وب سرور، لازم است برنامه زیر را در Arduino IDE کامپیوتر خود کپی کنید تا به مراحل بعدی یعنی. بارگذاری برنامه در ماژول ESP32.

اگر هنوز برنامه Arduino IDE را بر روی کامپیوتر خود نصب نکرده اید، می توانید با دانلود این برنامه از قسمت نرم افزار وبسایت آردوینو، نسخه مناسب سیستم خود را دانلود و نصب کنید.

/*********
  Rui Santos
  Complete instructions at https://RandomNerdTutorials.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() {
  
}

پس از انتقال قطعه کد بالا، باید مشخصات شبکه وای فای خود را در این قسمت وارد کنید:

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

توضیحات کد:

بیایید نگاهی به قسمت های مختلف برنامه بیندازیم:

  • قسمت تعریف، پایه های ورودی و خروجی کنترلر موتور را مشخص می کند، هر موتور توسط دو پایه کنترل می شود.
#define MOTOR_1_PIN_1    14
#define MOTOR_1_PIN_2    15
#define MOTOR_2_PIN_1    13
#define MOTOR_2_PIN_2    12
  • در قطعه کد زیر یک وب سرور حاوی دکمه ها و یک صفحه ایجاد کرده ایم که با کلیک بر روی هر دکمه، یک درخواست HTML به URL مخصوص آن دکمه ارسال می شود.
<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>
  • URL درخواست برای هر دکمه در زیر فهرست شده است:

حرکت رو به جلو

<ESP_IP_ADDRESS>/action?go=forward

به عقب حرکت کنید

حرکت به سمت چپ

برو راست

خودت را متوقف کن

هنگامی که هر دکمه را رها می کنید، یک درخواست توقف نیز باید به /action?go=stop ارسال شود تا ربات تنها با فشار دادن یک دکمه حرکت کند.

پرس و جوها را با دستورات شرطی مدیریت کنید

ما از دستورات شرطی برای پاسخ به درخواست های ارسال شده به URL های فهرست شده استفاده می کنیم:

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);
}

پس از افزودن نام و رمز عبور شبکه وای فای، می توانیم برنامه را در ماژول ESP32-CAM آپلود کنیم. آپلود کد روی این برد با بردهای دیگری مانند آردوینو یا NodeMCU کمی متفاوت است، بنابراین بهتر است ابتدا درباره نحوه آپلود کد بحث کنید.

نحوه آپلود برنامه ماژول ESP32-CAM

با فرض نصب برنامه آردوینو در این مرحله پس از انجام اتصالات ذکر شده باید افزونه ESP32 را بر روی آردوینو IDE نصب کنیم برای این کار از تب File گزینه Preferences را باز می کنیم.

برای نصب افزونه ESP32 از منوی File گزینه Preferences را انتخاب کنید
  • آدرس زیر را در بخش URLهای مدیر تابلوی اضافی اضافه کنید و با کلیک بر روی دکمه OK پنجره تنظیمات را ببندید.
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
آدرس نوشته شده در بخش URLهای مدیر تابلوهای اضافی را انتخاب و کپی کنید

توجه: اگر قبلاً آدرس دیگری (مانند آدرس پلاگین ESP8266) را در این قسمت وارد کرده اید، کافی است یک کاما (,) بین آنها قرار دهید تا URL ها از هم جدا شوند. مثلا:

https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json, http://arduino.esp8266.com/stable/package_esp8266com_index.json
  • اکنون Boards Manager را از Tools > Board > Boards Manager باز کنید.
گزینه Boards Manager را انتخاب کنید
  • کلمه ESP32 را در کادر جستجو تایپ کنید و ESP32 by Espressif Systems را از آیتم های ظاهر شده نصب کنید.
نصب ESP32 از Espressif Systems
  • پس از چند دقیقه (بسته به سرعت اینترنت شما) این بسته نصب می شود.
تکمیل مرحله نصب ESP32 از Espressif Systems

اکنون نرم افزار ما آماده است.

  • سپس برای آپلود برنامه ابتدا برد ESP32-CAM خود را از طریق ماژول مبدل USB به سریال (ماژول مبدل مدل FT232RL) به کامپیوتر متصل کنید. برای این منظور اتصالات خود را مطابق تصویر زیر انجام دهید:
نحوه اتصال ماژول ESP32-CAM به مبدل USB به سریال

مبدل FT232RL دارای جامپر است که به شما امکان می دهد بین 3.3 ولت و 5 ولت یکی را انتخاب کنید. مطمئن شوید که این جامپر روی 5 ولت تنظیم شده است.

توجه: ممکن است ترتیب پین ماژول مبدلی که استفاده می کنید با این تصویر متفاوت باشد، در این صورت جای نگرانی نیست و فقط باید به نام پین ها دقت کنید و آنها را به درستی وصل کنید.

توجه: لازم است هنگام آپلود برنامه، پین GPIO0 را به GND متصل کنید.

پین های FT232RL روی مبدل USB به سریال

اتصالات مبدل به ماژول ESP32 در جدول زیر آمده است:

ESP32-CAMبرنامه نویس FTDI
GNDGND
5 ولتVCC (5 ولت)
U0RTX
U0TRX
GPIO 0GND
  • در این مرحله از برنامه آردوینو، برد AI-Thinker ESP32-CAM را از قسمت Tools > Board انتخاب کنید و سپس از قسمت Tools > Port پورتی که مبدل USB به سریال به آن متصل است را انتخاب کنید.

اکنون می توانیم روی دکمه آپلود کلیک کنیم:

نماد دکمه آپلود برنامه
  • پس از کامپایل شدن برنامه، عبارت …Link در پنجره اشکال زدایی ظاهر می شود. در این مرحله باید دکمه ریست (RST) روی ماژول را یک بار فشار دهید.
پیام اتصال در پنجره اشکال زدایی برنامه
دکمه تنظیم مجدد ماژول ESP32-CAM
  • پس از چند ثانیه، برنامه با موفقیت آپلود می شود:
نمایش پیام "آپلود کامل شد" در IDE
  • حالا باید پین GPIO0 را از GND جدا کنیم و دوباره دکمه ریست روی برد را فشار دهیم تا برنامه شروع شود.

تست کردن برنامه

پس از آپلود برنامه، باید برنامه آپلود شده را تست کنید و مطمئن شوید که درست کار می کند، برای این کار مانیتور سریال را باز کنید تا آدرس IP ماژول را ببینید.

نشانی IP برنامه آپلود شده ماژول ESP32-CAM را نشان دهید

همانطور که در تصویر بالا مشاهده می کنید، آدرس IP برد ما 192.168.1.87 است. در این مرحله یک مرورگر را باز کنید و آدرس IP را در آن وارد کنید. اکنون باید وب سرور را ببینید:

با وارد کردن آدرس IP در مرورگر، برای کنترل از راه دور دستگاه ربات وارد سرور وب شوید

روی دکمه ها کلیک کنید و با دیدن مانیتور سریال بررسی کنید که ارسال دستورات بدون تاخیر و وقفه انجام شود.

پس از بررسی دکمه های وب سرور، صفحه سریال را دنبال کنید

اگر همه دکمه ها به خوبی کار می کنند، زمان تکمیل مدار ماشین ربات فرا رسیده است.

تکمیل مدار و ساخت ربات دوربین با ماژول ESP32-CAM

پس از مونتاژ شاسی ربات می توانید طبق مراحل زیر از نمودار زیر برای اتصال مدار استفاده کنید.

ابتدا باید ESP32-CAM را همانطور که در تصویر نشان داده شده است به درایور موتور متصل کنید. برای ساخت مدار می توانید از یک برد مدار کوچک یا برد دارای سوراخ استفاده کنید.

نحوه سیم کشی و اتصال مدار ربات دوربین با ماژول ESP32-CAM و درایور موتور

جدول زیر نحوه اتصال ماژول ESP32-CAM و درایور موتور را نشان می دهد:

درایور موتور L298NESP32-CAM
در 1GPIO 14
در 2GPIO 15
IN3GPIO 13
IN4GPIO 12

همانطور که می بینید، ما تمام این اتصالات را روی یک تخته پانچ کوچک انجام دادیم.

تخته با یک سوراخ کوچک برای اتصال یک ماژول ESP32-CAM
  • سپس موتورها را به پایانه های ماژول درایور موتور متصل کنید.

توجه: پیشنهاد می شود همانطور که در شکل شماتیک نشان داده شده است، یک خازن لنز 100 نانوفاراد را روی پایانه های هر موتور لحیم کنید. این خازن افزایش ولتاژ را کاهش می دهد. همچنین می توانید در مسیر خروج سیم قرمز رنگ از ماژول پاوربانک یک کلید قرار دهید و با آن برق مدار را خاموش و روشن کنید.

  • در نهایت برق را از طریق ماژول پاوربانک وصل کنید. برای این کار باید یک سیم را با انتهای میکرو USB قطع کنید و از سیم های مثبت و منفی آن استفاده کنید. ما در این پروژه برق ESP32-CAM و موتورها را از یک منبع تامین کردیم و نتیجه خوب و مطلوبی گرفتیم.

توجه: همانطور که گفته شد، موتورها به جریان بسیار زیادی نیاز دارند، بنابراین اگر فکر می کنید ربات شما کند حرکت می کند، ممکن است لازم باشد از منبع تغذیه جداگانه برای موتورها استفاده کنید.

در نهایت، ربات ماشین ما به شکل زیر خواهد بود:

دوربین رباتی که با استفاده از ماژول ESP32-CAM ساخته شده است

نمایش عملکرد ربات بر روی دستگاه کنترل از راه دور

پس از اتمام سیم کشی و اتصال ربات، به مرورگر خود برگردید و آدرس IP را وارد کنید. اکنون باید بتوانید ربات ماشین خود را رانندگی کنید و تصویر را از دوربین مشاهده کنید. این وب سرور روی موبایل یا دسکتاپ به خوبی کار می کند.

استفاده از وب سرور برای کنترل ماشین ربات و حرکت آن

توجه: شما فقط می توانید این وب سرور را در یک سیستم در یک زمان باز کنید.

نتیجه

در این آموزش یاد گرفتیم که چگونه یک ماشین دوربین بسازیم و آن را از راه دور از طریق وب سرور کنترل کنیم. شما می توانید این پروژه را بهبود ببخشید، به عنوان مثال برنامه را تغییر دهید تا ESP32-CAM در حالت هات اسپات کار کند. بنابراین ربات در هر کجا که باشد می تواند یک شبکه وای فای ایجاد کند و هر وسیله ای که نزدیک آن باشد می تواند به شبکه متصل شود و ربات را کنترل کند.

در پایان امیدواریم با انجام این پروژه جالب به نتیجه دلخواه رسیده باشید اما اگر در حین یادگیری یا ساخت این ربات ماشین با مشکل یا سوالی مواجه شدید جای نگرانی نیست زیرا تیم متخصص RoboQ همیشه آماده پاسخگویی به سوالات عزیزان برای این منظور نظرات و سوالات خود را در قسمت نظرات همین صفحه با ما در میان بگذارید و در اسرع وقت پاسخ خود را دریافت کنید.