Контроль за климатом

Источник
 
Перед выполнением данного подпроекта повторите следующие лабораторные работы: про RGB светодиод, про платформу NodeMCU и про датчик BME280
 
Общий план выполнения работы:
  1. Разработать схему устройства, способного измерять основные погодные параметры и сигнализировать о достижении какого-то из них или нескольких определённого (критического) значения.
  2. Реализовать данную схему.
  3. Написать программу для микроконтроллера.
  4. Провести тестирование устройства.

Непосредственное выполнение работы:

Необходимые материалы:

  1. Плата NodeMCU - 1 шт.
  2. Макетная плата - 1 шт.
  3. RGB - светодиод - 1 шт.
  4. Резисторы 220 Ом - 3 шт.
  5. Датчик BME280 - 1 шт.
  6. Провода: "мама - мама" - 3 шт., "папа - мама" - 4 шт. 

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

Схема подключения:

RGB - светодиод подключите к пинам D5, D6, D7, GND.

Датчик BME280 подключите к плате по схеме:
VCC -> 3V
GND -> G
SCL -> D1
SDA -> D2

Разработка скетча.

Вам необходимо скачать c официального сайта приложение Alterozoom. Чтобы получить лицензию, пишите на почту branislava.jankovic.1998@ gmail.com

Повторите эту и эту лабораторную работу.

Создаём новый скетч и называем его, наример, Climate. Проверяем правильность выбранных плат и порта.

Подключаем все необходимые библиотеки:

   #include <ARpcArduStrHlp.h>
   #include <ARpcDevice.h>
   #include <ARpcDeviceState.h>
   #include <ARpcIDevEventsCallback.h>
   #include <ARpcIMessageCallback.h>
   #include <ARpcIWriteCallback.h>
   #include <ARpcRealDeviceMessageDispatch.h>
   #include <ARpcSrvReady.h>
   #include <ARpcStarNetDevice.h>
   #include <ARpcStarNetEndPoint.h>
   #include <ARpcStarNetEndPointDevice.h>
   #include <ARpcStarNetStreamWriter.h>
   #include <ARpcStreamParser.h>
   #include <ARpcStreamWriter.h>
   #include <ARpcUuid.h>

   #include <BearSSLHelpers.h>
   #include <CertStoreBearSSL.h>
   #include <ESP8266WiFi.h>
   #include <ESP8266WiFiAP.h>
   #include <ESP8266WiFiGeneric.h>
   #include <ESP8266WiFiMulti.h>
   #include <ESP8266WiFiScan.h>
   #include <ESP8266WiFiSTA.h>
   #include <ESP8266WiFiType.h>
   #include <WiFiClient.h>
   #include <WiFiClientSecure.h>
   #include <WiFiClientSecureAxTLS.h>
   #include <WiFiClientSecureBearSSL.h>
   #include <WiFiServer.h>
   #include <WiFiServerSecure.h>
   #include <WiFiServerSecureAxTLS.h>
   #include <WiFiServerSecureBearSSL.h>
   #include <WiFiUdp.h>
   #include <ESP8266HTTPClient.h>
 
   #include <Wire.h>
   #include <BME280.h>
   #include <BME280I2C.h>
   #include <BME280I2C_BRZO.h>
   #include <BME280Spi.h>
   #include <BME280SpiSw.h>
   #include <EnvironmentCalculations.h>
 
Создаём необходимые объектыконстанты, глобальные переменные:
 
   uint16_t port=4081;
   WiFiServer server(port);
   WiFiClient client;
   IPAddress bCastSenderIp;
   WiFiUDP bCastCli;

   bool        connecting=false;
   const char* ssid="SSID";       //ssid Вашей сети
   const char* password="password"; //пароль Вашей сети

   unsigned long lastSyncMillis=0;
 
   const char *deviceName="eco_monitorunutra";  // имя устройства
   const ARpcUuid deviceId("{25068ba3-8b00-48cb-bfa7-fcee771a36a7}");
 
Описение интерфейса управления:
 
   const char *interfaceStr="<controls>"
   "<group title=\"ECO_SETTINGSunutrasnji\">"
     "<control title=\"MAX TEMP UP\" command=\"maxtempup\"/>"
     "<control title=\"MAX TEMP DOWN\" command=\"maxtempdown\"/>"
     "<control title=\"MIN TEMP UP\" command=\"mintempup\"/>"
     "<control title=\"MIN TEMP DOWN\" command=\"mintempdown\"/>"
     "<control title=\"MAX HUM UP\" command=\"maxhumup\"/>"
     "<control title=\"MAX HUM DOWN\" command=\"maxhumdown\"/>"
     "<control title=\"MIN HUM UP\" command=\"minhumup\"/>"
     "<control title=\"MIN HUM DOWN\" command=\"minhumdown\"/>"
     "<control title=\"PRESS\" command=\"press\"/>"
   "</group>"
   "</controls>";
 
   const char *sensorsDef="<sensors>"
   "<sensor name=\"temp_ns\" type=\"f32_sv_d3\"/>"
   "<sensor name=\"hum_ns\" type=\"f32_sv_d3\"/>"
   "<sensor name=\"press_ns\" type=\"f32_sv_d1\"/>"
   "</sensors>";
 

