- PVSM.RU - https://www.pvsm.ru -

Интернет радио, продолжение

Радио 2.0

Радио 2.0

Всем привет! Приемник интернет-радио, собранный в предыдущей статье Интернет-радио на базе ESP32 и ЦАП UDA1334A [1] , имел некоторые недостатки. А именно: были частые потери сигнала wifi, медленная и глючная работа в целом, случайные перезагрузки. Короче, он перестал меня устраивать и я решил продолжить тему проигрывателя интернет радио, но на базе другой аппаратной платформы.

Варианты аппаратной части

В первую очередь, как я думал, основные проблемы связаны с использованием ESP32, ее или не хватало по мощности, или код karadio не слишком оптимизирован... Но неважно, надо искать что -то более мощное, как мне на тот момент казалось. Подключать будущее устройство планировалось так же через miniJack 3.5 к колонкам на столе.

Сначала я решил попробовать одноплатники, но тест на том же Orange Pi Zero показал удовлетворительные качества по встроенному ЦАП, решил смотреть куда то еще. Итого, сформировался примерно такой список критериев к будущей аппаратной основе:

  • скорее всего ARM

  • нормальный встроенный ЦАП

  • разьем minijack 3.5

  • питание от microUsb или usb type C

  • желательно, экран

Иии под эти критерии подходит идеально... Обычный бюджетный смартфон на Android! Уже все собрано, причем в компактном плоском корпусе. И с проигрыванием проблем нет, мультимедиа возможности - один из основных параметров более менее современного смартфона. Вопрос только к программной части, но про это дальше. К сожалению, у меня доступного для опытов смартфона не было, поэтому было решено купить просто новый бюджетный аппарат.

Выбор в итоге пал на Oppo A83. Я покупал за 3300Р версию с 4 Гб, на момент написания статьи вариант с 6 Гб (хотя, это и не важно) стоит 2300-3000Р на маркетплейсах. Более чем бюджетно, на мой взгляд.

Oppo A83

Oppo A83

Сразу оговорюсь, что телефон планировалось использовать исключительно как проигрыватель радио и дополнительно ничего там не хранить и не использовать. Итого, как тест были установлены пару приложений интернет радио из .apk, они работали, радио играли, но не те, что я хотел, и хотелось какой-то гибкой настройки. В идеале, хотелось взаимодействия со смартфоном примерно так же, как с ESP32 прошитой karadio. Когда интерфейс чисто для базовых манипуляций (поменять громкость, переключить станцию), а вся глубокая и тонкая настройка выполняется через веб интерфейс, к которому я могу подключиться с любого устройства в той же сети. Решено двигаться в этом направлении.

Приложение

Ничего одновременно и простого (чисто под интернет радио) и с наличием веб интерфейса я не нашел. Были попытки использовать Plex, но, по мне, это мегакомбайн, который, к тому же, не сразу завелся на телефоне (на Oppo A83 относительно старый Android 9.1). В итоге я немного забросил эту идею, пока не появилось чуть больше времени и подписка gpt plus :) . Решено полностью навайбкодить решение под свои нужны! Сразу оговорюсь, я не являюсь разработчиком под Android или IOT, поэтому не могу оценить качество полученного говнокода результата. По итогу, функционально, он меня более чем устроил.

Писать проект будем полностью через диалог с chatgpt. Сначала я решил определиться с решением и планируемым в использовании языков/фреймворков.

Варианты реализации

Сравнительная таблица анализа по выбору.

Вариант

Фон надёжность

Android Auto/гарнитура

Скорость разработки

Кроссплатформ

Проблемы CORS

PWA

средняя

базово (Media Session)

высокая

да (веб)

да

TWA

как PWA

как PWA

высокая

да (веб)

да

Capacitor/Cordova

средняя/высокая*

да (через плагины)

высокая

да (iOS/Android)

нет (ч/з натив)

React Native

высокая

да

