Previous Entry Share Next Entry
Измерение CO2 - любимая всеми тема ардуины.
snickser
Итак, следующий наш опыт будет проводиться с датчиком углекислого газа, что куплен за "баснословные" деньги ($26) в известном интернет магазине китайских товарищей.
Модель датчика MH-Z19.



Про этот датчик уже много известного написано, и что он стабильный, и что правильный, и некоторое количество примеров его использования, но некоторые вопросы для меня оставались без ответа, пока я сам не попробовал их решить. В первую очередь разговор пойдёт про PWM выход и всё с ним связанное.

По документации датчик постоянно выдаёт в PWM порт информацию о текущем значении CO2 в PPM, надо только правильно его поймать. Это мы и будем делать.
Длина циклов подробно описана в PDF на сайте разработчика, и есть пара моментов на которые я хочу обратить внимание:
а) совершенно ненужно высчитывать длину тишины, как мне кажется.
б) квантование 2мс отрезками не всегда происходит точно, бывают и половинчатые значения.

Перейдём к алгоритму описания работы.
Читаем цифровой вход, как только там появляется высокий уровень сигнала (HIGH) запоминаем этот момент (началась передача) и продолжаем читать до момента его перехода в низкий.уровень (закончилась). Что там дальше "в хвосте" нас не особо интересует... )
Вычисляем длину сигнала и вычитаем из него 2мс. Но тут оказалось очень интересное дело, лучше считать не в миллисекундах, а в микросекундах (функция micros(); ). Наведённый эффект в том, что результирующий PPM можно вычислить до единиц, и даже долей единиц. А не с шагом кратным пяти. Что позволяет лицезреть совершенно различные значения.

Следующей трудностью оказалась - синхронизация, так как сигнал подаётся всегда, и шанс попасть в середину цикла велик, приходится иной раз ждать его завершение, что несколько увеличивает время выполнения чтения, иногда и в два раза... По алгоритму мы начинаем считать сигнал только при переходе от логического нуля к единице, а если попали на единицу - ждём нуля.

Оформляем вывод значения на свой любимый TM1637 дисплей, и получаем вполне рабочий образец, в который можно прикрутить и свои штуковины, так как вызов в виде функции более прост.


Но весь этот код можно легко заменить одной строчкой, как оказывается есть специальная функция для подобного, хорошо иногда читать документацию ;)


tH = pulseIn(pwmPin,HIGH,3000000);
PPM = 5000 * (((tH)/1000.0)-2)/1000;



Но самый интересный способ - это читать датчик по прерываниям. При этом мы не занимаем время работы ардуины, нам не нужны лишние циклы и коды. Переменные сами заполняются и можно их использовать в любое удобное нам время.
Для этого объявим функцию которая будет заниматься учётом. Она просто записывает время в момент своего вызова, но чтоб понять в какой мы фазе (HIGH/LOW) делаем одно быстрое чтение.

unsigned long tC_last2=0,tC_last=0,tC=0;
void get_z19(){
  tC_last2 = tC_last;
  tC_last = tC;
  tC = micros();
  int a = digitalRead(pwmPin);
  if(a == HIGH){
//    tH = tC_last - tC_last2;
    tL = tC - tC_last;
  } else {
    tH = tC - tC_last;
//    tL = tC_last - tC_last2;
  }
}

В setup() вешаем нашу функцию на прерывание, помня что в UNO только PIN2 и PIN3 поддерживают прерывания.

  attachInterrupt(digitalPinToInterrupt(pwmPin), get_z19, CHANGE);

А в коде loop() в нужное нам место вставляем простой подсчёт по формуле

  z19_ppm = 5000 * (tH/1000.0-2)/((tH+tL)/1000.0-4);





?

Log in