Skip to content

stream volume change via gpio buttons (incomplete)#208

Open
BerlinJoker wants to merge 2 commits intoCarlosDerSeher:developfrom
BerlinJoker:develop
Open

stream volume change via gpio buttons (incomplete)#208
BerlinJoker wants to merge 2 commits intoCarlosDerSeher:developfrom
BerlinJoker:develop

Conversation

@BerlinJoker
Copy link
Contributor

Rough implementation that supports 2 configurable gpio buttons that trigger volume up/down for the underlying stream. Additional buttons like play, pause etc should be doable as well, actually even easier (as changing the volume requires to first pull the current stream volume to calculate the new value that then gets sent back).
Includes the additional entries for menuconfig (gpios, stream name, volume step size and debounce time)

Related discussion/comment: #152 (reply in thread)

please note: this was developed starting from the master branch, so I wasn't able to resolve one remaining conflict when switching to develop for the commit, but had to leave one thing out:

In my based-on-master version there was an additional line to fill the global for the server IP address in main.c right after the ENDIF of the whole #if SNAPCAST_SERVER_USE_MDNS block (which on develop was moved into a separate file I believe):

strlcpy(g_snapserver_host, ipaddr_ntoa(&remote_ip), sizeof(g_snapserver_host));

Therefore I don't think this PR version actually runs (as pulling the current volume value will probably fail without the server IP), and is purely meant as a "base for discussion". In my local version it does run, on a https://learn.adafruit.com/adafruit-esp32-feather-v2 , adjusting the volume of the underlying MPD server.

Requires &controlscript=meta_mpd.py to be added to the stream URL entry in snapserver.conf (plus a snapserver restart), and in my case I had to also install python3-mpd python3-musicbrainzngs for the meta_mpd.py script to actually run. I only use the volume information, so technically the musicbrain part isn't needed for my usecase, but the whole script fails otherwise.

Disclosure: done via Claude

Example log output from idf.py monitor:

I (6018877) vol_btn: Vol+
I (6018952) vol_btn: Volume 35 → 40
I (6021153) vol_btn: Vol+
I (6021225) vol_btn: Volume 40 → 45
I (6023267) vol_btn: Vol+
I (6023334) vol_btn: Volume 45 → 50
I (6027196) vol_btn: Vol+
I (6027268) vol_btn: Volume 50 → 55
I (6033650) vol_btn: Vol-
I (6033717) vol_btn: Volume 55 → 50
I (6035579) vol_btn: Vol-
I (6035649) vol_btn: Volume 50 → 45

Copy link
Owner

@CarlosDerSeher CarlosDerSeher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some general things:

  • I think volume buttons would be more appropriate in audio_board/custom_audio_board component
  • using BSD sockets for tcp connection is fine for now, but it is using more RAM than netconn api (which is my choice for sanpclient on esp32). I want to have best possible support for those boards without PSRAM so we need to keep peak RAM usage to a minimum.
  • the connect - communicate - disconnect flow will be a good choice for netconn too because the communication will be thread safe. How is the latency with this? You can't "feel" any delay while volume control don't you?

#endif

/* snapserver JSON-RPC control port */
#define SNAPRPC_PORT 1705
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't remember if this is already configurable in menuconfig, but if it isn't then it definitely should be.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't remember if this is already configurable in menuconfig, but if it isn't then it definitely should be.

when switching from mdns to manual snapserver configuration there is a setting for the 1704 port, but not for the 1705 port. I guess it could be included there, with 1705 kept as default

/* Task tuning */
#define VOL_TASK_STACK 8192 /* bytes — needs room for 8 KB static recv buf */
#define VOL_TASK_PRIO 5
#define VOL_POLL_MS 20 /* GPIO poll interval */
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer an interrupt based approach as this will result in lower power consumption when player is paused.

@CarlosDerSeher
Copy link
Owner

regarding the buttons and audio board. You could check out https://github.com/espressif/esp-adf/tree/v2.6/components/esp_peripherals. There is some unused code in the trunk which I disabled when porting stuff because I didn't have use for it. We probably could bring this back, this way we would have a standardized way of doing button handling and get support for all those espressif boards for free. You could even ask claude to do it the ADF way I guess. Give it an example or the link I provided and also reference https://github.com/espressif/esp-adf/tree/v2.6/components/audio_board to see what it gets you. I am always surprised how well this often works.

@BerlinJoker
Copy link
Contributor Author

  • I think volume buttons would be more appropriate in audio_board/custom_audio_board component

Why in the audio board section? Looking at the current menuconfig entries for that, it gives me the config options for the separate DAC modules, while the buttons would be connected directly to the esp32. Or wait, what does "audio board" refer to? Just the DAC part, or a combination board that has both the esp and the DAC on it?
My "gut feeling" still tells me that having gpio control options configured in something named "audio board" is somehow off. One aspect around that: when implementing this properly again, I would directly want to include other control options as well (play, pause, mute, next track, previous track, ...) which would move it even further away from the pure audio scope.

Side note: unfortunately the meta_mpd script doesn't support a command to tell mpd to play a certain playlist or URL. If that would be possible, then I'd LOVE to include gpio configuration for custom commands, which would allow to have physical buttons for pre-defined radiostations etc.

@BerlinJoker
Copy link
Contributor Author

  • I want to have best possible support for those boards without PSRAM so we need to keep peak RAM usage to a minimum

Just for more context for me: what was the original motivation to stick to a "PSRAM optional" route? Price of the boards? Availability? ...?

@BerlinJoker
Copy link
Contributor Author

  • How is the latency with this? You can't "feel" any delay while volume control don't you?

Nope, it's super reactive, no delays I can notice.

@CarlosDerSeher
Copy link
Owner

Google some of the audio boards supported. Quite a few of them have buttons on them (which have pre defined gpios), I don't know all of them but lyrat v4.3 for example has vol+, vol-, set, play in the form of touch buttons. So audio board in that sens means a full featured board with ESP, DAC and other peripherals like SD card, buttons, etc. Then there is the custom board, which is a special case where users can freely configure what they want to achieve. The naming origins from ESP ADF.

Just for more context for me: what was the original motivation to stick to a "PSRAM optional" route? Price of the boards? Availability? ...?

Because of the personal challenge it presented. I wanted to squeeze out the most of the HW I could when I started with this. Price is a valid argument too normally, but it is not so much of a difference for modules with the same flash size in <500 quantities (~0.1€).

As I said, sockets are OK for now, although I think claude could do the refactoring for netconn easily. As more and more features are added the RAM usage starts to climb up anyway. We just should ensure users will be able to disable everything they don't want so we can always build a minimal footprint (dumb) playback device.

@CarlosDerSeher
Copy link
Owner

OK, I just had a look at ADF again and reusing their code would mean a lot of porting again or pull in all that bloated audio pipeline stuff which we need to avoid. So I suggest to do our own implementation or use a already existing component somebody else did.

@CarlosDerSeher
Copy link
Owner

CarlosDerSeher commented Mar 1, 2026

Ha, just found out espressif ships their own button implementation which looks promising.

example

@BerlinJoker
Copy link
Contributor Author

Ah, got it with the audio board naming, thanks :)

And I get the part with the personal challenge. What for me makes the esp32 port of snapclient so appealing is the fact that an esp32 doesn't have to be powered all the time but can be switched on and off together with the amp. It boots super fast, and there is no file system or sd card that gets corrupted on sudden powerloss. And the bigger buffer values enabled by having PSRAM seem to at least partially make up for my glitchy home-environment wifi :)

About how to go from here: I think I'll wait until all the other stuff in develop is also in master, and would try to work from that version as the start point. I don't have (enough) routine with working on code that is being worked on by multiple people at the same time, and the resulting merge conflicts etcetc.

If someone else sees this and feels more confident already now: go for it :)

@BerlinJoker
Copy link
Contributor Author

Quick update on this: I started looking into the proposed changes, and in parallel into how to expand the meta_mpd script to support url playback: snapcast/snapcast#1513

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants