О цифровых термометрах на основе Arduino было сказано немало. Все они либо подключались к компьютеру, либо выводили температуры сразу на дисплей.
Но мне был нужен уличный термометр, который автономно и отправляет данные на сайт. Итак, приступим.
Что нам понадобится:
- Arduino Duemilanove (Freeduino 2009)
- Ethernet Shield v2
- цифровой датчик температуры — DS18B20
- вентилятор для корпуса (120 мм)
- банка от водоэмульсионки или клея ПВА (2 литра)
- светодиод
- витая пара
Задачи
Опрашивать датчик температуры по шине 1-Wire и каждые 3 секунды самостоятельно отправлять результаты на Web-сервер, на котором они будут храниться.
Алгоритм работы устройства:
- присваиваем нашему Ethernet Shield`у MAC адрес и ip-адрес
- инициализируем соединение с сервером на 80 порт
- получаем данные с цифрового датчика температуры, по 1-Wire шине
- формируем GET запрос
- отправляем GET запрос
- разрываем соединение
Исходный код скетча:
Комментарии по ходу кода должны внести ясность.
include <Ethernet.h>
// Библиотеки ниже нет в стандартной поставке среды разработки Arduino.
// придётся её скопировать.
include <DallasTemperature.h>
// MAC-адрес нашего устройства
byte mac[] = { 0x00, 0x3A, 0xF1, 0x19, 0x69, 0xFC };
// ip-адрес устройства
byte ip[] = { 192, 168, 1, 156 };
// ip-адрес удалённого сервера
byte server[] = { 79, 140, 28, 20 }; // измените на свой
char temp[6];
byte isdata=0;
Client client(server, 80); // 80-порт.
DallasTemperature tempSensor;
void setup()
{
Ethernet.begin(mac, ip); // Инициализируем Ethernet Shield
tempSensor.begin(7); // Датчик температуры на 7-й пин
Serial.begin(9600); // Скорость консольного порта 9600 (пригодится для отладки)
}
void loop()
{
delay(3000); // задержка в 3 сек.
// Соединяемся
if (client.connect()) {
Serial.println("connecting..."); // Serial.println для отладки. Лучше его оставить, на всякий случай, потом будет легче понять, в чём проблема.
// Обработчик ошибок датчика
switch(tempSensor.isValid())
{
case 1:
Serial.println("Invalid CRC"); // ошибка контрольной суммы
tempSensor.reset(); // сбросить девайс
return;
case 2:
Serial.println("Invalid device"); // какой-то "левый" датчик :)
tempSensor.reset(); // сбросить девайс
return;
}
Serial.println("connected");
char buf[80];
float f=tempSensor.getTemperature(); // получаем температуру
Serial.println(tempSensor.getTemperature());
// Ниже извращения с отделением дробной части и целой. Почему-то Arduino не хочет работать с float.
// Вместо числа вставляет вопросик. Наверное, виной тому отсутствие аппаратной поддержки работы с
// числами с плавающей запятой в Arduino. Буду рад увидеть более красивое решение в комментариях.
int temp1 = (f - (int)f) * 100; // выделяем дробную часть
// Составляем GET запрос. Переменная code нужна для того, чтобы вражеский термометр не слал какие попало значения.
// проверяется на стороне Web-сервера.
sprintf(buf, "GET /class/backend/meteo.php?temp=%0d.%d&code=123456 HTTP/1.0", (int)f, abs(temp1));
Serial.println(buf);
client.println(buf); // Отправляем GET запрос
client.println("Host: opck.info"); // Указываем, какой конкретно host на данном ip нас интересует.
client.println();
} else {
Serial.println("connection failed");
}
while (client.available()) {
isdata=1;
char c = client.read(); // Читаем, что нам ответил Web-сервер
Serial.print(c);
}
if (!client.connected()) {
isdata=0;
Serial.println();
Serial.println("disconnecting.");
client.stop(); // Завершаем соединение
}
}
Сборка устройства:
- первую «ногу» датчика цепляем на «минус» GND
- вторую «ногу» (DQ) на 7-й пин
- третью на «плюс»
- вторую и третью нужно соединить резистором на ~ 4,7 К. Но я заменил резистор на светодиод и получил индикатор обращения к шине датчика (ВНИМАНИЕ! Без резистора или светодиода работать ничего не будет. Не забудьте!)
По идее, вот и всё. Должно работать.
Работает, но боевые условия показали, что когда падает солнечный свет на датчик, тот может нагреваться и показывать температуру гораздо выше реальной. Всё правильно — он покажет температуру на солнце. А нам нужна температура воздуха.
В первый раз для этого был собран корпус из-под банки от кофе, обёрнутый в фольгу. Но это ничем не помогло.
Изучение фотографий реальных метео-станций помогло найти решение. Корпус для датчика должен быть больше, и к тому же иметь активную вентиляцию для таких случаев.
Делаем корпус для датчика
Подходящей по размеру оказалась банка от водоэмульсионной краски (такие же бывают из-под клея ПВА, объёмом 2-3 литра). В нижней части банки делаем отверстие под вентилятор. И прикрепляем его к банке. В центре банки размещаем площадку под датчики, диаметром немного меньшим самой банки, чтобы воздух мог циркулировать.
Несколько фото:
Банка от водоэмульсионки
Детали и банка с проделанным отвестием
Установка датчика в корпус
Датчик в корпусе, вид сверху
Как вы помните, резистор я заменил светодиодом, поэтому делаем и для него отверстие, чтобы всегда было видно работу устройства.
Корпус, вид сбоку
Крышка от банки нам не нужна, вместо неё нужен навес, такой, чтобы и воздух пропускал, и чтобы атмосферные осадки не попадали внутрь (датчик-то будет расположен на улице).
Корпус для Arduino я сделал из пластмассовой коробки от mp3-плеера Explay C360.
Arduino
Backend, принимающий данные:
На стороне сервера работает скрипт, к которому обращается термометр. Скрипт проверяет правильность секретного кода, чтобы показания нельзя было подменить.
А затем добавляет новую запись в таблицу MySql. Потом эти данные можно выводить как угодно. При этом каждую минуту данные за прошедшую минуту усредняются и добавляются в другую таблицу.
Нужно это для того, чтобы:
1. проще было делать выборки в базе (не правда ли, удобнее указать конкретную минуту и получить результат)
2. выборки были быстрее (за год ~500 000 записей вместо 10 000 000)
Во время длительной работы датчика обнаружилась проблема, иногда он самопроизвольно (раз в 3-4 часа) выдаёт рандомное значение. Поэтому я добавил проверку на изменение температуры больше чем на 1 градус в течении 15 секунд. Такие значения игнорируются.
Что из этого всего вышло:
На своём сайте я разместил страничку с термометром и добавил график температуры за последние сутки.
Посмотреть можно тут: Ethernet термометр.
Недостатки:
Точность датчика 0.5* С, что для меня недостаточно. Но есть способ улучшить его характеристики. Понадобится ещё один, или более датчиков (желательно из разных партий). Получаем данные со всех датчиков и считаем среднее арифметическое. Так можно добиться точности до сотых градуса.
Планы на будущее:
- датчик влажности
- датчик давления
- датчик скорости ветра
- датчик освещённости
- поставить несколько таких в городе и делать свои прогнозы погоды
- питать Arduino по Power over Ethernet
- автоматизировать включение и частоту вращения вентилятора в зависимости от освещения
- удалённое управление
- сброс данных на случай отсутствия связи (для меня это критично)
Известные мне недостатки:
— высокая цена — 2180 руб. (Freeduino 2009 (800 р.) + Ethernet Shield v2 (1300 р.) + 1 датчик (80 р.))
— если вентилятор включить слишком быстро, то он сам вносит погрешность в температуру, обдувая датчик. Он не должен сдувать, а лишь проталкивать воздух.
Ссылки по теме:
Блог Arduino на Хабре
Онлайн-термометр
DS18B20 Datasheet
PS: статья целиком и полностью принаддежит liderman — все вопросы к нему