flipper zero
Использую ли я «Флиппер»
У меня тут спросили, использую ли «Флиппер Зеро», — давно ничего про него не рассказывал. Использую, конечно, и очень интенсивно! С тех пор как потерял магнитный ключ от подъезда, каждый день туда и обратно прохожу, прикладывая вместо него «Флиппер». Всё недосуг заняться этим вопросом.
Хорошо, что ещё в самом начале я записал на «Флиппер» данные всех магнитных ключей, которые у меня были — домашний, рабочий, родственников и так далее.
Casio WQV-3
К заметке, где я писал про камеру для «Флиппера», один из читателей прислал ссылку на часы «Casio
Разумеется я загорелся их купить.
На «Ебее» их можно найти без проблем, но с пересылкой часов через границу сейчас большие проблемы. Без особой надежды я пошерстил «Авито» и оказалось, что, хотя первой модели часов там нет, зато лежит третья, которую я тут же себе купил и ношу уже несколько дней.
Оцените какой
Всего в линейке таких часов было четыре модели с номерами «1», «2», «3» и,
Конечно снимки хочется из часов
Под рукой, из того, что поддерживает инфракрасный обмен, у меня только «Флиппер». В принципе, если знать протокол, можно под него написать программу, которая списывала бы снимки на карточку, оттуда перекинуть на ноутбук — ноль проблем.
Описание протокола найти было нетрудно. Но позже оказалось, что в третьей модели «Касио» перешли на некий стандартный протокол передачи, а его описание я ещё даже не искал. Возможно, раз протокол стандартный, снимки можно получить
Я немного посмотрел АПИ для работы с инфракрасником во «Флиппере» и, пока не забыл узнанное, сделаю заметки для себя.
Для передачи сигналов подойдёт одна из двух функций. Если повезёт это будет infrared_send, которая принимает команды в одном из стандартных кодеков, либо infrared_send_raw_ext, где придётся возиться с ручным кодированием битов.
Зависит от того, что за кодировка сигналов используется в этом самом «стандартном протоколе». Если это
Наименее очевидными были для меня тайминги. Я
Ещё фотки камерой «Флиппера»
Когда берёшь в руки молоток, всё вокруг становится похожим на гвозди. Раз уж я примотал камеру к «Флипперу», надо всё вокруг пофотографировать. Мы с дочкой решили сфоткать домочадцев.
Жена на работе, себя я фоткал вчера, поэтому слева направо: улитки Тайгер и Найгер, кот Марсик, пёс Бейсик и дочь Ева.
Камера для «Флиппера»
Около трёх месяцев назад, болтаясь по интернету, я, волею случая, наткнулся на репозиторий с софтом, позволяющим подключить к «Флипперу» небольшую
Выглядело это забавно, да и цена вопроса — всего 400 рублей, плюс комплект перемычек за 30. Невеликие траты за такую прикольную штуку. Я
Если внимательно следовать инструкции, то всё просто: четырьмя перемычками соединяешь «Флиппер» и плату, ещё одной замыкаешь между собой два штырька — это нужно, чтобы залить в камеру прошивку. Потом скачиваешь редактор для «Ардуино», там ставишь плагин, далее через «Флиппер» с его помощью заливаешь в камеру прошивку, размыкаешь перемычку. Потом через магазин приложений ставишь нужную программу и развлекаешься.
Единственная хитрость — плагин надо ставить второй версии, а не третьей, в третьей
Экранчик у «Флиппера» маленький
Доделал приложение пароля для «Войи»
Глаза боятся, руки делают.
Начал потихоньку — накидал сначала
В этот раз столкнулся
Чтобы получить стрелку в другу строну, повернул её в редакторе, проделал всё то же, что с первой, но при отображении стрелка получалась
Как я уже рассказывал, инструментарий для компиляции программ сам конвертирует картинки в нужный «Флипперу» формат. Я не ковырял, но по виду это обычный XBM — формат двухцветной
Предполагаю, что при преобразовании входного изображения в двухцветное, используется
Сначала я решил это проверить. Поводил «пипеткой» на картинке, не нашёл. Нарисовал заново. Это ситуацию не исправило. Решил посмотреть в бинарном виде сконвертированные файлы, оказалось сложновато, плюнул. Пошёл смотреть в чём разница у исходных файлов. Оказалось, что один у меня в формате
Подумал, что проблема в этом, но как преобразовать одно в другое сходу не разобрался. Утилитой magick начал конвертировать проблемную картинку в PNG, перебирая все известные мне ключи подряд, пока в один момент дырки не пропали. Я так и не выяснил какое сочетание настроек мне помогло, потому что в процессе потерял исходную «дырявую» картинку, а делать новую не хотелось — мне ещё предстояло много программировать.
«Что это было, Бэрримор» — непонятно до сих пор, но на всякий случай записываю себе этот случай, чтобы в следующий раз хотя бы примерно понимать как с ним бороться.
Оператор «__has_include»
Одна штука мне очень не нравилась в программировании под «Флиппер». Фреймворк устроен так, что картинки, используемые в приложениях, должны внедряться определённым способом.
А именно — в манифесте приложения, в директиве fap_icon_assets мы указываем папку, каждая картинка в которой, позже, на этапе сборки, будет преобразована в специальный массив. Этот массив всегда называется I_имя_файла_картинки. Массивы засовываются в файл со специальным именем, который надо импортировать в код через #include.
Поскольку это происходит во время сборки, редактор (я использую «Визуал Студио Код») этот файл не видит, поэтому подчёркивает мне его импорт и все использования массивов, которые в нём описаны, как ошибку.
Лично меня такое раздражает. В редакторе мне хочется видеть полное отсутствие ошибок.
В «Виселице» я с этим смирился, а сегодня
Получается, что если файл для импорта ещё не готов, то мы его не импортируем, а нужный нам массив объявляем, как имеющий внешнюю реализацию. В итоге, в редакторе ошибок не остаётся.
Ещё про инженерный пароль от «Войи»
Ну началось 😅 Теперь меня не отпускают мысли как можно доработать приложение для получения инженерного пароля, которое я написал вчера.
Как я говорил, у «Флиппера» есть проблема — он ничего не знает о часовом поясе. В каждом приложении, где нужно его знать, приходится
Я пока не стал заморачиваться, просто показываю три кода из трёх разных дней. Это, конечно, не совсем правильно. Учитывая, что часовые пояса у нас бывают от
В общем, решил сделать как все — окошко с настройкой часового пояса. Если пояс задан, он будет показан на кнопке и выводиться будет только один код, если нет, на кнопке будет написано
Инженерный пароль от «Войи»
Чтобы совсем не позабыть как писать и компилировать программы для «Флиппера», написал небольшую программу, которая выводит инженерный пароль для моей машины.
Пароль там можно даже на калькуляторе вычислять, но это неудобно — надо сложить поциферно
День берётся по часовому поясу Китая, а поскольку «Флиппер» ничего про часовые пояса не знает, поэтому приходится показывать три числа — для разных дней.
Полезное упражнение, подзабыл некоторые особенности, как самой сборки, так и наличествующего АПИ.
А что «Флиппер»?
Меня тут недавно спросили — куда пропали мои посты про «Флиппер»? Пропали, потому что писать особо нечего. У меня в голове есть идея небольшого, полезного лично для меня приложения для него, кроме того, незакрытым гештальтом висит на душе добавление туда Юникода, но времени нет ни на первое, ни на второе.
Пока лето, в выходные я в разъездах, в будни тоже хочется
Пока же я его использую только в быту — чтобы
Завтра — отпуск!
Завтра отпуск. Я надеялся позаниматься «Флиппером Зеро», но вовремя сообразил, что отношение к этому устройству по всему миру неоднозначное. Лучше его за границу не возить, тем более со снимающей ограничения прошивкой, которая стоит у меня.
В принципе, устройство везти не обязательно, можно использовать «песочницу» с минимальный кодом. Его хватит для моей задачи — добавить, наконец, в прошивку поддержку Юникода. Я пока забросил эту задачу, планирую заняться в отпуске, не всё же на пляже тюленить.
Юникод во «Флиппере»: глубока ли кроличья нора
Юникод во «Флиппере» неисчерпаем, как атом.
Сначала мне стало просто интересно почему для «Флиппера» нет ни одного русскоязычного приложения, потом я выяснил что мешает это сделать и научился обходить эти ограничения, потом написал русскоязычную игру, после чего залез в прошивку и начал добавлять в неё поддержку Юникода, погружаясь всё глубже и глубже.
К моим коммитам отнеслись довольно благосклонно, но в один момент дело застопорилось — авторы прошивки решили обновить сторонний фреймворк для вывода на экран, что могло повлиять на мои изменения, поэтому работу над ними я отложил. Но уже тогда у меня возникла мысль, что надо бы добавить Юникод и в библиотеку работы со строками, которую использует «Флиппер Зеро».
Пока я ждал обновления, занял себя векторизацией функции подсчёта длины строки в кодировке
И вот сегодня я получил новый комментарий к моему коммиту от авторов прошивки — мне порекомендовали заняться тем, о чём я
Размер доработок всё ширится, а результат всё дальше. Интересно, насколько глубока кроличья нора?
Конец ускоренной strlen на «Флиппере»
В итоге, эпопея с ускорением функции замера длины строки в кодировке
У меня с самого начала этой затеи были сомнения. Я понимал, что для того, чтобы решить
Тем не менее, я с удовольствием повозился инструкциями SIMD. До этого
Моя функция, кстати, претерпела некоторые изменения:
size_t furi_string_utf8_length(FuriString* str) {
size_t len = string_size(str->string);
const char * cstr = string_get_cstr(str->string);
const char* end = cstr + len;
if(len >= sizeof(uint8x4_t)) {
const char* vend = end - sizeof(uint8x4_t);
int8x4_t zero = 0x00000000;
int8x4_t one = 0x01010101;
int8x4_t threshold = -1077952577; // -65, -65, -65, -65
do {
int8x4_t vec = *(int8x4_t*)cstr;
__ssub8(threshold, vec);
uint8x4_t result = __sel(one, zero);
len -= __usada8(result, zero, 0);
cstr += sizeof(uint8x4_t);
} while(cstr <= vend);
}
while(cstr < end) {
signed char c = *cstr++;
len -= c < -64;
}
return len;
}Оказалось, что тип FuriString хранит в себе бинарную длину строки, поэтому я переделал код, чтобы он не пытался читать чужую память. В векторизованой части это было вероятно, но своих рассуждений и дискуссии по коммиту я сделал вывод, что в случае «Флиппера» это не имеет
Тем не менее, если использовать бинарную длину, код получается более аккуратным и немного упрощается, так что грех не воспользоваться. Интересно бы, кстати, измерить наивный алгоритм без векторной части, но с использованием бинарной длины, наверное займусь этим в ближайшее время.
В итоге, решая эту, возможно несуществующую, проблему, я опробовал на практике векторные инструкции расширений NEON и DSP SIMD, научился делать два параллельных запроса на вливание в «Гитхабе» и ещё раз вспомнил как компилировать и заливать свою прошивку во «Флиппер Зеро».
Сделал PR с новой strlen во «Флиппер»
Спасибо Шурику Бабаеву за помощь, добавил свой коммит с векторизованной функцией для измерения длины строки в кодировке
Попутно выяснилось, что «Флиппер» использует функцию замера длины из библиотеки M*LIB. Можно было бы закоммитить новую версию в неё, но не стал этого делать по двум причинам.
Кстати, сделал итоговые замеры. Миллион итераций с четырьмя короткими строками (от ЯБ12341 до ЯБ12341234) моя версия делает за шесть секунд, тогда как прежняя — за 39.
Надо бы добавить мой strlen во «Флиппер»
Можно подумать, что я забросил возню с «Флиппером», так как
До недавнего времени я её надеялся ускорить, поэтому пытался вносить
Будем считать, что с этим вопросом я закончил, хочу теперь попробовать добавить этот код в прошивку «Флиппера». Остались сделать два шага.
На замерах моя реализация обогнала встроенную на порядок — восемь секунд против 55 на двух миллионах итераций по небольшой строке. Но теперь надо воткнуть её в прошивку, чтобы учесть влияние реализации строкового типа FuriString, в котором будет использоваться эта функция и провести новые замеры. Надеюсь сделать это в ближайшие выходные.
Второй шаг для меня сложнее. У меня уже есть выписанная прошивка «Флиппера», в которой торчит непринятый коммит. Я никогда раньше не создавал на «Гитхабе» два разных запроса в рамках одного чужого проекта, не знаю как это делается. Если
Замеры strlen_uft8 на «Флиппере Зеро»
Ну что же, я наконец сравнил свой вариант функции определения длины строки в кодировке
Оказалось, что обычные функции замера времени под «Флиппером» не работают. мне не удалось получить значение текущего времени ни одним из способов к которым я привык на Си, поэтому опять пришлось читать исходники прошивки «Флиппера».
В итоге обнаружился метод, выводящий затраченное время с секундной точностью:
FuriHalRtcDateTime curr_dt;
furi_hal_rtc_get_datetime(&curr_dt);
uint32_t start = furi_hal_rtc_datetime_to_timestamp(&curr_dt);
for (int i = 0; i<1000000; i++)
len += strlen_utf8("This is a test и русских букв тоже");
furi_hal_rtc_get_datetime(&curr_dt);
uint32_t stop = furi_hal_rtc_datetime_to_timestamp(&curr_dt);Хотелось бы точнее, но этого, в принципе, хватает, чтобы понять какая функция быстрее и на сколько. Надо просто сделать очень много итераций. Я попробовал запускать на миллионе и десяти миллионах.
В итоге, на десяти миллионах наивная реализация выполняется примерно за 80 секунд, моя и чужая быстрая — около 50, явного лидера из этих двух реализаций выделить не удалось. Все замеры делал несколько раз.
DSP SIMD для Flipper Zero
В этом версии нет никакой защиты цикла от чтения за границей буфера, мне показалось, что во «Флиппере» это неважно, но возможно это не так.
Ну что ж, разобрался я что за зверь такой этот DSP SIMD и запрограммировал на нём векторизированную версию функции для измерения длины строки.
Сегодня описывать и замерять уже не буду, и так времени много ушло, но по крайней мере всё работает на настоящем «Флиппере».
Сразу можно заметить, что вектора тут вдвое короче — всего четыре байта, да и функций для работы с ними гораздо меньше, но этого набора оказалось достаточно, чтобы всё векторизовать.
#include <arm_acle.h>
size_t strlen_arm_utf8(char * str) {
uint32_t len = 0;
int8x4_t zero = 0x00000000;
int8x4_t one = 0x01010101;
int8x4_t threshold = -1077952577; // -65, -65, -65, -65
for (;;) {
uint8x4_t result;
int8x4_t vec = *(int8x4_t *) str;
__uadd8(0xFFFFFFFF, vec); result = __sel(zero, one);
if (result > 0) {
if (result == 0x01000000) {
__ssub8(threshold, vec); result = __sel(zero, one);
return __usada8(result, 1, len);
}
break;
}
__ssub8(threshold, vec); result = __sel(zero, one);
len = __usada8(result, zero, len);
str += sizeof(uint8x4_t);
}
for (signed char c; (c = *str); str++) {
if (c >= -64) {
len++;
}
}
return len;
}
ARM бывают разные
Преодолев внутреннее сопротивление, я
Во «Флиппере» стоит процессор ARM, а они, как оказалось, бывают очень разные.
Я ещё в самом начале посмотрел спецификацию процессора «Флиппера» и, увидев слово SIMD, совершенно успокоился — в моём понимании это означало, что я могу использовать любые команды векторизации, которые мне необходимы. Тут я немного поторопился, но меня подгоняло желание разобраться как устроена векторизация. Теорию я знал, а тут подвернулась, пусть немного синтетическая, но задача, которую руки чесались решить.
Как я уже говорил, мне захотелось срезать углы, поэтому функцию до этого момента я писал на своём ноутбуке, раз так случилось, что у меня на нём тоже процессор ARM. И лишь сегодня я попробовал вставить её в одну из своих программ и собрать её для «Флиппера».
Сначала компилятор заявил мне, что не нашёл некоторые векторные функции, в частности функцию нахождения минимума vminvq_u8 и суммы vaddvq_u8.
Ветку с vminvq_u8 я временно убрал, а функцию суммы vaddvq_u8 решил заменить на поэлементное сложение (vaddq_u8), которая, вроде, компилятору знакома.
Мысль простая — в векторах, сумму которых мы находим, на каждой позиции нули и единицы, поскольку каждый элемент вектора у нас восьмибитный, в общем случае у нас поместится результат 255 операций, а потом я могу просто просуммировать элементы циклом.
uint8x16_t len_v = vdupq_n_u8(0);
int reset = 0;
// тут у нас цикл
uint8x16_t gt = vcgtq_s8(operand, threshold);
len_v = vaddq_u8(vandq_u8(gt, delta), len_v);
if (++reset >= 255) {
for (size_t i = 0; i < sizeof(uint8x16_t); i++) {
len += *((uint8_t *)&len_v + i);
}
reset = 0;
len_v = vdupq_n_u8(0);
}Я был очень доволен решением (оно не совсем моё, его мне навеял код, используемый в PHP), но оказалось, что оно тоже не компилируется.
Сборка заругалась на ошибку target specific option mismatch, означающую, что указанное при компиляции железо не поддерживает те возможности, которые я пытаюсь использовать в коде. Пришлось лезть в тулчейн, чтобы разобраться какие ключи компиляции сейчас используются.
В файле compile_commands.json можно увидеть, что при сборке используется ключ -mfpu=fpv4-sp-d16, тогда как мне, чтобы использовать нужные команды векторизации нужен ключ -mfpu=neon.
Так как ключи компиляции указывали разработчики «Флиппера», думаю, они в точности знали в какие значения их выставить. Согласно спецификации на борту использованного во «Флиппере» процессора SIMD есть, но, видимо,
Пора перенести измерение длины на «Флиппер»
Кажется я то ли прокрастинирую, то ли боюсь неудачи. Предполагалось, что векторизованный алгоритм измерения длины строки в кодировке
Он вполне может не скомпилироваться — не уверен, что на «Флиппере» есть
UTF-8 на ARM
Пока проект внедрения Юникода во «Флиппер Зеро» на паузе, — разработчики занимаются обновлением одной из важных библиотек, я вспомнил, что
Например,
Любопытно посмотреть как обстоят дела с ускорением таких вещей на процессоре, который установлен на «Флиппере». Правда до него у меня руки ещё не дошли, но я уже потренировался на своём «Макбуке», благо тут процессор той же архитектуры.
Идея всё та же — векторизация. За референс я взял наивную реализацию с перебором по одному байту:
size_t strlen_naive(const char* str) {
size_t len = 0;
char ch;
while ((ch = *str++)) {
len += (ch & 0b11000000) != 0b10000000;
}
return len;
}Попробовал несколько вариантов, но лучше всех показали себя две функции: та, которую я упоминал в первом абзаце и переделанная нашими общими с братишкой усилиями реализация, найденная в недрах PHP.
После переделки она стала выглядеть так:
size_t strlen_php(unsigned char * p) {
size_t len = 0;
const int8x16_t threshold = vdupq_n_s8(-64);
const uint8x16_t delta = vdupq_n_u8(1);
for(;;) {
int8x16_t operand = vld1q_s8((const int8_t * ) p);
if (vminvq_u8(operand) == 0) {
break;
}
uint8x16_t lt = vcltq_s8(operand, threshold);
len += sizeof(uint8x16_t) - vaddvq_u8(vandq_u8(lt, delta));
p += sizeof(uint8x16_t);
}
for (signed char c; (c = *p); p++) {
if (c >= -64) {
len++;
}
}
return len;
}Повторюсь, у меня пока не дошли руки попробовать это всё на «Флиппере», но на моём «Макбуке» результаты выглядят следующим образом. Это десять тысяч прогонов на русскоязычном тексте, размер которого чуть больше ста тысяч символов.
Name Length Time
---- ------ ----
PHP 117465 97797
Fast 117465 143780
Naïve 117465 531535Не скажу, что я делал всё как положено, — надо бы считать среднее и отклонение, разносить по разным файлам, научиться прибивать процесс к производительным ядрам, но разница в результатах достаточно показательна (я запускал несколько десятков раз), поэтому, как мне кажется, и так нормально.
Цифры в попугаях (больше — хуже), которые выдаёт сишная функция clock(), компилировал компилятором Clang 15 с ключом -O3.
«Виселица» в магазине приложений
Я долго не знал как выглядит наша с дочкой игра «Виселица» в магазине приложений «Флиппера Зеро» —
А на днях вспомнил, что
Честно сказать, необходимость постоянно использовать