Привет, Всем! Не прошло и полугода с момента выхода статьи про один из вариантов реализации «Умного Дома», как хочется представить Вам результат создания устройства контроля климатических параметров для Умного Дома, типа “ALLinONE” или датчик «Всего»!
Знакомьтесь, VisEl Sensor! Устройство в одном корпусе, измеряет температуру, влажность, давление, концентрацию углекислого газа, уровень освещенности, да еще и имеет датчик движения!



Тогда



Примерно спустя месяц после написания статьи, начали одолевать вопросы, вроде – «Ну все равно, это же какая-то самодеятельность и колхоз, где ж найти оборудование, которое выполнит все мои требования и хотелки, типа “AllinOne”, да еще и выглядеть будет эстетично?». И касался этот вопрос в основном оборудования, которое и выполняет функции измерения и контроля параметров обстановки в помещении.

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

Ну правда же, допустим мы хотим контролировать в помещении температуру, влажность… предложений по таким датчикам на рынке много. А если еще и освещенность…ну если постараться, то найдем. Ну а если еще и датчик движения захотим? – Конечно есть, и много вариантов. Хорошо, ну а концентрацию вредных примесей в воздухе контролировать? – Да и такие есть (правда настольные).
В итоге для полноценной реализации Умного Дома, мы нахлобучиваем на стене кучу датчиков на квадратный метр, от разных производителей, имеющих разные способы взаимодействия и различающихся по внешнему виду, и радуемся «Ну вот работает же, круто!»

А хочется установить что-то одно! Которое будет делать – все!

Вот и пришла идея создать некое устройство-датчик, в едином законченном решении, которое будет выполнять практически все функции необходимых измерений и иметь внешне привлекательный эстетичный вид.

Размышления


Каким же должен быть датчик «Всего»?

  1. Корпус – самая главная составляющая устройства. Ведь конечный потребитель, не вдаваясь в детали начинки, будет видеть именно красивую коробочку, вписывающуюся в интерьер помещения. Само устройство логично размещать где-нибудь на стене помещения, чтобы минимизировать взаимодействие с ним (так сказать, установить и забыть). Где ж взять корпус? Правильно – нигде! Только создать индивидуальный, под собственные нужды, а не выпиливать отверстия и не прикручивать болтиками.
  2. Коммуникация – ну проводную можно сделать, используя какой-нибудь протокол… Стоп! Так у нас же чуть ли не в каждой квартире или доме, уже есть прекрасный способ коммуникации устройств, как во внутренней сети, так и во всём Интернете… WiFi. Вот его и возьмём. Ну соединились мы с устройством по ВайФаю, а как оно данные то передавать будет? Гуглим – «Самый распространенный протокол взаимодействия устройств в Интернете Вещей» — MQTT – легко и… решено короче!
    А вот питание решено сделать все-таки проводным. Во-первых, устройство с таким количеством датчиков на борту, потребляет многовато для автономного питания, во-вторых саму идеологию «правильного» построения системы умного дома, нужно рассматривать на этапе строительства или капитального ремонта помещения, чтобы заранее предусмотреть возможность питания и коммуникации отдельных устройств.
  3. Начинка – Нам же, нужно связать воедино несколько датчиков, различающихся по способам коммуникации, в одном месте и передать вот это вот всё, в другое, да еще и по WiFi… Вот тут нам поможет ESP8266! Отличное готовое решение с кучей возможностей на борту маленькой платки, в том числе реализации логики взаимодействия с датчиками. А сами датчики то, где взять? Пфф, Arduino сообщество в тандеме с Алиэк…… сс, решат все наши проблемы!

Сейчас


Реализация


Корпус
Так как готового решения корпуса для нашего устройства нет, решено изготовить его самим. И учитывая стоимость изготовления при помощи литья пластмассы под давлением в промышленных условиях, или литья в силиконовую форму (сделал кучу запросов в компании и очумел от цен), остановимся на 3D печати.
С помощью 3D принтера и небольшой постобработки изделия, можно изготовить очень даже хороший и готовый вариант корпуса.



