Ялинка, зажгись! Частина 3: веб-інтерфейс і додаток для Android

Цим текстом майстер Гамбс завершує опис своєї нової ялинкової гірлянди. 2015 р. Москва

Привіт, Хабр!

Отже, ми дісталися до фінального етапу: раз у нас є гірлянда, якою управляє нанокомпьютер Black Swift з вбудованим Wi-Fi, то логічно зробити для неї веб-інтерфейс і смартфонное додаток, щоб помигати світлодіодом, якщо ви розумієте, про що я.

  1. Гірлянда, підключення Black Swift і середовище складання під OpenWRT на C/C++
  2. Софт на C, робота з GPIO і програмна ШІМ
  3. Веб-інтерфейс і додаток для Android
Але спочатку — на прохання читачів публікуємо відео працює ялинкової гірлянди. Не думаю, що хтось не бачив ялинкових гірлянд, думаю, що просто не всі вірять, що я правда 28-29 грудня пішов за світлодіодами, щоб прикрасити ялинку…



За кадром сиджу я і однією рукою тримаю фотоапарат, а інший перемикаю режими її роботи, тикаючи мишкою в браузер.

Тепер же, коли останні сліди недовіри випарувалися, продовжимо. У попередніх серіях ми отримали працює контролер гірлянди, вміє приймати команди через UNIX-сокет — в них задається режим роботи, а також швидкість і яскравість гірлянди. Найпростіше прошарок між вебом і сокетом зробити на банальному PHP — це буквально кілька рядків.

Веб-сервер і PHP5 на нанокомпьютере

У нас в Black Swift вже стоїть стандартний веб-сервер uhttpd, обслуговуючий штатний веб-інтерфейс LuCI. Щоб працювати з PHP, ми поставимо другої веб-сервер — lighttpd (я от думаю, у фінальну прошивку його і php5 треба просто за замовчуванням включити), а також зручний текстовий редактор nano:

opkg update
opkg install nano
opkg install lighttpd lighttpd-mod-cgi
opkg install php5 php5-cgi
/etc/init.d/lighttpd enable


Веб-сервер і PHP підтягнуть свої залежно самі. Середні три команди, я думаю, очевидні, перша ж підтягне з репозиторію оновлений список пакетів, а остання включить для lighttpd автозапуск при старті системи.

Тепер трохи підправимо конфіги:
nano /etc/config/uhttpd


У перших рядках шукаємо директиви «list listen_http», яких там дві штуки, і міняємо в них порт :80 на :8080 (якщо ще який-небудь). Потім перезапускаємо uhttpd командою /etc/init.d/uhttpd restart.

Аналогічним чином правимо /etc/lighttpd/lighttpd.conf (він довгий, для пошуку потрібного тексту nano використовується комбінація Ctrl-W):

server.modules = (
"mod_cgi"
)


В конфіги є довгий закомментированный список плагінів, нам потрібен тільки mod_cgi, який буде працювати з php_cgi.

server.document-root = "/www/tree"

index-file.names = ( "index.html", "default.html", "index.htm", "default.htm", "index.php" )

cgi.assign = ( ".php" => "/usr/bin/php-cgi" )


Тут все досить очевидно для всіх, хто хоч раз бачив веб-сервер на лінуксі: коренева папка, кореневі файли (додаємо до списку index.php) і прив'язка до файлів *.php конкретного обробника.

Тепер відкриваємо /etc/php.ini і правимо один рядок:

doc_root = "/www/tree"


І фінальний штрих — /etc/init.d/lighttpd start

Тепер ми маємо на порту 80 веб-сервер з працюючим PHP, так що залишається тільки створити каталог /www/tree і покласти в нього файл index.php. Який, звичайно, спочатку треба написати.

index.php

Завдання також гранично банальна, скажімо прямо.

Писати в файловий сокет із PHP не просто, а дуже просто:

$sockf = fsockopen("unix:///tmp/treelights.sock", 0, $errno, $errstr);
if ($sockf)
{
$command = $cmd . " " . $val;
fwrite($sockf, $command);
fclose($sockf);
}


Де $cmd — команда, яку ми хочемо передати, наприклад, brightness, а $val — відповідне значення, наприклад, 2.

