Pull to refresh

Ethernet термометр на основе Arduino

Developing for Arduino *
Arduino

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

Что нам понадобится:

  • Arduino Duemilanove (Freeduino 2009)
  • Ethernet Shield v2
  • цифровой датчик температуры — DS18B20
  • вентилятор для корпуса (120 мм)
  • банка от водоэмульсионки или клея ПВА (2 литра)
  • светодиод
  • витая пара


Задачи

Опрашивать датчик температуры по шине 1-Wire и каждые 3 секунды самостоятельно отправлять результаты на Web-сервер, на котором они будут храниться.

Алгоритм работы устройства:

  1. присваиваем нашему Ethernet Shield`у MAC адрес и ip-адрес
  2. инициализируем соединение с сервером на 80 порт
  3. получаем данные с цифрового датчика температуры, по 1-Wire шине
  4. формируем GET запрос
  5. отправляем GET запрос
  6. разрываем соединение


Исходный код скетча:

Комментарии по ходу кода должны внести ясность.

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(); // Завершаем соединение
 }
}



Сборка устройства:

  1. первую «ногу» датчика цепляем на «минус» GND
  2. вторую «ногу» (DQ) на 7-й пин
  3. третью на «плюс»
  4. вторую и третью нужно соединить резистором на ~ 4,7 К. Но я заменил резистор на светодиод и получил индикатор обращения к шине датчика (ВНИМАНИЕ! Без резистора или светодиода работать ничего не будет. Не забудьте!)


По идее, вот и всё. Должно работать.
Работает, но боевые условия показали, что когда падает солнечный свет на датчик, тот может нагреваться и показывать температуру гораздо выше реальной. Всё правильно — он покажет температуру на солнце. А нам нужна температура воздуха.

В первый раз для этого был собран корпус из-под банки от кофе, обёрнутый в фольгу. Но это ничем не помогло.

Изучение фотографий реальных метео-станций помогло найти решение. Корпус для датчика должен быть больше, и к тому же иметь активную вентиляцию для таких случаев.

Делаем корпус для датчика

Подходящей по размеру оказалась банка от водоэмульсионной краски (такие же бывают из-под клея ПВА, объёмом 2-3 литра). В нижней части банки делаем отверстие под вентилятор. И прикрепляем его к банке. В центре банки размещаем площадку под датчики, диаметром немного меньшим самой банки, чтобы воздух мог циркулировать.
Несколько фото:

Банка от водоэмульсионки

Банка от водоэмульсионки
Детали и банка с проделанным отвестием
Детали и банка с проделанным отвестием

Установка датчика в корпус
Установка датчика в корпус

Датчик в корпусе, вид сверху
Датчик в корпусе, вид сверху

Как вы помните, резистор я заменил светодиодом, поэтому делаем и для него отверстие, чтобы всегда было видно работу устройства.

Корпус, вид сбоку
Корпус, вид сбоку

Крышка от банки нам не нужна, вместо неё нужен навес, такой, чтобы и воздух пропускал, и чтобы атмосферные осадки не попадали внутрь (датчик-то будет расположен на улице).

Корпус для Arduino я сделал из пластмассовой коробки от mp3-плеера Explay C360.

Arduino
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 — все вопросы к нему
Tags:
Hubs:
Total votes 76: ↑71 and ↓5 +66
Views 74K
Comments Comments 96

Please pay attention