ESP32 웹을 통한 자동차 제어

이 가이드는 ESP32를 사용하여 WiFi를 통해 스마트폰이나 PC의 웹 브라우저에서 로봇 차를 무선으로 제어하는 방법을 보여줍니다. 제어는 WebSocket이라는 것을 사용하는 그래픽 웹 사용자 인터페이스를 통해 이루어지며, 이를 통해 차량을 부드럽고 동적으로 제어할 수 있습니다.

ESP32 controls 로봇 자동차 via web

준비물

1×ESP32 ESP-WROOM-32 개발 모듈 쿠팡 | 아마존
1×USB 케이블 타입-A to 타입-C (USB-A PC용) 쿠팡 | 아마존
1×USB 케이블 타입-C to 타입-C (USB-C PC용) 아마존
1×2WD RC Car 쿠팡 | 아마존
1×L298N 모터 드라이버 모듈 쿠팡 | 아마존
1×IR 리모컨 키트 아마존
1×CR2025 배터리 (IR 리모컨용) 아마존
1×1.5V AA Battery (for ESP32 and Car) 아마존
1×점퍼케이블 쿠팡 | 아마존
1×브레드보드 쿠팡 | 아마존
1×(추천) ESP32용 스크루 터미널 확장 보드 쿠팡 | 아마존
1×(추천) Breakout Expansion Board for ESP32 쿠팡 | 아마존
1×(추천) ESP32용 전원 분배기 쿠팡 | 아마존
공개: 이 포스팅 에 제공된 일부 링크는 아마존 제휴 링크입니다. 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

2WD RC 자동차 및 WebSocket 정보

이제, 왜 WebSocket을 사용하나요? 여기 정보가 있습니다:

  • WebSocket 없이는 자동차의 방향을 변경할 때마다 페이지를 새로고침해야 합니다. 이상적이지 않죠!
  • 하지만 WebSocket을 사용하면, 웹페이지와 ESP32 사이에 특별한 연결을 설정합니다. 이를 통해 페이지를 새로고침하지 않고도 배경에서 ESP32에 명령을 보낼 수 있습니다. 결과는? 로봇 자동차가 실시간으로 매끄럽게 움직입니다. 꽤 멋지지 않나요?

간단히 말해서, WebSocket 연결은 로봇의 원활하고 실시간 제어를 가능하게 합니다.

2WD RC 자동차와 WebSocket에 대한 구체적인 튜토리얼이 있습니다. 각 튜토리얼에는 하드웨어 핀아웃, 작동 원리, ESP32에 대한 배선 연결, ESP32 코드에 대한 자세한 정보와 단계별 지침이 포함되어 있습니다. 다음 링크에서 자세한 정보를 알아보세요:

작동 원리

ESP32 코드는 웹 서버와 WebSocket 서버를 모두 생성합니다. 다음은 그 작동 방식입니다:

  • 웹 브라우저에 ESP32의 IP 주소를 입력하면, 웹 브라우저는 ESP32로부터 웹페이지(사용자 인터페이스)를 요청합니다.
  • ESP32의 웹 서버는 웹페이지 콘텐츠(HTML, CSS, JavaScript)를 보내는 것으로 응답합니다.
  • 그러면 웹 브라우저는 웹페이지를 표시합니다.
  • 웹페이지 내의 JavaScript 코드는 ESP32상의 WebSocket 서버에 WebSocket 연결을 설정합니다.
  • 이 WebSocket 연결이 설정되면, 웹페이지의 버튼을 누르거나 놓으면 JavaScript 코드가 이 WebSocket 연결을 통해 명령을 ESP32 쪽으로 조용히 전송합니다.
  • ESP32상의 WebSocket 서버는 명령을 받고 나면, 그에 따라 로봇 자동차를 제어합니다.

아래 표는 사용자의 행동에 따라 웹페이지가 ESP32에 보내는 명령 목록을 보여줍니다:

