Метеостанция для народного мониторинга

p-a-h-a
Offline
Зарегистрирован: 17.01.2019

Собрал метеостанцию, данніе обновляются каждіе 15 минут+-30сек. Тут можно посмотреть с нее данные https://narodmon.ru/5928 Посмотрел пример с narodmon.ru под ардуино. Перепилил. Работает от двух батареек типа D, сейчас перевел на питание от LiFePo4 c подзарядкой от солнечной батарейки до 3,37В. Умеет работать с датчиком BME280, BMP280, c одним или несколькими DS18B20. Автоматически определяет наличие датчиков и шлет по ним инфу на сервер. При первом запуске (или отсутствии сети) создает точку доступа на 5 секунд (нужно успеть подключится, сделано с целью экономии батареек при пропадании wifi). В веб интерфейсе можно просканировать сети и подключится к имеющийся либо вписать название сети вручную. после чего на сайт начинают отправлятся данные а в SERIAL печатается уникальный ID станции, который потом привяжется к сайту. Дополнительно станция отсылает напряжение VCC, уровень сигнала wifi в %. Все показания, статистику на графиках можно смотреть в приложениях для разных платформ в том числе Android, IOS.

Лучше собирать на голой ESP. но под руками был только NOD MCU.

Схема заряда работает так: При достижении на солнечной батареи напряжения 3,7В (примерно) стабилизатор tl431 шунтирует солнечную батарею рассеивая излишек энергии в тепло. Далее на диоде падает примерно 0,4В и на выходе получаем 3,3В. В моих условиях солнечная батарея выдает максимум 8 мА тока(теневая сторона). tl321 может взять на себя до 100 мА. Соответственно не стоит брать мощную солнечную батарею.

И собственно код для Arduino IDE:

#include <FS.h>
#include <ESP8266WiFi.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>          //https://github.com/tzapu/WiFiManager
#include <DallasTemperature.h>
#include <Wire.h>
#include <BMx280I2C.h>
byte bmx_not_found = false;
BMx280I2C bmx280(0x76);
ADC_MODE(ADC_VCC);// Будем измерять напряжение на VCC внутри МК
#define debug false // вывод отладочных сообщений
#define postingInterval  300000 // интервал между отправками данных в миллисекундах (5 минут)
#define ONE_WIRE_BUS 14 // GPIO к которому подключен DS18B20
#define TEMPERATURE_PRECISION 10 // точность бит.DS18B20 Если глючит или врет, уменьшить до 9
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
int NumberOfDevices; //сколько датчиков найдем.
unsigned long lastConnectionTime = 0;           // время последней передачи данных
String Hostname; //имя железки - выглядит как ESPAABBCCDDEEFF т.е. ESP+mac адрес.
void wifimanstart() { // Волшебная процедура начального подключения к Wifi.
  // Если не знает к чему подцепить - создает точку доступа ESP8266 и настроечную таблицу http://192.168.4.1
  // Подробнее: https://github.com/tzapu/WiFiManager
  WiFiManager wifiManager;
  wifiManager.setTimeout(1);//устанавливает время ожидания до выключения портала конфигурации полезно, чтобы все повторилось или пошло спать в секундах
  //следующие 4 строки не обязательны - ускоряют соединение
  
//  IPAddress _ip = IPAddress(192, 168, 0, 58);
//  IPAddress _gw = IPAddress(192, 168, 0, 1);
//  IPAddress _sn = IPAddress(255, 255, 255, 0);
//  wifiManager.setSTAStaticIPConfig(_ip, _gw, _sn);
  
  wifiManager.setDebugOutput(debug);
  wifiManager.setMinimumSignalQuality(1);//минимальное качество сигнала в % для попытки соединится
  if (!wifiManager.autoConnect("meteostation")) {
    Serial.println("failed to connect and hit timeout");
    //Если не видно интернет то включаем точку доступа на 5 секунд и если не успели подключиться то спим 20 минут.
    ESP.deepSleep(930e6);//СПИМ 20 МИНУТ
  }
  if (debug) Serial.println("connected...");
}

