Site logo

Developer Blog

Pavel Koltyshev

Метеостанция на ионисторе

Содержание

В этой статье я расскажу об одном из своих любительских проектов на ESP8266 - метеостанции на Arduino.

Да! Это очередная метеостанция на Arduino :)

Но не спешите закрывать эту статью, у этого проекта есть своя изюминка, а именно автономная работа без использования аккумуляторов и батарей в суровых условиях уральской зимы.

Для профессионалов эта статья будет не очень интересна, а для тех, кто любит мастерить своими руками и узнавать что-то новое - прошу к прочтению.

В статье я буду часто говорить про Arduino, притом что собственно сама Arduino Uno (или другая модель) в проекте не использовалась. Но поскольку использовались SDK Arduino и Arduino-библиотеки, будем считать этот проект Arduino-проектом.

Идея

Метеостанций на Arduino такое великое множество, что это, наверное, самый популярный проект после моргания светодиодом у начинающих.

Мне хотелось чтобы, у меня за окном на балконе было какое-то устройство, собирающее погодные данные: температуру, влажность и давление. Также устройство будет измерять напряжение питания, чтобы можно было понимать, как все хорошо/плохо с питанием в ночное время суток.

Устройство должно работать 24/7 и не требовать обслуживания.

Данные должны передаваться в реальном времени после каждого изменения (раз в полчаса) по Wi-Fi на домашний MQTT-сервер. Результаты измерений можно будет посмотреть в панели управления с примитивной веб-мордой.

Самое сложное определиться с питанием устройства. Идею тянуть провода на балкон я сразу отмел как эстетически неправильную. Литиевый аккумулятор тоже не подойдет, особенно в условиях уральской зимы он будет быстро разряжаться и деградировать. Литиевая батарея, возможно, бы подошла, но не для питания ESP и точно не для подключения по Wi-Fi.

Для питания метеостанции решено было поставить солнечную батарею и ионистор для накопления энергии. Были опасения, что в ночное время суток ESP съест весь заряд ионистора. Но даже если бы это и случилось, то ионистор не боится полного разряда, в отличие от литиевых аккумуляторов.

Ионистор для питания ESP8266 хорош еще тем, что может отдавать очень высокий ток. А у ESP8266 всегда была с этим проблема, при недостаточном питании по току - модуль может зависнуть. Такое бывает, например, если вы подключаете ESP8266 к DC-DC преобразователю питания, который не дает нужное значение по току.

Компоненты

Набор компонентов

Собираем устройство

ESP-01

ESP-01

Как я писал выше в ESP-01 есть проблема с выходом из глубокого сна. ESP-01 можно легко отправить в глубокий сон вызвав ESP.deepSleep(timeout, mode), но модуль никогда из него не выйдет. Для решения этой проблемы мы должны соединить контакты RESET и GPIO16. Это довольно непросто сделать из-за малого размера контактов. Когда таймаут нахождения в глубоком сне истечет, то на контакте GPIO16 появится низкий уровень напряжения (LOW), будучи подключенным к RESET, он перезагрузит ESP-01.

ESP-01 доработанный

Также нам нужно добавить немного обвязки для корректной работы ESP-01:

Питание

Питание модуля

Ионистор подключаем напрямую к выходным контактам +/- DC-DC преобразователя. К этим же контактам подключаем ESP-01 и сенсоры (к VCC и GND).

На вход DC-DC преобразователя подключим солнечную батарею. Перед плюсовым контактом DC-DC преобразователя поставим диод Шоттки, чтобы в ночное время суток ионистор не начал разряжаться через солнечную батарею.

Ионистор

Пару слов об ионисторе. Я использовал ионистор или как еще его называют суперконденсатор на 100F, 3.8V. Он мне понравился малым саморазрядом. Для питания ESP-01 ионистор дает хороший ток, модуль у меня ни разу не зависал. Стоят ионисторы немало, ионистор на 100F обошелся мне в 800 руб. По виду и весу ионистор напоминает обычный электролитический конденсатор. Также нужно, иметь в виду что, ионисторы не вечны, в зависимости от условий эксплуатации он может прослужить вам от 5 до 10 лет.

Солнечные батареи

Блоки солнечных батарей были соединены параллельно и приклеены на пластиковую доску.

