Товары из Китая

Безумный дом. Датчик с батарейным питанием на базе ESP8266


Безумный дом. Датчик  с батарейным питанием на базе  ESP8266

Я как-то обещал рассказать, как сделать WiFi датчик для умного дома на базе ESP8266 с батарейным питанием. Тупо подключить батарейку и ничего не предпринять для экономии энергии — не самая лучшая мысль, средний потребляемый ток модуля 70ма, емкость хорошей батарейки АА 1800 — 2700 mAh, хватит ее на 38 часов в лучшем случае. Хотелось бы менять батарейки раз в год или реже.

Вариант с дополнительным к ESP8266 малопрожорливым процессором, например, MSP430, не рассматриваем — батарея все-таки достаточно емкая, постараемся обойтись минимумом дополнительных деталей.

Что пишут в спецификации про потребление тока ESP8266:

  • Deep Sleep mode ~20 uA
  • С выключенным WiFi — 15 mA
  • Полностью все включено — до 150-200 mA в пике

Кроме того, подключение к сети занимает много времени, без подключения контроллер стартует и выполняет необходимые задачи гораздо быстрее. И снова спать, нечего энергию понапрасну тратить.

Алгоритм следующий — микросхема уходит в глубокий сон, просыпается раз в 10 минут без подключения к сети и измеряет температуру. Не забудьте, для того, чтобы разбудить ESP8266, нужно соединить вход сброса с GPIO16!

Если температура изменилась меньше, чем на пол градуса — засыпаем снова. Раз в час подключаемся к сети и сообщаем — еще живой, напряжение батареи и текущую температуру. Если температура изменилась — подключаемся к сети до истечения часа и передаем новые значения. Не знаю, может я чего-то не понимаю, или задница через два П пишется, но если микроконтроллер заснул с запретом на WiFi, включить WiFi после просыпания мне не удалось. Проще оказалось усыпить его снова на несколько секунд с просыпанием в нормальном режиме, с включенным WiFi.

  if (TimeToUpdate)  

{
reset_RTC_counter();
ESP.deepSleep(FAST_SLEEP_TIME*1000000); //uS
}
else
{
if (boot_counter>=5)
{
reset_RTC_counter();
ESP.deepSleep(SLEEP_TIME*1000000);
}
else ESP.deepSleep(SLEEP_TIME*1000000, WAKE_RF_DISABLED);
}

Счетчик включений и последнюю переданную температуру где-то надо хранить, а в режиме сна память отключается. Единственное, что сохраняется — это регистры счетчика реального времени, там и устраиваем хранилище.

Здесь спрятан код
void reset_RTC_counter(void)

{
uint32_t bootcount =0;
ESP.rtcUserMemoryWrite(0, &bootcount, sizeof(bootcount));
}
uint16_t RTC_bootcount(void)
{
uint32_t bootcount;
uint16_t low_word;
uint16_t high_word;
uint16_t return_word;
ESP.rtcUserMemoryRead(0, &bootcount, sizeof(bootcount));
low_word = bootcount & 0xFFFF;
high_word = (bootcount>>16) & 0xFFFF;
if (low_word!=high_word)
{
low_word = 0;
high_word = 0;
}
return_word = low_word;
low_word++;
high_word++;
bootcount = (high_word << 16) + low_word;
ESP.rtcUserMemoryWrite(0, &bootcount, sizeof(bootcount));
return (return_word);
}
void SaveFloatRtc(float fnum1, float fnum2)
{
union
{
uint32_t ltemp;
float ftemp;
};
ftemp = fnum1;
ESP.rtcUserMemoryWrite(1, <emp, sizeof(ltemp));
ftemp = fnum2;
ESP.rtcUserMemoryWrite(2, <emp, sizeof(ltemp));
}
float GetFloatFromRtc(uint8_t offset)
{
union
{
uint32_t ltemp;
float ftemp;
};
ESP.rtcUserMemoryRead(offset+1, <emp, sizeof(ltemp));
return ftemp;
}

Схема приведена ниже. Для питания используется преобразователь с повышение напряжения BL8530 с очень маленьким током холостого хода (без подключенных внешних компонентов) — <7uA. Есть преобразователи и более экономичные, но это — аутентичная китайская разработка и продается на Али очень дешево.

Безумный дом. Датчик  с батарейным питанием на базе  ESP8266

Безумный дом. Датчик  с батарейным питанием на базе  ESP8266

