Хакер - JavaScript для умного дома. Arduino устарел, да здравствует ESP32!
Содержание статьи
Какую плату выбрать?
Когда неофит от IoT полезет в интернет, одним из первых модулей, которые он найдет, будет ESP8266. И действительно, он обладает массой достоинств: дешевый, много различных плат на его основе, позволяющих использовать его как самостоятельное устройство и подключать к сложным Arduino-based проектам. Но ESP8266, выпущенный в 2014 году, довольно быстро перестал удовлетворять запросы пользователей, и в 2015 году компания-разработчик Espressif выпускает новый микроконтроллер — ESP32.
Различия между ESP8266 и ESP32Точно так же, как и в случае с ESP8266, разработчики создали довольно много плат, базирующихся на новом микроконтроллере. В данной статье все примеры тестировались и проверялись на плате MH-ET LIVE ESP32 DevKit. Плата для обзора была любезно предоставлена интернет-магазином Amperkot.
Pinoutmap платыСколько процессорных ядер у ESP32?
- Одно, зачем ему больше?
- Два, меньше уже не делают
- ESP32 — это серия из четырех чипов с одним и двумя ядрами
Начинаем программирование
Как и у любой платы, основанной на ESP32, у MH-ET LIVE ESP32 DevKit есть достаточно большой набор языков программирования. Во-первых, это Arduino C, во-вторых, Lua, а в-третьих и в-четвертых — MicroPython и Espruino. Про Espruino — сборку JS для программирования микроконтроллеров — уже рассказывалось в ][, но в той статье разбиралась работа только на плате Espruino Pico, заточенной под Espruino.

INFO
К сожалению, портирование Espruino на ESP32 еще не до конца завершено. Часть возможностей, например обновление по воздуху и Bluetooth, недоступна. Но так как Espruino — open source проект, любой может добавить свою функциональность.
Установка
-
Скачиваем на официальном сайте свежую сборку Espruino. А если не доверяешь готовым сборкам, то можно собрать прошивку самостоятельно:
# Get the Espruino source code git clone https://github.com/espruino/Espruino.git cd Espruino # Download and set up the toolchain ('source' is important here) source scripts/provision.sh ESP32 # Clean and rebuild make clean && BOARD=ESP32 make -
Несмотря на то что мы будем программировать на JS, для установки все равно нужен Python, а конкретно
esptool.py. Повторяя свою предыдущую статью, скажу, что для его установки, при условии, чтоPythonуже установлен, достаточно набрать в консоли/терминале:pip install esptool. - В терминале перейти в папку с прошивкой. Кроме самого файла Espruino, здесь лежат файлы
bootloader.binиpartitions_espruino.bin. Это необходимые компоненты, но в некоторых сборках их может не быть, тогда их придется скачать отсюда. -
Запускаем процесс прошивки, не забыв изменить порт, указанный в данном примере, на свой, а также при необходимости указать другое имя прошивки. Здесь она называется
espruino_esp32.bin.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).
Espruino Web IDEПеред первым запуском нужно залезть в настройки, вкладка COMMUNICATIONS, и убедиться, что скорость общения выставлена на 115200, а также изменить поле Save on Send с No на Yes, иначе все программы после перезапуска слетят.
Теперь достаточно запустить IDE, подключиться к плате и набрать в консоли 1+2: если ты получил 3, значит, все настроено правильно и можно начинать полноценную работу.
Какую платформу для разработки прошивок ESP32 рекомендуют использовать Espressif, AWS IoT и Google Cloud IoT?
- Ubuntu LTS
- Mongoose OS
- Kolibri OS
Hello world
Во всех языках программирования, предназначенных или модифицированных для программирования микроконтроллеров, самая простая программа — так называемый Blink, мигание встроенным светодиодом. Но это как-то скучно. Поэтому нашей первой программой станет программа для управления светодиодом с помощью веб-страницы. И действительно, JS — это же язык веба.
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.
Ты уже делал свои проекты с Arduino?
- Еще нет
- Только помигал светодиодами
- Делал, и теперь замахнулся на большее
Из стандартных виджетов нам понадобятся всего два: Toggle и Color Picker.
Adafruit IO Dashboardvar 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();
Есть ли у ESP32 встроенная флеш-память?
- Нет, она не нужна
- Да, как же без нее?
- Есть только в модификации D2WD
Приступим к разбору программы.
- Для начала объявляем переменные, необходимые для подключения к 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 — цифровой;
- датчик звука — цифровой.
Кроме этого, для управления светом нам понадобится реле.
Схема подключения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-брокере.
SmartRoom DashboardКакая плата полностью совместима с Espressif ESP32-DevKitC?
- Таких нет, но подобных много
- Watterott ESP-WROOM32-Breakout
- Goouuu Tech ESP-32F
Заключение
Примеры, разобранные в данной статье, призваны продемонстрировать, что в создании собственных IoT-девайсов нет ничего сложного. Действительно, JS, на котором базируется Espruino, довольно несложный ЯП, а количество уроков по нему в интернете зашкаливает. В начале статьи я писал, что самостоятельная разработка дешевле, чем покупное устройство. В качестве примера, подтверждающего это, скажу, что одна розетка, управляемая по Wi-Fi, стоит в среднем примерно в два раза больше платы, используемой в обзоре, и в 10–20 раз больше, чем реле, необходимое для управления обычной розеткой. Выводы делай сам.
Читайте ещё больше платных статей бесплатно: https://t.me/hacker_frei