User's Action Button Command Car Action
PRESS UP 1 MOVE FORWARD
PRESS DOWN 2 MOVE BACKWARD
PRESS LEFT 4 TURN LEFT
PRESS RIGHT 8 TURN RIGHT
PRESS STOP 0 STOP
RELEASE UP 0 STOP
RELEASE DOWN 0 STOP
RELEASE LEFT 0 STOP
RELEASE RIGHT 0 STOP
RELEASE STOP 0 STOP

2WD RC 자동차와 ESP32 간의 배선도

ESP32 2wd rc car 연결 배선도

이 이미지는 Fritzing을 사용하여 만들어졌습니다. 이미지를 확대하려면 클릭하세요.

ESP32 및 다른 구성 요소에 전원을 공급하는 방법에 대해 잘 알지 못하는 경우, 다음 튜토리얼에서 안내를 찾을 수 있습니다: ESP32 전원 공급 방법.

ESP32 2wd rc car via web

일반적으로 두 개의 전원이 필요합니다:

  • L298N 모듈을 통한 모터용 하나.
  • ESP32 보드용 또 다른 하나, L298N 모듈(모터 드라이버).

하지만 모든 것을 위해 단 하나의 전원원으로 단순화할 수 있습니다 - 1.5V 배터리 4개를 사용하여 총 6V입니다. 방법은 다음과 같습니다:

  • 아래와 같이 배터리를 L298N 모듈에 연결하세요.
  • ENA와 ENB 핀에서 점퍼 두 개를 제거하고 L298N 모듈의 5볼트로 연결하세요.
  • 5VEN이라고 표시된 점퍼(도표에 노란색 원으로 표시됨)를 추가하세요.
  • 배터리에서 직접 전원을 공급하기 위해 L298N 모듈의 12V 핀을 ESP32의 Vin 핀에 연결하세요.

2WD RC 자동차에는 온/오프 스위치가 있어, 선택적으로 스위치를 통해 배터리를 연결하여 자동차의 전원을 켜고 끌 수 있습니다. 간단하게 하고 싶다면, 스위치는 무시하면 됩니다.

ESP32 코드

웹페이지의 콘텐츠(HTML, CSS, JavaScript)는 index.h 파일에 별도로 저장됩니다. 그래서 우리는 Arduino IDE에서 두 개의 코드 파일을 가질 것입니다:

  • ESP32 코드인 .ino 파일로, 웹 서버와 웹소켓 서버를 생성하고 자동차를 제어합니다.
  • 웹페이지의 내용이 포함된 .h 파일입니다.

사용 방법

  • ESP32를 처음 사용하는 경우, ESP32 - 소프트웨어 설치을 참조하세요.
  • 위 이미지와 같이 배선하세요.
  • ESP32 보드를 마이크로 USB 케이블을 사용하여 PC에 연결하세요.
  • PC에서 Arduino IDE를 엽니다.
  • 올바른 ESP32 보드(예: ESP32 Dev Module)와 COM 포트를 선택하세요.
  • Arduino IDE의 왼쪽 탐색 바에 있는 Library Manager 아이콘을 클릭하여 라이브러리 관리자를 엽니다.
  • “DIYables ESP32 WebServer”를 검색한 다음, DIYables가 만든 DIYables ESP32 WebServer를 찾으세요.
  • Install 버튼을 클릭하여 DIYables ESP32 WebServer 라이브러리를 설치하세요.
ESP32 웹 서버 라이브러리

Arduino IDE에서 새 스케치를 생성하고 이름을 지정하세요. 예를 들어, newbiely.kr.ino

아래 코드를 복사하여 Arduino IDE로 열기

