Linux на X96 Max Plus

Сегодня будем экономить на Raspberry Pi и поставим armbian на X96 Max Plus.

Прошу обратить внимание!

Моя модификация - X96MaxPlus_2101. Соответственно, действия описаны для этой модификации. Для других модификаций действия могут отличаться.

Подробнее о модификациях: 4pda

Подготовка USB накопителя

Для начала скачиваем образ файловой системы для нужного процессора отсюда: ophub/amlogic-s9xxx-armbian.

Ищем в списке последний релиз для процессора s905x3. В момент написания статьи это был файл Armbian_23.11.0_amlogic_s905x3_lunar_6.1.62_server_2023.11.12.img.gz.

Ищем USB флешку. Моя была на 8 ГБ (успешно загружался и с 16). С помощью какой-либо программы записываем образ диска на неё. Я использовал Rufus. Можно использовать что угодно, что умеет записывать сырой образ диска на накопитель (Balena Etcher / dd / etc.).

В Rufus сразу выбираем .gz архив, программа сама его распаковывает в процессе записи.

rufus

После записи образа на накопителе открываем раздел fat32, в нём файл uEnv.txt. Меняем имя файла в параметре FDT на meson-sm1-x96-max-plus-2101.dtb:

LINUX=/zImage
INITRD=/uInitrd
-- FDT=/dtb/amlogic/meson-sm1-x96-max-plus-100m.dtb
++ FDT=/dtb/amlogic/meson-sm1-x96-max-plus-2101.dtb
APPEND=root=UUID=a40af2d2-999f-4b37-98b6-973b0e6faf52 rootflags=data=writeback rw rootfstype=ext4 console=ttyAML0,115200n8 console=tty0 no_console_suspend consoleblank=0 fsck.fix=yes fsck.repair=yes net.ifnames=0 max_loop=128 cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory swapaccount=1

Загрузка с USB накопителя

Вставляем USB накопитель в приставку с отключенным питанием, зажимаем Reset (находится внутри гнезда AV), подключаем питание, держим Reset ~5 секунд.

На видео нет логотипа стоковой прошивки, потому что на устройство был уже установлен armbian.

После запуска скрипт предложит сконфигурировать систему.

firsrun

После предложения ввести новый пароль суперпользователя лучше немного подождать, так как система всё ещё загружается и будут выводиться сообщения из журнала, сбивающие с толку.

Отвечаем на вопросы и завершаем установку.

Установка на eMMC

На данном этапе вся конфигурация сохраняется на USB накопитель. Для того, чтобы загрузить armbian нужно будет каждый раз нажимать reset при загрузке.

Чтобы установить armbian на встроенный eMMC накопитель и загружаться с него нужно выполнить:

armbian-install -m yes

Без параметра -m у меня отказывалась загружаться операционная система.

Сам параметр расшифровывается как “Use Mainline u-boot” (см. тут).

При установке будет предложено выбрать устройство и файловую систему (я выбрал X96 Max Plus 2101 и ext4). После того, как скрипт завершит работу, вводим poweroff, извлекаем USB накопитель и переподключаем питание. Система теперь будет загружаться со встроенной памяти.

Если приставка к Wi-Fi не подключена, то советую это сделать (с помощью armbian-config или nmcli). Через Ethernet подключить вроде как не получится, проблемы с драйвером.

Если вы уже подключили приставку к Wi-Fi, то можно управлять ей с помощью ssh (в дистрибутиве уже настроен ssh сервер).

Решение проблем с нестабильным Wi-Fi

В процессе работы я начал наблюдать подобные сообщения ядра:

