본문 바로가기
Hardware/MCU(Arduino,ESP8266)

ESP8266-12 + DHT22, 웹에서 온도 습도 확인하기 2.0

by lovey25 2020. 10. 9.
반응형

어제 포스팅에서는 ESP12 보드에 DHT22 센서 모듈을 연결해서 간단하게 웹페이지에서 온도와 습도를 확인할 수 있는 예제를 따라 해 봤었는데요. 이번에는 웹페이지에서 보이는 온습도가 일정 시간 간격으로 자동으로 업데이트되도록 업그레이드해 봤습니다.

 

ESP8266-12 + DHT22, 웹에서 온도 습도 확인하기

이전 포스팅에서 다룬 아두이노를 통해서 DHT 온습도 센서 값을 읽는 예제의 업그레이드 버전으로 이번에는 ESP8266 칩셋 기반의 ESP-12 모듈을 이용해서 웹에서 온습도를 읽어오는 예제입니다. 아��

kwonkyo.tistory.com

하드웨어

결선은 이전 포스팅과 동일하기 때문에 생략합니다. 앞에 언급된 이전 글 참고해 주세요. ^^

소프트웨어 (펌웨어 코딩)

동작의 흐름을 먼저 설명하자면, ESP12 모듈은 일정한 시간 간격으로 DHT22센서로부터 온도와 습도 값을 요청해서 각각을 전역 변수에 저장하고 있습니다. 동시에, 웹페이지가 로딩이 되면 별도의 설정시간 간격으로 ESP12 모듈의 전역 변수에 저장된 온도와 습도 값을 요청하고 전달받은 수치로 웹페이지의 기존 수치를 대체합니다.

말로 풀어쓰니 간단해 보이는데 값이 여기저기 왔다 갔다 하고 HTML 코드와 C++ 코드를 동시에 생각해야 하니 좀 헷갈리더라고요. 하지만 찬찬히 따라가 보면 그리 어려운 건 아닌 것 같습니다. 아래 코드는 이전 포스팅에서 업데이트한 것이기 때문에 변경한 부분을 중심으로 설명하도록 하겠습니다. 

mainPage.html 파일 내용

먼저 설명을 쉽게 하기위해서 HTML 코드부터 살펴보겠습니다.

const char MAIN_page[] PROGMEM = R"=====(
<!DOCTYPE html>
<html lang="ko">
<head>
<meta name="viewport"content="width=device-width,initial-scale=1,user-scalable=no"/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
<style>
body{text-align:center;font-family:verdana;}
h2{font-size: 2.0rem}
button{border:0;border-radius:1.5rem;background-color:#0066ff;color:rgb(255, 255, 255);line-height:2.4rem;font-size:1.2rem;width:100%}
p { font-size: 1.6rem; }
  .units { font-size: 1.2rem; }
  .dht-heading{
    font-size: 1.5rem;
    vertical-align:middle;
    padding-bottom: 5px; padding-top: 15px;
  }
</style>
</head>

<TITLE>
WIFI Controller
</TITLE>

<BODY>
<div style="text-align:center;display:inline-block;min-width:280px;">
<h2>온습도 모니터링</h2>
<p>
  <span class="dht-heading">온도/습도</span>
</p>
<p>
  <span>
  <i class="fas fa-thermometer-half" style="color:#059e8a;"></i>
    <sup id="temperature">@@TEMP@@</sup>
    <sup class="units">&deg;C / </sup>
  </span> 
  <span>
  <i class="fas fa-tint" style="color:#00add6;"></i> 
    <sup id="humidity">@@HUMI@@</sup>
    <sup class="units">%</sup>
  </span>
</p>
</div>
</BODY>

<script>
    setInterval(function ( ) {
      var xhttp = new XMLHttpRequest();
      xhttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
          document.getElementById("temperature").innerHTML = this.responseText;
        }
      };
      xhttp.open("GET", "/temperature", true);
      xhttp.send();
    }, 10000 ) ;
    
    setInterval(function ( ) {
      var xhttp = new XMLHttpRequest();
      xhttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
          document.getElementById("humidity").innerHTML = this.responseText;
        }
      };
      xhttp.open("GET", "/humidity", true);
      xhttp.send();
    }, 10000 ) ;
    </script>

</HTML>
)=====";

47~71행에 <script> 코드가 추가되었고 다른 부분은 동일합니다. 자바스크립트를 사용해서 일정한 시간 간격으로 ESP12 모듈로부터 값을 확인하도록 하는데요. "setInterval(function(), 10000);" 함수가 10초마다 한 번씩 내부 스크립트를 실행합니다. 저도 자바스크립트는 잘 몰라서 정확한 문법을 이해하지는 못했지만 반복 실행되는 스크립트의 내용은 대충 웹페이지의 로딩이 완료되면 "/temperature"주소를 ESP12에 요청을 해서 전달받은 문자열을 HTML 코드 중 "id=temperature" 속성을 가지는 요소에 대입하시오~입니다. 그리고 그 아래에 반복되는 단락은 똑같이 "id=humidity"에 적용되는 부분을 한 번 더 반복하는 거죠.

