ESP32

ESP32 Arduino: Temperature, humidity and CO2 concentration web server

기하 2021. 8. 16. 04:23

이 ESP32 자습서에서는 

CO2 센서  DHT22 온도 및 습도 센서를 사용하여

클라이언트가 온도, 습도 및 CO2 측정값을 검색할 수 있도록

API를 노출하는 HTTP 웹 서버를 개발하는 방법을 확인할 것 입니다. 

테스트는 ESP32 개발 보드에  통합된  DFRobot의 ESP32 모듈을 사용하여 수행되었습니다  .

소개

이러한 측정은 두 개의 별개의 센서를 사용하여 주변 환경에서 수집됩니다.

웹 서버와 관련하여 여기 에서 찾을 수 있는 비동기 HTTP 웹 서버 라이브러리를 사용합니다 . 

이전 자습서에서 다루었듯이 이 라이브러리를 사용하면 비동기 HTTP 웹 서버를 설정할 수 있습니다.

 

즉, 원래 ESP8266 웹 서버에서와 같이 들어오는 클라이언트를 처리하기 위해

일부 개체를 주기적으로 폴링할 필요가 없습니다. 

 

이 라이브러리는 매우 다양하며 많은 기능을 제공합니다. 

그 중 일부는 이전 게시물에서 다루었고 이 기사의 끝 부분 에 있는

" 관련 게시물 " 섹션에서 목록을 확인할 수 있습니다 .

 

아직 라이브러리를 설치하고 테스트하지 않았다면

   입문자 튜토리얼 을 확인하는 것이 좋습니다.

 튜토리얼에서는 라이브러리와 해당 종속성을 설치하는 방법과 시작하는 방법을 설명합니다.

 

CO2 센서로 우리는 이전 튜토리얼  에서 다룬 DFRobot  아날로그 적외선 CO2 센서  사용할 것  입니다. 이 센서는 NDIR 기술을 기반으로 하며 여기에서 해당 Wiki를 참조할 수 있습니다 .

이전 게시물에서 자세히 설명했듯이 센서는 0.4V ~ 2.0V 범위에서 공기 중 CO2 농도에 비례하는 아날로그 전압을 출력합니다. 이러한 전압은 0ppm(백만분율)의 농도에 해당하고 각각 5000ppm.

상기 센서에 의해 출력 된 아날로그 전압을 얻기 위해, 우리는 ESP32 사용해야 않습니다  nalog에 D igital C onverter ( ADC ). 에서 설명하고있는 바와 같이  아두 이노 코어의 개방 문제는 analogRead의 우리가 사용하는 기능은 일치하지 않는 값을 반환합니다. 이것은  현재 analogRead 기능 에서 보상되지 않는 ADC의 비선형성 으로 인해 발생 합니다.

그럼에도 불구하고 단순성을 위해 CO2 센서와의 상호 작용에 대한 이전 자습서에서 했던 것처럼 0V ~ 3.3V 범위에서 ADC의 선형 동작을 가정합니다. 당연히 이것은 CO2 측정에 약간의 부정확성을 도입할 것입니다. 즉, 매우 정확한 측정이 필요한 경우 이 코드를 사용해서는 안 됩니다.

마지막으로 DFRobot DHT22 모듈을 사용하여 온도 및 습도 측정값을  얻습니다 . DHT22는 주변 환경에서 습도와 온도를 모두 측정할 수 있는 센서이며 마이크로컨트롤러와 데이터를 교환하기 위한 단일 와이어 인터페이스가 있습니다.

센서의 단일 와이어 인터페이스에 대한 하위 수준 세부 정보를 추상화하는 상위 수준 API를 사용하여 센서와 상호 작용하기 위해  ESP32와 호환되는  Arduino 라이브러리 를 사용 합니다 . Arduino IDE 라이브러리 관리자에서 설치할 수 있습니다.