Для начала проверим потребление энергии без преобразователя. ESP8266 плюс оба датчика с режиме глубокого сна потребляют около 17мкА и при работе с сетью в среднем около 70мА — как и обещано в описании ESP8266, датчики температуры потребляют пренебрежимо мало.

Безумный дом. Датчик  с батарейным питанием на базе  ESP8266

Поиграемся с преобразователем напряжения.

Безумный дом. Датчик  с батарейным питанием на базе  ESP8266

На выходе рекомендуют ставить конденсатор 100-200 микрофарад. У меня большое изобилие танталовых конденсаторов на 22 микрофарад — их и попытался везде воткнуть. Не пролезло — ток холостого хода оказался почти 80 мкА. Смотрим, что за дела? Видимо, самый короткий генерируемый пульс достаточен, чтобы изрядно перезарядить маленькую емкость, пульсации больше вольта.

Безумный дом. Датчик  с батарейным питанием на базе  ESP8266

Разругавшись с жабой, ставлю на выход 330 мкФ — у меня их мало, а других номиналов в закромах вообще не нашлось. Пульсации ушли, ток холостого хода упал до 16 мкА — с учетом импульсного потребления понятно, что мультиметр будет врать, но и так сойдет.

Почему такая странная конфигурация датчиков, а не просто два одинаковых температурных сенсора? Это долгая история, но я расскажу.

Много лет назад, еще в другой жизни, многие фирмы высылали всем желающим бесплатные образцы микросхем, 3-5 видов, по 2-5 штуки каждого. Все были равны, но в силу служебного положения, я был равнее многих. Мне полагалось от своей фирмы до 50 штук 5 видов в день, желтые штаны и 2 «ку». Не обязательно на свой адрес, мог заказать и в адрес любого потенциального покупателя. Естественно, все это было для работы, но и для личного пользования не возбранялось — без злоупотреблений, конечно. Даже устраивался ежегодный конкурс самоделок среди сотрудников. Условие участия в конкурсе — использование в самоделке микросхем фирмы. Я даже раз в число призеров входил, приз — значок и маечка.

Безумный дом. Датчик  с батарейным питанием на базе  ESP8266

Ну так вот, один раз я заказал какие-то LDO для демо-платы или для тестовой платы, уже не помню. Штук 20 микросхем. Все спаял (вру, конечно — сам не паял) — не работает. Начал разбираться — одна из микросхем имеет непонятные надписи. На пакетике — все правильно, а микросхемы туда затолкали другие, но в таком же корпусе. На работе разбираться, что прислали, было некогда, утащил их домой с надеждой вечером разобраться и заказал новые. До разборок дело дошло лет через 10 🙂 Эти микросхемы и оказались датчиками температуры и у меня их почти два десятка — надо же их как-то использовать.

Ну а второй сенсор — выносной для измерения температуры на улице или в соседнем помещении. Лучше, чем DS18B20 для этой цели вряд ли что-то найдешь.

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

Безумный дом. Датчик  с батарейным питанием на базе  ESP8266

Корпус проектируется в OpenSCAD. Зачем? Исключительно чтобы позлить некоторых местных специалистов, которые его просто на дух не переносят 🙂

Безумный дом. Датчик  с батарейным питанием на базе  ESP8266

Для того, чтобы все работало, в Home Assistant, очевидно, нужно установить MQTT сервер и работать уже через него.

Безумный дом. Датчик  с батарейным питанием на базе  ESP8266

Просто натыкать все мышью не получится, нужно ручками редактировать configutation.yaml и кое-чего там дописать, но, когда уже написана программа для ESP8266, это уже совсем несложно.

configutation.yaml
mqtt:

sensor:
- name: "Temperature outside"
state_topic: "garage/sensor/temperature/outside"
unit_of_measurement: "°C"

- name: "Temperature inside"
state_topic: "garage/sensor/temperature/inside"
unit_of_measurement: "°C"

- name: "Battery Voltage"
state_topic: "garage/sensor/battery_level"
unit_of_measurement: "V"

- name: "Temperature boiler"
state_topic: "workshop/sensor/temperature/boiler"
unit_of_measurement: "°C"

- name: "Temperature workshop"
state_topic: "workshop/sensor/temperature/inside"
unit_of_measurement: "°C"

- name: "Workshop Battery Voltage"
state_topic: "workshop/sensor/battery_level"
unit_of_measurement: "V"
icon: mdi:battery-60

Теперь нужно добавить свой датчик на панель — тоже ручками, но тут уже просто натыкать мышью.

Безумный дом. Датчик  с батарейным питанием на базе  ESP8266

