Категории

Датчик качества воздуха CJMCU-6814: что измеряет и как подключить к ESP32

2025-09-30 08:28:00 | Статья из категории: IOT умный дом

Датчик CJMCU-6814 - качество воздуха в метеостанции на ESP32

Подключение к Arduino или ESP32 — почему нужна подтяжка

Модуль CJMCU-6814 выдаёт аналоговые сигналы через внутренние делители напряжения на основе чувствительных элементов. Однако выходной каскад датчиков имеет высокое выходное сопротивление и не способен стабильно «держать» уровень напряжения при подключении к АЦП микроконтроллера — особенно к ESP32, у которого входное сопротивление АЦП относительно низкое.

Без дополнительной подтяжки к опорному напряжению (обычно 3.3 В) сигнал на входе АЦП:

Поэтому на практике почти все пользователи добавляют подтягивающий резистор 47 кОм между каждым аналоговым выходом (A0, A1, A2) и шиной 3.3 В. Это:

Схема подключения одного канала:


[CO] ────┬──── [R1 = 10 кОм] ────► GPIO ESP32
         └──── [R2 = 20 кОм] ────► GND
Это классический делитель напряжения:
Вход: 5 В → выход: 5 В × (20 кОм / (10 кОм + 20 кОм)) = 3.33 В ✅

Про этот резистор я так и не понял но вот дальше нейросеть про резистор 47ком говорит. Скорее всего это из комментариев про калибровку показаний - -

CJMCU-6814 (A0) ───┬───[47 кОм]─── 3.3 В
                   │
                   └─── GPIO34 (ESP32)

То же самое повторяется для A1 и A2. Резисторы 47 кОм — компромисс между стабильностью сигнала и минимальным влиянием на исходное напряжение с датчика.

Без этой доработки модуль может казаться «нестабильным» или «неработающим» — хотя на самом деле проблема в согласовании импедансов, а не в самом датчике.

Короч вот схема с сайта https://forum.arduino.cc/t/cjmcu-6814-adapter-board-with-mics-6814-co-nh3-no2-sensor/595612 там плотно обсуждают этот модуль. только резисторы надо ПОДТЯГИВАТЬ К 5 ВОЛЬТАМ а не к 3

ХЗ, я замерил при такой схеме все равно на каком то выходе 4 вольта это дофига для есп. буду делитель напряжения делать

Комментарии из интернета

После добавления подтягивающих резисторов 47 кОм на каждый канал (как показано на схеме) датчик начал выдавать стабильные и логичные показания. В чистом воздухе значения составили: CO ~950/1024, NH₃ ~900/1024, NO₂ ~200/1024.

Два канала (CO и NH₃) корректно реагируют на алкоголь — аналоговые значения падают до 50/1024, что соответствует росту ppm до ~300. Канал NO₂ слабо реагирует на алкоголь (не должен), но не откликается и на газовую плиту или городской трафик — что вызывает вопросы о его калибровке или состоянии.

Согласно документации Sensirion (MiCS-6814), оптимальное значение сигнала — VCC/2 (~512/1024). Для этого я экспериментально подобрал резисторы: CO — 680 кОм, NH₃ — 470 кОм, NO₂ — 20 кОм. Получил все каналы около 500/1024 в покое.

На плате адаптера CJMCU-6814 нет встроенных нагрузочных резисторов — только резисторы нагревателей (27 Ом, 120 Ом, 820 Ом) и два нераспознанных SMD-конденсатора. Значит, подтяжка — обязательная доработка для работы с ESP32/Arduino.

Использовал модифицированную библиотеку MICS-6814-I2C (без I²C-части) для упрощения калибровки. Показания в ppm пока условны — требуют калибровки по эталонным газам. Но реакция на алкоголь подтверждает работоспособность двух каналов.


#include <Arduino.h>
#include <math.h>

// Пины подключения (измените под вашу плату)
#define PIN_NH3 A7   // NH3 (восстановительный)
#define PIN_CO  A3   // CO, этанол и др. (RED — восстановительный)
#define PIN_NO2 A10  // NO2 (OX — окислительный)

// Базовые значения АЦП в чистом воздухе (после калибровки)
uint16_t NH3_baseADC = 0;
uint16_t RED_baseADC = 0;  // для CO
uint16_t OX_baseADC  = 0;  // для NO2

// Простое усреднение по 100 отсчётам
uint16_t readADC(uint8_t pin) {
  uint32_t sum = 0;
  for (int i = 0; i < 100; i++) {
    sum += analogRead(pin);
    delay(2);
  }
  return (uint16_t)(sum / 100);
}

