라즈베리파이반

라즈베리파이 등 컴퓨터계열 게시판입니다.

제목ESP 모듈 와이파이 연결 : HTTP 통신2021-10-14 21:07
작성자user icon Level 4
첨부파일첨부파일.zip (37.92MB)

88x31.png


이번에는 웹에서 아두이노를 제어하기위해 HTTP 통신으로 ESP 모듈과 통신해보겠습니다.


HTTP는 HyperText Transfer Protocol의 약자로 웹브라우저상에서 HTML 문서를 전송하는 프로토콜을 말합니다. 클라이언트에서 서버로 요청(Request)을 보내면 서버로부터 응답(Response)를 받는 단방향 통신입니다.

mb-file.php?path=2021%2F10%2F14%2FF3951_1.png
 


1. WiFiEsp 라이브러리 이용


WiFiEsp 라이브러리는 소프트웨어 시리얼 통신으로 웹서버를 작성할 수 있도록 하는 라이브러리입니다.

ESP-01의 경우 사용할 수 있는 GPIO가 0과 2 2개뿐이므로 이 라이브러리를 통해 GPIO의 확장이 가능합니다.


mb-file.php?path=2021%2F10%2F14%2FF3962_2.png
 

우선 펌웨어를 업데이트 해야합니다. 저번에 올린 AT 펌웨어는 SDK 버전이 낮아서 WiFiEsp 라이브러리의 이용이 불가합니다.


배선은 다음과 같이 합니다.

아두이노 UNO

ESP-01 

3.3V

VCC / CH_PD

GND

GND / GPIO0

GND - RESET

 

RX

RX 

TX

TX 



mb-file.php?path=2021%2F10%2F14%2FF3973_3.ps.png
ESP 모듈에 RX는 3.3V를 받아야하므로 1k저항을 1:2로 연결해줍니다.


펌웨어: esp_iot_sdk_v1.5.0_15_11_27.zip 

플래쉬툴: FLASH_DOWNLOAD_TOOLS_v2.4_150924.rar 


두 파일을 다운받습니다.

플래쉬툴을 열고 다음과 같이설정합니다.

mb-file.php?path=2021%2F10%2F14%2FF3955_4.png 

bin 파일:

  bin\at\nobooteagle.flash.bin - 0x00000

  bin\at\nobooteagle.irom0text.bin - 0x40000

  bin\blank.bin - 0xfe000

  bin\blank.bin - 0x7e000

FLASH SIZE: 8Mbit

BAUDRATE: 115200


START를 누르면 펨웨어가 업데이트 됩니다. Leaving... 메시지가 뜨면 업데이트 성공입니다.

mb-file.php?path=2021%2F10%2F14%2FF3956_5.png
 

통신속도를 변경하기위해 배선을 다음과 같이 합니다.

아두이노 UNO

ESP-01

3.3V

VCC / CH_PD 

GND

GND

GND - RESET

 

RX

RX

TX

TX


mb-file.php?path=2021%2F10%2F14%2FF3957_7.png
ESP 모듈에 RX는 3.3V를 받아야하므로 1k저항을 1:2로 연결해줍니다.

펌웨어 업데이트할때 배선에서 GPIO0에 연결되었던 선만 제거하면 됩니다.


아두이노IDE를 열고 시리얼모니터에 들어가서 다음과 같은 명령어를 입력합니다.

AT+UART_DEF=9600,8,1,0,0


mb-file.php?path=2021%2F10%2F14%2FF3959_6.png
주의: 통신속도는 115200보레이트로 해주세요.


이제 WiFiEsp 라이브러리를 설치합니다.

아두이노 IDE에서 스케치 > 라이브러리 포함하기 > 라이브러리 관리... 를 선택합니다.

mb-file.php?path=2021%2F10%2F14%2FF3960_8.png
 

WiFiEsp를 검색하여 라이브러리를 설치합니다.

mb-file.php?path=2021%2F10%2F14%2FF3961_9.png
 

다음과 같이 프로그래밍 합니다.

#include "WiFiEsp.h"

#include "SoftwareSerial.h"


#define ssid  "와이파이 이름"

#define pass  "비밀번호"

#define RX    2

#define TX    3

#define LED   8


SoftwareSerial esp(RX, TX);

int status = WL_IDLE_STATUS;

WiFiEspServer server(80);