средняя

да

нет

Flutter

высокая

да

средняя

да

нет

Нативный Kotlin

макс

да (лучше всего)

ниже

нет

нет

Как вариант, я рассматривал как нативное приложение, так и чисто веб, которое открывается в браузере телефона. Но в итоге решил пойти все же в натив. Не скажу точно почему (скорее просто из любопытства) решил в итоге выбрать вариант с flutter. Ну и посмотреть на dart.

Flutter Radio

Так, спустя пару вечеров вайбкодинга родилось приложение flutter radio. Я решил не менять изначальное название и не особо менял какие-то дефолтные настройки проекта, они меня более чем устроили. Сам проект доступен на github [2], и через github release [3]выложил уже собранный apk для установки ни телефон.

Основные возможности приложения:

  • Тёмная тема, полноэкранный режим.

  • Поддержка MP3/AAC потоков

  • Плей/пауза, след/пред станция, громкость (слайдер).

  • Редактирование станций: добавление, удаление, изменение порядка (перетаскивание).

  • Отображение текущей играющей композиции (ICY-метаданные, если их отдаёт станция).

  • Встроенный веб-пульт по Wi-Fi:

  • Удалённое управление со страницы в локальной сети.

  • Локальный REST API (OpenAPI спецификация).

Требования (Android):

  • Android 7.0+ (API 24+) — рекомендуется Android 8.0+.

  • Подключение к интернету (для потоков).

Использованные технологии:

  • Flutter (Material 3).

  • just_audio [4] — плеер для работы с потоками.

  • audio_service [5] — уведомление/сервис воспроизведения и интеграция с системными контролами.

  • Чистый dart:io для HTTP-сервера (без внешних зависимостей).

  • ICY-метаданные из just_audio (icyMetadataStream).

Домашняя страница

Домашняя страница

Как предустановленные значения станций я добавил несколько станций моей любимой площадки 1.FM, значения станций можно поменять после запуска приложения. Вот, например, вариант ссылки на список [6]доступных станций, тысячи их! Выбирайте на свой вкус и цвет.

Как базовые настройки (кроме редактирования списка самих станций) я добавил тоггл для старта веб интерфейса и тоггл по логике паузы.

Т.к. радиостанции - это бесконечный прогрессивный поток (MP3/AAC по HTTP), то плеер (ExoPlayer внутри just_audio) ведёт себя так:

  • при Pause он просто останавливает воспроизведение и оставляет уже загруженный буфер;

  • при Play он продолжает с того места буфера, где остановился (получается эффект «тайм-шифта»), а не «переподключение на живой край».

Решено сделать настройкой - или обеспечить проигрывание с живого края, или с места паузы. Кому как больше нравится. Мне больше нравится «живой край».

Из известных багов в приложении осталось неявное переключение тогглов. При первичном старте и нажатии на тоггл он не отображается переключенным. Но, если закрыть меню настроек (шестерня), и открыть снова, то он будет отображаться включенным. Попытки пофиксить это приводили gpt в цикл ошибок и генерации явно некомпилируемого кода. В итоге я решил, что поведение не критично. Тогллы активируются (обычно) один раз при старте и кривотой можно пренебречь. После активации тоггла веб интерфейса отображается адрес в локальной сети (предполагаем, что устройство в локальной сети роутера) для подключения к нему.

Меню настроек

Меню настроек

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

Отображение веб интерфейса в мобильном Safari

Отображение веб интерфейса в мобильном Safari

Итого, со стабильностью работы с сетью и воспроизведением самих радиостанций проблем нет. Это основное, ради чего все затевалось. Но, были некоторые неудобства по интерфейсу.

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

Интернет радио, продолжение - 6

Поэтому родилась идея сделать пульт для управления. По сути, это клиент, который по http будет использовать то же API веба, но при этом он будет аналоговым по интерфейсам управления. Телефон, в итоге, стоит в напечатанной клипсе [7]за монитором.

