mrWeles

mrWeles

Пикабушник
Дата рождения: 01 мая 1989
поставил 53776 плюсов и 480 минусов
отредактировал 0 постов
проголосовал за 0 редактирований
Награды:
5 лет на Пикабу
6986 рейтинг 50 подписчиков 36 подписок 4 поста 2 в горячем

Подробный разбор датчика качества воздуха на базе ESP8266

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

http://pikabu.ru/story/universalnyiy_datchik_s_esp8288_datch...


Итак, для начала Вам понадобится:

1. ESP8266 Esp-07 - 120 руб

2. Плата-адаптер для ESP - 11 руб

3. Линейный стабилизатор напряжения на 3.3В. В моем случае XC6206 - 2.5 руб

4. Датчик СО2 MH-Z19 - 1500 руб

5. Датчик температуры и влажности DHT22 - 130 руб

6. I2C монохромный дисплей SSD1306 - 180 руб

7. Монтажная плата - 10 руб

8. Резистор 10 КОм - 0.5 руб

Итого: 1954 руб


Цены указаны ориентировочные, состоянием на декабрь-январь 2017 г.


Собираем всё в соответствии со схемой.

Подробный разбор датчика качества воздуха на базе ESP8266 Esp8266, Arduino, Mh-z19 arduino, Своими руками, Умный дом, Автоматизация, Smart home, Длиннопост

Между первым и вторым выводами DHT22 необходимо припаять резистор на 10 КОм. Без него скетч будет выдавать ошибку.

Обязательно обратите внимание на то что ESP8266 питается от 3.3В, а в качестве цепи питания схема использует 5В, для того чтобы ESP не сгрела, необходимо впаять на обратной стороне платы адаптера, линейный стабилизатор на 3.3В и выпаять перемычку на передней части. То что нужно сдлать отмечено на картинке ниже.

Некоторые могут заметить что неплохо было бы поставить делители на входах ESP, однако как показала практика, всё работает стабильно и в оде экспериментов ни одна ESP'шка не пострадала)

Подробный разбор датчика качества воздуха на базе ESP8266 Esp8266, Arduino, Mh-z19 arduino, Своими руками, Умный дом, Автоматизация, Smart home, Длиннопост

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


Далее нам нужно подключить USB to TTL конвертер к TX, RX и GND выводам ESP.

Не забываем что TX RX подключаются зеркально, т.е. TX ESP к RX конвертера и наоборот.

При загрузке скетча GPIO0 должен быть подключен к земле, это переводит ESP в режим загрузки.


Перейдем к скетчу.


Подключение библиотек:


extern "C" { //Это нужно для работы таймера

#include "user_interface.h"

}

#include <DHT.h> //Библиотека для работы с датчиком температуры/влажности

#include <SoftwareSerial.h> //Программный UART для MH-Z19

#include <ESP8266WiFi.h> //Работа с Wi-Fi

#include <WiFiUdp.h> //Работа с UDP пакетами

#include <EEPROM.h> //Эмуляция EEPROM на флешке для хранения статистики

#include <Wire.h> //I2C шина для подключения дисплея

#include "SSD1306.h" //Библиотека для работы с дисплеем

#define DHTPIN 13 //Говорим на какой ноге будет висеть сигнальная нога DHT22


Объявление переменных:


SoftwareSerial mySerial(14, 12); //Говорим библиотеке на каких ногах будет висеть программный UART

IPAddress ipServidor(192, 168, 1, 1); //Адрес шлюза (в моем случае роутера)

IPAddress ipCliente(192, 168, 1, 5); //Адрес нашего устройства

IPAddress Subnet(255, 255, 255, 0); //Подсеть

IPAddress broadcastIp(192, 168, 1, 255); //Адрес для широковещатльных сообщений


Здесь стоит отметить что стандартный broadcast адрес 255.255.255.255 на ESP'шке не работает.


WiFiUDP Udp; //Говорим что будем использовать UDP