Солнечная панель

Солнечная панель

Готовая панель с 8 солнечными батареями была аккуратно размещена на балконе. Наверное, можно было поиграться с углом падения солнечных лучей и придумать более удачное решение для наибольшей эффективности. Но я решил не заморачиваться с этим.

Солнечную панель я поставил впритык к окну на улицу, чтобы как можно больше света попадало на нее. Было бы еще эффективнее вынести ее на улицу, но для этого пришлось бы делать герметизацию.

Солнечная панель

Модули датчиков

Все модули датчиков подключаем по I2C к ESP-01. Питание у датчиков будет такое же, как у ESP-01 (3.3V).

Я использовал обычные гребенки с контактами, чтобы можно было легко доставать модули (для других проектов). Думаю, что для более правильной версии проекта контакты должны быть припаяны оловом для надежности соединения.

Модули сенсоров

Корпус устройства

К корпусу устройства были надежно приклеены магниты для крепления на подоконнике. Сам корпус был обработан герметиком.

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

Пластиковый колпачок используется для вывода вентиляционных отверстий. Под внешним колпачком находится еще один защищающий от влаги. Смысл в том, чтобы воздух мог достигнуть датчиков, а вот для влаги это было бы проблематично.

При наличии 3D принтера уверен, что можно придумать что-то в разы лучше.

Корпус устройства Корпус устройства

Схема подключения модулей

Схема подключения модулей

Пишем прошивку

Для программирования я использовал PlatformIO.

Код прошивки:

// main.cpp
#include <Adafruit_BMP280.h>
#include <Adafruit_HTU31D.h>
#include <Arduino.h>
#include <ArduinoJson.h>
#include <ESP8266WiFi.h>
#include <INA226_WE.h>
#include <PubSubClient.h>
#include <Wire.h> // I2C

#define uS_TO_M_FACTOR 6e7                            // Количество микросекунд в минуте
#define DEEP_SLEEP_TIMEOUT (int)(30 * uS_TO_M_FACTOR) // Продолжительность глубокого сна (в минутах)
#define RECONNECTION_LIMIT 3 // Число допустимых попыток переподключения
#define RECONNECTION_DELAY 500 // Задержка перед переподключением (в миллисекундах)
#define WIFI_SSID "ENTER WI-FI SSID"
#define WIFI_PASSWORD "ENTER WI-FI PASSWORD"
#define MQTT_SERVER "ENTER MQTT SERVER IP ADDRESS"
#define MQTT_PORT 1883
#define MQTT_CLIENT_ID "Weather Station"
#define MQTT_TOPIC "devices/weather-station/status"

#define BMP280_I2C 0x76
#define HTU31D_I2C 0x40
#define INA226_I2C 0x41

Adafruit_BMP280 bmp = Adafruit_BMP280();
Adafruit_Sensor *bmp_pressure = bmp.getPressureSensor();
Adafruit_HTU31D htu = Adafruit_HTU31D();
INA226_WE ina226 = INA226_WE(INA226_I2C);
WiFiClient espClient;
PubSubClient client(espClient);

struct sensors_data {
  float temperature;
  float humidity;
  float pressure;
  float voltage;
};

struct sensors_data sensors_data;

float convertHumidity(float hPa) {
  float one_mmHg_in_Pa = 101325.0 / 760.0;
  return (hPa * 100.0) / one_mmHg_in_Pa;
}

void bmpEnable(bool val) {
  if (val) {
    bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, Adafruit_BMP280::SAMPLING_X16, Adafruit_BMP280::SAMPLING_X16,
                    Adafruit_BMP280::FILTER_X16, Adafruit_BMP280::STANDBY_MS_1);
  } else {
    bmp.setSampling(Adafruit_BMP280::MODE_SLEEP, Adafruit_BMP280::SAMPLING_NONE, Adafruit_BMP280::SAMPLING_NONE,
                    Adafruit_BMP280::FILTER_OFF, Adafruit_BMP280::STANDBY_MS_4000);
  }
}