void setup() {
  pinMode(12, OUTPUT); digitalWrite(12, HIGH); //d6
  pinMode(13, OUTPUT); digitalWrite(13, HIGH); //d7
  DeviceAddress tempDeviceAddress;
  Serial.begin(115200);
  sensors.begin(); //ds18b20
  NumberOfDevices = sensors.getDeviceCount(); //поищем.
  for (int i = 0; i < NumberOfDevices; i++) {
    if (sensors.getAddress(tempDeviceAddress, i)) sensors.setResolution(tempDeviceAddress, TEMPERATURE_PRECISION);  //настроим.
  }
  sensors.requestTemperatures(); //Начали измерение ds18b20

  Wire.begin();
  if (!bmx280.begin()) {
    Serial.println("begin() failed. check your BMx280 Interface and I2C Address.");
    bmx_not_found = true;
  }
  if (debug){  if (bmx280.isBME280())Serial.println("sensor is a BME280"); else Serial.println("sensor is a BMP280");}
  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
  while (!bmx280.measure()) {
    delay(10); // Ждем измерение
  }
  while (!bmx280.hasValue()) {
    delay (10);
  }
   wifimanstart();
  Hostname = "ESP" + WiFi.macAddress();
  Hostname.replace(":", "");


  WiFi.hostname(Hostname);
  //wifiManager.resetSettings();   сброс настроек wifi

 

  if (debug){Serial.println(WiFi.localIP()); Serial.println(WiFi.macAddress()); Serial.print("Narodmon ID: "); Serial.println(Hostname);}
  lastConnectionTime = millis() - postingInterval + 15000; //первая передача на народный мониторинг через 15 сек.
}

bool SendToNarodmon() { // Собственно формирование пакета и отправка.
  bmx280.measure();
  DeviceAddress tempDeviceAddress;

  WiFiClient client;
  String buf;
  buf = "#" + Hostname + "\n"; //mac адрес для авторизации датчика
  while (!bmx280.hasValue()) {
    delay (5);
  }
  if (bmx_not_found == false) { // если bmx подключен то выводим с него данные
    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"; //показания давления
  }
  for (int i = 0; i < NumberOfDevices; i++)  { //перечисляем датчики 18b20 и их показания
    sensors.getAddress(tempDeviceAddress, i);
    // buf = buf + "#TEMPC#";
    buf = buf + "#";
    for (uint8_t i = 0; i < 8; i++) {
      if (tempDeviceAddress[i] < 16) buf = buf + "0";  // адрес датчика
      buf = buf  + String(tempDeviceAddress[i], HEX);
    }
    buf = buf + "#" + String(sensors.getTempCByIndex(i)) + "#DS18B20 №" + String(i + 1) + "\n"; //и температура
  }
  buf = buf + "#VCC#" + String(ESP.getVcc() + 350) + "#Напряжение батареи\n"; //показания температуры
  int WIFIRSSI=(WiFi.RSSI()+100)*2;
  constrain(WIFIRSSI, 0, 100);
  buf = buf + "#WIFI#"  + String(WIFIRSSI) + "#Уровень WI-FI " + String(WiFi.SSID()) + "\n"; // уровень WIFI сигнала
  buf = buf + "##\n"; //окончание передачи

  // попытка подключения
  if (!client.connect("narodmon.ru", 8283)) {
    Serial.println("connection failed"); return false; // не удалось;
  }
  else  {
    client.print(buf); // и отправляем данные
    if (debug) Serial.print(buf);
    delay(100);
    while (client.available()) {
      String line = client.readStringUntil('\r'); // если что-то в ответ будет - все в Serial

      //Serial.print(line);
    }
  }

  return true; //ушло
}

void loop() {
  yield();
  if (WiFi.status() == WL_CONNECTED) { // ну конечно если подключены
      if (SendToNarodmon()) {
      if (debug){Serial.print (millis());}
      digitalWrite(12, LOW); digitalWrite(13, LOW);// отключаем питание датчиков
      ESP.deepSleep(930e6);
    } else {ESP.deepSleep(930e6);}
  }
  yield();
}

Вот что вышло:

p-a-h-a
Offline
Зарегистрирован: 17.01.2019

Библиотеки, без ссылок стандартные или добавляются через стандартный репозиторий.

Перед прошивкой рекомендую заменить #define debug false на true и посмотреть что там в serial мониторе

  wifiManager.setMinimumSignalQuality(1); тут время в секундах когда появляется точка доступа

ESP.deepSleep(930e6) 930 - время в секундах интервалов отправки данных на сервер.

PS Гайвер бог ардуино. Жду комментов)

sadman41
Онлайн
Зарегистрирован: 19.10.2016

А где тут Ардуино?

p-a-h-a
Offline
Зарегистрирован: 17.01.2019

Код написан в среде разработки Arduino ide.

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

p-a-h-a пишет:

PS Гайвер бог ардуино.

а пошто ты ему на форум это не вывалил? 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

DetSimen пишет:

p-a-h-a пишет:

PS Гайвер бог ардуино.

а пошто ты ему на форум это не вывалил? 

да ладно, мне как раз для мониторинга кондеев надо было, а тут и скетч подоспел, осталось дописать строку передачи на GSM шлюз, голые ESP имеются )))

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

ua6em пишет:

да ладно, мне как раз для мониторинга кондеев надо было, а тут и скетч подоспел

Ни в коем случае этот говнокод не бери, он непродуман чуть более, чем полностью. 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

