Уже не первый год, как во многих графических интерфейсах есть ночной цветовой режим — в смысле не тёмная тема, а светлая, но с пониженной цветовой температурой. Есть такая функция и в KDE. А недавно я обнаружил, что свежая Kubuntu 24.10 при прокрутке на значке "ночной" цветовой гаммы умеет ещё и менять яркость монитора. То есть не просто программно домножать компоненты R, G, B на коэффициент, меньший единицы, а буквально регулировать яркость подсветки монитора. Да, именно отдельностоящего монитора, подключенного по HDMI.
Впрочем, самой по себе возможности подобного управления я уже успел удивиться пару лет назад, когда решил загуглить, что же это за пункт меню под названием "DDC/CI" со значениями Enabled и Disabled. До чего, оказывается, техника дошла: когда-то давно пользовался здоровенным ЭЛТ монитором, у которого было чудо техники — USB-вход для управления настройками через специальный виндовый софт, а теперь вот случайно обнаруживаешь, что уже есть независимый от производителя стандарт (и I²C-шина в HDMI-разъёме), а из репозитория можно просто поставить консольную или графическую утилиту.
Впрочем, самой по себе возможности подобного управления я уже успел удивиться пару лет назад, когда решил загуглить, что же это за пункт меню под названием "DDC/CI" со значениями Enabled и Disabled. До чего, оказывается, техника дошла: когда-то давно пользовался здоровенным ЭЛТ монитором, у которого было чудо техники — USB-вход для управления настройками через специальный виндовый софт, а теперь вот случайно обнаруживаешь, что уже есть независимый от производителя стандарт (и I²C-шина в HDMI-разъёме), а из репозитория можно просто поставить консольную или графическую утилиту.
Пару лет назад я заказал на Алиэкспрессе отладочную плату с STM32 с намерением "ну надо же когда-нибудь приобщиться к программированию микроконтроллеров, а то чё я как лох-то?..". И вот, наконец-то решился: буду моргать светодиодом! Впрочем, не спешите с раздражением пролистывать очередной гайд по установке Arduino IDE (или что там обычно в таких случаях ставят) — сегодня будет занимательное (надеюсь) чтение technical reference manual на микроконтроллер и документации по системе команд Armv7. Ну и практика, конечно: ассемблер, gdb, все дела...
Целью сегодняшней "лабораторки" будет написание минимального примера, который можно запустить на железе, без использования готовых библиотек и прочей "магии" — буквально, получаем управление с первых тактов работы CPU, хотя и с некоторым читерством: я воспользуюсь возможностью отладки с компьютера, чтобы загрузить код прямиком в SRAM и передать на него управление.
Сразу уточню, что я не являюсь профессиональным писателем "загрузочного кода", равно как и программистом микроконтроллеров — у меня больше "компиляторостроительная" специализация — поэтому приведённый код и подход ни в коем случае не best practices, это скорее заметки вида "итак, я почти ничего не знаю про этот микроконтроллер, и я примерно понимаю, куда читать — давайте попробуем сделать хоть что-то".
#моргаем_светодиодом
Целью сегодняшней "лабораторки" будет написание минимального примера, который можно запустить на железе, без использования готовых библиотек и прочей "магии" — буквально, получаем управление с первых тактов работы CPU, хотя и с некоторым читерством: я воспользуюсь возможностью отладки с компьютера, чтобы загрузить код прямиком в SRAM и передать на него управление.
Сразу уточню, что я не являюсь профессиональным писателем "загрузочного кода", равно как и программистом микроконтроллеров — у меня больше "компиляторостроительная" специализация — поэтому приведённый код и подход ни в коем случае не best practices, это скорее заметки вида "итак, я почти ничего не знаю про этот микроконтроллер, и я примерно понимаю, куда читать — давайте попробуем сделать хоть что-то".
#моргаем_светодиодом
Для начала, с чем будем работать. В качестве отладочной платы используется одна из Black pill, в моём случае WeAct v3.0 с микроконтроллером STM32F401CDU6. Кстати, вот этот сайт, похоже, будет очень полезен как справочник: stm32-base.org — например, вот страница, описывающая мою плату (там, правда, указана микросхема ...CEU6).
Для отладки я буду использовать open hardware адаптер NanoDAP, о котором, когда-нибудь, наверное, напишу подробнее, а пока просто скажу, что из него торчит с одной стороны USB, а с другой — пины JTAG, SWD и UART. В случае STM32 я буду использовать интерфейс SWD.
При подаче на плату питания по USB или через SWD (только не одновременно двумя способами — думаю, последствия будут не ахти...), изначально в ней запускается то, что в неё прошил производитель. В моём случае это код, который... мигает светодиодом.Эксперимент завершён, расходимся.
#моргаем_светодиодом
Для отладки я буду использовать open hardware адаптер NanoDAP, о котором, когда-нибудь, наверное, напишу подробнее, а пока просто скажу, что из него торчит с одной стороны USB, а с другой — пины JTAG, SWD и UART. В случае STM32 я буду использовать интерфейс SWD.
При подаче на плату питания по USB или через SWD (только не одновременно двумя способами — думаю, последствия будут не ахти...), изначально в ней запускается то, что в неё прошил производитель. В моём случае это код, который... мигает светодиодом.
#моргаем_светодиодом
После того, как SWD-интерфейс физически присоединён, нужно понять, как через него подключиться отладчиком к микроконтроллеру. Для работы с JTAG- и SWD-адаптерами можно использовать OpenOCD: будучи правильно сконфигурирована, эта программа подсоединится к аппаратному адаптеру и откроет сетевой порт, на котором будет ожидать подключения от GDB, реализуя протокол gdb extended-remote.
Чтобы объяснить OpenOCD, с чем ей нужно работать, создадим файл
и запустим openocd:
Теперь можно запустить gdb (а точнее, придётся установить gdb-multiarch, поскольку ваш компьютер, скорее всего, тоже использует систему команд x86_64, а не 32-битный ARM) и подключиться к OpenOCD:
В примере выше
На 32-битном ARM инструкции могут иметь длину как 2, так и 4 байта, и как можно догадаться по примеру выше, мимо начала "второй с конца" инструкции я промахнулся. Попробуем ещё раз:
Вот, теперь больше похоже на правду.
#моргаем_светодиодом
Чтобы объяснить OpenOCD, с чем ей нужно работать, создадим файл
nanodap.cfg с таким содержимым:# тип адаптера, с которым будем работать
adapter driver cmsis-dap
# имеющаяся у меня реализация cmsis-dap представляется компьютеру
# как USB-устройство с VendorID 0xc251, ProductID 0xf001
cmsis_dap_vid_pid 0xc251 0xf001
transport select swd
source [find target/stm32f4x.cfg]
и запустим openocd:
$ sudo openocd -f nanodap.cfg
Open On-Chip Debugger 0.12.0
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : CMSIS-DAP: SWD supported
Info : CMSIS-DAP: JTAG supported
Info : CMSIS-DAP: SWO-UART supported
Info : CMSIS-DAP: Atomic commands supported
Info : CMSIS-DAP: Test domain timer supported
Info : CMSIS-DAP: FW Version = 2.0.0
Info : CMSIS-DAP: Interface Initialised (SWD)
Info : SWCLK/TCK = 1 SWDIO/TMS = 1 TDI = 0 TDO = 0 nTRST = 0 nRESET = 1
Info : CMSIS-DAP: Interface ready
Info : clock speed 2000 kHz
Info : SWD DPIDR 0x2ba01477
Info : [stm32f4x.cpu] Cortex-M4 r0p1 processor detected
Info : [stm32f4x.cpu] target has 6 breakpoints, 4 watchpoints
Info : starting gdb server for stm32f4x.cpu on 3333
Info : Listening on port 3333 for gdb connections
Теперь можно запустить gdb (а точнее, придётся установить gdb-multiarch, поскольку ваш компьютер, скорее всего, тоже использует систему команд x86_64, а не 32-битный ARM) и подключиться к OpenOCD:
>>> target extended :3333
Remote debugging using :3333
warning: No executable has been specified and target does not support
determining executable automatically. Try using the "file" command.
0x0800343e in ?? ()
>>> x/10i $pc-8
0x8003436: mcr2 7, 5, pc, cr10, cr13, {7} @ <UNPREDICTABLE>
0x800343a: @ <UNDEFINED> instruction: 0xfacc42b8
=> 0x800343e: beq.n 0x80034aa
0x8003440: uxtb.w r10, r9
0x8003444: bl 0x80009d4
0x8003448: mov r7, r0
0x800344a: cmp.w r10, #1
0x800344e: bne.n 0x80034aa
0x8003450: ldrb.w r1, [r8]
0x8003454: ldrh.w r0, [r8, #2]
В примере выше
>>> — это приглашение gdb, а $pc — обозначение регистра, указывающего на текущую инструкцию: program counter. x/10i $pc - 8 означает "отобрази мне содержимое памяти (x), начиная с адреса, заданного выражением $pc - 8, при этом интерпретируй содержимое как инструкции (i) в количестве 10 штук".На 32-битном ARM инструкции могут иметь длину как 2, так и 4 байта, и как можно догадаться по примеру выше, мимо начала "второй с конца" инструкции я промахнулся. Попробуем ещё раз:
>>> x/10i $pc-6
0x8003438: bl 0x80009d4
0x800343c: cmp r0, r7
=> 0x800343e: beq.n 0x80034aa
0x8003440: uxtb.w r10, r9
0x8003444: bl 0x80009d4
0x8003448: mov r7, r0
0x800344a: cmp.w r10, #1
0x800344e: bne.n 0x80034aa
0x8003450: ldrb.w r1, [r8]
0x8003454: ldrh.w r0, [r8, #2]
Вот, теперь больше похоже на правду.
#моргаем_светодиодом
Как видно из карточки платы на stm32-base, красный светодиод намертво подключен к питанию, а вот синим мы может управлять через GPIO ножку C13.
Теперь нужно найти документ, описывающий, какие регистры управляют GPIO на данном конкретном микроконтроллере, и куда они отображены в памяти. В моём случае это RM0368 — 847-страничный reference manual, но целиком читать мы его не будем: интересующие нас сейчас части — это 2.3 Memory map, из которой мы узнаём, что регистры, управляющие GPIOC, отображаются, начиная с адреса 0x40020800, и дальше рекомендуется свериться с разделом 8.4.11 для подробностей. Поскольку я не знаком с использованием GPIO на этом микроконтроллере, от краткого содержания промотаю немного назад, на начало раздела 8.4 GPIO registers. Отсюда мы узнаём, что регистр GPIOC_ODR (output data register) находится по смещению 0x14 среди прочих GPIOC_* регистров, и в нём нас интересует 13-й бит, а верхнюю половину менять не следует.
Что ж, отладчик всё ещё остановлен где-то посреди оригинальной прошивки, которая мигает светодиодом. Значит, вывод GPIO уже правильно настроен и, наверное, можно помигать светодиодом в ручном режиме, записывая правильные значения в регистр. Итак, сейчас светодиод не горит, а в регистре лежит
Как видно, 13-й бит выставлен в 1, а старшие биты, которые не следует менять — все нули. Попробуем переключить: 13-й бит выставим в 0, а все остальные и так нули:
Хоба, зажглось!
А теперь потухло! Значит, 0 — светодиод горит, 1 — не горит. Здесь я неявно подразумеваю, что на 32-битной платформе тип
#моргаем_светодиодом
Теперь нужно найти документ, описывающий, какие регистры управляют GPIO на данном конкретном микроконтроллере, и куда они отображены в памяти. В моём случае это RM0368 — 847-страничный reference manual, но целиком читать мы его не будем: интересующие нас сейчас части — это 2.3 Memory map, из которой мы узнаём, что регистры, управляющие GPIOC, отображаются, начиная с адреса 0x40020800, и дальше рекомендуется свериться с разделом 8.4.11 для подробностей. Поскольку я не знаком с использованием GPIO на этом микроконтроллере, от краткого содержания промотаю немного назад, на начало раздела 8.4 GPIO registers. Отсюда мы узнаём, что регистр GPIOC_ODR (output data register) находится по смещению 0x14 среди прочих GPIOC_* регистров, и в нём нас интересует 13-й бит, а верхнюю половину менять не следует.
Что ж, отладчик всё ещё остановлен где-то посреди оригинальной прошивки, которая мигает светодиодом. Значит, вывод GPIO уже правильно настроен и, наверное, можно помигать светодиодом в ручном режиме, записывая правильные значения в регистр. Итак, сейчас светодиод не горит, а в регистре лежит
>>> x/x 0x40020814
0x40020814: 0x00002000
Как видно, 13-й бит выставлен в 1, а старшие биты, которые не следует менять — все нули. Попробуем переключить: 13-й бит выставим в 0, а все остальные и так нули:
>>> set *(int*)0x40020814 = 0
Хоба, зажглось!
>>> set *(int*)0x40020814 = 0x2000
А теперь потухло! Значит, 0 — светодиод горит, 1 — не горит. Здесь я неявно подразумеваю, что на 32-битной платформе тип
int будет считаться 32-битным, а short, наверное, 16-битным. К сожалению, никакого бинарника с содержательной отладочной информацией в gdb сейчас не загружено, поэтому он не знает об изысках вроде uint16_t.#моргаем_светодиодом
Ну и, наконец, напишем минимальную программу. Для её компиляции я буду использовать clang (пока что программа будет только на ассемблере, но пусть так...) и ld.lld для линковки. Во-первых, я лучше знаком с этим тулчейном, а главное, если у вас на компьютере есть clang, значит почти наверняка он умеет работать с кодом для Armv7 и кучи других популярных наборов инструкций, а в случае gcc всё-таки понадобилась бы armv7-специфичная сборка. Конечно, в реальном проекте всё равно будет нужна libc и прочие библиотеки и заголовочные файлы, поэтому в любом случае придётся искать правильный полноценный тулчейн вместе с sysroot, но у нас же сейчас цель — написать свой велосипед полностью с нуля, поэтому можно обойтись первым попавшимся clang-ом и эквивалентами binutils из LLVM для просмотра, что же у нас в итоге лежит внутри ELF-файла.
Для начала прикинем, что придётся писать в ассемблерном файле помимо самих инструкций. Для этого попросим
Часть этого кода можно с натяжкой считать "лишними деталями". Плюс то, что начинается с
#моргаем_светодиодом
Для начала прикинем, что придётся писать в ассемблерном файле помимо самих инструкций. Для этого попросим
clang сгенерировать ассемблерный код (-S) для Armv7-M (-target armv7m-unknown-none — так называемый target triple, например, на обычном десктопе он бы был x86_64-linux-gnu) и напечатать его в терминал (-o -) для максимально простой программы на C — настолько простой, что clang прочитает её прямо со стандартного ввода (-x c -). Более того, я эту программу напишу прямо в командной строке через перенаправление ввода (<<< ...):$ clang -target armv7m-unknown-none -S -x c - -o - <<< 'int f(void) { return 42; }'
clang: warning: unknown platform, assuming -mfloat-abi=soft
clang: warning: unknown platform, assuming -mfloat-abi=soft
clang: warning: unknown platform, assuming -mfloat-abi=soft
.text
.syntax unified
.eabi_attribute 67, "2.09" @ Tag_conformance
.cpu cortex-m3
.eabi_attribute 6, 10 @ Tag_CPU_arch
.eabi_attribute 7, 77 @ Tag_CPU_arch_profile
.eabi_attribute 8, 0 @ Tag_ARM_ISA_use
.eabi_attribute 9, 2 @ Tag_THUMB_ISA_use
.eabi_attribute 34, 1 @ Tag_CPU_unaligned_access
.eabi_attribute 17, 1 @ Tag_ABI_PCS_GOT_use
.eabi_attribute 20, 1 @ Tag_ABI_FP_denormal
.eabi_attribute 21, 0 @ Tag_ABI_FP_exceptions
.eabi_attribute 23, 3 @ Tag_ABI_FP_number_model
.eabi_attribute 24, 1 @ Tag_ABI_align_needed
.eabi_attribute 25, 1 @ Tag_ABI_align_preserved
.eabi_attribute 38, 1 @ Tag_ABI_FP_16bit_format
.eabi_attribute 18, 4 @ Tag_ABI_PCS_wchar_t
.eabi_attribute 26, 2 @ Tag_ABI_enum_size
.eabi_attribute 14, 0 @ Tag_ABI_PCS_R9_use
.file "-"
.globl f @ -- Begin function f
.p2align 2
.type f,%function
.code 16 @ @f
.thumb_func
f:
.fnstart
@ %bb.0:
movs r0, #42
bx lr
.Lfunc_end0:
.size f, .Lfunc_end0-f
.cantunwind
.fnend
@ -- End function
.ident "Ubuntu clang version 19.1.1 (1ubuntu1)"
.section ".note.GNU-stack","",%progbits
.addrsigЧасть этого кода можно с натяжкой считать "лишними деталями". Плюс то, что начинается с
@ — расставленные компилятором комментарии.#моргаем_светодиодом
Изначально я предполагал, что получу код, который можно загрузить сразу после сброса, и всё заработает, но похоже, что нужно читать подробности по Reset and clock control, а это уже отдельная тема. Поэтому пока напишу лишь про загрузку кода "на ходу" в уже проинициализированный контроллер. Как оказалось, для этого в gdb есть удобная команда load, которой можно передать ELF-файл, и gdb его вгрузит по указанным в файле виртуальным адресам.
Теперь нужно этот ELF как-то получить. А что для этого нужно? Содержимое секций — это в моём случае машинный код, тут, вроде, всё понятно: создаю entry.S, пишу туда ассемблерные инструкции, дописываю немного служебных директив вроде
Судя по тому, что указано в таблицах в разделе 2.4 Boot configuration, в каком бы мы режиме ни загрузились, embedded SRAM всегда начинается с адреса 0x20000000 — строчка
мы сначала превращаем
... и #моргаем_светодиодом ! Фух, на сегодня всё :)
Теперь нужно этот ELF как-то получить. А что для этого нужно? Содержимое секций — это в моём случае машинный код, тут, вроде, всё понятно: создаю entry.S, пишу туда ассемблерные инструкции, дописываю немного служебных директив вроде
.text и .p2align, и готово. В объектном файле, сгенерированном компилятором (здесь — entry.o) есть несколько секций: таблица строк с именем .strtab, секция с описанием релокаций, которые линкер должен применить к коду (но это ещё одна отдельная тема, а в финальном бинарнике в нашем случае их остаться всё равно не должно) и т. д. Также есть секция .text, содержащая машинный код, который "ну, допустим, по нулевому адресу". А откуда берутся адреса загрузки в финальном файле? Тут на помощь придёт другой тип "исходника": linker script. В первом приближении это описание, как взять пачку входных файлов и перекомпоновать секции оттуда, чтобы получить выходной файл. Поскольку мне хочется сделать максимально простой пример, скрипт получился такой:SECTIONS {
. = 0x20000000;
.text : { *(.text) }
/DISCARD/ : { INPUT_SECTION_FLAGS(SHF_ALLOC) *(*) }
}Судя по тому, что указано в таблицах в разделе 2.4 Boot configuration, в каком бы мы режиме ни загрузились, embedded SRAM всегда начинается с адреса 0x20000000 — строчка
. = 0x20000000 присваивает это значение "счетчику" текущего адреса выходного файла. Далее говорится собрать секции с именем .text из всех входных файлов (*(.text)) и сформировать из этого секцию .text результата. А всё остальное, что могло бы быть загружено в память (в отличие от таблицы строк и прочей "справочной" информации) — выкинуть (discard): плата, конечно, недорогая, так что градус паранойи поменьше, да и при записи во всякие однократно записываемые "фьюзы", наверняка, есть какая-нибудь защита от дурака, но всё-таки лучше не грузить что попало куда попало, если это не планировалось. При помощи командclang -target thumbv7m-unknown-none -c entry.S
ld.lld -o image.elf -T image.lds entry.o
мы сначала превращаем
entry.S в бинарник entry.o, а потом при помощи image.lds, приведённого выше, формируем image.elf. Собираем, загружаем с помощью gdb:>>> load image.elf
Loading section .text, size 0x32 lma 0x20000000
Start address 0x00000000, load size 50
Transfer rate: 400 bits in <1 sec, 50 bytes/write.
>>> set $pc=0x20000000
>>> continue
... и #моргаем_светодиодом ! Фух, на сегодня всё :)
Между прочим, 31 марта (то есть за день до 1 апреля) празднуется международный день бэкапа!
Раз уж сегодня День Дурака, похвастаюсь своими недавним достижением из рубрики "я сделяль": колпачок для разъёма XT60, отлитый из термоклея. По моему, идея повторить призматическую форму разъёма, изготовив опалубку из малярного скотча — это достаточно, ну эээ... празднично. Не то, чтобы такой разъём был необходим, чтобы запитать Orange Pi 5 (это как раз тот самый #маленький_но_гордый_сервер), но просто я уже задолбался экспериментировать со способами уменьшить падение напряжения (нет, на OPi5 всё нормально с питанием через Type-C, просто захотелось избавиться от мешанины кабелей).
Disclaimer: если что, я ни в коем случае не призываю подобное повторять. Тем более там, где разъёмы на десятки ампер действительно необходимы. Просто конкретно мне конкретно в этом случае такой способ показался допустимым (колпачка в комплекте не было, надеть термоусадку и не усадить её при пайке было проблематично, а сооружать комок хитро подсунутой изоленты не хотелось).
Disclaimer: если что, я ни в коем случае не призываю подобное повторять. Тем более там, где разъёмы на десятки ампер действительно необходимы. Просто конкретно мне конкретно в этом случае такой способ показался допустимым (колпачка в комплекте не было, надеть термоусадку и не усадить её при пайке было проблематично, а сооружать комок хитро подсунутой изоленты не хотелось).
Захотел купить более удобное жало для паяльника, решил загуглить, что именно подразумевается под обозначением "900M-T-I" (запрос содержал конкретно эту строку без других слов), а гугл мне предложил помочь с математическими задачками (что бы под этим ни подразумевалось). Такой формат "быстрых ответов" я увидел впервые.
Из примера на cppreference:
class ThreadsafeCounter
{
mutable std::mutex m; // The "M&M rule": mutable and mutex go together
int data = 0;
// ...
}
🔥3
На Алиэкспрессе каталог каждого продавца может быть сгруппирован по категориям, и вот сразу у нескольких продавцов радиодеталей обнаружилась загадочная категория с названием "повел". В принципе, иногда даже "технические" аббревиатуры хорошо звучат, например, "мосфет", а уж "маркетинговый" термин тем более будет красивым. Хоть я и не планировал покупать загадочные по́велы, но интересно же, что так назвали — наверное, какую-нибудь мудрёную и современную микросхему, подающую питание на что-нибудь ещё более заковыристое и высокотехнологичное — ну, там, от power и electronics...
Почему-то в этой категории были просто какие-то светодиоды. Ну, думаю, может особый тип для каких-нибудь умных светодиодных лент... А потом до меня дошло: светодиод — это же грамматическая форма свинца: lead / led / LED. Ну а в названии категории потерялись точки над "ё".
Почему-то в этой категории были просто какие-то светодиоды. Ну, думаю, может особый тип для каких-нибудь умных светодиодных лент... А потом до меня дошло: светодиод — это же грамматическая форма свинца: lead / led / LED. Ну а в названии категории потерялись точки над "ё".
❤2
Загадка: что может содержать файл с именем proton-pack.c в ядре Linux?
Ответ:противодействие уязвимостям типа Spectre 😀
Пояснение в комментарии в "шапке":"If there's something strange in your neighbourhood, who you gonna call?"
Ответ:
Пояснение в комментарии в "шапке":
В копилку научного юмора и искусства в целом: Dance Your PhD 2025. Если обладатели главного приза кажутся слишком шумными, то вот, например, красивый спокойный клип про лазерное охлаждение атомов до сверхнизких температур что бы это ни значило :)
YouTube
Laser Cooling & Ultracold Atoms | Dance Your PhD 2025 [Physics & AI/Quantum Winner]
Lasers are awesome. Not only because they look cool, but also because they are extremely useful to science. It might sound counter-intuitive, but to make the coldest things that exist, possibly in the entire universe, we need lasers. By carefully tuning the…
Обновил телефон (с Самсунга обратно на Моторолу и прекрасно себя чувствую), обновил ОС на телефоне. После обновления до Android 15 захотел поставить из F-Droid уже упоминавшийся на этом канале SatStat, да не тут то было: оказалось, что в Android 15 слишком старые приложения поставить нельзя (документация). Ну, то есть, не совсем так: во-первых, судя по всё той же странице документации, гайки начали закручивать ещё в Android 14. Во-вторых, поставить приложение всё же можно — с компьютера.
Итак, что я наблюдаю в F-Droid: плашка "Это приложение было разработано для старой версии Андроид..." (что пока ещё ничего не означает: у самого приложения F-Droid, через которое я смотрю каталог, в собственной карточке такая же плашка и ничего) и сказано, что совместимых с моим устройством версий нет. Кнопки "установить", соответственно, тоже. Проверяю предположение: из F-Droid на телефоне перекидываю через любой мессенджер ссылку на приложение себе же в браузер на компьютере. Из списка версий выбираю какую-нибудь посвежее, скачиваю APK-файл и пробую установить через ADB (добавлены переводы строк, чтобы не так сильно растягивать текст по горизонтали):
Ага, то самое изменение из Android 15. По совету из всё той же документации добавляю опцию
Работает! Едва ли я бы хотел таким способом ставить что-то из Google Play — хотя бы потому, что не совсем понятно, откуда брать "эталонный" APK, который точно-точно без дополнительных троянов, да и при наличии альтернатив, возможно, стоит поискать какое-нибудь менее заброшенное приложение. Но к программам из репозитория* F-Droid лично у меня доверия больше, а правильный APK можно без лишних плясок с бубном скачать по удобной ссылке. В общем, опцияудобная полезная, но использовать нужно на свой страх и риск: здесь никто за вас новому apk подпись не проверит.
* Почему важно уточнение именно о "репозитории" F-Droid — потому что существуют сторонние репозитории в том же формате, их так же можно добавлять в официальный клиент F-Droid, но это будут уже совершенно независимые, не контролируемые и не проверяемые авторами F-Droid источники ПО.
#android
Итак, что я наблюдаю в F-Droid: плашка "Это приложение было разработано для старой версии Андроид..." (что пока ещё ничего не означает: у самого приложения F-Droid, через которое я смотрю каталог, в собственной карточке такая же плашка и ничего) и сказано, что совместимых с моим устройством версий нет. Кнопки "установить", соответственно, тоже. Проверяю предположение: из F-Droid на телефоне перекидываю через любой мессенджер ссылку на приложение себе же в браузер на компьютере. Из списка версий выбираю какую-нибудь посвежее, скачиваю APK-файл и пробую установить через ADB (добавлены переводы строк, чтобы не так сильно растягивать текст по горизонтали):
$ adb install /tmp/com.vonglasow.michael.satstat_30600.apk
Performing Streamed Install
adb: failed to install /tmp/com.vonglasow.michael.satstat_30600.apk:
Failure [INSTALL_FAILED_DEPRECATED_SDK_VERSION: App package must target
at least SDK version 24, but found 23]
Ага, то самое изменение из Android 15. По совету из всё той же документации добавляю опцию
--bypass-low-target-sdk-block:$ adb install --bypass-low-target-sdk-block /tmp/com.vonglasow.michael.satstat_30600.apk
Performing Streamed Install
Success
Работает! Едва ли я бы хотел таким способом ставить что-то из Google Play — хотя бы потому, что не совсем понятно, откуда брать "эталонный" APK, который точно-точно без дополнительных троянов, да и при наличии альтернатив, возможно, стоит поискать какое-нибудь менее заброшенное приложение. Но к программам из репозитория* F-Droid лично у меня доверия больше, а правильный APK можно без лишних плясок с бубном скачать по удобной ссылке. В общем, опция
* Почему важно уточнение именно о "репозитории" F-Droid — потому что существуют сторонние репозитории в том же формате, их так же можно добавлять в официальный клиент F-Droid, но это будут уже совершенно независимые, не контролируемые и не проверяемые авторами F-Droid источники ПО.
#android
Кстати, похоже, что в Андроиде есть разрешения, которые в принципе доступны и без рутования телефона, но выдаваться должны через ADB, а не обычный пользовательский интерфейс (тут, впрочем, нужно оговориться, что я совсем не Андроид-разработчик, и, например, не вполне уверен, что можно заявлять о неком стандартном интерфейсе управления правами, хотя в документации я и вижу достаточно чёткое разделение на normal, runtime a.k.a. dangerous permissions и ряд других типов).
Например, в репозитории F-Droid есть приложение LogFox, которое при первом запуске просит выдать права через ADB:
После чего при начале записи логов система переспросит уже на экране устройства, хотим ли мы позволить приложению читать "глобальные" логи. Если разрешили, можно видеть, например, такое:
Или вот, например, SaverTuner: при первом запуске ждёт, пока ему не выдадут разрешение:
После чего позволяет выбирать один из нескольких профилей экономии, а внутри профиля — переключать большое количество параметров из серии "разрешить анимации", "отложить полное резервное копирование" и т.д.
Лично я для себя пока не нашёл "полезный в быту" пермишн и приложение, его использующее, но сам факт существования таких разрешений кажется мне полезным знанием.
#android
Например, в репозитории F-Droid есть приложение LogFox, которое при первом запуске просит выдать права через ADB:
adb shell pm grant com.f0x1d.logfox android.permission.READ_LOGS
После чего при начале записи логов система переспросит уже на экране устройства, хотим ли мы позволить приложению читать "глобальные" логи. Если разрешили, можно видеть, например, такое:
libPowerHal: [setClusterFreq] system_server: sysfs_freq set cpu freq: 1750000 -1 1850000 -1
Или вот, например, SaverTuner: при первом запуске ждёт, пока ему не выдадут разрешение:
adb shell pm grant s1m.savertuner android.permission.WRITE_SECURE_SETTINGS
После чего позволяет выбирать один из нескольких профилей экономии, а внутри профиля — переключать большое количество параметров из серии "разрешить анимации", "отложить полное резервное копирование" и т.д.
Лично я для себя пока не нашёл "полезный в быту" пермишн и приложение, его использующее, но сам факт существования таких разрешений кажется мне полезным знанием.
#android
Первого апреля я уже хвастался колпачком для разъёма XT60, отлитым из термоклея. И вот недавно история получила не менее первоапрельское продолжение... Как я уже говорил, столь суровый разъём требовался, чтобы подключить одноплатник, питающийся от пяти вольт, без значительного падения напряжения на разъёме, потому что со штекерами-бочонками я к тому моменту уже намучился. Но кроме одноплатника линии 5 и 12 вольт шли ещё и на "метеостанцию" на ESPHome, роутер и ethernet-свич, поэтому мешанина проводов была та ещё. Я захотел хоть как-то это смотать и убрать в обычную электромонтажную распределительную коробку, а чтобы упростить себе перекоммутацию в стеснённых условиях, повесить эту коробку на край столешницы при помощи примерно таких крючков с Алиэкспресса.
Начитавшись отзывов, мол, "крючки хорошие, но мне пришли без липучек", осмотрел то, что прислали мне. С грустью отметил, что у меня тоже просто крючки, и стал лепить на двусторонний скотч. А они, заразы, ещё и как из вредности к скотчу не липнут... Ладно, к распределительной коробке с горем пополам прилепил на термоклей (который, разумеется, тоже отваливался, но я упорный, я через край чисто механически прихватил, а потом долго подрезал ножом, чтобы нигде не клинило...). Но не буду же я к столу ответную часть тоже на термоклей лепить... Тут я вспомнил про сантехнический скотч, решил сделать из него кольцо клеевой стороной наружу и хоть как-то уже прилепить ответную часть крючка.
Отклеил от мотка скотча небольшой участок, приложил туда крючок... а он отваливается!!! Да вы что, издеваетесь, что же это за крючки такие, что их даже чудо-скотч не берёт?!? И тут-то я пригляделся и заметил, что на скотче осталась прозрачная плёночка... 🤦♂️ В общем, всё нормально: честь сантехнического скотча восстановлена, а я усвоил урок, что если тебе кажется, что некая поверхность отваливается от двустороннего скотча, термоклея и прочих нормальных липучек, словно специально спроектирована, чтобы легко отваливаться от любого клея, то возможно, тебе не кажется. После снятия защитной плёнки "предустановленный" клеевой слой крючка благополучно прилип, куда следовало.
Начитавшись отзывов, мол, "крючки хорошие, но мне пришли без липучек", осмотрел то, что прислали мне. С грустью отметил, что у меня тоже просто крючки, и стал лепить на двусторонний скотч. А они, заразы, ещё и как из вредности к скотчу не липнут... Ладно, к распределительной коробке с горем пополам прилепил на термоклей (который, разумеется, тоже отваливался, но я упорный, я через край чисто механически прихватил, а потом долго подрезал ножом, чтобы нигде не клинило...). Но не буду же я к столу ответную часть тоже на термоклей лепить... Тут я вспомнил про сантехнический скотч, решил сделать из него кольцо клеевой стороной наружу и хоть как-то уже прилепить ответную часть крючка.
Отклеил от мотка скотча небольшой участок, приложил туда крючок... а он отваливается!!! Да вы что, издеваетесь, что же это за крючки такие, что их даже чудо-скотч не берёт?!? И тут-то я пригляделся и заметил, что на скотче осталась прозрачная плёночка... 🤦♂️ В общем, всё нормально: честь сантехнического скотча восстановлена, а я усвоил урок, что если тебе кажется, что некая поверхность отваливается от двустороннего скотча, термоклея и прочих нормальных липучек, словно специально спроектирована, чтобы легко отваливаться от любого клея, то возможно, тебе не кажется. После снятия защитной плёнки "предустановленный" клеевой слой крючка благополучно прилип, куда следовало.