본문 바로가기

ESP32

ESP32 이벤트 사용 Web Server(센서값 자동 업데이트)

반응형

 

 

ESP32 이벤트 사용 Web Server(센서값 자동 업데이트)

 

이 튜토리얼은 Arduino IDE로 프로그래밍된 ESP32 웹 서버에서 서버 전송 이벤트(SSE)를 사용하는 방법을 보여줍니다. SSE를 사용하면 브라우저가 HTTP 연결을 통해 서버에서 자동 업데이트를 수신할 수 있습니다. 예를 들어 업데이트된 센서 판독값을 브라우저로 보내는 데 유용합니다. 새 판독값이 있을 때마다 ESP32가 클라이언트로 전송하고 추가 요청 없이 웹 페이지를 자동으로 업데이트할 수 있습니다.

 

ESP32 웹 서버, 서버 전송 이벤트(SSE) 사용, 센서 판독값 자동 업데이트 Arduino

 

예를 들어, BME280 온도, 습도 및 압력 센서의 센서 판독값을 표시하는 웹 서버를 빌드합니다. BME280에 대해 자세히 알아보려면 다음 가이드를 읽어보세요.

 

Arduino IDE를 사용한 BME280 센서가 있는 ESP32(압력, 온도, 습도)

 

ESP8266에 대한 유사한 서버 전송 이벤트 가이드도 있습니다.

 

Server-Sent Events(SSE) 소개

 

Server-Sent Events(SSE)를 사용하면 클라이언트가 HTTP 연결을 통해 서버에서 자동 업데이트를 받을 수 있습니다.

 

ESP32 ESP8266 Server-Sent Events 웹 서버 작동 방식

 

클라이언트가 SSE 연결을 시작하고 서버가 이벤트 소스 프로토콜을 사용하여 클라이언트에 업데이트를 보냅니다. 클라이언트는 서버로부터 업데이트를 받지만 초기 핸드셰이크 후에는 서버에 데이터를 보낼 수 없습니다.

 

이는 업데이트된 센서 판독값을 브라우저로 보내는 데 유용합니다. 새 판독값이 있을 때마다 ESP32가 클라이언트로 보내고 추가 요청 없이 웹 페이지를 자동으로 업데이트할 수 있습니다. 센서 판독값 대신 GPIO 상태, 동작 감지 시 알림 등 프로젝트에 유용할 수 있는 모든 데이터를 보낼 수 있습니다.

 

중요: Server-Sent Events(SSE)는 Internet Explorer에서 지원되지 않습니다.

 

프로젝트 개요

 

이 프로젝트를 위해 빌드할 웹 페이지는 다음과 같습니다.

 

BME280 웹 서버, 서버 전송 이벤트(SSE) 사용 ESP32 Arduino

 

  • ESP32 웹 서버는 BME280 온도, 습도 및 압력 판독값이 있는 세 장의 카드를 표시합니다.
  • ESP32는 30초마다 센서에서 새로운 판독값을 받습니다.
  • 새로운 판독값이 있을 때마다 보드(서버)는 서버 전송 이벤트를 사용하여 클라이언트로 전송합니다.
  • 클라이언트는 데이터를 수신하고 그에 따라 웹 페이지를 업데이트합니다.
  • 이렇게 하면 새로운 판독값이 있을 때마다 웹 페이지가 자동으로 업데이트됩니다.

 

작동 원리?

 

다음 다이어그램은 서버 전송 이벤트가 웹 페이지를 업데이트하는 방식을 요약한 것입니다.

 

서버 전송 이벤트(SSE)가 있는 ESP32 BME280 웹 서버 작동 원리

 

1. 클라이언트가 SSE 연결을 시작하고 서버는 /events URL에서 이벤트 소스 프로토콜을 사용하여 클라이언트로 업데이트를 전송합니다.

2. ESP32는 새로운 센서 판독값을 받습니다.

3. 다음 이름을 가진 이벤트로 판독값을 클라이언트에 전송합니다: '온도', '습도' 및 '압력';