센서에서 온도 측정값을 얻는 방법에 대한 자세한 자습서는  게시물을 참조하세요 . 습도 측정을 위해 이것을 확인 하십시오 .

테스트는 ESP32 개발 보드에  통합된  DFRobot의 ESP32 모듈을 사용하여 수행되었습니다  .

전자 회로도

ESP32를 센서에 연결하는 데 필요한 전자 회로도는 각각 마이크로 컨트롤러의 단일 핀에만 연결하면 되므로 매우 간단합니다. 그림 1은 다이어그램을 보여줍니다.

그림 1 - 전자 회로도.

앞서 언급했듯이 CO2 센서는 아날로그 전압을 출력하기 때문에 ESP32의 아날로그 핀에 연결해야 합니다. 반면 DHT22 센서에는 디지털 인터페이스가 있으므로 ESP32의 모든 디지털 핀에 연결할 수 있습니다.

ESP32에 연결될 각 센서의 핀은 일반적으로 이전 다이어그램에서 데이터로 레이블이 지정되지만 물리적 장치에서는 이러한 핀에 레이블이 없습니다. 따라서 우리는 연결된 전선의 색상을 고려해야 합니다.

평소와 같이 두 장치에서 빨간색 선은 VCC에 해당하고 검정색은 GND에 해당합니다. DHT22 모듈에서 녹색 와이어는 데이터 핀에 해당하고 CO2 모듈에서 파란색 와이어는 데이터 핀에 해당합니다.

다른 장치가 데이터 핀에서 다른 색상을 가질 수 있는지 확실하지 않지만 빨간색과 검은색 와이어는 각각 VCC 및 GND의 규칙을 따라야 합니다. 연결을 진행하기 전에 장치를 주의 깊게 분석하십시오.

DHT22와 CO2 센서는 공급 전압이 다릅니다. DHT22 함께 전원이 3.3 V 와 함께 CO2 센서 5 V . 사용자는 같은 저렴한 전원 사용  동시에 다른 핀에 5 V 및 3.3 V 출력 모두를 제공 할 수있다.

또한 모든 3가지 장치에 공통적인 기반이 있어야 모든 것이 예상대로 작동한다는 사실을 잊지 마십시오.

포함 및 전역 변수

가장 먼저 해야 할 일은 코드가 작동하는 데 필요한 모든 포함을 작성하는 것입니다. 우리는 많은 다른 기능을 사용하고 있기 때문에 3개의 다른 라이브러리를 포함해야 합니다.

ESP32를 WiFi 네트워크에 연결하여 클라이언트가 웹 서버에 연결할 수 있도록 하려면 WiFi.h 라이브러리 가 필요합니다 .

1 #include "WiFi.h"

서버를 설정하려면 서버 경로 및 기능을 구성하기 위해 인스턴스화할 클래스를 노출하는 ESPAsyncWebServer.h 라이브러리 를 포함해야 합니다.

1 #include "ESPAsyncWebServer.h"

그런 다음 DHT22 와 상호 작용할 수 있는 매우 간단한 API를 노출하는 클래스에 액세스할 수 있는 DHTesp.h 라이브러리 가 필요합니다.  이 클래스는 내부에서 사용하는 단일 와이어 프로토콜의 하위 수준 세부 정보를 추상화합니다.

1 #include "DHTesp.h"

이미 언급했듯이 이 가스의 공기 농도에 비례하는 아날로그 전압을 단순히 출력하기 때문에 CO2 센서와 상호 작용하기 위해 라이브러리가 필요하지 않습니다.

전역 변수 선언으로 이동하여 CO2 센서에 연결할 아날로그 핀의 번호를 변수에 저장하므로 나중에 다른 핀을 사용하고 싶을 때 쉽게 변경할 수 있습니다.

