
Итак, сейчас у меня на руках последняя модель из линейки SmartBand Pro. А, это довольно хорошее железо: Bestechnic BES2700iMP Cortex-M33, 16Mb inner SPI flash, ~2.5Mb SRAM, 16Mb PSRAM, external SPI Nand Flash 512/256Mb.
Все это на базе хорошего софта RTOS NuttX, UI lvgl v9,
app engines: LUA, AiotJS(quickjs в девичестве) - Vue подобный JavaScript-фреймворк.
Lua - встроены в циферблаты, а на базе AiotJS работают приложения.
Проблема глобалки
В глобальных моделях Xiaomi менеджмент ведет себя очень глупо, вместо того,
чтобы нанять, ну хотя бы одного разработчика, и сделать десяток простых приложений
для международной аудитории, просто удаляют этот движок из часов для глобальных релизов.
Цель сегодняшней задачи
Используя доступные на борту инструменты написать и запустить простое приложение калькулятор.
Как по мне, которое давно должно быть в стандартном наборе приложений часов.
Подготовка
Когда я несколько лет назад смотрел на первые часы Сяоми с движком LUA, Redmi Watch 4, а особенность всей сегодняшней задачи, является то, что LUA является отправной точкой,
благодаря чему возможно это решение, из него можно вызвать os.execute(“”) в OS shell, я обратил внимание, что разработчики Longcheer (подрядчик Xiaomi), импортировали практически все приложения из базового набора NuttX.
И среди них интересны три команды ‘insmod’, ‘lsmod’, ‘rmmod’:
insmod — загружает и устанавливает модуль ОС в систему из файла по указанному пути, регистрируя его под заданным именем.
lsmod — выводит список всех загруженных в данный момент модулей ОС, включая их имена, размер секции кода (text) и размер секции данных (data)
rmmod — выгружает (удаляет) установленный модуль из системы по его имени.
Тогда я отложил эту задачу, так как не знал как правильно собрать модуль и реализовывал свои интеграции через прямые инжекции кода в firmware, но новое решение через модули дает следующие преимущества:
не нужно жестко привязывать адрес кода
не нужно выделять память под глобальные переменные, все работает автоматически
можно быстро проверить концепцию, без нарушения функционирования системы, и возможных фатальных последствий для устройства, требующих разборки устройств и подключения программатора.
можно загружать выборочно, что требуется в моменте
загрузка кода для дебага сильно быстрее, чем лить прошивку приложения.
В общем, гибкость сильно повышается и уменьшается время для получения готового приложения.
Загружаемые модули функционируют аналогично модулям ядра (LKM) в Linux. Они позволяют динамически добавлять код (например, драйверы устройств или новые функции) в работающую операционную систему без необходимости её полной пересборки и перезагрузки. Подробнее об модулях можно ознакомится в документации NuttX
insmod умеет загружать 2 типа модулей ELF и NXFLAT, в ELF установщик поддерживает ET_REL (перемещаемый объект) и ET_DYN (разделяемый объект). Их главное различие заключается в том, на каком этапе и кто связывает их код с реальными адресами в памяти. адреса. ET_REL - cтатический линковщик (ld) в процессе компиляции, ET-DYN - Динамический линковщик ядра при каждом старте программы.
я остановится на варианте ET_REL - он более простой для сборки приложения. Итак, на данном этапе, у нас есть два важных момента, как будем запускать приложение:
Используем LUA циферблат для деплоя(загрузки) .elf приложения в часы
Вызовем команду os.execute(“lsmod /data/mb10p_app.elf mb10p_app”) для запуска/истанцирования.
Устройство приложений подсистемы Xiaomi
Разработчики Longcheer разработали и написали довольно неплохой структурированный и продуманный код системы, с выделением подсистем часов в отдельные независимые и расширяемые модули, одно из них, и интересных нам - подсистема приложений.
Ключевые моменты этой подсистемы:
весь интерфейс часов разделен логически на отдельные приложения
для управления приложениями выделена отдельная сущность - менеджер приложений.
для запуска приложение должно быть зарегистрировано, и если необходимо - добавлено в launcher.
Вот так выглядит global vtable встроенных Xiaomi приложений одной и модели часов.
global vtable встроенных Xiaomi приложений
g_xiaomi_apps ├── register_app_launcher ├── register_app_system ├── register_app_home ├── register_app_watchface ├── register_app_setupwizard ├── register_app_easter_egg ├── register_app_control_center ├── register_app_notifications ├── register_app_carcontrol ├── register_app_check_tool ├── register_app_calendar ├── register_app_tourling ├── register_app_settings ├── register_app_alipay ├── register_app_timer ├── register_app_worldclock ├── register_app_recorder ├── register_app_chronograph ├── register_app_todolist ├── register_app_perpetual_calendar ├── register_app_compass ├── register_app_alarm ├── register_app_find_phone ├── register_app_flashlight ├── register_app_media ├── register_app_nfccard ├── register_app_weather ├── register_app_voice ├── register_app_mijia ├── register_app_share_device ├── register_app_interconnect ├── register_app_remote_camera ├── register_app_phone ├── register_app_contact ├── register_app_womenhealth ├── register_app_vitality ├── register_app_breath ├── register_app_pressure ├── register_app_oxygen ├── register_app_heartrate ├── register_app_sleep ├── register_app_training ├── register_app_course ├── register_app_record ├── register_app_sports ├── register_app_activities └── register_app_fusion_center
у приложений выделены интерфейсы, и они состоят
и двух главных структур miwear_app_t и miwear_page_t
typedef struct miwear_app_t { int proto_app; int field_4; char *name; char *icon; uint16_t app_id; uint16_t flags; int field_14; int field_18; char *(*app_get_name)(void); int (*on_uninstall)(void); int field_24; int field_28; int field_2C; int field_30; int field_34; int (*on_signal)(void); int field_3C; } miwear_app_t; typedef struct miwear_page_t { void *parent_descriptor; uint8_t _pad_4[0x0C]; void *page_name; uint16_t page_id; uint16_t app_id; uint16_t flags; uint8_t _pad_1a[0x02]; int32_t scheduler_deadline; int32_t scheduler_priority; uint32_t async_destroy_state; uint8_t lifecycle_state; uint8_t layer; uint8_t page_kind; uint8_t _pad_2b[0x01]; void *activity_context; void *root_object; void *on_signal; void *runtime_default_56; uint8_t _pad_3c[0x04]; void *registry_prev; void *registry_next; void *runtime_parent; void *on_create; void *on_resume; void *on_newdata; void *on_start; void *on_pause; void *on_stop; void *on_destroy; void *on_back; void *on_keyevent; void *get_scroll_obj; } miwear_page_t;
Менеджер приложений имеет следующий интерфейс:
struct packagemanager_api_s { int (*install)(miwear_app_t *app, miwear_page_t *pages[], int count); int (*recover)(miwear_app_t *app); int (*uninstall)(miwear_app_t *app); int (*unregister)(miwear_app_t *app); int (*update_notify)(miwear_app_t *app); void *(*widgets_add)(miwear_app_t *app, void *widgets, int count); void (*widgets_remove)(miwear_app_t *app); int (*set_hidden)(miwear_app_t *app, int hidden); int (*is_hidden)(miwear_app_t *app); };
минимальный код регистрации приложения выглядит вот так
int register_app_flashlight() { miwear_page_t *pages[1]; pages[0] = &flashlight_page_main; return p_app_install(&app_flashlight, pages, 1); }
Собственно это самая главная информация, которая нужна для реализации любого приложения в системе часов.
Реализация загрузочного модуля
Теперь приступаем непосредственно к написанию и сборки загружаемого модуля приложения.
/* * mb10p_hello.c * * Loadable NuttX userspace module for Xiaomi Mi Band 10 Pro (mb10pro). * Loaded at runtime via the NSH command: * * insmod /data/mb10p_hello.elf hello_module * * ============================================================================ */ #include <stdint.h> #include "nuttx/syslog.h" #include "platform/miwear_system.h" static int say_hello_module(void) { syslog(LOG_WARN, "[hello_module] hello"); return 0; } // ---- ELF ENTRY POINT --------------------------------------------------- * __attribute__((constructor)) int module_initialize(void) { syslog(LOG_WARN, "[hello_module] loaded"); say_hello_module(); return 0; /* OK; loader adds us to the module registry */ } __attribute__((destructor, used)) static void module_uninitialize(void) { syslog(LOG_WARN, "[hello_module] unloaded"); }
тут важный момент - помечаем конструктор и деструктор,
именно их дергает insmod, rmmod при установке удалении.
модуль делает системный вызов syslog - и пишет в лог, пока этого достаточно,
чтобы проверить работу загрузки модуля.
Сборка модуля
Очень важный этап, не просто написать код, а собрать его так, чтобы insmod разобрал .elf, загрузил в RAM, сделал релокацию адресов и истанцировал его.
В итоге должно получиться вот что
c:\Xiaomi\_app_native_code>arm-none-eabi-readelf -hS mb10p_hello.elf ELF Header: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: REL (Relocatable file) Machine: ARM Version: 0x1 Entry point address: 0x1 Start of program headers: 0 (bytes into file) Start of section headers: 828 (bytes into file) Flags: 0x5000000, Version5 EABI Size of this header: 52 (bytes) Size of program headers: 0 (bytes) Number of program headers: 0 Size of section headers: 40 (bytes) Number of section headers: 15 Section header string table index: 14 Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .text PROGBITS 00000000 000034 000030 00 AX 0 0 4 [ 2] .rel.text REL 00000000 0002a0 000018 08 I 12 1 4 [ 3] .rodata PROGBITS 00000030 000064 000043 01 AMS 0 0 1 [ 4] .data PROGBITS 00000073 0000a7 000000 00 WA 0 0 1 [ 5] .bss NOBITS 00000073 0000a7 000000 00 WA 0 0 1 [ 6] .init_array INIT_ARRAY 00000074 0000a8 000004 04 WA 0 0 4 [ 7] .rel.init_array REL 00000000 0002b8 000008 08 I 12 6 4 [ 8] .fini_array FINI_ARRAY 00000078 0000ac 000004 04 WA 0 0 4 [ 9] .rel.fini_array REL 00000000 0002c0 000008 08 I 12 8 4 [10] .comment PROGBITS 00000000 0000b0 00004e 01 MS 0 0 1 [11] .ARM.attributes ARM_ATTRIBUTES 00000000 0000fe 000034 00 0 0 1 [12] .symtab SYMTAB 00000000 000134 000130 10 13 18 4 [13] .strtab STRTAB 00000000 000264 00003b 00 0 0 1 [14] .shstrtab STRTAB 00000000 0002c8 000071 00 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), I (info), L (link order), O (extra OS processing required), G (group), T (TLS), C (compressed), x (unknown), o (OS specific), E (exclude), y (purecode), p (processor specific)
.ld файл линковщика
/* Compact ET_REL layout */ ENTRY(module_initialize) SECTIONS { .text : { *(.text .text.*) } .rodata : { *(.rodata .rodata.*) } .data : { *(.data .data.*) } .bss : { *(.bss .bss.* COMMON) } .init_array : { KEEP(*(.init_array)) KEEP(*(.init_array.*)) } .fini_array : { KEEP(*(.fini_array)) KEEP(*(.fini_array.*)) } }
Итого, должны соблюдаться следующий ряд условий
тип ELF - REL (Relocatable file)
никаких import/export символов
конструктор промаркирован
никаких __aeabi_* импортов от математики, так как нет маппинга на встроенные lib, loader .ELF в insmod просто режекнет такой .ELF - не загрузит и выдаст ошибку.
Сборка модуля приложения
Теперь имплементируем интерфейсы и структуры в модуле приложения,
для регистрации приложения.
static char notification_title[] = "Voilà! ?"; static char notification_src[] = "Calculator"; static char notification_body[] = "I'm in apps. Find me there."; static char APP_ICON[] = "/data/calc_logo_112.png"; static struct notification_msg_t done_notification = { .message_id = UINT64_C(0xBE57BEEF), .title = notification_title, .source = notification_src, .body = notification_body, .small_icon_path = APP_ICON, .large_icon_path = APP_ICON, .start_reminder = 1u, }; static miwear_app_t g_calc_app = { .proto_app = 0, .field_4 = 0, .name = APP_NAME, .icon = APP_ICON, .app_id = CALC_APP_ID, .flags = 0, .field_14 = 0, .field_18 = 0, .app_get_name = calc_app_get_name, .on_uninstall = 0, .field_24 = 0, .field_28 = 0, .field_2C = 0, .field_30 = 0, .field_34 = 0, .on_signal = 0, .field_3C = 0, }; static miwear_page_t g_calc_page = { .parent_descriptor = 0, .page_name = PAGE_NAME, .page_id = CALC_PAGE_ID, .app_id = CALC_APP_ID, .flags = 0, .scheduler_deadline = 0, .scheduler_priority = 0, .async_destroy_state = 0, .lifecycle_state = 0, .layer = 1, .page_kind = 0, .activity_context = 0, .root_object = 0, .on_signal = calc_page_on_signal, .runtime_default_56 = 0, .registry_prev = 0, .registry_next = 0, .runtime_parent = 0, .on_create = calc_page_on_create, .on_resume = 0, .on_newdata = 0, .on_start = 0, .on_pause = 0, .on_stop = 0, .on_destroy = calc_page_on_destroy, .on_back = 0, .on_keyevent = 0, .get_scroll_obj = 0, }; static void register_app(void) { if (app_lookup(CALC_APP_ID) == 0) { /* Register the calculator app with its main page. */ miwear_page_t *pages[] = { &g_calc_page }; int res = g_packagemanager_api->install(&g_calc_app, pages, 1); syslog(LOG_WARN, "%s pm_app_install(%s) rc=%d\n", LOG_TAG, g_calc_app.name, res); /* Add the app icon to the launcher grid. */ int rc = app_launcher_add(CALC_APP_ID); syslog(LOG_WARN, "%s app_launcher_add rc=%d\n", LOG_TAG, rc); if (res == 0) { char *curr_face = g_watchface_config && g_watchface_config->current_face ? g_watchface_config->current_face->id : ""; syslog(LOG_WARN, "curr face: %s\n", curr_face); watchface_manager_delete_watchface(curr_face); watchface_manager_reset_watchface(NULL); lvx_notification_insert_message(&done_notification); } } else { syslog(LOG_WARN, "%s add already exists", LOG_TAG); } } static void timer_callback(lv_timer_t *timer) { (void)timer; lv_timer_delete(post_timer_id); post_timer_id = 0; register_app(); syslog(LOG_WARN, "%s timer fired", LOG_TAG); } static void call_deferred(void) { post_timer_id = lv_timer_create(timer_callback, 500, 0); syslog(LOG_WARN, "%s lv_timer_create -> %p\n", LOG_TAG, post_timer_id); } __attribute__((constructor)) int module_initialize(void) { syslog(LOG_WARN, "%s loaded\n", LOG_TAG); // There is a problem to call UI functions in shell thread. // So there is a solution, to run timer and callback will fired // in lvgl loop thread, otherwise it fuckup the shell thread // and system reboots. call_deferred(); return 0; }
Я думаю, что тут все понятно и без лишних слов,
я определяю глобальные обьекты g_calc_app и g_calc_page,
регистрирую приложение в системе через вызов
g_packagemanager_api->install(&g_calc_app, pages, 1);
системый вызов app_launcher_add добавляет приложение в грид лаунчера,
ну и дополительно, я добавил атоматическое удаление циферблата,
для этого дергаются два вызова. watchface_manager_delete_watchface, перед этим я разобрал структуру таблицы хранилища циферблатов системы, чтобы вытащить id текущего циферблата, ну и вызов watchface_manager_reset_watchface(NULL) нужен, чтобы система обновила UI после удаления, иначе циферблат удален, а на дисплее все равно отображается.
плюс, для улучшения визуалиции, и пробы функционала добавил вызов
системного уведомления через lvx_notification_insert_message
Непосредственно калькулятор
Создание страницы приложения калькулятора, скучная и не сильно интересная часть, поэтому большую часть кода я опушу, полное приложение, вы можете посмотреть на моем github.
Ссылка будет ниже.
Больше всего внимания заслуживает lvgl часть, математику я дал на реализацию LLM, единственно, что важно там, я указал компилятору что процессор умеет во float-point operations
через директивы -mfloat-abi=hard -mfpu=fpv5-sp-d16 и убедился что компилятор не нагенерил мне aeabi вызовов.
Итак
lv_obj_t *calc_page_create(lv_obj_t *parent) { /* Full-screen container. */ lv_obj_t *root = lv_obj_create(parent, 0); lv_obj_set_size(root, SCREEN_W, SCREEN_H); /* Background. */ lvx_obj_set_style_bg_color(root, 0xFF101018u); lv_obj_set_style_bg_opa(root, 255u, 0); /* Default lv_obj padding shifts every TOP_LEFT aligned child; clear * it so the calculator grid is positioned against true (0,0). */ lv_obj_set_style_pad_left (root, 0, 0); lv_obj_set_style_pad_right (root, 0, 0); lv_obj_set_style_pad_top (root, 0, 0); lv_obj_set_style_pad_bottom(root, 0, 0); /* Display label at the top. Spans full screen width with a uniform * left+right gap so it doesn't kiss either edge. */ lv_obj_t *disp = lv_label_create(root); calc_disp_label = disp; lv_label_set_text(disp, calc_display); lv_obj_set_size(disp, SCREEN_W - 16, CALC_DISP_H); lv_obj_align(disp, LV_ALIGN_TOP_RIGHT, -8, 12); lv_style_t *lb_style = (lv_style_t *)LB_TEXT_STYLE_1; lvx_obj_set_style_text(disp, lb_style, 255, 0); lv_obj_set_style_text_align(disp, LV_ALIGN_TOP_RIGHT, 0); /* Button grid -- 4 cols, 5 rows. The "0" key spans two columns. */ static const char *const labels[5][4] = { { "C", "<=", "%", "/" }, { "7", "8", "9", "*" }, { "4", "5", "6", "-" }, { "1", "2", "3", "+" }, { "0", "", ".", "=" }, }; lv_coord_t total_w = 4 * CALC_BTN_W + 3 * CALC_BTN_GAP; lv_coord_t start_x = (SCREEN_W - total_w) / 2; lv_coord_t start_y = CALC_DISP_H + 24; for (int r = 0; r < 5; ++r) { for (int c = 0; c < 4; ++c) { const char *lbl = labels[r][c]; if (lbl[0] == '\0') continue; lv_coord_t w = CALC_BTN_W; if (r == 4 && c == 0) w = 2 * CALC_BTN_W + CALC_BTN_GAP; lv_coord_t x = start_x + c * (CALC_BTN_W + CALC_BTN_GAP); if (r == 4 && c == 0) { /* Wide "0" spans columns 0-1; anchor it flush-left. */ x = start_x; } lv_obj_t *btn = lv_btn2_create(root); lv_obj_set_size(btn, w, CALC_BTN_H); lv_obj_align(btn, LV_ALIGN_TOP_LEFT, x, start_y + r * (CALC_BTN_H + CALC_BTN_GAP)); lv_btn_set_text(btn, lbl); /* Function row (top) a bit lighter; other keys dark-grey; * orange "=". */ if (lbl[0] == '=') lvx_obj_set_style_bg_color(btn, 0xFF9500u); else if (r == 0) lvx_obj_set_style_bg_color(btn, 0x5C5C5Cu); else lvx_obj_set_style_bg_color(btn, 0x333333u); lv_obj_add_event_cb(btn, calc_on_press, LV_EVENT_CLICKED, (void *)lbl); } } return root; }
Как видно, тут пошел стандартный lvgl, в данной модели, это v9.
Кстати, очень рад, что китайцы использовали эту популярнейшую либу, а не свой китайский PersimUI, который я уже встречал в ряде моделей, можно использовать SquareLine Studio / LVGL Pro, к примеру, для визуального построения UI и генерации кода.
Platform mapping
Собственно, самая сложная часть тут - это вытащить API из платформы, я использую IDA Pro, и гоняю LLM через MCP, предварительно разметив сегменты прошивки,
и добавив опорные точки для упрощения анализа, к сожалению, или к счастью, (пока не понятно ?) LLM справляется посредственно, приходится перепроверять все и добивать кучу того, что не может найти.
На текущий момент я реализовал приложение под 2 платформы, последние на текущую дату:
MB10 Pro CN: 3.101.043 | 3.121.043*
MB10 Pro GL: 3.201.016 | 3.221.016* * - это мои моды сток прошивки.
Упаковка в циферблат
Самый быстрый и простой вариант, через оригинальный редактор Easyface, и мой компилятор, ссылки приложены ниже, в исходниках подготовлены проекты циферблатов и выполняется деплой после компиляции, а в моем MiFitness mod есть админка библиотеки циферблатов, где можно загрузить в development секцию циферблатов и установить.
Build environment
gcc version 10.2.1 20201103
VScode build tasks включены в исходный код, так что можете легко собрать мои проекты и написать свое
Результат реализации
Итак, что мы имеем в итоге:
insmod акцепит модуль и загружает приложение в RAM, успешно ремаппит адреса.
приложение успешно регистируется и запускается
работает быстро, тут ни LUA ни AiotJS рядом не стояли
ну бонусом оно умеет все системные штуки и имеет доступ ко всему что есть в часах, это очень круто.
К слову - бинарник, что я в итоге получил - mb10p_app_3.101.043.elf - 6.424 bytes.
Ограничения реализации.
Поскольку данное приложение инстанцируется через запуск ручной команды, оно естественно будет потеряно при перезагрузке устройства, но его можно быстро переустановить, ну и часы долгоиграющие - поэтому не такая большая проблема.
A в будущем я планирую добавить user script инициализации при загрузке системы в мод прошивки, которые я выпускаю, соответсвенно можно будет добавлять свои приложения туда и система будет поднимать их на старте.
Что можно еще
Да, блин, у меня разбегаются глаза - что можно еще
можно добавить ответы на сообщения telegram/whatsapp
можно сделать кастомные шедулер и notification менеджер под любую задачу - метроном, томато таймер, water drink
можно делать вызовы в сеть и запускать на часах что угодно, ну к примеру, я уже реализовал через Lua циферблат и curl - Алису, управляю колонками и умным домом через них.
сделать кастомный центр уведомлений
можно заменить любое приложение своим
можно расширить API Lua модулей и AiotJS это кроме банального написания нативных приложений и игр.
Canopus
Исследование и работа над данным решением меня вдохновил проект китайского разработчика SearchStars, он же автор приложения Astrabox, написанное на Tauri, который первым разобрался с insmod, и сделал свой модуль Canopus.
Его модуль истанцирует девайс /dev/canopus, а потом он через Lua последством fd общается через read/write, элегантное и простое решение, поскольку интерграция Lua модулей не тривиальная вещь, а данный механизм доступен из коробки, так же модуль добавляет динамически страницы в приложение Canopus, которое точно так же регистрируется в система.
Поверх этого модуля он написал крутой модуль - в котором реализовал A2DP+AVDTP audio stack, который позволяет pair-ить колонки и наушники и играть звук в них, сейчас реализованы MB9/MB10 Pro, MB11.
Часть кода написана на Rust - так что кому интересно - внизу оставлю ссылки.
Вот так это выглядит на девайсе

NuttX OS binary loader - https://nuttx.apache.org/docs/latest/components/binfmt.html
NuttX OS insmod - https://nuttx.apache.org/docs/latest/applications/nsh/commands.html#insmod-install-an-os-module
NuttX OS Shell - https://nuttx.apache.org/docs/latest/applications/nsh/index.htmlCalculator App src https://github.com/m0tral/MiWatchNativeApps
Easyface - https://github.com/m0tral/EasyfaceCanopus - https://github.com/AstralSightStudios/Canopus
Canopus-Module-BluetoothAudio https://github.com/Searchstars/Canopus-Module-BluetoothAudioTG news - https://t.me/mi_watch_news TG чат - https://t.me/mi_watch_int
Комментарии (10)

NutsUnderline
06.09.2026 18:19xiaomi очень стараеться не быть amazfit (huami). Вероятно они не делают api для простых часов чтобы не вредить продажам их более дорогих часов на wearos

kenomimi
06.09.2026 18:19Скорее всего, это никому, кроме 3.5 гиков, не нужно. Я вот не представляю, какое я бы сделал приложение для своих часов - там есть всё, что нужно на смарт-часах.

NutsUnderline
06.09.2026 18:19такая же нога, в общем :) на 4pda люди высказывают очень разные желания по функционалу часов. например - "плыву на лодке и хочу видеть скорость". проще дать людям возможность реализовать то на что фантазии разработчиков не хватило. и еще более удивительно оказывается что уже реализовано. например я очень сильно удивился увидев приложение для homeassistant для часов amazfit. по числу скачиваний в соотв. маркете в принципе можно судить что гиков оказалось больше чем 3.5

