ESP8266 – ARDUINO IDE 예제 (WiFi 통신)
본 문서는 아두이노 개발환경(IDE)를 이용해서 ESP8266 모듈을 다루는 예제들을 포함하고 있습니다.
- ESP8266 – ARDUINO IDE 개발환경 설치
- ESP8266 – ARDUINO IDE 예제 (GPIO 제어)
- ESP8266 – ARDUINO IDE 예제 (WiFi 통신)
- ESP8266 – ARDUINO IDE 예제 (MQTT)
.
ESP8266을 위한 다양한 예제가 이미 갖추어져 있습니다. [Arduino IDE – File – Examples – ESP8266xxx] 예제들을 보면 다양한 기능들을 구현하는 방법이 나옵니다. 여기서 소개하는 예제는 NodeMCU V1 (ESP12E 기반) 보드에서 동작 테스트를 한 예제들입니다. 그리고 소개를 위해 예제 코드에 약간의 변형이 가해졌습니다.
NodeMCU 보드는 아래와 같이 핀이 배치되어 있으며, 펌웨어 업로드를 위해 필요한 FTDI 모듈을 내장하고 있어서 PC에 USB 연결만 하면 사용할 수 있습니다.
.
WiFi 통신 기본
WiFi 통신을 위한 기초 예제입니다. 아래에서 소스코드를 구하실 수 있습니다.
이 소스코드는 단순히 ESP8266 모듈을 공유기에 연결만 시킵니다. 그리고 그 과정에서 발생되는 WiFi 이벤트를 받아서 처리하는 방법을 설명합니다.
테스트를 위해서는 파일 상단에 공유기(AP)의 SSID, 비밀번호를 지정해줘야 합니다.
const char *ssid = "your_ssid"; const char *password = "your_pass";
소스코드의 setup() 초기화 함수를 보겠습니다.
void setup() { Serial.begin(115200); // delete old config WiFi.disconnect(true); delay(1000); WiFi.onEvent(WiFiEvent); WiFi.begin(ssid, password); Serial.println("Wait for WiFi... "); }
WiFi 가 ESP8266 core에서 제공하는 통신 기능들에 접근할 수 있는 인스턴스입니다. WiFi.onEvent() 함수를 이용해 이벤트가 발생했을 때 실행될 콜백함수를 지정할 수 있습니다.
WiFi.onEvent(WiFiEvent);
위 처럼 지정되어 있으므로 이벤트가 발생하면 WiFiEvent() 함수가 호출됩니다. 이벤트가 발생했을 때 하고 싶은 작업을 WiFiEvent() 함수에 구현하면 됩니다.
사용가능한 함수는 ESP8266WiFiType.h 에서 확인하실 수 있습니다.
typedef enum { WIFI_EVENT_STAMODE_CONNECTED = 0, WIFI_EVENT_STAMODE_DISCONNECTED, WIFI_EVENT_STAMODE_AUTHMODE_CHANGE, WIFI_EVENT_STAMODE_GOT_IP, WIFI_EVENT_STAMODE_DHCP_TIMEOUT, WIFI_EVENT_SOFTAPMODE_STACONNECTED, WIFI_EVENT_SOFTAPMODE_STADISCONNECTED, WIFI_EVENT_SOFTAPMODE_PROBEREQRECVED, WIFI_EVENT_MAX } WiFiEvent_t;
보시다시피 AP에 연결된 후 IP를 받아오는 과정까지 이벤트를 발생합니다.
SSID, 패스워드를 이용해 AP에 연결하는 함수는 setup() 안에 있는 WiFi.begin() 입니다.
- WiFi.begin(ssid, password);
.
WiFi Client (HTTP Request)
이번 예제는 ESP8266 모듈을 공유기에 연결하고, 외부의 HTTP 서버에 요청을 보내서 결과(HTML 또는 파일)를 받아오는 예제입니다.
이 문서의 예제들은 모두 아래 SSID, 패스워드를 자신의 환경에 맞게 지정해줘야 동작합니다.
const char *ssid = "your_ssid"; const char *password = "your_pass";
이번 예제에서 HTTP 통신을 통해 받아올 서버도 미리 지정해주세요.
const char* host = "hardcopyworld.com";
먼저 setup() 함수의 내용부터 보겠습니다. 주요 내용만 뽑아내면 아래와 같습니다.
void setup() { ...... WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } ...... Serial.println("IP address: "); Serial.println(WiFi.localIP()); }
WiFi.begin(ssid, password) 함수를 이용해서 공유기에 연결합니다. 이후 이어지는 while() 반복문은 공유기에 접속할 때까지 기다리기 위해 사용했습니다.
공유기에 접속되면 아래 코드처럼 Serial 통신으로 할당받은 IP를 출력해 주는 것이 편리합니다.
- Serial.println(WiFi.localIP());
loop() 반복함수에서 주기적으로(약 5초 간격) HTTP 요청을 서버로 보냅니다. 그리고 받은 결과를 Serial 통신으로 출력해줍니다. 주요 코드만 발췌하면 아래와 같습니다.
void loop() { delay(5000); ...... // Use WiFiClient class to create TCP connections WiFiClient client; const int httpPort = 80; if (!client.connect(host, httpPort)) { Serial.println("connection failed"); return; } // We now create a URI for the request String url = "/"; // This will send the request to the server client.print(String("GET ") + url + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n"); int timeout = millis() + 5000; while (client.available() == 0) { if (timeout - millis() < 0) { Serial.println(">>> Client Timeout !"); client.stop(); return; } } // Read all the lines of the reply from server and print them to Serial while(client.available()){ String line = client.readStringUntil('\r'); Serial.print(line); } }
가장 먼저 나오는 WiFiClient 클래스는 TCP 연결을 만들기 위해 사용합니다. host와 port 를 지정하면 해당 서버에 TCP 연결을 만듭니다. 이때 사용되는 host 변수에는 도메인 이름 [hardcopyworld.com] 이 사용되어야 합니다.
WiFiClient client; const int httpPort = 80; if (!client.connect(host, httpPort)) { Serial.println("connection failed"); return; }
TCP 연결이 성공하면 HTTP Request 형식에 맞게 내용을 작성해서 전송하면 됩니다.
client.print(String("GET ") + url + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n");
여기서 url 변수는 host 도메인 내부에서 내가 원하는 리소스까지의 경로입니다. 만약 [http://www.google.com/my/resource.html] 에 요청을 보내고 싶다면 host는 [http://www.google.com] 이 됩니다. url 은 [/my/resource.html] 로 지정해줘야 합니다.
만약 http://google.com 메인 페이지의 내용을 얻고 싶다면 host는 [http://google.com], url은 [/] 로 지정해주면 됩니다.
client.print() 함수를 이용해서 요청이 전송되면 일정 시간 후 응답이 옵니다.
while (client.available() == 0) { if (timeout - millis() < 0) { Serial.println(">>> Client Timeout !"); client.stop(); return; } }
client 의 상태를 체크해서 응답이 왔는지 확인한 코드입니다.
HTTP Response가 도착하면 아래 코드로 한 라인씩 읽어 Serial 통신으로 출력할 수 있습니다. 특별한 기능을 만들고 싶을 때 이 부분을 적절히 수정하면 됩니다.
while(client.available()){ String line = client.readStringUntil('\r'); Serial.print(line); }
.
Web server
이번에는 ESP8266 모듈을 웹 서버로 만드는 방법입니다. 이 방법을 사용하면 외부 장치에서 브라우저로 ESP8266 모듈에 접속해서 HTML 페이지를 볼 수 있습니다. 소스코드는 아래에서…
테스트 전 GPIO13 (D7) 핀에 LED를 하나 연결해두세요. 외부에서 접속 요청이 오면 LED가 켜지도록 만들겁니다.
파일 상단에서 공유기 설정을 해줘야 합니다.
const char *ssid = "your_ssid"; const char *password = "your_pass"; ESP8266WebServer server ( 80 ); const int led = 13;
ESP8266WebServer 클래스에 웹 서버로 동작하기 위한 기능들이 들어 있습니다. 80번 포트로 HTTP 통신을 하도록 지정되어 있습니다. 일반적인 웹 서버의 통신 포트로 브라우저에서 특별히 지정하지 않으면 80번 포트를 사용합니다.
setup 함수의 주요 코드부터 보겠습니다.
void setup ( void ) { ...... WiFi.begin ( ssid, password ); ...... server.on ( "/", handleRoot ); server.on ( "/test.svg", drawGraph ); server.on ( "/inline", []() { server.send ( 200, "text/plain", "this works as well" ); } ); server.onNotFound ( handleNotFound ); ...... server.begin(); ...... }
WiFi.begin() 함수로 공유기에 접속합니다. 이후 공유기 접속을 기다리고 할당받은 IP 주소를 Serial 통신으로 출력해줍니다.
sever.on() 함수부터 본격적인 서버설정 코드입니다. 코드에서는 3개의 URL에 대한 콜백함수를 지정해 줬습니다.
- http://192.168.x.x/ : 서버의 루트[/] 경로에 대한 요청은 handleRoot() 콜백함수가 처리
- http://192.168.x.x/inline : [/inline] 경로에 대한 요청은 인라인 함수가 처리 – [this works as well] 메시지를 전달
- http://192.168.x.x/test.svg : [/test.svg] 경로에 대한 접근은 drawGraph 콜백함수가 처리
만약 위 경로에 해당되지 않는 요청이 올 경우는 handleNotFound() 콜백함수가 처리하도록 지정되어 있습니다.
이상 서버 설정이 끝나면 아래 코드로 서버를 시작합니다.
- server.begin();
loop() 함수에서는 외부에서 접속 요청이 오는지 모니터링 하는 코드가 들어 있을 뿐입니다.
void loop ( void ) { server.handleClient(); }
요청이 들어오면 해당되는 콜백함수가 실행됩니다. handleRoot() 함수를 보시면 HTML 문자열을 client에게 전송하는 방법을 알 수 있습니다.
조금 특이한게 drawGraph() 콜백함수인데, [/test.svg] 파일에 대한 요청이 오면 처리해주는 함수입니다. svg 는 벡터 이미지를 생성하기 위한 XML 파일입니다. svg 파일은 이미지 파일이 아니라 벡터 이미지를 그리기 위한 정보를 XML 텍스트로 담고 있습니다. 이 파일을 브라우저가 받으면 해당 정보대로 이미지를 그려 보여주기 때문에 마치 이미지 파일처럼 쓸 수 있습니다.
그래서 drawGraph() 콜백함수는 일련의 XML 데이터를 문자열로 만들어 전송해줍니다. 브라우저에서 [http://x.x.x.x/test.svg] 경로에 접속해보면 이미지가 보일껍니다.
앞서 handleRoot() 콜백함수가 생성하는 HTML 코드에도 [/test.svg] 이미지가 포함되어 있습니다. 따라서 브라우저는 [/test.svg] 에 대한 요청도 추가로 해서 벡터 이미지가 포함된 화면을 그려줍니다.
.
Web Socket (server)
앞선 예제처럼 웹 서버는 외부에서의 요청이 있을때마다 HTML 페이지를 응답으로 보내줍니다. 그럼 브라우저는 HTML 페이지를 화면에 그려줍니다. 이 방식은 전통적인 웹의 동작 방식이지만 화면 혹은 서버에 데이터 변화가 있는지 확인하기 위해 주기적으로 웹 서버에 (같은 URL 주소에) 요청을 다시 보내고 화면을 다시 그려야 합니다. 그보다는 백그라운드로 서버와 브라우저가 연결되어 양방향으로 통신을 할 수 있다면 불필요한 HTTP 요청을 없애고 화면 일부만 업데이트가 가능할 것입니다.
Web Socket 이 이런 자유로운 통신 방식을 제공해줍니다. HTML5에 포함된 Web Socket은 웹 서버와 클라이언트(브라우저의 JavaScript) 간 TCP/IP 연결을 유지하게 해 줌으로써 양방향 데이터 전송도 지원합니다. 브라우저가 업데이트를 확인하기위해 계속 서버를 체크하지 않고도 서버에서 오는 알림에 반응할 수 있는 push가 가능합니다.
다만, 이 기능을 사용하기 위해서는 비교적 최신의 브라우저를 사용해야 하는 제약이 있습니다. 그리고 당연히 서버에는 WebSocket 통신을 위한 준비가 되어 있어야 합니다. 이번 예제는 ESP8266 모듈에 웹 서버 + 웹 소켓 기능을 심을겁니다.
웹 소켓 사용을 위해서는 라이브러리를 추가로 설치해줘야 합니다.
그리고 불행히도, 현재(2016, 01 기준) 아두이노 IDE – 패키지 매니저로 배포되는 ESP8266 core 에는 라이브러리 지원에 필요한 코드가 일부 빠져있어서 에러가 발생합니다. 따라서 이번 예제를 테스트하기 위해서는 최신 ESP8266 core for Arduino 코드를 받아 수동으로 업데이트 해줘야 합니다.
웹 소켓 테스트 예제는 아래 링크에 있습니다.
ino 스케치 파일의 상단에 보면 웹 서버, 웹 소켓을 위한 인스턴스가 선언되어 있습니다. 코드를 보면 아실 수 있듯 웹 서버와 웹소켓은 각각 80번, 81번 포트를 사용합니다.
ESP8266WebServer server = ESP8266WebServer(80); WebSocketsServer webSocket = WebSocketsServer(81);
setup() 함수부터 보겠습니다.
void setup() { ...... WiFiMulti.addAP("your_ssid", "your_pass"); ...... webSocket.begin(); webSocket.onEvent(webSocketEvent); ...... // handle index server.on("/", []() { // send index.html server.send(200, "text/html", html_string); }); server.begin(); ...... }
앞선 예제와는 조금 다르게 WiFiMulti.addAP() 함수로 공유기엔 연결 했습니다. 별 차이 없습니다만, 여러개의 AP를 등록해서 자동 연결되도록 관리해주는 방법입니다.
이어서 웹 소켓을 초기화하고 웹 소켓 관련 이벤트(연결, 데이터수신, 종료)가 발생하면 처래해 줄 콜백 함수 webSocketEvent()를 등록 했습니다.
웹 서버는 [http://192.168.x.x] 처럼 루트[/] 경로에 대한 요청이 올 때 html_string 텍스트를 전달하도록 해뒀습니다. html_string은 html.h 파일에 들어있는 HTML 코드입니다.
HTML 코드에는 같은 서버의 81번 포트에 웹 소켓으로 연결하도록 자바스크립트 코드가 들어가 있습니다. 그리고 웹 소켓으로 RGB LED 밝기를 조절하기 위한 코드가 들어가 있습니다.
즉, 브라우저로 ESP8266 모듈에 [http://192.168.x.x] 경로로 접속하면 HTML 코드를 다운로드 받아 브라우저가 화면을 그려줍니다. 그리고 백그라운드로 웹 소켓을 ESP8266 모듈과 연결해둡니다. 브라우저 화면에는 3개의 슬라이드 바가 있는데, 바를 움직이면 웹 소켓으로 ESP8266 모듈에 데이터를 전송합니다. 그럼 ESP8266 모듈이 데이터를 받아 RGB LED 색상을 조절합니다.
ESP8266 모듈이 데이터를 받으면 앞서 등록한 webSocketEvent() 콜백 함수가 호출됩니다.
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght) { switch(type) { case WStype_DISCONNECTED: break; case WStype_CONNECTED: { ...... webSocket.sendTXT(num, "Connected"); } break; case WStype_TEXT: USE_SERIAL.printf("[%u] get Text: %s\n", num, payload); if(payload[0] == '#') { // decode rgb data uint32_t rgb = (uint32_t) strtol((const char *) &payload[1], NULL, 16); analogWrite(LED_RED, ((rgb >> 16) & 0xFF)); analogWrite(LED_GREEN, ((rgb >> 8) & 0xFF)); analogWrite(LED_BLUE, ((rgb >> 0) & 0xFF)); } break; } }
웹 소켓 연결 요청이 와서 연결되면 case WStype_CONNECTED: 부분이 실행됩니다. 여기 코드를 보시면 웹 소켓으로 데이터를 보내는 방법을 알 수 있습니다. 아래 함수를 이용해서 상대방(브라우저)에게 데이터를 보냅니다.
- webSocket.sendTXT(num, “Connected“);
위 함수는 특정 상대방에게 데이터를 보내는 방법입니다. 연결된 장치들 모두에게 데이터를 보낼 때는 아래 함수를 사용하면 됩니다.
- webSocket.broadcastTXT(“Message from ESP8266!!“);
case WStype_CONNECTED: 여기에 적힌 코드들은 웹 소켓으로 데이터를 받았을 때를 위한 것입니다. 데이터를 분석해서 RGB 색상값을 얻은 뒤, RGB LED 색을 조절하겠죠.
이 외에도 2대의 ESP8266 모듈을 이용해 재밌는 실험을 해볼 수 있습니다. 하나의 ESP8266 모듈은 웹 소켓 서버로 만들고, 다른 한 대는 웹 소켓 클라이언트로 만들어서 두 대의 ESP8266 모듈이 웹 소켓 통신을 하도록 하는겁니다.
이 방법은 아래의 두 예제 파일을 참고해서 직접 만들어 보시길 바랍니다.
HTTP 서버, 클라이언트 기능을 모두 활용하는 아래 예제도 참고하세요.
- https://github.com/amiravni/ESP8266-Water-Heater-Control/blob/master/TheDude.ino
PS.
- ESP8266 Arduino IDE 안에는 WiFi 통신 관련된 다양한 예제가 마련되어 있습니다. [File – Example] 메뉴에서 예제를 불러와 테스트 해볼 수 있습니다. 하지만 최신 ESP8266 core 로 업데이트 한 이후, 많은 예제들이 오동작 하는 문제가 있었습니다.
- 예제들의 동작이 확인 되면 문서에 추가하겠습니다.
- 보다 파워풀한 WiFi 통신 기능을 테스트하고 싶다면 Sming framework 을 추천합니다. 개발환경 구축과정이 조금 복잡해도 아두이노 개발환경보다 더 안정적이고 유용합니다.