void update_sensors_data() {
  // HTU sensor
  htu.enableHeater(true); // Enable power
  sensors_event_t humidity_event, temperature_event;
  htu.getEvent(&humidity_event, &temperature_event);
  htu.enableHeater(false); // Disable power
  sensors_data.temperature = temperature_event.temperature;
  sensors_data.humidity = humidity_event.relative_humidity;

  // BMP sensor
  bmpEnable(true);
  sensors_event_t pressure_event;
  bmp_pressure->getEvent(&pressure_event);
  bmpEnable(false);
  sensors_data.pressure = convertHumidity(pressure_event.pressure);

  // INA sensor
  ina226.powerUp();
  ina226.readAndClearFlags();
  sensors_data.voltage = ina226.getBusVoltage_V();
  ina226.powerDown();
}

bool wifi_connect() {
  if (WiFi.SSID() != "") {
    WiFi.begin(); // Быстрое подключение к ранее запомненной Wi-Fi точке (обычно менее 1 сек).
  } else {
    WiFi.persistent(true); // Разрешить запись данных о последнем Wi-Fi подключении в память
    WiFi.mode(WIFI_STA);
    WiFi.setAutoConnect(true); // При включении подключаться к последней точке доступа
    WiFi.setAutoReconnect(false); // Переподключаться при неудачном подключении
    WiFi.begin(WIFI_SSID, WIFI_PASSWORD); // Долгое подключение (обычно около 5 сек).
  }
  int conn_result = WiFi.waitForConnectResult();
  return conn_result == WL_CONNECTED;
}

void mqtt_subscribe_cb(char *topic, byte *payload, unsigned int length) {}

bool send_message(char *message) {
  client.setServer(MQTT_SERVER, MQTT_PORT);

  int count = 0;
  while (!client.connect(MQTT_CLIENT_ID)) {
    count += 1;
    if (count == RECONNECTION_LIMIT) {
      return false;
    }
    delay(RECONNECTION_DELAY);
  }

  client.publish(MQTT_TOPIC, message);
  client.disconnect();
  return true;
}

bool send_sensors_message() {
  StaticJsonDocument<256> doc;
  char message[256];
  doc["temperature"] = sensors_data.temperature;
  doc["humidity"] = sensors_data.humidity;
  doc["pressure"] = sensors_data.pressure;
  doc["voltage"] = sensors_data.voltage;
  serializeJson(doc, message);
  return send_message(message);
}

bool start_htu() {
  int count = 0;
  while (!htu.begin(HTU31D_I2C)) {
    count += 1;
    if (count == RECONNECTION_LIMIT) {
      return false;
    }
    delay(RECONNECTION_DELAY);
  };
  htu.enableHeater(false);
  return true;
}

bool start_bmp() {
  int count = 0;
  while (!bmp.begin(BMP280_I2C)) {
    count += 1;
    if (count == RECONNECTION_LIMIT) {
      return false;
    }
    delay(RECONNECTION_DELAY);
  }
  bmpEnable(false);
  return true;
}

bool start_ina() {
  int count = 0;
  while (!ina226.init()) {
    count += 1;
    if (count == RECONNECTION_LIMIT) {
      return false;
    }
    delay(RECONNECTION_DELAY);
  }
  ina226.powerDown();
  return true;
}

void setup() {
#if defined(BOARD_ESP_01S) || defined(BOARD_ESP_01)
  Wire.begin(2, 0); // Настройка I2C (SDA, SCL)
#endif

#ifdef BOARD_ESP_01S
#define LED_BUILTIN 2 // Для ESP-01S
#endif

#ifdef BOARD_ESP_01
#define LED_BUILTIN 1 // Для ESP-01
#endif

  pinMode(LED_BUILTIN, OUTPUT);

  if (start_htu() && start_bmp() && start_ina()) {
    update_sensors_data();

    if (wifi_connect()) {
      send_sensors_message();
    }
  }

  // WAKE_RF_DEFAULT - проснуться с включенным модулем Wi-Fi
  ESP.deepSleep(DEEP_SLEEP_TIMEOUT, WAKE_RF_DEFAULT);
}

void loop() {}

Настройки PlatformIO для ESP-01 и ESP-01S:

; platformio.ini
[env:esp_01]
platform = espressif8266
board = esp01_1m
framework = arduino
upload_speed = 3000000
monitor_speed = 74880
lib_deps =
	knolleary/pubsubclient@^2.8
	adafruit/Adafruit BMP280 Library@^2.3.0
	adafruit/Adafruit HTU31D Library@^1.1.0
	bblanchon/ArduinoJson@^6.18.3
	wollewald/INA226_WE@^1.2.3
