Метеостанция на Arduino с беспроводным датчиком температуры. BMP085 Barometric Pressure Sensor Module for arduino (или как сделать метеостанцию своими руками)

Для Windows 23.06.2019
Для Windows

Я тестировал отдельные части системы на Arduino UNO. Т.е. подключал к уно ESP модуль и изучал его, отключал, затем подключал nRF24 и т.д. Для финальной реализации заоконного датчика выбрал Arduino Pro Mini как наиболее близкую к Uno из миниатюрных.



По энергопотреблению Arduino Pro Mini также выглядит неплохо:

  • нет преобразователя USB-TTL, который сам по себе «кушает» много,
  • светодиод подключен через 10к резистор.

Для продвинутого сбережения энергии планировалось:

  • удалить светодиод - индикатор питания на Arduino Pro Mini (я пожалел, не стал портить плату)
  • либо использовать «голую» сборку на микропроцессоре Atmel ATmega328 (не использовал)
  • использовать библиотеку Low Power Library или JeeLib .

Из библиотек выбрал Low Power Library , она проста и содержит только то, что нужно.


Для центрального блока, поскольку к нему планировалось подключить многочисленную периферию, была выбрана плата Arduino Mega. К тому же она полностью совместима с UNO и имеет больше памяти. Забегая наперед скажу, что этот выбор полностью оправдался.


Купить Arduino Mega можно примерно за $8.

Питание и энергопотребление

Теперь про питание и энергопотребление.


Arduino Pro Mini бывают двух видов:

  • на напряжение питания 5В и частоту 16МГц
  • на напряжение питания 3,3В и частоту 8МГц.

Поскольку радио-модуль nRF24L01+ требует для питания 3,3 В, а быстродействие здесь не важно, то покупайте Arduino Pro Mini на 8MHz и 3,3В.


При этом диапазон питающего напряжения Arduino Pro Mini составляет:

  • 3,35-12 В для модели 3,3 В
  • 5-12 В для модели 5 В.

У меня уже была Arduino Pro Mini на 5В, только поэтому я её и использовал. Купить Arduino Pro Mini можно примерно за $4.


Питание центрального блока будет от сети 220 В через небольшой блок питания, дающий на выходе 12В, 450mA, 5W. Типа такого за $5. Там еще есть отдельный вывод на 5В.



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


Поэтому Arduino Pro Mini и радиомодуль nRF24 будут запитываться от связки 4-х Ni-Mh аккумуляторов.


И помните, максимальная емкость современного аккумулятора примерно 2500-2700mAh, всё что больше это либо маркетинговые уловки (Ansmann 2850) либо обман (UltraFire 3500).


Li-Ion аккумуляторы я не использую по нескольким причинам:

  • очень дорогие
  • при снижении температуры окружающего воздуха ниже 0°C происходит снижение мощности литий-ионного аккумулятора до 40-50%
  • те которые дешёвые производятся без защиты и небезопасны (при КЗ или разряде могут взрываться и гореть, см. кучу роликов на ютюбе)
  • стареют, даже если не используются (впрочем это можно сказать обо всех химических элементах), через 2 года Li-Ion батарея теряет около 20% ёмкости.

Для прототипа вполне можно обойтись качественными Ni-MH AA или AAA аккумуляторами. Тем более, что нам не нужны большие токи. Единственный минус Ni-MH аккумуляторов - это их долгая зарядка.

Общая схема метеостанции

Подведем итоги. Вот общая схема как всё работает.



Продолжение следует.

Теги: Добавить метки

Продолжаем развивать нашу метеостанцию.

Перед тем, как перейти к обновлению, хочу внести немного ясности.

Мне написал один из наших коллег с вопросом, по какой причине введен сторожевой таймер?

Сторожевой таймер стоит на случай ч.п. Как показывает практика, ENC28J60 не тянет более (если не подводит память) 4 одновременных соединений. Учитывая сколько служебных соединений, постоянно происходит для поддержания работы самой сети, и просто левый трафик, создаваемый всяческими домашними игрушками (например, современные телевизоры, сканируют доступные хосты в сети и открытые у них порты) конструкция попросту уходит в ступор. ENC28J60 не умеет самостоятельно работать с сетевыми протоколами и все реализовано в библиотеках. Возможно дело именно в них.
Проверял все доступные библиотеки и разные модули (вдруг брак), но добиться стабильной работы в течении длительного времени у меня не получилось. Максимальный срок был порядка 3-4 недель.
Именно для этого там крутится "пес" и в случае чего дергает контроллер. После этого проблема ушла.
Также не отрицаю, что возможно в моей домашней сети есть определенные нюансы или проблемы. Но раз проблема была у меня, она может выплыть и у другого человека. Я пока нашел только такое решение.
Насколько мне известно, на чипах от Wiznet (W5100 и выше) этого нет, ну или просто плохо искали.

