Лілка. Від ідеї до проєкту

Усім привіт! У цій статті поговоримо про те, як розвинулась українська навчально-ігрова консоль Лілка. Що з’явилося нового і — найголовніше — як втілити власну ідею за допомогою цієї крутої консолі.

Долучайтесь до великого літнього збору для «Хартії». В цей раз збираємо 10 мільйонів гривень на «Вампірів», які будуть щодня та щоночі кошмарити окупантів. Посилання на банку.

Стаття розрахована на початківців, тож пояснюватиму все максимально просто. Навіть якщо ви ніколи не писали код для мікроконтролерів — нічого страшного: саме з Лілки зручно почати. Ми пройдемо шлях від «що це взагалі таке» до робочих застосунків, які ви зможете запустити вже сьогодні.

Що таке Лілка

Якщо ви вперше чуєте про Лілку, рекомендую ознайомитись з моєю попередньою статтею і коротко нагадаю.

Лілка — це українська open source портативна навчально-хакерська консоль на базі мікроконтролера ESP32-S3 від Андерсона та його команди. Вона має власну друковану плату, операційну систему KeiraOS, готову бібліотеку для спрощення розробки, набір вбудованих застосунків, ігор, демо, детальну документацію та активну спільноту в Discord.

Коли я писав попередню статтю Лілка мала один єдиний репозиторій. Наразі проєкт налічує їх 16, перерахую основні:

  • keira — основна операційна система консолі
  • sdk — бібліотека для роботи з апаратною частиною
  • lilka — схеми та документація, друковані плати Лілки (залізо)
  • examples — приклади C++, Lua та mJS коду для Keira
  • catalog — сайт та застосунок для Лілки для перегляду застосунків спільноти
  • flasher — сайт для прошивки Лілки прямо з браузера
  • keira_dynapp — приклад динамічного C++ застосунку для KeiraOS

Також є окремі репозиторії для конкретних прошивок та застосунків:

  • doom_port — порт DOOM
  • arduino-nofrendo — NES-емулятор портований під Лілку
  • PicoPico — порт фентезі-консолі PICO-8 для Лілки
  • mpeg_stream_player — програвач відео
  • BLE_Controller — прошивка Bluetooth-контролер (мишка, клавіатура та ігровий контролер для Windows та Linux)
  • IR_module та RF_module — cхеми модулів для роботи з інфрачервоними та радіосигналами

Як влаштована Лілка всередині?

На перший погляд Лілка проста: екран, кнопки, SD-картка. Але якщо зазирнути глибше — усередині живе ціла екосистема: мікроконтролер, операційна система, скриптові рушії, завантажувач застосунків і веб-інтерфейс.

Лілка це конкурент Raspberry Pi?

Всередині Лілки працює китайський мікроконтролер ESP32 від компанії Espressif. Маленький чип, дешевий, економний, вмикається за мілісекунди. Усередині немає операційної системи у звичному сенсі — ні файлової системи, ні монітора, ні клавіатури. Він просто безперервно виконує єдиний шматок коду, записаний у його флеш-пам’ять. Лілка використовує конкретну версію — ESP32-S3-WROOM-1-N16R8: два ядра Xtensa LX7 на 240 МГц, 16 МБ флеш-пам’яті і 8 МБ PSRAM.

Raspberry Pi — це вже мікрокомп’ютер на основі процесора. На ньому працює повноцінна Linux-подібна ОС, є вихід HDMI, USB-порти і сотні мегабайтів оперативної пам’яті. Він добре підходить для задач, що потребують серйозних обчислень: мультимедіа, веб-сервери, комп’ютерний зір. Детальніше в окремому відео. І хоча мікроконтролер ніколи не зможе виконувати важконавантажені завдання. Мені подобається тенденція, коли пристрій на основі контролера все більше нагадує смартфон, просто з своїми простими застосунками.

⚙️ FreeRTOS і ESP-IDF

Сам по собі чип — це просто кремній. Щоб із ним працювати, потрібен набір інструментів. Espressif зібрала такий під назвою ESP-IDF (Espressif IoT Development Framework) — офіційний SDK для розробки під ESP32. Він містить адаптовані під цей чип форки кількох проєктів, зокрема FreeRTOS для багатозадачності і lwIP для роботи з мережею.

FreeRTOS — це невеличка операційна система реального часу (RTOS). Її головна робота — дати розробнику абстракцію потоків (у термінах FreeRTOS їх називають «задачами», але суть та сама). Два шматки коду можуть виконуватися «паралельно», а система перемикається між ними.

Порівняння мікроконтролера та процесора

У ESP32-S3 два фізичні ядра. Одне ядро відповідає за малювання на екрані, друге виконує ваш код. Саме тому, навіть коли ваш Lua-скрипт нічого особливого не робить, екран усе одно оновлюється — цим займається окреме ядро.

Але навіть із двома ядрами кожне з них виконує кілька задач по черзі, а не одночасно. Перемиканням керує планувальник задач (task scheduler) — диспетчер FreeRTOS. Працює це так: ваш код виконується безперервно, доки не дійде до виклику sleep(), delay() чи подібного. Саме в цей момент планувальник може передати керування іншій задачі. Тож затримки у програмі — це не просто зручність, а жорстка вимога: без них інші задачі ніколи не отримають час процесора.

Lilka SDK і KeiraOS

Поверх ESP-IDF працює Lilka SDK — C+±бібліотека, що ховає всю «залізну» складність консолі. Замість того щоб вручну ініціалізувати дисплей ST7789 по SPI чи налаштовувати I2S для аудіопідсилювача MAX98357A, розробник просто викликає lilka::display.drawText() або lilka::audio.play(). Усю чорну роботу SDK бере на себе.

А KeiraOS — це і є операційна система (прошивка) Лілки, побудована поверх FreeRTOS, ESP-IDF і Lilka SDK (назва — на честь кота розробника 🐈). Саме її бачить користувач при ввімкненні: меню, застосунки, файловий менеджер, Wi-Fi-утиліти. Її завдання, радше прикладне, ніж системне: узяти все наявне і перетворити на щось зрозуміле кожному — ще й в умовах жорсткої економії пам’яті, адже 8 МБ PSRAM — це не так багато, як здається.

Рівні архітектури KeiraOS

У KeiraOS є дві ключові абстракції — застосунок (app) і сервіс (service), і обидві реалізовані як окремі RTOS-задачі. Застосунок — це те, що запускає користувач: гра, утиліта, скрипт. Він займає передній план, читає натискання кнопок і малює на екрані. Сервіс — це фонова задача, що працює паралельно: наприклад, Wi-Fi-сервіс тримає з’єднання з мережею, поки ви граєте, а сервіс веб-менеджера обробляє запити з браузера. Сервіси не мають інтерфейсу — вони просто працюють у фоні.

Нововведення в проєкті Лілка

З часу виходу попередньої статті у проєкту Лілка з’явилось багато нововведень. Коротко розповім про них

У проєкту з’явився власний каталог застосунків. Щось на кшталт Play Market, чи App Store. Він доступний як для перегляду у браузері, так і на самій консолі.

Веб-прошивач дозволяє прошити Лілку новою версією KeiraOS

Файловий веб-менеджер. Дозволяє завантажувати, та вивантажувати файли з Лілки. Створювати каталоги, переглядати файли. А також прошивати прошивки .bin прямо з браузера! Для роботи потрібно увімкнути сервіс веб у сервісах. Також тепер не потрібно пам’ятати IP адресу Лілки, адже з’явився сервіс mDNS і Лілка доступна в локальній мережі за адресою lilka.local.

В Lua API додали I2C, SPI, ШІМ (PWM), TCP-сокети, керування адресованими світлодіодами та MQTT протокол. Це круто, я в захваті!

Портували проєкт фентезі консолі PICO-8 для Лілки. Що таке PICO-8 можна дізнатись у цьому цікавому відео.

Покращилась підтримка застосунків mJS. З’явився розділ документації з mjs API та приклади застосунків

Лілка офіційно почала продаватись на RadioKit, Radio LED, Arduino.ua та Ebay. Також з’явились магазини і від учасників спільноти ectronz.com та solderdemon. Персонально мені хочеться, щоб Лілка також продавалась на майданчику Tindie.

З того що було, але я не приділив достатньо часу в першій статті це прошивки Meshtastic та Bruce

Я почав вести блог про Лілку англійською на Hackaday, а також написав статтю у Вікіпедію. Зʼявились статті на Hackster та чудовий огляд від Daniel Allen. Дякую Allen!

Був проведений Lilka Workshop з збірки Лілки. За участі хакерспейсу Nu31 розробника black.ghost та засновника Андерсона і мене.

Фото з заходу Lilka Workshop

А ще у Лілки з’явилися власні стікери — і вони чудові. Стікери я взагалі обожнюю, а ці ще й напрочуд гарні. У продажу їх поки немає, але я із задоволенням поділюся. Для цього опублікуйте допис про Лілку в соцмережі (бажано з фото) і напишіть мені про це в особисті повідомлення тут, на DOU.

Стікери Лілка

Каталог застосунків

Однією з найзручніших можливостей Лілки повноцінний каталог застосунків — доступний як з браузера, так і прямо з консолі без потреби у комп’ютері. Каталог розміщено за адресою catalog.lilka.dev У ньому будь хто може розмістити власний застосунок! Каталог містить кілька розділів:

Каталог застосунків Лілка

Apps — власне самі застосунки. Застосунок може бути як грою, так і якоюсь програмою, чи утилітою. Усі застосунки можна розділити за типами C++, Lua, mJS, та архів (прошивки в форматі .bin та інші формати) Детальніше опишу нижче

Mods — усе що стосується модифікації заліза, або вигляду Лілки: корпуси, кришки, кнопки, модулі розширення тощо. Відкритість Лілки дозволяє експерементувати та змінювати її на власний розсуд.

Authors — Apps та Mods відсортовані за авторами

Examples — приклади застосунків. Оскільки мало хто знає про окремий репозиторій з прикладами його додали прямо в магазин застосунків.

Documentation — опис того як додати свій застосунок у каталог

Різниця застосунків та прошивок

Перш ніж переходити до конкретних інструментів, варто розібратися з термінами. На Лілці є дві принципово різні категорії того, що можна запускати:

🔧 Прошивка (firmware) — це повноцінне програмне забезпечення, що повністю керує консоллю. Воно записується безпосередньо в пам’ять мікроконтролера ESP32-S3 і запускається при увімкненні пристрою. Прошивка має повний контроль над усіма ресурсами: екраном, кнопками, звуком, мережею, GPIO. По суті, це «операційна система» Лілки. Для встановлення нової прошивки потрібно перепрошити пристрій — через PlatformIO, esptool або веб-флешер.

📦 Застосунок (app) — це окрема сутність, що працює всередині прошивки KeiraOS. Застосунок не має прямого доступу до заліза: він звертається до нього через API, який надає прошивка. Застосунки обмежені тими можливостями, що передбачила прошивка.

KeiraOS — це і є основна, «штатна» прошивка від команди Лілки. Саме її більшість користувачів встановлює одразу після збирання. Усередині KeiraOS можна запускати десятки різних застосунків — і саме на них зосереджена більшість гайдів і прикладів. Але KeiraOS не єдина прошивка: під Лілку портовані й альтернативні системи з власним функціоналом, про які поговоримо далі.

білка може запускати багато різних прошивок

Застосунки KeiraOS
KeiraOS дозволяє запускати застосунки трьох типів:

🧩 Вбудовані C++ застосунки. Написані на C++ як частина самої прошивки KeiraOS. Це ігри, демо, утиліти, що завжди доступні в меню після першого ввімкнення. Для додавання власного вбудованого застосунку потрібно змінити вихідний код KeiraOS і перепрошити пристрій. Підхід складніший, зате дає повний доступ до всіх можливостей бібліотеки lilka.