나는 핀 35를 사용할 것이지만 아날로그 전압 측정을 지원하는 한 원하는 경우 다른 것을 사용할 수 있습니다. 지원되는 핀에 대한 자세한 내용은 여기 에서 확인할 수 있습니다 . ESP32에는 2개의 ADC가 있고 18개의 측정 채널을 지원하지만 두 번째 아날로그-디지털 변환기( ADC2 ) 의 채널은 WiFi 드라이버가 시작될 때 사용할 수 없습니다[1]. 우리의 경우입니다.

1 int analogPin = 35;

동일한 접근 방식에 따라 DHT22 센서에 연결된 핀 번호를 유지하기 위한 전역 변수도 선언합니다. 이 경우 DHT22와 ESP32 간의 통신 프로토콜이 디지털이므로 아날로그 핀을 사용할 필요가 없습니다.

1 int dhtPin = 27;

DHT22 핀 번호 외에, 우리는 또한 클래스의 객체 필요  DHTesp 우리는 센서 인터페이스 초기화를 모두 사용하고, 온도 및 습도에서 측정을 얻을 것이라는이다.

1 DHTesp dht;

이미 언급했듯이 서버를 구성하는 데 사용할 AsyncWebServer 클래스의 개체도 필요합니다  .

이 클래스의 생성자는 서버가 수신할 포트 번호를 입력으로 받습니다. 기본 HTTP 포트인 포트 80 을 사용할 것입니다. 이 값은 정수로 전달됩니다.

1 AsyncWebServer server(80);

전역 변수 선언을 완료하려면 ESP32가 연결할 WiFi 네트워크의 자격 증명이 필요합니다. 보다 정확하게는 네트워크 이름( SSID )과 비밀번호 가 필요 합니다.

1
2
const char* ssid = "YourNetworkName";
const char* password = "YourNetworkPass";

CO2 측정 기능

CO2 센서와 상호 작용하기 위해 라이브러리를 사용하지 않기 때문에 아날로그 전압을 CO2 농도로 변환하는 데 필요한 계산을 직접 코딩해야 합니다. 따라서 우리는 이 논리를 함수에 캡슐화할 것입니다.

함수 getCo2Measurement 를 호출하고  센서에서 측정한 CO2 농도와 함께 정수 값을 반환합니다.

1
2
int getCo2Measurement() {
// Measurement processing code
}

함수 구현에서 사용할 코드는 기본적으로 이전 자습서 에서 다룬 코드입니다 .

먼저 센서에서 출력되는 아날로그 전압을 얻습니다. analogRead 함수 를 호출 하고 센서에 연결된 아날로그 핀의 번호를 입력으로 전달하여 수행합니다. 여기에서 사용할 analogPin 이라는 변수에 핀 번호가 저장되어 있음을 기억 하십시오.

1 int adcVal = analogRead(analogPin);

ESP32 ADC의 기본 작동 모드를 사용하고 있기 때문에 12  비트 너비로 작동한다는 의미 입니다. 이것은 analogRead 함수 호출이 0 에서 4095 사이의 값을 반환 함을 의미합니다 .

0V와 3.3V의 전압 값 사이에서 ADC의 선형 동작을 가정하면 간단한 비율로 전압을 얻을 수 있습니다.

그럼에도 불구하고  ADC의 이 선형 동작은 사실이 아니며 작성 시점에 AnalogRead 기능 구현 에서 보상되지 않는다는 점을 이전 자습서 에서 기억하십시오 .

1 float voltage = adcVal * (3.3 / 4095.0);

사용된 CO2 센서는 자체 점검 과정에서 문제가 감지되면 0V의 아날로그 전압을 출력하므로 조건부 블록에서 해당 시나리오를 설명 하고 검증되면 -1 결과를 반환 합니다.

그렇게 하면 이 기능을 사용할 때 상위 애플리케이션 계층에서 일부 오류 검사를 수행하고 센서가 올바르게 작동하지 않는 경우 클라이언트에 메시지를 반환할 수 있습니다.