ino파일 내용

이번엔 ino파일의 변경부분 살펴보겠습니다.

#include "DHT.h"
#include <ESP8266WiFi.h>
#include <WiFiClient.h> 
#include <ESP8266WebServer.h>
#include "mainPage.html"

#define DHTPIN 5     // GPIO5
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);

float h = 0.0;
float t = 0.0;

unsigned long preMills =0;
const long interval = 5000;

const char *ssid = "[SSID를입력]";
const char *password = "[네트워크비밀번호를입력]";

String strTemp, strHumi, myLocalIP;
ESP8266WebServer server(80);        // server on port 80
IPAddress myIP;

void setup() {
  Serial.begin(115200);
  dht.begin();

  WiFi.begin(ssid, password);   
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }  
  myIP = WiFi.localIP();
  char buf[18];                           // https://gist.github.com/loosak/76019faaefd5409fca67
  sprintf(buf, "%d.%d.%d.%d", myIP[0], myIP[1], myIP[2], myIP[3]);
  myLocalIP = String(buf);

  Serial.println("IP: " + myLocalIP);

  server.on("/", [](){
    String s = (const __FlashStringHelper *)MAIN_page;  // 웹페이지 HTML 불러오기
    
    strTemp = (String)t;                                // 온습도값을 문자열로 변환
    strHumi = (String)h;
    
    s.replace("@@TEMP@@", strTemp);                     // 변환된 온습도 수치를 HTML placeholder와 교체
    s.replace("@@HUMI@@", strHumi);  

    server.send(200, "text/html", s);                   // HTML 코드를 반환
  });      

  server.on("/temperature", [](){
    server.send(200, "text/plain", String(t).c_str());
  });     

  server.on("/humidity", [](){
    server.send(200, "text/plain", String(h).c_str());
  });
  
  server.onNotFound([]() {
    server.send(404,"text/plain", "404: Not found");
  });
  
  server.begin();
}

void loop() {
  ArduinoOTA.handle();
  server.handleClient();
  delay(0);

  getData();
}

// 설정된 interval 간격마다 온습도 확인
bool getData() {
  unsigned long curMills = millis();
  if (curMills - preMills >= interval)
  {
    preMills = curMills;
    h = dht.readHumidity();         // 상대습도 읽기
    t = dht.readTemperature();      // 섭씨온도 읽기

    // Check if any reads failed and exit early (to try again).
    if (isnan(h) || isnan(t)) {
      Serial.println(F("Failed to read from DHT sensor!"));
    return false;
    }
  }
  return true;
}

getData()라는 사용자 함수는 loop() 함수 내부에 있기 때문에 반복적으로 호출이 되겠죠 하지만 getData()는 호출이 되면 마지막으로 getData() 함수가 응답을 했던 시간과 현재 시간을 계산해서 그 간격이 "interval = 5000" 그러니까 5초가 경과했을 때만 내부 명령이 실행되고 그렇지 않으면 바로 종료됩니다. 한마디로 5초에 한 번씩 dht센서로부터 온도와 습도 값을 읽어오라는 뜻이겠죠. 이렇게 읽어온 온습도 값은 전역 변수인 "t", "h"에 각각 저장됩니다.

52~62행에는 웹클라이언트의 요청에 대응할 함수가 추가되었습니다. 앞서 살펴본 HTML 코드에서 자바스크립트가 10초에 한 번씩 "/temperature", "/humidity" 주소를 요청했을 때 실행되는 함수입니다. 마지막 "server.onNotFound"는 정의되지 않은 주소를 요청했을 때 404 에러를 반환하기 위한 부분입니다.

"/temperarue" 주소가 요청되었다면 "server.send(200,"text/plain", String(t).c_str());" 명령이 실행되는데 전역 변수 "t"를 문자열로 변환해서 웹페이지로 전달합니다. 다시 말해 새로 측정된 온도 수치를 웹 클라이언트로 문자열 형태로 전달하는 것이죠. 참고로 이번 코드에서의 또 다른 차이점은 바로 람다 함수를 사용했다는 것인데 원래 온도 값을 요청하는 이 코드를 람다 함수 없이 표현했다면 아래와 같이 분리된 형태가 될 것입니다. 하지만 람다 함수의 표현을 사용하여서 코드를 좀 더 심플하게 보이도록 하고 있습니다.

void readTemperature() {
	server.send(200, "text/plain", String(t).c_str());
}

server.on("/temperature", readTemperature);

아제 코드를 업로드 해보면 10초에 한 번씩 측정값이 자동으로 업데이트되는 것을 확인할 수 있습니다. 

 

끝!

반응형

댓글