📥 Завантажувані прошивки (.bin) скомпільовані застосунки у форматі .bin, які можна покласти на SD-картку і запускати без перепрошивки. Тобто з KeiraOS прошивку можна відкрити прошивку, вона завантажиться. В ній можна зробити все необхідне, а після вимкнення та увімкнення Лілки KeiraOS повертається.

📜 Lua-скрипти (.lua). Текстові файли з кодом, що виконуються вбудованим інтерпретатором Lua 5.4.6 безпосередньо з SD-картки. Не потребують компіляції: написав скрипт, поклав на картку, запустив — за секунди бачиш результат. Lua наразі найзручніша мова для початківців: документація повна, прикладів багато, спільнота допомагає.

⚡ mJS-скрипти (.js) З релізу v2.6.5 у KeiraOS повноцінно з’явилася підтримка mJS — мінімалістичної версії JavaScript від Cesanta. Як і Lua, ці скрипти запускаються прямо з SD-картки, без компіляції. Підтримка mJS поки активно розвивається — деякі функції з’являться у наступних релізах. Якщо знаєте JavaScript, mJS дозволяє писати застосунки зі знайомим синтаксисом без необхідності вивчати нову мову.

Кожен підхід має сильні сторони. C++ — для серйозних, ресурсомістких застосунків (емулятори, відеоплеєри, демо з графікою). Lua і mJS — для швидких експериментів, навчання, перших ігор: цикл «написав → побачив результат» триває секунди, не хвилини.

Альтернативні прошивки: Лілка як платформа

Як я вже згадував, KeiraOS — головна, але не єдина прошивка для Лілки. Сама по собі KeiraOS уміє чимало: запускає ігри й застосунки, працює з сенсорами через GPIO (I2C, UART, SPI, ADC, PWM) і навіть запускає ретро-ігри NES прямо з SD-картки через емулятор Nofrendo. Та найцікавіше попереду. Оскільки Лілка вміє запускати сторонні прошивки у форматі .bin, спільнота наробила цілу купу альтернативних систем — від мови Rust до інструментів кібербезпеки. Саме це перетворює Лілку з простої консолі на повноцінну платформу для експериментів. Пройдімося найцікавішими

Pixeler — альтернативна прошивка, портована з проєкту «Ideal Console». Її головна фішка: інтерфейс можна розробляти одночасно для мікроконтролера і для комп’ютера тим самим кодом. Тобто ви проєктуєте й тестуєте UI прямо на ПК , не перепрошиваючи щоразу пристрій — це сильно пришвидшує розробку.

Написана на C++ на фреймворку Pixeler, має готові приклади GUI: меню, екрани налаштувань, файловий менеджер, Wi-Fi, MP3-плеєр і прості 2D-ігри. Є навіть підтримка мультиплеєра через сервер. Використовує графічний драйвер Arduino_GFX — більше вбудованих шрифтів (зокрема кирилиця) і більше гнучкості. Запускається прямо з KeiraOS як .bin-файл. Один нюанс: збирається через Pioarduino і несумісна зі звичайним PlatformIO.
Корисні посилання: гілка на сервері.

Вигляд меню Pixeler на Лілці

вигляд інтерфейсу Pixeler

Вигляд меню Pixeler

Lilka Oxide — це переосмислення старого проєкту Rustilka та спосіб писати прошивки на Rust замість C++. Встановлюється окрема прошивка з rust.lilka.dev, і далі застосунки пишуться на Rust із доступом до екрана, кнопок та іншого заліза.

Підхід працює інакше, ніж KeiraOS. KeiraOS — повноцінна система з меню застосунків. Lilka Oxide ближча до Arduino: ваш код — це і є вся прошивка. Щоб запустити іншу — треба перепрошити пристрій.

Технічно під капотом — esp-hal (напівофіційна бібліотека від інженерів Espressif) і embedded-hal, яка уніфікує доступ до заліза через трейти Rust. Це означає, що бібліотека для якогось датчика (наприклад, BME280) легко підключається до будь-якого мікроконтролера, що підтримує embedded-hal.

Замість FreeRTOS використовується embassy — асинхронний фреймворк. Програма не блокується, чекаючи завершення довгих операцій, а паралельно виконує інші задачі. У поєднанні з двома ядрами ESP32-S3 виходить швидко.

Підхід радше для тих, хто вже знайомий з Rust. C++ SDK простіший і працює «з коробки» — добрий старт для початківців. Lilka Oxide сучасніша й гнучкіша (безпека пам’яті, асинхронність), але складніша в налаштуванні. Почати, проте, легко: проєкт генерується зі шаблону зі стандартними опціями, які потім можна нарощувати під свої потреби.

Почати, проте, легко: проєкт генерується зі шаблону. Стандартний набір опцій — буфер дисплея 8092, шина SPI RefCellBus, без Alloc, без async (однопотоковий код), без ініціалізації радіо. Це найменше, з чого зручно стартувати, а далі нарощувати під свої потреби.

Корисні посилання: гілка на сервері, підготовка середовища розробки, підготовка проєкту.

Прошивка Lilka Oxide на Лілка

ESPHome LVGL — перетворює пристрій на частину розумного дому. Побудована на платформі ESPHome, де пристрій описують простим і зрозумілим конфігом у форматі YAML замість складного коду, а інтерфейс малюється фреймворком LVGL прямо на екрані. Лілку навіть офіційно внесли до списку підтримуваних плат ESPHome.

На екрані можна показувати стан пристроїв із Home Assistant (наприклад, увімкнено бойлер чи ні) та рівень батареї. Прошивка вміє Wi-Fi, оновлення «по повітрю» (OTA), веб-інтерфейс, виявлення в мережі через mDNS, інтеграцію з Home Assistant через Native API, роботу як Bluetooth-проксі і навіть звук через I2S.

Корисні посилання: GitHub проєкту, сторінка на ESPHome Devices.

Приклад прошивки ESPHome для керування розумним будинком ESPHome

PICO-8 — популярна «фентезі-консоль» для навчання й створення ігор. Вона навмисно обмежена, щоб простіше було зрозуміти основи розробки ігор. Ігри виглядають ретро, але це не емуляція — це окреме середовище зі своєю графікою, звуком і мовою на основі Lua. (PICO-8 належить Lexaloffle Games; PicoPico — неофіційний плеєр, не пов’язаний з ними.)

PicoPico — це реалізація ідеї PICO-8 для ESP32: вона дає запускати ігри в стилі PICO-8 прямо на Лілці без комп’ютера. Усередині — власний рушій Lua й полегшений рендер графіки. Реалізовано не все, тож деякі ігри працюють із обмеженнями чи без звуку, але демки й навіть відома Celeste вже запускаються. Збирається через PlatformIO і прошивається як звичайний .bin. Детальніше в окремому відео.

Запуск гри Celeste на Лілка через прошивку PicoPico

Retro-go перетворює Лілку на повноцінну ретро-ігрову консоль. Проєкт спершу створили для консолі ODROID-GO, незвичної тим, що в її серці — мікроконтролер ESP32, а не звичайний процесор. Саме тому Retro-go вдалося адаптувати й під Лілку. Ви отримуєте зручний лаунчер і набір емуляторів: NES, SNES, Game Boy, Sega, NEC, Coleco, Atari Lynx — і навіть DOOM. Прошивка поширюється як образ .img, тож встановлюється через веб-флешер або esptool.

Інтерфейс прошивки Retro-go

BLE Controller перетворює пристрій на універсальний Bluetooth-контролер у трьох режимах: геймпад, миша і клавіатура. Тобто Лілку можна використовувати не лише як консоль, а як повноцінний пристрій вводу для комп’ютера чи телефона. У режимі геймпада — хрестовина й шість кнопок; у режимі миші хрестовина рухає курсор, A/B — кліки; клавіатура має екранну розкладку з перемиканням мов. Заряд показується на екрані й передається по Bluetooth. Режими перемикаються утриманням START і SELECT. Встановлюється через веб-флешер або esptool. Відео.

Прошивка BLE Controller на Лілці

Serial Modem перетворює Лілку на справжній WiFi-модем у дусi 90-х. Він сумісний із командами Hayes (AT-команди), тож класичні комп’ютери можуть виходити в інтернет через звичайний RS232 — як колись через dial-up. Підключення — через перетворювач RS232-TTL (наприклад MAX3232). Далі «набираєте» не телефонні номери, а одразу хости й порти — скажімо, BBS-сервери чи Telnet. Підтримується повний стек PPP, тож навіть Windows 95 може вийти в мережу: достатньо набрати «777». Є також передавання файлів по XMODEM/YMODEM і веб-інтерфейс для налаштувань.

Прошивка Serial Modem на Лілці

Приклад використання старого комп'ютера, прошивки Serial Modem та пошуковика Frog Find

Game Boy Printer перетворює Лілку на емулятор принтера Game Boy 🖨️. Якщо у вас є Game Boy Camera або гра з підтримкою принтера, Лілка прийме від неї зображення, покаже на екрані й збереже на SD-картку у форматі PNG. Працює у двох режимах: Direct Mode (приймає зображення в реальному часі по кабелю) і Web Mode (додає веб-інтерфейс — переглядаєте й завантажуєте картинки з браузера). Для під’єднання потрібен кабель Game Boy Link і перетворювач рівнів 5V → 3.3V — він обов’язковий, бо пряме під’єднання спалить ESP32. Лінії підключаються до пінів 21, 48 і 47.

Приклад використання прошивки Game Boy Printer

Приклад надрукованих фото за допомогою прошивки Game Boy Printer

Lilka AI перетворює пристрій на портативного AI-асистента з підтримкою OpenAI 🤖. Можна вести повноцінні розмови з ChatGPT прямо на пристрої, зі збереженням історії в межах сесії. Є два режими: чат (на екрані з’являється віртуальна клавіатура — пишете повідомлення й читаєте відповідь) і Serial (спілкуєтеся через термінал по RS-232). Для роботи потрібен ключ OpenAI API; ключ, модель і Wi-Fi налаштовуються через вбудований веб-інтерфейс.

Інтерфейс прошивка Lilka AI

Приклад Serial режиму прошивки Lilka Ai

Lilka Desktop Monitor перетворює Лілку на маленький бездротовий монітор для вашого комп’ютера. Дозволяє тренслювати усе що відбується на екрані вашого комп’ютера прямо на 1.69″ TFT-дисплей Лілки в реальному часі через WiFi, і робить це без зайвих рухів — усе акуратно інтегровано з KeiraOS і запускається як звичайний .bin.

Прошивка Lilka Desktop Monitor

Mpeg Video Streaming — це ще один спосіб транслювати відео з комп’ютера на Лілку по WiFi, але вже через MJPEG. Ланцюжок простий: на комп’ютері GStreamer захоплює екран, масштабує його під дисплей (280×240) і кодує в JPEG-кадри; ті летять по TCP через WiFi на ESP32-S3, де бібліотека TJpgDec декодує їх у реальному часі, а Arduino_GFX малює на дисплеї ST7789. По суті — невеличкий бездротовий екран, що працює на потоці окремих кадрів.

Прошивка Mpeg Video Streaming

Li’l Video — невеличкий відеоплеєр, що дозволяє дивитися відео зi звуком прямо на пристрої. Відео зберігаються на SD-картці у спеціальному форматі, придатному для скромного заліза ESP32. Щоб почати, кладете готовий файл player.bin у теку Video, а для кожного відео створюєте окрему підтеку з двома файлами — окремо відео- й аудіодоріжка. Відео треба заздалегідь підготувати безкоштовною програмою ffmpeg — вона конвертує звичайний mp4 у формат, який розуміє Лілка. Керування просте: ↑↓ — вибір відео, A — старт; під час відтворення ті самі кнопки регулюють гучність. Відео.

Прошивка Li'l Video