Переходим к обновлению

Самое главное, мы уходим от чипа ENC28J60 и переходим на W5100 . Я пытался реализовать все на старом чипе, но не хватает памяти микроконтроллера из-за очень больших библиотек для ENC28J60 . При использовании нового чипа, стандартной библиотеки от разработчика и всех внесенных изменений, остается еще более 20% свободной памяти микроконтроллера ATMega328 . А это, новые плюшки!

В этой версии (назовем её второй) добавлена возможность передачи показаний с датчиков по беспроводной связи используя частоту 433 мГц . Сами модули я брал у Китайцев, маркировка XY-MK-5V . Хочу отметить, что качество передачи далеко от совершенства. Возможны потери сигнала, шумы, не возможность одновременной передачи и т.д и т.п. Но их цена (менее $1 за комплект) компенсируют эти недостатки. Скажу Вам по секрету, что именно эти (самые дешевые) модули стоят во многих фирменных метеостанциях для домашнего использования. Ого, неожиданно?

Начнем с базовой станции

Мы переходим на Arduino UNO и Ethernet Shield (первой версии) на базе чипа W5100 . Это бутерброд и описывать его нету смысла. Я опишу только дополнительно задействованные контакты для модулей XY-MK-5V .

Модуль передатчика использует питание 5V , GND (куда без матушки то) и D2 пин на контроллере. Изменить контакт D2 (DATA) можно, используя функцию vw_set_tx_pin из библиотеки vw.

В отличии от предыдущего скетча, в этом задействованы две дополнительные библиотеки:

#include #include

Сам скетч

Скрытый текст

#include #include #include #include #include #include #include #include #define DHTTYPE DHT22 #define DHTPIN 5 DHT dht(DHTPIN, DHTTYPE); byte mac = {0x54, 0x34, 0x31, 0x31, 0x31, 0x31}; char server = "narodmon.ru"; int port = 8283; IPAddress ip(192,168,0,201); EthernetClient client; BMP085 dps = BMP085(); long Temperature = 0, Pressure = 0; float H, dP, dPt; bool interval = true; EasyTransferVirtualWire ET; struct SEND_DATA_STRUCTURE{ byte ID; // Идентификатор устройства int Temperature; // Температура float Pressure; // Давление float Humidity; // Влажность float dewPoint; // Точка росы/инея }; SEND_DATA_STRUCTURE broadcast; void setup() { // Инициализация сторожевого таймера (Watchdog timer) wdt_disable(); delay(8000); wdt_enable(WDTO_8S); // Инициализация консоли Serial.begin(9600); // Инициализация датчика DHT dht.begin(); // Инициализация модуля 433 мГц ET.begin(details(broadcast)); vw_set_ptt_inverted(true); vw_set_tx_pin(2); vw_setup(2000); // Стартуем сеть, если не дождались данных с DHCP сервера то // присваеваем себе адрес самостоятельно if (Ethernet.begin(mac) == 0) Ethernet.begin(mac, ip); // Инициализация 1-Wire Wire.begin(); delay(200); // Инициализация BMP180 с корректировкой высоты // dps.init(MODE_STANDARD, 3200, true); // Инициализация BMP180 dps.init(); Serial.println(Ethernet.localIP()); // Отправляем первые данные сразу после включения устройства send_info(true); } // dewPoint function NOAA // reference (1) : http://wahiduddin.net/calc/density_algorithms.htm // reference (2) : http://www.colorado.edu/geography/weather_station/Geog_site/about.htm double dewPoint(double celsius, double humidity) { // (1) Saturation Vapor Pressure = ESGG(T) double RATIO = 373.15 / (273.15 + celsius); double RHS = -7.90298 * (RATIO - 1); RHS += 5.02808 * log10(RATIO); RHS += -1.3816e-7 * (pow(10, (11.344 * (1 - 1/RATIO))) - 1) ; RHS += 8.1328e-3 * (pow(10, (-3.49149 * (RATIO - 1))) - 1) ; RHS += log10(1013.246); // factor -3 is to adjust units - Vapor Pressure SVP * humidity double VP = pow(10, RHS - 3) * humidity; // (2) DEWPOINT = F(Vapor Pressure) double T = log(VP/0.61078); // temp var return (241.88 * T) / (17.558 - T); } void send_info(bool eth) { bool fail = true; while(fail) { // Пытаемся считать данные с датчика влажности DHT до тех пор, пока не получим // результат. В 90% случаев все работает нормально, но нам нужны 100% if((H = dht.readHumidity()) >= 0) { // Получение влажности и температуры с датчика BMP180 dps.getPressure(&Pressure); dps.getTemperature(&Temperature); // Подсчитываем точку росы, если температура на улице выше 0 градусов Цельсия // и ожидаем результат выше 0, в противном случае выводим 0. Это необходимо // чтобы не вводить в заблуждения в зимее время года. // dP = Temperature>0?((dPt=dewPoint(Temperature*0.1, H))<0?0:dPt):0; dP = dewPoint(Temperature*0.1, H); // Отправляем данные в эфир 433 мГц broadcast.ID = 1; broadcast.Temperature = floor(Temperature*0.1); broadcast.Pressure = floor(Pressure/133.3*10)/10; broadcast.Humidity = floor(H*10)/10; broadcast.dewPoint = floor(dP*10)/10; ET.sendData(); delay(250); if(eth) { // Подключаемся к серверу "Народный мониторинг" if(client.connect(server, port)) { // Начинаем передачу данных // адрес_устройства_в_проекте, имя_устройства, GPS широта, GPS долгота client.print(F("#fe-31-31-0e-5a-3b#Arduino Uno#71.344699#27.200014\n")); // Температура client.print(F("#T0#")); client.print(Temperature*0.1); client.print(F("#Температура\n")); // Давление client.print("#P1#"); client.print(Pressure/133.3); client.print(F("#Давление\n")); // Влажность client.print("#H1#"); client.print(H); client.print(F("#Влажность\n")); // Точка росы\инея client.print("#T1#"); client.print(dP); client.print((dP <= 0)? F("#Точка инея\n"):F("#Точка росы\n")); //client.print(F("#Точка росы\n")); // Отправляем конец телеграммы client.print("##"); // Даем время отработать Ethernet модулю и разрываем соединение delay(250); client.stop(); } } // Останавливаем цикл, если передача завершена fail = !fail; break; } delay(250); } } void loop() { // Каждые 4 секунды сбрасываем сторожевой таймер микроконтроллера // Каждые 6 минут отправляем данные на "Народный мониторинг" // Каждые 30 секунд отсылаем данные в эфир 433 if(!(millis()%1000)) wdt_reset(); if(!(millis()%360000)) send_info(true); if(!(millis()%30000)) send_info(false); }

