본 문서는 아두이노 개발환경(IDE)를 이용해서 ESP8266 모듈을 다루는 예제들을 포함하고 있습니다.

ESP8266 Arduino IDE 는 ESP8266 모듈의 펌웨어를 제작하는 가장 간단한 방법입니다. ESP8266 Arduino IDE 개발환경을 설치하는 방법은 아래 문서를 참고하세요.

ESP8266을 위한 다양한 예제가 이미 갖추어져 있습니다. [Arduino IDE – File – Examples – ESP8266xxx] 예제들을 보면 다양한 기능들을 구현하는 방법이 나옵니다. 여기서 소개하는 예제는 NodeMCU V1 (ESP12E 기반) 보드에서 동작 테스트를 한 예제들입니다. 그리고 소개를 위해 예제 코드에 약간의 변형이 가해졌습니다.

NodeMCU 보드는 아래와 같이 핀이 배치되어 있으며, 펌웨어 업로드를 위해 필요한 FTDI 모듈을 내장하고 있어서 PC에 USB 연결만 하면 사용할 수 있습니다.

Node-MCU-Pin-Out-Diagram1

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 을 추천합니다. 개발환경 구축과정이 조금 복잡해도 아두이노 개발환경보다 더 안정적이고 유용합니다.
  • Sming framework 구축방법, Sming 을 이용한 예제들