build_flags = -D PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3 -D BOARD_ESP_01
board_build.f_cpu = 80000000L

[env:esp_01s]
platform = espressif8266
board = esp01_1m
framework = arduino
upload_speed = 3000000
monitor_speed = 74880
lib_deps =
	knolleary/pubsubclient@^2.8
	adafruit/Adafruit BMP280 Library@^2.3.0
	adafruit/Adafruit HTU31D Library@^1.1.0
	bblanchon/ArduinoJson@^6.18.3
	wollewald/INA226_WE@^1.2.3
build_flags = -D PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3 -D BOARD_ESP_01S
board_build.f_cpu = 80000000L

Проблемы

Проверка на герметичность

После пары дождливых дней в сентябре моя метеостанция показывала очень высокие показатели влажности, около 90%. Я не поверил в эти результаты, ведь дождя за окном уже не было. Как оказалось, сильный дождь смог залить станцию водой через вентиляционные отверстия в верхней части корпуса.

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

Больше эта проблема не возникала. Показания влажности были примерно равны тем, что доступны в интернете. Если бы у меня был 3D-принтер, то корпус можно было сделать намного лучше. Важно сделать так, чтобы при попадании воды сверху, ваш корпус не позволял ей стекать через вентиляционные отверстия вниз.

Морозные и пасмурные дни

Ночью метеостанция использует энергию, запасенную в ионисторе в дневное время. Зимой иногда солнца бывает мало, но станции его хватало. Видимо, большая солнечная батарея успевала заряжать ионистор до нужных значений.

Иногда было тревожно, когда шел сильный снег и уже в 4 часа дня было темно. Мне казалось, что вот сегодня точно заряда в ионисторе не хватит и станция перестанет присылать показания погоды. Но нет, даже в самый темный день все обходилось. По моим подсчетам у ионистора всегда был запас энергии на 3-4 часа до наступления рассвета.

Зимний холод тоже оказывал влияние на ионистор. Чем ниже температура, тем как бы больше тратилось энергии на каждое измерение. Я думаю в холодное время просто усиливался саморазряд ионистора.

Несмотря ни на что, всю зиму станция работала без перебоев, заряда в ионисторе хватало на всю ночь с запасом.

Подведем итоги

Погодная станция

Ионистор и солнечная батарея - вполне годное решение для питания автономных устройств. Особенно в тех условиях, где литиевый аккумулятор не подходит (отрицательные температуры). Хоть сам ионистор не вечен, но такие устройства могут работать годами, а может, и десятилетиями, не требуя обслуживания.

Солнечная батарея в этом проекте смотрится очень громоздко. Думаю ее размеры, можно сократить за счет увеличения емкости ионистора или выбора другого более экономичного по питанию модуля вместо ESP.

Модуль ESP-01 я бы заменил на что-то другое. ESP не подходит для проектов с малым энергопотреблением. При использовании Wi-Fi в ESP-01 потребление энергии слишком велико (от 70 мА до 200 мА). Просто тогда он был у меня под рукой и его миниатюрный размер идеально подходил для проекта. Сейчас я бы подобрал какой-нибудь модуль Bluetooth у Nordic Semiconductor с низким энергопотреблением.

Корпус устройства нужно лучше продумать. Он не должен нагреваться летом и промокать осенью в дождливое время. Главное вентиляционные отверстия не должны давать возможность воде пробираться внутрь устройства. Плата устройства не должна быть впритык ко дну корпуса. Если влага всё-таки проберется вовнутрь, это не позволит плате намокнуть.

Это был интересный опыт, устройство проработало практически год без обслуживания, передавая данные на MQTT-сервер через каждые полчаса. Больше всего мне понравилось, что устройство не требовало обслуживания. Автономная работа таких девайсов впечатляет. Ионистор удивительный радиокомпонент, “миниатюрная батарейка” способная отдавать высокие токи, который вполне может пригодиться для накопления энергии для автономных проектов (систем автополива на даче, уличных светильников и т.п.).

Спасибо, что дочитали эту статью до конца, удачных самоделок!