SSD1306 display(0x3c, 4, 5); //Объявляем ноги на которых будет висеть дисплей

DHT dht(DHTPIN, DHT22); //Берем из define ногу для DHT22


byte cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; // команда запроса данных у MH-Z19


Для запроса данных на скорости 9600 (8 bit, stop — 1, parity — none) нужно отправить следующие девять байт:

• 0xFF — начало любой команды

• 0x01 — первый сенсор (он всего один)

• 0x86 — команда

• 0x00, 0x00, 0x00, 0x00, 0x00 — данные

• 0x79 — контрольная сумма.


В ответ придет что-то такое:

• 0xFF — начало любого ответа

• 0x86 — команда

• 0x01, 0xC1 — старшее и младшее значение (256 * 0x01 + 0xC1 = 449)

• 0x3C, 0x04, 0x3C, 0xC1 — в документации сказано, что должно приходить что-то типа 0x47, 0x00, 0x00, 0x00, но на деле приходит непонятно что.

• 0x7B — контрольная сумма.


Контрольная сумма считается следующим образом: берутся 7 байт ответа (все кроме первого и последнего), складываются, инвертируются, увеличиваются на 1: 0x86 + 0x01… + 0xC1 = 0x85, 0x85 xor 0xFF = 0x7A, 0x7A + 1 = 0x7B.


Согласно документации сенсору требуется около трех минут, чтобы выйти на рабочий режим. Первое время после включения он будет выдавать или 5000ppm, или 400ppm. После особо усердной пайки может приходить в себя несколько часов.

(скопировал с гиктаймса у Hellsy22)


unsigned char response[9]; //Сюда пишем ответ MH-Z19

unsigned int ppm = 0; //Текущее значение уровня СО2

float hum; //Текущее значение уровня влажности


float temp; //Текущее значение уровня температуры

unsigned int coCurrentHour = 0; //Значение СО2 в текущем часе

unsigned int tempCurrentHour = 0; //Значение тмпературы в текущем часе

unsigned int humidityCurrentHour = 0; //Значение влажности в текущем часе

const char *ssid = "имя_вашей_сети_в_кавычках";

const char *password = "пароль_от_вашей_сети_в_кавычках";

unsigned int localPort = 1900; //Порт по которому мы будем рассылать данные

long t = 0; //Счётчик

int displayClk = 0; //Счётчик

Для того чтобы хранить статистику мы должны записывать данные за последние 24 часа по каждому параметру в ОЗУ и один раз в час обновлять значения в енергонезависимой памяти. Так как энергонезависимая память хранит данные побайтово, а у нас есть значения типа float и int которые занимают боьше одного байта, мы разбиваем каждое значение на старший и младший байт. При чтении из памяти производим обратную процедуру.


unsigned int coHour[250] = {0}; //Масив с суточной статистикой СО2

unsigned int tempHour[250] = {0}; //Масив с суточной статистикой температуры

unsigned int humidityHour[250] = {0}; //Масив с суточной статистикой влажностиbyte coHigh[26]; //Массив старших байтов значения СО2

byte coLow[26]; //Массив младших байтов значения СО2

byte tempHigh[26]; //Массив старших байтов значения температуры

byte tempLow[26]; //Массив младших байтов значения температуры

byte humidityHigh[26]; //Массив старших байтов значения влажности

byte humidityLow[26]; //Массив младших байтов значения влажности

Блок Setup это что-то вроде автозагрузки в ПК, весь хранящийся здесь код выполняется один раз при загрузке устройства.