К самим модулям необходимо добавить антенну. Для 433 мГц достаточно обычного медного провода длинной 17 см . Без антенны можете забыть о нормальной работе.

Переходим к самой важной части этого обновления - локальная беспроводная станция

Для её реализации (на коленке) я использовал аналог Arduino NANO (на базе ATMega328 ) и TFT дисплей на чипе ST7735S с разрешением 128 x 160

Скрытый текст



Распиновка дисплей -> контроллер

============================= LED | 3.3V SCK | SCK (13) SDA | MOSI (11) A0 | DC (9) RESET | RST (8) CS | CS (10) GND | GND VCC | 5V ============================

Модуль приемник подключается также как передатчик, только DATA к пину D7 .

Пару снимков, как это выглядит:

Скрытый текст

Скетч приемника

Скрытый текст

#include #include #include #include int x, y; int w = 128, h = 160; int size; // 433 EasyTransferVirtualWire ET; struct SEND_DATA_STRUCTURE{ byte ID; // Идентификатор устройства int Temperature; // Температура float Pressure; // Давление float Humidity; // Влажность float dewPoint; // Точка росы/инея }; SEND_DATA_STRUCTURE broadcast; int Log_Temperature = -1; float Log_Pressure = -1; float Log_Humidity = -1; float Log_dewPoint = -1; // TFT #define cs 10 #define dc 9 #define rst 8 char Temperature, Pressure, Humidity, dewPoint; String info; TFT TFTscreen = TFT(cs, dc, rst); void setup(){ Serial.begin(9600); // Инициализация модуля 433 мГц ET.begin(details(broadcast)); vw_set_ptt_inverted(true); vw_set_rx_pin(7); vw_setup(2000); vw_rx_start(); // Инициализация и начальная настройка дисплея TFTscreen.begin(); TFTscreen.setRotation(2); TFTscreen.background(0, 0, 0); // Рисуем статические элементы // 1. Заходите к нам в гости TFTscreen.stroke(255, 255, 255); TFTscreen.setTextSize(1); TFTscreen.text(" ", 10, 10); // 2. Описание показаний с датчиков TFTscreen.text("mmHg", w/2+5, 80); TFTscreen.text("%", w/2+5, 100); TFTscreen.text("C", w/2+5, 120); broadcast.Temperature = 0; broadcast.Pressure = 0; broadcast.Humidity = 0; broadcast.dewPoint = 0; TFTPrint(); } void loop(){ if(ET.receiveData()){ if(broadcast.ID == 1) TFTPrint(); /* Serial.println(broadcast.Temperature); Serial.println(broadcast.Pressure); Serial.println(broadcast.Humidity); Serial.println(broadcast.dewPoint); Serial.println(); */ } } void changes(int size, int x, int y, bool up, bool clear = false) { if(clear) TFTscreen.stroke(0, 0, 0); else { changes(size, x, y, !up, true); TFTscreen.stroke((up)?0:255, 0, (up)?255:0); } if((size%2) == 0) size++; while(size > 0) { TFTscreen.line(x, y, x+(size--), y); ++x, (up)?--y:++y, --size; } /* while(size > 0) { TFTscreen.line(x, y, (up)?x+size-1:x, (up)?y:y+size-1); ++x, ++y, --size; } */ } int x_center(int w, int length, int size) { return floor((w-length*(size*5)+size*2)/2); } int x_alignment_right(int w, int length, int size) { return ceil(w-length*(size*5)+size*2); } void TFTPrint() { size = 3; // ================================================================================== // Вывод показаний температуры // ================================================================================== if(broadcast.Temperature != Log_Temperature) { TFTscreen.setTextSize(size); // Затираем устаревшие данные String info = String(Log_Temperature); info.concat(" C"); if(Log_Temperature > 0) info = "+"+info; info.toCharArray(Temperature, info.length()+1); TFTscreen.stroke(0, 0, 0); TFTscreen.text(Temperature, x_center(w, info.length()+1, size), 35); // Выводим новые показания info = String(broadcast.Temperature); info.concat(" C"); if(broadcast.Temperature > 0) info = "+"+info; info.toCharArray(Temperature, info.length()+1); // Меняем цвет значения температуры в зависимости от самой температуры int r, g = 0, b; if(broadcast.Temperature > 0) { r = map(broadcast.Temperature, 0, 40, 255, 150); // Красный b = map(broadcast.Temperature, 0, 40, 30, 0); // Изменяем оттенок для более наглядного перехода через ноль } else { r = map(broadcast.Temperature, -40, 0, 0, 30); // Изменяем оттенок для более наглядного перехода через ноль b = map(broadcast.Temperature, -40, 0, 150, 255); // Синий } TFTscreen.stroke(b, g, r); // ВНИМАНИЕ: в библиотеке перепутаны позиции цветов, место RGB используется BGR! TFTscreen.text(Temperature, x_center(w, info.length()+1, size), 35); } size = 1; // ================================================================================== // Вывод показаний давления // ================================================================================== if(broadcast.Pressure != Log_Pressure) { TFTscreen.setTextSize(size); // Затираем устаревшие данные info = String(Log_Pressure); info.toCharArray(Pressure, info.length()); TFTscreen.stroke(0, 0, 0); TFTscreen.text(Pressure, x_alignment_right(w/2-5, info.length(), size), 80); // Выводим новые показания info = String(broadcast.Pressure); info.toCharArray(Pressure, info.length()); TFTscreen.stroke(255, 255, 255); TFTscreen.text(Pressure, x_alignment_right(w/2-5, info.length(), size), 80); changes(10, 106, 85, (broadcast.Pressure > Log_Pressure)?true:false); } else { changes(10, 106, 85, true, true); changes(10, 106, 85, false, true); } // ================================================================================== // Вывод показаний влажности // ================================================================================== if(broadcast.Humidity != Log_Humidity) { TFTscreen.setTextSize(size); // Затираем устаревшие данные info = String(Log_Humidity); info.toCharArray(Humidity, info.length()); TFTscreen.stroke(0, 0, 0); TFTscreen.text(Humidity, x_alignment_right(w/2-5, info.length(), size), 100); // Выводим новые показания info = String(broadcast.Humidity); info.toCharArray(Humidity, info.length()); TFTscreen.stroke(255, 255, 255); TFTscreen.text(Humidity, x_alignment_right(w/2-5, info.length(), size), 100); changes(10, 106, 105, (broadcast.Humidity > Log_Humidity)?true:false); } else { changes(10, 106, 105, true, true); changes(10, 106, 105, false, true); } // ================================================================================== // Вывод показаний точки росы\инея // ================================================================================== if(broadcast.dewPoint != Log_dewPoint) { TFTscreen.setTextSize(size); // Затираем устаревшие данные info = String(Log_dewPoint); info.toCharArray(dewPoint, info.length()); TFTscreen.stroke(0, 0, 0); TFTscreen.text(dewPoint, x_alignment_right(w/2-5, info.length(), size), 120); // Выводим новые показания info = String(broadcast.dewPoint); info.toCharArray(dewPoint, info.length()); TFTscreen.stroke(255, 255, 255); TFTscreen.text(dewPoint, x_alignment_right(w/2-5, info.length(), size), 120); changes(10, 106, 125, (broadcast.dewPoint > Log_dewPoint)?true:false); } else { changes(10, 106, 125, true, true); changes(10, 106, 125, false, true); } // Обновляем значения в логах для последующего сравнения показаний Log_Temperature = broadcast.Temperature; Log_Pressure = broadcast.Pressure; Log_Humidity = broadcast.Humidity; Log_dewPoint = broadcast.dewPoint; }

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