m0tral Автор
06.09.2026 18:19вы о чем, документация на API и IDE для движка AiotJS существуют уже несколько лет
https://iot.mi.com/vela/quickapp/en/tools/
а сам движок впервые был еще на часах Xiaomi Watch S1/Color 2 - тогда их производила компания 70mai, на хорошо документированном микроконтроллере STM32U5
По Lua же нет хорошей документации, хотя там почти весь API стоковые либы Lua 5.4
Сяоми не сильно заботит глобальный рынок, их внутренний явно потребляет отлично,
и я представляю почему, иногда проще забить x, чем проходить регуляцию стран задротов типа Германии, а России и подавно..

m0tral Автор
06.09.2026 18:19а продажи флагманов Mi Band компании и рядом не стояли с wearOS линейкой,
я купив в 2014 году LG Watch R - до сих пор не вижу подвижек в сторону увеличения автономности моделей, и я не куплю wearOs/Apple только поэтому, компаниям насрать на потребиля и вы ничего не можете с этим сделать, а в моделях Сяоми я получаю и крутую автономность и систему в которой я могу все что мне нужно.а маржу они сейчас берут за счет продаж керамики и золотых(цыганских) версий своих дешевых моделей.

v_0ver
06.09.2026 18:19Спасибо за статью, у меня тоже Mi Band 10 Pro и я давно хотел сделать пару игр на акселерометре. Но никак не мог подступиться.

