Retro Game Duck Hunt written on LUA

Всем привет, меня зовут Алексей Ляховский, я на протяжение последних 10 лет занимаюсь изучением, разработкой и развитием экосистемы часов Xiaomi для глобального сообщества.

Я разобрал формат циферблатов Xiaomi последних поколений, сделал распаковщик циферблатов, и компилятор их для оригинального старого редактора циферблатов Xiaomi, сделал мод MiFitness, где активные пользователи сообщества создают и публикуют кастом циферблаты для часов, собрал из китайского IDE отдельный автономный эмулятор часов, для проверки циферблатов и приложений, модифицирую и дорабатываю оригинальные прошивки часов, а так же создаю приложения для данных моделей на JerryScript и LUA, о чем мы познакомимся подробнее чуть позже на примере данной игры.

Что такое часы Xiaomi сейчас

Как известно, Xiaomi выпускает довольно неплохие часы и браслеты, но у этой компании есть ряд особенностей и решений, которые, с моей точки зрения, не позволяют стать #1 в данном виде девайсов.

Вот некоторые из них:

  • полное игнорирование глобального сообщества и их потребностей (эта тема вообще типична для многих китайских компаний, и будем честны, их внутренний рынок позволяет себя так вести) а, именно, отсутствие форматирования даты, начало недели классически у них с понедельника, языковая поддержка даже если есть, она с ошибками, которые очень долго исправляют, если исправляют вообще, нет поддержки сообщений для чего либо, кроме их WeChat, порой даже нет иконок приложений, а сколько я разбирал багов, которые банально правятся конфигурацией, это просто не сосчитать, подразделение QA у них явно слабое.

  • слишком короткий интервал жизни устройства, год максимум, Xiaomi это именно та компания, которая выпустила и забыла, никогда не ждите от нее каких либо улучшений устройства.

  • NFC модуль, он классически не распаивается на плате для глобал моделей, нет возможности в заводском исполнении использовать карты доступа и проездные (те самые Mifare карты), классически у Xiaomi - это платежные карты, Xiaomi Pay теперь это называется, и ключи и проездные для Китая, и никак иначе. Лично для меня это всегда весомая причина покупать именно китайскую модель, потому что лично мне нравится пользоваться как ключами (Москвенок, домофоны) так и картами типа Тройка.

  • При столь богатом внутреннем устройстве и API для приложений - почти полное или практически полное отсутствие SDK для разработчиков, доступ на самом деле есть у разработчиков с китайским ID - и ситуация не меняется никак с годами, а китайские разработчики банально не знают что такое i18n, чтобы быстро и незатратно делать поддержку приложений для глобальных пользователей.

  • Отключение или даже полное исключение движков приложений из глобальных версий часов, из-за лени или отсутствия интереса у менеджмента Xiaomi развивать это направление.

При всем при этом мне очень нравятся современные часы Xiaomi, потому что система используемая внутри, очень привлекательна для различных доработок и интеграций.

HyperOS и с чем ее едят

Сначала давайте познакомимся, что такое HyperOS в часах Xiaomi и на что следует обратить внимание в первую очередь.
Итак, самое первое и важное HyperOS - это маркетинговое название и стремление компании сформировать свой узнаваемые единый бренд, и это понятно, что же внутри у разных моделей и часов на самом деле?

Компания Xiaomi это крупный агрегатор, у которого на подряде работает ряд компаний в данном направлении, если раньше это был партнер Huami, который после mb7 ушел в полностью независимое направление.
Модели Mi Watch, Watch S1, S1 Active производила небезызвестная нам компания 70mai.
Сейчас же это ряд компаний: Longcheer, Sifli и еще ряд неизвестных мне подрядчиков.
Несмотря на то, что интерфейс устройств одинаковый внешне, внутри это очень разные устройства и железу и программному обеспечению.

В 2021/2022 компания Xiaomi году начала выделять бренд VelaOS своей операционной системы, первые часы, запущенные на базе этой системы, это Watch S1 Pro.