p-a-h-a, прекратите заниматься жирным троллингом.

При возникновении малейшего срача, тема будет снесена.

p-a-h-a
Offline
Зарегистрирован: 17.01.2019

Подскажите, как правильно соединятся с wifi без dhcp? Читал про UDP, но где найти инфу? 3,5 сек работы очень много для батареек. Как уменьшить время соединения?
PS. Как на форуме встретили, так и приходится общатся.

sadman41
Онлайн
Зарегистрирован: 19.10.2016

И что про UDP пишут - хорошая вещь или плохая? Куда ее Гивер припаивать рекомендует?

Без DHCP штоб работать - это Static IP прописать ESP требуется.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

DetSimen пишет:

ua6em пишет:

да ладно, мне как раз для мониторинга кондеев надо было, а тут и скетч подоспел

Ни в коем случае этот говнокод не бери, он непродуман чуть более, чем полностью. 

может предложишь что готовое, на esp8266, что требуется - мониторинг температуры c записью на SD карточку раз в пять минут вполне разумно, если температура превышает заданную, то  отправляется строка запроса (ссылка http) - эта та, которая поднимет меня среди ночи и погонит устранять неисправность )))

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

ua6em пишет:

может предложишь что готовое

Нет. 

p-a-h-a
Offline
Зарегистрирован: 17.01.2019

Я бы копал в сторону использования RTC памяти (512 байт) и в нее бы писал при каждом пробуждении данные. Это если лог большой на флешке не нужен. Да и встроенной в esp флеш памяти предостаточно.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

DetSimen пишет:

ua6em пишет:

может предложишь что готовое

Нет. 

вот так всегда )))
Я тут подумал, подумал и зачем эта мышиная возня с SD коль доступна целая база данных, она кстати может накапливать данные со многих датчиков, что сильно упрощает обслуживание, и кое что нашлось )))

p-a-h-a
Offline
Зарегистрирован: 17.01.2019

Собственно как забрать данные с народмона другой ESP

#include <ArduinoJson.h>
#define SensorID "54516,51830,51843,54519,52446,54544,57282,4309" // номера конкретных датчиков с народмона
#define number_of_sensors 8 // количество опрашиваемых датчиков из строки выше

#define debug true
void POST_Narodmon() {
  HTTPClient http;
  http.begin("http : // narodmon. ru/api");//убрать все пробелы
  http.addHeader("Content-Type", "application/x-www-form-urlencoded");

  StaticJsonDocument<400> doc; // Создаем и наполняем json для последующей отправки на сервер
  doc["cmd"] = "sensorsValues";
  doc["sensors"] = SensorID;
  doc["uuid"] = uuid;// это md5 mac адреса ESP которой запрашиваем данные
  doc["api_key"] = api_key; // берется из личного кабинета народного мониторинга

  http.POST(doc.as<String>());// Json запрос на сервер
  if (debug) Serial.println(doc.as<String>());
  doc.clear();
  DynamicJsonDocument Answer(1532);                                 // Инициализируем буфер под JSON // Эта константа определяет размер буфера под содержимое JSON 8ми датчиков (расчитывается тут https://arduinojson.org/v5/assistant/)
  deserializeJson(Answer, http.getString());                     // Парсим JSON-содержимое ответа сервера
  http.end();
  //Serial.println(Answer.as<String>()); //Выводим содержимое что прислал сервер
  if (debug) {
    serializeJsonPretty(Answer, Serial);  //Выводим содержимое что прислал сервер красиво по строчкам
    Serial.println();
  }

  for (int i = 0; i < number_of_sensors; i++) {// Парсим значение датчиков и выводим в сериал
    if (debug) Serial.println(Answer["sensors"][i]["value"].as<String>());//тут выводятся значения, полученные с датчиков где i номер фигурных скобок из json ответа, по факту номер датчика,
    if (Answer["sensors"][i]["value"] == "null") {
      //LOGIN_Narodmon(); //отдельная функция для авторизации в случае использования приватных датчиков
      break;
    }
  }
Serial.println("Время передачи " + Answer["sensors"][0]["value"].as<String>() + " сек");
Serial.println("Влажность " + Answer["sensors"][1]["value"].as<String>() + " %");
Serial.println("Атмосферное давление " + Answer["sensors"][2]["value"].as<String>() + " мм рт. ст.");
Serial.println("Температура на улице " + Answer["sensors"][4]["value"].as<String>() + " градусов цельсия");
Serial.println("Напряжение аккумулятора " + Answer["sensors"][6]["value"].as<String>() + " В");
Serial.println("Уровень сигнала WI-FI " + Answer["sensors"][7]["value"].as<String>() + "%");

  Answer.clear();
 }

Только не берите этот говнокод никуда. Он не оптимизирован.