Далі все очевидно: користувач рухає повзунок (в HTML5 з'явилися повзунки, ура-ура), javascript висмикує його положення і передає в PHP-файл:

<p style="text-align: center;"><label for="brightness">Brightness</label>
<input type="range" min="1" max="10" value="<?php echo (11 - $values[1]); ?>" id="brightness" step=1 onchange="setBrightness(value)" oninput="displayBrightness(value)">
<output for="brightness" id="bLevel"><?php echo (11 - $values[1]); ?></output>

і

function displayBrightness(brightness) {
document.querySelector('#bLevel').value = brightness;
}

function setBrightness(brightness) {
url = 'index.php?cmd=brightness&val=';
location.href = url.concat(brightness);
}


Перша функція при переміщенні повзунка відразу ж змінює чисельне значення поряд з ним, друга передає команду і це значення в PHP-файл відразу, як тільки користувач повзунок відпустить. NB: перші реалізації HTML5 страждали тим, що onchange і oninput в range працювали однаково, вискакуючи при кожному зсуві повзунка, але зараз нам це вже не дуже важливо.

Ви вже, напевно, звернули увагу на два моменти: JS викликає той же index.php, в якому він написаний, тільки з параметрами, а у HTML є вставки на PHP, подставляющие при генерації коду якесь певне раніше положення повзунка.

Перше зроблено тому, що для простоти демонстрації я не використовував AJAX, а друге — щоб при відкритті сторінки вона показала поточний стан гірлянди, якщо таке раніше встановлювалося.

Обробка переданих з файлом параметрів проста:

$cmd=($_GET['cmd']);
$val=($_GET['val']);
if (!empty($cmd))
{
$sockf = fsockopen("unix:///tmp/treelights.sock", 0, $errno, $errstr);
/* далі ви вже знаєте */
}


Тут все настільки ж банально: якщо параметри передали, то ми спочатку віддамо їх в сокет, а потім покажемо веб-інтерфейс, якщо не передали — просто покажемо веб-інтерфейс.

Налаштувань гірлянди у нас негусто, тому зберігати їх логічно у звичайному файлі. Однак тут варто згадати, що програму на C ми писали без урахування збереження параметрів, тому і PHP після перезапуску системи повинен показувати параметри за замовчуванням, а не збережені раніше. Зробити це в OpenWRT дуже просто — зберігайте все непотрібне в /tmp, він живе в ОЗП і при перезавантаженні зникає назавжди.

if (file_exists("/tmp/tree.set"))
{
$settings = file_get_contents("/tmp/tree.set");
// Mode, Brightness, Speed
$values = explode(",", $settings);
}
else
{
$values[0] = "0";
$values[1] = "1";
$values[2] = "1";
}


Ну і після встановлення нових параметрів гірлянди, звичайно, їх треба записати:

$settings = $values[0] . "," . $values[1] . "," . $values[2];
file_put_contents("/tmp/tree.set", $settings);


Загалом, на цьому з будівництвом веб-інтерфейсу фактично все — додаємо аналогічним чином інші кнопки і полузнки і отримуємо результат: https://github.com/olegart/treelights/краплі/master/php/index.php

Тепер на нашу ялинку можна зайти з браузера.

Додаток для Android і Network Service Discovery

Disclaimer: взагалі я сам під Android вмію писати приблизно ніяк, так що не судіть строго. З іншого боку, той факт, що у мене вийшло і воно працює, багато говорить про простоту реалізації подібних застосувань Black Swift, коли прототипи всіх основних частин системи можна зробити в прямому сенсі слова на коліні.

Отже, у нас є веб-інтерфейс по якомусь IP-адресою. Банальним способом було б показати на смартфоні його вміст в компоненті WebView, але ми підемо трохи далі і зробимо автовизначення цієї адреси (ну право слово, не будете ж ви дружині диктувати «Дорога, вимкни гірлянду, вона на 192.168.1.158, якщо DHCP їй щось нове не дав») за допомогою сервісу Network Service Discovery. NSD нормально працює в Android починаючи з чогось типу 4.1 або 4.2, але навряд чи нас це зараз зупинить.

У світлі дисклеймера не буду розповідати, як писати під Android, а відразу дам посилання: додаток, яке я робив для свого інтерфейсу «розумного будинку». Його треба скачати, покласти кудись акуратно, потім поставити Чоловічий Studio, відкрити в ньому проект і трохи поправити.



Відкриваємо Gradle Scripts → build.gradle (Module: app) і міняємо в applicationId «lightcontrol» на що-небудь більш адекватне новорічній ялинці. Хоча взагалі можете і «lightcontrol» залишити, у вас напевно плутанини з софтом «розумного будинку» з такою ж назвою не буде.



Така ж косметика: Manifests → AndroidManifest.xml міняємо android:label на що-небудь про ялинку (NB: com.example.lightcontrol.app тут і у всіх інших місцях, крім build.gradle, ми не чіпаємо!). Аналогічно йдемо в res → values → strings.xml і змінюємо значення app_name на що-небудь про Різдво.



Нарешті, відкриваємо основний код програми і на його початку змінюємо значення змінної TAG на що-небудь своє. Це слово треба запам'ятати, він нам знадобиться на наступному кроці — справа в тому, що з цього імені додаток буде шукати потрібний сервіс в локальній мережі. Нехай буде «Treelights», наприклад.

Все поміняли? Можна ще пройтися по виводимо з додатками повідомленнями (я не морочився з локалізацією, все забито прямо в код) і поміняти для краси фрази в дусі «управління світлом в будинку» на «управління гірлянди на ялинці».

Тепер фінал: Build → Generate signed АПК, створюємо свій ключ для підпису додатка і власне компілюємо все. З точки зору чуйності користувальницького інтерфейсу Android Studio абсолютно кошмарен, але складання проекту займає секунд десять, не більше, після чого вам або вивалюється помилка в балці або пропозицію відкрити в explorer.exe папку з готовим АПК. Відкриваємо, копіюємо app-release.apk на смартфон і встановлюємо (в налаштування Android треба включити установку з усіх джерел).

Тепер повертаємося до ялинки і налаштовуємо там сервіс avahi, який і буде розсилати повідомлення, одержувані компонентом NSD:

opkg update 
opkg install avahi-daemon
nano /etc/avahi/services/http.service


Міняємо рівно один пункт: в тезі name проставляємо ім'я, яке містить слово, раніше вписане нами в змінну TAG в мобільному додатку (це було слово «Treelights»):



Зберігаємо, відкриваємо /etc/avahi/avahi-daemon.conf і вписуємо в першу секцію рядок enable-dbus=no (у нас немає DBUS, тому без неї avahi при старті буде лаятися матом).

Фінальний крок:

/etc/init.d/avahi-daemon enable
/etc/init.d/avahi-daemon start


Знову беремо в руки смартфон, запускаємо наш додаток і радіємо, бачачи, як через частку секунди пошуків воно відкриває веб-інтерфейс ялинкової гірлянди.

Залишився до Нового року час можна витратити на малювання красивого веб-інтерфейсу з крупними кнопками.

Замість висновку

Я відразу передбачаю два питання з серії «навіщо ти це зробив»: 1) навіщо взагалі потрібна гірлянда з Wi-Fi і 2) навіщо її робити на Black Swift, а не на тому ж Raspberry, так як габарити тут ролі не грають.

