
ESP32와 MH-Z19B 이산화 탄소 농도 센서를 사용해서 대기중 이산화탄소 농도를 확인하는 방법
데이터시트
https://www.winsen-sensor.com/d/files/infrared-gas-sensor/mh-z19b-co2-ver1_0.pdf
MH-Z19B 를 사용해서 이산화 탄소 농도를 읽는 방법은 크게 PWM 방식과 UART 를 사용한 두가지 방식이 있다.

PWM 방식은 MH-Z19B 가 출력하는 PWM을 1004ms 동안 read 한 뒤 HIGH 지속 시간과 LOW 지속 시간을 구분해 확인할 수 있다.

UART 를 사용한 방식은 지정된 커맨드를 보낸 뒤 MH-Z19B 가 Response하는 데이터를 확인 후 연산해서 확인할 수 있다.
UART를 사용하는 방법이 좀 더 편히 확인 할 수 있다.
URAT를 사용할 수 없거나 GPIO를 한개만 사용해야 하는 경우 PWM 을 사용하는게 좋을것 같다.
아래는 UART 를 사용한 회로도와 소스코드 예제

회로를 구성할 때 반드시 주의해야할 점이 있다.
5V 핀에서 5V 전압이 나오는지 반드시 확인 후 연결해야한다.
5V 단자로 출력은 없고 외부에서 입력 전원만 받는 핀 일 수 있다
2025.04.22 - [분류 전체보기] - ESP32-S3-DevkitC-1 5V 핀 출력 활성화

MH-Z19B 는 동작전압이 5V이기 때문에 5V를 공급 해줘야 한다. UART는 3.3V라서 다행.
외부에서 5V 를 공급해준다면 별다른 문제가 없지만 별다른 장치가 없이 진행 하라면 내 블로그에 ESP32 S3 5V 설정 관련 글을 참고.
2025.04.22 - [분류 전체보기] - ESP32-S3-DevkitC-1 5V 핀 출력 활성화
소스코드 중 일부
int mh_z19_read_co2()
{
uint8_t cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
uart_write_bytes(MH_Z19_UART_NUM, (const char *)cmd, sizeof(cmd));
uint8_t response[9];
int len = uart_read_bytes(MH_Z19_UART_NUM, response, sizeof(response), 100 / portTICK_PERIOD_MS); // 약 100ms 대기
if (len == 9 && response[0] == 0xFF && response[1] == 0x86)
{
/////RAW DATA 출력
//
printf("RAW DATA: ");
for (int i = 0; i < len; i++)
{
printf("%02X ", response[i]);
}
printf("\n");
//
//////////
int co2 = response[2] * 256 + response[3];
return co2;
}
else
{
ESP_LOGW(TAG, "잘못된 응답 수신 또는 타임아웃");
return -1;
}
}
위 함수에서 RAW DATA 를 출력 후 측정 값을 return 하게 된다.


RAW DATA 받은 정보를 데이터시트에서 알려준대로 연산해보면
0x02 * 256 + 0x49
2 * 256 + 73 = 586 ppm 을 확인할 수 있고
소스코드에서
int co2 = response[2] * 256 + response[3];
이렇게 연산해서 Return 한다.
전체 소스코드
#include "driver/uart.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/portmacro.h"
#include "esp_timer.h"
#define MH_Z19_UART_NUM UART_NUM_1
#define MH_Z19_TXD_PIN 12
#define MH_Z19_RXD_PIN 13
#define MH_Z19_UART_BUF_SIZE (1024)
static const char *TAG = "MH-Z19";
void mh_z19_init()
{
const uart_config_t uart_config = {
.baud_rate = 9600,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
};
uart_driver_install(MH_Z19_UART_NUM, MH_Z19_UART_BUF_SIZE * 2, 0, 0, NULL, 0);
uart_param_config(MH_Z19_UART_NUM, &uart_config);
uart_set_pin(MH_Z19_UART_NUM, MH_Z19_TXD_PIN, MH_Z19_RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
}
int mh_z19_read_co2()
{
uint8_t cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
uart_write_bytes(MH_Z19_UART_NUM, (const char *)cmd, sizeof(cmd));
uint8_t response[9];
int len = uart_read_bytes(MH_Z19_UART_NUM, response, sizeof(response), 100 / portTICK_PERIOD_MS); // 약 100ms 대기
if (len == 9 && response[0] == 0xFF && response[1] == 0x86)
{
/////RAW DATA 출력
//
printf("RAW DATA: ");
for (int i = 0; i < len; i++)
{
printf("%02X ", response[i]);
}
printf("\n");
//
//////////
int co2 = response[2] * 256 + response[3];
return co2;
}
else
{
ESP_LOGW(TAG, "잘못된 응답 수신 또는 타임아웃");
return -1;
}
}
void app_main(void)
{
mh_z19_init();
while (1)
{
int co2 = mh_z19_read_co2();
if (co2 > 0)
{
ESP_LOGI(TAG, "CO2 농도: %d ppm", co2);
}
// 약 1초 대기 (esp_timer 사용)
esp_rom_delay_us(1000 * 1000); // 1000ms = 1초
}
}
'ESP32 > ESP32-S3' 카테고리의 다른 글
| ESP-IDF ESP32 (ESP32-S3) 스태핑 모터 RMT 제어 DM556 모터 드라이버 (5) | 2025.04.20 |
|---|---|
| ESP32 S3 디버그/업로드(USB OTG) 포트 시리얼 메세지 출력하는법 (0) | 2024.12.13 |