Безумный дом. Датчик  с батарейным питанием на базе  ESP8266

Безумный дом. Датчик  с батарейным питанием на базе  ESP8266

На всякий случай исходный код — мало ли кому может пригодится.

Исходники
#include <Arduino.h>

#include <Wire.h>
#include <DS18B20.h>
#include <ESP8266WiFi.h>
#include <ArduinoMqttClient.h>
#define TMP75_ADDRESS 0x48
#define TMP75_TEMP_REGISTER 0
#define TMP75_CONF_REGISTER 1
#define TMP75_THYST_REGISTER 2
#define TMP75_TOS_REGISTER 3
#define TMP75_CONF_SHUTDOWN 0
#define TMP75_CONF_OS_COMP_INT 1
#define TMP75_CONF_OS_POL 2
#define TMP75_CONF_OS_F_QUE 3
// time in seconds
//#define SLEEP_TIME 60
#define SLEEP_TIME 600
#define FAST_SLEEP_TIME 10
// ADC caluibration
#define CALIB_CODE 0x2ee
#define CALIB_VOLTAGE 3.06

//#define GARAGE 1
const char* ssid = "your_SSID";
const char* password = "your_pasword";
const char* mqtt_broker = "192.168.0.253"; // use your own!
const int mqtt_port = 1883;
const char* mqtt_username = "homeassistant"; // use your own!
const char* mqtt_password = "quo8gaelai6zah4uthaefaer4theilai0oth0Cai0phoopahShungie5ohs4Chee"; // use your own!

#ifdef GARAGE
const char *topic[] = {"garage/sensor/temperature/inside",
"garage/sensor/temperature/outside",
"garage/sensor/battery_level"};
#else
const char *topic[] = {"workshop/sensor/temperature/inside",
"workshop/sensor/temperature/boiler",
"workshop/sensor/battery_level"};
#endif

WiFiClient wifiClient;
MqttClient mqttClient(wifiClient);
//uint8_t ds_address[] = {0x28, 0x69, 0xD8, 0x13, 0x00, 0x00, 0x00, 0x9B};
#ifdef GARAGE
uint8_t ds_address[] = {0x28, 0x13, 0xec, 0x13, 0x00, 0x00, 0x00, 0x57};
#else
uint8_t ds_address[] = {0x28, 0x69, 0xD8, 0x13, 0x00, 0x00, 0x00, 0x9B};
#endif
void TMP75_ctrl(uint8_t ctrl)
{
Wire.begin(); // Join the I2C bus as a master
Wire.beginTransmission(TMP75_ADDRESS); // Address the TMP75 sensor
Wire.write(TMP75_CONF_REGISTER); // Address the Configuration register
Wire.write(ctrl); // Set the temperature resolution
Wire.endTransmission(); // Stop transmitting
}
float readTemp()
{
uint16_t regdata = 0xFFFF;
TMP75_ctrl(0xA1); // One-Shot
Wire.beginTransmission(TMP75_ADDRESS);
Wire.write(TMP75_TEMP_REGISTER); // pointer reg
Wire.endTransmission();
Wire.requestFrom(TMP75_ADDRESS, 2);
regdata = Wire.read();
regdata <<= 8;
regdata |= ((uint16_t)Wire.read());
return ((float)(int16_t)regdata / 32) / 8;
//return ((float)(int16_t)regdata / 16) / 8;
}


void reset_RTC_counter(void)
{
uint32_t bootcount =0;
ESP.rtcUserMemoryWrite(0, &bootcount, sizeof(bootcount));
}
uint16_t RTC_bootcount(void)
{
uint32_t bootcount;
uint16_t low_word;
uint16_t high_word;
uint16_t return_word;
ESP.rtcUserMemoryRead(0, &bootcount, sizeof(bootcount));
low_word = bootcount & 0xFFFF;
high_word = (bootcount>>16) & 0xFFFF;
if (low_word!=high_word)
{
low_word = 0;
high_word = 0;
}
return_word = low_word;
low_word++;
high_word++;
bootcount = (high_word << 16) + low_word;
ESP.rtcUserMemoryWrite(0, &bootcount, sizeof(bootcount));
return (return_word);
}
void SaveFloatRtc(float fnum1, float fnum2)
{
union
{
uint32_t ltemp;
float ftemp;
};
ftemp = fnum1;
ESP.rtcUserMemoryWrite(1, <emp, sizeof(ltemp));
ftemp = fnum2;
ESP.rtcUserMemoryWrite(2, <emp, sizeof(ltemp));
}
float GetFloatFromRtc(uint8_t offset)
{
union
{
uint32_t ltemp;
float ftemp;
};
ESP.rtcUserMemoryRead(offset+1, <emp, sizeof(ltemp));
return ftemp;
}

