WeatherStation. Часть 2: Ethernet модуль


Часть 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 модуль”»

  1. У меня вопрос, как я понял, вы на Ардуино вешаете одновременно Ethernet-шилд и модуль nRF24L01+. Но судя по инструкции Ethernet-шилд использует пины 10, 11, 12, 13 для обмена данными между шилдом и Ардуино и пользователей настоятельно просят не использовать их для других целей. Но для работы с модулем nRF24L01+ по средствам библиотеки RF24 или Mirf так или иначе эти пины используются тоже. Не вызывает ли такое подключения конфликта или вы используете другое подключение и библиотеки.

    • Я не использовал готовые библиотеки, а портировал свою, написанную для AVR-GCC (Arduino-вариант не сохранился, оригинал брать тут. Для работы нужна либа по ссылке ниже, одна из версий)
      А чтобы решить конфликт с Ethernet-шилдом, запустил софтварный SPI, опять-таки портированный с моей либы для AVR-GCC (так же, оригинал лежит тут, брать версию Software)

Добавить комментарий

Arduino

Что такое Arduino?
Зачем мне Arduino?
Начало работы с Arduino
Для начинающих ардуинщиков
Радиодетали (точка входа для начинающих ардуинщиков)
Первые шаги с Arduino

Разделы

  1. Преимуществ нет, за исключением читабельности: тип bool обычно имеет размер 1 байт, как и uint8_t. Думаю, компилятор в обоих случаях…

  2. Добрый день! Я недавно начал изучать программирование под STM32 и ваши уроки просто бесценны! Хотел узнать зачем использовать переменную типа…

3D-печать AI Android Arduino Bluetooth CraftDuino DIY IDE iRobot Kinect LEGO OpenCV Open Source Python Raspberry Pi RoboCraft ROS swarm ИК автоматизация андроид балансировать бионика версия видео военный датчик дрон интерфейс камера кибервесна манипулятор машинное обучение наше нейронная сеть подводный пылесос работа распознавание робот робототехника светодиод сервомашинка собака управление ходить шаг за шагом шаговый двигатель шилд юмор

OpenCV
Робототехника
Будущее за бионическими роботами?
Нейронная сеть - введение