마찬가지로, 반환된 전압이 0.4V 미만이고 0V보다 큰 경우도 처리합니다. 이는 센서가 여전히 예열 단계에 있음을 의미하며 Wiki 제품에 따라 3분이 소요 됩니다. 이 경우 -2  을 반환 합니다.

1
2

4
5
6
7
8
9
10
11
12
if (voltage == 0)
{
    return -1;
}
else if (voltage < 0.4)
{
    return -2;
}
else
{
    // Measurement handling
}

전압 측정값이 0.4V보다 큰 경우 CO2 농도로 변환합니다. 먼저 0.4V는 0ppm의 농도에 해당하므로 측정값에서 0.4V 예열 임계값을 제거합니다.

1 float voltageDiference = voltage - 0.4;

그런 다음 센서는 0.4V ~ 2.0V에서 전압과 CO2 농도 사이에 선형 관계가 있으므로 다른 비율을 적용하여 백만 분율 ( ppm ) 단위의 농도 값을 얻습니다 .

1 return (int) ((voltageDiference * 5000.0) / 1.6);

전체 기능 코드는 아래에서 볼 수 있습니다.

1
2

4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int getCo2Measurement() {


  int adcVal = analogRead(analogPin);


  float voltage = adcVal * (3.3 / 4095.0);


  if (voltage == 0)
  {
    return -1;
  }
  else if (voltage < 0.4)
  {
    return -2;
  }
  else
  {
    float voltageDiference = voltage - 0.4;
    return (int) ((voltageDiference * 5000.0) / 1.6);
  }
}

설정 코드

이제 우리는 일부 인터페이스를 초기화하고 웹 서버를 구성하는 Arduino 설정 기능으로 이동합니다.

먼저 DHT22 센서와의 인터페이스를 초기화합니다. 이를 수행하기 위해 이전에 선언된 DHTesp 개체 에서 설정 메서드를  호출 합니다.

이 메소드는 센서에 연결된 마이크로컨트롤러의 디지털 핀 번호를 입력으로 받습니다. 이 값이 dhtPin 이라는 전역 변수에 저장되어 있다는 것을 기억하십시오. 이 변수 는 언급된 설정 방법의 인수로 전달할 것 입니다.

1 dht.setup(dhtPin);

다음으로 직렬 인터페이스를 초기화하여 코드에서 메시지를 출력할 수 있습니다. 우리의 경우 WiFi 연결 중에 일부 피드백 메시지를 인쇄하는 데 사용하고 해당 연결이 설정된 후 네트워크에서 ESP32에 할당된 IP를 인쇄하여 나중에 클라이언트에서 웹 서버에 연결할 수 있습니다.

1 Serial.begin(115200);

ESP32를 WiFi 네트워크에 연결 하기 위해 WiFi.h 라이브러리를 가져올 때 사용할 수 있는 WiFi 라는 외부 변수  시작 메소드를 호출합니다 . 입력으로 이 메서드는 전역 변수에도 저장한 네트워크 이름과 암호를 받습니다.

1 WiFi.begin(ssid, password);

다음으로, WiFi 네트워크에 연결될 때까지 해당 상태에 대해 언급된 WiFi 외부 변수를 폴링합니다 . status 메서드 를 호출 하고 WL_CONNECTED 열거 값 과 비교하여 간단히 수행할 수 있습니다 .

우리가 사용할 이 폴링 접근 방식은 매우 간단하고 코드를 짧게 유지하지만 그다지 강력하지는 않습니다. 자격 증명이 잘못된 경우 오류 처리 메커니즘이 없기 때문에 프로그램이 무한 루프에 들어갑니다. 물론 실제 응용 시나리오의 경우 WiFi 네트워크에 연결하려고 할 때 발생할 수 있는 오류를 고려해야 합니다.

1
2

4
while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
}

연결이 설정되면 ESP32에 할당된 로컬 IP를 직렬 포트에 인쇄합니다. WiFi extern 변수 에서 localIP 메서드를 호출하여 이를 수행합니다 .