Как мне показалось, дизайн это та часть проекта, которая отнимает большую часть времени!

Скрытый текст

Часть данных сфабрикованы для отображения некоторых элементов дизайна.

Артефакты на дисплее, это пыль и прочая грязь скопившаяся за долго время нахождения дисплея в... где то там, ... ну там, не помню откуда его достал! Отстаньте!

В скетче имеются функции позиционирования. Они довольно примитивны, но позволяют добиться определенных эффектов.

  1. x_center
  2. x_alignment_right

Первая производит центровку текста, а вторая выравнивание по правой части указанной зоны. Все вычисления производятся относительно размеров заданного текста, исходя из выражения 1 size = 1PX х 1PX сегмента шрифта.

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

Кстати, цвет и оттенок основной температуры изменяется в зависимости от самой температуры. Довольно спорное решение, но на мой взгляд, визуально комфортное. Я некоторое время бился над ней, и понял, что значения в функции stroke , объекта TFT дисплея, указаны в неверном порядке. BGR место RGB . Это ошибка разработчика, ну или я что-то не понимаю.

PS : Все довольно интересно, но на мой взгляд заслуживает дальнейшего развития. Чем и займемся через какое то время.

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



Так как погодные условия здесь достаточно переменчивы, а температура колеблется в диапазоне 30-40°С, я решил сделать домашнюю метеостанцию.

