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 
#include <ESP8266WiFi.h> 
#include <ESP8266WebServer.h>
#include "mt8060_decoder.h"
const char* ssid = "MikroTik-951"; 
const char* password = "FAKEPASSWORD"; 
String co2_value = ""; 
String tmp_value = ""; 
String hum_value = ""; 
int error_count = 0; 
ESP8266WebServer server(80); 
void setup()
{
  Serial.begin(115200); 
  WiFi.begin(ssid, password); 
  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(500);
    Serial.print("."); 
  }
  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); 
          break;
        case TEMPERATURE:
          tmp_value = String((double)message->value / 16 - 273.15, 1); 
          Serial.print("TMP:"); 
          Serial.println(tmp_value); 
          break;
        case CO2_PPM:
          co2_value = String(message->value, DEC); 
          Serial.print("CO2:");
          Serial.println(co2_value); 
          break;
        default:
          break;
      }
    }
  }
}
void co2_show() { 
  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() { 
  String json = "[{\"co2\":"; 
  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(); 
}
, 
mt8060_decoder.cpp:
#include "mt8060_decoder.h"
#define MT8060_MAX_MS  2 
#define MT8060_MSG_LEN 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
#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') 
if (code == 200) then 
  local data = json.decode(raw_data) 
  if (data ~= nil) then 
    if (data[1].uptime_min > 2) then 
      grp.update('S_CO2_CO2', data[1].co2) 
      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
№3: ( ) . ( ) , .
№4: CO₂, , . , .
 №5
№5: CO₂, , . .

►
, CO₂, 
. ( , ), CO₂. LM :
Event-based( ), , CO₂:

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

:
Arduino, : 
telegram.me/IOTandSmarthome14 , , 
« » 10%- , GEEKT-MK.