[Из песочницы] Делаем гирлянду с ребенком

image


Уровень сложности: для начинающих.

Идея возникла, когда под новый год сломалась старая гирлянда. Сын посмотрел на RGB-светодиодную ленту и спросил, можно ли сделать из нее гирлянду. Можно — сказал я, и сын начал долго описывать, как именно должна мигать гирлянда. Я ничего не понял, и решил сделать так, чтобы он мог сделать так как он хочет самостоятельно. Начал я с анализа собственных пожеланий к проекту:

  1. Графическая среда разработки типа Scratch
  2. offline работа — возможность работы устройства при выключенном компьютере
  3. Работа по воздуху, без подключения провода
  4. Надежность — никакая загруженная программа не должна «повесить» устройство до необходимости перезагрузки


Аппаратная платформа


транзисторы

За основу была взята плата ESP8266 c NodeMCU (вроде такой), к ней добавилась пара транзисторов и резисторов. Выбранные модули имеют регулятор напряжения на 3–20 вольт, что позволяет питать его от того же источника, что и ленту. Выходы esp дают напряжение 3 вольта с предельным током в 12mA, так что для управления лентой я использовал обычные биполярные транзисторы.

image

Чтобы из микроконтроллера не вышел белый дым, я поставил между выходом и базой резистор в 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-а.

image

В отличие от 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 (сейчас обновляется только модуль с загруженной программой).

© Geektimes