Tambahkan WiFi ke Monitor Kualitas Udara Anda: meter CO2 untuk rumah pintar

CO₂ - (, NDIR- ).
, , .



, — , ( ) . , , , .

, : CO₂ WiFi ESP8266.

WiFi Inside


:



4 :



, . ( ):



— , , :



4 :



, , . : V (voltage) — , D(data) — , (clock) — , G(ground) — .
G, GND, «» — , (, V) . , G — «», V — «», . … , «» .

, clock, , — , , ( D).
( UART/RS-232), , (=) ( 1200, 9600, 115200 ), , , , .
( , ), — . , , , .

:



, :



, . , - , « » .

ESP8266:



UART ( , , ) .




Arduino c ESP8266( , ). , , , esp8266.ru.

: , fedorro, , .

,
, CO2_meter.ino:

#define PIN_CLOCK  14 // ,     "" —  
#define PIN_DATA   12 // ,     "D" — 

#include <ESP8266WiFi.h> 
#include <ESP8266WebServer.h>
#include "mt8060_decoder.h"

const char* ssid = "MikroTik-951"; // 
const char* password = "FAKEPASSWORD"; // 
String co2_value = ""; //    ppm
String tmp_value = ""; //    
String hum_value = ""; //    
int error_count = 0; //   (,   ,    )


ESP8266WebServer server(80); //  

void setup()
{
  Serial.begin(115200); // UART
  WiFi.begin(ssid, password); //  WiFi

  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(500);
    Serial.print("."); //    UART,    
  }
  Serial.println("");
  Serial.print("WiFi connected, IP ");
  Serial.println(WiFi.localIP());

  server.on("/co2", co2_show); //     ,   
  server.on("/tmp", tmp_show);
  server.on("/hum", hum_show);
  server.on("/json", json_show);
  server.onNotFound(NotFound_show); //  
  server.begin();

  pinMode(PIN_CLOCK, INPUT); //   
  pinMode(PIN_DATA, INPUT); //   
  attachInterrupt(digitalPinToInterrupt(PIN_CLOCK), interrupt, FALLING); //      
}


void interrupt() //      
{
  bool dataBit = (digitalRead(PIN_DATA) == HIGH);
  unsigned long ms = millis();
  
  mt8060_message* message = mt8060_process(ms, dataBit); //       ,    ,        ,   ,   .

  if (message) { //    
    if (!message->checksumIsValid) // ,     
    {
      error_count++;
    }
    else //   ...
    {
      switch (message->type) //...     ...
      {
        case HUMIDITY: 
          hum_value = String((double)message->value / 100, 0); //   
          Serial.print("HUM:"); 
          Serial.println(hum_value); //   UART
          break;

        case TEMPERATURE:
          tmp_value = String((double)message->value / 16 - 273.15, 1); //     
          Serial.print("TMP:"); // -! ! -> (. )(. )
          Serial.println(tmp_value); //   UART
          break;

        case CO2_PPM:
          co2_value = String(message->value, DEC); //    CO₂
          Serial.print("CO2:");
          Serial.println(co2_value); //   UART
          break;

        default:
          break;
      }
    }
  }
}

void co2_show() { //,  CO₂  
  server.send(200, "text/plain", co2_value);
}

void tmp_show() { //,    
  server.send(200, "text/plain", tmp_value);
}

  void hum_show() { //,    
  server.send(200, "text/plain", hum_value);
}

void NotFound_show() { //,      
  String form = "<!DOCTYPE html><html lang=\"en\"><head><meta http-equiv=\"Content-Type\" content=\"text/html\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, user-scalable=no\"><title>Dadget 8060 CO₂ monitor</title><style>.c{text-align: center;} div,input{padding:5px;font-size:1em;} input{width:95%;} body{text-align: center;font-family:verdana;} button{border:0;border-radius:0.3rem;background-color:#1fa3ec;color:#fff;line-height:2.4rem;font-size:1.2rem;width:100%;} .q{float: right;width: 64px;text-align: right;}</style><style type=\"text/css\"></style></head><body><div style=\"text-align:left;display:inline-block;min-width:260px;\"><h1>Dadget 8060 CO₂ monitor</h1><form action=\"/co2\" method=\"get\"><button>CO₂ value ([co2] ppm now)</button></form><br><form action=\"/tmp\" method=\"get\"><button>Temperature value ([tmp]° now)</button></form><br><form action=\"/hum\" method=\"get\"><button>Humidity value ([hum]% now)</button></form><br><form action=\"/json\" method=\"post\"><button>JSON(all values)</button></form></div></body></html>";
  form.replace("[co2]", co2_value); //  
  form.replace("[hum]", hum_value);
  form.replace("[tmp]", tmp_value);
  server.send(200, "text/html", form); //  
}

void json_show() { //,      JSON
  String json = "[{\"co2\":"; //  c JSON 
  json += co2_value;
  json += ",\"tmp\":";
  json += tmp_value;
  json += ",\"hum\":";
  json += hum_value;
  json += ",\"serial\":";
  json += ESP.getChipId();
  json += ",\"errors\":";
  json += error_count;
  json += ",\"uptime_min\":";
  json += String(millis()/60000);
  json += "}]";

  server.send(200, "application/json", json); //
}