4. 클라이언트는 서버에서 보낸 이벤트에 대한 이벤트 리스너를 가지고 있으며 해당 이벤트에서 업데이트된 센서 판독값을 수신합니다;

5. 최신 판독값으로 웹 페이지를 업데이트합니다.

 

Arduino IDE 준비

 

Arduino IDE를 사용하여 ESP32 보드를 프로그래밍하므로 Arduino IDE에 설치되어 있는지 확인하세요.

 

Arduino IDE 2 (Windows, Mac OS X, Linux)에 ESP32 보드 설치

 

라이브러리 설치 - 비동기 웹 서버

 

웹 서버를 빌드하려면 ESPAsyncWebServer 라이브러리를 사용합니다. 이 라이브러리는 제대로 작동하려면 AsyncTCP 라이브러리가 필요합니다. 아래 링크를 클릭하여 라이브러리를 다운로드하세요.

 

ESPAsyncWebServer

AsyncTCP

 

이러한 라이브러리는 Arduino 라이브러리 관리자를 통해 설치할 수 없으므로 라이브러리 파일을 Arduino 설치 라이브러리 폴더에 복사해야 합니다. 또는 Arduino IDE에서 Sketch > Include Library > Add .zip Library로 이동하여 방금 다운로드한 라이브러리를 선택할 수 있습니다.

 

라이브러리 설치 - BME280 센서

 

BME280 센서 모듈에서 판독값을 얻으려면 Adafruit_BME280 라이브러리를 사용합니다. 또한 Adafruit_Sensor 라이브러리를 설치해야 합니다. 다음 단계에 따라 Arduino IDE에 라이브러리를 설치합니다.

 

1. Arduino IDE를 열고 Sketch > Include Library > Manage Libraries로 이동합니다. Library Manager가 열립니다.

 

2. 검색 상자에서 "adafruit bme280"을 검색하여 라이브러리를 설치합니다.

 

Arduino IDE에 BME280 라이브러리 설치

 

BME280 라이브러리를 사용하려면 Adafruit Unified Sensor도 설치해야 합니다. 다음 단계에 따라 Arduino IDE에 라이브러리를 설치합니다.

 

3. 검색 상자에서 "Adafruit Unified Sensor"를 검색합니다. 아래로 스크롤하여 라이브러리를 찾아 설치합니다.

 

Adafruit Unified Sensor Driver 라이브러리 설치

 

라이브러리를 설치한 후 Arduino IDE를 다시 시작합니다.

 

BME280 센서에 대해 자세히 알아보려면 가이드를 읽어보세요: Arduino IDE를 사용한 BME280 센서가 있는 ESP32(압력, 온도, 습도).

 

회로 구축

 

ESP32에서 서버 전송 이벤트를 사용하는 방법을 예시하기 위해 BME280 센서에서 브라우저로 센서 판독값을 전송합니다. 따라서 BME280 센서를 ESP32에 연결해야 합니다.

 

필요한 부품: 이 튜토리얼을 완료하려면 다음 부품이 필요합니다.

 

  • BME280 센서 모듈
  • ESP32(최고의 ESP32 개발 보드 읽기)
  • 브레드보드
  • 점퍼 와이어

회로도

 

BME280 센서 모듈과 I2C 통신을 사용합니다. 이를 위해 다음 회로도에 표시된 대로 센서를 기본 ESP32 SCL(GPIO 22) 및 SDA(GPIO 21) 핀에 연결합니다.

 

ESP32 배선과 BME280 회로도

 

추천 자료: ESP32 핀아웃 참조: 어떤 GPIO 핀을 사용해야 합니까?

 

SSE(Server-Sent Events)를 사용하는 ESP32 웹 서버용 코드

 

다음 코드를 Arduino IDE에 복사합니다.

 

/*********
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-web-server-sent-events-sse/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*********/

#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <Adafruit_BME280.h>
#include <Adafruit_Sensor.h>

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

// Create an Event Source on /events
AsyncEventSource events("/events");