1 Serial.println(WiFi.localIP());

WiFi 연결 절차가 완료되면 서버 경로 구성을 처리합니다. 기본적으로 서버의 각 끝점에 대해 처리 기능을 지정해야 합니다.

각 측정 유형( 온도 , 습도  CO2)에 따라 경로가 있습니다 . 각 경로는 HTTP GET 요청만 수신합니다.

따라서 구성할 첫 번째 경로는 CO2 측정 경로입니다. " /co2 "라고 합니다.

1
2
server.on("/co2", HTTP_GET, [](AsyncWebServerRequest * request) {
//Route handling function
});

핸들링 함수 내에서 먼저 이전 섹션에서 정의한 함수를 사용하여 CO2 농도 측정을 얻습니다.

1 int measurement = getCo2Measurement();

다음으로 함수가 반환한 값에 따라 클라이언트에 반환하는 메시지를 작성합니다. 두 개의 특별한 반환 값 -1  -2 가 있음을 기억하십시오  . 이는 각각 센서 오류 또는 센서가 여전히 예열되어 있음에 해당합니다. 두 경우 모두 클라이언트에 반환할 메시지는 상황을 설명하는 문자열입니다.

유효한 측정인 경우 정수를 문자열로 변환하고 측정 단위(백만분의 일)를 추가합니다.

1
2
if(measurement == -1){message = "Sensor is not operating correctly";}
else if(measurement == -2){message = "Sensor is pre-heating";}
else {message = String(measurement) + " ppm";}

마지막으로 HTTP 상태 코드 200(성공)과 함께 메시지를 클라이언트에 반환합니다.

값 200은 센서에 결함이 있거나 여전히 예열 중일 때 논쟁의 여지가 있을 수 있습니다. 한편으로는 서버 코드가 잘 실행되었고 이는 애플리케이션 로직에서 예측된 두 가지 경우입니다. 반면에 측정값을 검색할 수 없었기 때문에 다른 상태 코드가 반환될 수 있었습니다.

당연히 이것은 이 게시물의 범위를 벗어나는 보다 개념적인 논의입니다. 따라서 이 예에서는 모든 상황에 대해 200을 사용합니다.

1 request->send(200, "text/plain", message);

다음으로 우리는 단순히 "/온도"가 될 온도 경로를 구성할 것 입니다.

1
2
server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest * request) {
//Route handling code
});

처리 코드는 CO2 경로 처리 기능보다 간단합니다. 먼저 DHTesp  객체  getTemperature 메소드를  호출하여 온도 측정값을 얻습니다  .

이 함수는 인수를 사용하지 않고 부동 소수점으로 섭씨 온도를 반환합니다.

1 float temperature = dht.getTemperature();

그런 다음 온도를 문자열로 변환하고 단위를 추가한 다음 클라이언트에 반환합니다.

1 request->send(200, "text/plain", String(temperature) + " ºC");

마지막 경로는 " /humidity " 라고 하며 습도 측정값을 클라이언트에 반환합니다.

경로 처리 기능은 이전 기능과 매우 유사합니다. 습도를 얻기 위해 다른 방법을 호출할 것으로 예상합니다. 이 방법을 getHumidity 라고  합니다 . 또한 인수를 사용하지 않으며 습도를 백분율로 반환합니다.

1
2

4
5
6
server.on("/humidity", HTTP_GET, [](AsyncWebServerRequest * request) {


    float humidity = dht.getHumidity();


    request->send(200, "text/plain", String(humidity) + " %");
});

설정을 완료하기 위해 AsyncWebServer 개체 에서 begin 메서드를  호출하여 클라이언트로부터 들어오는 요청을 수신하기 시작합니다.

1 server.begin();

최종 코드