Структура VelaOS

Итак, ядро VelaOS - это Apache NuttX — это RTOS (операционная система реального времени) с открытым исходным кодом, разработанная для встраиваемых систем. Она ориентирована на совместимость с POSIX (стандартами UNIX-подобных ОС), что делает её удобной для разработчиков, привыкших к Linux/UNIX-среде.

В часах на базе этой системы у разработчиков есть доступ к shell системы, и вы можете взаимодействовать как со стандартными приложениями shell NuttX так и дополнительными приложениями прошивки часов.
Выглядит это примерно вот так:

NuttX shell

В качестве графической библиотеки система использует lvgl, это очень популярный фреймворк для embedded устройств, это важно, потому что имплементация LUA, о которой мы будем говорить, тоже использует его.

Следующая часть VelaOS:
эта два встроенных движка для приложений: Aiot-JS и LUA.
И вот эта часть самая интересная и занимательная, и классически вы не услышите нигде ни в каких обзорах никакой информации об этом. А именно эта часть и отвечает за переход часов из категории фитнес-браслет в умные часы.

И запоминаем опять важный аспект, все дешевые модели Xiaomi хотя и называются HyperOS, в маркетинговом нейминге, не являются VelaOS устройствами, они используют дешевое железо и имеют ограниченный функционал, никаких NuttX OS и движков приложений там нет в помине.

как правило, это устройства стоимостью ниже 40$:
Mi Band 8, Mi Band 9 Active, Redmi Watch 5 Active, Redmi Watch 5 Lite,

исключение только Mi Band 9 и последующие Mi Band 10 будет тоже
на базе VelaOS => true HyperOS, это имиджевый продукт Xiaomi и самый массовый,
Mi Band 9 на сегодня самое лучшее устройство компании по цена/возможности.

Aiot-JS приложения

3rd party apps в часах - это те самые приложения, которые работают на этой движке, база этого - это JerryScript - очень популярная и пожалуй одна из самых старых реализаций для embedded устройств. Сначала компания 70mai внедрила первой их в часы Watch S1 - это полная копия приложений Huawei для их системы LiteOS/HarmonyOS, IDE это калька DevEco Studio oт Huawei.

Вот простой пример на приложения на базе этой системы и API часов.

Huawei DevEco, 3rd party app HAP (Huawei fast app)

Компания Longcheer же развила этот фреймфорк и он стал называться AiotJS. Для разработки уже используется fork VSCode и интегрированным туда эмулятором для отладки приложений Aiot IDE - текущая версия 1.6

Здесь гайд от Xiaomi как использовать ее, на китайском, на англ версию у меня не получается переключиться, хотя часть Vela API точно есть на английском.

Пример приложения Aiot IDE

Для эмуляции используется qemu совместно с Android Virtual Device подсистемой, эмулятор построен на базе системы часов Watch S3 - ярким и типичным представителей семейства часов на VelaOS.

Ура, VelaOS документация обновилась, и теперь даже доступен ряд примеров приложений

Примеры Aiot-JS приложения на github/gitee

Вот ссылка на эти примеры, можете сами посмотреть как тут все устроено,
как выглядит UI и работа с API часов https://iot.mi.com/vela/quickapp/zh/samples/

Ну и тут опять появляется ньюанс, вы не сможете нормально прогрузить готовые приложения в часы, для этого вас нужна регистрация у китайцев с китайским номером,
естественно я давно решил эту проблему специальным приложением модом официального приложения MiFitness.

LUA приложения

По мне самая мощная и интересная часть, это движок LUA.

Изначально он задумывался разработчиками для динамических программируемых циферблатов, но он отлично подходит для разработки приложений и имеет гораздо больше возможностей, потому что

1. Имеет доступ к shell системы, через стандартный модуль библиотеки os.execute()
2. Использует практически прямой вызовы lgvl функций.
3. Имеет ряд готовых UI элементов.

В часах Redmi Watch 4 я развил этот функционал, и LUA приложения можно запускать как приложения, а это очень мощный инструмент для быстрого развития функционала часов.

