ESP, IoT Метеостанция народного мониторинга

S_Sergey_G

✩✩✩✩✩✩✩
23 Фев 2021
165
3
Позже внедрю в метеостанцию.
Вы внедрили это в метеостанцию? Поделитесь пожалуйста.
Сейчас одна esp постоянно сидит на розетке и ждет пакета ESP-NOW. А датчик работает от 2х батареек ААА
Можно полностью код на метеостанцию с ESP-NOW.
 
Изменено:

Dimel

✩✩✩✩✩✩✩
28 Янв 2021
6
0
Может быть глупый вопрос, но... Зачем ножки для перевода esp в deep sleep соединять через резистор 200 ом? Во всех примерах, что в интернетах соединяют напрямую.
P.s. просто на esp-01 неудобно резистор вешать, проще проводок
 

S_Sergey_G

✩✩✩✩✩✩✩
23 Фев 2021
165
3
@Dimel, возможно сопротивление ставили в ESP-01, там немного другая разводка. В ESP-01S ставлю перемычку без сопротивления.
 

Dimel

✩✩✩✩✩✩✩
28 Янв 2021
6
0
@S_Sergey_G, да одинаковая там схема. Вернее ее отсутствие. Вывод gpio 16 просто висит в воздухе. Кстати на вемосах мини последних на плате даже контактные площадки есть, повесив на которые соплю припоя можно в deep sleep переводить.
 

S_Sergey_G

✩✩✩✩✩✩✩
23 Фев 2021
165
3
Помогите отправлять данные на локальный MQTT.
Код от p-a-h-a.
C++:
#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; //ушло
}