// Timer variables
unsigned long lastTime = 0;  
unsigned long timerDelay = 30000;

// Create a sensor object
Adafruit_BME280 bme;         // BME280 connect to ESP32 I2C (GPIO 21 = SDA, GPIO 22 = SCL)

float temperature;
float humidity;
float pressure;

// Init BME280
void initBME(){
    if (!bme.begin(0x76)) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }
}

void getSensorReadings(){
  temperature = bme.readTemperature();
  // Convert temperature to Fahrenheit
  //temperature = 1.8 * bme.readTemperature() + 32;
  humidity = bme.readHumidity();
  pressure = bme.readPressure()/ 100.0F;
}

// Initialize WiFi
void initWiFi() {
    WiFi.mode(WIFI_STA);
    WiFi.begin(ssid, password);
    Serial.print("Connecting to WiFi ..");
    while (WiFi.status() != WL_CONNECTED) {
        Serial.print('.');
        delay(1000);
    }
    Serial.println(WiFi.localIP());
}

String processor(const String& var){
  getSensorReadings();
  //Serial.println(var);
  if(var == "TEMPERATURE"){
    return String(temperature);
  }
  else if(var == "HUMIDITY"){
    return String(humidity);
  }
  else if(var == "PRESSURE"){
    return String(pressure);
  }
  return String();
}

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
  <title>ESP Web Server</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
  <link rel="icon" href="data:,">
  <style>
    html {font-family: Arial; display: inline-block; text-align: center;}
    p { font-size: 1.2rem;}
    body {  margin: 0;}
    .topnav { overflow: hidden; background-color: #50B8B4; color: white; font-size: 1rem; }
    .content { padding: 20px; }
    .card { background-color: white; box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5); }
    .cards { max-width: 800px; margin: 0 auto; display: grid; grid-gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
    .reading { font-size: 1.4rem; }
  </style>
</head>
<body>
  <div class="topnav">
    <h1>BME280 WEB SERVER (SSE)</h1>
  </div>
  <div class="content">
    <div class="cards">
      <div class="card">
        <p><i class="fas fa-thermometer-half" style="color:#059e8a;"></i> TEMPERATURE</p><p><span class="reading"><span id="temp">%TEMPERATURE%</span> &deg;C</span></p>
      </div>
      <div class="card">
        <p><i class="fas fa-tint" style="color:#00add6;"></i> HUMIDITY</p><p><span class="reading"><span id="hum">%HUMIDITY%</span> &percnt;</span></p>
      </div>
      <div class="card">
        <p><i class="fas fa-angle-double-down" style="color:#e1e437;"></i> PRESSURE</p><p><span class="reading"><span id="pres">%PRESSURE%</span> hPa</span></p>
      </div>
    </div>
  </div>
<script>
if (!!window.EventSource) {
 var source = new EventSource('/events');
 
 source.addEventListener('open', function(e) {
  console.log("Events Connected");
 }, false);
 source.addEventListener('error', function(e) {
  if (e.target.readyState != EventSource.OPEN) {
    console.log("Events Disconnected");
  }
 }, false);
 
 source.addEventListener('message', function(e) {
  console.log("message", e.data);
 }, false);
 
 source.addEventListener('temperature', function(e) {
  console.log("temperature", e.data);
  document.getElementById("temp").innerHTML = e.data;
 }, false);
 
 source.addEventListener('humidity', function(e) {
  console.log("humidity", e.data);
  document.getElementById("hum").innerHTML = e.data;
 }, false);
 
 source.addEventListener('pressure', function(e) {
  console.log("pressure", e.data);
  document.getElementById("pres").innerHTML = e.data;
 }, false);
}
</script>
</body>
</html>)rawliteral";

