Раскладка по репозиториям

master
SmartUA 2021-09-01 21:23:20 +03:00
parent a8b206c3a1
commit d8a09949f6
28 changed files with 13 additions and 1456 deletions

View File

@ -56,7 +56,7 @@
#define CCS811_ADDR 0x5B //Default I2C Address
//#define CCS811_ADDR 0x5A //Alternate I2C Address
unsigned int VersionSW = 46; //65536 Версия прошивки
unsigned int VersionSW = 48; //65536 Версия прошивки
//15 - добавлено то, се, забыл вообще дописать что добавлено Serial
//19 - вывод в консоль всех действий, ошибки с обновлнеием - починил, прияногое мигание светодиодом, тест для поиска metrics.
@ -83,6 +83,7 @@ unsigned int VersionSW = 46; //65536 Версия прошивки
//43 - Больше задержек для чтения i2c
//44 - Новая либа твок "maarten-pennings" Спаркфан и Адафрут суки - с ошибками изначально пишут на всех форумах.
//46 - Смена локального сервера времени, хрони на одроиде. Установка базовых линий по новой.
//48 - алгоритм фильтрации
WiFiManager wm; // обьект менеджера
WiFiManagerParameter custom_field;
@ -93,7 +94,7 @@ Preferences OTApreferences; //Обьект хранения настроек х
CCS811 ccs811(23);
ClosedCube_HDC1080 hdc1080;
StaticJsonDocument<200> doc;
StaticJsonDocument<200> doc;
WiFiClient espClient;
PubSubClient MqttClient(espClient);
@ -132,6 +133,15 @@ unsigned int cntWhile = 4;
bool mqttSendFlag = false;
bool flagblink = true;
float k = 0.1;
float expRunningAverage(float newVal) {
RTC_DATA_ATTR static float filVal = 0;
filVal += (newVal - filVal) * k;
return filVal;
}
void ledset(byte color = 0, bool blink = false)
{
M5.dis.setBrightness(30); //Половина яркости
@ -620,7 +630,7 @@ int SendMqttReq(bool sendVal = true, bool sendStatus = true, byte statusConn = 1
if (sendVal == true)
{
doc["t"] = TempAv;
doc["t"] = expRunningAverage(TempAv);
doc["h"] = HumAv;
doc["eco"] = (int)Eco2Av;
doc["tvoc"] = (int)TvocAv;

BIN
airatoms.bin Normal file

Binary file not shown.

View File

@ -1,253 +0,0 @@
//20.02.2019
//by Reptiloid software
#include "Arduino.h"
#include "AC_Dimmer.h"
#define DIMM_MAX 5
#define AC_FREQ 50
#define MAX_HEAT_DIV 10
//======================================
#define ZERO_VAL 19850
#define K 77
#define SSR 0
#define DIMM 1
#define HEAT 2
//=======================================
struct dimmer
{
unsigned char flag;
unsigned char val;
unsigned char pin;
unsigned char SSR_state;
unsigned char heat_val;
};
struct sort
{
unsigned char val;
unsigned char pin;
};
//==========================================
volatile unsigned char cnt;
volatile unsigned char dims;
volatile unsigned char lim;
volatile unsigned char not_z;
volatile unsigned char heat_cnt = 0;
dimmer dimmer[DIMM_MAX];
sort sort_vals[DIMM_MAX];
sort sort[DIMM_MAX*2];
//===============================================
void Dimmer_init_begin()
{
TCCR1A=0;
TCCR1B=2;
TIMSK1=1;
EICRA=3;
EIMSK=1;
EIFR=1;
for(unsigned char i=0; i<DIMM_MAX; i++)
{
dimmer[i].pin = 0xff;
dimmer[i].val = 0;
dimmer[i].flag = SSR;
dimmer[i].SSR_state = 0;
}
for(unsigned char i=0; i<DIMM_MAX*2; i++)
{
sort[i].pin = 0xff;
}
}
void Dimmer_pin_assign(unsigned char dim_num, unsigned char dim_pin)
{
dimmer[dim_num].pin = dim_pin;
}
void Dimmer_init_end()
{
unsigned char j=0;
for(unsigned char i=0; i<DIMM_MAX; i++)
{
if(dimmer[i].pin != 0xff)
{
sort_vals[j].pin = dimmer[i].pin;
j++;
pinMode(dimmer[i].pin, OUTPUT);
}
}
dims = j;
}
void tim_form()
{
unsigned char j;
not_z = 0;
for(j = 0; (sort_vals[j].val == 0 && j < dims); j++) not_z++;
lim = dims - not_z;
sort[lim*2-1] = sort_vals[dims-1];
for(j=0; j<lim-1; j++)
{
sort[lim*2-2-j].val = sort_vals[dims-1-j].val - sort_vals[dims-j-2].val;
sort[lim*2-2-j].pin = sort_vals[dims-j-2].pin;
}
sort[lim-1].val = 255 - sort_vals[dims-1].val + sort_vals[not_z].val;
sort[lim-1].pin = sort_vals[dims-1].pin;
for(j = 0; j<lim-1; j++)
{
sort[j] = sort[j+lim];
}
}
void SSR_switch(unsigned char dim_num, unsigned char state)
{
Dimm_value(dim_num, 0);
dimmer[dim_num].flag = SSR;
dimmer[dim_num].SSR_state = state;
if(state == 0) digitalWrite(dimmer[dim_num].pin, 0);
}
void Dimm_value(unsigned char dim_num, unsigned char power)
{
if(power > 240) power = 240;
dimmer[dim_num].flag = DIMM;
dimmer[dim_num].val = power;
unsigned char j;
for(unsigned char i = 0; i<dims; i++)
if(sort_vals[i].pin == dimmer[dim_num].pin)
{
if(power > sort_vals[i+1].val && i<dims-1)
{
for(j = 1; ((power > sort_vals[i+j].val) && (i+j<dims)); j++)
{
sort_vals[i+j-1] = sort_vals[i+j];
}
sort_vals[i+j-1].val = power;
sort_vals[i+j-1].pin = dimmer[dim_num].pin;
}
else if(power < sort_vals[i-1].val && i>0)
{
for(j = 1; ((power < sort_vals[i-j].val) && (i-j>=0)); j++)
{
sort_vals[i-j+1] = sort_vals[i-j];
}
sort_vals[i-j+1].val = power;
sort_vals[i-j+1].pin = dimmer[dim_num].pin;
}
else sort_vals[i].val = power;
i = dims;
}
}
void Heater(unsigned char dim_num, unsigned char heat_power)
{
Dimm_value(dim_num, 0);
dimmer[dim_num].flag = HEAT;
dimmer[dim_num].heat_val = heat_power;
if(heat_power == 0) digitalWrite(dimmer[dim_num].pin, 0);
}
//======================================================================
ISR(INT0_vect)
{
if(sort_vals[dims-1].val != 0 && dims > 0)
{
tim_form();
cnt=lim*2-1;
TCNT1 = 0xffff - (ZERO_VAL - K*sort[cnt].val);
}
for(unsigned char i = 0; i<DIMM_MAX; i++)
{
if(dimmer[i].flag != DIMM)
{
if(dimmer[i].flag == HEAT)
{
if(dimmer[i].heat_val > heat_cnt) digitalWrite(dimmer[i].pin, 1);
else digitalWrite(dimmer[i].pin, 0);
}
else
{
if(dimmer[i].SSR_state > 0) digitalWrite(dimmer[i].pin, 1);
}
}
}
if(heat_cnt < MAX_HEAT_DIV-1) heat_cnt++;
else heat_cnt = 0;
}
ISR(TIMER1_OVF_vect)
{
if(sort_vals[dims-1].val != 0 && dims > 0)
{
digitalWrite(sort[cnt].pin, 1);
delayMicroseconds(30);
digitalWrite(sort[cnt].pin, 0);
while(sort[cnt-1].val == 0)
{
cnt--;
digitalWrite(sort[cnt].pin, 1);
delayMicroseconds(30);
digitalWrite(sort[cnt].pin, 0);
}
cnt--;
TCNT1 = 0xffff - K*sort[cnt].val;
}
}

View File

@ -1,32 +0,0 @@
//20.02.2019
//by Reptiloid software
/*
Îãðàíè÷åíèÿ:
- Äëÿ êîíòðîëÿ ôàçû èñïîëüçîâàòü òîëüêî D2
- Íå èñïîëüçîâàòü ïèíû D9 è D10 äëÿ ØÈÌ (Analog.Write())
òîëüêî öèôðîâîé âûõîä/âõîä digitalWrite()
*/
//====================================================
#ifndef AC_Dimmer_h
#define AC_Dimmer_h
#include <avr/io.h>
#include <avr/interrupt.h>
#include "Arduino.h"
//=================================================
void Dimmer_init_begin();
void Dimmer_pin_assign(unsigned char dim_num, unsigned char dim_pin);
void Dimmer_init_end();
void Dimm_value(unsigned char dim_num, unsigned char power);
void SSR_switch(unsigned char dim_num, unsigned char state);
void Heater(unsigned char dim_num, unsigned char heat_power);
#endif

View File

@ -1,110 +0,0 @@
//20.02.2019
//by Reptiloid software
/*
Ограничения:
- Для контроля фазы использовать только D2 (прерывание INT0)
(порт M2 диммера подключать к порту D2 АРДУИНО)!!!
- Не использовать пины D9 и D10 для ШИМ Analog.Write()!!!
только цифровой выход/вход digitalWrite() и digitalRead()
Функционал:
- Данная библиотека позволяет регулировать нагрузку(и),
подключенную в промышленную сеть 220В с помощью АУРДУИНО
различными встроенными функциями описанными ниже
Как работать:
1) Чтобы начать работу нужно подключить библиотеку AC_Dimmer.h
2) Обозначить и раздать номера диммерам (нумерация от 0!!!), используемым в проекте:
#define <"имя димера"> <"порядковый номер">
Например:
#include <AC_Dimmer.h>
#define Dimmer_1 0
#define Dimmer_2 1
#define Dimmer_3 2
3) Инициализировать димер(ы) в void setup().
- Инициализацию выполнять строго между функций Dimmer_init_begin() и Dimmer_init_end();
- Инициализировать с помощью функции Dimmer_pin_assign():
Dimmer_pin_assign(<"имя димера">, <"порт(пин) ардуино димера">);
Например:
void setup()
{
Dimmer_init_begin();
Dimmer_pin_assign(Dimmer_1, 3);
Dimmer_pin_assign(Dimmer_2, A3);
Dimmer_pin_assign(Dimmer_3, A2);
Dimmer_init_end();
}
3) В void loop() использовать нужную функцию:
- Dimm_value(<"имя димера">, <"значение мощности от 0 до 255">)
- Heater(<"имя димера">, <"значение мощности от 0 до 10">)
- SSR_switch(<"имя димера">, <значения 1 или 0 (HIGH/LOW)>)
Например: void loop()
{
Dimm_value(Dimmer_3, 64); // 25% мощность на диммере№3
Heater(Dimmer_2, 3); //30% мощности на диммере№2 (при условии что там ТЕН или другая низкоинерциооная нагрузка)
Dimm_value(Dimmer_1, 128); //50% мощности на диммере№1
delay(1000); //установленные высше параметры удерживаются 1сек
SSR_switch(Dimmer_3, LOW); // отключим все диммеры на 1сек
SSR_switch(Dimmer_2, LOW);
SSR_switch(Dimmer_1, LOW);
delay(1000);
}
Код даного примера предполагает плавное поочередное зажигание и
и тушение трех ламп накаливания, подключенных к ардуино каждая через независимый диммер
Демонстрацию работы библиотеки на тестовом стенде и схему можно посмотреть здесь:
https://www.youtube.com/watch?v=9fhkJQIRmEA
Так же можно использовать для теста одиночного диммера
*/
#include <AC_Dimmer.h>
#include <math.h> //если Вы хотите использовать данную библитеку в своем в проекте,
//математическая библиотека <math.h> не обязательна к включению
#define Dimmer_1 0
#define Dimmer_2 1
#define Dimmer_3 2
unsigned char i = 0;
unsigned char range = 70; // Диапазон регулировки
// (255 - максимальное значение)
void setup()
{
Dimmer_init_begin();
Dimmer_pin_assign(Dimmer_1, 3);
Dimmer_pin_assign(Dimmer_2, A3);
Dimmer_pin_assign(Dimmer_3, A2);
Dimmer_init_end();
}
void loop()
{
i++;
Dimm_value(Dimmer_1, range*fabs(sin(i*2*3.14/255)));
Dimm_value(Dimmer_2, range*fabs(sin((i+(255/3))*2*3.14/255)));
Dimm_value(Dimmer_3, range*fabs(sin((i+(255*2/3))*2*3.14/255)));
delay(25);
}

View File

@ -1,51 +0,0 @@
//30.03.2019
//by Reptiloid software
/*
Данный скетч реализует ручную независимую регулировку мощности для каждого диммера
с помощью соответствующего потенциометра на аналоговых пинах
(рекомендуемы номинал 1-100К)
Так же можно использовать для теста одиночного диммера
Подробное описание работы и синтаксиса библиотеки можете
посмотреть в примере AC_DIMMER_FADE
Демонстрацию работы библиотеки на тестовом стенде и схему можно посмотреть здесь:
https://www.youtube.com/watch?v=9fhkJQIRmEA
*/
#include <AC_Dimmer.h>
#define Dimmer_1 0
#define Dimmer_2 1
#define Dimmer_3 2
void setup()
{
Dimmer_init_begin();
Dimmer_pin_assign(Dimmer_1, 3);
Dimmer_pin_assign(Dimmer_2, A3);
Dimmer_pin_assign(Dimmer_3, A2);
Dimmer_init_end();
}
int adc_read;
void loop()
{
adc_read = analogRead(A4)/4;
Dimm_value(Dimmer_1, adc_read); // потенциометры на пинах А4, А5, А6
adc_read = analogRead(A5)/4;
Dimm_value(Dimmer_2, adc_read); // для ручной регулировки мощности
adc_read = analogRead(A6)/4;
Dimm_value(Dimmer_3, adc_read); // на каждый диммер
delay(50);
}

View File

@ -1,78 +0,0 @@
/*
Данный скетч позволяет запустить димер в режиме FADE для платы ESP8266
- 2сек диммер плавно увеличивает мощность
- 2сек диммер плавно уменьшает мощность
Для того чтобы успешно прошить даный скетч Вам нужно:
- скачать дистрибутив для работу с ESP8266 в Arduino IDE
- скачать библиоеки Ticker.h
- подключить димер на соответствующие пины или назначить свои
При работе с ESP8266 подключать библиотеку AC_Dimmer и использовать ее функции не нужно
Достаточно прошить лишь данный пример
Для управления можностью используется переменная val (смотрите ниже)
*/
#include <ESP8266WiFi.h>
#include <Ticker.h>
Ticker blinker;
const byte interruptPin = D7; // пин диммера М2
const byte dimPin = D5; // пин диммера М1
int val = 0; // переменная, которая определяет мощность от 0 до 10000
int power = 0;
int rise = 0;
void ICACHE_RAM_ATTR handleInterrupt();
void ICACHE_RAM_ATTR onTimerISR() // обработчик прерывания таймера
{
digitalWrite(dimPin, HIGH);
delayMicroseconds(40);
digitalWrite(dimPin, LOW);
timer1_write(50000);//10мс
}
void handleInterrupt() // обработчик прерывания по фронту (от пина детекции фазы димера)
{
power = 49000 - 4.785*val;
timer1_write(power);
if(val < 10000 && rise == 1) val = val + 100; // плавное наростание
else rise = 0;
if(val > 0 && rise == 0) val = val - 100; // плавное угасание
else rise = 1;
}
void setup()
{
pinMode(interruptPin, INPUT_PULLUP);
pinMode(dimPin, OUTPUT);
attachInterrupt(digitalPinToInterrupt(interruptPin), handleInterrupt, RISING);
timer1_attachInterrupt(onTimerISR);
timer1_enable(TIM_DIV16, TIM_EDGE, TIM_SINGLE);
// здесь можно писать свой код (setup())
//
// здесь можно писать свой код (setup())
}
void loop()
{
// здесь можно писать свой код
//
// здесь можно писать свой код
}

View File

@ -1,99 +0,0 @@
/*
Даный скетч позволяет управлять сетевым диммером DMR-02 через WiFi
с помощью ESP8266 и смартфона с любой точки планеты, где есть доступ в интернет
Скетч позволит управлять нагрузкой (лампой) с помощью ползунка на Вашем смартфоне по WiFi
Код работает в неблокирующем режиме (можно добавлять свой код)
Управление со смартфона будет происходить с помощью программы Blynk (Play market/APP store)
Перед началом работы рекомендуется разобраться с функионалом данного софта
пример работы (ВИДЕО)
https://www.youtube.com/watch?v=Z_Vvgx4QuSY
Для того чтобы успешно прошить даный скетч Вам нужно:
- скачать дистрибутив для работу с ESP8266 в Arduino IDE
- скачать библиоеки Blynk и Ticker.h
- скачать приложение Blynk на смартфон
- создать проект в приложении Blynk для управления димером
- получить на почту Ваш ЛИЧНЫЙ токен с приложения Blynk и вписать его ниже
- вписать имя и пароль WiFi для подключения ESP8266 (смотрите ниже)
- подключить димер на соответствующие пины или назначить свои
При работе с ESP8266 подключать библиотеку AC_Dimmer и использовать ее функции не нужно
Достаточно прошить лишь данный пример
Для управления можностью используется переменная val (смотрите ниже)
*/
#include <ESP8266WiFi.h>
#include <Ticker.h>
#include <BlynkSimpleEsp8266.h>
Ticker blinker;
const byte interruptPin = D7; // пин диммера М2
const byte dimPin = D5; // пин диммера М1
int power = 0;
int val = 0; // переменная задающая мощность от 0 до 10000
char auth[] = "7wE5GzzU2zs83pOZKSbMxfEu_G53y4V6"; // Ваш ЛИЧНЫЙ токен от приложения Blynk
char ssid[] = "AndroidAP"; // имя WiFi
char pass[] = "23412123"; // пароль WiFi
void ICACHE_RAM_ATTR handleInterrupt();
void ICACHE_RAM_ATTR onTimerISR() // обработчик прерывания таймера
{
digitalWrite(dimPin, HIGH);
delayMicroseconds(40);
digitalWrite(dimPin, LOW);
timer1_write(50000);//10мс
}
void handleInterrupt() // обработчик прерывания по фронту (от пина детекции фазы димера)
{
power = 49000 - 4.75*val;
timer1_write(power);
}
void setup()
{
Serial.begin(9600);
Blynk.begin(auth, ssid, pass);
pinMode(interruptPin, INPUT_PULLUP);
pinMode(dimPin, OUTPUT);
attachInterrupt(digitalPinToInterrupt(interruptPin), handleInterrupt, RISING);
timer1_attachInterrupt(onTimerISR);
timer1_enable(TIM_DIV16, TIM_EDGE, TIM_SINGLE);
// здесь можно писать свой код (setup())
//
// здесь можно писать свой код (setup())
}
void loop()
{
Blynk.run(); // функция поллинга данных по WiFi
// здесь можно писать свой код
//
// здесь можно писать свой код
}
BLYNK_WRITE(V0) // функция считывания данных по WiFi
{
val = param.asInt();
}

View File

@ -1,9 +0,0 @@
#include <AC_Dimmer.h> KEYWORD1
Dimmer_pin_assign KEYWORD1
Dimmer_init_end KEYWORD1
Dimmer_init_begin KEYWORD1
Heater KEYWORD2
Dimm_value KEYWORD2
SSR_switch KEYWORD2

View File

@ -1,28 +0,0 @@
# AC_dimmer-dmr-0.2.1
Последняя версия библиотеки на сетевой диммер dmr-0.2.x
Пример работы (Demo-video): https://www.youtube.com/watch?v=9fhkJQIRmEA
Пример работы c ESP8266 (управление диммером через Wifi с помощью смартфона) (Demo-video): https://www.youtube.com/watch?v=Z_Vvgx4QuSY
- Работа в трех режимах:
- режим трердотелого реле (вкл/выкл с учетом фазы)
- фазовая регулировка (классический режим)
- полуволновый режим (для повышеных мощностей для мощных нагревателей)
- позволяет параллельную работу нескольких димеров на одной плате Ардуино
- Добавлен пример работы с ESP8266 по Wifi
Библиотека Ардуино: [AC_Dimmer.zip](https://github.com/serhiy23412/AC_dimmer-dmr-0.2.1/blob/master/AC_Dimmer.zip)
Простой пример на Raspberry Pi (by Andy): [Raspberry Pi example.txt](https://github.com/serhiy23412/AC_dimmer-dmr-0.2.1/blob/master/Raspberry_Pi_example.txt)
Более детальная информация: [AC_Dimmer_Docum.docx](https://github.com/serhiy23412/AC_dimmer-dmr-0.2.1/blob/master/AC_Dimmer_Docum.docx)

View File

@ -1,45 +0,0 @@
import RPi.GPIO as GPIO
import time
import math
PHASE_DFREQ = 100
DRIVER_MIN = 0.3
DRIVER_MAX = 0.95
power = DRIVER_MIN
def phase_callback(channel):
global power
if power < DRIVER_MIN:
power = DRIVER_MIN
if power > DRIVER_MAX:
power = DRIVER_MAX
time.sleep(math.acos(2.0 * power - 1.0) / (math.pi * PHASE_DFREQ))
GPIO.output(23, GPIO.HIGH)
time.sleep(0.00003)
GPIO.output(23, GPIO.LOW)
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(23, GPIO.OUT) # dimmer control
GPIO.setup(24, GPIO.IN) # phase control
GPIO.add_event_detect(24, GPIO.BOTH, callback=phase_callback, bouncetime=9)
try:
print(power)
time.sleep(15)
while True:
power += 0.05
print(power)
time.sleep(10)
except KeyboardInterrupt:
pass
finally:
GPIO.remove_event_detect(24)
GPIO.cleanup()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,5 +0,0 @@
[.ShellClassInfo]
InfoTip=Ê ýòîé ïàïêå îòêðûò ñîâìåñòíûé äîñòóï â Èíòåðíåòå.
IconFile=C:\Program Files\Google\Drive\googledrivesync.exe
IconIndex=16

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,743 +0,0 @@
/*
Отправка данных на MQTT про обновление
*/
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager
#include <Preferences.h> //Сохранение настроек хеша прошивки
#include <HTTPClient.h>
#include <ESP32httpUpdate.h> //Библиотека ОТА обновлений
#include "M5Atom.h" //Библиотека атома для функции Led и Кнопки, можно упразднить и убрать
#include <WiFiUdp.h> //Udp клиент
#include <NTPClient.h> //NTP запрос времени
#include <TimeLib.h> //Внутреннее время
#include <Wire.h> //Библиотека дял I2C
#include <PubSubClient.h> //Mtqq
#include <ArduinoJson.h> //Упакова в JSon - удобная библиотека
#include <ESPmDNS.h>
#include "MHZ19.h"
#include <SoftwareSerial.h>
#include "SparkFun_SCD30_Arduino_Library.h" //Click here to get the library: http://librarymanager/All#SparkFun_SCD30
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET 4
//Наша кнопочка при нажатии на которую произойдет вызов wifi менеджера и перезагрузка в станцию
#define TRIGGER_PIN 39
#define RX_PIN 19
#define TX_PIN 22
#define BAUDRATE 9600
#define i2cSDA 25
#define i2cSCL 21
MHZ19 myMHZ19;
SoftwareSerial mySerial(RX_PIN, TX_PIN);
unsigned int VersionSW = 20;
//17 - локальный брокер
//19 - какие то траблы в офисе
//20 - Ручное удаление
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
WiFiManager wm; // обьект менеджера
Preferences OTApreferences; //Обьект хранения настроек хеша прошивки
String JsonData;
StaticJsonDocument<200> doc;
SCD30 airSens30;
WiFiClient espClient;
PubSubClient MqttClient(espClient);
IPAddress IpMqtt;
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "192.168.89.210", 0, 20000); //Собственно сервер времени смещение и частоат запроса, но он вручную
const PROGMEM char *willmess = "{\"conn\":\"err\"}";
const char *mqttHostName = "metrics"; //Хостнейм брокера metrics.local cctv.automation.art //192.168.89.210
unsigned int mqttPort = 1883; //Порт брокера 1883 8889
String getMacAddress();
String macc = getMacAddress();
String topicTemp = "aastudio/sens/" + macc + "/data/0";
String willTopicTemp = "aastudio/sens/" + macc + "/status";
const PROGMEM char *mqttLogin = "AA_Lab", *mqttPass = "automation.art$";
const char *mqttIPHost; //тут хранится IP хоста по хостнейму
unsigned long timingUpdate=0, timingReqSensor=0, timingSendMqtt=0; //Таймеры для millis()
int PROGMEM nextM5Update = 450000, nextMqttSend = 60000; //каждые 7.5 минут запрос обновления с сервера
//Отправка
bool typeSensor = false, GlobalOled = false;
bool mqttSendFlag = false;
bool safetyStateNow = false, safetyStateOld = false;
int reqCounter = 0;
long rssi = 0;
int GlobalCo2 = 0;
char bufTopic[140], bufWillTopic[150], bufAngleTopic[150];
void ledset(byte color = 0)
{
M5.dis.setBrightness(60); //Половина яркости
switch (color)
{
case 2:
M5.dis.drawpix(0, 0xf00000); //Зеленый
break;
case 1:
M5.dis.drawpix(0, 0x00ff00); //Красный
break;
case 3:
M5.dis.drawpix(0, 0x0000ff); //Синий
break;
case 4:
M5.dis.drawpix(0, 0xffffff); //Белый
break;
case 5:
M5.dis.drawpix(0, 0xffec00); //желтый
break;
default:
M5.dis.clear();
break;
}
}
//Настройки
void setup()
{
Serial.begin(115200);
Serial.setDebugOutput(true);
M5.begin(true, false, true);
Wire.begin(32, 26);
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C))
{ // Address 0x3D for 128x64
GlobalOled = false;
}
else
{
GlobalOled = true;
display.display();
display.clearDisplay();
}
// 1й - Просто датчик scd30
// 2й - Датчик золотой MH-z19
if (macc == "94B97EAD9EDC" || macc == "94B97E8ADF7C")
{
typeSensor = true;
}
else if (macc == "24A160542E48" || macc == "94B97E8ADF80")
{
typeSensor = false;
}
if (typeSensor == true)
{
Wire.begin(i2cSDA, i2cSCL);
if (airSens30.begin() == false)
{
Serial.println("Air sensor not detected. Please check wiring. Freezing...");
}
airSens30.setMeasurementInterval(10); //Change number of seconds between measurements: 2 to 1800 (30 minutes)
airSens30.setAltitudeCompensation(324); //Set altitude of the sensor in m
// airSens30.setAmbientPressure(835); //Current ambient pressure in mBar: 700 to 1200
float offset = airSens30.getTemperatureOffset();
Serial.print("Current temp offset: ");
Serial.print(offset, 2);
Serial.println("C");
//airSens30.setTemperatureOffset(5);
}
else
{
mySerial.begin(BAUDRATE);
myMHZ19.begin(mySerial);
myMHZ19.autoCalibration(false);
}
delay(50);
pinMode(TRIGGER_PIN, INPUT);
WiFi.mode(WIFI_STA);
delay(100);
Serial.println(topicTemp);
Serial.println(willTopicTemp);
topicTemp.toCharArray(bufTopic, topicTemp.length() + 1);
willTopicTemp.toCharArray(bufWillTopic, willTopicTemp.length() + 1);
// wm.resetSettings(); // wipe settings
std::vector<const char *> menu = {"wifi", "info", "param", "sep", "restart", "exit"};
wm.setMenu(menu);
// set dark theme
wm.setClass("invert");
//set static ip
// wm.setSTAStaticIPConfig(IPAddress(10,0,1,99), IPAddress(10,0,1,1), IPAddress(255,255,255,0)); // set static ip,gw,sn
// wm.setShowStaticFields(true); // force show static ip fields
// wm.setShowDnsFields(true); // force show dns field always
// wm.setConnectTimeout(20); // how long to try to connect for before continuing
wm.setConfigPortalTimeout(30); // auto close configportal after n seconds
wm.setMinimumSignalQuality(10); // set min RSSI (percentage) to show in scans, null = 8%
wm.setScanDispPerc(true); // show RSSI as percentage not graph icons
wm.setWiFiAutoReconnect(true); // if true, enable autoreconnecting
bool res;
res = wm.autoConnect("Co2M5Portal", "12345678"); // Подключение к анонимной точке доступа
if (!res)
{
Serial.println("Failed to connect or hit timeout");
ESP.restart();
}
setMqttServer(mqttHostName, mqttPort);
if (reqNtpTime() == 1)
{
reqNtpTime();
}
}
IPAddress str2IP(String str)
{
IPAddress ret(getIpBlock(0, str), getIpBlock(1, str), getIpBlock(2, str), getIpBlock(3, str));
return ret;
}
int getIpBlock(int index, String str)
{
char separator = '.';
int found = 0;
int strIndex[] = {0, -1};
int maxIndex = str.length() - 1;
for (int i = 0; i <= maxIndex && found <= index; i++)
{
if (str.charAt(i) == separator || i == maxIndex)
{
found++;
strIndex[0] = strIndex[1] + 1;
strIndex[1] = (i == maxIndex) ? i + 1 : i;
}
}
return found > index ? str.substring(strIndex[0], strIndex[1]).toInt() : 0;
}
int setMqttServer(const char *mqttHostNameF, unsigned int mqttPortF)
{
mdns_init();
IPAddress ipaddr;
OTApreferences.begin("ota-config");
OTApreferences.putString("mqttip", "0");
delay(1000);
String keyIpMqtt = OTApreferences.getString("mqttip", "0");
Serial.print("keyIpMqtt: ");
Serial.println(keyIpMqtt);
if (keyIpMqtt == "0")
{
//Запрос по хостнейму .local
ipaddr = MDNS.queryHost(mqttHostNameF, 4000);
//Таймаут 4 секунды
//Если не получили по хостнейму МДНС
Serial.print("mdns: ");
Serial.println(ipaddr);
if (ipaddr.toString() == "0.0.0.0")
{
//Запрос к ДНС по адресу сайта
int err = WiFi.hostByName(mqttHostNameF, ipaddr);
////real internal timeout in lwip library is 14[s]
//Если получили по хостнейму
if (err == 1)
{
Serial.print("hostByName: ");
Serial.println(ipaddr);
MqttClient.setServer(ipaddr, mqttPortF);
OTApreferences.putString("mqttip", ipaddr.toString());
}
else
{
Serial.print("All method not FOUND IP");
}
}
else
{ //Если все хорошо
OTApreferences.putString("mqttip", ipaddr.toString());
MqttClient.setServer(ipaddr, mqttPortF);
}
}
else
{
MqttClient.setServer(str2IP(keyIpMqtt), mqttPortF);
}
OTApreferences.end();
return 0;
}
void setServCall(IPAddress SetIpaddr)
{
MqttClient.setServer(SetIpaddr, mqttPort);
// MqttClient.setCallback(callback);
}
//Функция получения данных из MQTT если мы подпишемся на топики
// 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();
// }
//Запрос времени NTP и установка локлаьного времени
int reqNtpTime()
{
if (timeClient.update())
{
uint32_t timeEpoch = timeClient.getEpochTime();
if (timeEpoch < 1600000000)
{
Serial.println("Error reqNtpTime() Time");
return 1;
}
setTime(timeEpoch);
// Serial.print(timeEpoch);
// Serial.print(" <=ntp== ==device=> ");
// Serial.println(now());
}
return 0;
}
//Нажатие кнопки для сброса
void checkButton()
{
Serial.println("Button RESET Pressed");
disconnectMQTT();
delay(100);
wm.resetSettings();
OTApreferences.begin("ota-config");
OTApreferences.putString("mqttip", "0");
OTApreferences.end();
ESP.restart();
}
//Обновление прошивки, происходит проверка и загрузка
//Делается Get запрос на хостинг проверяется хеш, если хеш
void OTAUpdate()
{
////Serial.\println("OTAUpdate()");
bool flagOTA = false;
String keyOTA;
String payload;
OTApreferences.begin("ota-config");
if (WiFi.status() == WL_CONNECTED)
{
HTTPClient http;
String serverPath = "http://meteosence.s-host.net/airqa/airquality.php?meteopas=
&mac=" + macc + "&meteodata=gethash";
http.begin(serverPath.c_str());
int httpResponseCode = http.GET();
if (httpResponseCode > 0)
{
Serial.print("HTTP Response code: ");
Serial.println(httpResponseCode);
payload = http.getString();
Serial.println(payload);
if (payload != "errno" || payload != "errfi")
{
keyOTA = OTApreferences.getString("md5HashOTA");
if (keyOTA.length() <= 0)
{
OTApreferences.putString("md5HashOTA", "undifined");
}
keyOTA = OTApreferences.getString("md5HashOTA");
if (payload != keyOTA)
{
flagOTA = true;
OTApreferences.putString("md5HashOTA", payload);
//Serial.println("flagOTA = true;");
}
}
}
else
{
Serial.print("Error code HTTP: ");
Serial.println(httpResponseCode);
}
// Free resources
http.end();
}
else
{
Serial.println("WiFi Disconnected");
}
if (flagOTA == true)
{
flagOTA = false;
//Serial.println("flagOTA = false;");
t_httpUpdate_return ret = ESPhttpUpdate.update("http://meteosence.s-host.net/airqa/co2/co2.bin");
//После update ничего не происходит, такая вот особенность.
//Если все прошло хорошо, перезагрузка на новую прошивку
//Serial.print("ret ");
//Serial.println(ret);
switch (ret)
{
case HTTP_UPDATE_FAILED:
//Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
//Serial.println(ESPhttpUpdate.getLastError());
//Serial.println(ESPhttpUpdate.getLastErrorString().c_str());
//Serial.println("HTTP_UPDATE_FAILD Error");
ESP.restart();
break;
case HTTP_UPDATE_NO_UPDATES:
//Serial.println("HTTP_UPDATE_NO_UPDATES");
ESP.restart();
break;
case HTTP_UPDATE_OK:
//Serial.println("HTTP_UPDATE_OK");
ESP.restart();
break;
}
}
OTApreferences.end();
}
//Получение мак адреса
String getMacAddress()
{
uint8_t baseMac[6];
esp_read_mac(baseMac, ESP_MAC_WIFI_STA);
char baseMacChr[18] = {0};
sprintf(baseMacChr, "%02X%02X%02X%02X%02X%02X", baseMac[0], baseMac[1], baseMac[2], baseMac[3], baseMac[4], baseMac[5]);
return String(baseMacChr);
}
//Отправка данных по MQTT
int SendMqttReq(bool sendVal = true, bool sendStatus = true, byte statusConn = 1)
{
if (mqttSendFlag == true)
{
char resultString[200];
String JsonData = "";
rssi = WiFi.RSSI();
unsigned long timeNow = now();
// {
// "val": 1,
// "ts": 1617194420
// }
// Last device status (ts=59s ago):
// {
// "conn": "on",
// "ts": 1617204271,
// "exp": 1617204871
// }
if (sendVal == true)
{
if (typeSensor == true)
{
doc["t"] = airSens30.getTemperature();
doc["h"] = airSens30.getHumidity();
GlobalCo2 = airSens30.getCO2();
doc["co2"] = GlobalCo2;
doc["ts"] = timeNow;
serializeJson(doc, JsonData);
doc.remove("ts");
doc.remove("t");
doc.remove("h");
doc.remove("co2");
}
else
{
int CO2 = 0;
int8_t Tempz19 = 0;
/* note: getCO2() default is command "CO2 Unlimited". This returns the correct CO2 reading even
if below background CO2 levels or above range (useful to validate sensor). You can use the
usual documented command with getCO2(false) */
CO2 = myMHZ19.getCO2(); // Request CO2 (as ppm)
GlobalCo2 = CO2;
Serial.print("CO2 (ppm): ");
Serial.println(CO2);
Tempz19 = myMHZ19.getTemperature(); // Request Temperature (as Celsius)
Serial.print("Temperature (C): ");
Serial.println(Tempz19);
doc["t"] = Tempz19;
doc["co2"] = CO2;
doc["ts"] = timeNow;
serializeJson(doc, JsonData);
doc.remove("ts");
doc.remove("t");
doc.remove("co2");
}
doc.clear();
doc.garbageCollect();
Serial.println(JsonData); //Вывод JSON строки в консоль
JsonData.toCharArray(resultString, JsonData.length() + 1);
MqttClient.publish(bufTopic, resultString, true);
}
if (sendStatus == true)
{
const char *conn;
switch (statusConn)
{
case 1:
conn = "on";
break;
case 2:
conn = "off";
break;
case 3:
conn = "slp";
break;
case 4:
conn = "upd";
break;
default:
break;
}
doc["conn"] = conn;
doc["rssi"] = rssi;
doc["bsid"] = WiFi.BSSIDstr();
doc["ts"] = timeNow;
doc["exp"] = timeNow + 600;
doc["sv"] = VersionSW;
JsonData = "";
serializeJson(doc, JsonData);
Serial.println(JsonData); //Вывод JSON строки в консоль
doc.clear();
doc.garbageCollect();
JsonData.toCharArray(resultString, JsonData.length() + 1);
MqttClient.publish(bufWillTopic, resultString, true);
}
}
return 0;
}
//Переподключение при петери связи с MQTT
//7 раз проверили и вернулись в общий цикл что бы вдруг что втянуть обновления
void reconnectMqtt()
{
byte circle = 0;
while (!MqttClient.connected())
{
circle++;
if (circle == 5)
{
break;
}
String maccrandom = macc + String(random(0xffff), HEX);
const char *clientId = maccrandom.c_str();
if (MqttClient.connect(clientId, mqttLogin, mqttPass, bufWillTopic, 2, true, willmess))
{
Serial.println("Mqttconnect - OK");
mqttSendFlag = true;
SendMqttReq(false, true, 1);
}
else
{
Serial.print("Mqtt.state() = ");
Serial.println(MqttClient.state());
/*
-4 : MQTT_CONNECTION_TIMEOUT - the server didn't respond within the keepalive time
-3 : MQTT_CONNECTION_LOST - the network connection was broken
-2 : MQTT_CONNECT_FAILED - the network connection failed
-1 : MQTT_DISCONNECTED - the client is disconnected cleanly
0 : MQTT_CONNECTED - the client is connected
1 : MQTT_CONNECT_BAD_PROTOCOL - the server doesn't support the requested version of MQTT
2 : MQTT_CONNECT_BAD_CLIENT_ID - the server rejected the client identifier
3 : MQTT_CONNECT_UNAVAILABLE - the server was unable to accept the connection
4 : MQTT_CONNECT_BAD_CREDENTIALS - the username/password were rejected
5 : MQTT_CONNECT_UNAUTHORIZED - the client was not authorized to connect
*/
mqttSendFlag = false;
delay(500);
}
}
}
int printOled()
{
if (typeSensor == true)
{
Wire.begin(32, 26);
}
// if (display.begin(SSD1306_SWITCHCAPVCC, 0x3C))
// { // Address 0x3D for 128x64
Serial.println("PRINT OLED");
display.display();
delay(100);
// Clear the buffer
display.clearDisplay();
display.invertDisplay(true);
delay(500);
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.setCursor(10, 20);
display.println(String(GlobalCo2)+String(" ppm"));
display.setTextSize(1);
display.setCursor(5, 5);
display.println(String("-CO2- "));
display.display(); // Show initial text
delay(100);
// }
if (typeSensor == true)
{
Wire.begin(i2cSDA, i2cSCL);
}
return 0;
}
void loop()
{
M5.update();
if (WiFi.status() != WL_CONNECTED)
{
Serial.println(WiFi.status());
ESP.restart();
}
if (!MqttClient.connected())
{
reconnectMqtt();
}
if (millis() - timingSendMqtt > nextMqttSend)
{
SendMqttReq();
if (GlobalCo2 <= 320)
{
ledset(4); //Белый
}
else if (GlobalCo2 > 320 && GlobalCo2 <= 1000)
{
ledset(2); //зеленый
}
else if (GlobalCo2 > 1000 && GlobalCo2 <= 2000)
{
ledset(5); //желтый
}
else if (GlobalCo2 > 2000 && GlobalCo2 <= 5000)
{
ledset(1); //красный
}
if (GlobalOled == true)
{
printOled();
}
timingSendMqtt = millis();
}
// 40 секунд и происходит сброс настроек WIFI
if (M5.Btn.wasReleasefor(40000))
{
checkButton();
}
//По таймеру запруск обновления прошивки
if (millis() - timingUpdate > nextM5Update)
{
reqNtpTime();
SendMqttReq(false, true, 4);
OTAUpdate();
SendMqttReq(false, true, 1);
timingUpdate = millis();
}
//Таймер отправки данных в брокер
MqttClient.loop();
}
int disconnectMQTT()
{
MqttClient.disconnect();
return 0;
}

Binary file not shown.

Binary file not shown.