Интерес к интернету вещей растет с каждым днем, свои курсы по технологии IoT запустили и Cisco, и Samsung. Но большинство этих курсов базируются на собственном железе компаний, довольно дорогом, в то время как практически все то же самое можно сделать на гораздо более дешевом железе самостоятельно, получив при этом массу удовольствия и полезных навыков.
Какую плату выбрать?
Когда неофит от IoT полезет в интернет, одним из первых модулей, которые он найдет, будет ESP8266. И действительно, он обладает массой достоинств: дешевый, много различных плат на его основе, позволяющих использовать его как самостоятельное устройство и подключать к сложным Arduino-based проектам. Но ESP8266, выпущенный в 2014 году, довольно быстро перестал удовлетворять запросы пользователей, и в 2015 году компания-разработчик Espressif выпускает новый микроконтроллер — ESP32.
Точно так же, как и в случае с ESP8266, разработчики создали довольно много плат, базирующихся на новом микроконтроллере. В данной статье все примеры тестировались и проверялись на плате MH-ET LIVE ESP32 DevKit. Плата для обзора была любезно предоставлена интернет-магазином Amperkot.
Начинаем программирование
Как и у любой платы, основанной на ESP32, у MH-ET LIVE ESP32 DevKit есть достаточно большой набор языков программирования. Во-первых, это Arduino C, во-вторых, Lua, а в-третьих и в-четвертых — MicroPython и Espruino. Про Espruino — сборку JS для программирования микроконтроллеров — уже рассказывалось, но в той статье разбиралась работа только на плате Espruino Pico, заточенной под Espruino.
К сожалению, портирование Espruino на ESP32 еще не до конца завершено. Часть возможностей, например обновление по воздуху и Bluetooth, недоступна. Но так как Espruino — open source проект, любой может добавить свою функциональность.
Установка
- Скачиваем на официальном сайте свежую сборку Espruino. А если не доверяешь готовым сборкам, то можно собрать прошивку самостоятельно:
12345678[crayon-67861a092d6ff210068558 inline="true" class="python"]# Get the Espruino source codegit clone https://github.com/espruino/Espruino.gitcd Espruino# Download and set up the toolchain ('source' is important here)source scripts/provision.sh ESP32# Clean and rebuildmake clean && BOARD=ESP32 make - Несмотря на то что мы будем программировать на JS, для установки все равно нужен Python, а конкретно esptool.py. Повторяя свою предыдущую статью, скажу, что для его установки, при условии, что Python уже установлен, достаточно набрать в консоли/терминале: pip install esptool.
- В терминале перейти в папку с прошивкой. Кроме самого файла Espruino, здесь лежат файлы bootloader.bin и partitions_espruino.bin. Это необходимыекомпоненты, но в некоторых сборках их может не быть, тогда их придется скачать отсюда.
- Запускаем процесс прошивки, не забыв изменить порт, указанный в данном примере, на свой, а также при необходимости указать другое имя прошивки. Здесь она называется espruino_esp32.bin.
12345678910111213[crayon-67861a092d702827762986 inline="true" class="python"]esptool.py \--chip esp32 \--port /dev/ttyUSB0 \--baud 921600 \--after hard_reset write_flash \-z \--flash_mode dio \--flash_freq 40m \--flash_size detect \0x1000 bootloader.bin \0x8000 partitions_espruino.bin \0x10000 espruino_esp32.bin
IDE
Разработчики Espruino создали свою IDE, Espruino Web IDE. Эта программа распространяется через Chrome Web Store, также существуют нативные приложения для Windows (32 и 64).
Перед первым запуском нужно залезть в настройки, вкладка COMMUNICATIONS, и убедиться, что скорость общения выставлена на 115200, а также изменить поле Save on Send с No на Yes, иначе все программы после перезапуска слетят.
Теперь достаточно запустить IDE, подключиться к плате и набрать в консоли 1+2: если ты получил 3, значит, все настроено правильно и можно начинать полноценную работу.
Hello world
Во всех языках программирования, предназначенных или модифицированных для программирования микроконтроллеров, самая простая программа — так называемый Blink, мигание встроенным светодиодом. Но это как-то скучно. Поэтому нашей первой программой станет программа для управления светодиодом с помощью веб-страницы. И действительно, JS — это же язык веба.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
var wifi = require("Wifi"); wifi.startAP('EspruinoAP', { password: '0123456789', authMode: 'wpa2' },function() { console.log(`AP started`); }); function onPageRequest(req, res) { var a = url.parse(req.url, true); if (a.pathname=="/") { res.writeHead(200, {'Content-Type': 'text/html'}); res.end("<H1><center>Hello, ][aker!</center></H1>"); } else if (a.pathname=="/on") { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end("Enable"); digitalWrite(D2, false); } else if (a.pathname=="/off") { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end("Disable"); D2.write(true); } else { res.writeHead(404, {'Content-Type': 'text/plain'}); res.end("404: Page "+a.pathname+" not found"); } } require("http").createServer(onPageRequest).listen(80); |
Можно заметить, что синтаксис практически ничем не отличается от обычного JS. Давай разбираться, что же происходит в этой программе.
- var wifi = require(«Wifi») — для начала мы подгрузили необходимый нам модуль для работы с Wi-Fi. Логично будет задаться вопросом: а откуда мы его взяли? Допустим, есть встроенные в прошивку модули. А если нам нужно загрузить с какого-нибудь внешнего сайта? Функция require поддерживает синтаксис вида require(«https://github.com/espruino/EspruinoDocs/blob/master/devices/PCD8544.js»);, а WebIDE для поиска модулей онлайн, по умолчанию используется https://www.espruino.com/modules.
- Следующий блок кода отвечает за поднятие точки доступа с именем EspruinoAP и паролем 0123456789. В случае успешного запуска в консоль выводится соответствующее сообщение.
- Функция onPageRequest — собственно сам веб-сервер. В этой функции разбирается адрес и проверяется, что нужно сделать, в зависимости от запроса:
- если загружается первая страница — /, то вернуть 200-й заголовок и сообщение типа text/html «Hello, ][aker!», в обрамлении HTML-тегов;
- если загружается страница включения — /on, то вернуть200-й заголовок и сообщение Enable, а также включить светодиод. Заметим, что используется привычная Arduin’щикам функция digitalWrite(pin, value);
- небольшое отличие в случае страницы выключения —/off, для выключения светодиода используется не функция digitalWrite(pin, value), а метод write(value);
во всех остальных случаях возвращаем ошибку «404 — Page Not Found».
- А последняя строка собственно поднимает сервер, с внутренней функцией onPageRequest, на 80-м порте.
Важно заметить, что мы можем возвращать различный контент: обычный текст, HTML, XML и так далее.
RGB-лампочка, управляемая по MQTT
Очень часто создание своего умного дома начинается именно с подсветки. Но просто включать/выключать светодиодную лампу — это банально. Для освещения небольших помещений, а также для украшения и создания праздничной атмосферы нередко используются адресные RGB-светодиоды. Воспользуемся ими и мы. В качестве MQTT-брокера и клиента возьмем Adafruit IO.
Из стандартных виджетов нам понадобятся всего два: Toggle и Color Picker.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
var ssid = 'SSID'; var password = 'PASSW'; var count = 16; var LedPIN = D23; var wifi = require('Wifi'); function colorall(color,count,pin) { Color_Array = new Uint8ClampedArray(count*3); for (var i = 0;i < Color_Array.length; i+=3) { Color_Array[i ] = color[0]; Color_Array[i+1] = color[1]; Color_Array[i+2] =color[2]; } require("neopixel").write(pin, Color_Array); } function hexToR(h) {return parseInt((cutHex(h)).substring(0,2),16); } function hexToG(h) {return parseInt((cutHex(h)).substring(2,4),16); } function hexToB(h) {return parseInt((cutHex(h)).substring(4,6),16); } function cutHex(h) {return (h.charAt(0)=="#") ? h.substring(1,7) : h; } wifi.connect(ssid, {password: password}, function() { console.log('Connected to Wifi. IP address is:', wifi.getIP().ip); }); var mqtt = require("MQTT").connect({ host: "io.adafruit.com", username: "<LOGIN>", password: "<SECRET-KEY>" }); mqtt.on('connected', function() { mqtt.subscribe("<LOGIN>/feeds/exampledashboard.enable"); mqtt.subscribe("<LOGIN>/feeds/exampledashboard.colorlamp"); console.log("Connect"); }); mqtt.on('publish', function (pub) { console.log("topic: "+pub.topic); console.log("message: "+pub.message ); if (pub.topic == "<LOGIN>/feeds/exampledashboard.enable") { if (pub.message=="OFF") { colorall([0,0,0], count, LedPIN); } else { colorall([128,128,128], count, LedPIN); } } else if (pub.topic == "<LOGIN>/feeds/exampledashboard.colorlamp") { var hex_color = pub.message; var rgb_color = [hexToG(hex_color),hexToR(hex_color), hexToB(hex_color)] ; colorall(rgb_color, count, LedPIN); } }); mqtt.connect(); |
Приступим к разбору программы.
- Для начала объявляем переменные, необходимые для подключения к Wi-Fi, а также количество светодиодов и пин, к которому они подключены.
- Функция colorall(color,count,pin) отвечает за одновременное окрашивание всех светодиодов в один цвет. Для этого следует создать массив, в который count раззаписать по очереди три составляющих цвета. То есть для окрашивания двух светодиодов в синий цвет массив должен быть такой: [0,0,255,0,0,255]. Как можно заметить, в этой функции подключается предзагруженная библиотека neopixel. И еще один интересный факт: здесь перепутан порядок цветов — не RGB, а GRB. Это очень странно, но так есть.
- Следующие четыре функции нужны для перевода цвета из шестнадцатеричной записи в RGB. Они нам понадобятся, ведь Color Picker отправляет цвет как строку вида #RRGGBB.
- Подключаемся к Wi-Fi, в случае успешного подключения в лог выведется сообщение.
- Теперь необходимо подключиться к MQTT-брокеру. В первый раз, когда попробуешь запустить, интерпретатор может выдать ошибку, что такая библиотека не найдена. На самом устройстве ее действительно нет, она подгружается из интернета, поэтому, если подключение к интернету не удалось, она не сможет скачаться. Подожди, пока установится стабильное подключение, и перезагрузи устройство.
- Теперь надо описать функциональность клиента. У MQTT-клиента есть различные события, на которые можно (и нужно) повесить обработчики.
- Когда соединение установлено (connected), надо подписаться (mqtt.subscribe(topic)) на необходимые топики: один соответствует переключателю, другой — Color Picker’у. И для удобства выведем в консоль сообщение об успешном подключении.
- Теперь надо прописать, что необходимо делать при появлении в том или ином топике какого-либо сообщения (publish). Для начала выведем имя топика и текст сообщения.
- Переключатель может возвращать всего два сообщения: OFF или ON. Логично, что при получении первого сообщения необходимо выключить все светодиоды, а вот что делать при включении — остается на выбор программиста. Я решил зажечь все светодиоды белым цветом, с 50%-й яркостью.
- Если же пользователь изменяет цвет, то мы переводим цвет в RGB, точнее в GRB-запись и записываем этот цвет во все светодиоды.
- И разумеется, необходимо инициировать подключение к MQTT-брокеру.
MQTT-сервисы
Кроме Adafruit IO, есть и другие MQTT-сервисы, с похожей функциональностью. Если говорить про полностью «ручное» управление — первым на ум приходит Eclipse Mosquitto. Этот брокер можно установить на домашний компьютер или на Raspberry Pi и при помощи встроенных утилит mosquitto_sub и mosquitto_sub вручную (или используя Python) создавать топики, подписываться на них, отправлять сообщения.
В Arduino-кругах очень популярен сервис Blynk. У этого сервиса есть библиотеки для Arduino, клиенты под iOS и Android, а также его сервер распространяется через GitHub и может быть поднят на любом компьютере. Есть небольшой нюанс: каждый виджет, добавляемый на панель приложения, «стоит» определенное количество внутренних единиц. Изначального баланса хватит на большинство приложений, но если тебе захочется сделать полноценную программу и выложить ее в App Store / Play Market (а Blynk и такое позволяет), то придется раскошелиться.
Кроме этого, и в App Store, и в Play Market наберется достаточно много клиентских приложений, а в интернете есть масса MQTT-брокеров. Например, CloudMQTT. У него доступен бесплатный тариф — самое то для начала.
Система контроля состояния комнаты
Представим, что у нас есть комната, в ней ряд датчиков и нам нужно следить, все ли там в порядке. Постоянно смотреть за показателями надоест довольно быстро, поэтому нам требуется, чтобы в определенных случаях включался свет, причем не какой-то светодиод, а полноценная лампа. А всю информацию будем отправлять опять же на Adafruit IO.
Датчики у нас следующие:
- датчик уровня воды — аналоговый;
- датчик температуры и влажности DHT11 — цифровой;
- датчик звука — цифровой.
Кроме этого, для управления светом нам понадобится реле.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
var ssid = 'SSID'; var password = 'PASSW'; var TempRTSensorPin = D27; var WaterSensorPin = D26; // Аналоговый пин var SoundPin = D14; // Аналоговый пин var RelayPin = D12; const SoundLevel = 3.8; const Waterlevel = 30; var wifi = require('Wifi'); wifi.connect(ssid, {password: password}, function() { console.log('Connected to Wifi. IP address is:', wifi.getIP().ip); mqtt.connect(); }); var dht = require("DHT11").connect(TempRTSensorPin); var mqtt = require("MQTT").connect({ host: "io.adafruit.com", username: "<LOGIN>", password: "<SECRET-KEY>" }); mqtt.on('connected', function() { console.log("Connect"); }); function EnableLight() { digitalWrite(RelayPin,true); } setInterval(function() { if (analogRead(SoundPin)*100>SoundLevel) || (analogRead(WaterSensorPin) * 100 > Waterlevel) { D2.write(false); EnableLight(); } }, 100); setInterval(function() { dht.read(function (a) {console.log("Temp is "+a.temp.toString()+" and RH is "+a.rh.toString()); mqtt.publish("<LOGIN>/feeds/smartroom.rh", a.rh.toString()); mqtt.publish("<LOGIN>/feeds/smartroom.temperature", a.temp.toString()); mqtt.publish("<LOGIN>/feeds/smartroom.waterlevel",""+analogRead(WaterSensorPin)); },5000); |
В данной программе нам опять понадобится подключение к Wi-Fi и MQTT-брокеру. Они аналогичны первой программе, единственное отличие — MQTT-клиент при подключении не подписывается ни на какие топики.
В начале объявим, какие пины для чего мы будем использовать. И если первый пример теоретически мог бы быть запущен на ESP8266, то этот пример требует только ESP32, ведь нам нужно уже два аналоговых пина.
Для датчика температуры и влажности существует собственная библиотека, которую необходимо подключить: require(«DHT11»).
Реле управляется обычным изменением данных на цифровом пине. 0 — реле выключено, 1 — включено. Таким образом, подключая лампы, удлинители и прочие устройства через реле, ими можно управлять через интернет. Это один из самых частых IoT-приемов.
Но самое интересное — это, конечно, функция setInterval(function,time). Всем, кто писал на Arduino C, известна основная функция void Loop(){} — это функция, которая постоянно вызывается после загрузки программ. Так вот, setInterval гораздо круче. Во-первых, можно (и нужно) задать время повтора в миллисекундах, а во-вторых, можно задавать вызовы нескольких функций, причем с разной частотой.
Еще из интересных нюансов у меня есть переменные SoundLevel и Waterlevel, поскольку и датчик уровня воды, и датчик звука — аналоговые, показания на разных платах могут различаться и необходимо провести калибровку. На датчике звука установлен потенциометр, для регулировки чувствительности.
Кроме этого, в данном скрипте появился вызов функции mqtt.publish(field,data), он отправляет значение поля data в заданный канал на MQTT-брокере.
Заключение
Примеры, разобранные в данной статье, призваны продемонстрировать, что в создании собственных IoT-девайсов нет ничего сложного. Действительно, JS, на котором базируется Espruino, довольно несложный ЯП, а количество уроков по нему в интернете зашкаливает. В начале статьи я писал, что самостоятельная разработка дешевле, чем покупное устройство. В качестве примера, подтверждающего это, скажу, что одна розетка, управляемая по Wi-Fi, стоит в среднем примерно в два раза больше платы, используемой в обзоре, и в 10–20 раз больше, чем реле, необходимое для управления обычной розеткой. Выводы делай сам.