После очередной бессонной ночи и утреннего просмотра лекций д-ра Комаровского стало ясно что влажность, на которую я внимания никогда не обращал, имеет значение для здоровья моего маленького ребенка. Детский кашель очень тесно связан с уровнем влажности (humidity) воздуха, и желательно этот самый уровень отслеживать и им управлять. Собственно для управления уровнем влажности был включен уже давно валявшийся увлажнитель sharp kc-6500e. Но хотелось еще чего нибудь smart, да и увлажнитель один, а комнат много, надо решать где он больше востребован. Температурные показатели легко ощутить без дополнительных приборов. Холодно или тепло в комнате обычно определяешь без градусников, а вот влажность физически ощутить сложнее. Поэтому было принято решение купить/собрать (нужное подчеркнуть) датчики для мониторинга уровня влажности в комнатах.
Побродив по магазинам и просторам сети я понял что не могу купить просто метеостанцию в каком нибудь *Mart, т.к. предлагающиеся устройства никак не вписываются в современность. Ну не буду я бегать каждый час по всем комнатам и записывать показания с экранов метеостанций, хочу чтобы мне эти данные отправлялись куда нибудь, а я потом их проанализирую. На правильную мысль навела статья на Habrhabr. Короче нужно устройство с разъемом Ethernet или wifi или zigbee… Проводные сети я всегда считал более надежными, да и сеть дома проложена с запасом.
Arduino, PoE, DHT11/21/22, narodmon.ru
Постепенно выработались некие требования к решению:
сбор данных и передача их в какие либо сервисы (собственные или сторонние)
физический интерфейс RJ45 (ethernet)
работа без батареек
приглядный внешний вид, отсутствие мотков с проводами вокруг.
возможность перетащить в другой дом (с другой сетью и провайдером) без доп настроек.
измерение уровня влажности и температуры. При необходимости добавление сенсоров.
если собирать самому, то без особых заморочек. Я не дружу с паяльником и программатором.
Исходя из требований к снижению количества проводов и работы без батареек я решил что лучшим решением будет использование PoE (Power over ethernet) для датчика. К тому же незадолго перед этим был заказан коммутатор с PoE под нужды видео-наблюдения. При необходимости, можно подключить и с помощью обычного блока питания.
На данный момент на рынке нашел чешский HWg-STE PoE, отечественный netping, оба вроде отвечают требованиям.
Сервис narodmon.ru предлагает кучу вариантов датчиков и неплохой сервис по отслеживанию их состояния. Средняя стоимость датчиков составляет в среднем 3000 руб., что в общем то меня уже не пугало так сильно как детский кашель. И все бы хорошо, но PoE ни у кого нет. Всем подавай отдельные блоки питания, а это куча лишних проводов, занятая розетка и вообще не интересно. Самое простое решение которое пришло в голову — взять за основу решение на Arduino и чуть чуть его изменить под свои нужды. Если три платы заменить на одну, и добавить PoE, то получим то что нужно.
Для датчика были заказаны:
Arduino Ethernet Rev3 WITH PoE (A000074) = €54.90 + доставка €9 (arduino.cc) В сумме на 2600 руб.
DHT22 2302 Digital Temperature and Humidity Sensor Module = 11$ или 340 руб. (При заказе из Китая их лучше комплектовать с товарами погабаритнее чтобы по почте быстрее шло. Меньше посылка — дольше идет. В моем случае 60 дней).
Как потом оказалось нужен еще USB 2 Serial Converter (A000059) для программирования Arduino, поскольку в этой модели нет USB разъема, его место занял Ethernet. Включать еще €10 в стоимость устройства или нет непонятно, т.к. он нужен только при программировании. В общем плюс еще 400 руб.
Итого требуется электроники на 3340 руб. Если закупать не Made in Italy, а клоны, то будет дешевле, но надо еще найти клоны с PoE модулем. При покупке всего этого добра в России увеличиваем бюджет до 4500 руб. Все элементы доставлялись почтой России, часть дошла за 30 дней, а часть до сих пор в пути, вместо DHT 22 пока установил DHT 11 — они дешевле, но менее точные. Пожалуйста не используйте DHT 11.
С сенсорами вообще вышла какая то неувязка. По идее это должны быть цифровые сенсоры, и распиновка по всем datashit с незадействованным третьим пином.
Но мне с Amperka.ru пришли сенсоры уже с резисторами, с разъемом и работали как аналоговые. При этом судя по пайке в этом варианте не использовался 4й пин (как на картинке справа).
Реализация на DHT 11/21/22 (пожалуйста не покупайте DHT11 — используйте DHT22)
Аналоговое подключение сенсоров.
Скетч который у меня заработал в итоге с аналоговым вариантом DHT11 с библиотекой от амперки (не забываем менять MAC адрес устройства на свой, большая часть кода взята отсюда, отсюда и отсюда ):
// создаём объект-сенсор
DHT sensor = DHT();
const unsigned long postingInterval = 600000; // интервал между отправками данных в миллисекундах (10 минут)
unsigned long lastConnectionTime = 0; // время последней передачи данных
boolean lastConnected = false; // состояние подключения
int HighByte, LowByte, TReading, SignBit, Tc_100, Whole, Fract;
char replyBuffer[106];
char CurTemp[6];
// ========================Задаем данные сети======================
byte mac[] = { 0x92, 0xA2, 0xDA, 0x0D, 0xD2, 0x3A }; //mac — адрес ethernet shielda
// byte ip[] = { 192, 168, 1, 177 }; // ip адрес ethernet shielda (в случае DHCP не нужен)
// byte subnet[] = { 255, 255, 255, 0 }; //маска подсети (в случае DHCP не нужен)
// EthernetServer server(80); //порт сервера
EthernetClient client; //
int ledPin = 9; // указываем что светодиод будет управляться через 9 Pin
String readString = String(30); //string for fetching data from address
boolean LEDON = false; //изначальный статус светодиода — выключен
IPAddress server(94,19,113,221); // IP сервера
// ===============================================================
Serial.println(«Failed to configure Ethernet using DHCP»);
// ничего не делаем
for(;;)
;
}
// секунда для инициализации Ethernet
delay(1000);
// секунда для инициализации Ethernet
delay(1000);
lastConnectionTime = millis()-postingInterval+15000; //первое соединение через 15 секунд после запуска
//запускаем Ethernet
// Ethernet.begin(mac);
//устанавливаем pin 9 на выход
pinMode(ledPin, OUTPUT);
//enable serial datada print
Serial.begin(9600);
Serial.println(«Port Test!»); // Тестовые строки для отображения в мониторе порта
Serial.println(«GO!»);// Тестовые строки для отображения в мониторе порта
delay(1000);
// методом attach объявляем к какому контакту подключен
// сенсор. В нашем примере это нулевой аналоговый контакт
sensor.attach(A0);
//
// после подачи питания ждём секунду до готовности сенсора к работе
delay(2000);
// lastConnectionTime = millis()-postingInterval+15000; //первое соединение через 15 секунд после запуска
}
void loop()
{
// метод update заставляет сенсор выдать текущие измерения
delay(5000);
sensor.update();
//switch (sensor.getLastError())
//{
// case DHT_ERROR_OK:
// char msg[128];
// данные последнего измерения можно считать соответствующими
// методами
// sprintf(msg, «Temperature = %dC, Humidity = %d%%»,
// sensor.getTemperatureInt(), sensor.getHumidityInt());
// Serial.println(msg);
// break;
// case DHT_ERROR_START_FAILED_1:
// Serial.println(«Error: start failed (stage 1)»);
// break;
// case DHT_ERROR_START_FAILED_2:
// Serial.println(«Error: start failed (stage 2)»);
// break;
// case DHT_ERROR_READ_TIMEOUT:
// Serial.println(«Error: read timeout»);
// break;
// case DHT_ERROR_CHECKSUM_FAILURE:
// Serial.println(«Error: checksum error»);
// break;
//}
if (client.available()) {
char c = client.read();
}
void itos(int n, char bufp[3]) //int to string
{
char buf[3]={‘0′,’0’,»};
int i = 1;
while (n > 0) {
buf[i] = (n % 10)+48;
i—;
n /= 10;
}
for (i=0; i<3; i++)
bufp[i]=buf[i];
}
У этого решения множество багов:
1. DHT11 в таком исполнении всегда врал. Возможно бракованные сенсоры попались, но врали они безбожно. Влажность почти всегда на одном уровне 33%.
2. Результаты иногда странным образом задваивались, то есть вместо 33% влажности получалось 66%. В код нужно добавлять контроль результата измерения в сравнении с последними данными.
3. Часть позволяющая устройству работать в качестве веб сервера закоментированна, поскольку я её после отладки не использовал, может надо кому.
Цифровое подключение сенсоров
Схема соединения и код который заработал с цифровым подключением DHT22. Сенсор который был у меня уже имел необходимую обвязку (подтягивающий резистор).
Библиотека для DHT здесь и теперь он поддерживает и DHT21.
Код:
//
// FILE: dht_test.pde
// PURPOSE: DHT library test sketch for Arduino. Works with web service narodmon.ru
//
#define DHT22_PIN 7
const unsigned long postingInterval = 400000; // интервал между отправками данных в миллисекундах (5 минут)
unsigned long lastConnectionTime = 0; // время последней передачи данных
boolean lastConnected = false; // состояние подключения
int HighByte, LowByte, TReading, SignBit, Tc_100;
char replyBuffer[106];
char CurTemp[6],CurHum[6];
// ========================Задаем данные сети======================
byte mac[] = {
0x92, 0xA2, 0xDA, 0x0D, 0xD2, 0x3A }; //mac — адрес ethernet shielda
EthernetClient client; //
int ledPin = 9; // указываем что светодиод будет управляться через 9 Pin
String readString = String(30); //string for fetching data from address
boolean LEDON = false; //изначальный статус светодиода — выключен
IPAddress server(94,19,113,221); // IP сервера narodmon.ru
// ===============================================================
void setup()
{
delay(4000);
// Ethernet connection:
if (Ethernet.begin(mac) == 0) {
Serial.println(«Failed to configure Ethernet using DHCP»);
// ничего не делаем. doing nothing
for(;;)
;
}
// пара секунд для инициализации Ethernet. Couple of seconds just to warm up Ethernet.
delay(2000);
lastConnectionTime = millis()-postingInterval+15000; //первое соединение через 15 секунд после запуска. First connection after 15 sec timeout.
//устанавливаем pin 9 на выход
pinMode(ledPin, OUTPUT);
Serial.begin(115200);
Serial.println(«DHT TEST PROGRAM «);
Serial.print(«LIBRARY VERSION: «);
Serial.println(DHT_LIB_VERSION);
Serial.println();
Serial.println(«Type,tstatus,tHumidity (%),tTemperature (C)»);
}
void loop()
{
// READ DATA
Serial.print(«DHT22, t»);
int chk = DHT.read22(DHT22_PIN);
switch (chk)
{
case DHTLIB_OK:
Serial.print(«OK,t»);
break;
case DHTLIB_ERROR_CHECKSUM:
Serial.print(«Checksum error,t»);
break;
case DHTLIB_ERROR_TIMEOUT:
Serial.print(«Time out error,t»);
break;
default:
Serial.print(«Unknown error,t»);
break;
}
// DISPLAY DATA
delay (1000);
// TempDig = DHT.temperature;
dtostrf(DHT.temperature, 4, 1, CurTemp);
delay (1000);
dtostrf(DHT.humidity, 4, 1, CurHum);
Serial.print (CurTemp);
Serial.print(«,t»);
Serial.println (CurHum);
Внимание апдейт — я пересмотрел свое отношение к коду — оказалось что датчик корректно отображал показания только при первом включении. Дальше показания не менялись до следующей перезагрузки. Поэтому исходный код я изменил, и новый вставил в статью на место старого. Кроме того, обращаю внимание — для работы скетча необходимо установить соответствующую библиотеку. Библиотека для DHT не входит в стандартный набор устанавливаемый вместе с IDE Adruino. Библиотеку я использовал не с Amperka.ru.
готовое устройство:
В качестве корпуса использовал корпуса от FDD и CD.
на руках нет ds18b20 чтобы проверить, но сложного быть ничего не должно. Нужно именно добавить, или заменить DHT22 на ds18b20? на первую ногу ds18b20 нужно подать GND, на последнюю +5V, а среднюю подключить к Digital выходам Arduino, например к 2му. Дополнительно установить поттягивающий резистор 4.7k между +5V и средней ножкой ds18b20. Если лень соединять +5V для DHT и DS, то можно DHT переключить на +3, он продолжит работать. Схема для DS здесь http://bildr.org/2011/07/ds18b20-arduino/ Что касается кода, то для начала устанавливаем библиотеку onewire ( http://www.pjrc.com/teensy/arduino_libraries/OneWire.zip копируем в папку с библиотеками Ардуино IDE). Затем вписать в начало кода и в часть loop все то что указано по ссылке bildr.org. Ну и затем — наша задача добавить в переменную replybuffer еще один сенсор (мак адрес + цифра) и значение. Нужно убедиться что в начале мы задали нужную длину переменной, например char replyBuffer[106];
добавить перед httpRequest(); :
strcat(replyBuffer,"&"); for (int k=0; k<6; k++) { int b1=mac[k]/16; int b2=mac[k]%16; char c1[2],c2[2];
if (b1>9) c1[0]=(char)(b1-10)+'A'; else c1[0] = (char)(b1) + '0'; if (b2>9) c2[0]=(char)(b2-10)+'A'; else c2[0] = (char)(b2) + '0';
может кто подскажет более подходящий вариант корпуса?
а как сюда добавить ds18b20 ?
на руках нет ds18b20 чтобы проверить, но сложного быть ничего не должно. Нужно именно добавить, или заменить DHT22 на ds18b20?
на первую ногу ds18b20 нужно подать GND, на последнюю +5V, а среднюю подключить к Digital выходам Arduino, например к 2му. Дополнительно установить поттягивающий резистор 4.7k между +5V и средней ножкой ds18b20. Если лень соединять +5V для DHT и DS, то можно DHT переключить на +3, он продолжит работать.
Схема для DS здесь http://bildr.org/2011/07/ds18b20-arduino/
Что касается кода, то для начала устанавливаем библиотеку onewire ( http://www.pjrc.com/teensy/arduino_libraries/OneWire.zip копируем в папку с библиотеками Ардуино IDE).
Затем вписать в начало кода и в часть loop все то что указано по ссылке bildr.org.
Ну и затем — наша задача добавить в переменную replybuffer еще один сенсор (мак адрес + цифра) и значение.
Нужно убедиться что в начале мы задали нужную длину переменной, например char replyBuffer[106];
добавить перед httpRequest();
:
strcat(replyBuffer,"&");
for (int k=0; k<6; k++)
{
int b1=mac[k]/16;
int b2=mac[k]%16;
char c1[2],c2[2];
if (b1>9) c1[0]=(char)(b1-10)+'A';
else c1[0] = (char)(b1) + '0';
if (b2>9) c2[0]=(char)(b2-10)+'A';
else c2[0] = (char)(b2) + '0';
c1[1]='