최종 전체 코드는 아래에서 볼 수 있습니다. 서버가 비동기식으로 작동하기 때문에 클라이언트 요청을 확인하기 위해 서버를 폴링할 필요가 없기 때문에 기본 루프 기능을 비워 둘 수 있습니다. 당연히 이것은 훨씬 더 깨끗하고 효율적인 코드로 이어집니다.

1
2

4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include "WiFi.h"
#include "ESPAsyncWebServer.h"
#include "DHTesp.h"


int analogPin = 35;
int dhtPin = 27;


DHTesp dht;


const char* ssid = "YourNetworkName";
const char* password =  "YourNetworkPass";


AsyncWebServer server(80);


int getCo2Measurement() {


  int adcVal = analogRead(analogPin);


  float voltage = adcVal * (3.3 / 4095.0);


  if (voltage == 0)
  {
    return -1;
  }
  else if (voltage < 0.4)   {
    return -2;
  }


  else   {
    float voltageDiference = voltage - 0.4;
    return (int) ((voltageDiference * 5000.0) / 1.6);
  }


}


void setup() {
  dht.setup(dhtPin);


  Serial.begin(115200);


  WiFi.begin(ssid, password);


  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }


  Serial.println(WiFi.localIP());


  server.on("/co2", HTTP_GET, [](AsyncWebServerRequest * request) {
     
    int measurement = getCo2Measurement();     
     
    String message;    
     
    if (measurement == -1) {
      message = "Sensor is not operating correctly";
    }else if (measurement == -2) {
      message = "Sensor is pre-heating";
    }else {
      message = String(measurement) + " ppm";
    }    
     
    request->send(200, "text/plain", message);


  });


  server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest * request) {


    float temperature = dht.getTemperature();


    request->send(200, "text/plain", String(temperature) + " ºC");
  });


  server.on("/humidity", HTTP_GET, [](AsyncWebServerRequest * request) {


    float humidity = dht.getHumidity();


    request->send(200, "text/plain", String(humidity) + " %");
  });


  server.begin();
}


void loop() {}


코드 테스트

이전 코드를 테스트하려면 모든 전자 장치를 배선한 후 컴파일하고 ESP32 장치에 업로드하기만 하면 됩니다.

절차가 완료되면 Arduino IDE 직렬 모니터를 엽니다. WiFi 연결이 성공적으로 설정되는 즉시 그림 2와 유사한 출력이 표시되어야 합니다.

그림 2 - WiFi 네트워크에 대한 성공적인 연결.

보시다시피 네트워크에서 ESP32에 할당된 IP가 인쇄됩니다. 서버에 도달하는 데 필요하므로 해당 IP를 복사합니다.

이제 서버에 요청을 보내려면 원하는 웹 서버를 열고 주소 표시줄에 다음을 입력하고 방금 복사한 IP로 {yourDeviceIP}  변경 하고 이전에 정의한 경로 이름 중 하나로 {route} 를 변경합니다. .

1 http://{yourDeviceIp}/{route}

그림 3은 CO2 측정 경로로 요청을 보낸 결과를 보여줍니다. 환기가 잘 되는 장소에 있는지 여부에 따라 값이 달라집니다. 또한 ESP32 ADC의 선형 동작을 가정하기 때문에 측정이 약간의 부정확성에 영향을 받는다는 점을 기억하십시오.

그림 3 - ESP32 서버에서 반환된 CO2 측정값.

온도 경로를 시도하면 그림 4와 유사한 출력을 얻을 수 있습니다. 그림과 같이 경로 처리 함수에서 정의한 것처럼 단위가 추가된 온도 값을 반환합니다.

그림 4 - ESP32 서버에서 반환된 온도 측정값.

마지막으로 그림 5는 습도 끝점에 대한 결과를 보여줍니다. 동작은 비슷하며 예상대로 측정값과 단위를 얻습니다.

그림 5 - ESP32 서버에서 반환된 습도 측정값.


관련 게시물


참고문헌

[1]  http://esp-idf.readthedocs.io/en/latest/api-reference/peripherals/adc.html?highlight=analog