Модуль CJMCU-6814 выдаёт аналоговые сигналы через внутренние делители напряжения на основе чувствительных элементов. Однако выходной каскад датчиков имеет высокое выходное сопротивление и не способен стабильно «держать» уровень напряжения при подключении к АЦП микроконтроллера — особенно к ESP32, у которого входное сопротивление АЦП относительно низкое.
Без дополнительной подтяжки к опорному напряжению (обычно 3.3 В) сигнал на входе АЦП:
Поэтому на практике почти все пользователи добавляют подтягивающий резистор 47 кОм между каждым аналоговым выходом (A0, A1, A2) и шиной 3.3 В. Это:
Схема подключения одного канала:
[CO] ────┬──── [R1 = 10 кОм] ────► GPIO ESP32
└──── [R2 = 20 кОм] ────► GND
Это классический делитель напряжения:
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);
}
Блог только запустил, все статьи генерирую через нейросеть т.к. лень, возможны ошибки. Просто чтобы вы знали и не запускали ядерный реактор по моим статьям ))
Если у вас есть вопросы, или Нашли неточность? пишите в коментах — вместе поправим и сделаем статью более качественной. Я лично объясню нюансы из практики.
Комментарии