void setup() {

Serial.begin(9600); //Запускаем аппаратный UART

mySerial.begin(9600); //Запускаем программный UART

WiFi.begin(ssid, password); //Задаем параметры работы Wi-Fi

WiFi.mode(WIFI_STA);

WiFi.config(ipCliente, ipServidor, Subnet);

Udp.begin(localPort); //Запускаем UDP

EEPROM.begin(512); //Задаем размерность энергонезависимой памяти

display.init(); //Инициализируем дисплей

display.flipScreenVertically(); //Переворачиваем координатную сетку дисплея

dht.begin(); //Подключаем датчик температуры и влажности

delay(2000); //Ждём

В случае с ESP8266 иногда нужно намеренно делать задержки в выполнении программы чтобы успел отработать Wi-Fi стек и все фоновые процессы

for (unsigned int h = 0; h != 24; h++) { //Читаем данные из EEPROM

coHigh[h] = EEPROM.read(h);

delay(5);

coLow[h] = EEPROM.read(h + 24);

delay(5);

tempLow[h] = EEPROM.read(h + 48);

delay(5);

tempHigh[h] = 0;

humidityLow[h] = EEPROM.read(h + 72);

delay(5);

humidityHigh[h] = 0;


coHour[h] = word(coHigh[h], coLow[h]); //Собираем данные из отдельных байтов

tempHour[h] = word(tempHigh[h], tempLow[h]);

humidityHour[h] = word(humidityHigh[h], humidityLow[h]);


Udp.beginPacket(broadcastIp, localPort); //Отправляем статистику за последние сутки в UART и в сеть всем желающим

Serial.print("C"); Serial.print(h); Serial.print(":"); Serial.print(coHour[h]); Serial.print("|"); Serial.print(tempHour[h]); Serial.print("|"); Serial.print(humidityHour[h]); Serial.println("|");

Udp.write("C"); Udp.print(h); Udp.write(":"); Udp.print(coHour[h]); Udp.write("|"); Udp.print(tempHour[h]); Udp.write("|"); Udp.print(humidityHour[h]); Udp.write("|");

Udp.endPacket();

}

}

WiFiClient client;


Блок Loop представляет собой бесконечный цикл


void loop()

