Реклама ⓘ
Главная » Arduino
Призовой фонд
на апрель 2024 г.
1. 100 руб.
От пользователей

Похожие статьи:


Реклама ⓘ

Cервер домашней метеостанции на Arduino + Виджет на Android для вывода данных

Одним из первых моих проектов при изучении Arduino была работа с цифровым датчиком температуры DS18B20. Затем я подключил три датчика DS18B20 и отправлял показания на сайт. Не так давно приобрел датчик температуры и влажности и к конкурсу решил создать проект сервера домашней метеостанции, при обращению к которому выдаются результаты с датчиков по JSON и написать виджет к телефону Android, показывающий данные показания. Виджет выводит показания с датчиков при нахождении дома (домашняя сеть), или из любого другого места при подключении телефона к интернету.

Создание домашнего сервера метео на Arduino

Дома подключены три датчика DS18B20 и один датчик DHT11

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

При написании программы использовались следующие библиотеки Arduino

  • Ethernet - библиотека для работы с Ethernet-shield
  • spi - взаимодействовать с устройствами поддерживающими SPI протокол
  • onewire - взаимодействие с устройствами по протоколу 1-Wire
  • dht - Arduino библиотека для работы с датчиками DHT11,DHT22

Создаем web-сервер, присваиваем ip  и порт обращения 10001, и при обращении к серверу опрашиваем датчики DS18B20 и DHT11. Чтобы сервер не тратил время на опрос и поиск кодов датчиков DS18B20 я внес уже полученные коды в массив my_addr. Результат сервер отдает в формате JSON.

Вот код скетча

#include "Ethernet.h"
#include "SPI.h"
#include "OneWire.h"
#include "DHT.h"

#define DHTTYPE DHT11  // DHT 11 
DHT dht(8, DHTTYPE);

OneWire  ds(7);  // on pin 7

