ESP, IoT LedBasic - Встроенный язык программирования световых эффектов для WS2812B на ESP8266

VktrSansara

✩✩✩✩✩✩✩
1 Дек 2018
10
3
Новомосковск
LedBasic — это интерпретируемый BASIC-подобный язык и виртуальная машина (VM) для создания анимаций на адресных светодиодных лентах. Программы пишутся в текстовом виде, компилируются в байт-код прямо на контроллере и выполняются неблокирующим образом — параллельно с остальным кодом Arduino.
Она позволяет писать анимации для светодиодов в виде простого текста (скриптов), компилировать их в компактный байт-код и воспроизводить без блокировки основного цикла микроконтроллера.

Основные возможности:
  • Неблокирующее выполнение— tick() вызывается из loop(), не мешает другим задачам
  • Независимость от типа ленты — подключение через коллбэки (совместимо с любой версией NeoPixelBus)
  • Полный язык: переменные A–Z, арифметика, условия, циклы, подпрограммы, функции
  • Управление скоростью—setSpeed(percent) масштабирует все задержки без изменения скрипта.
  • Встроенные LED-функции: SIN8, COS8, PIXEL, RND, ABS, MIN, MAX
  • HSV и RGB— SET_HSVс аппаратной конвертацией без float
  • Браузерная IDE — редактор, эмулятор, отладчик, экспорт — всё в одном HTML-файл
Особенности библиотеки:
  • Аппаратная независимость (Hardware Agnostic)
    LedBasic ничего не знает о железе. Она не использует напрямую FastLED или NeoPixelBus. При создании объекта вы передаете ей коллбэки (функции обратного вызова) для установки цвета пикселя, вывода буфера на ленту и очистки. Это значит, что вы можете использовать абсолютно любую библиотеку для работы со светодиодами.
  • Асинхронность (Неблокирующая работа)
    Внутри используется конечный автомат (State Machine). Команды WAIT или DELAY не вешают микроконтроллер (ESP8266/ESP32). Функция tick() быстро отрабатывает часть инструкций и отдает управление обратно, что позволяет параллельно работать с Wi-Fi, MQTT или веб-сервером.
  • Встроенная математика и эффекты
    Реализованы собственные таблицы синуса и косинуса (sin8, cos8) и конвертер HSV -> RGB. Библиотеке не нужны тяжелые зависимости.
  • Компиляция на лету и запуск из памяти
    Текстовый скрипт компилируется в компактный бинарный байт-код. Скрипты можно хранить в файловой системе (LittleFS) и запускать прямо оттуда.
  • Защита от зависаний (Watchdog protection)
    За один вызов tick() виртуальная машина выполняет не более 1000 инструкций. Если в BASIC-скрипте написан бесконечный цикл GOTO без пауз, микроконтроллер не зависнет и не уйдет в перезагрузку (Watchdog reset).

Особенности языка LedBasic:
  • Переменные и математика: 26 переменных (A-Z), поддерживающих 16-битные целые числа. Доступна базовая математика (+, -, *, /, %).
  • LED-команды: SET (RGB цвет), SET_HSV (цвет по тону, насыщенности и яркости), FILL (заливка), CLEAR (очистка), BRIGHT (яркость).
  • Отрисовка: Команды SHOW (вывести на ленту), WAIT (вывести и ждать), DELAY (просто пауза).
  • Математические функции: Встроены быстрые функции для создания эффектов: RND (случайное число), SIN8 и COS8 (табличные синусы/косинусы для плавных волн), MIN, MAX, ABS.
  • Управление потоком: Классические FOR...TO...STEP...NEXT, циклы IF...THEN, переходы GOTO и подпрограммы GOSUB...RETURN.
Скачать:
Библиотека на - GitVerse
Библиотека с - Яндекс.Диск
Мануалы и справочники - Яндекс.Диск
Дополнительно:
Online LedBasic IDE

Функции логически разделены на группы: инициализация, загрузка кода, управление выполнением и настройки.

Инициализация объекта:

LedBasic(uint16_t count, LBSetPixel setPixelCb, LBShow showCb, LBClear clearCb)
Конструктор класса.
Создает экземпляр виртуальной машины.
Библиотека не привязана к конкретному оборудованию, поэтому при создании объекта вы обязаны передать количество светодиодов и три функции-коллбэка (обычно это лямбда-выражения), которые "объясняют" библиотеке, как управлять вашей лентой.
  • Аргументы:
    • count — общее количество пикселей на вашей ленте.
    • setPixelCb — функция вида void(uint16_t pos, uint8_t r, uint8_t g, uint8_t b). Устанавливает цвет конкретного пикселя.
    • showCb — функция вида void(). Вызывает обновление ленты (отправку данных из буфера на светодиоды).
    • clearCb — функция вида void(). Полностью гасит ленту (заливает черным цветом).