{

display.clear(); //Готовим дисплей к выводу текста и говорим что всё ок, но мы ждем

display.setTextAlignment(TEXT_ALIGN_LEFT);

display.setFont(ArialMT_Plain_16);

display.drawString(0, 40, "LOADING....");

display.display();

delay(5000);

mySerial.write(cmd, 9); //Запрашиваем данные у MH-Z19

memset(response, 0, 9); //Чистим переменную от предыдущих значений

mySerial.readBytes(response, 9); //Записываем свежий ответ от MH-Z19

unsigned int i;

byte crc = 0;//Ниже магия контрольной суммы

for (i = 1; i < 8; i++) crc += response[i];

crc = 255 - crc;

crc++;


String stringBr;

float prevHum = hum;

float prevTemp = temp;

hum = dht.readHumidity(); //Получаем текущую влажность

temp = dht.readTemperature(); //Получаем текущуютемпературу

int intHum = hum; //Переводим значения в int для упрощения обработки

int intTemp = temp;


//Проверяем контрольную сумму и если она не сходится - перезагружаем модуль

if ( !(response[0] == 0xFF && response[1] == 0x86 && response[8] == crc) ) {

Serial.println("CRC error: " + String(crc) + " / " + String(response[8]));

ESP.restart();

}

else {

unsigned int responseHigh = (unsigned int) response[2];

unsigned int responseLow = (unsigned int) response[3];

ppm = (256 * responseHigh) + responseLow;

Serial.print("Time: " + String(t) + " sec\t" + "CO2: " + String(ppm) + " ppm\t"); //Выводим данные на UART для отладки

Udp.beginPacket(broadcastIp, localPort); //Отправляем данные в сеть

Udp.print(t); Udp.write("["); Udp.print(ppm); Udp.write("]");

if (isnan(hum) || isnan(temp)) { //Проверяем получили ли данные температуры и влажности

Serial.println(" Data reading error!"); //Если получена ошибка то отправляем предыдущее значение в сеть


Udp.write("["); Udp.print(prevHum); Udp.write("]"); Udp.write("["); Udp.print(prevTemp); Udp.write("]");

display.setTextAlignment(TEXT_ALIGN_LEFT);

display.setFont(ArialMT_Plain_16);

display.drawString(64, 20, "DHT Data Error!"); //Выводим сообщение об ошибке на дисплей

}

else

{

//Если всё ок - выводим данные на UART и отправляем в сеть

Serial.println(" Temperature: " + String(temp) + " *C " + "Humidity: " + String(hum) + " %\t");

Udp.write("["); Udp.print(hum); Udp.write("]"); Udp.write("["); Udp.print(temp); Udp.write("]");

tempCurrentHour = (tempCurrentHour + intTemp) / 2;

humidityCurrentHour = (humidityCurrentHour + intHum) / 2;

}

Udp.endPacket();

delay(100);

}

//Считаем статистику

t = t + 2;

coCurrentHour = (coCurrentHour + ppm) / 2;

if (t == 720) {

//Если пришло время обновлять статистику, запускаем цикл и сдвигаем все значения на одно

for (unsigned int d = 0; d != 24; d++) {

if (d == 23) {

coHour[d] = coCurrentHour;

tempHour[d] = tempCurrentHour;

humidityHour[d] = humidityCurrentHour;

Serial.print("C"); Serial.print(String(d)); Serial.print(":"); Serial.print(String(coHour[d])); Serial.print("|"); Serial.print(String(tempHour[d])); Serial.print("|"); Serial.print(String(humidityHour[d])); Serial.println("|");

Udp.beginPacket(broadcastIp, localPort);

Udp.write("C"); Udp.print(d); Udp.write(":"); Udp.print(coHour[d]); Udp.write("|"); Udp.print(tempHour[d]); Udp.write("|"); Udp.print(humidityHour[d]); Udp.write("|");

Udp.endPacket();

delay(100);

coCurrentHour = ppm;

tempCurrentHour = intTemp;

humidityCurrentHour = intHum;

t = 0;

}

else {

coHour[d] = coHour[d + 1];

tempHour[d] = tempHour[d + 1];

humidityHour[d] = humidityHour[d + 1];

Serial.print("C"); Serial.print(String(d)); Serial.print(":"); Serial.print(String(coHour[d])); Serial.print("|"); Serial.print(String(tempHour[d])); Serial.print("|"); Serial.print(String(humidityHour[d])); Serial.println("|");

Udp.beginPacket(broadcastIp, localPort);

Udp.write("C"); Udp.print(d); Udp.write(":"); Udp.print(coHour[d]); Udp.write("|"); Udp.print(tempHour[d]); Udp.write("|"); Udp.print(humidityHour[d]); Udp.write("|");

Udp.endPacket();

delay(100);

}

coHigh[d] = highByte(coHour[d]);

coLow[d] = lowByte(coHour[d]);

tempHigh[d] = highByte(tempHour[d]);

tempLow[d] = lowByte(tempHour[d]);

humidityHigh[d] = highByte(humidityHour[d]);

humidityLow[d] = lowByte(humidityHour[d]);

//Записываем обновленные данные в EEPROM

EEPROM.write(d, coHigh[d]);

delay(50);

EEPROM.write(d + 24, coLow[d]);

delay(50);

EEPROM.write(d + 48, tempLow[d]);

delay(50);

EEPROM.write(d + 72, humidityLow[d]);

delay(50);

EEPROM.commit();

}

}

//Проверяем значение счётчика и выводим на дисплей текущие показания

if (displayClk == 1) {

display.clear();

display.setFont(ArialMT_Plain_24);

display.setTextAlignment(TEXT_ALIGN_CENTER);

display.drawString(64, 0, "CO2");

display.drawString(64, 35, String(ppm) + " ppm");

}

if (displayClk == 2) {

display.clear();

display.setFont(ArialMT_Plain_24);

display.setTextAlignment(TEXT_ALIGN_CENTER);

display.drawString(64, 0, "Temp");

display.drawString(64, 35, String(temp) + " °C");

}

if (displayClk == 3) {

display.clear();

display.setFont(ArialMT_Plain_24);

display.setTextAlignment(TEXT_ALIGN_CENTER);

display.drawString(64, 0, "Humidity");

display.drawString(64, 35, String(hum) + " %");

displayClk = 0;

}

display.display();


На этом пожалуй и закончу пост. Код можно скопировать загрузить в ESP, я ничего не вырезал.

Если у кого-то есть Crestron, в следующем посте могу описать модуль который парсит данные на стороне контроллера.

Если у кого-то будут предложения по оптимизации скетча - с радостью их выслушаю. В комментариях пишите какое еще устройство вы хотели бы увидеть и предлагайте варианты корпусов для текущего (чукча не творческий).

Показать полностью 2

Универсальный датчик с ESP8266, датчиками температуры, влажности, СО2 и плюшками

Универсальный датчик с ESP8266, датчиками температуры, влажности, СО2 и плюшками Esp8266, Arduino, Mh-z19 arduino, Автоматизация, Умный дом, Своими руками, Длиннопост

Доброго времени суток мои подписчики и все интересующиеся темой!


Давно хотел написать пост, но всё руки не доходили, так что бонусом предлагаю описание устройства которое под силу собрать любому пикабшнику с паяльником.


Итак, стоит начать с того что мне привалило счастье в виде контроллера автоматизации Crestron CP2e. Для тех кто не знаком с продукцией Crestron, это достаточно древний, но тем не менее пригодный для домашнего использования контроллер, который умеет управлять оборудованием по Ethernet, rs232, IR, аналоговым входам/выходам и при помощи реле.

Огромным плюсом является наличие ПО при помощи которого можно рисовать относительно красивые интерфейся для мобильным утройст, фирменных панелей и т.д.

Фото железяки прилагаю.

Универсальный датчик с ESP8266, датчиками температуры, влажности, СО2 и плюшками Esp8266, Arduino, Mh-z19 arduino, Автоматизация, Умный дом, Своими руками, Длиннопост

Пропущу автоматизацию домашнего кинотеатра, если Вам будет интересно то опишу в следующем посте.

После того как я наигрался стандартными фишками, мне захотелось сделать что-то своё, после не долгих раздумий выбор пал в сторону датчика качества воздуха, в моём случае это датчики температуры/влажности и CO2, но так как я живу на съемной квартире и ковырять стенку для прокладки кабелей и красивого монтажа мне никто не даст, датчик должен быть беспроводным.


Исходя из требований и опыта людей на хабрахабре, было решено использовать в качестве мозгов Wi-Fi модуль ESP8266, скетчи для которого можно писать на множестве языков в том числе и на С в Arduino IDE.


Датчик СО2 выбирался спонтанно, но как потом оказалось правильно. MH-Z19 умеет определять концентрацию СО2 в диапазоне от 0 до 5000 ррm (частей на миллион) на практике 1500 ppm бывает только если в комнате спит два человека и закрыты двери, поэтому если вы выберете более дешевый датчик ничего страшного не случится.

Для сомневающихся табличка из тырнета.

Универсальный датчик с ESP8266, датчиками температуры, влажности, СО2 и плюшками Esp8266, Arduino, Mh-z19 arduino, Автоматизация, Умный дом, Своими руками, Длиннопост

Датчик температуры и влажности (2 в 1) выбирался по отзывам и ширине диапазона. Я выбрал AM2302 он же DHT22 он рассчитан на измерение уровня влажности в диапазоне от 0% до 100%. При этом точность измерений находится в диапазоне 2%-5%.

Измеряет температуру в диапазоне от -40 до 125 градусов с точностью плюс-минус 0.5 градусов.

Если температура у меня дома выйдет за границы диапазона, до показания датчика это последнее о чём я буду думать)