Другие глобальные переменные и объекты:
 

   float maxtemp = 30;
   float temp    = 24;
   float mintemp = 18;
   float maxhum  = 70;
   float hum     = 55;
   float minhum  = 0;
   float pressure= 1;
 
   BME280I2C bme;

   BME280I2C bme1;

   byte coldPin = D5;
   byte humPin = D7;
   byte hotPin = D6;

   byte settingStep = 2;
 
Необходимые классы интерфейса управления, объекты:
 
   class NetWriteCb
   :public ARpcIWriteCallback
   {
   public:
   void writeData(const char *d,unsigned long sz)override
     {
   client.write(d,sz);
     }
   void writeStr(const char *str)override
     {
   client.print(str);
     }
   void writeStr(const __FlashStringHelper *str)override
     {
   client.print(str);
     }
   }netCb;

   class SerialWriteCb
   :public ARpcIWriteCallback
   {
   public:
   void writeData(const char *d,unsigned long sz)override
     {
        Serial.write(d,sz);
     }
   void writeStr(const char *str)override
     {
        Serial.print(str);
     }
   void writeStr(const __FlashStringHelper *str)override
     {
        Serial.print(str);
     }
   }serialCb;
   ARpcDevice wifiDev(300,&netCb,&deviceId,deviceName);
   ARpcDevice serialDev(300,&serialCb,&deviceId,deviceName);
 
Массивы данных основных погодных параметров:
 
   float sTemp[3];
   void writeTemp() {
       sTemp[0]=mintemp;
       sTemp[1]= temp;
       sTemp[2]=maxtemp;
       wifiDev.disp().writeMeasurementB("temp_ns",sTemp,3);
   }

   float sHum[3];
   void writeHum() {
      sHum[0]=minhum;
      sHum[1]=hum;
      sHum[2]=maxhum;
      wifiDev.disp().writeMeasurementB("hum_ns",sHum,3);
   }

   float sPress[1];
   void writePress() {
   sPress[0]=pressure;
      wifiDev.disp().writeMeasurementB("press_ns",sPress,1);
   }
 
Callback-класс для обработки команд, вызывается библиотекой ARpc:
 
   class EventsCb
      :public ARpcIDevEventsCallback
         {
           public:
           explicit EventsCb(ARpcDevice *d)
             {
                 dev=d;
              }

    virtual void processCommand(const char *cmd,const char *args[],unsigned char        argsCount)
    {
      byte cmdOK = 1;
      byte cmdTemp = 0, cmdHum = 0, cmdPress = 0;
      if (strcmp(cmd,"maxtempup") ==0) { if (maxtemp < 60) { maxtemp = maxtemp +      settingStep; cmdTemp = 1; }}
      else if(strcmp(cmd,"maxtempdown") ==0) { if (maxtemp > -30) { maxtemp = maxtemp - settingStep; cmdTemp = 1; }}
      else if(strcmp(cmd,"mintempup") ==0) { if (mintemp < 60) { mintemp = mintemp + settingStep; cmdTemp = 1; }}
      else if(strcmp(cmd,"mintempdown") ==0) { if (mintemp > -30) { mintemp = mintemp - settingStep; cmdTemp = 1; }}
      else if(strcmp(cmd,"maxhumup") ==0) { if (maxhum < 100) { maxhum = maxhum + settingStep; cmdHum = 1; }}
      else if(strcmp(cmd,"maxhumdown") ==0) { if (maxhum > 0) { maxhum = maxhum - settingStep; cmdHum = 1; }}
      else if(strcmp(cmd,"minhumup") ==0) { if (minhum < 100) { minhum = minhum + settingStep; cmdHum = 1; }}
      else if(strcmp(cmd,"minhumdown") ==0) { if (minhum > 0) { minhum = minhum - settingStep; cmdHum = 1; }}
      else {
          wifiDev.disp().writeErr("Unknown cmd");
          cmdOK = 0;
             }
      if (cmdOK == 1) {
      if (cmdTemp == 1) writeTemp();
      if (cmdHum == 1) writeHum();
      if (cmdPress == 1) writePress();
      wifiDev.disp().writeOk();
      }
   }

   virtual void onSyncMsg()
      {
           lastSyncMillis=millis();
      }

   private:
   ARpcDevice *dev;
   }serialEcb(&serialDev),wifiEcb(&wifiDev);

   class SrcReadyCb
      :public ARpcISrvReadyCallback
         {
            public:
              void processSrvReadyMsg(const ARpcUuid &srvId,const char *srvName)
                {
                  serialDev.disp().writeInfo("Server detected:     ",bCastSenderIp.toString().c_str(),srvName);
                 if(client.connected()||connecting)return;
                 serialDev.disp().writeInfo("Connecting to server...");
                 connecting=true;
                 client.connect(bCastSenderIp,port);
              for(int i=0;i<20;++i)
                 {
                     if(client.connected())
                     break;
                     delay(100);
                 }
        serialDev.disp().writeInfo("Connected to server");
        connecting=false;
        wifiDev.resetParser();
        lastSyncMillis=millis();
       }
    }srvReadyCb;

   ARpcSrvReady srvReadyParser(200,&srvReadyCb);
 
    void connectWifi()
       {
          WiFi.begin(ssid,password);
          while(WiFi.status()!=WL_CONNECTED)
             {
                 delay(500);
                 serialDev.disp().writeInfo("Wifi connecting");
             }
         serialDev.disp().writeInfo("WiFi connected");
   }
 