Bruce — прошивка, що перетворює пристрій на інструмент для тестування безпеки мереж і систем. Підтримує багато можливостей: атаки на WiFi, Bluetooth, радіочастоти, RFID, інфрачервоне керування тощо. Її задумали як доступнішу альтернативу Flipper Zero, а підтримку для Лілки додали ентузіасти спільноти. Встановлюється дуже просто — через веб-флешер прямо на сайті bruce.computer, нічого додатково ставити не треба. Важливо: Bruce — лише для легального тестування власних пристроїв і мереж або з дозволу власника.

запуск Bruce Firmware на Лілці

результат роботи одного з застосунків Bruce Firmware на Лілці

Doom для Лілки. Це порт класичної гри Doom (1993) на Лілку, що працює на ESP32-S3. Порт неофіційний: Doom належить id Software, і проєкт із ними не пов’язаний. Найцікавіше тут технічне — розробники взяли модифікований рушій Doom Generic і вмістили весь основний код гри приблизно в 400 КБ оперативної пам’яті, а решту навантаження винесли в PSRAM завдяки динамічній алокації. Звук теж підтримується — через I2S та п’єзодинамік.

Перед запуском варто врахувати один момент: сам порт не містить ресурсів гри, тож додатково потрібен WAD-файл. Можна або спіратити оригінальний doom.wad, Найлегше і не дорого його купити на GOG.com, можна використати Shareware версію Покладіть файл у корінь SD-картки — і можна грати.

Gif анімація Doom на Лілці

Doom + Doom2 на GOG

Таймер даркруму — професійний таймер для фотопроявочної кімнати (даркруму). Він веде вас через усі етапи проявлення (проявник, стоп, фіксаж, промивка): до трьох пресетів по десять кроків, з автоматичним перемиканням і звуковими підказками — сигнал на старті, мітки кожні 30 секунд і зворотний відлік останніх 10, тож на екран можна не дивитися. Є окремий секундомір для тесту Dmax, двомовний інтерфейс (українська/англійська), регульовані яскравість і гучність, а пресети зручно редагувати з телефона через вбудований WiFi веб-інтерфейс.

Прошивка таймер для даркруму

І це ще не все — список постійно зростає, бо Лілка відкрита для всіх. Тут немає поділу на розробників і користувачів: будь-хто може взяти приклад, змінити пару рядків — і ось уже з’являється щось своє. Тож не лишайтеся осторонь — можливо, саме вашого застосунку бракує цьому списку

Як завантажити прошивку в Лілку?

Я розповів про всі ці чудові прошивки — KeiraOS і цілий десяток альтернатив. Але виникає логічне запитання: а як, власне, завантажити прошивку на сам пристрій? 🤔

Спершу маленьке уточнення. Прошивка буває у двох виглядах: готовий скомпільований файл (.bin або .img), який просто заливають на пристрій, або вихідний код, який спершу треба зібрати на комп’ютері, щоб отримати файл для прошивання. Способів кілька — від найпростішого до «для просунутих».

⚡ Lilka Flasher Найпростіший спосіб — офіційний веб-флешер за адресою flasher.lilka.dev. Він працює прямо в браузері, нічого встановлювати не треба, а прошивка передається на пристрій через USB-кабель.

Флешер дозволяє встановити офіційну KeiraOS — з вибором мови, української чи англійської. Просто під’єднайте Лілку кабелем, виберіть файл, натисніть «Connect and Flash» і оберіть COM-порт. Один важливий момент: сайт використовує Web Serial API, тож знадобиться Chrome, Edge або Opera — Firefox не підійде.

Інтерфейс Lilka flasher

🌐 Keira Web Manager (OTA по Wi-Fi) Про Keira Web Manager я докладніше розповідаю в розділі про передачу файлів. Але коротко нагадаю, що це сервіс KeiraOS, що доступний за IP адресою Лілки, чи іменем lilka.local Він вміє і завантажувати прошивки Прямо в браузері можна оновити прошивку «по повітрю» (OTA) — просто перетягніть .bin-файл у вікно. Тут є нюанс: підтримується лише KeiraOS, і потрібен non-merged .bin-файл із релізів на GitHub, а не «склеєний» (merged).

завантаження прошивки через Keira Web Manager

Є й приємна дрібниця: прошивку можна запустити прямо з браузера — натисніть іконку ракети 🚀 поряд із .bin-файлом, підтвердіть «Boot this file via Multiboot?», і вгорі з’явиться смужка прогресу, поки Лілка вантажить новий образ. Щоправда, з досвіду: деякі прошивки можуть просто не запускатися — і тоді вони не стартують ні з Web Manager, ні з файлового менеджера на самому пристрої.

завантаження прошивки через Keira Web Manager

💻 Esptool CLI. Esptool — офіційна утиліта від Espressif, тобто компанії, що робить чип ESP32, на якому працює Лілка. Працює через командний рядок: відкриваєте термінал, вводите команду — і прошивка заливається на пристрій по USB.

Цікаво, що esptool насправді працює «під капотом» майже всіх інших інструментів — PlatformIO і навіть деяких веб-флешерів, просто там він схований за графічним інтерфейсом. Тут же ви взаємодієте з ним напряму, що дає більше гнучкості: можна залити будь-який .bin-файл, стерти пам’ять пристрою або зробити резервну копію поточної прошивки. Але доведеться трохи призвичаїтися до командного рядка.

прошивання через esptool у терміналі

🌍 Esptool Web. Якщо хочете прошити сторонню прошивку, є веб-версія від самих Espressif під назвою Esptool Web по суті, той самий esptool, що працює в браузері. Нічого встановлювати не треба, працює по USB і підтримує будь-який .bin-файл. Як і Lilka Flasher, вимагає Chrome, Edge або Opera.

esptool у браузері (esptool-js)

🛠️ PlatformIO (збірка з вихідного коду) Якщо ви пишете власний застосунок або хочете зібрати KeiraOS із вихідного коду — використовуйте PlatformIO у Visual Studio Code. Клонуєте репозиторій, відкриваєте проєкт, під’єднуєте Лілку кабелем (важливо: лише Type-C → Type-A; кабелі USB 3.0 і Type-C → Type-C не підтримуються), переводите її в режим завантажувача, утримуючи кнопку SELECT під час увімкнення — і PlatformIO збирає та заливає прошивку. Потім натискаєте RESET, і Лілка стартує з новою прошивкою. Детальніше в цій статті.

Приємна деталь: під час першого прошивання разом із прошивкою встановлюється завантажувач (bootloader). Після цього вже не треба вручну тримати SELECT — PlatformIO сам переводитиме Лілку в режим завантажувача при кожному наступному Upload.

збірка та заливання KeiraOS у PlatformIO

📁 Запуск прошивки з File Manager KeiraOS уміє запускати .bin-файли прямо з SD-картки — без кабелю й перепрошивання. Просто киньте файл на картку, відкрийте File Manager у KeiraOS і запустіть звідти. Після вимкнення й повторного ввімкнення Лілка повертається до KeiraOS — наче нічого й не було. Це дуже зручно, коли хочеться просто спробувати якусь прошивку чи скористатися нею один раз, не проходячи через повне перепрошивання.

запуск .bin-файлу з File Manager на Лілці

Як передати файли на Лілку

Якщо ж ви пишете на .lua, або .mjs варто знати як передати його на SD картку Лілки. Найочевидніший спосіб: вийняти картку, під’єднати її до комп’ютера через usb card reader і перекинути файли. Працює, але щоразу виймати картку — довго й незручно. На щастя, KeiraOS має два бездротові способи роботи з файлами — без жодного кабелю чи картрідера

USB-A Card Reader

🌐 Keira Web Manager — це вбудований у KeiraOS веб-сервіс. Достатньо ввімкнути його у сервісах і відкрити браузер на будь-якому пристрої в тій самій Wi-Fi-мережі — і ви одразу отримуєте зручний файловий менеджер прямо у вкладці браузера. До речі, IP-адресу запам’ятовувати не треба: завдяки mDNS Лілка доступна за адресою lilka.local.

Що він уміє:

— переглядати файли і скриншоти з SD-картки прямо в браузері;

— завантажувати файли на пристрій і вивантажувати їх на комп’ютер чи телефон;

— копіювати файли між теками;

— оновлювати прошивку «по повітрю» (OTA) простим перетягуванням: киньте .bin-файл у вікно браузера — і все.

Окремо варто похвалити дизайн — чистий, інтуїтивний і просто приємний. Як на веб-інтерфейс, що працює на мікроконтролері, це приємна несподіванка

головний екран Keira Web Manager

Інтерфейс Keira Web Manager

📡 FTP Вбудований у KeiraOS FTP-сервер дозволяє під’єднатися до Лілки як до звичайного файлового сервера. Підійде будь-який FTP-клієнт — і на комп’ютері, і на телефоні. Щоб його увімкнути потрібно перейти в налаштування >> сервіси >> FTP. Також потрібно обрати FTP клієнт. На комп’ютері чудовий вибір — FileZilla: безкоштовний, простий і перевірений роками. На мобільному варіантів багато, наприклад FTP Client Pro для iOS чи AndFTP для Android. Після під’єднання ви отримуєте повний доступ до файлової системи SD-картки: завантаження, вивантаження, створення тек — як зі звичайним сервером.

FTP сервіс на Лілці

під’єднання до Лілки через FileZilla

📋 Pastebin. У розділі Applications KeiraOS є ще один зручний інструмент — застосунок Pastebin. Він дозволяє витягувати код прямо з pastebin.com: вводите посилання або ID паста — і скрипт миттєво опиняється на пристрої. Ідеально, щоб швидко спробувати Lua-сніпет, який знайшли в інтернеті чи отримали від друга — без комп’ютера й кабелів, прямо з консолі

застосунок Pastebin на Лілці

Розробка за допомогою ШІ

Тема використання ШІ в програмуванні серед розробників суперечлива. Але по перше я не розробник, а по друге це чудовий інструмент для навчання мовам програмування, розуміння принципів як працюють застосунки і взагалі чудовий інструмент для творчості. Моя порада, навіть якщо ШІ пише для вас застосунок, спробуйте розібратись у деталях: як змінити розмір шрифту, цей колір, механіку, тощо. Це цікаво. Я люблю використовувати Claude. Також з документації Lua зробив артефакт. Артефакти дозволяють легко видозмінювати, або використовувати документ. Все що треба зробити — натиснути кнопку customize. Аналогічно зробив артефакт з mJS документації; Також роблю презентацію з програмування на Lua. Вона ще не повністю готова, але з часом дороблю її.

Артефакт Claude, який можна змінити під себе

Це не реклама claude, подібні можливості є і в Gemini і в ChatGPT, використовуйте те що вам більше підходить. Потім копіюю усю документацію Лілки і приклади в окремий docx, чи txt докуменет. Згодовую його ШІ та прошу ознайомитись. Після думаю над ідеєю що саме я хочу написати гру чи застосунок. Які їх особливості? Може це має бути головоломка, або підключити якийсь сенсор та обробляти дані з нього.

Написання застосунків на Lua

Lua — одна з тих мов програмування, що читається легко і природно. Код виглядає майже як звичайний текст — не доводиться боротися зі складними правилами, щоб зрозуміти, що відбувається. Але що робить Lua особливо цікавою саме на Лілці — це повна відсутність компіляції ⚡ Пишеш код, кладеш файл на microSD-картку і запускаєш. Все. Хочеш спробувати нову гру, маленьку утиліту чи підключити сенсор? Просто скопіював файл — і готово. Жодних додаткових інструментів, прошивок чи очікувань — результат одразу в руках.

Лілка надає повний набір модулів для роботи з усією апаратною частиною. На екрані малюємо через display, стан кнопок зчитуємо через controller, звуки і мелодії програємо через buzzer. Якщо потрібні ресурси з картки — є resources і sdcard. Для математики й геометрії — відповідні модулі math і geometry. Хочете підключити зовнішній сенсор — gpio дає доступ до пінів, а serial — до UART. Також є wifi для мережі, http для запитів, crypto для шифрування і state для збереження стану програми між запусками. Усі ці модулі підвантажуються автоматично — нічого імпортувати вручну не потрібно.