~LedBasic() - Деструктор класса. Вызывается при удалении объекта. Автоматически освобождает динамическую память (RAM), если она была выделена под байт-код с помощью malloc.


Подготовка и компиляция скриптов:

bool compileFromText(const char* scriptText)

Компилирует текстовый скрипт на языке BASIC в бинарный байт-код прямо в оперативной памяти (RAM) микроконтроллера.
  • Аргументы: Строка const char*, содержащая полный текст скрипта (строки должны разделяться символом переноса \n).
  • Как работает: Парсит текст, переводит команды в числа (токены), выделяет нужное количество RAM и сохраняет туда байт-код. Строит индекс для быстрых переходов GOTO.
  • Возвращает: true при успешной компиляции, false если не хватило памяти.

bool compileBasToBin(const char* basPath, const char* binPath)
Читает текстовый файл BASIC из файловой системы LittleFS, компилирует его построчно и сразу записывает готовый байт-код в другой файл.
  • Аргументы:
    • basPath — путь к исходному текстовому файлу (например, "/effect.bas").
    • binPath — путь, куда сохранить бинарный файл (например, "/effect.bin").
  • Особенности: Очень экономит оперативную память, так как не загружает весь текст в RAM. Полезно для предварительной компиляции скриптов.

bool loadBinFile(const char* path)
Загружает в оперативную память заранее скомпилированный бинарный файл из файловой системы LittleFS.
  • Аргументы: path — путь к бинарному файлу (например, "/effect.bin").
  • Возвращает: true при успехе, false если файл не найден или ошибка чтения.

bool loadBinMemory(const uint8_t* code)
Подключает байт-код напрямую из указателя памяти.
  • Аргументы: code — массив uint8_t, содержащий байт-код.
  • Особенности: Эта функция не копирует массив в новую память и не вызывает malloc. Она просто сохраняет указатель. Это идеально подходит для хранения жестко зашитых (hardcoded) эффектов во Flash-памяти микроконтроллера с использованием PROGMEM.

Управление и выполнение:

void play()

Запускает выполнение загруженного скрипта с самой первой строки.
  • Как работает: Обнуляет все переменные (A-Z), сбрасывает стек вызовов (GOSUB, FOR), переводит виртуальную машину в статус VM_RUNNING. Если скрипт не загружен — функция просто проигнорируется.

void stop()
Останавливает выполнение скрипта.
  • Как работает: Переводит машину в статус VM_STOPPED и автоматически вызывает ваш коллбэк clearCb, чтобы выключить все светодиоды на ленте.

void tick()
Главная функция-движок.
Ее обязательно нужно поместить внутрь главного цикла loop() вашего скетча, чтобы анимация работала.
  • Как работает: За один вызов читает и выполняет порцию инструкций (байт-кода).
  • Асинхронность: Если в скрипте встречается команда WAIT 500, функция tick() запоминает время, переводит VM в режим ожидания и сразу же завершает свою работу, отдавая управление основному loop(). При следующих вызовах она будет проверять встроенный таймер (millis()), и продолжит работу только когда время выйдет.
  • Безопасность: Чтобы микроконтроллер не завис на конструкции вида 10 GOTO 10, tick() имеет лимит (budget): за один проход она выполняет максимум 1000 инструкций.

bool isRunning() const
  • Возвращает: true, если скрипт в данный момент выполняется (включая моменты паузы WAIT/DELAY). Возвращает false, если скрипт остановлен функцией stop() или дошел до конца программы (маркера конца файла).

Управление скоростью:
Библиотека позволяет "на лету" изменять скорость воспроизведения эффектов, воздействуя на длительность пауз WAIT и DELAY в скриптах, без необходимости переписывать сам код скрипта.

void setSpeed(uint16_t percent)
Устанавливает множитель скорости в процентах (от 1 до 1000).
  • Аргументы:percent — скорость в процентах.
    • 100 — нормальная скорость (по умолчанию, 1 к 1).
    • 200 — в 2 раза быстрее (задержка WAIT 100 превратится в 50 мс).
    • 50 — в 2 раза медленнее (задержка WAIT 100 превратится в 200 мс).

uint16_t getSpeed() const
  • Возвращает: текущую установленную скорость в процентах.
C++:
void setup() {
    strip.begin();

    // 1. Инициализация (basic создана глобально)

    // 2. Компиляция текста из строки
    basic.compileFromText("10 SET 0, 255, 0, 0\n20 WAIT 1000\n30 CLEAR\n40 WAIT 1000\n50 GOTO 10\n");

    // Можно загрузить из файла: basic.loadBinFile("/blink.bin");

    // 3. Запуск программы
    basic.play();
}