Список LUA модулей Redmi Watch 4

Честно говоря, до часов Сяоми я никогда не сталкивался с языком LUA, хотя я придерживаюсь в разработке типизированных языков и откровенно не люблю использование везде и вся интерпретируемых, что сейчас крайне модно, но та быстрота с которой можно делать UI часов, который работает практические как написанные на нативном коде, я быстро полюбил разработку именно на нем в контексте данных устройств.

Пример Аналогового циферблата
local lvgl = require("lvgl")
local dataman = require("dataman")

-- load our submodules
require "image"
require "root"

local function getItemsPosition()

    local width = lvgl.HOR_RES()
    local height = lvgl.VER_RES()

	local imgPosition = {
        background = { (width - 336) // 2, (height - 336) // 2 },
        timeHour =	 { width // 2, height // 2 },
        timeMinute = { width // 2, height // 2 },
        timeSecond = { width // 2, height // 2 },
    }

    return imgPosition
end

-- prepare main watchface fuction
local function entry()

    local root = createRoot()
	
	local watchface = {}
	
    -- setup watchface elements positions
	local pos = getItemsPosition()
	
    -- create watchface elements
    watchface.background = Image(root, "img_0001.png", pos.background)
    watchface.timeHour =   HandImage(root, "arrHour.png", pos.timeHour)
    watchface.timeMinute = HandImage(root, "arrMinute.png", pos.timeMinute)
    watchface.timeSecond = HandImage(root, "arrSecond.png", pos.timeSecond)
	
    -- attach events and setup update behaviour
    dataman.subscribe("timeHour", watchface.timeHour.widget, function(obj, value)
		local hour = value // 0x100
		local hourPos = 7200 // 24 * hour
		obj:set { angle = hourPos }
    end)

    dataman.subscribe("timeMinute", watchface.timeMinute.widget, function(obj, value)
		local min = value // 0x100
		local minPos = 3600 // 60 * min
        obj:set { angle = minPos }
    end)

    dataman.subscribe("timeSecond", watchface.timeSecond.widget, function(obj, value)
		local sec = value // 0x100
		local secPos = 3600 // 60 * sec
        obj:set { angle = secPos }
    end)

end

-- execute watchface function
entry()

Вот простой пример аналогового циферблата, как видите понятный лаконичный код.
Сейчас LUA приложение это часть циферблата, т.е. он упаковывается в циферблат, устанавливается как циферблат и запускается как циферблат, в код часов встроен автоматический обработчик, который видит, что если файл имеет расширение .lua - то запускается интерпретатор LUA.

Но как я говорил, для часов Redmi Watch 4 я сделал специальную прошивку, в которую можно ставить LUA приложения как отельные сущности и такие вещи как утилиты, игры, приложения теперь можно запускать независимо от активного циферблата.

Для разработки приложений на LUA я использую тоже VSCode с аддонами для языка LUA, плюс 2 решения для отладки приложений, это полноценный отладчик под Windows/Ubuntu под x86 архитектура и эмулятор часов на базе qemu-avd-Watch S3.

Окружение для разработки мы разберем во второй части статьи.

Я надеюсь, вы теперь имеете больше представления как устроены часы Xiaomi/Redmi,
что такое HyperOS/Vela OS и какие приложения и как можно создавать.

Основные ссылки, если вы хотите самостоятельно
более глубоко ознакомится с материалами по этой теме
NuttX OS - https://nuttx.apache.org/
LVGL - https://lvgl.io/
VelaOS - https://iot.mi.com/vela/quickapp/zh/guide/
VelaOS app samples - https://iot.mi.com/vela/quickapp/zh/samples/
Xiaomi Watchface Editor (Easyface) - https://github.com/m0tral/EasyFace
Xiaomi Watch Emulator - https://github.com/m0tral/MiWatchEmulator

Мой Telegram-канал, посвященный часам Xiaomi
https://t.me/mi_watch_news а также чат https://t.me/mi_watch_int