Функция setup():
 
   int step = 0;

   void setup() {
   // pin definition
   pinMode(coldPin,OUTPUT);
   pinMode(humPin,OUTPUT);
   pinMode(hotPin,OUTPUT);
   lightRGB(-1);

   delay(2000);

   Serial.begin(9600);
   wifiDev.disp().installDevEventsHandler(&wifiEcb);
   wifiDev.disp().setControls(interfaceStr);
   wifiDev.disp().setSensors(sensorsDef);
   serialDev.disp().installDevEventsHandler(&serialEcb);
   serialDev.disp().setControls(interfaceStr);
   serialDev.disp().setSensors(sensorsDef);

   connectWifi();

   bCastCli.begin(port);

   // Start the server
   server.begin();
   serialDev.disp().writeInfo("Server started");

   // Print the IP address
   serialDev.disp().writeInfo(WiFi.localIP().toString().c_str());
   serialDev.resetStream();

   Wire.begin();
      if(!bme.begin())
         {
            Serial.println("Could not find BME280 sensor!");
            delay(1000);
         }
     if(!bme1.begin())
        {
            Serial.println("Could not find BME280 sensor!");
            delay(1000);
        }
    }

   void checkBCastCli()
       {
           static int sz=0;
           sz=bCastCli.parsePacket();
           if(sz>0)
              {
                  bCastSenderIp=bCastCli.remoteIP();
                  for(int i=0;i<sz;++i)
                  srvReadyParser.putByte(bCastCli.read());
              }
        }

   void checkWifiClient()
       {
          if(client)
              {
                  if(!client.connected())
                     {
                        serialDev.disp().writeInfo("Client connection lost");
                        client=server.available();
                        if(client)
                           {
                              serialDev.disp().writeInfo("Take next pending incoming connection");
                              lastSyncMillis=millis();
                              wifiDev.resetStream();
                              wifiDev.resetParser();
                           }
                       delay(100);
                   }
                   else
                       {
                           while(client.available())
                           wifiDev.putByte(client.read());
                       }
                   }
                  else
                       {
                           client=server.available();
                           if(client)
                               {
                                    serialDev.disp().writeInfo("Take next pending incoming connection");
                                    lastSyncMillis=millis();
                                    wifiDev.resetStream();
                                    wifiDev.resetParser();
                                }
                           delay(100);
                        }
           }

Функция loop():

   void loop() {
      step++;
      while(Serial.available())
      serialDev.putByte(Serial.read());
      if(WiFi.status()!=WL_CONNECTED)
      connectWifi();
      checkBCastCli();
      checkWifiClient();
      if(((millis()-lastSyncMillis)>12000)&&client.connected())
         {
             client.stop();
             checkWifiClient();
          }
       delay(50);

      if (step % 20 == 0) {
         BME280::TempUnit tempUnit(BME280::TempUnit_Celsius);
         BME280::PresUnit presUnit(BME280::PresUnit_Pa);
         bme.read(pressure,temp,hum,tempUnit,presUnit);
         writeTemp();
         writeHum();
         writePress();
         step = 0;
        }
      if (step % 20 == 0) {
      // check temp, light RGB led
      if (temp > maxtemp) lightRGB(2);
      if ((temp >= mintemp) && (temp <= maxtemp)) lightRGB(-1);
      if (temp < mintemp) lightRGB(0);
      // humidity
      if (hum > maxhum)lightRGB(-1);
         }
    }

 Функция, которая включает RGB - свтодиод, разный цвет в зависимости от пороговых значений тепературы, влажности и давления:

   void lightRGB(byte RGB) {
      if (RGB == 2) {
         digitalWrite(coldPin,0);
         digitalWrite(humPin,0);
         digitalWrite(hotPin,1);
      }
      if (RGB == 1) {
         digitalWrite(coldPin,0);
         digitalWrite(humPin,1);
         digitalWrite(hotPin,0);
      }
      if (RGB == 0) {
         digitalWrite(coldPin,1);
         digitalWrite(humPin,0);
         digitalWrite(hotPin,0);
      }
      if (RGB == -1) {
         digitalWrite(coldPin,0);
         digitalWrite(humPin,0);
         digitalWrite(hotPin,0);
       }
   }
 
 
 

/messages/6111