Для Lua підходить будь-який текстовий редактор, але найзручніший — VS Code: він безкоштовний і має розширення для Lua з підсвічуванням синтаксису та автодоповненням. Sublime Text і Notepad++ теж популярні, якщо хочеться чогось легшого. Спеціально для Лілки є підтримка Live Lua — можна надсилати код прямо на пристрій через USB, не виймаючи карту, що дуже зручно під час розробки.

Застосунок повтори послідовність

Застосунок повтори послідовність. Наскільки довгу послідовність ви здатні запам’ятати? На екрані чотири фігури — кожна прив’язана до своєї кнопки напрямку, кольору та музичної ноти. Натискаєш A — гра підсвічує одну фігуру і програє її ноту. Завдання: повторити стрілкою, що відповідає тій фігурі. Влучив — послідовність росте на одну ланку. Помилився — на екрані з’являється твій рахунок і найкращий результат, а через дві секунди гра скидається.

Уся логіка живе у lilka.update(delta) і організована як машина станів з п’яти етапів: IDLE → SHOW → INPUT → WIN/LOSE. Стан SHOW через таймер зворотного відліку та змінну lit підсвічує фігури по одній з викликами buzzer.play() між ними. INPUT крок за кроком звіряє кожне натискання кнопки з масивом збереженої послідовності.

Функція lilka.draw() нічого не знає про логіку гри — вона просто читає поточний стан і lit і малює відповідно. Кожен кадр екран очищається і перемальовується з нуля. Код застосунку.

Застосунок повтори послідовність

Застосунок вгадай число використовує math.random — стандартну математичну функцію Lua — щоб на старті вибрати випадкове ціле число від 0 до 100. Не потрібно вигадувати власну логіку генерації: Lua усе робить сама.

Далі підхоплює архітектура Лілки: Keira автоматично викликає lilka.update() 30 разів на секунду для обробки натискань, а одразу після — lilka.draw() для відмалювання. Тож гра реагує на UP/DOWN миттєво, без жодних ручних затримок. Гравець коригує своє припущення, тисне A — і програма звіряє його з загаданим числом, відповідаючи «Higher!» або «Lower!»

Коли число вгадано, на екрані з’являється результат: скільки спроб знадобилось і цікавий факт про це число — якщо такий є у вбудованому словнику фактів. Натискання A починає нову гру з новим випадковим числом. Код застосунку.

Lua застосунок вгадай число

Таймер веде зворотний відлік і програє мелодію, коли час вийшов. На початку програми задаємо кольори і змінні — fields зберігає три числа, які налаштовуються кнопками (години, хвилини, секунди), cursor вказує, яке поле зараз активне, а running і finished працюють як вимикачі — або true, або false.

Lua застосунок таймер

Уся логіка відбувається у lilka.update, що викликається близько 30 разів на секунду. Саме тут перевіряються кнопки і йде відлік: на кожному кадрі від remaining віднімається delta — невеличкий шматочок часу. Коли remaining досягає нуля, програється мелодія і програма переходить у стан finished. Кнопки UP/DOWN змінюють значення поля з автоматичним перенесенням через оператор % — після 59 секунд воно повертається до 0. За візуальну частину відповідає lilka.draw — щокадру очищає екран і перемальовує все з нуля. Великі цифри по центру, жовта смужка під активним полем показує, що саме редагується, а підказки для A та B завжди видно знизу. Код застосунку.

Усі три застосунки з цієї статті були написані за один вечір, і кожна з них уже робить щось реальне: відлічує час, загадує число, перевіряє пам’ять. Lua на Лілці дозволяє перетворити ідею на робочий застосунок дуже швидко — без додаткових інструментів і складного налаштування.

Підключення сенсорів до Лілки за допомогою Lua

Коли починаєш працювати з сенсорами, майже одразу натрапляєш на дивні терміни High і Low. Звучить складно, а насправді все набагато простіше. Уявіть звичайний вмикач світла: він або ввімкнений, або вимкнений. В електроніці High і Low означають саме це.

High означає, що на піні є напруга — іншими словами, сигнал «увімкнений» ⚡ Для мікроконтролера це зазвичай логічна одиниця. Low, навпаки, означає що напруги немає (або вона дуже мала), що відповідає логічному нулю. Тож будь-який простий сенсор спілкується з пристроєм цією самою мовою: або «так», або «ні».

На Лілці це відчувається особливо інтуїтивно. Натиснули кнопку — отримали High. Відпустили — Low. Сенсор руху вловив рух — знову High. Нічого не відбувається — Low. Хоча ці терміни і ростуть з електротехнічних понять, на старті достатньо думати про них просто як про два стани, між якими постійно перемикається ваш пристрій.

Ця проста ідея — фундамент для більшості наступних прикладів. Як тільки ви її засвоїте, робота з сенсорами перестає здаватися складною і починає більше скидатися на збирання конструктора, де все логічно і передбачувано.

GPIO, піни та порт розширення

Розрізнення піна й GPIO найбільше стає у пригоді на етапі коду. Пін і GPIO це різні речі. Пін це порядковий номер фізичного контакту, а GPIO логічна адреса (віртуальна сутність, назва у коді). Тому наприклад на схемі консолі кнопка C це пін 18, а GPIO 10

У ESP32-S3 є так звана Pin Matrix — гнучка система, що дозволяє призначати різні функції (I2C, SPI, UART тощо) майже будь-якому фізичному піну. Кожен GPIO можна налаштувати як цифровий вхід чи вихід, як ADC (аналогово-цифровий перетворювач), DAC (цифро-аналоговий), вихід PWM (широтно-імпульсна модуляція) або як один із послідовних інтерфейсів.

Коротко про протоколи:

— SPI (Serial Peripheral Interface) — швидкий протокол для під’єднання одного периферійного пристрою: дисплея, SD-картки. Чотири лінії: MOSI, MISO, SCK, CS.

— I2C (Inter-Integrated Circuit) — повільніший, зате дозволяє під’єднати багато пристроїв до однієї шини (лише дві лінії: SDA і SCL), кожен зі своєю унікальною адресою. Чудово підходить для сенсорів.

— UART (Universal Asynchronous Receiver-Transmitter) — послідовний протокол для зв’язку з комп’ютером чи іншим пристроєм. Дві лінії: TX і RX.

Порт розширення на Лілці — це набір вільних GPIO-пінів, виведених на роз’єм. Саме сюди під’єднують зовнішні модулі: сенсори, сервоприводи, реле, ультразвукові далекоміри тощо.

Розпіновка консолі

Пін

Функція

46

Живлення дисплея (вихід GPIO)

1

I2S LRCLK (аудіо)

42

I2S BCLK (аудіо)

2

I2S DOUT — вивід аудіо

3

ADC — напруга батареї

18

SPI CLK — дисплей

17

SPI MOSI — дисплей

15

ST7789 DC

7

ST7789 CS

38

Кнопка Up

41

Кнопка Down

39

Кнопка Left

40

Кнопка Right

5

Кнопка A

6

Кнопка B

10

Кнопка C

9

Кнопка D

4

Кнопка Start

0

Кнопка Select

Керування реле

Цей застосунок вмикає та вимикає реле CW-020 кнопкою A на Лілці. Реле — це електричний вмикач, який може керувати зовнішніми пристроями: лампою, вентилятором тощо. Модуль має low-level trigger — він активується, коли пін IN отримує низький сигнал (~0V). Саме тому програма одразу при запуску виставляє HIGH, щоб реле випадково не спрацювало під час завантаження. Кнопка A перемикає реле, кнопка B вимикає реле і виходить з програми. На екрані видно поточний стан: зелене «State: ON» або червоне «State: OFF» 🟢🔴

Застосунк керування реле CW-20

Підключення:

Лілка

Реле CW-020

3.3V

VCC

GND

GND

12

IN

local relay_pin = 12
local relay_on = false

function lilka.init()
    gpio.set_mode(relay_pin, gpio.OUTPUT)
    gpio.write(relay_pin, gpio.HIGH)
end

function lilka.update(delta)
    local state = controller.get_state()

    if state.a.just_pressed then
        if relay_on then
            relay_on = false
            gpio.write(relay_pin, gpio.HIGH)
        else
            relay_on = true
            gpio.write(relay_pin, gpio.LOW)
        end
    end

    if state.b.just_pressed then
        gpio.write(relay_pin, gpio.HIGH)
        util.exit()
    end
end

function lilka.draw()
    display.fill_screen(display.color565(0, 0, 0))

    display.set_text_color(display.color565(255, 255, 255))
    display.set_cursor(10, 32)
    display.print("Relay control")

    if relay_on then
        display.set_text_color(display.color565(0, 200, 0))
    else
        display.set_text_color(display.color565(200, 0, 0))
    end
    display.set_cursor(10, 64)
    display.print(relay_on and "State: ON" or "State: OFF")

    display.set_text_color(display.color565(255, 255, 255))
    display.set_cursor(10, 100)
    display.print("A - toggle")
    display.set_cursor(10, 120)
    display.print("B - exit")
end

📡 Датчик відстані HC-SR04P

Цей застосунок вимірює відстань до об’єкта за допомогою ультразвукового сенсора HC-SR04P і виводить результат у сантиметрах на екран Лілки. Сенсор працює як кажан 🦇 — він посилає ультразвуковий імпульс і чекає на відлуння. Що далі об’єкт, то довше йде звук. Застосунок вимірює цей час і конвертує його в сантиметри.

Ілюстрація принципу роботи датчика відстані

Сама формула проста: відстань (см) = час × 34300 / 2. Число 34300 — це швидкість звуку в сантиметрах за секунду, а ділимо навпіл тому, що звук долає шлях двічі — туди до об’єкта й назад.

Ключова технічна складність — у Lua немає вбудованої затримки в мікросекундах, а сенсору саме така потрібна. Розв’язок простий: util.sleep(0.001) дає імпульс у 1 мс — це у 100 разів більше, ніж необхідний мінімум 10 мкс, і працює стабільно. Покази плавні завдяки усередненню восьми вимірювань і фільтру викидів — жодних раптових стрибків до 800 см Якщо сенсор не підключений, на екрані з’являється підказка зі схемою підключення. Кнопка B — вихід. Код застосунку.

Вигляд застосунку для виміру відстані

Підключення:

Лілка

HC-SR04P

3.3V

VCC

GND

GND

Pin 12

Trig

Pin 13

Echo

🚦 Світлофор

Цей застосунок працює з модулем тришарового світлофора — чорна плата з трьома великими світлодіодами: червоним, жовтим і зеленим 🔴🟡🟢 Кнопка A на Лілці вмикає або вимикає червоний світлодіод, кнопка B керує жовтим, кнопка D — зеленим, а кнопка C завершує програму.

На початку програма оголошує три змінні: led_pin_r = 21, led_pin_y = 47, led_pin_g = 48. Це зручні назви для пінів на роз’ємі розширення Лілки, до яких приєднані сигнальні дроти модуля. Замість того щоб писати скрізь числа 21, 47 і 48, ми даємо їм читабельні імена — r для червоного, y для жовтого, g для зеленого.

Пін — це фізичний контакт на платі Лілки 🔌 Кожен пін може працювати у двох режимах: видавати напругу назовні (OUTPUT) або вимірювати напругу ззовні (INPUT). Функція gpio.set_mode обирає режим. Функція gpio.writeвмикає або вимикає напругу. Якщо проводити аналогію — gpio.set_mode це наче найняти працівника і визначити йому роль: оратор (OUTPUT — видає сигнал) або слухач (INPUT — приймає сигнал). А gpio.write — це конкретна команда тому працівнику: gpio.LOW означає мовчи (0 вольт, світлодіод вимкнений), gpio.HIGH — говори голосно (3.3 вольти, світлодіод увімкнений)

