Подключаем к ESP8266 датчик SenseAir S8-0053
Сегодня ко мне приехал новый датчик измерения уровня CO2: SenseAir S8-0053.
Читал о нем достаточно, что бы предположить, что он будет лучше, чем имеющийся у меня на данный момент MH-Z19B, который и сам по себе неплох, по крайней мере уровень CO2 он действительно измеряет, а не угадывает, как делают многие более бюджетные варианты. К сожалению его функция автокалибровки, если не отключить ее вовремя, через некоторое время благополучно начнет занижать полученные значения, поэтому приходится либо проветривать чаще, либо жить с теми значениями, которые он выдает. Автокалибровка в S8 устроена по другому, и по даташиту она учитывает последние 15 дней измерений (а по факту отвечает, что 180 часов), что в свою очередь, при условии обычного проветривания помещения, уже будет выдавать более правдивые значения гораздо чаще.
Но в общем датчик то приехал, а как оказалось библиотек для него в интернет не подвезли еще. Поэтому поискав некоторое время, понял, что прийдется писать под него обработчик самому. Был конечно вариант использовать ESPEasy, в котором, как я понял, поддержка этого датчика присутствует, но это значит мне потребуется еще одна ESP, а это не рационально, учитывая, что у меня практически без дела запущен D1 Mini для проброса мультикастов от Xiaomi Gateway в MQTT.
В результате, практически на коленке был написан такой скетч. Его основная задача была дать мне понять, смогу с работать с этим датчиком или нет, поэтому данные в нем никуда не передаются, а просто выводятся на консоль, для тестов. Тем не менее, возможно кому-нибудь это будет интересно или полезно, как база для своих дальнейших проектов.
#include <ESP8266WiFi.h>
#include <SoftwareSerial.h>
#include <arduino_secrets.h>
#include "arduino_secrets.h"
const char* ssid = SECRET_SMARTHOME_WIFI_SSID;
const char* password = SECRET_SMARTHOME_WIFI_PASSWORD;
#define D7 (13)
#define D8 (15)
SoftwareSerial s8Serial(D7, D8);
int s8_co2;
int s8_co2_mean;
int s8_co2_mean2;
float smoothing_factor = 0.5;
float smoothing_factor2 = 0.15;
byte cmd_s8[] = {0xFE, 0x04, 0x00, 0x03, 0x00, 0x01, 0xD5, 0xC5};
byte abc_s8[] = {0xFE, 0x03, 0x00, 0x1F, 0x00, 0x01, 0xA1, 0xC3};
byte response_s8[7] = {0, 0, 0, 0, 0, 0, 0};
const int r_len = 7;
const int c_len = 8;
boolean wifi_reconnect() {
Serial.printf("\nConnecting to %s ", ssid);
WiFi.hostname("TestUnit");
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
Serial.printf("\nConnected to the WiFi network: %s\n", ssid);
return true;
}
void s8Request(byte cmd[]) {
s8Serial.begin(9600);
while(!s8Serial.available()) {
s8Serial.write(cmd, c_len);
delay(50);
}
int timeout=0;
while(s8Serial.available() < r_len ) {
timeout++;
if(timeout > 10) {
while(s8Serial.available()) {
s8Serial.read();
break;
}
}
delay(50);
}
for (int i=0; i < r_len; i++) {
response_s8[i] = s8Serial.read();
}
s8Serial.end();
}
unsigned long s8Replay(byte rc_data[]) {
int high = rc_data[3];
int low = rc_data[4];
unsigned long val = high*256 + low;
return val;
}
void co2_measure() {
s8Request(cmd_s8);
s8_co2 = s8Replay(response_s8);
if (!s8_co2_mean) s8_co2_mean = s8_co2;
s8_co2_mean = s8_co2_mean - smoothing_factor*(s8_co2_mean - s8_co2);
if (!s8_co2_mean2) s8_co2_mean2 = s8_co2;
s8_co2_mean2 = s8_co2_mean2 - smoothing_factor2*(s8_co2_mean2 - s8_co2);
Serial.printf("CO2 value: %d, M1Value: %d, M2Value: %d\n", s8_co2, s8_co2_mean, s8_co2_mean2);
return;
}
void get_abc() {
int abc_s8_time;
s8Request(abc_s8);
abc_s8_time = s8Replay(response_s8);
Serial.printf("ABC Time is: %d hours\n", abc_s8_time);
return;
}
void setup() {
Serial.begin(115200);
delay(10);
if(WiFi.status() != WL_CONNECTED) {
wifi_reconnect();
}
get_abc();
}
void loop() {
co2_measure();
delay(10 * 1000L);
}
Ссылка на GitHub репозиторий – тут.
Распиновка датчика выглядит вот так:
G+ – Power supply plus terminal.
Unprotected against reverse connection!
4.5 V to 5.25 V
G0 – Power supply minus terminal
Sensor’s reference (ground) terminal
UART_RxD – UART data receive line Configured as digital input
UART_TxD – UART data transmission line Configured as digital output
Соответственно, в скетче используются пины
D7 – UART_TxD
D8 – UART_RxD
Ссылка на даташит:
http://www.co2meters.com/Documentation/Datasheets/DS-S8-3.2.pdf
У людей в интернете почему то присутствуют разные последовательности для опроса, взял из документации, по первой из ссылок взял идею и реализацию усреднения значений, что бы когда вы чихнете рядом с датчиком, его не зашкалило сразу же. По второй ссылке у человека интересный проект мультисенсора, но к сожалению код целиком увидеть не удалось. По третьей ссылке можно увидеть примеры управляющих последовательностей для опроса модели и версии прошивки. Правда мне это запилить так и не удалось, поэтому в финальный вариант это не вошло.
Ну и общие мои впечатления от датчика: понравился. Удобный форм-фактор, что бы сделать из него и D1 Mini этакий бутербродик, главное не забыть отверстием для забора воздуха наверх это сделать, точно показывает (хотя конечно в качестве эталона использую показания MH-Z19B), быстро реагирует на повышение CO2 около него, ну и в целом как то он поприятнее вышеназванного MH-Z19B.
Ссылки на страницы, которые были полезны при изучении процесса работы работы с этим датчиком:
https://mysku.ru/blog/china-stores/75322.html
https://www.instructables.com/id/Weather-and-Air-Quality-Monitor/
https://github.com/miaoski/esp8266-co2
https://rmtplusstoragesenseair.blob.core.windows.net/docs/Dev/publicerat/TDE2067.pdf (не без опечаток, как и положено хорошей документации)
код вызывает ряд вопросов
например
while(s8Serial.available()) {
s8Serial.read();
break;
}
почему вместо While не написать просто
if(s8Serial.available())
s8Serial.read();
?
да и внешний while(s8Serial.available() < r_len )
– что за проверка на < r_Len, в чем ее физический смысл?
по первому вопросу -потому что читаем побитно, в случае if мы прочитаем только один бит
по второму из той же серии – читаем столько сколько мы ожидаем получить
А где вы взяли адреса регистров, номера команд и вообще особенности протокола? Везде пишут уже готовый код, а откуда берутся данные нигде не написано
Не самый оперативный ответ, но все таки отвечу: в конце статьи есть ссылка на документацию от производителя, в этой пдфке все расписано и даны примеры
https://rmtplusstoragesenseair.blob.core.windows.net/docs/Dev/publicerat/TDE2067.pdf