В чем заключаются функции погодной станции для дома?
Метеостанция на Ардуино с дисплеем – устройство, собирающее данные о погоде и условиях окружающей среды с помощью множества датчиков.

Обычно это следующие датчики:

  • ветра
  • влажности
  • дождя
  • температуры
  • давления
  • высоты

Моя цель – сделать портативную настольную метеостанцию своими руками.

Она должна уметь определять следующие параметры:

  • температуру
  • влажность
  • давление
  • высоту

Шаг 1: Покупаем нужные компоненты







  • DHT22 , датчик температуры и влажности.
  • BMP180 , датчик давления.
  • Припой
  • Однорядный разъем на 40 выходов

Из оборудования вам понадобятся:

  • Паяльник
  • Плоскогубцы для носоупоров
  • Провода

Шаг 2: Датчик температуры и влажности DHT22







Для измерения температуры используются разные датчики. Популярностью пользуются DHT22, DHT11, SHT1x

Я объясню, чем они отличаются друг от друга, и почему я использовал именно DHT22.

Датчик AM2302 использует цифровой сигнал. Этот датчик работает на уникальной системе кодировки и сенсорной технологии, поэтому его данные надежны. Его сенсорный элемент соединен с 8-битным однокристальным компьютером.

Каждый сенсор этой модели термокомпенсированный и точно откалиброванный, коэффициент калибровки находится в однократно программируемой памяти (ОТР-память). При чтении показаний сенсор будет вызывать коэффициент из памяти.

Маленький размер, низкое потребление энергии, большое расстояние передачи (100 м) позволяют AM2302 подходить почти ко всем приложениям, а 4 выхода в один ряд делают монтаж очень простым.

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

DHT11

Плюсы: не требует пайки, самый дешевый из трех моделей, быстрый стабильный сигнал, дальность свыше 20 м, сильная интерференция.
Минусы: Библиотека! Нет вариантов разрешения, погрешность измерений температуры +/- 2°С, погрешность измерений уровня относительной влажности +/- 5%, неадекватный диапазон измеряемых температур (0-50°С).
Области применения: садоводство, сельское хозяйство.

DHT22

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

SHT1x

Плюсы: не требует пайки, сглаженные кривые, малые погрешности измерений, быстрое срабатывание, низкое потребление энергии, автоматический режим сна, высокая стабильность и согласованность данных.
Минусы: два цифровых интерфейса, погрешность в измерении уровня влажности, диапазон измеряемых температур 0-50°С, нужна библиотека.
Области применения: эксплуатация в суровых условиях и в долгосрочных установках. Все три датчика относительно недорогие.