[   83.751980] ------------[ cut here ]------------
[   83.754595] firmware failed to ack driver for entering Deep Power mode
[   83.760091] WARNING: CPU: 1 PID: 9 at drivers/net/wireless/realtek/rtw88/ps.c:106 rtw_power_mode_change+0x154/0x1a0 [rtw88_core]
[   83.771493] Modules linked in: cpufreq_userspace cpufreq_conservative cpufreq_powersave sunrpc hci_uart btqca btrtl btbcm btintel rtw88_8822cs rtw88_8822c rtw88_sdio rtw88_core bluetooth panfrost mac80211 gpu_sched snd_soc_meson_axg_sound_card drm_shmem_helper snd_soc_meson_card_utils snd_soc_meson_g12a_tohdmitx cfg80211 snd_soc_meson_axg_tdmout snd_soc_meson_codec_glue rfkill snd_soc_meson_axg_frddr libarc4 snd_soc_meson_axg_fifo zram ir_nec_decoder meson_gxbb_wdt meson_imeson_vdec(C) videobuf2_dma_contig v4l2_mem2mem snd_soc_meson_axg_tdm_interface snd_soc_meson_axg_tdm_formatter tcp_bbr sch_fq ip_tables x_tables hid_logitech_hidpp hid_logitech_dj
[   83.832293] CPU: 1 PID: 9 Comm: kworker/u8:0 Tainted: G         C         6.1.62-ophub #1
[   83.840398] Hardware name: AMedia X96 Max+ (DT)
[   83.844885] Workqueue: phy0 rtw_watch_dog_work [rtw88_core]
[   83.850404] pstate: 60400009 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
[   83.857302] pc : rtw_power_mode_change+0x154/0x1a0 [rtw88_core]
[   83.863166] lr : rtw_power_mode_change+0x154/0x1a0 [rtw88_core]
[   83.869031] sp : ffffffc00a18bcc0
[   83.872309] x29: ffffffc00a18bcc0 x28: ffffff800005bd00 x27: ffffff800000ec00
[   83.879382] x26: ffffff80030908e0 x25: ffffff8003092080 x24: 0000000000000001
[   83.886454] x23: 0000000000068dbc x22: 00000013843748d4 x21: 0000000000000001
[   83.893527] x20: 0000000000000000 x19: ffffff8003092080 x18: ffffffffffffffff
[   83.900599] x17: 0000000000000004 x16: 0000000000000000 x15: 0000000000000006
[   83.907671] x14: 0000000000000000 x13: ffffffc009d9ce50 x12: 0000000000000648
[   83.914744] x11: 0000000000000218 x10: ffffffc009e4ce50 x9 : ffffffc009d9ce50
[   83.921816] x8 : 00000000ffffdfff x7 : ffffffc009e4ce50 x6 : 0000000000000001
[   83.928889] x5 : 0000000000000000 x4 : 0000000000000000 x3 : 0000000000000000
[   83.935961] x2 : 0000000000000000 x1 : 0000000000000000 x0 : ffffff80000a7000
[   83.943035] Call trace:
[   83.945810]  rtw_power_mode_change+0x154/0x1a0 [rtw88_core]
[   83.950970]  rtw_sdio_deep_ps+0x90/0xd0 [rtw88_sdio]
[   83.955886]  rtw_enter_lps+0xf4/0x110 [rtw88_core]
[   83.960629]  rtw_watch_dog_work+0x25c/0x270 [rtw88_core]
[   83.965890]  process_one_work+0x1dc/0x370
[   83.969858]  worker_thread+0x168/0x4e0
[   83.973567]  kthread+0xd4/0xdc
[   83.976585]  ret_from_fork+0x10/0x20
[   83.980123] ---[ end trace 0000000000000000 ]---

Связано это с проблемами входа wi-fi модуля в режим экономии энергии. Поэтому эту самую экономию мы отключим.

Открываем файл /etc/NetworkManager/conf.d/default-wifi-powersave-on.conf и значение параметра wifi.powersave изменяем с 3 на 2:

sudo nano /etc/NetworkManager/conf.d/default-wifi-powersave-on.conf
[connection]
# Values are 0 (use default), 1 (ignore/don't touch), 2 (disable) or 3 (enable).
-- wifi.powersave = 3
++ wifi.powersave = 2

После этого перезагружаемся (команда reboot). Проверить, отключилось ли энергосбережение можно командой:

sudo iwconfig wlan0 | grep "Power Management"

Результат должен быть Power Management:off.

Вывод произвольного текста на семисегментный дисплей

В репозитории ophub/amlogic-s9xxx-armbian описывается как включать или отключать индикаторы на дисплее (TL;DR: sudo armbian-openvfd). Однако нет информации как выводить произвольный текст вместо часов.

После изучения исходников сервиса openvfd стало понятно, что сервис использует канал по пути /tmp/openvfd_service. При записи в этот файл, сервис отключает вывод по умолчанию (часы) и выводит заданную информацию. Сама информация передаётся в виде структуры vfd_display_data (openvfd_drv.h). В этой структуре достаточно установить mode = DISPLAY_MODE_TITLE и заполнить string_main.

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

Что нужно сделать?

Клонировать репозиторий arthur-liberman/linux_openvfd:

git clone https://github.com/arthur-liberman/linux_openvfd

Перейти в директорию репозитория:

cd linux_openvfd

Создаём исходный файл программы. Я назвал его set-display-text.c:

nano set-display-text.c

Код:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include "driver/openvfd_drv.h"

#define SERVICE_PIPE "/tmp/openvfd_service"