m0tral Автор
06.09.2026 18:19для этого достаточно обычного Lua - примеры с акселерометром у меня давно на github
https://github.com/m0tral/MiWatchLuaWatchfaces
нативные приложения - это жесткая привязка к версии прошивки - и найти API может далеко не каждый, но бонус за это - глубокая интеграция с системой и практически не ограниченные возможности, плюс производительность
AeYronu
Отлично! Это именно то чего не хватает. И не только этой модели. А вообще всей продукции от Xiaomi/Redmi.
Этот решение уже есть на 4PDA?
Можно ли как то портировать данное решение на Redmi Watch 5 Active?
m0tral Автор
Нельзя, Active линейка говно, на дешевом железе и FreeRTOS обычно, чтобы делать такое нужно чтобы было
NuttX OS - linux подобная система
Lua engine
Shell содержал рабочий insmod загрузчик
Например, первые часы у Сяоми на NuttX OS - Watch S1 Pro,
там есть 2 из 3, но нет Lua engine.
У Redmi Watch 5 - Lua engine просто вырезан, тоже нельзя таким способом как я показал, но можно через обновление прошивки.
На Redmi Watch 5 Active - проц не Bestechnic, a там платформа
SiFli SF32LB551 - https://wiki.sifli.com/en/silicon/index.html
SRAM 1Mb, internal flash - 0, external SPI nand 2x8Mb,
на двух внешних nand чипах, на которых хранилище циферблатов 2.6мб
на них даже OTA пакет не полноценный, ресурсы на часах не обновляются, только приложение, потому что тупо нет места куда его сохранить при обновлении, 2 внешних nand флеш по 8мб - видимо у кого то осталось много этого говна в закормах, я не могу иначе это обьяснить
Модели на которых такое возможно:
mb9, mb10, mb11,
mb8/mb9/mb10 pro
RW4, RW5(c оговоркой), RW6, RW7 (выйдут в ~декабре)
Watch S3, S4, S5