Одним из первых моих проектов при изучении 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,0×92}};
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
Прикрепленные файлы:
- ArduinoMeteo.rar (686 Кб)
- ArduinoMeteo.rar (686 Кб)
- ArduinoWeatherServ.zip (2 Кб)