아두이노 우노 R4 - 웹을 통해 자동차 제어
이번 튜토리얼에서는 Arduino Uno R4 WiFi를 사용하여 웹을 통해 2WD RC 로봇 자동차를 제어하는 방법을 안내합니다. 이 가이드에서는 Arduino Uno R4의 강력한 내장 WiFi 기능을 활용하여 원격으로 로봇 자동차를 설정하고 제어하는 단계를 안내합니다. 이 튜토리얼을 마치면 다음을 배우게 됩니다:
RC 카를 제어하기 위해 왜 WebSocket이 필요할까
2WD 로봇 카를 Arduino Uno R4 WiFi에 연결하는 방법.
웹 인터페이스를 제공하여 로봇 카를 좌/우로 돌리고, 앞/뒤로 이동할 수 있도록 Arduino Uno R4 WiFi를 프로그래밍하는 방법.
이 튜토리얼은 로봇 공학 및 웹 기반 제어 시스템에 관심이 있는 취미자, 학생 및 모든 사람에게 완벽합니다. 아두이노에 초보자이든 경험자이든 관계없이, 우리의 단계별 지침은 2WD RC 로봇 자동차를 원격으로 제어하려는 목표를 달성하는 데 도움을 줄 것입니다.
로봇 공학과 웹 기술을 결합하는 이 흥미로운 여정을 시작해 봅시다!
1 | × | Arduino UNO R4 WiFi | Amazon | |
1 | × | USB Cable Type-C | 쿠팡 | Amazon | |
1 | × | 2WD RC Car | 쿠팡 | Amazon | |
1 | × | L298N Motor Driver Module | 쿠팡 | Amazon | |
1 | × | IR Remote Controller Kit | Amazon | |
1 | × | CR2025 Battery (for IR Remote controller) | Amazon | |
1 | × | 1.5V AA Battery (for Arduino UNO R4 and Car) | Amazon | |
1 | × | Jumper Wires | Amazon | |
1 | × | Breadboard | 쿠팡 | Amazon | |
1 | × | (Recommended) Screw Terminal Block Shield for Arduino UNO R4 | 쿠팡 | Amazon | |
1 | × | (Recommended) Breadboard Shield For Arduino UNO R4 | 쿠팡 | Amazon | |
1 | × | (Recommended) Enclosure For Arduino UNO R4 | Amazon | |
1 | × | (Recommended) Power Splitter For Arduino UNO R4 | Amazon | |
공개: 이 섹션에서 제공된 링크 중 일부는 제휴 링크입니다. 이 링크를 통해 구매한 경우 추가 비용없이 수수료를 받을 수 있습니다. 지원해 주셔서 감사합니다.
웹소켓이 없으면 자동차의 방향을 변경할 때마다 페이지를 새로 고쳐야 합니다. 이 방법은 그다지 효율적이지 않습니다.
웹소켓을 사용하면 웹페이지와 Arduino UNO R4 사이에 전용 연결이 생성됩니다. 이 설정을 통해 페이지를 새로 고칠 필요 없이 Arduino UNO R4에 명령을 보낼 수 있습니다. 그 결과 로봇 자동차는 빠르고 매끄럽게 반응합니다. 놀랍지 않나요?
요약하면, WebSocket은 로봇을 부드럽고 실시간으로 제어할 수 있게 해줍니다.
Arduino UNO R4에서 WebSocket 사용에 대한 자세한 가이드를 제공합니다. 제공된 링크를 방문하여 자세히 알아보세요: Arduino UNO R4 - WebSocket 튜토리얼
Arduino UNO R4 코드는 웹 서버와 WebSocket 서버를 생성합니다. 작동 방식은 다음과 같습니다:
Arduino UNO R4의 IP 주소를 웹 브라우저에 입력하면, Arduino UNO R4로부터 사용자 인터페이스 웹페이지를 요청합니다. Arduino UNO R4의 웹 서버는 HTML, CSS, JavaScript로 구성된 웹페이지의 콘텐츠를 보내 응답합니다. 그런 다음 웹 브라우저가 이 웹페이지를 표시합니다. 웹페이지의 JavaScript 코드는 Arduino UNO R4의 WebSocket 서버와 WebSocket 연결을 시작합니다. 이 WebSocket 연결이 작동하면, 웹페이지에서 버튼을 누르거나 놓는 것이 해당 연결을 통해 Arduino UNO R4로 명령을 전송합니다. Arduino UNO R4의 WebSocket 서버는 이러한 명령을 받아 로봇 카를 지시대로 제어합니다.
다음은 사용자가 수행하는 작업에 따라 웹페이지에서 Arduino UNO R4로 전송된 명령어를 표시하는 표입니다:
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 |
이 이미지는 Fritzing을 사용하여 만들어졌습니다. 이미지를 확대하려면 클릭하세요.
일반적으로 두 가지 다른 전력원을 필요로 합니다:
이 설정을 더 간단하게 만들려면 한 개의 전원, 즉 네 개의 1.5V 배터리를 사용하여 총 6V를 만드세요. 방법은 다음과 같습니다:
설명서에 표시된 대로 L298N 모듈에 배터리를 연결합니다.
ENA와 ENB 핀에서 두 개의 점퍼를 제거합니다.
5VEN으로 표시된 점퍼를 장착합니다 (도면에서 노란색 원으로 표시됨).
L298N 모듈의 12V 핀을 Arduino UNO R4의 Vin 핀에 연결합니다. 이렇게 하면 배터리를 사용하여 Arduino UNO R4에 전원을 공급할 수 있습니다.
2WD RC 자동차에는 전원 스위치가 있습니다. 이를 사용하여 배터리를 연결하거나 연결 해제할 수 있어 필요할 때 자동차의 전원을 켜거나 끌 수 있습니다. 더 간단한 것을 원한다면 스위치는 사용하지 않을 수 있습니다.
웹 페이지의 내용(HTML, CSS, JavaScript)은 index.h라는 별도의 파일에 저장됩니다. 따라서 우리는 Arduino IDE에서 두 개의 코드 파일을 사용할 것입니다.
다음 지침을 단계별로 따르세요:
USB 케이블을 사용하여 Arduino Uno R4 보드를 컴퓨터에 연결합니다.
컴퓨터에서 Arduino IDE를 실행합니다.
적절한 Arduino Uno R4 보드(예: Arduino Uno R4 WiFi) 및 COM 포트를 선택합니다.
Arduino IDE의 왼쪽에 있는 Library Manager 아이콘을 클릭하여 라이브러리 관리자를 엽니다.
라이브러리 관리자에서 “mWebSockets”를 검색하고 Dawid Kurek의 버전을 찾습니다.
MWebSockets 라이브러리를 설치하려면 Install 버튼을 클릭합니다.
#include <WiFiS3.h>
#include <WebSocketServer.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 7
#define IN1_PIN 6
#define IN2_PIN 5
#define IN3_PIN 4
#define IN4_PIN 3
#define ENB_PIN 2
const char *ssid = "YOUR_WIFI_SSID";
const char *password = "YOUR_WIFI_PASSWORD";
using namespace net;
WebSocketServer webSocket(81);
WiFiServer server(80);
int status = WL_IDLE_STATUS;
void setup() {
Serial.begin(9600);
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);
digitalWrite(ENB_PIN, HIGH);
Serial.begin(9600);
String fv = WiFi.firmwareVersion();
if (fv < WIFI_FIRMWARE_LATEST_VERSION)
Serial.println("Please upgrade the firmware");
while (status != WL_CONNECTED) {
Serial.print("Attempting to connect to SSID: ");
Serial.println(password);
status = WiFi.begin(ssid, password);
delay(4000);
}
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
server.begin();
webSocket.onConnection([](WebSocket &ws) {
const auto protocol = ws.getProtocol();
if (protocol) {
Serial.print(F("Client protocol: "));
Serial.println(protocol);
}
ws.onMessage([](WebSocket &ws, const WebSocket::DataType dataType,
const char *message, uint16_t length) {
String cmd_str = String((char *)message);
int command = cmd_str.toInt();
Serial.print("command: ");
Serial.println(command);
switch (dataType) {
case WebSocket::DataType::TEXT:
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");
}
break;
case WebSocket::DataType::BINARY:
Serial.println(F("Received binary data"));
break;
}
});
ws.onClose([](WebSocket &, const WebSocket::CloseCode, const char *,
uint16_t) {
Serial.println(F("Disconnected"));
});
Serial.print(F("New WebSocket Connnection from client: "));
Serial.println(ws.getRemoteIP());
});
webSocket.begin();
}
void loop() {
webSocket.listen();
WiFiClient client = server.available();
if (client) {
while (client.connected()) {
if (client.available()) {
String HTTP_header = client.readStringUntil('\n');
if (HTTP_header.equals("\r"))
break;
Serial.print("<< ");
Serial.println(HTTP_header);
}
}
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close");
client.println();
String html = String(HTML_CONTENT);
client.println(html);
client.flush();
delay(100);
client.stop();
}
}
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 라는 이름의 파일을 생성하려면 다음 중 하나를 수행하십시오:
const char *HTML_CONTENT = R"=====(
<!DOCTYPE html>
<html>
<head>
<title>Arduino Uno R4 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;
}
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:
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>Arduino Uno R4 - 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>
)=====";
아래와 같은 오류가 표시됩니다:
In file included from c:\Users\YOU_ACCOUNT\Documents\Arduino\libraries\mWebSockets\src/utility.h:3:0,
from c:\Users\YOU_ACCOUNT\Documents\Arduino\libraries\mWebSockets\src/WebSocket.h:5,
from c:\Users\YOU_ACCOUNT\Documents\Arduino\libraries\mWebSockets\src/WebSocketServer.h:5,
from C:\Users\YOU_ACCOUNT\Documents\Arduino\newbiely.com\newbiely.com.ino:2:
C:\Users\YOU_ACCOUNT\Documents\Arduino\libraries\mWebSockets\src/platform.h:54:12: fatal error: Ethernet.h: No such file or directory
# include <Ethernet.h>
^~~~~~~~~~~~
compilation terminated.
exit status 1
이 오류를 수정하려면:
폴더로 이동: C:\Users\YOUR_ACCOUNT\Documents\Arduino\libraries\mWebSockets\src/
Config.h라는 이름의 파일을 찾아 텍스트 편집기로 엽니다.
26번째 줄로 이동하면 다음과 같이 표시됩니다:
#define NETWORK_CONTROLLER ETHERNET_CONTROLLER_W5X00
#define NETWORK_CONTROLLER NETWORK_CONTROLLER_WIFI
Connecting to WiFi...
Connected to WiFi
Arduino UNO R4 Web Server's IP address IP address: 192.168.0.2
웹페이지를 Arduino UNO R4와 WebSocket으로 연결하려면 CONNECT 버튼을 누르세요.
이제 웹 인터페이스를 사용하여 차를 왼쪽 또는 오른쪽으로 회전시키고, 앞으로 또는 뒤로 이동할 수 있습니다.
제어 버튼 이미지는 Arduino UNO R4에 저장되어 있지 않아서 메모리를 절약합니다. 대신, 이미지들은 온라인에 저장됩니다. 따라서, 웹 제어 페이지에서 이 이미지를 로드하려면 휴대폰이나 컴퓨터가 인터넷에 연결되어 있어야 합니다.
※ NOTE THAT:
파일 이름이 index.h인 파일의 HTML을 변경했지만 newbiely.com.ino이라는 이름의 파일에서는 아무것도 변경하지 않으면, 이 코드를 Arduino UNO R4에 컴파일하고 업로드할 때 Arduino IDE는 HTML 콘텐츠를 업데이트하지 않습니다. Arduino IDE가 HTML 콘텐츠를 업데이트하도록 하려면 newbiely.com.ino 파일에 빈 줄이나 주석을 추가하는 것과 같은 작은 변경을 해야 합니다.
제공된 Arduino UNO R4 코드는 각 줄에 대한 설명을 포함하고 있습니다. 코드 내의 주석을 꼭 읽어보세요!
위의 코드가 작동하면 Arduino UNO R4의 WiFi 모듈 최신 버전을 업데이트해 주세요.