ESP32 Radio Remote

Идея сделать пульт была реализована как связанный проект, также выложен на gihub [8]. Аналоговый пульт, очень похожий внешне на первое решение, но внутри которого только плата ESP32. Мне очень нравилась работа экрана 2.42" OLED и отображение информации на нем, управление через энкодер. Поэтому компоновка будет примерно такой же. Запасные детали от старого проекта еще были в наличии (ESP32, экран, энкодер), поэтому решил все сделать на их базе. Также в процессе эксплуатации я решил, что переключение станций удобнее сделать на отдельные кнопки, поэтому в финальной версии предполагается использование двух кнопок без фиксации.

Кстати по кнопкам. Сначала я купил просто обычные кнопки без фиксации, но они были очень тугими, и поразмыслив, понял, что идеально на роль кнопок без фиксации подходят клавиатурные свитчи! Они бывают разные с разной тактильной отдачей, я решил остановится на cherry MX Brown (как раз они были в наличии). В итоге финальная версия корпуса и решения именно на них.

Интернет радио, продолжение - 7

Схема сборки

Интернет радио, продолжение - 8

Основные компоненты:

  • ESP 32 38P

  • Цифровой энкодер

  • Дисплей 2.42" OLED 4 pin

  • Кнопка без фиксации (2 шт)

Модели для печати находятся в папке /stl проекта. Я печатал корпус полностью из PLA Carbon Fiber, очень уж понравилось делать из него корпуса. Рекомендации по печати и расположению деталей аналогичны предыдущему проекту. Плату решил расположить с выводом питания слева и немного уменьшить размеры корпуса. Сборка также аналогична, требуется 10 болтов M2.5x5 мм. Единственное, что я поменял при сборке - полностью срезал изоляцию с крайних ножек ESP32. Так она не мешает фиксации ESP32 болтами на площадке корпуса.

Из основных фишек самого пульта, которые я решил реализовать:

1) Тройное нажатие энкодера - отображение текущего адреса для доступа к веб интерфейсу

2) Двойное нажатие - переключение уровня яркости. Варианты значение (0.255) можно настроить как массив значений в веб интерфейсе

3) Инверсия энкодера по громкости. Думаю, это или косяк кода, или сборки, но, для удобства стоит инвертировать поворот. Хотя, кому-то может, наоборот, удобнее как по умолчанию.

4) Ускорение при вращении громкости. Можно установить его, и при долгом вращении шаг будет сам собой увеличиваться. Мне показалось удобным. За меньший поворот делать больше громкость

5) Ночной режим. При прошествии (300с по умолчанию) и при отсутствии взаимодействия с пультом экран снижает яркость до значения (настраивается, по умолчанию 10).

6) Выключение экрана - спустя значение (3600с по умолчанию) при отсутствии взаимодействия с пультом экран вообще выключается.

7) Информация об исполнителе и композиции. В виде бегущей строки или (если помещается в 2 строки) без.

8) Настройки, которые остались как легаси в режиме добавления фичей (режим STA/AP, сохранять уровень громкости при перезагрузке), но решил их оставить.

Более подробная инструкция по заливке прошивке есть в readme [9]проекта, не вижу смысла ее тут дублировать.

После заливки прошивки и первого запуска через какое то время (не сразу, не пофиксил этот некритичный баг) будет переход в режим AP, те плата станет точкой доступа, к которой можно подключиться и выполнить базовую настройку SSID и пароль целевого WIFI, ip адрес телефона с flutter_radio. Дополнительно можно указать токен, но его же нужно задавать в flutter_radio. Это предварительный функционал по аутентификации запросов, который пока решил не реализовывать. Для домашней сети считаю это излишним.

AP режим

AP режим

Выводы