Дисплей куплен на одном популярном китайском сайте и изначально предназнаяался для другого проекта.


Теперь о реализации.


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


Если кого-то заинтересует подробная реализация (скетч и схема) пишите в ЛС, с радостью поделюсь, а для всех остальных - результат.


Внешний вид и индикация показаний (фото засвечены лампой, дисплей на самом деле яркий)

Универсальный датчик с ESP8266, датчиками температуры, влажности, СО2 и плюшками Esp8266, Arduino, Mh-z19 arduino, Автоматизация, Умный дом, Своими руками, Длиннопост
Универсальный датчик с ESP8266, датчиками температуры, влажности, СО2 и плюшками Esp8266, Arduino, Mh-z19 arduino, Автоматизация, Умный дом, Своими руками, Длиннопост
Универсальный датчик с ESP8266, датчиками температуры, влажности, СО2 и плюшками Esp8266, Arduino, Mh-z19 arduino, Автоматизация, Умный дом, Своими руками, Длиннопост
Универсальный датчик с ESP8266, датчиками температуры, влажности, СО2 и плюшками Esp8266, Arduino, Mh-z19 arduino, Автоматизация, Умный дом, Своими руками, Длиннопост

Интерфейс в Crestron с суточной статистикой

Универсальный датчик с ESP8266, датчиками температуры, влажности, СО2 и плюшками Esp8266, Arduino, Mh-z19 arduino, Автоматизация, Умный дом, Своими руками, Длиннопост