void setup() {
  Serial.begin(115200);
  initWiFi();
  initBME();


  // Handle Web Server
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html, processor);
  });

  // Handle Web Server Events
  events.onConnect([](AsyncEventSourceClient *client){
    if(client->lastId()){
      Serial.printf("Client reconnected! Last message ID that it got is: %u\n", client->lastId());
    }
    // send event with message "hello!", id current millis
    // and set reconnect delay to 1 second
    client->send("hello!", NULL, millis(), 10000);
  });
  server.addHandler(&events);
  server.begin();
}

void loop() {
  if ((millis() - lastTime) > timerDelay) {
    getSensorReadings();
    Serial.printf("Temperature = %.2f ºC \n", temperature);
    Serial.printf("Humidity = %.2f \n", humidity);
    Serial.printf("Pressure = %.2f hPa \n", pressure);
    Serial.println();

    // Send Events to the Web Client with the Sensor Readings
    events.send("ping",NULL,millis());
    events.send(String(temperature).c_str(),"temperature",millis());
    events.send(String(humidity).c_str(),"humidity",millis());
    events.send(String(pressure).c_str(),"pressure",millis());
    
    lastTime = millis();
  }
}

 

다음 변수에 네트워크 자격 증명을 삽입하면 코드가 바로 작동합니다.

 

const char* ssid = "REPLACE_WITH_YOUR_SSID";

const char* password = "REPLACE_WITH_YOUR_PASSWORD";

 

코드 작동 방식

 

코드 작동 방식을 알아보려면 계속 읽거나 데모 섹션으로 건너뜁니다.

 

라이브러리 포함

 

Adafruit_Sensor 및 Adafruit_BME280 라이브러리는 BME280 센서와 인터페이스하는 데 필요합니다.

 

#include <Adafruit_BME280.h>
#include <Adafruit_Sensor.h>

 

WiFi, ESPAsyncWebServer 및 AsyncTCP 라이브러리는 웹 서버를 만드는 데 사용됩니다.

 

#include <WiFi.h>
#include "ESPAsyncWebServer.h"

 

네트워크 자격 증명

 

ESP32가 Wi-Fi를 사용하여 로컬 네트워크에 연결할 수 있도록 다음 변수에 네트워크 자격 증명을 삽입합니다.

 

const char* ssid = "REPLACE_WITH_YOUR_SSID";

const char* password = "REPLACE_WITH_YOUR_PASSWORD"; AsyncWebServer 및 AsyncEventSource

 

포트 80에서 AsyncWebServer 객체를 만듭니다.

AsyncWebServer server(80);

 

다음 줄은 /events에서 새 이벤트 소스를 만듭니다.

AsyncEventSource events("/events");

 

변수 선언

 

lastTime 및 timerDelay 변수는 X초마다 센서 판독값을 업데이트하는 데 사용됩니다. 예를 들어, 30초(30000밀리초)마다 새로운 센서 판독값을 가져옵니다. timerDelay 변수에서 지연 시간을 변경할 수 있습니다.

 

unsigned long lastTime = 0;

unsigned long timerDelay = 30000;

 

기본 ESP32 I2C 핀에 bme라는 Adafruit_BME280 객체를 만듭니다.

Adafruit_BME280 bme;

 

온도, 습도 및 압력 float 변수는 BME280 센서 판독값을 보관하는 데 사용됩니다.

 

float temperature;
float humidity;
float pressure;

 

BME280 초기화

다음 함수는 BME280 초기화 함수를 호출합니다.

 

void initBME(){
  if (!bme.begin(0x76)) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }
}

 

BME280 판독값 가져오기

getSensorReading() 함수는 BME280 센서에서 온도, 습도 및 압력 판독값을 가져와 온도, 습도 및 압력 변수에 저장합니다.

 

void getSensorReadings(){
  temperature = bme.readTemperature();
  // Convert temperature to Fahrenheit
  //temperature = 1.8 * bme.readTemperature() + 32;
  humidity = bme.readHumidity();
  pressure = bme.readPressure()/ 100.0F;
}

 

Wi-Fi 초기화

 

다음 함수는 ESP32를 Wi-Fi 스테이션으로 설정하고 이전에 정의한 ssid와 비밀번호를 사용하여 라우터에 연결합니다.

 

void initWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi ..");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(1000);
  }
  Serial.println(WiFi.localIP());
}

 

Processor

 

processor() 함수는 웹 페이지를 빌드하는 데 사용된 HTML 텍스트의 모든 플레이스홀더를 브라우저로 보내기 전에 현재 센서 판독값으로 바꿉니다.

 

String processor(const String& var){
  getSensorReadings();
  //Serial.println(var);
  if(var == "TEMPERATURE"){
    return String(temperature);
  }
  else if(var == "HUMIDITY"){
    return String(humidity);
  }
  else if(var == "PRESSURE"){
    return String(pressure);
  }
}

 

이렇게 하면 처음으로 웹 페이지에 액세스할 때 현재 센서 판독값을 표시할 수 있습니다. 그렇지 않으면 새 판독값이 제공될 때까지 빈 공간이 표시됩니다(코드에서 정의한 지연 시간에 따라 시간이 걸릴 수 있음).

 

웹 페이지 빌드

 

index_html 변수에는 웹 페이지를 빌드하는 데 필요한 모든 HTML, CSS 및 JavaScript가 포함됩니다.

 

참고: 이 튜토리얼의 단순성을 위해 Arduino 스케치에서 사용하는 index_html 변수에 웹 페이지를 빌드하는 데 필요한 모든 것을 배치합니다. HTML, CSS 및 JavaScript 파일을 분리한 다음 ESP32 파일 시스템에 업로드하여 코드에서 참조하는 것이 더 실용적일 수 있습니다.

 

다음은 content index_html 변수입니다.

 