Насправді, звичайно, гірлянді не дуже потрібен Wi-Fi, а заміна BSB на RPi нічого в цьому не змінить. Але знаєте таку роботу Акерлофа «Market for Lemons», він в ній показував, як ринок при вільній конкуренції може самостійно скотитися в продаж дешевої погані, ось прямо як ті новорічні гірлянди, що лежать в магазинах? Акерлоф отримав за неї Нобелівку з економіки, а сама робота стала найбільш відома за ілюстрації процесу на прикладі автомобільного ринку — хоча в передмові, і сказано, що приклад не є важливим, ні реалістичним, а обраний просто з-за простоти і наочності пояснення.

Так от, в моєму випадку ситуація така ж, хіба що з Нобелівського комітету мені поки не дзвонили (Акерлоф, втім, теж 31 рік чекав дзвінка). Я хотів показати, наскільки просто використовувати Black Swift і з точки зору підключення, і з точки зору програмування в досить-таки комплексному проекті, що має спеціалізовану апаратну частину, веб-інтерфейс, і мобільний додаток. Фактично, це — нормальний, придатний приклад автоматизації пристрою в рамках популярної нині концепції «Інтернету речей».

При цьому, хоча я написав три статті і багато букв в них, якщо подивитися на обсяг підсумкової роботи — фактично це «проект вихідного дня», один вечір в якому піде на пайку гірлянди, а другий — на написання всього.

Наступного ж разу я покажу приклад розробки, в якій Black Swift критичний і труднозаменим — тому що габарити того ж Raspberry Pi будуть порівнянні з зовнішніми розмірами корпусу всього фінального пристрою.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.