Вот только при выборе пластика остановимся на PLA, потому что ABS при печати, имеет много минусов в виде деформации стенок при тепловой усадке с разницей в миллиметры, когда нам нужна точность в доли миллиметра. Да и PLA крепче, экологичнее, и все такое)))




Коммуникация
Как и было выше описано, ESP8266 обладает обширными возможностями коммуникации с другими устройствами.
Тут есть SPI, I?C, I?S, UART, 10-bit АЦП. И для того что бы собрать все датчики воедино и не придумывать схемы разделения питания, установку разъемов, есть готовое решение ESP8266 на плате проекта NODEMCU.
Здесь и разъем microUSB UART, и разделенные выходы питания для устройств с разным значением напряжения, и вход основного питания с широким диапазоном 5-20В. Что дает возможность использовать более распространенное значение питания слаботочных устройств – 12В.

Начинка
Проштудировав много datasheet-ов и рекомендаций энтузиастов решено применить в проекте такие датчики – BME280 (температура, влажность, давление) интерфейс I2C; GY-302 (BH1750) освещенность, интерфейс I2C; MH-Z14 и MH-Z19 (концентрация углекислого газа CO2) интерфейс UART; HC-SR501 и RCWL-0516 (детекторы движения).
Ремарка: датчики MH-Z14 и MH-Z19 по принципу одинаковые, отличаются только корпусом, а вот детекторы движения HC-SR501 и RCWL-0516 имеют совершенные различия в принципе действия. HC-SR501 – инфракрасный (оптический) в следствии чего сам датчик не получится скрыть и родную линзу Френеля придется разместить в видимой части корпуса. А вот датчик RCWL-0516 имеет принцип действия, основанный на эффекте Доплера (распространения волн в пространстве) и не боится препятствий в виде пластика корпуса.

Размещаем всю «Начинку» в спроектированном корпусе!



Делаем так, чтобы все заработало!


Так как у платы Nodemcu есть готовый разъем microUSB, первоначальную заливку скетча (микропрограммы платы для взаимодействия всех датчиков), производим через него, с компьютера с предустановленным комплексом Arduino IDE. Информация по Arduino IDE и особенностям подключения, есть в большом количестве в интернете. В дальнейшем, заливка нового скетча уже будет возможна через WiFi по технологии OTA, реализованным в этом же самом скетче.
Реализация коммуникации устройства с внешним миром, осуществляется по протоколу MQTT over WiFi. Устройство, в режиме издателя, периодически отправляет данные с измеряемыми параметрами, на сервер (брокер) MQTT с фиксированным адресом во внутренней локальной сети, в топики, названия которых складываются из измеряемого параметра и серийного номера устройства.
Код:
#include <Adafruit_BME280.h>
#include <BH1750.h>
#include <SoftwareSerial.h>;
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <ESP8266mDNS.h>
#include <ArduinoOTA.h>

#define SEALEVELPRESSURE_HPA (1013.25)
Adafruit_BME280 bme;

const char *ssid = "VisEl";
const char *password = "visense01";
const char *mqtt_server = "192.168.0.100";
//const char *mqtt_user = "xxxxxx";
//const char *mqtt_pass = "xxxxxx";

unsigned long pir_time;
unsigned long luxx_time;
unsigned long sens_time;
unsigned long co2_time;
boolean pir_stat;
int lux_val = 0;

WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];
int value = 0;

BH1750 lightMeter(0x23);
SoftwareSerial mySerial(13, 15);
byte cmd[9] = {0xFF,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79}; 
unsigned char response[9];