int main(int argc, char const *argv[])
{
    if (argc < 2)
    {
        fprintf(stderr, "Text not provided\n");
        exit(1);
    }

    // Открыть именованный канал сервиса
    int service_fd = open(SERVICE_PIPE, O_RDWR);

    if (service_fd < 0)
    {
        perror("Open " SERVICE_PIPE " failed\n");
        exit(1);
    }

    // Структура данных драйвера дисплея
    static struct vfd_display_data data;

    // Инициализировать структуру пустыми значениями
    memset(&data, 0, sizeof(data));

    // Режим вывода на экран - произвольный текст
    data.mode = DISPLAY_MODE_TITLE;

    // Скопировать максимум 4 символа
    strncpy(data.string_main, argv[1], 4);
    data.string_main[5] = '\0';

    // Записать в канал сервиса
    int written = write(service_fd, &data, sizeof(data));

    if (written != sizeof(data))
    {
        perror("Write failed\n");
    }

    close(service_fd);

    return 0;
}

Компилируем:

gcc -O2 -Werror set-display-text.c -o set-display-text

На всякий случай оставлю тут собранный бинарник: set-display-text

Проверяем.

Сначала запускаем драйвер и сервис (если на экране уже что-то выводится, то сервис уже запущен!):

sudo armbian-openvfd

Или

sudo armbian-openvfd 12

Где 12 - номер модели (x96maxplus s905x3).

Далее проверяем нашу утилиту:

sudo ./set-display-text lol
lol.jpg

Если всё работает, то можно скопировать исполняемый файл в локальное окружение для того, чтобы можно было его выполнять из любого места:

sudo cp set-display-text /usr/local/bin

Автозапуск драйвера и сервиса индикации

Открываем файл автозапуска и меняем строки:

sudo nano /etc/custom_service/start_service.sh
# Led display control
-- openvfd_enable="no"
++ openvfd_enable="yes"
-- openvfd_boxid="15"
++ openvfd_boxid="12"
[[ "${openvfd_enable}" == "yes" && -n "${openvfd_boxid}" && -x "/usr/sbin/armbian-openvfd" ]] && {
    armbian-openvfd ${openvfd_boxid} &&
        echo "[$(date +"%Y.%m.%d.%H:%M:%S")] The openvfd service started successfully." >>${custom_log}
}

openvfd_boxid можно узнать выполнив sudo armbian-openvfd.

Вывод текста на экран без root прав

Так как каналу /tmp/openvfd_service не установлено никаких групп кроме root, то ничего не остается, кроме как разрешить пользователю выполнять команду через sudo без пароля.

Для этого нужно создать файл /etc/sudoers.d/allow-set-display-text-nopasswd с содержимым multimote ALL=(ALL) NOPASSWD: /usr/local/bin/set-display-text, где:

  • allow-set-display-text-nopasswd - любое имя для правила
  • multimote - необходимое имя пользователя

Создать правило одной командой:

echo "multimote ALL=(ALL) NOPASSWD: /usr/local/bin/set-display-text" | sudo tee /etc/sudoers.d/allow-set-display-text-nopasswd

Отображение процентов печати в Klipper

Мне не раз попадалась информация об установки klipper на тв-приставки. Klipper - прошивка для 3d принтеров, состоящая из двух частей. Первая часть устанавливается на контроллер самого 3d принтера, а вторая на любой компьютер, который поддерживает установку Klipper.

Установка производится через kiauh и не вызывает никаких трудностей.

Для отображения прогресса печати на дисплее x96 Max Plus я использовал дополнение G-Code Shell Command.

Устанавливается дополнение тоже через kiauh. Для этого нужно перейти в меню Advanced:

kiauh1

И выбрать там G-Code Shell Command:

kiauh2

Теперь нужно добавить в printer.cfg следующее:

# Определить команду для вывода на дисплей
# Важно! Для команды sudo set-display-text должен быть отключен ввод пароля (см. sudo NOPASSWD)
[gcode_shell_command set_display_text]
command: sudo /usr/local/bin/set-display-text
verbose: False

# Перехватить команду M73
[gcode_macro M73]
rename_existing: M73.1
gcode:
    {% set P = params.P|default(0)|int %}
    M73.1 {rawparams}
    RUN_SHELL_COMMAND CMD=set_display_text PARAMS={P}

После этого можно использовать команду M73 для вывода прогресса печати на дисплей (например, для 55% - M73 P55). Осталось сконфигурировать слайсер для того, чтобы он добавлял эти команды в g-код. Для PrusaSlicer достаточно включить Supports remaining times в настройках принтера:

prusaslicer
printprogress
Прогресс печати на Ender-3