void loop() {
    // 4. Движок программы (не блокирует цикл!)
    basic.tick();

    // Пример управления на лету:
    // Если нажата кнопка, ускорить в 3 раза
    if (digitalRead(BTN_PIN) == LOW) {
        basic.setSpeed(300);
    } else {
        basic.setSpeed(100);
    }
}
Язык представляет собой классический диалект BASIC с номерами строк.

Типы данных и переменные
  • Поддерживается 26 глобальных целочисленных переменных (типа int16_t).
  • Имена переменных — одиночные заглавные буквы английского алфавита: от A до Z.
Команды управления светодиодами
  • SET pos, r, g, b — Установить цвет RGB (0-255) для пикселя с индексом pos.
  • SET_HSV pos, h, s, v — Установить цвет в формате HSV (Оттенок, Насыщенность, Яркость). Библиотека сама пересчитает это в RGB.
  • FILL r, g, b — Залить всю ленту одним цветом.
  • CLEAR — Выключить все светодиоды (залить черным).
  • SHOW — Отправить данные на ленту (применить изменения).
  • WAIT ms — Вызвать SHOW и подождать ms миллисекунд.
  • DELAY ms — Просто подождать ms миллисекунд (без обновления ленты).
  • BRIGHT val — Команда зарезервирована для изменения глобальной яркости.
Управление логикой (Control Flow)
  • GOTO line — Безусловный переход на строку с номером line.
  • GOSUB line ... RETURN — Вызов подпрограммы и возврат (глубина стека до 10 вызовов).
  • FOR var = start TO end [STEP step] ... NEXT var — Цикл со счетчиком (глубина вложенности до 8).
  • IF условие THEN команда — Условное выполнение. Поддерживаются операторы ==, !=, >, <, >=, <=. Особенность: после THEN может идти только одна команда.
Математика и функции
Поддерживается приоритет математических операций: +, -, *, /, % (остаток от деления), AND (побитовое И), OR (побитовое ИЛИ).
Встроенные функции:

  • RND(min, max) — Случайное число в диапазоне.
  • ABS(x) — Модуль числа.
  • MIN(a, b), MAX(a, b) — Минимальное / максимальное значение.
  • SIN8(x), COS8(x) — Быстрый синус/косинус (вход 0-255, выход 0-255).
  • PIXEL — Возвращает индекс последнего пикселя на ленте (общее количество - 1).
LedBasic IDE v3 — это легковесная, полностью работающая (не без багов конечно, но вполне работает.) в браузере среда разработки (IDE) и эмулятор для написания эффектов адресных светодиодных лент (подобных WS2812b) на Basic подобном языке.

Основные возможности:
  • Синтаксическая подсветка: Автоматическое окрашивание команд (синий), логики (фиолетовый), математики (желтый), переменных (зеленый) и чисел.
  • Автодополнение (Autocomplete): При вводе текста появляются подсказки с шаблонами команд (например, при вводе SET предложит SET_HSV P , C , 255 , 200).
  • Анализ ошибок (Линтер): Редактор «на лету» проверяет код. Если допущена ошибка (неизвестная команда, нет номера строки), код подчеркивается красной волнистой линией, а в левом поле появляется значок предупреждения.
  • Инструмент «Нумерация»: Так как язык требует номеров строк (10, 20, 30...), этот инструмент позволяет автоматически перенумеровать весь код с заданным шагом.
  • Автоформатирование: Кнопка «Формат» автоматически расставляет пробелы вокруг операторов и приводит команды к верхнему регистру для красоты кода.
Встроенный LED эмулятор:
Позволяет видеть результат работы кода прямо на экране без подключения реального "железа".
  • Формы вывода:
    Поддерживает 3 режима визуализации:
    • Полоса (Strip) — классическая прямая лента.
    • Кольцо (Ring) — светодиоды по кругу.
    • Матрица (Matrix) — прямоугольная 2D-сетка (с настройкой ширины и высоты).
  • Настройки: Можно менять количество пикселей (до 1024), масштаб (размер светодиодов).
  • Графика: Реалистичное отображение с эффектом "свечения" (Glow) вокруг ярких пикселей.
  • Производительность: Показывает реальный FPS работы эмулятора.
Отладка и мониторинг:
  • Консоль: Выводит логи компиляции, сообщения об ошибках и статус системы.
  • Переменные (Live-мониторинг): В реальном времени показывает текущие значения всех 26 доступных переменных (от A до Z) прямо во время работы анимации! Отличный инструмент для поиска логических ошибок.
  • Байт-код: Показывает результат компиляции. Текстовый BASIC переводится в компактные HEX-инструкции, которые можно посмотреть с привязкой к номерам строк.