void setup_wifi()
{
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);

// todo - go to deep sleep if wifi not available
uint8_t trial_counter=10;
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
trial_counter--;
if (trial_counter==0)
{
Serial.println();
Serial.println("WiFi not availale");
Serial.println("Deep sleep");
reset_RTC_counter();
ESP.deepSleep(SLEEP_TIME*1000000);
}
}
Serial.println();
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
WiFi.setAutoReconnect(true);
WiFi.persistent(true);
}

#define ONE_WIRE_GPIO 12
DS18B20 ds(ONE_WIRE_GPIO);

void setup()
{
float SensValues[3];
bool TimeToUpdate = false;
Serial.begin(115200);
delay(100);
Serial.println();
uint16_t boot_counter;
boot_counter = RTC_bootcount();
Serial.println();
Serial.print("Boot count ");
Serial.println(boot_counter);
Wire.begin();
Wire.setClock(400000);
//TMP75_ctrl(0x01); // SD mode 0.5C
TMP75_ctrl(0x21); // SD mode 0.25C
float prevT0 = GetFloatFromRtc(0);
float prevT1 = GetFloatFromRtc(1);

Serial.print("T©=");
// check TMP75
SensValues[0] = readTemp();
Serial.print(SensValues[0]);
Serial.print(", ");
// check DS18B20
if (ds.select(ds_address)) SensValues[1] = ds.getTempC();
else Serial.println("DS18B20 not found!");
Serial.println(SensValues[1]);
if(abs(prevT0-SensValues[0])>0.5) TimeToUpdate = true;
if(abs(prevT1-SensValues[1])>0.5) TimeToUpdate = true;
if (boot_counter==0)
{
setup_wifi() ;
delay(100);
pinMode(14, OUTPUT);
digitalWrite(14, HIGH);
uint16_t batterylevel = analogRead(A0);
Serial.print("ADC=0x");
Serial.print(batterylevel, HEX);
SensValues[2] = (float)batterylevel/CALIB_CODE*CALIB_VOLTAGE;
Serial.print(", V=");
Serial.println(SensValues[2]);

mqttClient.setUsernamePassword(mqtt_username, mqtt_password);
if (mqttClient.connect(mqtt_broker))
{
Serial.println("You're connected to the MQTT broker!");
for(uint8_t i=0; i<3; i++)
{
mqttClient.beginMessage(topic[i]);
mqttClient.print(SensValues[i]);
mqttClient.endMessage();
delay(10);
}
SaveFloatRtc(SensValues[0], SensValues[1]);
mqttClient.poll();
TimeToUpdate = false;
delay(500);
}
else
{
Serial.print("MQTT connection failed! Error code = ");
Serial.println(mqttClient.connectError());
}
}
Serial.println("Deep sleep mode");
if (TimeToUpdate)
{
reset_RTC_counter();
ESP.deepSleep(FAST_SLEEP_TIME*1000000); //uS
}
else
{
if (boot_counter>=5)
{
reset_RTC_counter();
ESP.deepSleep(SLEEP_TIME*1000000);
}
else ESP.deepSleep(SLEEP_TIME*1000000, WAKE_RF_DISABLED); // WAKE_RF_DISABLED
}
}

void loop() {}

Кормить такое устройство от аккумулятора — по-моему, просто расточительство. Срок службы аккумулятора, в зависимости от технологии, от трех лет для NiMH. Проще и дешевле раз в год поставить новые батарейки. А если очень важны габариты — пуговка 2450 спасет отца русской демократии, при небольших размерах она имеет емкость больше 500 мАч при напряжении 3 Вольта.

На этом у меня пока все, но тему можно развить дальше если не надоело и кому-то надо. Меня умный дом не привлекает и вроде как абсолютно не нужен, все делается исключительно для развлечения — нужно же пенсионеру чем-то заняться. Как в «Кавказской пленнице» — имею возможность купить козу сделать умный дом, но не имею желания.

Самое ценное в этой статье (Имею Мнение — Хрен Оспоришь) — это исходный код. Несмотря на кажущуюся простоту, некоторые вещи или нигде не описаны, либо описание найти не так просто. А все остальное — словесная шелуха вокруг.


СМОТРИ ТАКЖЕ

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *