امروزه طراحی و ساخت یک ربات ماشینی دوربین دار به یکی از آموزشهای جذاب و پرکاربرد در زمینههای آموزشی شاخه رباتیک تبدیل شدهاست و ما در این پروژه میتوانیم از ماژول ESP32-CAM کمک بگیریم و آنچه که ربات میبیند را با استفاده از دوربین این ماژول در وب سرور نمایش دهیم تا بتوانیم ربات را حتی در صورتی که از دید ما خارج میشود نیز کنترل کنیم، همچنین امکان کنترل این ربات از راه دور و از طریق وای فای را داشتهباشیم.
لازم به ذکر است، ما در این پروژه برای پروگرام کردن ماژول ESP32-CAM از Arduino IDE استفاده میکنیم.
اجزاء و ویژگیهای مهم در ساخت ربات ماشینی
قبل از شروع به کار اجازه دهید به مهمترین ویژگیها و قطعات مورد استفاده در این پروژه بپردازیم.
1) Wi-Fi
کنترل ربات دوربین دار ما قرار است با استفاده از وای فای و ماژول ESP32-CAM انجام شود. برای کنترل این ربات یک وب سرور رابط میسازیم که با هر دستگاه متصل به شبکه محلی قابل دسترس است. این صفحه وب، یک ویدیو زنده هم از آنچه ربات میبیند، نمایش میدهد. برای گرفتن تصویر بهتر، توصیه میشود از ماژول به همراه آنتن خارجی استفاده کنید، چون ممکن است بدون آنتن خارجی ویدیو به کندی پخش شود و همچنین کنترل ربات از طریق وب سرور نیز با کندی و تاخیر همراه باشد.
2) کیت شاسی ربات
در این پروژه از شاسی ربات دو چرخ استفاده میکنیم که شامل شاسی پلاستیکی، دو چرخ پلاستیکی، دو موتور گیربکس پلاستیکی، چرخ هرزگرد و سایر قطعات لازم برای ساخت ربات از جمله پیچ و اسپیسرها میباشد.
البته شما میتوانید بنا به خلاقیت خود از شاسیهای مختلف فلزی، چوبی یا پلاستیکی و از موتور و چرخهای متفاوتی برای این ماشین کنترلی دوربین دار استفاده کنید. اما برای شروع به کار، این شاسی کار را بسیار ساده میکند.
3) درایور موتور L298N
برای کنترل موتورهای DC روشهای مختلفی وجود دارد. ما در این پروژه از درایور موتور L298N استفاده میکنیم و دلیل آن این است که کنترل سرعت و جهت چرخش دو موتور را بسیار ساده میکند.
- برای آشنایی با این درایور موتور و نحوه کار با آن، توصیه میکنیم مقاله آموزش راه اندازی ماژول درایور موتور L298 با آردوینو را مطالعه کنید.
4) منبع تغذیه ربات
به منظور اجتناب از پیچیدگی مدار، ربات (موتورها) و ماژول ESP32 را به تغذیه یکسان متصل میکنیم و بدین منظور از یک کیس مینی پاور بانک تک خروجی استفاده شدهاست.
توجه: معمولاً جریان مصرفی موتورها زیاد است، بنابراین اگر احساس کردید ربات شما به درستی حرکت نمیکند، به یک منبع تغذیه خارجی نیاز خواهید داشت. به این معنا که باید از دو منبع تغذیه استفاده کنید؛ یکی برای موتورهای DC و دیگری برای ماژول ESP32.
قطعات مورد نیاز
پس از آشنایی با قطعات در ساخت ربات ماشینی دوربین دار، نوبت به فراهم کردن قطعات زیر میباشد که ما برای دسترسی سریعتر لینک هرکدام را برای شما قرار دادهایم:
نحوه کنترل ربات ماشینی دوربین دار
همانطور که متوجه شدید ما برای کنترل از راه دور این ربات کنترلی دوربین دار از وب سرور یا مرورگر گوشی موبایل خود استفاده خواهیم کرد. این وبسرور دارای ۵ دکمه است: جلو (Forward)، عقب (Backward)، چپ (Left)، راست (Right) و توقف (Stop). تا وقتی دست ما روی یکی از دکمهها باشد، ربات در جهت متناظر حرکت میکند و وقتی دکمه رها شود، ربات میایستد. با این حال برخی اوقات ممکن است با برداشتن دست از روی دکمه، ESP32 فرمان توقف را دریافت نکند. در این مواقع وجود دکمه stop کمک میکند بتوانیم ربات را متوقف کنیم.
کد آردوینو
پس از آشنایی با نحوه عملکرد اجزای این ربات ماشینی کنترلی دوربین دار و مشخص شده راه ارتباطی با وبسرور، لازم است برنامه زیر را در IDE آردوینو روی کامپیوتر خود، کپی کنید تا به مراحل بعدی یعنی آپلود برنامه روی ماژول ESP32 برسیم.
اگر تاکنون برروی کامپیوتر خود برنامه 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 = "\r\n--" PART_BOUNDARY "\r\n";
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
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: %uB\n",(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ها، پینهای ورودی خروجی کنترلکننده موتور تعریف شدهاند که هر موتور با دو پین کنترل میشود.
#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=backward
حرکت به چپ
/action?go=left
حرکت به راست
/action?go=right
توقف
/action?go=stop
وقتی هر دکمه رها میکنید نیز باید یک درخواست توقف روی آدرس /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 را باز میکنیم.
- آدرس زیر را در قسمت Additional Board Manager URLs اضافه میکنیم و پنجره Preferences را با کلیک روی دکمه OK میبندیم.
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
توجه: اگر قبلا آدرس دیگری (مثل آدرس افزونه 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 باز کنید.
- در قسمت جستجو واژه ESP32 را تایپ و از میان موارد ظاهر شده، ESP32 by Espressif Systems را نصب کنید.
- پس از چند دقیقه (بسته به سرعت اینترنت) این پکیج نصب میشود.
حال نرم افزار ما آماده است.
- سپس برای آپلود برنامه، ابتدا برد ESP32-CAM خود را توسط یک ماژول مبدل USB به سریال (ماژول مبدل مدل FT232RL) به کامپیوتر متصل کنید. بدین منظور اتصالات خود را مطابق تصویر زیر انجام دهید:
مبدل FT232RL یک جامپر دارد که اجازه میدهد بین ولتاژ های 3.3 و 5 ولت یکی را انتخاب کنید. اطمینان حاصل کنید که این جامپر روی 5V قرار گرفته است.
توجه: ممکن است ترتیب پینهای ماژول مبدلی که استفاده میکنید با این تصویر متفاوت باشد، در این صورت جای نگرانی نیست و فقط باید به نام پینها توجه کرده و آنها را به درستی متصل کنید.
توجه: لازم است پین GPIO0 هنگام آپلود برنامه به GND متصل شود.
اتصالات مبدل به ماژول ESP32 در جدول زیر ذکر شدهاند:
ESP32-CAM | FTDI Programmer |
GND | GND |
5V | VCC (5V) |
U0R | TX |
U0T | RX |
GPIO 0 | GND |
- در این مرحله در برنامه آردوینو، از قسمت Tools > Board، برد AI-Thinker ESP32-CAM را انتخاب و سپس از بخش Tools > Port پورتی که مبدل USB به سریال به آن متصل شده را انتخاب نمایید.
اکنون میتوانیم روی دکمه آپلود کلیک کنیم:
- پس از کامپایل برنامه، عبارت …Connecting در پنجره دیباگ ظاهر میشود. در این مرحله باید دکمه ریست (RST) روی ماژول را یک بار فشار دهیم.
- پس از چند ثانیه برنامه با موفقیت آپلود میشود:
- حال نیاز است پایه GPIO0 را از GND جدا کنیم و یک بار دیگر دکمه ریست روی برد را فشار دهیم تا برنامه اجرا شود.
تست برنامه
پس از آپلود برنامه، شما نیاز دارید تا برنامه آپلود شده را تست کنید و مطمئن شوید بهدرستی عمل میکند، برای اینکار سریال مانیتور را باز کنید تا آدرس IP ماژول را ببینید.
همانطورکه در تصویر بالا مشاهده میکنید آدرس IP برد ما 192.168.1.87 است. در این زمان یک مرورگر باز و آدرس IP را در آن وارد کنید. حال باید وب سرور را مشاهده نمایید:
بر روی دکمهها کلیک کنید و با مشاهده سریال مانیتور، بررسی کنید که ارسال دستورات بدون تأخیر و قطعی انجام میشود.
چنانچه همه دکمهها به خوبی کار کردند، وقت آن است که مدار ربات ماشینی را کامل ببندیم.
تکمیل مدار و ساخت ربات دوربین دار با ماژول ESP32-CAM
پس از سر هم کردن شاسی ربات، میتوانید با استفاده از شماتیک زیر سیمبندی مدار را طبق مراحل گفتهشده در ادامه انجام دهید.
ابتدا لازم است ESP32-CAM را طبق تصویر به درایور موتور متصل کنید که برای ساخت مدار میتوانید از یک برد بورد کوچک یا یک بورد سوراخ دار استفاده کنید.
جدول زیر نحوه اتصال ماژول ESP32-CAM و درایور موتور را نشان میدهد:
L298N Motor Driver | ESP32-CAM |
IN1 | GPIO 14 |
IN2 | GPIO 15 |
IN3 | GPIO 13 |
IN4 | GPIO 12 |
همانطورکه میبینید ما همه این اتصالات را روی یک بورد سوراخ دار کوچک انجام دادیم.
- سپس موتورها را به ترمینالهای ماژول درایور موتور متصل کنید.
توجه: پیشنهاد میشود همانطورکه در شماتیک نشان داده شده، یک خازن عدسی 100 نانوفاراد هم روی ترمینالهای هر موتور لحیم کنید. این خازن پرشهای ولتاژ را کاهش میدهد. همچنین میتوانید یک کلید در مسیر سیم قرمزی که از ماژول پاوربانک خارج میشود قرار دهید و با آن تغذیه مدار را خاموش و روشن کنید.
- در نهایت، تغذیه را توسط ماژول پاور بانک متصل کنید. برای این کار نیاز است یک سیم با یک سر micro USB را بریده و از سیمهای مثبت و منفی آن استفاده کنید. ما در این پروژه تغذیه ESP32-CAM و موتورها را از منبع یکسان تأمین کردیم و نتیجه خوب و مطلوبی هم گرفتیم.
توجه: همانطورکه اشاره شد موتورها جریان تقریباً زیادی نیاز دارند، بنابراین اگر احساس میکنید ربات شما به کندی حرکت میکند، شاید نیاز باشد از منبع تغذیه جداگانهای برای موتورها استفاده کنید.
در نهایت ربات ماشینی دوربین دار ما به شکل زیر درمیآید:
نمایش عملکرد ربات ماشینی با کنترل از راه دور
پس از اینکه سیم بندی و اتصالات ربات را تکمیل کردید، به مرورگر خود بازگردید و آدرس IP را وارد کنید. حال باید بتوانید ربات ماشینی خود را کنترل کنید و تصویر دوربین را نیز مشاهده کنید. این وب سرور روی موبایل یا دسکتاپ به خوبی کار میکند.
توجه: این وب سرور را در هر لحظه، فقط روی یک سیستم میتوانید باز کنید.
جمع بندی
در این آموزش آموختیم چگونه یک ماشین کنترلی دوربین دار بسازیم و از طریق یک وب سرور، آن را از راه دور کنترل کنیم. شما میتوانید این پروژه را بهبود دهید، مثلاً برنامه را طوری تغییر دهید که ESP32-CAM در مد هاتاسپات کار کند. بنابراین ربات هر جایی باشد میتواند یک شبکه وای فای بسازد و هر وسیله نزدیک آن بتواند به شبکه متصل شده و ربات را کنترل کند.
درپایان امیدواریم با انجام این پروژه جالب با ماژول ESP32-CAM، به نتیجه دلخواه خود رسیده باشید، اما درصورتی که در حین مطالعه یا ساخت این ربات دوربین دار دچار مشکل شدید یا سوالی ذهن شما را درگیر خود کرد، جای هیچ نگرانی نیست چراکه تیم کارشناسان مجموعه روبوایکیو همیشه آماده پاسخگویی به سوالات شما عزیزان هستند. به همین منظور در بخش دیدگاه این صفحه، نظرات و سوالات خود را با ما درمیان بگذارید و پاسخ خود را در اسرع وقت دریافت کنید.
۲۹ دیدگاه. Leave new
سلام
توسط شیلد ESP32CAM برنامه اصلآ آپلود نشد پس به آدرس
https://www.electroniclinic.com/esp32-cam-esp32-camera-programming-using-arduino-issues-fixed/
رفته و توسط arduino uno به عنوان پروگرامر. برنامه BLINK آپلود شد و کار کرد اما برنامه شما آپلود شد ولی پس از قطع اتصال پایه ها ی gnd و IO0 و زدن دکمه RST روی ESP32CAM آدرس IP مربوطه به دست نمی آیدو خطای زیر می آید
ets Jul 29 2019 12:21:46
rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:1216
ho 0 tail 12 room 4
load:0x40078000,len:10944
load:0x40080400,len:6360
entry 0x400806b4
Camera init failed with error 0x20004
درود بر شما علیرضا عزیز
به نظر میرسه ارور به این خاطر هست که ماژول شما به وای فای متصل نمیشود. لطفا بررسی کنید که اسم و رمز شبکه وای فای رو به درستی وارد میکنید.
سلام
از نظر اسم و رمز شبکه وای فای درست وارد میکنم.داداشم یک دوربین لامپی وای فای دار قبلآ خریده که توسط نرم افزار مربوط به اون تصویر دیده شده و جهت دوربین کنترل می شود, قبلآ درست کار میکرد اما چند وقتیست که به وای فای وصل نمیشود.داداشم میگه که بعد از قضیه تحریم IP وسایل خارجی این مشکل پیش اومده.آیا درست است یا نه?
تشکر
درود برشما دوست عزیز
در پاسخ به سوالتون باید بگیم که، خیر، ما اصلاً تو این پروژه از اینترنت استفاده نکردیم و با شبکه محلی کار میکنیم. بهعلاوه این مقاله طبق روش ذکر شده، تست شده. همانطور که عرض شد ابتدا اتصال وای فای رو بررسی کنید. برای مثال در منوی مودم، یا تنظیمات هاتاسپات گوشی موبایل میتونید دستگاههای متصل رو ببینید و چک کنید که ESP32 در بین اونها هست یا خیر.
موفق باشید 🙂
با سلام
در تنظیمات مودم خانگی تان Security Mode را wpa2-psk انتخاب فرمایید.
سلام ببخشید میشه این ماژول رو با۳.۷ولت راه اندازی کرد
سلام و درود برشما هادی عزیز
اصولاً تغذیه پیشنهادی ماژول esp32-cam بازه ۴.۷۵ تا ۵.۲۵ ولت است. اما احتمال داره بتونید با ۳.۷ ولت هم راهاندازی کنید که نیاز به تست دارد.
سلام و خسته نباشید
ممنون از سایت خوبتون و اموزش های مفید که به من خیلی کمک کردن
لطفاً اموزش راه اندازی و فیلم برداری با ماژول دوربین رنگی ov7670 رو هم بزارید.
ممنون
سلام بنیامین عزیز
خیلی ممنونیم ازت که مطالب ما رو دنبال میکنی، حتماً این آموزش رو در آینده میذاریم تو لیست موضوعاتمون.
سلام میشه فیلم و تصویر رو توسط بلوتوث به موبایل انتقال داد و یک روبات کنترلی ساخت؟
ممنان
درود بر شما علیرضا جان
امکانش هست اما همونطور که احتمالا تجربه کردهاید، بلوتوث سرعت مناسبی برای انتقال تصویر و ویدیو نداره. بنابراین در حالت عادی بلوتوث نتیجه خوبی نخواهد داد.
سلام و ممنون از آموزش خوبتون آیا میشه برنامه رو جوری تغییر داد که به اینترنت وصل بشه و مثلا بفرستیمش تو خیابون و از توی خونه اون رو کنترل کنیم حالا برای اینکه ربات رو دزد نبره هم فکری کرد
درود برشما محمدجواد عزیز
برای این کار نیاز هست یک سرور تهیه کنید و اطلاعات را روی سرور ارسال و دریافت کنید. بنابراین کل برنامه دچار تغییر میشه و برنامهنویسی سمت سرور هم به پروژه اضافه میشه که حقیقتا به سادگی عوض کردن چند خط کد نیست و آموزش مجزایی نیاز داره.
سلام میگم متور ها با ولتاژ ۵ کار میکنند
درود برشما دوست عزیز
موتورهایی که ما استفاده کردیم ۵ ولت هستند.
درود بر شما.
چطور میشه کد رو جوری تغییر داد تا ماشین با کلید های کیبورد هم حرکت کنه؟
سلام محمد عزیز
برای انجام این کار تنها کافی است دو تابع زیر رو به تگ script اضافه کنید:
document.addEventListener(‘keydown’,function(event){
var pressedkey = event.key;
if(pressedkey==”ArrowUp”){
toggleCheckbox(‘forward’);
}
if(pressedkey==”ArrowDown”){
toggleCheckbox(‘backward’);
}
if(pressedkey==”ArrowLeft”){
toggleCheckbox(‘left’);
}
if(pressedkey==”ArrowRight”){
toggleCheckbox(‘right’);
}
});
document.addEventListener(‘keyup’,function(event){
toggleCheckbox(‘stop’);
});
و تگ زیر را بعد از تگ h1 قرار بدهید:
توابع addEventListener هنگام فشرده شدن و رها کردن کلیدهای کیبورد اجرا میشوند. در تابع اول مشخص کردیم وقتی هر یک از کلیدهای جهت نما فشرده شود، تابع toggleCheckbox با دستور متناسب با کلید فراخوانی شود. در تابع دوم نیز وقتی هیچ کلیدی فشرده نشده تابع toogleCheckbox با دستور stop فراخوانی میشود.
سلام وقت بخیر
بسیار ممنونم از اموزش بسیار کامل تون
ببخشید فقط یه نکته با وایفای گوشی چند متر برد دارد در فضای کاملا باز و در فضایی که مثلا یع دیوار گچی بینشون باشه
سلام درود خدمت شما محمد عزیز
برد ماژول مشابه هات اسپات گوشی موبایل هست. با وجود دیوار بین ۵ تا ۱۰ متر و در فضای باز تا ۵۰ متر میتونید در نظر بگیرید.
سلام، ارور brownout detector was triggered رو دارم در سریال مانیتور، با تغذیه خارجی این ارور رو رفع کردم ولی باز آی پی آدرس رو برام نمایش نمیده، ولی داخل هات اسپات موبایل که نگاه میکنم، esp32 cam متصل شده هستش.
از بلند بودن طول کابل هم میشه؟ به نظرتون از کابل mini usb کوتاه تر استفاده کنم مشکل حل میشه؟
ممنون
درود برشما دوست و همراه عزیز
بنده تجریهای در خصوص این ارور ندارم اما ارور bownout مربوط به تغذیه است. طبیعتاً کابل بلند یا بی کیفیت میتواند ایجاد مشکل کند. همچنین بهتر است تغذیه ۵ ولت ماژول را از یک منبع خارجی مطمئن تامین کنید. میتوانید ولتاژ تغذیه ماژول را با استفاده از مولتیمتر یا اسیلوسکوپ دنبال کنید.
موفق باشید 🙂
سلام و عرض خسته نباشید خدمت شما
برای اینکه esp32 cam در مد هات اسپات کار کنه چه تغییر ی باید در کد ایجاد بشه
سلام هادی عزیز
ممنونیم از همراهی خوبتان در این آکادمی 🙂
برای انجام این کار لطفا لینک زیر رو دنبال کنید:
https://randomnerdtutorials.com/esp32-cam-access-point-ap-web-server/
سلام
موقع آپلود برنامه در ماژول این خطا رو نشان می ده مشکلش چیه؟
Sketch uses 844637 bytes (26%) of program storage space. Maximum is 3145728 bytes.
Global variables use 55064 bytes (16%) of dynamic memory, leaving 272616 bytes for local variables. Maximum is 327680 bytes.
esptool.py v4.5.1
Serial port COM9
A fatal error occurred: Could not open COM9, the port doesn’t exist
Failed uploading: uploading error: exit status 2
سلام یاسمین عزیز
این ارور مربوط به این است که پورت COM را داخل برنامه آردوینو به درستی انتخاب نکردهاید. ابتدا از device manager ویندوز، شماره پورتی که ماژول مبدل سریال به آن متصل است را پیدا کنید و سپس همان پورت را در آردوینو انتخاب کنید.
توی آردینو فقط یک پورت برای من میاد که Com هستش و منیجر کامپیوتر رو همچک کردم و همه این هارو درست انجام دادم اما بازم ارور میگیره
درود بر شما یاسمین عزیز
اگه مطمئن هستید همه اقدامات را صحیح و کامل انجام می دهید؛ یا پورت USB کامپیوتر شما مشکلی دارد (میتونید با پورتهای مختلف تست کنید) یا مبدل USB به سریال ایراد دارد (برای بررسی سلامت مبدل، میتوانید RX و TX مبدل را به هم وصل کنید و در نرمافزارهای ارتباط سریال مانند SSCOM، HERCULES یا سایر نرمافزارهای مشابه ارسال و دریافت اطلاعات رو بررسی کنید.)
سلام و عرض خسته نباشید خیلی ممنون از آموزش های خوبتون تمام وسایل های این پروژه را از فروشگاه خودتون خریدم که کیفیت خیلی خوبی داشت با راهنمایی های شما تونستم ماژول esp32 cam رو راه اندازی کنم
فقط من هم موقع آپلود کد به همین ارور برخوردم اما وقتی GND رو به پایه GND کنار ۵V وصل کردم درست شد
درود بر شما هادی عزیز
سپاس از اشتراک گذاری تجربتون 🙂