/* * 이 ESP32 코드는 newbiely.kr 에서 개발되었습니다 * 이 ESP32 코드는 어떠한 제한 없이 공개 사용을 위해 제공됩니다. * 상세한 지침 및 연결도에 대해서는 다음을 방문하세요: * https://newbiely.kr/tutorials/esp32/esp32-controls-car-via-web */ #include <DIYables_ESP32_WebServer.h> #include "index.h" #define CMD_STOP 0 #define CMD_FORWARD 1 #define CMD_BACKWARD 2 #define CMD_LEFT 4 #define CMD_RIGHT 8 #define ENA_PIN 14 // The ESP32 pin GPIO14 connected to the ENA pin L298N #define IN1_PIN 27 // The ESP32 pin GPIO27 connected to the IN1 pin L298N #define IN2_PIN 26 // The ESP32 pin GPIO26 connected to the IN2 pin L298N #define IN3_PIN 25 // The ESP32 pin GPIO25 connected to the IN3 pin L298N #define IN4_PIN 33 // The ESP32 pin GPIO33 connected to the IN4 pin L298N #define ENB_PIN 32 // The ESP32 pin GPIO32 connected to the ENB pin L298N // WiFi credentials const char WIFI_SSID[] = "YOUR_WIFI_SSID"; const char WIFI_PASSWORD[] = "YOUR_WIFI_PASSWORD"; // Create web server instance DIYables_ESP32_WebServer server; DIYables_ESP32_WebSocket* webSocket; // Web Page handlers void handleHome(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { server.sendResponse(client, HTML_CONTENT); // HTML_CONTENT is from index.h } // WebSocket event handlers void onWebSocketOpen(net::WebSocket& ws) { Serial.println("New WebSocket connection"); // Send welcome message const char welcome[] = "Connected to ESP32 WebSocket Server!"; } void onWebSocketMessage(net::WebSocket& ws, const net::WebSocket::DataType dataType, const char* message, uint16_t length) { String angle = String(message); int command = angle.toInt(); Serial.print("command: "); Serial.println(command); switch (command) { case CMD_STOP: Serial.println("Stop"); CAR_stop(); break; case CMD_FORWARD: Serial.println("Move Forward"); CAR_moveForward(); break; case CMD_BACKWARD: Serial.println("Move Backward"); CAR_moveBackward(); break; case CMD_LEFT: Serial.println("Turn Left"); CAR_turnLeft(); break; case CMD_RIGHT: Serial.println("Turn Right"); CAR_turnRight(); break; default: Serial.println("Unknown command"); } } void onWebSocketClose(net::WebSocket& ws, const net::WebSocket::CloseCode code, const char* reason, uint16_t length) { Serial.println("WebSocket client disconnected"); } void setup() { Serial.begin(9600); delay(1000); pinMode(ENA_PIN, OUTPUT); pinMode(IN1_PIN, OUTPUT); pinMode(IN2_PIN, OUTPUT); pinMode(IN3_PIN, OUTPUT); pinMode(IN4_PIN, OUTPUT); pinMode(ENB_PIN, OUTPUT); digitalWrite(ENA_PIN, HIGH); // set full speed digitalWrite(ENB_PIN, HIGH); // set full speed Serial.println("ESP32 Web Server and WebSocket Server"); // Configure web server routes server.addRoute("/", handleHome); // Start web server with WiFi connection server.begin(WIFI_SSID, WIFI_PASSWORD); // Enable WebSocket functionality webSocket = server.enableWebSocket(81); if (webSocket != nullptr) { // Set up WebSocket event handlers webSocket->onOpen(onWebSocketOpen); webSocket->onMessage(onWebSocketMessage); webSocket->onClose(onWebSocketClose); } else { Serial.println("Failed to start WebSocket server"); } } void loop() { // Then handle HTTP requests server.handleClient(); // Handle WebSocket server.handleWebSocket(); } void CAR_moveForward() { digitalWrite(IN1_PIN, HIGH); digitalWrite(IN2_PIN, LOW); digitalWrite(IN3_PIN, HIGH); digitalWrite(IN4_PIN, LOW); } void CAR_moveBackward() { digitalWrite(IN1_PIN, LOW); digitalWrite(IN2_PIN, HIGH); digitalWrite(IN3_PIN, LOW); digitalWrite(IN4_PIN, HIGH); } void CAR_turnLeft() { digitalWrite(IN1_PIN, HIGH); digitalWrite(IN2_PIN, LOW); digitalWrite(IN3_PIN, LOW); digitalWrite(IN4_PIN, LOW); } void CAR_turnRight() { digitalWrite(IN1_PIN, LOW); digitalWrite(IN2_PIN, LOW); digitalWrite(IN3_PIN, HIGH); digitalWrite(IN4_PIN, LOW); } void CAR_stop() { digitalWrite(IN1_PIN, LOW); digitalWrite(IN2_PIN, LOW); digitalWrite(IN3_PIN, LOW); digitalWrite(IN4_PIN, LOW); }
  • 코드 내의 WiFi 정보(SSID 및 비밀번호)를 자신의 네트워크 자격 증명과 일치하도록 수정하세요.
  • Arduino IDE에서 index.h 파일을 만드는 방법:
    • 직렬 모니터 아이콘 바로 아래 있는 버튼을 클릭하여 새 탭을 선택하거나, Ctrl+Shift+N 키를 사용하세요.
    아두이노 ide 2 adds file

    파일 이름을 index.h로 지정하고 OK 버튼을 클릭하세요.

    아두이노 ide 2 adds file index.h

    아래 코드를 복사하여 index.h에 붙여넣으세요.

    /* * 이 ESP32 코드는 newbiely.kr 에서 개발되었습니다 * 이 ESP32 코드는 어떠한 제한 없이 공개 사용을 위해 제공됩니다. * 상세한 지침 및 연결도에 대해서는 다음을 방문하세요: * https://newbiely.kr/tutorials/esp32/esp32-controls-car-via-web */ const char *HTML_CONTENT = R"=====( <!DOCTYPE html> <html> <head> <title>ESP32 Control Car via Web</title> <meta name="viewport" content="width=device-width, initial-scale=0.7, maximum-scale=1, user-scalable=no"> <style type="text/css"> body { text-align: center; font-size: 24px;} button { text-align: center; font-size: 24px;} #container { margin-right: auto; margin-left: auto; width: 400px; height: 400px; position: relative; margin-bottom: 10px; } div[class^='button'] { position: absolute; } .button_up, .button_down { width:214px; height:104px;} .button_left, .button_right { width:104px; height:214px;} .button_stop { width:178px; height:178px;} .button_up { background: url('https://esp32io.com/images/tutorial/up_inactive.png') no-repeat; background-size: contain; left: 200px; top: 0px; transform: translateX(-50%); } .button_down { background: url('https://esp32io.com/images/tutorial/down_inactive.png') no-repeat; background-size: contain; left:200px; bottom: 0px; transform: translateX(-50%); } .button_right { background: url('https://esp32io.com/images/tutorial/right_inactive.png') no-repeat; background-size: contain; right: 0px; top: 200px; transform: translateY(-50%); } .button_left { background: url('https://esp32io.com/images/tutorial/left_inactive.png') no-repeat; background-size: contain; left:0px; top: 200px; transform: translateY(-50%); } .button_stop { background: url('https://esp32io.com/images/tutorial/stop_inactive.png') no-repeat; background-size: contain; left:200px; top: 200px; transform: translate(-50%, -50%); } </style> <script> var CMD_STOP = 0; var CMD_FORWARD = 1; var CMD_BACKWARD = 2; var CMD_LEFT = 4; var CMD_RIGHT = 8; var img_name_lookup = { [CMD_STOP]: "stop", [CMD_FORWARD]: "up", [CMD_BACKWARD]: "down", [CMD_LEFT]: "left", [CMD_RIGHT]: "right" } var ws = null; function init() { var container = document.querySelector("#container"); container.addEventListener("touchstart", mouse_down); container.addEventListener("touchend", mouse_up); container.addEventListener("touchcancel", mouse_up); container.addEventListener("mousedown", mouse_down); container.addEventListener("mouseup", mouse_up); container.addEventListener("mouseout", mouse_up); } function ws_onmessage(e_msg) { e_msg = e_msg || window.event; // MessageEvent //alert("msg : " + e_msg.data); } function ws_onopen() { document.getElementById("ws_state").innerHTML = "OPEN"; document.getElementById("wc_conn").innerHTML = "Disconnect"; } function ws_onclose() { document.getElementById("ws_state").innerHTML = "CLOSED"; document.getElementById("wc_conn").innerHTML = "Connect"; console.log("socket was closed"); ws.onopen = null; ws.onclose = null; ws.onmessage = null; ws = null; } function wc_onclick() { if(ws == null) { ws = new WebSocket("ws://" + window.location.host + ":81"); document.getElementById("ws_state").innerHTML = "CONNECTING"; ws.onopen = ws_onopen; ws.onclose = ws_onclose; ws.onmessage = ws_onmessage; } else ws.close(); } function mouse_down(event) { if (event.target !== event.currentTarget) { var id = event.target.id; send_command(id); event.target.style.backgroundImage = "url('https://esp32io.com/images/tutorial/" + img_name_lookup[id] + "_active.png')"; } event.stopPropagation(); event.preventDefault(); } function mouse_up(event) { if (event.target !== event.currentTarget) { var id = event.target.id; send_command(CMD_STOP); event.target.style.backgroundImage = "url('https://esp32io.com/images/tutorial/" + img_name_lookup[id] + "_inactive.png')"; } event.stopPropagation(); event.preventDefault(); } function send_command(cmd) { if(ws != null) if(ws.readyState == 1) ws.send(cmd + "\r\n"); } window.onload = init; </script> </head> <body> <h2>ESP32 - RC Car via Web</h2> <div id="container"> <div id="0" class="button_stop"></div> <div id="1" class="button_up"></div> <div id="2" class="button_down"></div> <div id="8" class="button_right"></div> <div id="4" class="button_left"></div> </div> <p> WebSocket : <span id="ws_state" style="color:blue">closed</span><br> </p> <button id="wc_conn" type="button" onclick="wc_onclick();">Connect</button> <br> <br> <div class="sponsor">Sponsored by <a href="https://amazon.com/diyables">DIYables</a></div> </body> </html> )=====";
    • 이제 코드를 두 개의 파일에 가지고 있습니다: newbiely.kr.inoindex.h
    • Arduino IDE에서 Upload 버튼을 클릭하여 ESP32에 코드를 업로드하세요.
    • 시리얼 모니터를 엽니다.
    • 시리얼 모니터에서 결과를 확인하세요.
    COM6
    Send
    Connecting to WiFi... Connected to WiFi ESP32 Web Server's IP address IP address: 192.168.0.2
    Autoscroll Show timestamp
    Clear output
    9600 baud  
    Newline  
    • 표시된 IP 주소를 기록하고, 이 주소를 스마트폰이나 PC의 웹 브라우저 주소창에 입력하세요.
    • 아래와 같은 웹페이지를 보게 될 것입니다:
    ESP32 controls car via web browser

    웹페이지의 JavaScript 코드가 자동으로 ESP32와의 WebSocket 연결을 생성합니다.

    이제 웹 인터페이스를 통해 차를 좌/우로 회전시키거나 앞/뒤로 이동시킬 수 있습니다.

    ESP32의 메모리를 절약하기 위해, 컨트롤 버튼의 이미지는 ESP32에 저장되지 않습니다. 대신, 인터넷에 저장되어 있으므로, 웹 제어 페이지의 이미지를 불러오기 위해 휴대폰이나 PC가 인터넷 연결이 필요합니다.

    ※ 주의:

    • index.h의 HTML 내용을 수정하고 newbiely.kr.ino 파일은 건드리지 않으면, ESP32에 코드를 컴파일하고 업로드할 때 Arduino IDE가 HTML 내용을 업데이트하지 않습니다.
    • 이 경우 Arduino IDE가 HTML 내용을 업데이트하게 하려면, newbiely.kr.ino 파일에 변경 사항을 만들어야 합니다(예: 빈 줄 추가, 주석 추가....).

    줄별 코드 설명

    위의 ESP32 코드에는 줄마다 설명이 포함되어 있습니다. 코드의 주석을 읽어주세요!