Экспорт и интеграция с микроконтроллерами (Ардуино / ESP):
Программа создана не только для развлечения, но и для реального использования в проектах (например, на Arduino или ESP8266/ESP32). Код можно экспортировать в несколько форматов:
  • .bas — исходный текстовый код программы.
  • const char* — код в виде C++ строки для вставки прямо в скетч Arduino.
  • PROGMEM — код в виде массива символов, хранящегося во флеш-памяти микроконтроллера.
  • .bin hex — скомпилированный бинарный байт-код в виде C++ массива (uint8_t), готовый для выполнения на микроконтроллере (самый быстрый и компактный вариант).
Удобство интерфейса (UI/UX):
  • Горячие клавиши: Ctrl+Enter или F5 для запуска кода, Ctrl+S для сохранения.
  • Кастомизация UI: Можно изменять высоту нижней панели отладки и ширину левой панели эмулятора перетаскиванием мыши.
  • Настройки: Подстройка размера шрифта, размера табуляции (Tab), скорости работы виртуальной машины (setSpeed (%)).
  • Локальная работа: Все функции (загрузка, сохранение файлов, компиляция) работают прямо в браузере с использованием HTML5 File API, не требуя сервера.
2026-03-12_23-08-40.png2026-03-12_23-08-28.png
C++:
// =============================================================
//  main_rainbow.cpp — эффект «Радуга со случайными вспышками»
//
//  Вся лента показывает полный спектр радуги, равномерно
//  распределённый по длине. Радуга медленно вращается.
//  Случайные пиксели вспыхивают белым — как искры.
//
//  Использует PIXEL — автоматически подстраивается под
//  любую длину ленты без изменения скрипта.
//
//  Железо: лента WS2812B, ESP8266, DMA-пин GPIO3 (RX)
// =============================================================

#include <Arduino.h>
#include <NeoPixelBus.h>
#include "LedBasic.h"

// --- настройки ---
#define NUM_LEDS  60    // меняй под свою ленту
#define DATA_PIN   3    // DMA: только GPIO3 (RX)

using MyStrip = NeoPixelBus<NeoGrbFeature, NeoEsp8266DmaWs2812xMethod>;
MyStrip strip(NUM_LEDS, DATA_PIN);

LedBasic basic(
    NUM_LEDS,
    [](uint16_t pos, uint8_t r, uint8_t g, uint8_t b) {
        strip.SetPixelColor(pos, RgbColor(r, g, b));
    },
    []{ strip.Show(); },
    []{ strip.ClearTo(RgbColor(0, 0, 0)); strip.Show(); }
);

// =============================================================
//  Скрипт: радуга + случайные белые вспышки
//
//  H  — смещение оттенка (анимация вращения)
//  P  — текущий пиксель
//  C  — оттенок пикселя P
//  S  — случайное число для вспышки (0..14)
//
//  Каждый пиксель получает оттенок H + P * 256 / PIXEL —
//  это равномерно растягивает весь спектр по ленте.
//  С вероятностью 1/15 пиксель вспыхивает белым.
// =============================================================
const char* scriptText =
    "10 H = 0\n"           // начальное смещение оттенка

    // --- кадр: рисуем радугу пиксель за пикселем ---
    "100 P = 0\n"
    "110 C = H + P * 256 / PIXEL\n"  // оттенок: смещение + шаг по позиции
    "120 SET_HSV P , C , 255 , 170\n"

    // с вероятностью 1/15 — белая вспышка поверх
    "130 S = RND 0 , 14\n"
    "140 IF S == 0 THEN SET P , 255 , 255 , 220\n"

    "150 P = P + 1\n"
    "160 IF P <= PIXEL THEN GOTO 110\n"

    // показать кадр и прокрутить радугу
    "170 WAIT 25\n"
    "180 H = H + 4\n"
    "190 IF H > 255 THEN H = H - 256\n"
    "200 GOTO 100\n";

// =============================================================
void setup() {
    strip.Begin();
    strip.Show();
    basic.compileFromText(scriptText);
    basic.play();
}

void loop() {
    basic.tick();
}
Код:
Радуга:

10 H = 0
20 FOR I = 0 TO PIXEL
30   C = H + I * 256 / PIXEL
40   SET_HSV I , C , 255 , 180
50 NEXT I
60 WAIT 30
70 H = H + 2
80 IF H > 255 THEN H = H - 256
90 GOTO 20
 
Изменено:
  • Лойс +1
Реакции: BOT_Zilla