byte mac[] = { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
// IP адрес, назначаемый Ethernet shield:
byte ip[] = { 192, 168, 1, 111 };    

EthernetServer server(10001);

// коды датчиков DS18B20

byte my_addr[3][8]={{0x28,0x81,0xC4,0xBA,2,0,0,0x3B},
   {0x28,0x67,0xE5,0xC7,2,0,0,0xA0},
   {0x28,0xF6,0x98,0xBA,2,0,0,0x92}};


void setup() {
 Serial.begin(9600);
 Serial.println("start");
 // инициализация Ethernet shield
 Ethernet.begin(mac, ip);
 // запуск сервера
 server.begin();
 Serial.print("server ip ");
 Serial.println(Ethernet.localIP());
}


void loop () 
 {
 EthernetClient client = server.available();
 if (client) {
   // an http request ends with a blank line
   boolean currentLineIsBlank = true;
   while (client.connected()) {
   if (client.available()) {
   char c = client.read();
   if (c == '\n' && currentLineIsBlank) {
   client.println("HTTP/1.1 200 OK");
   client.println("Content-Type: text/html");
   client.println();
   client.print('{');
   client.print('"');client.print("meteo");client.print('"');client.println(":");
   client.print('{');
   for(int j=1;j<4;j++)
     {
     //Serial.print("temp");Serial.print(j);Serial.print("=");
     //int Temp=get_temp(j);
     //Serial.print(Temp/16);Serial.print(".");Serial.println(((Temp%16)*100)/16);
     client.print('"');client.print("temp");client.print(j);client.print('"');client.print(":");
     client.print('"');client.print(Temp/16);
     client.print(".");
     client.print(((Temp%16)*100)/16);client.print('"');client.print(',');
     }
     float h = dht.readHumidity();
     float t = dht.readTemperature();
     //Serial.print('"Humidity4":'); Serial.println(h);Serial.print(" %\t");
     //Serial.print('"Temp4":'); Serial.print(t);Serial.println(" *C");
     client.print('"');client.print("temp4");client.print('"');client.print(":");
     client.print('"');client.print(t);client.print('"');client.print(',');
     client.print('"');client.print("humidity4");client.print('"');client.print(":");
     client.print('"');client.print(h);client.print('"');
     client.println("}");
     client.print("}");
     break;
     }
      if (c == '\n') {
      currentLineIsBlank = true;
      } 
     else if (c != '\r') {
       currentLineIsBlank = false;
     }
    }
   }
   delay(1);
   client.stop();
   }
 }
// получение температуры датчика
int get_temp(int nn)
 {
byte i;
byte present = 0;
byte data[12];
byte addr[8];
int Temp;

 ds.reset();
 ds.select(my_addr[nn-1]);
 ds.write(0x44,1);    // start conversion, with parasite power on at the end
 delay(1000);    // maybe 750ms is enough, maybe not
 // we might do a ds.depower() here, but the reset will take care of it.

 present = ds.reset();
 ds.select(my_addr[nn-1]);
 ds.write(0xBE);    // Read Scratchpad
 for ( i = 0; i < 9; i++) {    // we need 9 bytes
   data[i] = ds.read();
 }
 Temp=(data[1]<<8)+data[0];
 Temp=Temp;
 return Temp;
 }

Теперь пишем виджет на Android, чтобы на телефоне видеть показания датчиков, находясь в любом месте при наличии соединения с интернет.

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

Для создания виджета вам необходимо:
1. Создать XML-layout файл со слоем, в котором описывается внешний вид виджета.
2. Создать XML файл метаданных, в котором   задаются различные характеристики виджета:

- layout-файл (из п.1.), чтобы виджет знал, как он будет выглядеть
- размер виджета, чтобы виджет знал, сколько места он должен занять на экране
- интервал обновления, чтобы система знала, как часто ей надо будет обновлять виджет

3. Создать BroadcastReceiver, который будет использован для обновления виджетов. Этот приёмник расширяет AppWidgetProvider, который обеспечивает жизненный цикл виджета.
4. Изменения в файле AndroidManifest.xml

Рассматривать программирование на Android я здесь не буду, займет слишком много места. Проблем при программировании обнаружилось много, сначала делал для Android 2.1, этот код для Android 2.3 уже давал ошибки при работе со строками, измененный код не пошел для Android 3.2 - изменились методы получения сетевых данных (работа стала возможна только в отдельном потоке), для Android 4.0 проверить не на чем, эмулятор постоянно виснет, а устройств нет. В общем времени для написания программы ушло больше недели. Для тех кому интересно в конце топика выложена ссылка на файлы данного проекта проекта для среды Eclipse.

При запуске виджета он пытается соединиться с внутренней сетью (192.168.1.111:10001 на тот случай, если я нахожусь дома), при неудаче с адресом в сети интернет хх.хх.хх.хх:10001

Если соединение удачно - парсим JSON-ответ выводим данные виджете и обновляем виджет

При неудаче - сообщение в виджет об отсутствии соединения

При нажатии на иконку происходит обновление данных. 

У меня дома интернет через ADSL-модем, необходимо открыть порт 10001

Архив с файлами проекта для среды Eclipse находится в файле ArduinoMeteo.rar

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

Продолжение статьи:
Cервер домашней метеостанции на Arduino + Виджет на Android. Добавление датчика BMP085
Cервер домашней метеостанции на Arduino - виджет для OS X

Прикрепленные файлы:

Теги:

Опубликована: Изменена: 10.01.2014 0 1
Я собрал 0 Участие в конкурсе 2
x

Оценить статью

  • Техническая грамотность
  • Актуальность материала
  • Изложение материала
  • Полезность устройства
  • Повторяемость устройства
  • Орфография
0

Средний балл статьи: 4.9 Проголосовало: 2 чел.

Комментарии (25) | Я собрал (0) | Подписаться

0
mr_smit #
Не понятно что за ардуино плата использовалась. И как подключена. На роутере/модеме по идее должен быть организован DynDNS. Иначе как обратиться к плате через интернет. А про это ни слова.
Ответить
0

[Автор]
victoruni #
Использоваться может любая Arduino-плата, но лучше стандартная (UNO, Due, у меня стоит китайская Funduino, чтобы Ethernet-shield становился в контакты Arduino-платы. Плата с помощью разъема RJ-45 подключена во внутреннюю сеть (к роутеру или прямо к ADSL-модему), создает web-сервер, к которому из сети обращаться можно 192.168.0.111:10001 (см. скетч для Arduino во вложении). На модеме ADSL - в Сетевой экран-> Настройка виртуального сервера открываем порт 10001, чтобы из внешней сети можно было обратиться к серверу, допустим 77.39.xx.xx:10001 (см. картинку).
Ответить
0
mr_smit #
У вас дома статический IP?
Ответить
0

[Автор]
victoruni #
Да, IP статический
Ответить
0
mr_smit #
Так надо было хотя бы про это сказать. Это редкость. У большинства динамический IP от провайдера. И без DynDNS (или аналога) - никак. Это уже поинтересней будет в плане настройки.
Ответить
0
romarik #
В простонародье это называется "докопаться". Автор очень интересную вещь реализовал, а настроить порт на роутере, и уж тем более использовать ЛЮБОЙ сервис DNS, даже не ваш любимый, это не надо быть 7 пядей во лбу. В общем слабовато вы тролльнули.
Ответить
0
raxp #
К плате обращаться в принципе незачем, реализуйте выгрузку данных на сайт, по тому же FTP. Или же пусть скрипт на сайте сам лезет по одному ему известному адресу платы, проброшенной через роутер, и читает нужные данные для последующего отображения на сайте. Виджет же (клиент) пусть парсит данные с сайта. Это решает проблему скрытия от посторонних глаз домашнего IP-ника. Собственно, так я и решал эту проблему.
Что касается динамического адреса, проблема скорее надумана, ибо почти все модели роутеров могут работать с DDNS. А некоторые брэнды типа ASUS и вовсе предоставляют собственный сервис для своей продукции. Если это не роутер, а типичный 3G-модем и прочее опсосное, то рациональнее будет самому ардуино-серверу выгружать данные на сайт.
Ответить
0

[Автор]
victoruni #
Да это более правильное решение. Мне желательно иметь сервер метео в домашней сети - обращение к серверу из ROS и голосовое оповещение о погоде - хотя и здесь можно обращаться к сайту, но на случай отсутствия интернета
Ответить
0
raxp #
Первое время так и было для пользователей в локальной сети прова (в домашней сети-то с этим проблем нет в любом случае), но потом количество обращений резко возросло, плюс к тому же хотелось масштабируемое решение, а при этом уже светить свой адрес совсем ни к чему. Ввиду анлима было принято решение выгружать данные на сайт.
Касательно голосового оповещения, можно конечно использовать для озвучки тот же http://translate.google.ru/translate_tts. Однако, ввиду тенденции сокращения своих сервисов гуглом (вон очередной ридер и войс для блэкберри закрыли), все же рациональнее будет воспользоваться любыми локальными TTS для синтеза, движок Nikolay неплох.
P.S. Впрочем, если набор фраз для озвучки температуры и прочего ограничен, то еще лучше будет просто наговорить себе базу wav-ок, загнать в ресурсы или в DLL-ку (это уже для виндузятников :)) и выбирать потихоньку из заместо синтеза.
Ответить
0
Scarhand #
А если у меня ip динамический, можно организовать доступ извне? Предварительно я создал хост hm1.dlinkddns.com, если я правильно понял то туда свой адрес прописывает роутер.
Ответить
0
talibanich #
Теперь необходимо настройки DDNS в вашем маршрутизаторе (а он в свою очередь должен поддерживать Dynamic DNS серверы)
Ответить
0
Scarhand #
Я настроил DDNS в роутере вот так
Но к сожалению, при переходе по ссылке http://****.dlinkddns.com:10001/ браузер пишет "Could not connect to remote server". Пробовал просканировать порт 10001, пишет что порт открыт.
Ответить
0
talibanich #
1. Не светите в комментариях свой личный сайт.
2. В настройках маршрутизатора сделайте проброс портов (Port Forwarding). С 10001 порта на 80. Об этом много статей в интернете, или создайте тему в соответствующем подфоруме по сетям.
Ответить
0
Scarhand #
Зачем делать проброс порта с 10001 на 80, если у меня сервер работает на 10001 порту?
Ответить
0
talibanich #
Чтобы попадать на ваш девайс, а не на веб-интерфейс маршрутизатора.
Ответить
0
Scarhand #
Спасибо, помогло. Вроде пока работает.
Ответить
0
ds18b20 #
Не поможете советом? Как организовать опрос нескольких шлейфов с ds18b20? Мне мало одного.
Шина не работает со звездой - только шина, а мне не хватает длины. Там же в спеках и рекомендациях на 1820 есть пример с отдельным мультиплексором, чтобы перебирать несколько шлейфов. Но это уже сильно сложнее, чем просто перебирать несколько ног с отдельными шлейфами на дуине (хотелось бы обойтись только ей, нано или микро хватает выше крыши) - благо ног свободных достаточно, но вот как закодить... Не силен. Вроде в скетче видно, что однажды объявляем какая у нас это нога, инициализируем либу - фас на эту ногу, и все, основной цикл только ее и опрашивает. Можно ли подменять гпио, переинициализировать библиотеку в основном цикле? Или инициализировать несколько экземпляров и подправить код на их независимый вызов? Как?
Ответить
0
CRImier #
Подменять GPIO и тому подобное - дохлый номер, C - статический язык. Самый нормальный подход - инициализировать несколько экземпляров, но было бы хорошо, если бы Вы где-то могли бы выложить код, чтобы можно было понять, какую библиотеку Вы используете и в чём именно проблема. Советую создать тему на форуме cxem.net, и не забудьте скинуть ссылку мне в ЛС
Ответить
0
prorok #
arduino 1.0.5 говорит что непонятный тип "DHT". Что можно сделать?
Ответить
0
CRImier #
Нужно в Интернете скачать нужную библиотеку. Вот ссылка на страницу с информацией об этой библиотеке.
Ответить
0
Gregory #
Сори, если слишком для чайников, такой вопрос возник. Если я хочу мерить температуру в одной комнате, а роутер в другой можно ли ардуино связать с ним через wi-fi?
Ответить
0
Роман #
А можно подключится из вне к серверу, у меня интернет 3ж usb модем
Ответить
0
Kamikadza #
Интересный проект. А сервер поднимался на роутере или у вас выделен отдельный ПК под сервер?
Ответить
0
Леонид #
Чтобы подключить одновременно несколько датчиков температуры DS18B20 необходимо сигнальную ножку подключить через резистор на 4.7 КОм на +5В, а +5В сделать общим (+ датчика не должен подсоединяться через резистор на + ардуино) иначе ничего работать не будет.
Ответить
0
LexResident #
Как-то странно резисторы на ds-ках стоят. Не по феншую. Автор, ты не перепутал?
Ответить
Добавить комментарий
Имя:
E-mail:
не публикуется
Текст:
Защита от спама:
В чем измеряется сила тока?
Файлы:
 
Для выбора нескольких файлов использйте CTRL

Модуль измерения тока на ACS712 (30А)
Модуль измерения тока на ACS712 (30А)
USB-реле (2 канала) Ручной фен 450 Вт с регулировкой температуры
вверх