void setup(){
  Serial.begin(9600);
  mySerial.begin(9600);
  Wire.begin(14, 12);
  pinMode(4, INPUT);
  setup_wifi();
  Serial.println(F("BME280 test"));
  if (! bme.begin(&Wire)){
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    client.publish("Service", "Could not find a valid BME280 sensor, check wiring!");
    while (1);
    }
    Serial.println("normal mode, 16x oversampling for all, filter off");
    client.publish("Service", "normal mode, 16x oversampling for all, filter off");
    Serial.println("0.5ms standby period");
    client.publish("Service", "0.5ms standby period");
    client.setServer(mqtt_server, 1883);
    client.setCallback(callback);
  if (lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE)){
    Serial.println(F("BH1750 Advanced begin"));
    client.publish("Service", "BH1750 Advanced begin");
    }
  else {
    Serial.println(F("Error initialising BH1750"));
    client.publish("Service", "Error initialising BH1750");
  }
  ArduinoOTA.onStart([]() {
    String type;
    if (ArduinoOTA.getCommand() == U_FLASH)
      type = "sketch";
    else // U_SPIFFS
      type = "filesystem";

    // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
    Serial.println("Start updating " + type);
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
    else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
    else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
    else if (error == OTA_END_ERROR) Serial.println("End Failed");
  });
  ArduinoOTA.begin();
  Serial.println("OTA Ready");
  client.publish("Service", "OTA Ready");  
}

void setup_wifi() {
  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect("ESP8266Client")) {
      Serial.println("connected");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void pir(){
  if (digitalRead(4)==HIGH && pir_stat==false){
    pir_stat = true;
    Serial.println("motion");
    String pirs = String(pir_stat);
    int pirl = pirs.length() + 1;
    char pira[pirl];
    pirs.toCharArray(pira, pirl);
    client.publish("Pir", pira);
    }
  else if (digitalRead(4)==LOW && pir_stat==true){
    pir_stat = false;
    Serial.println("end motion");
    String pirs = String(pir_stat);
    int pirl = pirs.length() + 1;
    char pira[pirl];
    pirs.toCharArray(pira, pirl);
    client.publish("Pir", pira);
    }
}

void co2(){
  while (mySerial.available()) {
    mySerial.read();
    }
    delay(5);
  mySerial.write(cmd, 9);
  memset(response, 0, 9);
  mySerial.readBytes(response, 9);
  int i;
  byte crc = 0;
  for (i = 1; i < 8; i++) crc+=response[i];
  crc = 255 - crc;
  crc++;
  if ( !(response[0] == 0xFF && response[1] == 0x86 && response[8] == crc) )
  {
    Serial.println("CRC error: " + String(crc) + " / "+ String(response[8]));
    client.publish("Service", "CO2 CRC Error");
    }
    else {
    unsigned int responseHigh = (unsigned int) response[2];
    unsigned int responseLow = (unsigned int) response[3];
    unsigned int ppm = (256*responseHigh) + responseLow;
    Serial.print (ppm);
    Serial.println (" ppm");    
    String ppms = String(ppm);
    int ppml = ppms.length() + 1;
    char ppp[ppml];
    ppms.toCharArray(ppp, ppml);
    client.publish("CO2", ppp);
}
}
    
void luxx(){
  int lux = lightMeter.readLightLevel();
  if (lux != lux_val){
    lux_val = lux;
    Serial.print(lux_val);
  Serial.println(" lux");
    String luxs = String(lux_val);
  int luxl = luxs.length() + 1;
  char luxa[luxl];
  luxs.toCharArray(luxa, luxl);
  client.publish("Lux", luxa);
}
}

void sens(){
    Serial.print(bme.readTemperature());
    Serial.println(" °C");
    int tem = bme.readTemperature();
    String tems = String(tem);
    int teml = tems.length() + 1;
    char tema[teml];
    tems.toCharArray(tema, teml);
    client.publish("Temp", tema);
    
    Serial.print(bme.readPressure() / 100.0F);
    Serial.println(" hPa");
    float pres = (bme.readPressure() / 100.0F);
    String presss = String(pres);
    int presl = presss.length() + 1;
    char presa[presl];
    presss.toCharArray(presa, presl);
    client.publish("Press", presa);
    
    Serial.print(bme.readHumidity());
    Serial.println(" %RH");
    int hum = bme.readHumidity();
    String hums = String(hum);
    int huml = hums.length() + 1;
    char huma[huml];
    hums.toCharArray(huma, huml);
    client.publish("Hum", huma);
}
void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
  ArduinoOTA.handle();
  if (millis() - pir_time > 500){
  pir_time = millis();
  pir();
  }
  if (millis() - luxx_time > 1000){
  luxx_time = millis();
  luxx();
  }
  if (millis() - sens_time > 7000){
  sens_time = millis();
  sens();
  }
  if (millis() - co2_time > 10000){
  co2_time = millis();
  co2();
  }  
}