void loop()
{
  server.handleClient(); //    HTTP
}

, mt8060_decoder.cpp:

// Based on https://github.com/fe-c/MT8060-data-read code
// All rights for reading code owned https://geektimes.ru/users/fedorro/
// and https://github.com/revspace

#include "mt8060_decoder.h"
#define MT8060_MAX_MS  2 //    ,    
#define MT8060_MSG_LEN 5 //     5 
#define MT8060_MSG_TYPE_BYTE_IDX          0
#define MT8060_MSG_VAL_HIGH_BYTE_IDX      1
#define MT8060_MSG_VAL_LOW_BYTE_IDX       2
#define MT8060_MSG_CHECKSUM_BYTE_IDX      3
#define MT8060_MSG_CR_BYTE_IDX            4
#define BITS_IN_BYTE                      8

static uint8_t buffer[MT8060_MSG_LEN]; //     
static int num_bits = 0;
static unsigned long prev_ms;

static mt8060_message _msg;
static mt8060_message *msg = &_msg;


void mt8060_decode(void) //  
{

  uint8_t checksum = buffer[MT8060_MSG_TYPE_BYTE_IDX] + buffer[MT8060_MSG_VAL_HIGH_BYTE_IDX] + buffer[MT8060_MSG_VAL_LOW_BYTE_IDX];   //   
  msg->checksumIsValid = (checksum == buffer[MT8060_MSG_CHECKSUM_BYTE_IDX] && buffer[MT8060_MSG_CR_BYTE_IDX] == 0xD); //   
  if (!msg->checksumIsValid) {
    return;
  }
  msg->type = (dataType)buffer[MT8060_MSG_TYPE_BYTE_IDX]; //   
  msg->value = buffer[MT8060_MSG_VAL_HIGH_BYTE_IDX] << BITS_IN_BYTE | buffer[MT8060_MSG_VAL_LOW_BYTE_IDX]; //   
}


//       ,     ,    
mt8060_message* mt8060_process(unsigned long ms, bool data)
{
  if ((ms - prev_ms) > MT8060_MAX_MS) {
    num_bits = 0;
  }
  prev_ms = ms;

  if (num_bits < MT8060_MSG_LEN * BITS_IN_BYTE) {
    int idx = num_bits / BITS_IN_BYTE;
    buffer[idx] = (buffer[idx] << 1) | (data ? 1 : 0);
    num_bits++;

    if (num_bits == MT8060_MSG_LEN * BITS_IN_BYTE) {
      mt8060_decode(); // 
      return msg; // 
    }
  }
  return nullptr; //  ,    
}


, mt8060_decoder.h
// Based on https://github.com/fe-c/MT8060-data-read code
// All rights for reading code owned https://geektimes.ru/users/fedorro/
// and https://github.com/revspace

#include <stdint.h>
#include <stdbool.h>

typedef enum
{
    HUMIDITY    = 0x41,
    TEMPERATURE = 0x42,
    CO2_PPM     = 0x50,
} dataType;

typedef struct {
    dataType type;
    uint16_t value;
    bool checksumIsValid;
} mt8060_message;

mt8060_message* mt8060_process(unsigned long ms, bool data);



, GitHub. - , , !

, …



! , :



, , , ( ), :




, « » « -» . , - , .

, ESP, , , - WiFi-… , , .

, , Logic Machine — . , , , .

Sheduled-( ), :



- :

local http = require('http')
local json = require('json')
local raw_data, code = socket.http.request('http://co2meter.lc/json') -- .       DNS-
if (code == 200) then --   200..
  local data = json.decode(raw_data) --    json   lua
  if (data ~= nil) then --    json-... 
    if (data[1].uptime_min > 2) then --        (   ""       )
      grp.update('S_CO2_CO2', data[1].co2) --    CO₂,    
      grp.update('S_CO2_TMP', data[1].tmp)
      grp.update('S_CO2_HUM', data[1].hum)
    end
  end
end


! :



. ( Dadget , )! Trends logs —> Add new trend log:



- , , :



, :



, CO₂ !
№1: CO₂.
№2: - ( ) CO₂ .



№3: ( ) . ( ) , .
№4: CO₂, , . , .



№5: CO₂, , . .




, CO₂, . ( , ), CO₂. LM :

Event-based( ), , CO₂:



, 1000ppm, 800, - :

--     
value = grp.getvalue("S_CO2_CO2") --      
if (value > 1000) then --  ,  1000ppm...
  grp.write('HP-7.1', true) --.. 
elseif (value < 800) then -- CO₂ ,  800ppm...
  grp.write('HP-7.1', false) --.. 
end 

, CO₂ 1000ppm, , 800ppm.

!



:




Arduino

, : telegram.me/IOTandSmarthome

14 , , « » 10%- , GEEKT-MK.

Source: https://habr.com/ru/post/id394333/


All Articles