Саме тому на початку програми gpio.set_mode викликається з gpio.OUTPUT для кожного піна — налаштовуємо його як вихід. Потім одразу викликається gpio.write з gpio.LOW — вимикаємо напругу. Це гарантує, що всі три світлодіоди вимкнені на старті програми.

Після налаштування програма заходить у нескінченний цикл while true do ♾️ Лілка постійно перевіряє стан кнопок по колу — дуже швидко, сотні разів на секунду. Кожного разу викликається controller.get_state() і перевіряється, чи натиснута конкретна кнопка.

Найцікавіший рядок — gpio.write(led_pin_r, 1 — gpio.read(led_pin_r)) Це простий математичний трюк. gpio.read зчитує поточний стан піна і повертає 1, якщо світлодіод увімкнений, або 0, якщо вимкнений. Потім 1 — 1 = 0 або 1 — 0 = 1. Тобто якщо світлодіод горів — він вимикається, якщо був вимкнений — вмикається. Одна кнопка одночасно і вмикає, і вимикає.

Кнопка C виконує команду break, що виходить з циклу і завершує програму 👋 Без неї цикл виконувався б нескінченно.

led_pin_r = 21
led_pin_y = 47
led_pin_g = 48

gpio.set_mode(led_pin_r, gpio.OUTPUT)
gpio.write(led_pin_r, gpio.LOW)
gpio.set_mode(led_pin_y, gpio.OUTPUT)
gpio.write(led_pin_y, gpio.LOW)
gpio.set_mode(led_pin_g, gpio.OUTPUT)
gpio.write(led_pin_g, gpio.LOW)
while true do
    if controller.get_state().a.pressed then
        gpio.write(led_pin_r, 1 - gpio.read(led_pin_r))
    end
    if controller.get_state().b.pressed then
        gpio.write(led_pin_y, 1 - gpio.read(led_pin_y))
    end
    if controller.get_state().d.pressed then
        gpio.write(led_pin_g, 1 - gpio.read(led_pin_g))
    end
    if controller.get_state().c.pressed then
        break
    end
end

Проста сенсорна кнопка

Цей застосунок — найпростіший приклад роботи з сенсором дотику, що поводиться як звичайна кнопка. Вона безперервно перевіряє стан одного піна, до якого підключений сенсор, і просто виводить результат на екран Лілки.

Коли ви торкаєтесь сенсора, на піні з’являється сигнал — це і є High. Програма зчитує цей сигнал і розуміє, що відбувся дотик, тож показує «TOUCHED» на екрані. Коли відпускаєте — сигнал зникає (Low), і на екрані з’являється «NOT TOUCHED».

Логіка тут максимально проста: лише два стани, що постійно змінюються залежно від того, торкаєтесь ви сенсора чи ні. Програма не зберігає історію, не рахує натискання, не додає жодних ускладнень — вона лише «дивиться» на пін прямо зараз і одразу показує результат.

Цей приклад наочно демонструє суть роботи з сенсорами: зчитуєш значення і реагуєш на нього. Це перший крок до складніших проєктів, де замість виведення тексту можна керувати світлодіодами, серво-моторами або навіть цілими системами розумного будинку.

touch_signal_pin = 12

gpio.set_mode(touch_signal_pin, gpio.INPUT)

function lilka.update()
    if controller.get_state().c.just_pressed then
        util.exit()
    end
end

function lilka.draw()
    display.fill_screen(display.color565(0, 0, 0))
    display.set_cursor(10, 32)
    local signal = gpio.read(touch_signal_pin)
    if signal == 1 then
        display.print("TOUCHED")
    else
        display.print("NOT TOUCHED")
    end
end

🌱 Датчик вологості ґрунту

Цей застосунок вимірює вологість ґрунту за допомогою Capacitive Soil Moisture Sensor v1.2 і виводить результат у відсотках на екран Лілки.

Вигляд застосунку з виміру вологості ґрунту

Існує два поширених типи датчиків вологості ґрунту. Резистивні датчики вимірюють опір між двома металевими щупами, встромленими в ґрунт — що більше вологи, то менший опір. Вони дешеві, але мають суттєвий недолік: металеві щупи іржавіють у вологому ґрунті вже за кілька тижнів. Ємнісні датчики вимірюють діелектричну проникність ґрунту без прямого контакту металу з вологою — тож служать набагато довше і є надійнішим вибором для постійного використання.

Сенсор підключається до аналогового піна на Лілці і повертає сире число від 0 до 4095 — це значення АЦП. Ємнісний сенсор має інвертовану логіку: що менше число, то більше вологи в ґрунті. Програма перетворює сире значення у відсотки за простою формулою, де DRY — значення в сухому повітрі (0%), а WET — у воді (100%). На екрані показуються і відсоток, і сире значення АЦП — корисно для калібрування під ваш конкретний модуль.

Покази плавні завдяки усередненню восьми вимірювань — число змінюється поступово, без раптових стрибків. На старті програма показує підказку з підключенням. A запускає вимірювання. B — вихід. Пін 13 використовується для аналогового зчитування. На ESP32-S3 перші 20 GPIO-пінів мають вбудований АЦП. Піни 12, 13 і 14 на роз’ємі розширення Лілки належать саме до цієї групи — тож це природний вибір для аналогових сенсорів. Код застосунку.

Підключення

Лілка

Sensor v1.2

3.3V

VCC

GND

GND

Pin 13

AOUT

Написання застосунків на mJS

mJS — це JavaScript-рушій від Mongoose OS. У порівнянні з Lua він трохи «важчий», зате дає знайомий синтаксис JavaScript — тієї самої мови, що працює на половині сучасного вебу. Готові JS-бібліотеки на кшталт React чи Next.js, на жаль, не підтримуються — лише базова мова та власні API Лілки. Для простих скриптів цього цілком достатньо.

А найкраще — ніякої компіляції, ніякого перепрошивання. Скрипт — це просто .js-файл на SD-картці: вибрав у меню і він запустився. Відредагував код, зберіг, запустив знову — увесь цикл займає секунди ⚡ Саме це робить Лілку чудовою платформою для експериментів і вивчення програмування.

Невелике уточнення перед тим, як зануритись: підтримка mJS — це досить свіже доповнення до KeiraOS, з’явилося у v2.6.5 і досі активно розвивається. У цілому все працює, але зрідка можна натрапити на дрібні шорсткості — наприклад, повідомлення error 2, що з’являється щоразу при виході зі скрипта. Воно безпечне, ні на що не впливає, просто закрийте його і працюйте далі. Нові можливості mJS і виправлення з’являються з кожним релізом, тож досвід стає все приємнішим

🐱 Cat App

Розберемо простий застосунок у стилі Bongo Cat — котика, який б’є лапками по столу, коли натискаєш кнопочки 🥁 Натиснув B — котик ляснув правою лапкою. D — лівою. Обидві одночасно — подвійний удар, як у справжнього мема. А якщо натиснути C — коротке моргання з глухим «бумк» у динамік 💭

Увесь «секрет» — чотири BMP-зображення на SD-картці: no.bmp (спокійний котик), left.bmp, right.bmp і both.bmp. Завантажуємо їх один раз на початку через resources.load_image(), і далі просто на кожному кадрі вирішуємо, яку картинку малювати зверху, дивлячись на стан кнопок.

Логіка настільки проста, що вся вміщається у десяток рядків: спочатку малюємо базове зображення (no), а потім поверх домальовуємо «варіант з лапкою» — якщо відповідна кнопка натиснута. Це типовий трюк з шарами: кожен кадр починаємо з чистої основи і нашаровуємо деталі зверху. Цікавий момент — buzzer.play(40, 100). Частота 40 Гц це вже майже на межі чутності, такий низький «бумк». Саме тому удари лапками звучать не як дзвінок, а як глухе «тиць-тиць» — ідеально для бонго.

mJS bongo cat app

А найкраще в цьому прикладі те, що це не лише застосунок — це шаблон. Замініть чотири BMP-файли на свої, і за 10 хвилин отримаєте власного персонажа: собаку, що висолоплює язика; робота, який моргає діодами; власне фото, що корчить міни на команду. Структура коду не зміниться жодним рядком — просто інші картинки. Саме тут mJS і виграє: не треба перепрошивати пристрій, не треба збирати проєкт. Поклав нові BMP на SD — і вже запускаєш нового персонажа ⚡

🌌 Starfield App

Відтворимо той класичний ефект польоту крізь космос — зорі проносяться повз камеру, яскравішають при наближенні і згасають у далечині 🚀 Чудовий приклад того, як ілюзію тривимірності можна побудувати з дуже простих складників.

Менші зірочки видно на екрані Лілки, але не видно на скріншоті.

Кожна зоря живе у трьох числах: sx, sy і sz. Перші два — її позиція на екрані, а sz — відстань до камери, тобто глибина. На кожному кадрі ми віднімаємо speed від sz, тобто зоря трохи наближається. Коли sz досягає нуля — зоря пролетіла повз камеру, і ми відправляємо її назад у далечінь зі свіжими випадковими координатами і максимальним sz ✨

Уся ілюзія 3D зводиться до одного рядка математики:

px = sx * 256 / sz + w / 2

Це називається перспективна проєкція — що ближча зоря (менший sz), то далі від центру екрана вона опиняється. Саме цей рух назовні від середини дисплея наше око читає як політ уперед крізь простір. Той самий трюк масштабує розмір і яскравість: близькі зорі стають яскравими крапками, далекі згасають до одного тьмяного пікселя. UP і DOWN змінюють швидкість у реальному часі.

Застосунок імітації польоту через космічний простір

А ось і родзинка: усе це — якихось 50 рядків коду, без жодного 3D-рушія, без бібліотек, з математикою не складнішою за множення і ділення. Лише три масиви і одна формула. Як тільки розумієш, як працює трюк — починаєш на ньому будувати: зробити зорі різнокольоровими, додати спалах гіперстрибка по натисканню A, перетворити крапки на космічні кораблі, що пролітають повз. Рушій уже є — лишилось дофантазувати решту.

🔴 Bouncing Ball App

Червона кулька літає по екрану, відбиваючись від стін, з ефектом вогняного хвоста за нею. Така собі базова «фізика без фізики» — найпростіший приклад того, як одне і те ж арифметичне правило, повторене 60 разів на секунду, створює відчуття живого руху.

Вигляд застосунку bouncing ball

Уся логіка зводиться до двох речей. Перша — рух: на кожному кадрі додаємо швидкість до позиції (x = x + vx), і кулька зміщується. Чим більша швидкість, тим помітніше стрибає — ніяких формул прискорення чи маси, лише два числа vx і vy. Друга — відбиття: коли кулька торкається стіни, інвертуємо знак відповідної швидкості (vx = -vx). Якщо рухалась праворуч — тепер ліворуч, і навпаки. Цей трюк — основа всіх ігор з польотом снарядів, від Pong до Breakout. Хвіст за кулькою — теж простий трюк: малюємо ще два кола трохи позаду основної позиції (x — vx * 2 і x — vx * 4), меншого радіуса й темніших кольорів. Чим швидше летить кулька, тим довшим і яскравішим виглядає хвіст, бо vx * 4 стає більшим зміщенням

D-pad змінює швидкість у реальному часі: стрілка вгору сповільнює вертикальний рух, вниз — прискорює; ліва і права стрілки роблять те саме по горизонталі. Можна розігнати кульку до шаленого темпу, або майже зупинити її в повітрі, спостерігаючи як хвіст стискається у точку. Код застосунку.

Та сама механіка лежить в основі половини всіх ігор — Pong, Arkanoid, Breakout, навіть звичайний скрінсейвер з логотипом DVD, що випадково вріжеться у ріг екрана 📐 Замінюй кульку на що завгодно: спрайт корабля, яблуко, м’яч для тенісу — і отримуєш цілком іншу гру з тим самим кістяком ⚡

🟠 DVD лого