// Калибровка: ждём стабилизации всех каналов (~5–15 минут)
void calibrate() {
  const uint8_t STABLE_WINDOW = 10;   // секунд
  const uint8_t DELTA = 3;            // допустимое отклонение

  uint16_t buf_nh3[STABLE_WINDOW] = {0};
  uint16_t buf_red[STABLE_WINDOW] = {0};
  uint16_t buf_ox[STABLE_WINDOW]  = {0};

  uint8_t ptr = 0;
  uint32_t sum_nh3 = 0, sum_red = 0, sum_ox = 0;

  bool stable_nh3 = false, stable_red = false, stable_ox = false;

  Serial.println("Калибровка... (датчик должен быть в чистом воздухе, прогрет 5+ мин)");
  delay(2000);

  while (!(stable_nh3 && stable_red && stable_ox)) {
    delay(1000);
    Serial.print(".");

    uint16_t v_nh3 = readADC(PIN_NH3);
    uint16_t v_red = readADC(PIN_CO);
    uint16_t v_ox  = readADC(PIN_NO2);

    // Обновляем скользящее окно
    sum_nh3 = sum_nh3 - buf_nh3[ptr] + v_nh3;
    sum_red = sum_red - buf_red[ptr] + v_red;
    sum_ox  = sum_ox  - buf_ox[ptr]  + v_ox;

    buf_nh3[ptr] = v_nh3;
    buf_red[ptr] = v_red;
    buf_ox[ptr]  = v_ox;

    uint16_t avg_nh3 = sum_nh3 / STABLE_WINDOW;
    uint16_t avg_red = sum_red / STABLE_WINDOW;
    uint16_t avg_ox  = sum_ox  / STABLE_WINDOW;

    stable_nh3 = (abs((int16_t)(avg_nh3 - v_nh3)) <= DELTA);
    stable_red = (abs((int16_t)(avg_red - v_red)) <= DELTA);
    stable_ox  = (abs((int16_t)(avg_ox  - v_ox))  <= DELTA);

    // Отладка нестабильных каналов
    if (!stable_nh3) { Serial.print("(NH3:" + String(abs(avg_nh3 - v_nh3)) + ")"); }
    if (!stable_red) { Serial.print("(CO:"  + String(abs(avg_red - v_red)) + ")"); }
    if (!stable_ox)  { Serial.print("(NO2:" + String(abs(avg_ox  - v_ox)) + ")"); }

    ptr = (ptr + 1) % STABLE_WINDOW;
  }

  NH3_baseADC = sum_nh3 / STABLE_WINDOW;
  RED_baseADC = sum_red / STABLE_WINDOW;
  OX_baseADC  = sum_ox  / STABLE_WINDOW;

  Serial.println("\nКалибровка завершена!");
  Serial.print("Базовые значения: NH3="); Serial.print(NH3_baseADC);
  Serial.print(", CO="); Serial.print(RED_baseADC);
  Serial.print(", NO2="); Serial.println(OX_baseADC);
}

// Правильное вычисление Rs/R0
float getRatio(uint16_t currentADC, uint16_t baseADC) {
  if (baseADC == 0 || currentADC == 0 || currentADC >= 1023) return 1.0;
  // Rs/R0 = (Vrl / (Vcc - Vrl)) / (Vrl0 / (Vcc - Vrl0))
  // где Vrl пропорционально ADC
  float num = currentADC * (1023.0 - baseADC);
  float den = baseADC * (1023.0 - currentADC);
  if (den == 0) return 1.0;
  return num / den;
}

// Расчёт ppm по формуле из даташита MiCS-6814
float getPPM(const char* gas, float ratio) {
  if (ratio <= 0) return -1;

  if (strcmp(gas, "CO") == 0) {
    return pow(ratio, -1.179) * 4.385;
  } else if (strcmp(gas, "NO2") == 0) {
    return pow(ratio, 1.007) / 6.855;
  } else if (strcmp(gas, "NH3") == 0) {
    return pow(ratio, -1.67) / 1.47;
  } else if (strcmp(gas, "C2H5OH") == 0) {
    return pow(ratio, -1.552) * 1.622;
  }
  return -1;
}

void setup() {
  Serial.begin(115200);
  Serial.println("MICS-6814: старт");
  
  // Обязательно: дайте датчику прогреться 2–5 минут перед калибровкой!
  // Если вы уже калибровали — закомментируйте calibrate() и задайте базовые значения вручную.
  calibrate();

  // Пример ручной калибровки (если не хотите ждать):
  // NH3_baseADC = 900;
  // RED_baseADC = 950;
  // OX_baseADC  = 200;
}

void loop() {
  uint16_t nh3_adc = readADC(PIN_NH3);
  uint16_t co_adc  = readADC(PIN_CO);
  uint16_t no2_adc = readADC(PIN_NO2);

  float ratio_nh3 = getRatio(nh3_adc, NH3_baseADC);
  float ratio_co  = getRatio(co_adc,  RED_baseADC);
  float ratio_no2 = getRatio(no2_adc, OX_baseADC);

  float ppm_nh3 = getPPM("NH3", ratio_nh3);
  float ppm_co  = getPPM("CO", ratio_co);
  float ppm_no2 = getPPM("NO2", ratio_no2);

  Serial.println("=== Показания ===");
  Serial.print("NH3: "); Serial.print(nh3_adc); Serial.print(" / "); Serial.print(NH3_baseADC);
  Serial.print(" → "); Serial.print(ratio_nh3, 3); Serial.print(" → "); Serial.print(ppm_nh3, 2); Serial.println(" ppm");

  Serial.print("CO:  "); Serial.print(co_adc);  Serial.print(" / "); Serial.print(RED_baseADC);
  Serial.print(" → "); Serial.print(ratio_co, 3);  Serial.print(" → "); Serial.print(ppm_co, 2);  Serial.println(" ppm");

  Serial.print("NO2: "); Serial.print(no2_adc); Serial.print(" / "); Serial.print(OX_baseADC);
  Serial.print(" → "); Serial.print(ratio_no2, 3); Serial.print(" → "); Serial.print(ppm_no2, 2); Serial.println(" ppm");

  Serial.println();
  delay(2000);
}
Теги: #esp32

Комментарии

Джон конор 01.10.2025 18:16
какой-то бесполезный датчик. так до кучи добавил, чтобы побольше данных было на экране

Оставить комментарий

← Назад к списку статей

Важно: Блог-эксперимент

Блог только запустил, все статьи генерирую через нейросеть т.к. лень, возможны ошибки. Просто чтобы вы знали и не запускали ядерный реактор по моим статьям ))
Если у вас есть вопросы, или Нашли неточность? пишите в коментах — вместе поправим и сделаем статью более качественной. Я лично объясню нюансы из практики.

Посетителей сегодня: 0


кто я | книга | контакты без контактов

© Digital Specialist | Не являемся сотрудниками Google, Яндекса и NASA