void setup() {

  pinMode(LED, OUTPUT);

  Serial.begin(9600);

  esp.begin(9600);

  WiFi.init(&esp);


  if (WiFi.status() == WL_NO_SHIELD) {

    Serial.println("WiFi shield not present");

    while (true);

  }


  while (status != WL_CONNECTED) {

    Serial.print("Attempting to connect to WPA SSID: ");

    Serial.println(ssid);

    status = WiFi.begin(ssid, pass);

  }


  Serial.println("You're connected to the network");

  printWifiStatus();


  server.begin();

}


void printWifiStatus() {

  Serial.print("SSID: ");

  Serial.println(WiFi.SSID());


  IPAddress ip = WiFi.localIP();

  Serial.print("IP Address: ");

  Serial.println(ip);


  Serial.println();

  Serial.print("To see this page in action, open a browser to http://");

  Serial.println(ip);

  Serial.println();

}


const char HTTP_HEAD[] PROGMEM = R"=====(

<!doctype html>

<html lang="ko">

  <head>

    <link rel="icon" href="data:,">

    <meta name="viewport"content="width=device-width,initial-scale=1,user-scalable=no"/>

    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

    <title>WiFi 컨트롤</title>

    <script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>

    <style>

      button {border: 0; border-radius: 0.3rem; background: #858585; color: #faffff; line-height: 2.4rem; font-size: 1.2rem; -webkit-transition-duration: 0.4s; transition-duration: 0.4s; cursor: pointer;}

      button.off {background: #1fa3ec;}

      button.off:hover {background: #0e70a4;}

      button.on {background: gold;}

      button.on:hover {background:#ddba01;}

      CENTER > * {width: 250px;}

    </style>

  </head>

)=====";


const char HTTP_BODY[] PROGMEM = R"=====(

  <body>

    <CENTER>

      <h2>WiFi 컨트롤</h2>

      <p>스위치</p>

)=====";


const char SWITCH_ON[] PROGMEM = R"=====(     <button id = "switch" class="on" onClick="$.post(`/switch/${$(this).hasClass('off')?'on':'off'}`,(data)=>{data.status?$('#switch').addClass('on').removeClass('off').text('ON'):$('#switch').addClass('off').removeClass('on').text('OFF')})">ON</button>)=====";

const char SWITCH_OFF[] PROGMEM = R"=====(      <button id = "switch" class="off" onClick="$.post(`/switch/${$(this).hasClass('off')?'on':'off'}`,(data)=>{data.status?$('#switch').addClass('on').removeClass('off').text('ON'):$('#switch').addClass('off').removeClass('on').text('OFF')})">OFF</button>)=====";

const char HTTP_END[] PROGMEM = R"=====(

    </CENTER>

  </body>

</html>

)=====";


RingBuffer buf(16);


void loop() {

  WiFiEspClient client = server.available();

  if (client) {

    Serial.println("New client"); 

    buf.init();

    while (client.connected()) {

      if (client.available()) {

        char c = client.read();

        buf.push(c);


        if (buf.endsWith("GET /")) {

          client.flush();

          client.println(F("HTTP/1.1 200 OK"));

          client.println(F("Content-type:text/html; charset=utf-8"));

          client.println(F("Connection: close"));

          client.println();

        

          client.print((const __FlashStringHelper *)HTTP_HEAD);

          client.print((const __FlashStringHelper *)HTTP_BODY);

          if (digitalRead(LED) == HIGH) {

            client.print((const __FlashStringHelper *)SWITCH_ON);

          } else {

            client.print((const __FlashStringHelper *)SWITCH_OFF);

          }

          client.print((const __FlashStringHelper *)HTTP_END);

          client.println();

          delay(1);

          break;

        }

        

        else if (buf.endsWith("POST /switch/on") || buf.endsWith("POST /switch/off")) {

          client.flush();

          client.println(F("HTTP/1.1 200 OK"));

          client.println(F("Content-type:application/json; charset=utf-8"));

          client.println(F("Connection: close"));          

          client.println();

          if (buf.endsWith("POST /switch/on")) {

            Serial.println(F("Turn led ON"));

            digitalWrite(LED, HIGH);

            client.println(F("{\"status\":1}"));

          } else {

            Serial.println(F("Turn led OFF"));

            digitalWrite(LED, LOW);

            client.println("{\"status\":0}");           

          }

          delay(1);

          break;

        }

      }

    }

    

    client.stop();

    Serial.println("Client disconnected");

  }

}

동적 웹페이지를 위해 버튼을 클릭하면 ajax post 요청을 하도록 작성했습니다.


다음과 같이 배선합니다.

아두이노 UNO

ESP-01

3.3V

VCC / CH_PD 

GND

GND 

GPIO8 - 330옴 저항 - LED - GND


RX(GPIO2)

TX

TX(GPIO3)

RX


mb-file.php?path=2021%2F10%2F14%2FF3963_10.png
ESP 모듈에 RX는 3.3V를 받아야하므로 1k저항을 1:2로 연결해줍니다.


보드를 아두이노UNO로 선택하고 업로드합니다.

시리얼모니터를 열면 다음과 같이 접속가능한 ip주소가 뜹니다.

mb-file.php?path=2021%2F10%2F14%2FF3964_11.png
 

크롬을 열고 페이지에 접속해봅시다.

mb-file.php?path=2021%2F10%2F14%2FF3965_ezgif.com-gif-maker.gif 

시리얼 통신을 거치다보니 약간의 딜레이는 있습니다.



2. ESP8266 라이브러리 이용


시리얼통신을 거치지 않고 HTTP 통신을 하려면 ESP 모듈에 웹페이지를 업로드하면 됩니다.


mb-file.php?path=2021%2F10%2F14%2FF3966_12.png 


우선 ESP8266 보드를 추가합니다.

파일>환경설정을 누릅니다.

mb-file.php?path=2021%2F10%2F14%2FF3967_13.png


추가적인 보드 매니저 URLs에 다음 주소를 추가합니다.

https://arduino.esp8266.com/stable/package_esp8266com_index.json


mb-file.php?path=2021%2F10%2F14%2FF3968_14.png

mb-file.php?path=2021%2F10%2F14%2FF3969_15.png

툴>보드>보드 매니저를 클릭합니다.

mb-file.php?path=2021%2F10%2F14%2FF3970_16.png
 

esp8266을 검색하고 설치합니다.

mb-file.php?path=2021%2F10%2F14%2FF3971_17.png


보드는 Generic ESP8266 Module로 선택합니다.

mb-file.php?path=2021%2F10%2F14%2FF3972_18.png
 

다음 코드를 작성합니다.

 #include <ESP8266WiFi.h>

#include <WiFiClient.h>

#include <ESP8266WebServer.h>

#include <ESP8266mDNS.h>


#ifndef STASSID

#define STASSID "와이파이 이름"

#define STAPSK  "비밀번호"

#endif


const char* ssid = STASSID;

const char* password = STAPSK;


ESP8266WebServer server(80);


const int led = 2;


const char MAIN_PAGE[] PROGMEM = R"=====(

<!doctype html>

<html lang="ko">

  <head>

    <link rel="icon" href="data:,">

    <meta name="viewport"content="width=device-width,initial-scale=1,user-scalable=no"/>

    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

    <title>WiFi 컨트롤</title>

    <script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>

    <style>

      button {border: 0; border-radius: 0.3rem; background: #858585; color: #faffff; line-height: 2.4rem; font-size: 1.2rem; -webkit-transition-duration: 0.4s; transition-duration: 0.4s; cursor: pointer;}

      button.off {background: #1fa3ec;}

      button.off:hover {background: #0e70a4;}

      button.on {background: gold;}

      button.on:hover {background:#ddba01;}

      CENTER > * {width: 250px;}

    </style>

  </head>

  <body>

    <CENTER>

      <h2>WiFi 컨트롤</h2>

      <p>스위치</p>

      __SWITCH__

    </CENTER>

  </body>

</html>   

)=====";


const char SWITCH_ON[] PROGMEM = R"=====(<button id = "switch" class="on" onClick="$.post(`/switch/${$(this).hasClass('off')?'on':'off'}`,(data)=>{data.status?$('#switch').addClass('on').removeClass('off').text('ON'):$('#switch').addClass('off').removeClass('on').text('OFF')})">ON</button>)=====";

const char SWITCH_OFF[] PROGMEM = R"=====(<button id = "switch" class="off" onClick="$.post(`/switch/${$(this).hasClass('off')?'on':'off'}`,(data)=>{data.status?$('#switch').addClass('on').removeClass('off').text('ON'):$('#switch').addClass('off').removeClass('on').text('OFF')})">OFF</button>)=====";


void handleRoot() {

  

  String main_page = (const __FlashStringHelper *)MAIN_PAGE;

  

  if (digitalRead(led) == HIGH) {

    main_page.replace("__SWITCH__", (String)(const __FlashStringHelper *)SWITCH_ON);

    digitalWrite(led, HIGH);

  } else {

    main_page.replace("__SWITCH__", (String)(const __FlashStringHelper *)SWITCH_OFF);

    digitalWrite(led, LOW);

  }

  server.send(200, "text/html", main_page);

}


void handleSwitchOn() {

  if (server.method() != HTTP_POST) {

    server.send(405, "text/plain", "Method Not Allowed");

  } else {

    digitalWrite(led, HIGH);

    server.send(200, "application/json", "{\"status\":1}");

  }

}


void handleSwitchOff() {

  if (server.method() != HTTP_POST) {

    server.send(405, "text/plain", "Method Not Allowed");

  } else {

    digitalWrite(led, LOW);

    server.send(200, "application/json", "{\"status\":0}");

  }

}


void handleNotFound() {

  String message = "File Not Found\n\n";

  message += "URI: ";

  message += server.uri();

  message += "\nMethod: ";

  message += (server.method() == HTTP_GET) ? "GET" : "POST";

  message += "\nArguments: ";

  message += server.args();

  message += "\n";

  for (uint8_t i = 0; i < server.args(); i++) {

    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";

  }

  server.send(404, "text/plain", message);

}


void setup(void) {

  pinMode(led, OUTPUT);

  digitalWrite(led, 0);

  Serial.begin(9600);

  WiFi.mode(WIFI_STA);

  WiFi.begin(ssid, password);

  Serial.println("");


  // Wait for connection

  while (WiFi.status() != WL_CONNECTED) {

    delay(500);

    Serial.print(".");

  }

  Serial.println("");

  Serial.print("Connected to ");

  Serial.println(ssid);

  Serial.print("IP address: ");

  Serial.println(WiFi.localIP());


  if (MDNS.begin("esp8266")) {

    Serial.println("MDNS responder started");

  }


  server.on("/", handleRoot);


  server.on("/inline", []() {

    server.send(200, "text/plain", "this works as well");

  });


  server.on("/gif", []() {

    static const uint8_t gif[] PROGMEM = {

      0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x10, 0x00, 0x10, 0x00, 0x80, 0x01,

      0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x2c, 0x00, 0x00, 0x00, 0x00,

      0x10, 0x00, 0x10, 0x00, 0x00, 0x02, 0x19, 0x8c, 0x8f, 0xa9, 0xcb, 0x9d,

      0x00, 0x5f, 0x74, 0xb4, 0x56, 0xb0, 0xb0, 0xd2, 0xf2, 0x35, 0x1e, 0x4c,

      0x0c, 0x24, 0x5a, 0xe6, 0x89, 0xa6, 0x4d, 0x01, 0x00, 0x3b

    };

    char gif_colored[sizeof(gif)];

    memcpy_P(gif_colored, gif, sizeof(gif));

    // Set the background to a random set of colors

    gif_colored[16] = millis() % 256;

    gif_colored[17] = millis() % 256;

    gif_colored[18] = millis() % 256;

    server.send(200, "image/gif", gif_colored, sizeof(gif_colored));

  });


  server.onNotFound(handleNotFound);


  if (MDNS.begin("esp8266")) {

    Serial.println("MDNS responder started");

  }


  server.on("/", handleRoot);

  server.on("/switch/on", handleSwitchOn);

  server.on("/switch/off", handleSwitchOff);

  server.begin();

  Serial.println("HTTP server started");

}


void loop() {                           

  server.handleClient();

  MDNS.update();

}


다음과 같이 배선합니다.

아두이노 UNO

ESP-01 

3.3V

VCC / CH_PD

GND

GND / GPIO0

GND - RESET

 

RX

RX 

TX

TX 


mb-file.php?path=2021%2F10%2F14%2FF3973_3.ps.png 

ESP 모듈에 RX는 3.3V를 받아야하므로 1k저항을 1:2로 연결해줍니다.

ESP 모듈에 업로드합니다.


GPIO0가 GND에 연결되어있으면 초기화시 프로그래밍 모드에 진입합니다.

업로드에 실패하면 RST핀에 GND를 연결 후 제거하면 리셋되면서 프로그래밍 모드에 진입합니다. 이때 다시 업로드 해보세요.


Leaving...이 뜨면 업로드 완료입니다.

mb-file.php?path=2021%2F10%2F14%2FF3974_19.png


GPIO0와 GND 연결선을 제거하고 리셋후 시리얼모니터를 열면 IP주소가 나옵니다.

mb-file.php?path=2021%2F10%2F14%2FF3975_20.png
 

이제 전원만 공급하면 ESP 모듈이 독립적으로 웹서버로 작동합니다.

다음과 같이 배선하세요.

아두이노 UNO

ESP-01

3.3V

VCC / CH_PD

GND

GND

GND - LED (-)극

GPIO2 - 330옴 저항 - LED (+)극


mb-file.php?path=2021%2F10%2F14%2FF3976_21.png 

주의: GPIO2 선은 전원부터 넣고 연결하세요.

연결하고 전원을 넣으면 프로그래밍 모드로 진입해버립니다. 

(GPIO0만 그런줄 알았는데 GPIO2도 그렇네요..)


mb-file.php?path=2021%2F10%2F14%2FF3977_ezgif.com-gif-maker%20%281%29.gif
확실히 반응속도는 빠르지만 ESP01은 리셋할때 GPIO가 GND에 연결되어있으면 프로그래밍모드로 진입되어 전원공급 후에 선을 연결해야한다는 불편한 점이 있습니다.



3. Tasmota 펌웨어 사용


ESP 모듈은 많은 IOT 제품에서 사용됩니다. 이런 제품들은 클라우드 환경에서 제어할 수 있도록 펌웨어가 올려져 있는데 로컬로 제어할 수 있도록 하는 커스텀 펌웨어중 하나가 Tasmota 펌웨어입니다.

수많은 제품들을 지원합니다. 글 작성을 기준으로 2112개의 제품을 지원하네요.

https://templates.blakadder.com/


또한 OTA(무선 업데이트)를 지원하기때문에 esp를 실사용할때는 커스텀 펌웨어를 올리는 것을 추천합니다.


우선 tasmotizer를 다운로드합니다 : https://github.com/tasmota/tasmotizer/releases

mb-file.php?path=2021%2F10%2F14%2FF3978_22.png 

tasmota 펌웨어를 다운로드합니다: http://ota.tasmota.com/tasmota/release/

mb-file.php?path=2021%2F10%2F14%2FF3979_23.png


다음과 같이 배선합니다.

아두이노 UNO

ESP-01 

3.3V

VCC / CH_PD

GND

GND / GPIO0

GND - RESET

 

RX

RX 

TX

TX 



mb-file.php?path=2021%2F10%2F14%2FF3973_3.ps.png
ESP 모듈에 RX는 3.3V를 받아야하므로 1k저항을 1:2로 연결해줍니다.


Tasmotizer를 실행하여 포트를 선택하고 다운받은 펌웨어를 선택한 뒤 Tasmotize!를 누릅니다.

mb-file.php?path=2021%2F10%2F14%2FF3980_24.png


mb-file.php?path=2021%2F10%2F14%2FF3981_25.png
mb-file.php?path=2021%2F10%2F14%2FF3982_26.png
완료되면 GPIO0 - GND 연결 선을 제거하고 리셋합니다. (RST를 GND에 연결한 후 제거하면 리셋됩니다.)


그 후 Send config를 누릅니다. 와이파이 이름과 비밀번호를 적고 Save를 누릅니다.

mb-file.php?path=2021%2F10%2F14%2FF3983_28.png
 

자동으로 재부팅(리셋)됩니다. Get IP를 눌러 아이피를 확인합니다.

mb-file.php?path=2021%2F10%2F14%2FF3984_29.png

mb-file.php?path=2021%2F10%2F14%2FF3985_30.png

크롬을 통해 해당 주소를 들어갑니다. 설정 메뉴를 누릅니다.

mb-file.php?path=2021%2F10%2F14%2FF3987_31.png
 

모듈 설정에 들어갑니다.

mb-file.php?path=2021%2F10%2F14%2FF3988_32.png

Generic(0)을 선택하고 저장합니다.
mb-file.php?path=2021%2F10%2F14%2FF3989_33.png

재시작됩니다. 다시 모듈 설정으로 들어갑니다.

mb-file.php?path=2021%2F10%2F14%2FF3990_34.png

GPIO0과 GPIO2는 리셋시 프로그램 모드가 되므로 패스하고 TX는 HIGH 신호가 계속 나오므로 패스..

RX를 LED 제어핀으로 사용하겠습니다. Relay를 선택하고 저장합시다.


다음과 같이 배선하세요.

아두이노 UNO

ESP-01

3.3V

VCC / CH_PD

GND

GND

GND - LED (-)극

RX - 330옴 저항 - LED (+)극


mb-file.php?path=2021%2F10%2F14%2FF3991_35.png
 

mb-file.php?path=2021%2F10%2F14%2FF3992_ezgif.com-gif-maker%20%282%29.gif
 


#ESP-01# ESP8266# HTTP 통신# 웹페이지# 와이파이 연결
댓글
자동등록방지
(자동등록방지 숫자를 입력해 주세요)