<!DOCTYPE HTML>
<html>
<head>
  <title>ESP Web Server</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
  <link rel="icon" href="data:,">
  <style>
    html {
      font-family: Arial; 
      display: inline-block; 
      text-align: center;
    }
    p { 
      font-size: 1.2rem;
    }
    body {  
      margin: 0;
    }
    .topnav { 
      overflow: hidden; 
      background-color: #50B8B4; 
      color: white; 
      font-size: 1rem; 
    }
    .content { 
      padding: 20px; 
    }
    .card { 
      background-color: white; 
      box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5); 
    }
    .cards { 
      max-width: 800px; 
      margin: 0 auto; 
      display: grid; 
      grid-gap: 2rem; 
      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
    .reading { 
      font-size: 1.4rem;  
    }
  </style>
</head>
<body>
  <div class="topnav">
    <h1>BME280 WEB SERVER (SSE)</h1>
  </div>
  <div class="content">
    <div class="cards">
      <div class="card">
        <p><i class="fas fa-thermometer-half" style="color:#059e8a;"></i> TEMPERATURE</p>
        <p><span class="reading"><span id="temp">%TEMPERATURE%</span> &deg;C</span></p>
      </div>
      <div class="card">
        <p><i class="fas fa-tint" style="color:#00add6;"></i> HUMIDITY</p>
        <p><span class="reading"><span id="hum">%HUMIDITY%</span> &percnt;</span></p>
      </div>
      <div class="card">
        <p><i class="fas fa-angle-double-down" style="color:#e1e437;"></i> PRESSURE</p><p><span class="reading"><span id="pres">%PRESSURE%</span> hPa</span></p>
      </div>
    </div>
  </div>
<script>
if (!!window.EventSource) {
  var source = new EventSource('/events');

  source.addEventListener('open', function(e) {
    console.log("Events Connected");
  }, false);

  source.addEventListener('error', function(e) {
    if (e.target.readyState != EventSource.OPEN) {
      console.log("Events Disconnected");
    }
  }, false);

  source.addEventListener('message', function(e) {
    console.log("message", e.data);
  }, false);

  source.addEventListener('temperature', function(e) {
    console.log("temperature", e.data);
    document.getElementById("temp").innerHTML = e.data;
  }, false);

  source.addEventListener('humidity', function(e) {
    console.log("humidity", e.data);
    document.getElementById("hum").innerHTML = e.data;
  }, false);

  source.addEventListener('pressure', function(e) {
    console.log("pressure", e.data);
    document.getElementById("pres").innerHTML = e.data;
  }, false);
}
</script>
</body>
</html>

 

HTML과 CSS가 어떻게 작동하는지에 대한 자세한 설명은 하지 않겠습니다. 서버에서 보낸 이벤트를 처리하는 방법만 살펴보겠습니다.

 

CSS

 

태그 사이에 CSS를 사용하여 웹 페이지의 스타일을 지정합니다. 원하는 대로 웹 페이지가 보이도록 자유롭게 변경하세요. 이 튜토리얼과 관련이 없으므로 이 웹 페이지의 CSS가 어떻게 작동하는지 설명하지 않겠습니다.

 

<style>
  html {
    font-family: Arial; 
    display: inline-block; 
    text-align: center;
  }
  p { 
    font-size: 1.2rem;
  }
  body {  
    margin: 0;
  }
  .topnav { 
    overflow: hidden; 
    background-color: #50B8B4; 
    color: white; 
    font-size: 1rem; 
  }
  .content { 
    padding: 20px; 
  }
  .card { 
    background-color: white; 
    box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5); 
  }
  .cards { 
    max-width: 800px; 
    margin: 0 auto; 
    display: grid; 
    grid-gap: 2rem; 
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
  .reading { 
    font-size: 1.4rem;  
  }
</style>

 

HTML

 

BODY 태그 사이에 사용자에게 표시되는 웹 페이지 콘텐츠를 추가합니다.

 

<body>
  <div class="topnav">
    <h1>BME280 WEB SERVER (SSE)</h1>
  </div>
  <div class="content">
    <div class="cards">
      <div class="card">
        <p><i class="fas fa-thermometer-half" style="color:#059e8a;"></i> TEMPERATURE</p>
        <p><span class="reading"><span id="temp">%TEMPERATURE%</span> &deg;C</span></p>
      </div>
      <div class="card">
        <p><i class="fas fa-tint" style="color:#00add6;"></i> HUMIDITY</p>
        <p><span class="reading"><span id="hum">%HUMIDITY%</span> &percnt;</span></p>
      </div>
      <div class="card">
        <p><i class="fas fa-angle-double-down" style="color:#e1e437;"></i> PRESSURE</p><p><span class="reading"><span id="pres">%PRESSURE%</span> hPa</span></p>
      </div>
    </div>
  </div>

 

"BME280 WEB SERVER (SSE)"라는 내용이 있는 제목 1이 있습니다. 상단 막대에 표시되는 텍스트입니다. 자유롭게 해당 텍스트를 수정하세요.

 

<h1>BME280 WEB SERVER (SSE)</h1>

 

그런 다음 센서 판독값을 분리된 div 태그에 표시합니다. 온도를 표시하는 문단을 잠깐 살펴보겠습니다.

 

<p><i class="fas fa-thermometer-half" style="color:#059e8a;"></i> TEMPERATURE</p>
<p><span class="reading"><span id="temp">%TEMPERATURE%</span> &deg;C</span></p>

 

첫 번째 문단은 단순히 "온도" 텍스트를 표시합니다. 색상과 아이콘도 정의합니다.

 

두 번째 문단에서 %TEMPERATURE% 자리 표시자가 태그로 둘러싸여 있는 것을 볼 수 있습니다. HTML id 속성은 HTML 요소에 대한 고유 id를 지정하는 데 사용됩니다.

 

특정 스타일을 가리키는 데 사용되거나 JavaScript에서 해당 특정 id를 가진 요소에 액세스하고 조작하는 데 사용할 수 있습니다. 바로 우리가 할 일입니다. 예를 들어, 클라이언트가 최신 온도 판독 값이 포함된 새 이벤트를 수신하면 id가 "temp"인 HTML 요소를 새 판독 값으로 업데이트합니다.

 

다른 판독 값을 업데이트하는 데도 비슷한 프로세스가 수행됩니다.

 

JavaScript - EventSource

 

JavaScript는

 

<script>
if (!!window.EventSource) {
  var source = new EventSource('/events');
  source.addEventListener('open', function(e) {
    console.log("Events Connected");
  }, false);
  source.addEventListener('error', function(e) {
    if (e.target.readyState != EventSource.OPEN) {
      console.log("Events Disconnected");
    }
  }, false);

  source.addEventListener('message', function(e) {
    console.log("message", e.data);
  }, false);

  source.addEventListener('temperature', function(e) {
    console.log("temperature", e.data);
    document.getElementById("temp").innerHTML = e.data;
  }, false);

  source.addEventListener('humidity', function(e) {
    console.log("humidity", e.data);
    document.getElementById("hum").innerHTML = e.data;
  }, false);

  source.addEventListener('pressure', function(e) {
    console.log("pressure", e.data);
    document.getElementById("pres").innerHTML = e.data;
  }, false);
}
</script>

 

 

태그 사이에 있습니다. 서버와 EventSource 연결을 초기화하고 서버에서 수신한 이벤트를 처리하는 역할을 합니다.

이것이 어떻게 작동하는지 살펴보겠습니다.

 

이벤트 처리

 

새 EventSource 객체를 만들고 업데이트를 보내는 페이지의 URL을 지정합니다. 이 경우 /events입니다.

 

if (!!window.EventSource) {
  var source = new EventSource('/events');

 

이벤트 소스를 인스턴스화한 후 addEventListener()로 서버에서 메시지를 수신할 수 있습니다.

 

다음은 AsyncWebServer 설명서에 표시된 대로 기본 이벤트 리스너입니다.

 

source.addEventListener('open', function(e) {
  console.log("Events Connected");
}, false);

source.addEventListener('error', function(e) {
  if (e.target.readyState != EventSource.OPEN) {
    console.log("Events Disconnected");
  }
}, false);

source.addEventListener('message', function(e) {
  console.log("message", e.data);
}, false);

 

그런 다음 '온도'에 대한 이벤트 리스너를 추가합니다.

 

source.addEventListener('temperature', function(e) {

 

새로운 온도 판독값이 제공되면 ESP32는 클라이언트에 이벤트("temperature")를 보냅니다. 다음 줄은 브라우저가 해당 이벤트를 수신할 때 발생하는 작업을 처리합니다.

 

console.log("temperature", e.data);
document.getElementById("temp").innerHTML = e.data;

 

기본적으로 브라우저 콘솔에 새 판독값을 인쇄하고 수신된 데이터를 웹 페이지의 해당 ID("temp")가 있는 요소에 넣습니다.

 

습도와 압력에 대해서도 비슷한 처리가 수행됩니다.

 

source.addEventListener('humidity', function(e) {
  console.log("humidity", e.data);
  document.getElementById("hum").innerHTML = e.data;
}, false);
 
source.addEventListener('pressure', function(e) {
  console.log("pressure", e.data);
  document.getElementById("pres").innerHTML = e.data;
}, false);
 
source.addEventListener('gas', function(e) {
  console.log("gas", e.data);
  document.getElementById("gas").innerHTML = e.data;
}, false);

 

setup()

 

setup()에서 직렬 모니터를 초기화하고 Wi-Fi와 BME280 센서를 초기화합니다.

 

Serial.begin(115200);

initWiFi();

initBME();

 

요청 처리

 

루트/URL에서 ESP32 IP 주소에 액세스하면 index_html 변수에 저장된 텍스트를 보내서 다음을 빌드합니다. 웹 페이지로 이동하여 프로세서를 인수로 전달하면 모든 플레이스홀더가 최신 센서 판독값으로 대체됩니다.

 

server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send_P(200, "text/html", index_html, processor);
});

 

서버 이벤트 소스

 

서버에서 이벤트 소스를 설정합니다.

 

// Handle Web Server Events
events.onConnect([](AsyncEventSourceClient *client){
  if(client->lastId()){
    Serial.printf("Client reconnected! Last message ID that it got is: %u\n", client->lastId());
  }
  // send event with message "hello!", id current millis
  // and set reconnect delay to 1 second
  client->send("hello!", NULL, millis(), 10000);
});
server.addHandler(&events);

 

마지막으로 서버를 시작합니다.

 

server.begin();

 

loop()

 

loop()에서 새 센서 판독값을 가져옵니다.

 

getSensorReadings();

 

직렬 모니터에 새 판독값을 인쇄합니다.

 

Serial.printf("Temperature = %.2f ºC \n", temperature);
Serial.printf("Humidity = %.2f % \n", humidity);
Serial.printf("Pressure = %.2f hPa \n", pressure);
Serial.println();

 

마지막으로 최신 센서 판독값으로 브라우저에 이벤트를 보내 웹 페이지를 업데이트합니다.

 

// Send Events to the Web Server with the Sensor Readings
events.send("ping",NULL,millis());
events.send(String(temperature).c_str(),"temperature",millis());
events.send(String(humidity).c_str(),"humidity",millis());
events.send(String(pressure).c_str(),"pressure",millis());

 

데모

 

ssid 및 password 변수에 네트워크 자격 증명을 삽입한 후 보드에 코드를 업로드할 수 있습니다. 올바른 보드와 COM 포트를 선택했는지 확인하는 것을 잊지 마세요.

 

코드를 업로드한 후 115200의 통신 속도로 직렬 모니터를 열고 온보드 EN/RST 버튼을 누릅니다. ESP IP 주소가 인쇄되어야 합니다.

 

ESP32 웹 서버, Server Sent Events 사용 직렬 모니터 데모

 

로컬 네트워크에서 브라우저를 열고 ESP32 IP 주소를 입력합니다. 센서 판독값을 모니터링하기 위한 웹 페이지에 액세스할 수 있어야 합니다.

 

BME280 웹 서버, Server-Sent Events ESP32 사용

 

판독값은 30초마다 자동으로 업데이트됩니다.

 

동시에 이전 인쇄 화면에 표시된 대로 직렬 모니터에서 새 센서 판독값을 받아야 합니다. 또한 클라이언트가 이벤트를 수신하고 있는지 확인할 수 있습니다. 브라우저에서 Ctrl+Shift+J를 눌러 콘솔을 엽니다.

 

서버에서 보낸 이벤트 브라우저 콘솔 ESP2 웹 서버 데모

 

마무리

 

이 튜토리얼에서는 ESP32에서 서버에서 보낸 이벤트를 사용하는 방법을 알아보았습니다. 서버에서 보낸 이벤트를 사용하면 웹 페이지(클라이언트)가 서버에서 업데이트를 받을 수 있습니다. 이를 사용하여 사용 가능한 경우 웹 서버 페이지에 새 센서 판독값을 자동으로 표시할 수 있습니다.

 

비슷한 튜토리얼이 있지만 BME680 환경 센서를 사용합니다. 다음 링크에서 튜토리얼을 확인할 수 있습니다.

 

BME680을 사용한 ESP32 웹 서버 – 기상 관측소(Arduino IDE)

 

서버에서 보낸 이벤트 대신 WebSocket 프로토콜을 사용하여 웹 페이지를 최신 상태로 유지할 수도 있습니다. 다음 튜토리얼을 살펴보고 ESP32로 WebSocket 서버를 설정하는 방법을 알아보세요.

 

ESP32 WebSocket 서버: 제어 출력(Arduino IDE)

 

다음 리소스를 통해 ESP32에 대해 자세히 알아보세요.

 

Arduino IDE로 ESP32 배우기

ESP32 및 ESP8266을 사용한 MicroPython 프로그래밍

추가 ESP32 프로젝트 및 가이드… 

 

여기까지 입니다. 읽어주셔서 감사합니다.

 

절대로 배움을 멈추지 마세요.

 

반응형

더욱 좋은 정보를 제공하겠습니다.~ ^^