Соединение

  • Vcc – 5В или 3,3В
  • Gnd – с Gnd
  • Data – на второй вывод Arduino

Шаг 3: Датчик давления BMP180



BMP180 – барометрический датчик атмосферного давления с I2C-интерфейсом.
Барометрические датчики атмосферного давления измеряют абсолютное значение окружающего воздуха. Этот показатель зависит от конкретных погодных условий и от высоты над уровнем моря.

У модуля BMP180 имелся 3,3В стабилизатор на 662кОм, который я, по собственной глупости, случайно взорвал. Пришлось делать обводку питания напрямую к чипу.

Из-за отсутствия стабилизатора, я ограничен в выборе источника питания – напряжение выше 3,3В разрушит датчик.
У других моделей может не быть стабилизатора, обязательно проверяйте его наличие.

Схема соединения датчика и шины I2C с Arduino (nano или uno)

  • SDA — A4
  • SCL — A5
  • VCC — 3.3V
  • GND – GND

Давайте немного поговорим о давлении, и его связи с температурой и высотой.

Атмосферное давление в любой точке непостоянно. Сложное взаимодействие между вращением Земли, наклоном Земной оси, приводит к появлению множества областей высокого и низкого давления, что, в свою очередь, приводит к ежедневной смене погодных условий. Наблюдая за изменением давления, вы можете сделать краткосрочный прогноз погоды.

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

Атмосферное давление также изменяется с высотой. Абсолютное давление в базовом лагере на Эвересте (5400 м над уровнем моря) ниже, чем абсолютное давление в Дели (216 м над уровнем моря).

Так как показатели абсолютного давления изменяются в каждой локации, мы будем обращаться к относительному давлению, или давлению на уровне моря.

Измерение высоты

Среднее давление на уровне моря 1013,25 ГПа (или миллибар). Если подняться над атмосферой, это значение упадет до нуля. Кривая этого падения вполне понятна, поэтому вы можете сами вычислить высоту над уровнем моря, используя следующее уравнение: alti=44330*

Если вы примите давление на уровне моря 1013,25 Гпа как р0, решением уравнения будет ваша текущая высота над уровнем моря.

Меры предосторожности

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

Защитите от нагревания. Для измерения давления необходимы точные температурные показания. Постарайтесь защитить датчик от перепадов температуры и не оставляйте его вблизи источников высоких температур.

Защитите от влаги. Датчик BMP180 чувствителен к уровню влажности, постарайтесь предотвратить возможное попадание воды на датчик.

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

Шаг 4: Собираем прибор







Устанавливаем однорядные разъемы для Arduino Nano. Вообще, мы обрезали их до нужного размера и немного зашкурили, так что они смотрятся, словно такими и были. Потом припаиваем их. После, устанавливаем однорядные разъемы для датчика DHT22.

Устанавливаем 10кОМ резистор от вывода данных к земле (Gnd). Все паяем.
Потом точно также устанавливаем однорядный разъем для датчика BMP180, питание делаем 3,3В. Соединяем все с шиной I2C.

В последнюю очередь подключаем LCD-дисплей, на ту же I2C шину, что и датчик BMP180.
(в четвертый разъем я планирую позже подключить RTC-модуль (часы реального времени), чтобы прибор еще и время показывал).

Шаг 5: Кодирование




Загрузите библиотеки

Чтобы установить библиотеки на Arduino, перейдите по ссылке

#include
#include #include #include "DHT.h" #include

SFE_BMP180 pressure;

#define ALTITUDE 20.56 #define I2C_ADDR 0x27 // <<- Add your address here. #define Rs_pin 0 #define Rw_pin 1 #define En_pin 2 #define BACKLIGHT_PIN 3 #define D4_pin 4 #define D5_pin 5 #define D6_pin 6 #define D7_pin 7

#define DHTPIN 2 // what digital pin we"re connected to

// Uncomment whatever type you"re using! //#define DHTTYPE DHT11 // DHT 11 #define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321 DHT dht(DHTPIN, DHTTYPE); LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin); float t1,t2;