Данные которые шлются в сеть по UDP

Универсальный датчик с ESP8266, датчиками температуры, влажности, СО2 и плюшками Esp8266, Arduino, Mh-z19 arduino, Автоматизация, Умный дом, Своими руками, Длиннопост

Посуточная статистика (рассылается каждый час и при обрыве питания)

Универсальный датчик с ESP8266, датчиками температуры, влажности, СО2 и плюшками Esp8266, Arduino, Mh-z19 arduino, Автоматизация, Умный дом, Своими руками, Длиннопост

Следующим этапом будет разработка корпуса для печати на 3d принтере и веб интерфейс чтобы при смене названия сети не нужно было заново прошивать модуль.


Спасибо тем кто дочитал, ещё раз повторюсь что готов поделиться исходниками со всеми желающими.

В комментариях жду критику, пожелания предложения и т.п.

Показать полностью 9

В тему о рабочих местах

Раз уж пошла тема о рабочих местах, выложу немного фото моих объектов.
Работаю системным интегратором, делаю из обычных домов умные, если не заминусуют запилю пару постов о своей работе, том как это делается и зачем вообще нужно)

Заказчики как правило не любят когда снимают их дома, поэтому фото не много и качество напрямую зависит от телефона который был у меня в период работы на объекте.

Первое фото с предыдущей работы, это наверное единственный объект с которого сохранились качественные фото.

В тему о рабочих местах Рабочее место, Интегратор, Crestron, Моё, Длиннопост

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

В тему о рабочих местах Рабочее место, Интегратор, Crestron, Моё, Длиннопост

Настенная сенсорная панель Crestron. Один из способов взаимодействия пользователя с системой.

В тему о рабочих местах Рабочее место, Интегратор, Crestron, Моё, Длиннопост

Колонки Bowers&Wilkins  для тех кто любит громко, качественно и ОЧЕНЬ дорого

В тему о рабочих местах Рабочее место, Интегратор, Crestron, Моё, Длиннопост

Переговорная комната с видеостеной и системой видеоконференции.

В тему о рабочих местах Рабочее место, Интегратор, Crestron, Моё, Длиннопост
Показать полностью 5

Ищу 03.08 компанию в Питере на пару часов. Комменты для минусов внутри

В понедельник еду в Питер по работе, будет пару свободных часов, хотелось-бы посмотреть город с экскурсоводом, желательно прекрасного пола, с меня приятная компания и сувенирчик из Москвы)
P.S. Картинка для привлчения внимания
Отличная работа, все прочитано!