Замінимо червону кульку на легендарний DVD-логотип і отримаємо той самий ностальгійний скрінсейвер з 2000-х 📺 Логіка руху і відбиття залишається тою самою — а от усе цікаве криється у роботі з картинкою.

mJS застосунок DVD лого

🖼️ Підключення картинок у mJS. У програму на Lilka можна додавати власні зображення — фото, лого, спрайти для ігор. Код для цього мінімальний:

let dvd = resources.load_image("dvd-white.bmp", colors.black);
display.draw_image(dvd, x, y);

Сам файл dvd-white.bmp має лежати в тій самій папці на SD-картці, що й .js-скрипт. Lilka знайде його за відносним шляхом автоматично.

🎨 Формат BMP, 24-bit. Lilka працює з простим BMP без стиснення — по 8 біт на кожен канал R, G, B. Не 1-bit чорно-білий, не 32-bit з альфою, саме 24-bit RGB. Перевірити готовий файл можна командою sips -g all dvd-white.bmp: у виводі має бути bitsPerSample: 8 і samplesPerPixel: 3.

✨ Прозорість — через колір, а не альфа-канал. BMP не підтримує прозорість напряму, тому Lilka робить її інакше: при завантаженні вказуєш «цей колір не малювати». Другий аргумент resources.load_image() — і є той transparencyColor. У прикладі вище чорний фон BMP стає прозорим, на екрані видно лише саме лого. Колір прозорості обирай такий, якого немає у самому зображенні: для білого лого — чорний фон, для чорного — білий.

📐 Оптимальний розмір. Дисплей Lilka — 240×280 пікселів. Орієнтири: 32×32 для дрібних об’єктів, 64×64 для головного персонажа гри, 128×128 і більше для центральних великих елементів. Для DVD-логотипа з пропорцією 2:1 чудово підходить розмір 240×108.

🪄 ImageMagick — універсальна утиліта командного рядка для роботи із зображеннями. Доступна для macOS (brew install imagemagick), Linux (apt install imagemagick) і Windows. Одна команда обрізає, інвертує, масштабує і конвертує у потрібний формат:

magick dvd.jpg -fuzz 10% -trim +repage -fill black -opaque white \
-resize 240x -type TrueColor BMP3:dvd-white.bmp

-trim прибирає білі поля навколо лого, -opaque white замінює білий фон чорним (щоб працювала прозорість), -resize 240x задає ширину зі збереженням пропорцій, а BMP3: гарантує саме той 24-bit BMP, який очікує Lilka 🎯

Що змінилось у коді порівняно з Bouncing Ball:

Замість одного радіуса r тепер дві змінні — dvdW і dvdH, бо логотип має різну ширину і висоту. Перевірка відбиття стала обчислюватися окремо для горизонталі і вертикалі: dvdW / 2 для лівої й правої стіни, dvdH / 2 для верхньої й нижньої. Замість трьох викликів fill_circle (кулька + два кола хвоста) тепер один display.draw_image(), який малює готову картинку. І ще одна дрібниця — координати для draw_image вказуються не до центру об’єкта (як у fill_circle), а до верхнього лівого кута, тому віднімаємо dvdW / 2 і dvdH / 2, щоб лого все одно «сиділо» по центру в точці (x, y).

// dvd_bounce.js — DVD logo bouncing around the screen
// Use D-pad to change logo speed. Press A to exit.
// Required: dvd-white.bmp on the SD card (24-bit BMP, e.g. 240x108).

let w = display.width;
let h = display.height;

// Load DVD logo. Black is treated as transparent.
let dvd = resources.load_image("dvd-white.bmp", colors.black);
let dvdW = 240;
let dvdH = 108;

let x = w / 2;
let y = h / 2;
let vx = 3;
let vy = 2;

let running = true;

while (running) {    // Update position    x = x + vx;    y = y + vy;
    // Bounce off walls    if (x - dvdW / 2 < 0 || x + dvdW / 2 > w) {        vx = -vx;        x = math.clamp(x, dvdW / 2, w - dvdW / 2);    }    if (y - dvdH / 2 < 0 || y + dvdH / 2 > h) {        vy = -vy;        y = math.clamp(y, dvdH / 2, h - dvdH / 2);    }
    // Draw    display.fill_screen(display.color565(20, 20, 60));    display.draw_image(dvd, math.round(x - dvdW / 2), math.round(y - dvdH / 2));
    // HUD    display.set_cursor(5, 15);    display.set_text_color(colors.white);    display.print("DVD Bounce | A=exit");
    display.queue_draw();
    // Input    let state = controller.get_state();    if (state.a.just_pressed) {        running = false;    }    if (state.up.pressed) {        vy = vy - 0.2;    }    if (state.down.pressed) {        vy = vy + 0.2;    }    if (state.left.pressed) {        vx = vx - 0.2;    }    if (state.right.pressed) {        vx = vx + 0.2;    }
    util.sleep(0.016);
}

Це той самий шаблон, що і cat.js — простий каркас, у який можна вкласти будь-яку картинку. Замінюй dvd-white.bmpна лого улюбленого гурту, фото улюбленця, аватарку друга — і за дві хвилини матимеш персональний скрінсейвер ⚡ І все ще лишається той легендарний момент очікування: коли логотип нарешті точно влучить у кут екрана 📐

🏓 Pong

Pong — гра, з якої колись почалась уся комп’ютерна індустрія. Atari запустила Pong у 1972 році, і саме він зробив відеоігри масовим явищем. Дві ракетки, м’ячик, чорний фон, біла розділова лінія посередині — і нічого більше не треба, щоб зачепити людей на десятиліття 🕹️

mJS застосунок Pong

Гравець керує лівою (синьою) ракеткою кнопками UP/DOWN, права (червона) рухається сама. М’ячик літає між ними, відскакуючи від ракеток і верхньої з нижньою стінок. Якщо суперник не встиг — ти отримуєш очко. Перший до... та ні, гра нескінченна, грайте скільки витримаєте 🏆

Половина коду — це знайома механіка з Bouncing Ball: м’яч рухається через bx + bvx, відбивається від стелі й підлоги через інверсію знаку швидкості. Ракетки додають усе цікаве поверх цієї бази.

🎯 Колізія м’яча з ракеткою. Коли м’яч торкається лівої ракетки, перевіряємо одночасно три умови: чи дістав до її x-координати (bx — br <= pw + 5), чи знаходиться на висоті ракетки (by >= p1y && by <= p1y + ph), і чи летить до неї назустріч (bvx < 0). Тільки якщо всі три виконуються — інвертуємо bvx і м’яч летить назад. Остання перевірка особливо важлива: без неї м’яч «застрягав би» в ракетці, безперервно міняючи напрямок.

⚡ Прискорення з кожним ударом. Після відбиття bvx додатково збільшується на 0.3. Тобто чим довше триває розіграш, тим швидше летить м’яч — і тим складніше встигнути за ним. Невелика деталь, що перетворює нудний обмін ударами на справжній двобій.

🤖 ШІ-суперник. Права ракетка має найпростіший AI у світі: на кожному кадрі вона дивиться на вертикальну позицію м’яча і рухається до нього зі швидкістю 3 пікселі за кадр. Це навмисно повільніше за швидкість гравця (4 пікселі) — інакше комп’ютер був би непереможним і грати стало б нецікаво. Зона «мертвої зони» ± 2 пікселя не дає ракетці тремтіти, коли м’яч точно навпроти неї.

📊 Лічильник очок. Якщо bx < 0 — м’яч вилетів за лівий край, плюс одне очко суперникові. Якщо bx > w — навпаки, очко гравцю. У будь-якому разі м’яч повертається в центр екрану зі стартовою швидкістю, і розіграш починається знову.

🎨 Чорний фон, біла розділова лінія, кольорові ракетки. Стилістично програма цитує оригінальний Pong 1972 року — мінімалізм, який нічим не зіпсуєш. Розділова лінія малюється циклом for, що ставить піксель кожні 10 одиниць висоти. Просто і ефектно ✨

// pong.js — Simple Pong game for one player vs CPU
// Left paddle: D-pad UP/DOWN. Press A to exit.
let w = display.width;
let h = display.height;

// Ball
let bx = w / 2;
let by = h / 2;
let bvx = 3;
let bvy = 2;
let br = 4;

// Paddles
let pw = 6;
let ph = 40;
let p1y = h / 2 - ph / 2; // Player (left)
let p2y = h / 2 - ph / 2; // CPU (right)
let pSpeed = 4;

// Score
let score1 = 0;
let score2 = 0;
let running = true;

while (running) {
    // --- Input ---
    let ctrl = controller.get_state();
    if (ctrl.a.just_pressed) {
        running = false;
    }
    if (ctrl.up.pressed) {
        p1y = p1y - pSpeed;
    }
    if (ctrl.down.pressed) {
        p1y = p1y + pSpeed;
    }
    p1y = math.clamp(p1y, 0, h - ph);
    // --- CPU AI ---
    let cpuTarget = by - ph / 2;
    if (p2y < cpuTarget - 2) {
        p2y = p2y + 3;
    }
    if (p2y > cpuTarget + 2) {
        p2y = p2y - 3;
    }
    p2y = math.clamp(p2y, 0, h - ph);
    // --- Ball physics ---
    bx = bx + bvx;
    by = by + bvy;
    // Top/bottom bounce
    if (by - br < 0 || by + br > h) {
        bvy = -bvy;
        by = math.clamp(by, br, h - br);
    }
    // Left paddle collision
    if (bx - br <= pw + 5 && by >= p1y && by <= p1y + ph && bvx < 0) {
        bvx = -bvx;
        bvx = bvx + 0.3;
        bx = pw + 5 + br;
    }
    // Right paddle collision
    if (bx + br >= w - pw - 5 && by >= p2y && by <= p2y + ph && bvx > 0) {
        bvx = -bvx;
        bvx = bvx - 0.3;
        bx = w - pw - 5 - br;
    }
    // Score
    if (bx < 0) {
        score2 = score2 + 1;
        bx = w / 2;
        by = h / 2;
        bvx = 3;
        bvy = 2;
    }
    if (bx > w) {
        score1 = score1 + 1;
        bx = w / 2;
        by = h / 2;
        bvx = -3;
        bvy = -2;
    }
    // --- Draw ---
    display.fill_screen(colors.black);
    // Center line
    for (let dy = 0; dy < h; dy = dy + 10) {
        display.draw_pixel(w / 2, dy, display.color565(60, 60, 60));
    }
    // Paddles
    display.fill_rect(5, math.round(p1y), pw, ph, colors.cyan);
    display.fill_rect(w - pw - 5, math.round(p2y), pw, ph, colors.red);
    // Ball
    display.fill_circle(math.round(bx), math.round(by), br, colors.white);
    // Score
    display.set_cursor(w / 2 - 40, 15);
    display.set_text_color(colors.cyan);
    display.print(score1);
    display.set_cursor(w / 2 + 30, 15);
    display.set_text_color(colors.red);
    display.print(score2);
    display.queue_draw();
    util.sleep(0.016);
}

Pong — ідеальна стартова точка для будь-якого ігрового експерименту. Хочеш Arkanoid? Заміни праву ракетку на стіну з цеглинок, і кожне попадання м’яча ламає одну з них. Хочеш Air Hockey на двох? Прибери AI-логіку правої ракетки і додай керування другому гравцю через інші кнопки. Хочеш футбол? Додай ще одну «ракетку» і ворота 🥅 Кістяк уже є — а далі тільки фантазія.

Як поділитися своїм застосунком зі спільнотою

Отже, ви написали свій перший застосунок для Лілки 🎉 Що далі? А далі найприємніше — нею можна поділитися з усіма! Готовий застосунок можна опублікувати в каталозі за адресою catalog.lilka.dev, і будь-хто зможе встановити його прямо з консолі.

