Совершенствуя свой комнатный термостат, о котором писал раньше, я задался целью дополнить его беспроводным датчиком температуры для измерения температуры воздуха на улице, собрать термостат с питанием от батареек и заменить модули приемник-передатчик RF 433MHz другой парой радиомодулей с большей дальностью связи при напряжении питания не более 3В. По ходу решения этих задач вырисовалась автономная метеостанция, речь о которой пойдет ниже.
Метеостанция состоит из двух узлов, назовем их для простоты гигрометром и термометром.
Гигрометр, построен на контроллере ATMEGA328P, измеряет температуру и влажность в помещении (датчик температуры и влажности DHT22) и напряжение питания гигрометра, которое обеспечивают две батарейки АА 1,5В. На контроллер поступает сигнал с приемника LoRa, который по эфиру принимает информацию с термометра (выносного датчика). Информация с контроллера выводится на ЖК-дисплей NOKIA 5110.
В термометре, собранном тоже на контроллере ATMEGA328P, измеряется температура воздуха на улице (датчик температуры DS18B20) и напряжение питания выносного узла, организованного на двух батарейках АА 1,5 В. Передатчик LoRa этого узла передает температуру и напряжение питания на гигрометр.
Контроллер ATMEGA328P и передатчик LoRa термометра для экономного расходования заряда батареек после измерений и отправки информации переводятся в режим сна. Напряжение питания на датчик DS18B20 программно подается только на время измерения температуры. Измерения и отправка данных с термометра выполняется с периодом около одной минуты.
В таком же режиме работа-сон работает и гигрометр. Продолжительность работы контроллера и приемника гигрометра несколько больше одной минуты (около 65 сек). Это сделано для уверенного приема сигнала с термометра — ведь работа термометра и гигрометра не синхронизированы. Затем ATMEGA328P и приемник LoRa переводятся на 14 мин в режим сна до пробуждения и старта очередного цикла. Питание на DHT22 подается только во время измерений.
Для программирования режима сна контроллеров ATMEGA328P используется библиотека LowPower.h.
С разрядом батареек на них понижается величина напряжения.
Нижний предел рабочего напряжения питания контроллера ATMEGA328P — 1,8В. При этом, заводская установка фьюз ATMEGA328P выполнена на мониторинг порога напряжения питания 2,7В, поэтому необходимо изменить заводские установки фьюз на мониторинг порога 1,8В, чтобы гарантировать работу контролера при напряжении питания ниже 2,7В при питании от батареек.
Внутренний генератор контроллера может не запускаться на частоте 16 МГц при напряжении питания 3В или несколько ниже. У меня оба контроллера работают с кварцем 16 МГц при пониженном напряжении питания 2,7…2,8В, поэтому я не стал менять кварц 16 МГц на 8 МГц.
Для сборки устройства понадобятся компоненты, перечень которых и их ориентировочная стоимость по ценам сайта AliExpress приведены в таблице.
Компонент | Цена, $ |
гигрометр | |
Контроллер ATMEGA328P-PU | 1,59 |
Датчик температуры и влажности DHT22 | 2,34 |
Приемник-передатчик LoRa Rа-01 | 3,95 |
ЖК-дисплей NOKIA 5110 | 1,91 |
Макетная (монтажная) плата, монтажные провода, батарейки АА, кварцевый резонатор 16 МГц (8 МГц), резисторы и др. | 4,00 |
термометр | |
Контроллер ATMEGA328P-PU | 1,59 |
Датчик температуры DS18B20 | 0,63 |
Приемник-передатчик LoRa Rа-01 | 3,95 |
Макетная плата (стеклотекстолит), монтажные провода, батарейки АА, кварцевый резонатор 16 МГц (8 МГц), резисторы и др. | 4,00 |
Всего (примерно): | 24 |
Гигрометр
Мозг гигрометра – контроллер ATMEGA328P. Он принимает сигналы с датчика DHT22 и формирует сигналы управления приемником LoRa и дисплеем NOKIA 5110.
В Интернете много нареканий на низкую точность измерения влажности датчика DHT22. На сегодня есть альтернатива: более современные датчики температуры и влажности HTU21 (GY21), (Vcc = 3...5 В), Si7021,(Vcc = 1,9… 3,6 В), SHT21, (Vcc = 2.1….3,6 В).
Я использую DHT22, поскольку расхождение между показаниями влажности моего экземпляра этого датчика и серийно выпускаемого термогигрометра LaCrosse WS-9024IT составляют не более 8-ми единиц, что вполне приемлемо для бытовых целей. Расхождение между показаниями влажности сильно увеличиваются, если величина напряжение питания DHT22 ниже 3В. Это и понятно, ведь напряжение питания DHT22 должно находиться в пределах 3…5В. Суммируя – идеально в этих условиях в схему гигрометра вписывается датчик Si7021.
На картинке ниже — цоколевка элементов метеостанции.
Фьюзы и многое другое различных контроллеров, в том числе ATMEGA328P, можно читать и редактировать утилитой SinaProg. Если вы впервые сталкиваетесь с этой программой, то несмотря на интуитивно понятный интерфейс, не пытайтесь после инсталляции приложения начинать с ним работать. Сначала почитайте тут, где HWman приводит необходимые дополнения SinaProg при использовании Arduino UNO и подробные инструкции.
Советую вначале прочитать заводские установки фьюз ATMEGA328P и сохранить их значения, чтобы вернуться к ним в случае неудачи. В моих контроллерах заводские установки фьюз бит такие: LOW: 0xFF, HIGH: 0xDE, EXTENDED: 0x05 (Vcc=2.7V, BODLEVEL=101). Фьюзы на мониторинг порога 1,8В: LOW: 0xFF, HIGH: 0xDE, EXTENDED: 0x06 (Vcc=1.8V, BODLEVEL=110).
Скетч гигрометра для загрузки в ATMEGA328P находится под спойлером.
/*
* Автономная метеостанция с беспроводным выносным датчиком и питанием от батареек
*/
#include <OneWire.h>
OneWire ds(7); //pin 13, Atmega328P
#include <SPI.h>
#include <LoRa.h>
#include <LowPower.h>
#define PowerDS18B20 (6) //pin 12 (Atmega328P), питаниe DS18B20
unsigned int sleepCounter; // счетчик, задающий время сна
float Tout; //температура
int i; // отсчет числа циклов при включении устройства интенсивного режима(20 циклов за 1 мин)
String messageTout; // LoRa-сообщение
float batteryLevel; // напряжение батареи
const int batteryPin = A0; // pin 23 (Atmega328P), к которому подключена батарея для измерения напряжения
void Measurement (){
//измерение температуры
byte data[2];
digitalWrite(PowerDS18B20, 1);
ds.reset();
ds.write(0xCC); // пропуск поиска по адресу (1 датчик)
ds.write(0x44); // команда на измерение
delay(700);
ds.reset();
ds.write(0xCC);
ds.write(0xBE); // передача регистров со значением температуры
data[0] = ds.read();
data[1] = ds.read();
Tout = ((data[1] << 8) | data[0]) * 0.0625;
// Serial.println(«Tout= „+ String(Tout));
digitalWrite(PowerDS18B20, 0);
// измерение напряжения батареи:
analogReference(INTERNAL);
int sensorValue = analogRead(A0);
batteryLevel = (sensorValue * 3.2 / 1024);
// Serial.println(“BAT= „+ String(batteryLevel));
}
void SetSynchLoRa () {
int counter = 0;
while (!LoRa.begin(433E6) && counter < 10) {
// Serial.print(“.»);
counter++;
delay(500);
}
/* if (counter == 10) {
// Serial.println(«Failed to initialize ...»);
}*/
LoRa.setSyncWord(0xF3);
}
void SendMessage (){
// отправка данных (температура, напряжение батареи)
messageTout = String(Tout) + "#" + String(batteryLevel);
// Serial.println(messageTout);
delay(250);
LoRa.beginPacket();
LoRa.print(messageTout);
LoRa.endPacket();
}
void setup() {
// Serial.begin(9600);
// Serial.println(«Initializing ...»);
pinMode(PowerDS18B20, OUTPUT);
SetSynchLoRa ();
}
void loop() {
// Serial.println("");
// Serial.println(«i = » + String(i));
if (i >= 30){// i >= 30 (1 мин) — обычный режим (измерение, отправка — 1 раз/1 мин)
for (sleepCounter = 5; sleepCounter > 0; sleepCounter--)
{
LoRa.sleep ();
LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
}
Measurement ();
SendMessage ();
LoRa.sleep ();
} else
{ //интенсивный режим продолжительностью 1 мин, отправка — 1 раз/2 сек
Measurement ();
SendMessage ();
delay (1000);
}
i++;
if (i >= 30) i = 30; //уменьшение разрядности заполнения счетчика
}
```
Для работы с контроллерами ATMEGA328P в качестве программатора я использую плату Arduino UNO. Процесс установки загрузчика и загрузки скетчей в контроллер ATMEGA328 с помощью платы Arduino UNO подробно описан тут. На Youtube есть хорошее видео на эту тему.
В скетче закомментированы команды вывода в монитор последовательного порта (Serial). В случае необходимости — раскомментируйте команды.
Цикл начинается с прослушивания эфира и приема информации приемником LoRa. Таймером установлено время прослушивания — 62 сек. В это время с периодом 5 сек обновляется информация на дисплее NOKIA и с периодом 20 сек (3 раза) датчиком DHT22 проводятся измерения температуры, влажности, а также уровень напряжения батареек через один из аналоговых входов контроллера. Напряжение питания на DHT22 подается только во время измерений с минимальной задержкой 2 сек, при которой датчик еще работает. Выход АЦП в скетче масштабирован на напряжение новых батареек, которое составляет 3,2В (1,6В х 2). Время прослушивания эфира выбрано несколько больше 1 мин для уверенного приема одной пачки с термометра, который работает на передачу с периодом 1 мин, но об этом ниже. Затем на 62-ой секунде контроллер и приемник переводятся в режим сна, который длится примерно 14 мин, т.е. период цикла «работа/сон» гигрометра — около 15 мин. Замечу, что режим сна в гигрометре – это вынуждения мера, призванная существенно уменьшить потребление. С другой стороны – за 15 минут температура и влажность воздуха кардинально не изменятся. Как показал расчет времени работы от батареек, режим сна можно уменьшить до 8-9 мин, но я не стал этого делать, поскольку в будущем узел будет дополнен функциями термостата, что приведет к увеличению потребления.
Для сравнения — в таблице ниже приведены характеристики термогигрометра LaCrosse WS-9024IT и гигрометра с этого проекта. Большинство параметров LaCrosse из таблицы не приводятся в ее техническом описании, а были измерены. Хотел дополнить таблицу результатами аналогичных любительских разработок, но, к сожалению, ничего не нашел.
Параметр | LaCrosse WS-9024IT | Сadil_TM |
Питание | 2хАА, 3В, Durasell | 2хАА, 3В, GP Ultra+, 2.72 Wh |
Потребление сна | 8 мкА | 10 мкА |
Продолжительность сна | 14 мин | |
Операционное потребление | 200 мкА | 3 мА |
Продолжительность работы | 65 сек | |
Период цикла работа/сон | 30 сек | 15 мин |
Время эксплуатации | более 2 лет | около 1 года |
Расчет времени работы.
Средний ток потребления: 3 мА / 15 + 0,01 мА = 0,21 мА, где 15 – скважность. Емкость батарейки GP Ultra+ в мА*час: 2.72 Вт*час / 1,5 В = 1,81 А*час = 1800 мА*час. Время эксплуатации: 1800 мА*час / 0,21 мА = 8600 час (11,9 мес).
Уточню:
— Расчет очень приблизительный, поскольку в основу расчета положено максимальное (пиковое) потребление гигрометра.
— Время работы LaCrosse приведено, исходя из собственного опыта. Этот девайс у меня давно.
Термометр
Емкость и напряжение батареек на морозе сильно понижаются. Поэтому, чтобы не подвергать батарейки да и устройство в целом столь сильным испытаниям я вынес за пределы помещения только датчик температуры DS18B20, а сам узел и батарейки находятся в помещении. DS18B20 соединяется с платой узла тонким трехжильным проводом. Это решение я подсмотрел в своей серийной метеостанции – разработчики уверены, что в квартире всегда найдется щель для укладки провода диаметром в несколько миллиметров.
Фьюзы для ATMEGA328P термометра такие же, как и для гигрометра.
Узел термометра тоже построен на контроллере ATMEGA328P. Он принимает сигнал с датчика DS18B20, измеряет напряжение питания и управляет передатчиком LoRa.
Скетч термометра – под спойлером.
/*
* Автономная метеостанция с беспроводным выносным датчиком и питанием от батареек
*/
#include <SPI.h>
#include <LoRa.h>
#include «DHT.h»
#define DHTPIN 3 // what digital pin we're connected to
#define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321
DHT dht(DHTPIN, DHTTYPE);
float Tin=0;
int Hin=0;
float BatteryInLevel; // напряжение батареи базы
String LoRaData, Tout_str, BatteryInLevel_str, BatteryOutLevel_str;
//sleep
#include <LowPower.h>
#define PowerDHT (4) //пин питания DHT22
unsigned int sleepCounter;
//Nokia 5110
#include <SPI.h>
#include <Adafruit_GFX.h> //https://esp8266.ru/forum/threads/esp8266-5110-nokia-lcd.1143/#post-16942
#include <Adafruit_PCD8544.h> //https://esp8266.ru/forum/threads/esp8266-5110-nokia-lcd.1143/#post-16942
//timer
#include <SimpleTimer.h>
SimpleTimer timer;
Adafruit_PCD8544 display = Adafruit_PCD8544(5, 7, 6);
void sendSensor(){
digitalWrite(PowerDHT, 1);
delay (2000);
Hin = dht.readHumidity();
Tin = dht.readTemperature();
/* if (isnan(Hin) || isnan(Tin)) {
// Serial.println(«Failed to read from DHT sensor!»);
return;
}*/
digitalWrite(PowerDHT, 0);
// измерение напряжения батареи:
analogReference(INTERNAL);
int sensorValue = analogRead(A4);
BatteryInLevel = (sensorValue * 3.2 / 1024);
}
void draw(){
display.clearDisplay();
//Tin
{
// Tin = 24.12; //ТЕСТ, удалить!
display.setTextSize(2);
display.setCursor(8,0);
display.println (Tin, 1); // один знак после запятой
display.setCursor(68,0);
display.println(«C»);
}
//Hin
{
// Hin = 58; // ТЕСТ, удалить!
display.setTextSize(2);
display.setCursor(8,16);
display.println(String(Hin)+ "%");
}
//Tout
{
char chr_Tout [12];
// Tout_str = «22.4»; //ТЕСТ, удалить!
Tout_str.toCharArray(chr_Tout, 5);
display.setTextSize(1);
display.setCursor(50,16);
display.println(String(chr_Tout)+«C»);
}
// Battery Out Level
{
char chr_BatteryOutLevel [12];
// BatteryOutLevel_str = «2.86»; //ТЕСТ, удалить!
BatteryOutLevel_str.toCharArray(chr_BatteryOutLevel, 4);
display.setTextSize(1);
display.setCursor(2,32);
display.println(«BAT Out: » + String(chr_BatteryOutLevel)+«V»);
}
// Battery In Level
{
// BatteryInLevel = 3.0; //ТЕСТ, удалить
display.setTextSize(1);
display.setCursor(2,40);
display.println(«BAT In: „);
display.setCursor(56,40);
display.println(BatteryInLevel, 1); //один знак после запятой
display.setCursor(74,40);
display.println(“V»);
}
display.display();
/* Serial.println(«Tin: » + String(Tin)+"*C");
Serial.println(«Hin: » + String(Hin)+"%");
Serial.println(«Tout: » + String(Tout_str)+"*C");
Serial.println(«BAT_In: » + String(BatteryInLevel)+«V»);
Serial.println(«BAT_Out: » + String(BatteryOutLevel_str)+«V»);
Serial.println("......"); */
}
void sleepDevice(){
// sleepCounter = 65 — 10 min
// sleepCounter = 91 — 14 min
for (sleepCounter = 91; sleepCounter > 0; sleepCounter--) //91!!!
{
LoRa.sleep ();
LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
}
LoRa.sleep ();
}
void SignalReception (){
// try to parse packet
int packetSize = LoRa.parsePacket();
if (packetSize) {
// read packet
while (LoRa.available()) {
LoRaData = LoRa.readString();
// Serial.println(«Принято: „+(LoRaData));
}
int pos1 = LoRaData.indexOf('#');
Tout_str = LoRaData.substring(0, pos1);
BatteryOutLevel_str = LoRaData.substring(pos1+1, LoRaData.length());
}
}
void setup() {
// Serial.begin(9600);
pinMode(PowerDHT, OUTPUT);
// инициализация и очистка дисплея
display.begin();
display.clearDisplay();
display.display();
display.setContrast(60); // установка контраста
display.clearDisplay();
display.setTextSize(2);
display.setCursor(12,16);
display.println (“>>>>>»); //индикация начала работы при включении
display.display();
dht.begin();
sendSensor();
draw();
while (!LoRa.begin(433E6)) {
// Serial.println(".");
delay(500);
}
// Диапазон для синхрослова – между «0-0xFF».
LoRa.setSyncWord(0xF3);
// Serial.println(«LoRa Initializing»);
timer.setInterval(20000, sendSensor);
timer.setInterval(5000, draw);
timer.setInterval(62000, sleepDevice);
}
void loop() {
SignalReception();
timer.run();
}
```
Вначале при подключении питания термометр на протяжении одной минуты работает в интенсивном режиме. Он измеряет температуру, напряжение батареек и посылает их значения в эфир через каждые 2 сек. Это сделано для удобства. Допустим, при замене батареек или отладке не придется ждать минуту. Информация с датчика появится на дисплее гигрометра в первые секунды после подключения батареек термометра и гигрометра. Естественно, стоит поторопиться и не делать разрыва во времени больше минуты между подключением батареек на обоих узлах.
Затем выносной датчик переходит в штатный режим. После измерений и отправки информации, которые длятся несколько больше 1-ой секунды, контроллер и передатчик программно переводятся в режим сна длительностью около 1-ой минуты.
Расчет времени работы.
Средний ток потребления: 8 мА / 60 + 0,01 мА = 0,14 мА, где 60 – скважность. Емкость батарейки GP Ultra+, (мА*час): 2.72 Вт*час / 1,5 В = 1,813 А*час = 1800 мА*час. Время эксплуатации: 1800 мА*час / 0,14 мА = 12800 час (18 мес).
Предыдущие уточнения относительно точности рассчитанного значения времени работы от одного набора батареек остаются в силе.
И сравнительная таблица. В ней есть результаты пары похожих проектов из Интернета.
Параметр | LaCrosse WS-9024IT | maniacbug | avs24rus | Сadil_TM |
Питание | 2хААА, 3В, Durasell | 3В, CR2450 Renata, 540 мА*ч | 3В, CR2450, 550-610 мА*ч< | 2хАА, 3В, GP Ultra+, 2.72 Wh |
Потребление сна | 10 мкА | 0,14 мА (?) | 14 мкА | 10 мкА |
Продолжительность сна | 1 мин | |||
Операционное потребление | 90 мкА | 13,57 мА | 16 — 18 мА | 8 мА |
Продолжительность работы | 0,027 сек | 1 сек | ||
Период цикла работа/сон | 5 сек | 1 мин | 10 мин | 1 мин |
Время эксплуатации | более 2 лет | более 0,5 года | около 1,5 года |
Если узлы собраны без ошибок, то на дисплее увидим такую картинку:
Из сравнительных таблиц видно, что операционное потребление любительских устройств на два порядка выше (более 100 раз!) выше, чем в аналогичной за функциями промышленной LaCrosse. Например, 8 мА против 90 мкА для выносного датчика и 3 мА: 200 мкА для гигрометра. Потребление сна примерно одинаково – около 10 мкА. Такое разительное отличие в операционном потреблении объясняется, на мой взгляд, тем, что контроллеры в любительских схемах запрограммированы на языке Arduino IDE, а в промышленных продуктах скорее всего на одном из языков низкого уровня или C#/C. Если же использовать регистры микроконтроллера, язык программирования C, то, уверен, можно выйти на потребление, сравнимое с промышленными образцами. Впрочем, это очень убедительно экспериментально показал HWman в своей публикации «Почему многие не любят Arduino». Выполнение простейшего скетча до десятка строк (Blink), выполненного на языке в одном случае Arduino и в другом — C приводит к проигрышу в производительности в 26 раз. Короче, повышенное потребление ресурсов – это плата за комфорт и небольшие усилия со стороны программиста – остальное за него в Arduino сделают «прожорливые» функции среды разработки. Предчувствую — придется напрячься и освоить C#/C…
Выводы
• Собранные гигрометр и термометр имеют слишком большое операционное потребление тока. Для уменьшения энергопотребления в оба узла введен режим сна. Существенно уменьшить операционное потребление можно, переписав скетч на языке C#/C.
• К двум батарейкам питания в схемах напрашивается третья, тогда автоматически решаются следующие проблемы — отпадает необходимость в переустановке фьюз, устойчивая работа контроллера на частоте 16 МГц, работа датчиков DHT22, DS18B20 вдали от нижнего порога напряжения питания. Последнее немаловажно, поскольку напряжение питания подается на датчики не прямо, а программно через ключ с пина контроллера, на котором падает порядка 1В.
• Применение радиомодулей LoRa, с дальностью связи при прямой видимости до 1,5 км, позволило установить устойчивую связь в пределах квартиры с питанием модулей на уровне 3В.
Ссылки по теме
Узел беспроводного датчика с низким энергопотреблением
Беспроводный Lighting-Sensor с питанием от CR2450
Термометр с беспроводной передачей данных
Превращаем Arduino в полноценный AVRISP программатор
LoRa и сон
Узнайте о битах конфигурации ATmega328P и о том, как использовать их с внешним кварцевым резонатором
Калькулятор фьюзов AVR
Почему многие не любят Arduino
Грандиозное тестирование батареек
Батарейки на морозе