Наверное, основной вывод, который я для себя вынес в рамках данного проекта - вайбкодинг, при должном подходе, работает! Не особо разбираясь в определенных технологиях, можно на них построить вполне рабочие решения под конкретно свои требования и задачи. Смог бы я такое сделать без LLM самостоятельно? Скорее всего да, но потратив на порядки больше времени, погружаясь в детали каждой технологии.

Будни вайбкодинга

Будни вайбкодинга

Какие основные рекомендации я вынес для себя:

  1. Выбрать фреймворк и язык. Не находил таких явных бенчмарков, но, более чем уверен, что разные модели пишут с разным качеством на разных языках. Стоит выбирать что-то, в чем используемая LLM сильна. В своем проекте я положился на выбор chatgpt.

  2. Если вы вообще не знаете с чего начать и как подступить к задаче - запустите глубокое исследование. Тот же chatgpt даже в бесплатной версии позволяет 5 исследований в мес., в рамках которых может проработать какие вообще варианты решения задачи существуют (готовые или нет).

  3. Нельзя пытаться сделать решение сразу. "Сделай приложение с фичами X, Y, Z" не работает. Нужно разбивать процесс на итерации "Сделай приложение с фичой X", "Добавь фичу Y", "Добавь фичу Z".

  4. В режиме вайбкодинга через диалог (да, пока я делал именно так) стоит сохранять промежуточные варианты, постоянно верифицируя их на ошибки. Если после очередной итерации что-то сломалось, присылать обратно текст вроде "Решение не сработало, давай добавим фичу Y, вот предыдущая работающая версия скрипта <source_code>".

  5. Очень часто надо понимать, что вообще реализуется и какими алгоритмами. Иногда LLM выбирает не самые очевидные и простые способы реализации, и нужно явно просить сделать по другому. Пример из проекта: при реализации мьюта в лайв режиме изначально планировалось прям все тормозить и при снятии паузы переподключаться к потоку. Это время и задержки. Я предложил: "А давай вместо стопа и переподключения просто будем делать громкость 0, сохраняя подключение к потоку. Тогда снятие паузы будет быстрым (возврат громкости к предыдущему значению)." - "Отличная идея, так и сделаем..."). Так вот, часто именно вы и есть генератор отличных идей :)

  6. Разделение контекстов. Если проект пилится на независимые решения,то стоит явно их разделить. При написании flutter_radio я попросл сделать скрипт под ESP32 и потом его скопировал в другой диалог, дальнейшее развитие и доработки вел уже в нем независимо от проекта по приложению на телефон. Это улучшает быстродействие и поиск ошибок.

  7. Идеального результата добиться (по крайней мере, по моему мнению) очень сложно или невозможно. Галлюцинации с некомпилируемым кодом, изменение по структуре, изменение имен переменных и т.д. Поэтому стоит вовремя остановится и решить, что этого достаточно.

    Иллюстрация
    Реакция после очередной версии кода

    Реакция после очередной версии кода
  8. В будущем точно буду попробовать режим работы в IDE, а не режим диалога, Context Engineering, MCP, вот это все.

Спасибо за внимание!

Автор: Dkstr

Источник [10]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/dart/435635

Ссылки в тексте:

[1] Интернет-радио на базе ESP32 и ЦАП UDA1334A: https://habr.com/ru/articles/703414/

[2] github: https://github.com/DmitrySazonov/flutter_radio

[3] github release : https://github.com/DmitrySazonov/flutter_radio/releases

[4] just_audio: https://pub.dev/packages/just_audio

[5] audio_service: https://pub.dev/packages/audio_service

[6] список : https://espradio.ru/stream_list/

[7] клипсе : https://www.thingiverse.com/thing:2839176

[8] gihub: https://github.com/DmitrySazonov/esp32_radio_remote

[9] readme : https://github.com/DmitrySazonov/esp32_radio_remote/blob/main/README.md

[10] Источник: https://habr.com/ru/articles/962696/?utm_campaign=962696&utm_source=habrahabr&utm_medium=rss