void setup() { Serial.begin(9600); lcd.begin (16,2); // <<-- our LCD is a 20x4, change for your LCD if needed // LCD Backlight ON lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE); lcd.setBacklight(HIGH); lcd.home (); // go home on LCD lcd.print("Weather Station"); delay(5000); dht.begin(); pressure.begin(); } void loop() { char status; double T,P,p0,a; status = pressure.startTemperature(); if (status != 0) { delay(status);

status = pressure.getTemperature(T); if (status != 0) { Serial.print("1"); lcd.clear(); lcd.setCursor(0,0); lcd.print("Baro Temperature: "); lcd.setCursor(0,1); lcd.print(T,2); lcd.print(" deg C "); t1=T; delay(3000);

status = pressure.startPressure(3); if (status != 0) { // Wait for the measurement to complete: delay(status);

status = pressure.getPressure(P,T); if (status != 0) {lcd.clear(); lcd.setCursor(0,0); lcd.print("abslt pressure: "); lcd.setCursor(0,1); lcd.print(P,2); lcd.print(" mb "); delay(3000);

p0 = pressure.sealevel(P,ALTITUDE); // we"re at 1655 meters (Boulder, CO)

a = pressure.altitude(P,p0); lcd.clear(); lcd.setCursor(0,0); lcd.print("Altitude: "); lcd.setCursor(0,1); lcd.print(a,0); lcd.print(" meters"); delay(3000); } } } } float h = dht.readHumidity(); // Read temperature as Celsius (the default) float t = dht.readTemperature(); t2=t; lcd.clear(); lcd.setCursor (0,0); // go to start of 2nd line lcd.print("Humidity: "); lcd.setCursor(0,1);lcd.print(h); lcd.print(" %"); delay(3000); lcd.clear(); lcd.setCursor (0,0); // go to start of 2nd line lcd.print("DHT Tempurature: "); lcd.setCursor(0,1); lcd.print(t); lcd.print(" deg C "); delay(3000); lcd.clear(); lcd.setCursor (0,0); // go to start of 2nd line lcd.print("Mean Tempurature: "); lcd.setCursor(0,1); lcd.print((t1+t2)/2); lcd.print(" deg C "); delay(3000); }

Я использовал версию Arduino 1.6.5, код точно к ней подходит, к более поздним так же может подойти. Если код по каким-либо причинам не подходит, используйте версию 1.6.5 как базовую.

Как-то прогуливаясь по городу увидел новый открывшийся магазин радиоэлектроники. Зайдя в него обнаружил большое количество шилдов для Ардуины т.к. у меня дома была Arduino Uno и Arduino Nano сразу пришла мысль поиграться с передатчиками сигнала на расстоянии. Решил купить самый дешевый передатчик и приемник на 433 МГц:

Передатчик сигнала.


Приемник сигнала.

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

Передатчик имеет следующие характеристики:
1. Модель: MX -FS - 03V
2. Радиус действия (зависит от наличия преграждающих предметов): 20-200 метров
3. Рабочее напряжение: 3.5 -12В
4. Размеры модуля: 19 * 19 мм
5. Модуляция сигнала: AM
6. Мощность передатчика: 10 мВт
7. Частота: 433 МГц
8. Необходимая длина внешней антенны: 25см
9. Простота подключения (всего три провода): DATA ; VCC ; земля.

Характеристики приемного модуля:
1. Рабочее напряжение: DC 5В
2. Ток: 4мA
3. Рабочая частота: 433,92 МГц
4. Чувствительность: - 105дБ
5. Размеры модуля: 30 * 14 * 7 мм
6. Небходима внешняя антенна: 32 см.

В просторах интернета сказано, что дальность передачи информации на 2Кб/сек может доходить до 150м. Сам не проверял, но в двухкомнатной квартире принимает везде.

Аппаратная часть домашней метеостанции

После нескольких экспериментов решил подключить к Arduino Nano датчик температуры, влажности и передатчик.


Датчик температуры DS18D20 подключается к ардуино следующим образом:

1) GND к минусу микроконтроллера.
2) DQ через подтягивающий резистор к земле и к выводу D2 Ардуины
3) Vdd к +5В.

Модуль передатчика MX -FS - 03V питается от 5 Вольт, вывод данных (ADATA) подключен к выводу D13.

К Ардуино Уно подключил LCD дисплей и барометр BMP085.


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

Приемник сигнала подключен к выводу D10.

Модуль BMP085 - цифровой датчик атмосферного давления. Датчик позволяет измерять температуру,давление и высоту над уровнем моря. Интерфейс подключения: I2C. Напряжение питания датчика 1.8-3.6 В

Подключается модуль к Arduino также, как и другие I2C устройства:

  • VCC - VCC (3,3 В);
  • GND - GND;
  • SCL - к аналоговому выводу 5;
  • SDA - к аналоговому выводу 4.
  • Очень низкая стоимость
  • Питание и I/O 3-5 В
  • Определение влажности 20-80% с 5% точностью
  • Определение температуры 0-50 град. с 2% точностью
  • Частота опроса не более 1 Гц (не более раза в 1 сек.)
  • Размеры 15.5мм x 12мм x 5.5мм
  • 4 вывода с расстоянием между ножками 0.1"

DHT имеет 4 вывода:

  1. Vcc (3-5V питание)
  2. Data out - Вывод данных
  3. Не используется
  4. Общий

Подключается к D8 Ардуины.

Программная часть домашней метеостанции

Передающий модуль измеряет и передает температуру раз в 10 минут.

Ниже привожу программу:

/* Версия скетча 1.0 Отсылаем температуру каждые 10мин. */ #include #include #include #define ONE_WIRE_BUS 2 //Пин подключения датчика Даллас OneWire oneWire(ONE_WIRE_BUS); DallasTemperature sensors(&oneWire); DeviceAddress insideThermometer; void setup(void) { //Serial.begin(9600); vw_set_ptt_inverted(true); // Необходимо для DR3100 vw_setup(2000); // Устанавливаем скорость передачи (бит/с) sensors.begin(); if (!sensors.getAddress(insideThermometer, 0)); printAddress(insideThermometer); sensors.setResolution(insideThermometer, 9); } void printTemperature(DeviceAddress deviceAddress) { float tempC = sensors.getTempC(deviceAddress); //Serial.print("Temp C: "); //Serial.println(tempC); //Формирование данных для для отправки int number = tempC; char symbol = "c"; //Служебный символ определения что это датчик String strMsg = "z "; strMsg += symbol; strMsg += " "; strMsg += number; strMsg += " "; char msg; strMsg.toCharArray(msg, 255); vw_send((uint8_t *)msg, strlen(msg)); vw_wait_tx(); // Ждем пока передача будет окончена delay(200); } void loop(void) { for (int j=0; j <= 6; j++) { sensors.requestTemperatures(); printTemperature(insideThermometer); delay(600000); } } //Определение адреса void printAddress(DeviceAddress deviceAddress) { for (uint8_t i = 0; i < 8; i++) { if (deviceAddress[i] < 16); //Serial.print("0"); //Serial.print(deviceAddress[i], HEX); } }

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

#include #include LiquidCrystal lcd(12, 10, 5, 4, 3, 2); #include dht11 sensor; #define DHT11PIN 8 #include #include BMP085 dps = BMP085(); long Temperature = 0, Pressure = 0, Altitude = 0; void setup() { Serial.begin(9600); vw_set_ptt_inverted(true); // Необходимо для DR3100 vw_setup(2000); // Задаем скорость приема vw_rx_start(); // Начинаем мониторинг эфира lcd.begin(16, 2); Wire.begin(); delay(1000); dps.init(); //lcd.setCursor(14,0); //lcd.write(byte(0)); //lcd.home(); } void loop() { uint8_t buf; // Буфер для сообщения uint8_t buflen = VW_MAX_MESSAGE_LEN; // Длина буфера if (vw_get_message(buf, &buflen)) // Если принято сообщение { // Начинаем разбор int i; // Если сообщение адресовано не нам, выходим if (buf != "z") { return; } char command = buf; // Команда находится на индексе 2 // Числовой параметр начинается с индекса 4 i = 4; int number = 0; // Поскольку передача идет посимвольно, то нужно преобразовать набор символов в число while (buf[i] != " ") { number *= 10; number += buf[i] - "0"; i++; } dps.getPressure(&Pressure); dps.getAltitude(&Altitude); dps.getTemperature(&Temperature); //Serial.print(command); Serial.print(" "); Serial.println(number); lcd.print("T="); lcd.setCursor(2,0); lcd.print(number); lcd.setCursor(5,0); lcd.print("P="); lcd.print(Pressure/133.3); lcd.print("mmH"); lcd.setCursor(0,1); lcd.print("T="); lcd.print(Temperature*0.1); lcd.print(" H="); lcd.print(sensor.humidity); lcd.home(); //delay(2000); int chk = sensor.read(DHT11PIN); switch (chk) { case DHTLIB_OK: //Serial.println("OK"); break; case DHTLIB_ERROR_CHECKSUM: //Serial.println("Checksum error"); break; case DHTLIB_ERROR_TIMEOUT: //Serial.println("Time out error"); break; default: //Serial.println("Unknown error"); break; } } }

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

Ниже прилагаю фото того что получилось:

Список радиоэлементов

Обозначение Тип Номинал Количество Примечание Магазин Мой блокнот
Передающая часть.
Плата Arduino

Arduino Nano 3.0

1 В блокнот
Датчик температуры

DS18B20

1 В блокнот
Резистор

220 Ом

1 В блокнот
Модуль передатчика MX-FS-03V (433 МГц) 1 В блокнот
Радиоприемная часть.
Плата Arduino

Arduino Uno

1 В блокнот
Подстроечный резистор 1 В блокнот
Резистор


Рекомендуем почитать

Наверх