#include <ESP8266WiFi.h>
//Настройки скетча:
#define debug false // Вывод отладочных сообщений. Замедляет работу. false по умолчанию
#define SendData true // Отправлять данные на сервер (для отладки). true по умолчанию
#define postingInterval 330e6 // интервал между отправками данных в секундах (330 сек=5,5 минут)
#define MinVccSleep 0e9 // Время спячки при севших батарейках. 0 - до передергивания питания.
#define VccDiode 840//Падение напряжения (мВ) на диоде в питании ESP от литиевого АКБ для коректировки показаний напряжения питания 3,95/3,49
#define maxVCC 4150// Максимальное напряжение питания. При превышении ЕСП не отключается а садит батарею.
#define minVCC 2900 // Минимальное напряжение питания.
#define swRx 3 // Не меняем, это пины UART
#define swTx 1 // Не меняем
#define swSCL 3 // Пины подключения датчика BMx280 IO3
#define swSDA 0 // Пины подключения датчика BMx280
#define measurePin 2 // сюда фоторезистор и на + а также конденсатор 2,2 мкФ и на минус.
#define ssid "****"
#define password "***"
IPAddress local_IP(192, 168, 1, 90);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
IPAddress primaryDNS(192, 168, 1, 1);
IPAddress secondaryDNS(8, 8, 4, 4);
#include "SoftwareSerial.h"
SoftwareSerial swSerial;
#include <Wire.h>
#include <BMx280I2C.h>
byte bmx_not_found = false;
BMx280I2C bmx280(0x76);
void ICACHE_RAM_ATTR InterruptMeasurePin();//ставим функцию прерываний InterruptMeasurePin() в RAM память для стабильной работы
uint32_t RCtime1;// тут хранится начальное время RC цепи
volatile uint32_t RCtime2;// тут хранится конечное время RC цепи
boolean LightSensor_not_found = false;
unsigned long TimeWIFI;// Переменная времени подключения к WIFI
ADC_MODE(ADC_VCC);// Будем измерять напряжение на VCC внутри МК
int VCC; //Напряжение батареи
uint32_t calculateCRC32(const uint8_t *data, size_t length);
struct { //Структура, хранящаяся в RTC памяти
uint32_t crc32;
uint32_t data[3];// Если нужно еще что-то хранить во сне то увеличиваем индекс 1 на нужное кол-во переменных и работаем как с переменной rtcData.data[1].... rtcData.data[125].
} rtcData;
uint32_t calculateCRC32(const uint8_t *data, size_t length) {// Расчет CRC RTC памяти чтоб понять битые там данные или нет
uint32_t crc = 0xffffffff;
while (length--) {
uint8_t c = *data++;
for (uint32_t i = 0x80; i > 0; i >>= 1) {
bool bit = crc & 0x80000000;
if (c & i) {
bit = !bit;
}
crc <<= 1;
if (bit) {
crc ^= 0x04c11db7;
}
}
}
return crc;
}
//Читаем данные из RTC памяти
void RTCread() {
static boolean firstRUN = true;
if (ESP.rtcUserMemoryRead(0, (uint32_t*) &rtcData, sizeof(rtcData))) {
uint32_t crcOfData = calculateCRC32((uint8_t*) &rtcData.data[0], sizeof(rtcData.data));
if (crcOfData != rtcData.crc32) {// При первом включении и при битых данных в RTC памяти
rtcData.data[0] = 1; // Назначаем данные по умолчанию при первом включении
rtcData.data[1] = 0;
rtcData.data[2] = 0;
}
}
}
//Обновляем данные в RTC памяти
void RTCupdate() {
rtcData.data[0]++;// Увеличиваем количество срабатываний
rtcData.data[1] = millis();// Запоминаем время передачи
rtcData.data[2] = TimeWIFI;//// Запоминаем время соединения WIFI
rtcData.crc32 = calculateCRC32((uint8_t*) &rtcData.data[0], sizeof(rtcData.data));
ESP.rtcUserMemoryWrite(0, (uint32_t*) &rtcData, sizeof(rtcData));// Write struct to RTC memory
}
// процедура подключения к Wifi. Также запускаем измерения с датчиков
void wificonect() {
WiFi.mode(WIFI_STA);
WiFi.setAutoConnect(true);
WiFi.setAutoReconnect(false);
WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS);
WiFi.begin(ssid, password);
int i = 0;
while (WiFi.status() != WL_CONNECTED) {
static boolean firstRUN = true;
if (firstRUN) { // один раз запускаем при каждой перезагрузке
RTCread();// Пока конектится wifi читаем переменніе из RTC памяти что сохранили перед прошлым сном
Measure();// Запускаем измерения датчиков
}
firstRUN = false;
if (debug) if (!(i % 100))swSerial.print (".");
delay(1);
i++; if (i > 5000) ESP.deepSleep(60e6, RF_NO_CAL);//СПИМ 1 МИНУТу если не было соединения в течении 5 секунд. Если у вас плохой конект с wifi то следует увеличить число до 10000
}
TimeWIFI = millis();// Запоминаем время подключения к WIFI
if (debug) {
swSerial.println();
if (!SendData) swSerial.println F("Отладочный режим. Данные на сервер не передаются.");
swSerial.print (millis());// тут можно посмотреть время подключения к wifi
swSerial.println F(" Подключено к wifi");
}
}
void setup() {
if (debug) {
swSerial.begin(115200, SWSERIAL_8N1, swRx, swTx, false, 256);//
swSerial.enableIntTx(false);
}
VCC = ESP.getVcc(); //Корректировка напряжения АКБ
if (VCC < minVCC) ESP.deepSleep(MinVccSleep); // спим при низком питании
wificonect();// подключаемся к сети
}
void loop() {
SendToNarodmon();
//digitalWrite(BMX280_VCC, LOW);// отключаем питание датчиков
RTCupdate();
if (debug) swSerial.print (millis());
if (VCC > maxVCC) {
digitalWrite(LED_BUILTIN, LOW);
delay(postingInterval / 1000); //разряжаем батарею
ESP.deepSleep(1e6, RF_NO_CAL); //перезагружаемся
} else {
ESP.deepSleep(postingInterval, RF_NO_CAL);
}
}
void Measure() { // Запуск измерения датчиков
//Запуск измерения освещенности посредством времени RC цепи.
pinMode(measurePin, OUTPUT);//разряжаем конденсатор
delay(1);
attachInterrupt (digitalPinToInterrupt (measurePin), InterruptMeasurePin, CHANGE);// включаем прерывание которое сработает при заряде конденсатора до логической единицы
pinMode(measurePin, INPUT);// начало заряда
RCtime1 = micros();// запоминаем время начала заряда конденсатора
if (digitalRead(measurePin)){
LightSensor_not_found = true;
detachInterrupt (digitalPinToInterrupt(measurePin));
}
//----------------------------------------------------
//BMX280
Wire.pins(swSDA, swSCL);//Настройка програмного I2C интерфейса
Wire.begin(swSDA, swSCL);
if (!bmx280.begin()) {
if (debug) swSerial.println F("Датчик BMx не обнаружен. Проверьте датчик.");
bmx_not_found = true;
}
if (debug && !bmx_not_found) {
swSerial.print F("Тип датчика ");
if (bmx280.isBME280())swSerial.println F("BME280");
else swSerial.println F("BMP280");
}
if (!bmx_not_found) { // Если датчик найден
bmx280.resetToDefaults(); //reset sensor to default parameters.
bmx280.writeOversamplingPressure(BMx280MI::OSRS_P_x16);
bmx280.writeOversamplingTemperature(BMx280MI::OSRS_T_x16);
if (bmx280.isBME280()) bmx280.writeOversamplingHumidity(BMx280MI::OSRS_H_x16);//Для BME
for (int i= 0; i<2; i++){
while (!bmx280.measure()) {}
while (!bmx280.hasValue()) {}
}
}
}
void InterruptMeasurePin() {// по прерыванию в момент заряда конденсатора запоминаем время
RCtime2 = micros();
}
bool SendToNarodmon() { // Собственно формирование пакета и отправка.
if (!bmx_not_found)
bmx280.measure();
if (debug) {
swSerial.print F("ip адресс: ");swSerial.println(WiFi.localIP()); swSerial.print F("Narodmon ID: ");
}
WiFiClient client;
String buf = "#ESP" + WiFi.macAddress() + "\n"; //mac адрес для авторизации датчика
buf.replace(":", "");// удаляем двоеточия из мак адреса
if (!bmx_not_found) { // если bmx подключен то выводим с него данные
while (!bmx280.hasValue()) {}
buf = buf + "#TEMPC#" + String(bmx280.getTemperature()) + "#Датчик температуры BMx280\n"; //показания температуры
if (bmx280.isBME280()) buf = buf + "#HUMID#" + String(bmx280.getHumidity()) + "#Датчик влажности BME280\n"; //показания влажности
buf = buf + "#PRESS#" + String(bmx280.getPressure64()) + "#Датчик давления BMx280\n"; //показания давления
}
if (!LightSensor_not_found){ // Если датчик освещенности с конденсатором подключены
if (!RCtime2){//Если конденсатор не успел зарядится
RCtime2=micros();
}
uint32_t Lux = constrain((RCtime2-RCtime1),1,200000);
Lux = cbrt(Lux)*10;
Lux = map(Lux, 1, 585, 585, 1);//попугаи света.
buf = buf + "#LIGHT#" + String(Lux) + "#Освещенность\n";
}
if (SendData) client.connect("narodmon.ru", 8283); // подключение
//Данные от ESP ( Напряжение питания,уровень wifi
buf = buf + "#VCC#" + String(VCC) + "#Напряжение батареи\n"; //показания температуры
buf = buf + "#WIFI#" + String(WiFi.RSSI()) + "#Уровень WI-FI " + String(WiFi.SSID()) + "\n"; // уровень WIFI сигнала
String worcktime = String(rtcData.data[2]); // Время соединения с wifi при предыдущей отправке
float WTime = worcktime.toInt(); WTime /= 1000;
buf = buf + "#UPTIME#" + String(WTime) + "#Время соединения с WIFI\n";
worcktime = String(rtcData.data[1]); // Время работы при предыдущей отправке
WTime = worcktime.toInt(); WTime /= 1000;
buf = buf + "#WORKTIME#" + String(WTime) + "#Время передачи данных\n";
buf = buf + "#WM#" + String(rtcData.data[0]) + "#№ показаний\n";
buf = buf + "##\n"; //окончание передачи
if (SendData) client.print(buf); // и отправляем данные
if (debug) {
swSerial.print(buf);
}
delay(10);// сделать 100 если нужен ответ или 10 если не нужен . Время активности увеличивается в 2 раза
if (SendData) {
while (client.available()) {
String line = client.readStringUntil('\r'); // если что-то в ответ будет - все в Serial
if (debug) {
swSerial.println(line);
}
}
}
return true; //ушло
}