Часть 0: Описание и концепция
Часть 1: Основной модуль
Ethernet модуль — далеко не последняя по важности деталь в проекте WeatherStation (кто не в курсе — погодная станция, отображающая дату, время, погоду\влажность за окном и прогноз погоды на утро, день, вечер и ночь). В этой статье я опишу, как и с каких ресурсов я беру время и погоду, в каком формате читаю, как обрабатываю и как передаю основному модулю.
Ethernet модуль
Модуль состоит из Arduino Uno с Ethernet-шилдом. Была мысль сделать его на МК AVR-семейства (типа ATmega8) и сетевого адаптера на ENC, и даже было заказано все необходимое, но из-за скорости работы доставки (спасибо Почте России за пока так и не доставленную за 2 месяца посылку) пришлось делать из того, что есть. Как только все придет — обязательно сделаю более дешевый аналогичный модуль.
Назначение этого «бутерброда» из Ардуины и шилда — лежать около роутера и обрабатывать запросы, поступающие с основного модуля.
Коммуникация с основным модулем
Двусторонняя связь с ним, как я уже говорил в предыдущих статьях, обеспечивается трансивером nRF24L01+. Причем размер сообщения (т.е. размер payload) я выбрал 16 байт — не много, но и не мало, половина от максимального. Каждое сообщение будет стандартизировано — каждый байт будет строго иметь свое предназначение. Сделано это для возможности легкого добавления модулей — мало ли, приспичит сделать что-нибудь еще… Пока я пришел к такому шаблону:
Байт 0 — «адрес» модуля-отправителя (0=основной, 1=Ethernet, 2=DHT и т.д.).
Байт 1 — тип сообщения — запрос (и его код), ответ или просто полезная ниформация.
Байт 2 — флаг ошибки. Если этот байт равен 1, то полезная информация = описание ошибки.
Байты 3-15 — полезная информация.
Какие запросы может послать основной модуль? Пока в планах только три: запрос точного времени, запрос прогноза погоды и информация о состоянии сети. Также он может информировать Ethernet модуль о данных, приходящих с температурного датчика — в будущем планируется сделать простенькую Web-админку с актуальными значениями и настройками всей системы.
Работа с Интернет (алгоритм)
Для того, чтобы узнать время и погоду, нужно сделать запрос в Сеть. Я решил считывать данные, выводимые PHP-скриптами (тем самым разгрузив медленную Ардуину от лишних вычислений — PHP выполняется на сервере), благо бесплатный хостинг и домен (даже второго уровня) сейчас — не проблема, да и у меня как раз работает один (заметили, какой адрес у больших картинок и схем в предыдущей статье?). Я нашел сервисы, с которых можно будет легко брать время и погоду, написал два PHP-скрипта (time.php и weather.php соотвественно) и теперь обращаюсь к ним Ардуинкой.
А алгоритм работы такой: Arduino принимает сигнал от nRF24L01+ (в этом случае, в отличии от основного модуля, удобнее ждать прерывание от трансивера, нежели опрашивать его каждые N секунд) генерирует нужный HTTP-запрос, посылает его на Web-сервер, ждет ответа, считывает его по 1 байту, отделяет полезную информацию («тело») от HTTP-заголовков и посылает обратно основному модулю. Вот так все несложно.
Время и дата
Время и дату я решил брать прямо с сервера — благо оно оказалось довольно точным. К тому же, в языке PHP есть отличная функция date(), которая выдает нужную информацию в форматированном виде.
Итак, был написан простейший скрипт:
<?php if ($_GET['utc']!=""){ $offset=$_GET['utc']; } else { $offset=0; } $offset+=4; // time to UTC (time on server - UTC-4) echo date("|Hisdmyw", strtotime("$offset hour")); die(); ?>
Посмотреть на его вывод можно здесь. Особенность этого маленького скрипта в том, что ему можно указывать необходимый часовой пояс. Вот так: time.php?utc=-2 или time.php?utc=+4 (МСК), а если не указать ничего, он выведет время UTC — по Гринвичу.
Разберу код по частям:
• Теги уведомляют сервер, что все что между ними есть PHP
• Строка 2 — проверка наличия параметра utc, если нет — то 0
• Строка 3 — компенсация часового пояса сервера (на этом сервере оказалось время UTC-4)
• Строка 4 — вывод даты и времени с учетом часового пояса
• Строка 5 заставляет сервер сразу закончить обработку скрипта. Как выяснилось, без нее Ардуина впустую ждет несколько секунд, что неприятно.
Формат вывода: |ччммссДДММГГд . чч — часы, мм — минуты, сс — секунды, и далее день, месяц, год. Последняя цифра — день недели, 1-7. Палочка в начале (|) нужна для легкого отсечения Ардуиной важной информации от HTTP-заголовков.
Погода
А вот с погодой все оказалось несколько сложнее… Функции weather() в PHP нет :). Пришлось брать ее (погоду) со стороннего ресурса. Парсить голый HTML не хотелось, а следовательно, Яндекс.Погода и Гисметео отпадали. И вот каким-то чудом был найден ресурс www.weather.ua (не реклама 🙂 ), который позволяет бесплатно брать информацию о погоде в формате XML! Причем она полностью кастомизабельна — количество дней, город и многое другое, да и прогноз дает как раз на утро, день, вечер и ночь! Ура, то что нужно, подумал я! В срочном порядке был изучен синтаксис обращения к этому сайту, его вывод и XML-парсер simplexml. И тут встал еще один вопрос — показывать прогноз на один день или со сдвигом? Что показывать в прогнозе на утро, если уже вечер? Было решено, что показываться всегда будет актуальный прогноз, то есть вечером показывать прогноз на утро следующего дня.
Итак, решено: запрашиваем у сайта прогноз погоды на 2 дня, сортируем (информация с сайта приходит не всегда «утро, день, вечер, ночь» — может прийти «вечер, ночь, утро, день»), считаем среднее значение температуры (в XML выводится максимальная и минимальная температура), конвертируем состояние погоды (cond) из их 100-бальной шкалы в мою, с 4 состояниями (ясно, облачно, дождь, снег).
Вот так выглядит у меня запрос к сайту:
http://xml.weather.co.ua/1.2/forecast/$city?dayf=2&userid=$user&lang=ru
Где:
• $city — ID города. Узнать его можно так: заходим на weather.ua, переходим на страницу города (найдя его в поиске) и смотрим в адресную строку. Все что после знака вопроса — и есть ID города. Для Москвы это 27, для меня (Чебоксары) — 1501.
• $user — какой-то идентификатор пользователя, видимо им нужен для статистики. Я указываю домен, с которого запрашиваю данные — naboko.tk
Пример ссылки (при переходе можно посмотреть вывод): http://xml.weather.co.ua/1.2/forecast/1501?dayf=2&userid=naboko.tk&lang=ru
Я написал вот такой скрипт (он уже менее красив, чем предыдущий…):
<?php function WC2DEC($tmp){ if ($tmp<10) return 1; else if ($tmp<40) return 2; else if ($tmp<80) return 3; else return 4; } if ($_GET['city']!=""){ $city=$_GET['city']; } else { $city=1501; } // Ссылка запроса - город, прогноз на 2 дня $url="http://xml.weather.co.ua/1.2/forecast/$city?dayf=2&userid=naboko.tk&lang=ru"; /* Загружаем XML и преобразуем в объект */ $xmlstr = @file_get_contents($url); if ( $xmlstr===false ) die('|ERR0'); // Error connect to XML $xml = simplexml_load_string($xmlstr); if ( $xml===false ) die('|ERR1'); // Error parse XML $day=$xml->forecast->day; // Чтобы путь короче был /* Сортировка в порядке ночь - утро - день - вечер */ switch ($day[0]['hour']){ case 3: $arr = array(4,1,2,3); break; case 9: $arr = array(3,4,1,2); break; case 15: $arr = array(2,3,4,1); break; case 21: $arr = array(1,2,3,4); break; } echo "|"; /* Вывод погоды в формате sAAsBBsCCsDD, s - знак, AA..DD - погода (с лид. нулем) */ for ($i=0; $i<4; $i++){ $t_min=$day[$arr[$i]]->t->min; $t_max=$day[$arr[$i]]->t->max; $t=($t_min+$t_max)/2; if($t>=0) { echo "+"; } else { echo "-"; } if (abs($t)<10) { echo "0"; } echo abs($t_min); } /* Вывод погоды в формате abcd, 1..4 (ясно, облачно, дождь, снег) */ for ($i=0; $i<4; $i++){ echo WC2DEC($day[$arr[$i]]->cloud); } die(); // Чтобы клиент сразу отцепился ?>
В работе скрипт можно посмотреть здесь. Как и прошлый скрипт, вывод можно подстроить под себя, не меняя исходник. ID города можно передать через параметр city, вот так: weather.php?city=27 (Москва). Без параметра скрипт выводит погоду для Чебоксар.
На этот раз поясню смысл лишь кратко:
• Строки 2-7 — функция, которая, получая на вход данные о состоянии погоды по 100-бальной шкале, выводит цифру от 1 до 4 — ясно, облачно, дождь, снег
• Строки 15-19 — забираю XML и превращаю его в объект, с которым удобно работать, при этом проверяя процесс на наличие ошибок
• Строки 22-31 создают массив с индексами дней в нужной последовательности
• Строки 34-42 считают среднее арифметическое температуры и выводят знак и значение (причем в обязательном порядке, и если число будет меньше 10, скрипт выведет лидирующий ноль)
• Строки 44-46 — вывод состояния погоды.
Скачать PHP скрипты (один архив) — *click* (мало ли, вдруг парсер на сайте съест пару тегов…)
Состояние проекта
Времени на все, к сожалению, не хватает. Надеюсь, сегодня я сделал все хорошо, остались еще 30 мая и 3, 6 июня… Так что разработка проекта немного подтормаживает: корпус основного модуля так и не готов, температурный модуль собран пока только на макетке… Вот начнется лето, и сразу все сделаю 🙂
0 комментариев на «“WeatherStation. Часть 2: Ethernet модуль”»
У меня вопрос, как я понял, вы на Ардуино вешаете одновременно Ethernet-шилд и модуль nRF24L01+. Но судя по инструкции Ethernet-шилд использует пины 10, 11, 12, 13 для обмена данными между шилдом и Ардуино и пользователей настоятельно просят не использовать их для других целей. Но для работы с модулем nRF24L01+ по средствам библиотеки RF24 или Mirf так или иначе эти пины используются тоже. Не вызывает ли такое подключения конфликта или вы используете другое подключение и библиотеки.
Я не использовал готовые библиотеки, а портировал свою, написанную для AVR-GCC (Arduino-вариант не сохранился, оригиналбрать тут . Для работы нужна либа по ссылке ниже, одна из версий)лежит тут , брать версию Software)
А чтобы решить конфликт с Ethernet-шилдом, запустил софтварный SPI, опять-таки портированный с моей либы для AVR-GCC (так же, оригинал