Для цього знадобиться акаунт на GitHub. Якщо ви ніколи про нього не чули — нічого страшного. GitHub — це щось на кшталт YouTube, тільки для коду: люди викладають туди свої проєкти, діляться ними й допомагають одне одному. А якщо у вас ще немає жодного репозиторію (так називають «теку» з проєктом) і ви ніколи з цим не працювали — не хвилюйтеся, усе можна зробити прямо в браузері, без встановлення жодних програм. Зараз покажу крок за кроком. До речі, цікавий факт: GitHub побудований на Git — системі, яку створив Лінус Торвальдс, автор ядра Linux ⚡

1. Створюємо власний репозиторій

Спершу — реєстрація. Зайдіть на github.com і створіть безкоштовний акаунт. Після цього створіть репозиторій для коду вашого застосунку: натисніть New repository, придумайте назву, поставте галочку Add a README file і натисніть Create repository. Готово — у вас є власний репозиторій!

Cтворення нового репозиторію на GitHub

Створення нового репозиторію GitHub

2. Додаємо файли до репозиторію

Тепер додамо опис і сам код. Щоб відредагувати файл README.md, натисніть на іконку олівця у правому верхньому куті й додайте короткий опис застосунку. Щоб зберегти, натисніть зелену кнопку Commit changes. README пишеться у форматі Markdown — це проста й зрозуміла мова розмітки тексту (заголовки, списки, жирний — усе кількома символами).

Опис можна написати самотужки, скористатися онлайн-редактором на кшталт markdownlivepreview.com чи stackedit.io, або попросити ШІ згенерувати Markdown-опис за вашим кодом.

Далі додамо сам код. Натисніть Add file → Create new file, угорі введіть назву файлу — наприклад timer.lua — і вставте свій код. Знову натисніть Commit changes. Або другий варіант завантажити готовий файл Add file → Upload files

Завантаження файлів в репозиторій

3. Робимо форк репозиторію каталогу

Щоб додати застосунок у каталог, треба внести зміни в його репозиторій. Але напряму редагувати чужий репозиторій не можна — спершу роблять його копію (форк), вносять зміни у себе, а потім надсилають їх на розгляд власнику. Відкрийте github.com/lilka-dev/catalog і натисніть кнопку Fork (з іконкою-«вилкою») у верхній частині сторінки. Так у вашому акаунті з’явиться власна копія каталогу.

кнопка Fork на сторінці каталогу

Fork репозиторію

4. Додаємо DESCRIPTION.md

Після форку репозиторій з’явиться у вашому списку під тією ж назвою lilka-dev/catalog. Відкрийте його, перейдіть у теку apps і натисніть Add file → Create new file.

У полі назви введіть timer/DESCRIPTION.md — так автоматично створиться тека timer, а в ній файл. Це опис застосунку для каталогу Лілки; його можна просто скопіювати з README.md, який ви зробили раніше.

створення DESCRIPTION.md у теці apps

5. Додаємо manifest.yml

Останній обов’язковий файл — manifest.yml. По суті, це «паспорт» застосунку: саме з нього каталог дізнається назву, автора, де лежить код і які файли треба завантажити на консоль. Без цього файлу застосунок не з’явиться в каталозі.

Вигляд manifest.yml

Створіть файл manifest.yml так само, як попередні — через Add file → Create new file. Нижче — приклад для таймера й пояснення кожного поля:

name: Your App Name
keira_version: 1.0.0
short_description: Brief description
description: "@DESCRIPTION.md"  # Or inline text
changelog: "@CHANGELOG.md"    # Or inline text
author: Your Name
license: MIT
icon: icon.png
screenshots:
  - screenshot1.png
  - screenshot2.png
sources:
  type: git
  location:
    origin: https://github.com/yourusername/yourrepo.git
executionfile:
  type: lua  # or other type
  location:
    origin: https://url-to-your-executable-file

name — назва застосунку, як вона показується в каталозі keira_version — мінімальна версія прошивки KeiraOS, потрібна для запуску застосунку

short_description — короткий опис, що видно у списку каталогу

description: «@DESCRIPTION.md» — означає, що повний опис береться з файлу DESCRIPTION.md у тій самій теці

changelog — це історія змін застосунку: список того, що додалося, виправилося чи змінилося у кожній версії. Простими словами — «що нового» від версії до версії.

author — нік автора на GitHub зі знаком @

license — ліцензія. Можна вказати відкриту, щоб інші могли користуватися й змінювати ваш код — наприклад MIT, GNU тощо. NONE означає, що автор її не вказав

icon — головне зображення застосунку — показується в центрі сторінки і як прев’ю в каталозі

screenshots — скриншоти застосунку, що показуються нижче, у розділі Screenshots

sources — посилання на репозиторій із вихідним кодом (звичайна адреса репозиторію на GitHub)

sources — посилання на репозиторій із вихідним кодом. У новій структурі це вже не один рядок, а блок: у typeзазначається тип джерела (git), а всередині location поле origin містить саму адресу репозиторію на GitHub — клонувальне посилання, що зазвичай закінчується на .git.

executionfile — головний файл, який запускає Лілка (раніше це поле звалося entryfile). Теж став блоком: type задає тип файлу (lua або інший), а в location поле origin містить пряме посилання на сам файл.

Окремо варто сказати, де взяти посилання origin для .lua-файлу. Достатньо відкрити файл коду на GitHub і натиснути кнопку Raw угорі (поряд з іконкою Copilot) — відкриється нова сторінка, і її адреса в браузері і є тим посиланням, яке вписується в origin.

Тут легко переплутати два origin, тож варто запам’ятати різницю: верхній, у sources, указує на весь репозиторій (.git), а нижній, в executionfile, — на один конкретний головний файл.

посилання на головний файл (executionfile)

6. Додаємо іконку та скриншоти

Щоб зображення показувалися в каталозі, їх треба покласти в теку вашого застосунку, а назви файлів мають точно збігатися з тим, що вказано в manifest.yml у полях icon і screenshots.

Завантажте зображення так само, як інші файли — через Add file → Upload files у теці застосунку. Наприклад, якщо в manifest.yml указано icon: image.png і screenshots: [image.png, image2.png], то у теці timer/ мають бути файли саме з такими назвами.

завантаження зображень у теку застосунку

7. Відкриваємо Pull Request

Коли файли DESCRIPTION.md і manifest.yml створені, а скриншоти завантажені — час надіслати свої зміни в основний репозиторій.

Це дуже просто. Натисніть кнопку Contribute (її іконка схожа на роздоріжжя, де дві стежки зливаються в одну — саме це й відбувається: ваші зміни вливаються в основний репозиторій). Далі натисніть зелену Open pull request.

кнопка Contribute та відкриття Pull Request

додайте короткий опис застосунку й натисніть Create pull request. Галочку Allow edits by maintainers лишіть увімкненою — це дозволить модераторам за потреби внести дрібні правки. Після перевірки ваш застосунок з’явиться в каталозі catalog.lilka.dev

Короткий опис застосунку при створенні pull request

Приклад готового pull request

Вітаю 🎉 — тепер ваш застосунок доступний усій спільноті Лілки!

Як писати восьмибітну музику на Лілці

LilTracker — це компактний музичний інструмент, вбудований у прошивку Keira, що перетворює Лілку на маленьку чиптюн-студію. Працює він за принципом класичних восьмибітних трекерів: музика складається не з нот на нотному стані, а з рядків у вертикальній таблиці, де кожен рядок — це один крок у часі, а кожен крок описує, що має прозвучати наступної миті. Спочатку це виглядає незвично, та щойно стає зрозумілою логіка таблиці, писати треки доволі просто.

Кнопки

  • Select перемикання між меню та режимом редагування
  • Кнопки зліва (UP, DOWN, LEFT, RIGHT), рухають курсор між рядками і полями
  • Кнопка A увійти та вийти з комірки. Затиснена кнопка B — запускає попереднє відтворення

Угорі розташовано меню з командами: відкрити, берегти та Сскинути. Поряд показано параметри проєкту:

  • Page — номер сторінки композиції
  • BPM — темп у тактах на хвилину
  • Len — загальна кількість сторінок
  • Vol — гучність
  • WAV — режим запису в аудіофайл

Коли вмикається відтворення, верхнє меню зникає, а на його місці з’являється лінія яка наживо малює звук, що грає цієї миті.

Структура сторінки в Liltracker

Заголовок над таблицею — для кожного каналу написано щось на кшталт 02: SQR, 02: SAW, 01: NOI. У кожному такому підписі дві окремі речі:

  • літери (SQR, SAW, NOI) — це звук каналу, тобто чим він грає (різкий квадрат, гостра пила, шум);
  • число перед ними (02, 01) — це просто порядковий номер патерну, як ярлик на папці. Воно ні на що не впливає, а лише допомагає не загубитися: патерни рахуються з нуля, тож п’ять штук матимуть номери 00, 01, 02, 03, 04.

кадр з відео Демосцена, трекери, FM-синтез та електронне андерграунд-мистецтво: музика 8- та 16-бітної ери

Згори вниз ідуть рядки, пронумеровані 0, 1, 2, 3... Кожен рядок — це один крок у часі, одна коротка мить. Музика грається просто: береться рядок 0, потім 1, потім 2 — і так донизу, мить за миттю.

Зліва направо таблиця поділена на три стовпчики — це три канали. Вони звучать одночасно, тому й розкладені поруч: зазвичай в одному стовпчику бас, у другому мелодія, у третьому ритм. Так одразу видно, хто що грає.

І нарешті, що стоїть у самій клітинці. На кожному кроці для каналу записано три значення поспіль, і їхній порядок завжди той самий: спочатку нота (що грати), далі гучність (як голосно), а потім ефект (як саме звучить). Нота → гучність → ефект.

Структура рядка у Liltracker

Усі значення записуються в шістнадцятковій системі (як це робилося на класичних чипах на кшталт SID 6581).

Кожна нота в LilTracker записується трьома символами за однією незмінною схемою: назва + ознака дієза + номер октави.

Зображення півтонів октави

На прикладі E-4 ці три частини такі:

  • E — назва ноти (Мі);
  • — — ознака дієза: риска означає «без дієза», тобто чиста нота;
  • 4 — номер октави.

Разом E-4 читається як «Мі четвертої октави». Якщо ж нота підвищена на півтон, замість риски ставиться решітка #: запис A#5 означає «Ля-дієз п’ятої октави». Тобто другий символ працює як перемикач — риска лишає ноту як є, решітка піднімає її на півтон угору.

