Уровень сложности: для начинающих.
Идея возникла, когда под новый год сломалась старая гирлянда. Сын посмотрел на RGB-светодиодную ленту и спросил, можно ли сделать из нее гирлянду. Можно — сказал я, и сын начал долго описывать, как именно должна мигать гирлянда. Я ничего не понял, и решил сделать так, чтобы он мог сделать так как он хочет самостоятельно. Начал я с анализа собственных пожеланий к проекту:
- Графическая среда разработки типа Scratch
- offline работа — возможность работы устройства при выключенном компьютере
- Работа по воздуху, без подключения провода
- Надежность — никакая загруженная программа не должна «повесить» устройство до необходимости перезагрузки
Аппаратная платформа
За основу была взята плата ESP8266 c NodeMCU (вроде такой), к ней добавилась пара транзисторов и резисторов. Выбранные модули имеют регулятор напряжения на 3-20 вольт, что позволяет питать его от того же источника, что и ленту. Выходы esp дают напряжение 3 вольта с предельным током в 12mA, так что для управления лентой я использовал обычные биполярные транзисторы.
Чтобы из микроконтроллера не вышел белый дым, я поставил между выходом и базой резистор в 220 Ом. У esp программный pwm, некоторые пины имеют специальное назначение, я подбирал экспериментально.
Прошивка
Перед использованием, esp8266 нужно прошить, я использую NodeMCU с lua, хотя она и имеет несколько меньше возможностей, чем Arduino с C++. Итак, проще всего начать с готовой прошивки с nodemcu-build.com. Для нашего проекта нужно будет добавить модуль pwm. Через некоторое время на почту упадет ссылка на образ. Нужно взять тот, что с float.
Прошить можно любым программатором отсюда, а я использую NodeMCU Flasher.
В NodeMCU есть файловая система, выполнение начинается с модуля init.lua, для загрузки я использую ESPlorer.
Мигаем диодом
В заблуждение меня ввел следующий момент. Внутренняя нумерация (GPIO0-16) используется только в native программах (Arduino/C++). Из lua используются обозначения с платы. Например, чтобы использовать GPIO16/D0(встроенный светодиод). из Lua, нужно писать gpio.write(0, gpio.HIGH). Помигать диодом можно прямо из консоли ESPlorer-а.
В отличие от Arduino, в nodemcu нельзя использовать delay и busy-loop, консоль и wifi используют для работы тот же самый процессор. Также в плату встроен watchdog, который перезагрузит ее если ваш код будет выполняться больше 500ms. Рекомендуется не занимать процессор больше чем на 2ms. Для решения проблемы существуют функции node.task.post и tmr
Выбор среды разработки
Сначала хотел использовать Scratch, но он не подошел мне, так как не позволяет работать оффлайн — программа исполняется на компьютере в среде scratch, а все платы для него работают пассивно. Пошарив в интернете, я наткнулся на Google Blockly. Это оказалось ровно тем, что нужно: он поддерживает кодогенерацию в lua, и создание собственных блоков. После экспериментов, я решил делать свой проект на основе web, выбрал nodejs в качестве сервера, и Blockly в качестве фронтенда. Абсолютно необходимо было создать 2 блока — установка цвета и задержка. Blockly имеет встроенные функции для работы с цветами в формате #ffffff, создание функции для установки цвета не составило никакой проблемы.
Blockly.Lua['set_color'] = function (block) { var parseColor = Blockly.Lua.provideFunction_( 'set_colour_rgb', ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(s)', ' local rs,gs,bs = s.match(s, "#(..)(..)(..)");', ' setColor(tonumber(rs, 16),tonumber(gs, 16),tonumber(bs, 16));', 'end']); var value_color = Blockly.Lua.valueToCode(block, 'Color', Blockly.Lua.ORDER_ATOMIC); var code = `set_colour_rgb(${value_color})n`; return code; };
Таким образом были закрыты 2 из 3х требований.
Функция sleep()
Тут пришлось конкретно подумать, как совместить надежную работу OTA с выполнением пользовательского кода. К счастью в lua есть библиотека coroutine
После раздумий и вдумчивого чтения документации я понял, что весь клиентский код нужно запускать в coroutine, и использовать yield вместо sleep, чтобы основной модуль мог установить таймер.
Также, для защиты платы от бесконечного цикла, я запатчил код после генератора, вставив yield(0) в начале каждой итерации каждого цикла
function MCUPostProcessLua(code) { return code.replace(/ do[ ]?n/, ' do ncoroutine.yield(0);n'); }
OTA загрузка
Я решил, реализовать самое простое для локальной сети решение, плата подключается к домашнему wifi, подключается к серверу по фиксированному адресу, отправляет уникальный идентификатор (чтобы корректно обрабатывать повторные подключения), и ожидает обновления программы через этот сокет.
Результат
github.com/farafonoff/BlocklyESP8266
Можно добавить очень много, например какую-то возможность авторизации для работы через публичные сети, user-friendly настройщик в режиме hostap, улучшить модульность init.lua и поддержку его обновления OTA (сейчас обновляется только модуль с загруженной программой).
Источник