Практика



Дальше, так как в собственной системе умного дома, я использую Raspberry PI3 + OpenHab 2, опишу на их примере, как настроить сервер MQTT и получать с него информацию.
Для установки сервера MQTT на Raspberry PI3, подключаемся по SSH и вводим следующие команды:

--Обновляемся--
sudo apt-get update
sudo apt-get upgrade

--Устанавливаем репозиторий--
sudo wget http://repo.mosquitto.org/debian/mosquitto-repo.gpg.key
sudo apt-key add mosquitto-repo.gpg.key
sudo rm mosquitto-repo.gpg.key
sudo wget http://repo.mosquitto.org/debian/mosquitto-stretch.list
sudo apt-get update

--Устанавливаем сервер MQTT--
sudo apt-get install mosquito

--Устанавливаем клиент MQTT--
sudo apt-get install mosquitto mosquitto-clients


По окончании установки, мы получаем уже работающий сервер (брокер) MQTT.
Далее в OpenHab в PaperUI, во вкладке Add-ons устанавливаем Binding MQTT.



По окончании установки, мы получаем уже работающий сервер (брокер) MQTT.

Далее в OpenHab в PaperUI, во вкладке Add-ons устанавливаем Binding MQTT.

После установки открываем конфигурационный файл:
sudo nano /etc/openhab2/services/mqtt.cfg

В этом файле нужно раскомментировать и заполнить строчки с указанием имени, адреса и типа подключения к серверу, пример:
ssl://localhost:8883
mybroker.url=tcp://localhost:1883

Если предусматривается аутентификация доступа к серверу, то в файле исправляем строчки с указанием user и password. В нашем скетче, для доступа по паролю, нужно также раскомментировать строчки user и password.

Так как автоматическое создание Things и Items в PaperUI при помощи Binding MQTT не происходит, нужные нам Items, мы создаем при помощи создания файла Items.
Для этого блокноте или Notepad++ создаем файл default.items и прописываем примерное содержимое:
Number ESP_c02 "CO2"{ mqtt="<[mybroker:CO2:state:default]" }
Number ESP_lux "Освещенность"{ mqtt="<[mybroker:Lux:state:default]" }
Number ESP_temp "Температура"{ mqtt="<[mybroker:Temperature:state:default]" }
Number ESP_hum "Влажность"{ mqtt="<[mybroker:Humidity:state:default]" }
Number ESP_press "Давление"{ mqtt="<[mybroker:Pressure:state:default]" }
Number ESP_motion "Движение"{ mqtt="<[mybroker:Pir:state:default]" }


После таких манипуляций в списке Items появятся наши, уже с данными от брокера.
Дальше мы уже настраиваем внешнее отображение созданных нами Items в виде плитки dashboard.

Вот и все!



Еще несколько фото устройства в интерьере:





Надеюсь данная статья была Вам интересна.
Более подробную информацию по устройству, модификациям и пр…. можно найти на сайте проекта VisEl.

Комментарии (0)