В одній октаві дванадцять півтонів: сім «чистих» нот і п’ять дієзних. Повний ряд із назвами такий: До (C-), До-дієз (C#), Ре (D-), Ре-дієз (D#), Мі (E-), Фа (F-), Фа-дієз (F#), Соль (G-), Соль-дієз (G#), Ля (A-), Ля-дієз (A#), Сі (B-).

Два місця в цьому ряду випадають із загального правила: між E і F, а також між B і C півтону немає. Саме тому записів E# і B# не існує — на клавіатурі цим проміжкам відповідає відсутня чорна клавіша (це й видно на ілюстрації вище).

Назва ноти однакова в усіх октавах — змінюється лише цифра в кінці. E-3, E-4 і E-5 — це те саме Мі, тільки в різних октавах: що більше число, то вище звучить нота.

Якщо корисно, можу одразу оформити цей фрагмент у docx у стилі решти статті.

Гучність. Гучність кроку — це двозначне шістнадцяткове число. Повна гучність ноти — 80; менші значення звучать тихіше (40 — приблизно вдвічі тихше за повну), а числа аж до FF навмисно перевантажують звук. Якщо лишити поле як .., гучність не змінюється — крок успадковує попередню. А щоб канал замовк, ноту вимикають командою OFF, а не нульовою гучністю.

Ефекти. Ефект складається з літери типу та двох шістнадцяткових цифр параметра.

Літера

Ефект

Що робить

A

Arpeggio

Швидко перемикає три ноти поспіль, імітуючи акорд (як у NES). Верхній напівбайт параметра — зсув другої ноти в півтонах, нижній — третьої. Приклад: A27 — друга нота на +2 півтона, третя на +7.

V

Vibrato

Модулює частоту ноти синусоїдою, створюючи вібрування. Верхній напівбайт — швидкість (Гц), нижній — глибина (0–15, де 15 — найсильніша). Приклад: V3F — 3 Гц, максимальна глибина.

T

Tremolo

Модулює гучність синусоїдою, створюючи пульсацію. Верхній напівбайт — швидкість (Гц), нижній — глибина (0–15). Приклад: T82 — 8 Гц, невелика глибина.

S

Slide

Плавно змінює гучність протягом ноти. Якщо заданий лише верхній напівбайт — гучність наростає; якщо лише нижній — згасає. Приклад: S03 — гучність плавно згасає.

.

None

Без ефекту. Записується як .00.

Приклад повного рядка: E-4 80 V3F — нота Мі-4 на повній гучності з вібрато 3 Гц. Замість звичайної ноти у полі можна поставити OFF — це миттєво глушить звук у каналі, даючи різкий обрив; або три крапки ..., що продовжують попередню ноту далі без перезапуску. Саме цим і ліплять ритмічну тканину треку: OFF робить паузи й удари, а крапки тягнуть довгі ноти.

Форми хвилі. Характер звуку задає форма хвилі. У новій версії її обирають не на весь канал, а на кожен крок окремо — у клітинці поряд із нотою стоїть невеликий значок форми, який змінюють стрілками. Найчастіше його лишають незмінним, і тоді крок успадковує форму з попередніх кроків. За замовчуванням перший канал починає квадратом, другий — пилою, третій — шумом, тож новий проєкт одразу звучить по-різному на трьох голосах. Поточну форму видно наживо на осцилографі вгорі.

Індекс

Назва

Англійська

Звучання

На екрані

0

Контину́ація

Continue

Подовження попередньої форми без перезапуску

CON

1

Квадрат

Square

Гучний, різкий, «пластмасовий» звук старих ігор (8-bit)

SQR

2

Пила

Sawtooth

Яскравий, агресивний, «двигунний» звук

SAW

3

Трикутник

Triangle

М’ягший і глаткіший за квадрат

TRI

4

Синус

Sine

Чистий, плавний, натуральний тон без артефактів

SIN

5

Шум

Noise

Білий шум, ідеальний для ударних, перкусії й звукових ефектів

NOI

типи хвиль у Liltracker

У клітинці форма позначається маленьким значком відповідної хвилі, а «континуація» — крапкою. Осцилограф показує сам сигнал: округлі горби синуса, гострі симетричні зубці трикутника, асиметричний зуб пили, прямокутні сходинки квадрата, нерівну «гребінку» шуму. На відміну від справжнього SID, LilTracker не обмежений залізом, тому всі ці форми синтезуються програмно й доступні одночасно.

Шум як ударні інструменти. Найвинахідливіше зроблено зі шумом. Сам по собі білий шум не має вираженої висоти — якщо просто крутити його частоту, нота майже не вгадується. Nintendo свого часу вирішила це інакше, і LilTracker домагається схожого результату: висота шуму залежить від частоти — що швидше «оновлюється» псевдовипадковий сигнал, то вищим він звучить. Тому, прискорюючи його, шум роблять «вищим», а сповільнюючи — «нижчим»: виходять справжні ноти шуму — низький удар, вищий клік, ще вищий скрип. На трьох каналах разом виходить мелодія, бас і повноцінна ритм-секція. Кроки висоти тут дискретні, а не плавні, тож значення варто свідомо підбирати, щоб сусідні ноти чулися по-різному.

Сторінки. Трек у LilTracker не вміщується в один екран — він складається зі сторінок. Одна сторінка — це один екран рядків патерну; написавши музику на першій, переходять на другу, потім на третю й так далі. Під час відтворення LilTracker програє всі сторінки поспіль як єдину композицію. У шапці за це відповідають два поля: Page показує номер поточної сторінки, а Len — скільки сторінок у треку загалом.

Написана музика лягає на SD-карту у двох форматах. Перший, .lt, — це власний формат Лілтрекера: він зберігає композицію в редагованому вигляді, щоб згодом повернутися й допрацювати. Другий, .wav, — готовий аудіофайл, який слухається будь-де на будь-якому пристрої. До речі, засновник Лілки — Андерсон написав кілька треків, завантажити їх можна тут.

Не варто боятися експериментувати. Крутити параметри ефектів і форми хвилі можна просто під час відтворення — саме так і намацується той живий, дихаючий «восьмибітний» звук, заради якого все й затівалося. Уся складність у деталях, а деталі виявляються через гру з параметрами.

Цікаві проєкти, які можна реалізувати

Нижче — суто власні побажання щодо застосунків і можливостей Лілки. Не обіцянка щось реалізувати й не прохання до когось, а просто перелік ідей і бачення розвитку проєкту. Я дуже захоплений IoT і розумним будинком, тож більшість ідей — звідти. Діліться своїми в коментарях.

Я дуже хочу, щоб програмування Лілки практикували в школі, коледжі, чи універі. Я готовий допомогти в реалізації навчального плану з програмування та розробки і навіть пайці, хоча паяю я не дуже) Тож, якщо ви зацікавлений та вмотивований викладач (бажано, але не обов’язково з Києва), напишіть мені тут на DOU в приватні повідомлення.

Ще одна моя мрія зробити журнал, чи брошуру про Лілку (що це за консоль, прошивки та проєкти) Я дуже натхнений прикладами MagPi та Hello World. Хочу, щоб продавались різні готові набори-коробки з деталями та журналами (IoT, Edge computing, радіокерування)

Лілка може виконувати роль клієнта IRC, або BBS. Хотілось би мати такі застосунки. Можливо в майбутньому, модуль розширення клавіатури маленький, або буде можливість приєднати клавіатуру по BLE.

Хочеться щоб BLE в Keira OS був доступний. Це дозволяє підключити клавіатуру, а також дозволяє експерименти з Beacon (bluetooth маячками)

Дуже хочеться, щоб Лілка, як платформа була офіційно присутня в репозиторіях Meshtastic та Bruce, а також щоб ці прошивки були в каталозі Лілка.

Персонально я мрію про універсальну програму, що може підключати в себе різні сенсори без написання програм. І щоб вона гарно показувала все на дашборді Лілки, чи у веб інтерфейсі. Чесно кажучи мені вдалось почати реалізовувати цю ідею і підключити два сенсори BMP280 та BME280 до LUA застосунку. З недоліків, оскільки LUA на той момент на Лілці не підтримував I2C, довелось його емулювати. Але зараз I2C, SPI та UART Luа підтримуються. За що дуже дякую розробникам. Тепер цей проєкт можна переписати.

Також хочеться застосунок в якому Лілка працює як TCP/IP — Modbus хаб. Це наприклад дозволить зчитувати на надсилати ModBus команди, наприклад на сонячні інвертори.

Хочеться, щоб розробники розробляли свої круті застосунки. І в майбутньому продавали їх за невелику суму, скажімо на itch.io Поки розробників не дуже багато, але якщо ви, найкращі читачі маєте будь які питання ви можете задати їх тут в коментарях, або в Discord спільноті Лілки.

Хочеться мати застосунок та модуль розширення для керування інфрачервоними пристроями (кондиціонерами, вентиляторами, зволожувачами, очищувачами, телевізорами, медіасистемами, тощо). Хочеться щоб застосунок Keira був щось, накшталт універсального пульта BroadLink.

Хочеться мати застосунок та модуль розширення RFID / NFC зчитувача-записувача. Щоб можна було перезаписати домофоний ключ, або зчитати дані закордоного паспорта. Також маю надію, що різноманітні модулі будуть продаватись, як магазинами, так і розробниками спільноти.

Я фанат Home Assistant та ESPHome. Хотілось би зробити клієнт Home Assistant на для KeiraOS. Оскільки Rest API відкритий та локальний, технічно це має бути можливо.

Мені дуже не вистачає онлайн редактору для коду для Lua та mJS, який однією кнопкою завантажує і можливо навіть запускає застосунок для Лілки. Щось накшталт Live Lua тільки без USB кабелю, щоб не доводилось вручну надсилати десятки ітерацій застосунку, а потім вручну їх запускати.

Також хочеться мати можливість запускати та користуватись mJS та Lua застосунками прямо в браузері. Тобто без використання Лілки зовсім.

Ідеї покращення Лілки

Не вистачає перекладу документації англійською

Додати шестигранні стійки для підтримки дисплею в розділ перелік деталей у документації

Дисплей. Хочеться більший і обов’язково прямокутний — закруглені кути бісять, бо доводиться зсувати зображення, щоб нічого не обрізалося. Мрію про кращу передачу кольорів і апаратне регулювання яскравості (ШІМ / PWM). OLED-версія як опція теж була б чудовою.

Кнопки. Хотілося б м’якші кнопки. Рішень тут кілька: перший варіант замінити поточні кнопки на силіконові. Проблема, подібні силіконові кнопки мають розмір 8×8, а посадкове місце Лілки 12×12. Другий варіант використати мембрану від d-pad контролера xbox, чи playstation.

GPIO та живлення. Дуже хочеться мати на GPIO 5 В, а не лише 3.3 В — це розширює перелік сумісних сенсорів і модулів. Не завадив би ESD-захист на лініях GPIO, щоб статика не вбивала плату. І, звісно, USB Type-C 3.0 для зарядки та передачі даних. Щоб можна було підключати Лілку, через кабель Type-C — Type-C, а не лише Type-C — Type-A.

Підключення сенсорів. Для сенсорів мрію про стандартний роз’єм — Grove або STEMMA QT / Qwiic, і бажано хоча б два, щоб під’єднувати кілька готових модулів одночасно без пайки, одним кабелем і без ризику переплутати дроти. Такий роз’єм одразу відкриває доступ до великого асортименту сумісних I2C-датчиків від Adafruit, SparkFun, Seeed та інших.

Корпус та акумулятор. Класно було б мати стандартизований корпус, який підходить під різну висоту екранів. Хочеться мати порт JST, щоб можна було підключати, а не паяти акумулятор до Лілки. Інший варіант контакти на платі та корпус розрахований під акумулятор формату Nokia або 18650. Хочеться акумулятор із передбачуваною кривою розряду, щоб індикатор заряду не брехав.

Додаткові компоненти. А як приємні дрібниці — RTC для відліку реального часу та гіроскоп з акселерометром як опція.

Висновок

Лілка пройшла великий шлях: один репозиторій перетворився на цілу екосистему з каталогом застосунків, веб-флешером, файловим менеджером і підтримкою одразу кількох мов. Але головне залишилось незмінним — це консоль, на якій приємно вчитися. Якщо ви новачок, почніть з Lua або mJS: Не бійтеся експериментувати: візьміть будь-який приклад із цієї статті, змініть пару рядків і подивіться, що вийде. Саме так і відбувається навчання. А коли зробите щось своє — поділіться у каталозі catalog.lilka.dev або в Discord-спільноті. Можливо, саме ваша гра стане для когось першою! 🚀

Сподобалась стаття? Можете подякувати донатом на збір. Долучайтесь до великого та важливого літнього збору для «Хартії». Збираємо 10 мільйонів гривень на «Куп’янських Вампірів», які будуть щодня та щоночі кошмарити окупантів. За донат кратний 300 гривень можна виграти один з п’яти цінних призів. Посилання на банку.

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті

👍ПодобаєтьсяСподобалось17
До обраногоВ обраному6
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

враховуючи розмір статі, можна було б викладати інформацію порціями, один раз на тиждень, півроку. Постійно підтримуючи зацікавленість

круто, але вже все розібрали немає в наявності :)

Класна штука, але потрібний маркетолог.

Підписатись на коментарі