RaspberryPi 4 in an Argon ONE case as DLNA renderer with output to a Teufel Cinebar One+ via USB

Table of Contents

This is my braindump on setting up:

  • Raspbian GNU/Linux 11 (bullseye) on a Raspberry Pi 4 Model B
  • housed in an Argon ONE case with power button and fan
  • USB audio to a Teufel Cinebar One+
  • Playing music via DLNA
Raspberry Pi 4B in an Argon ONE case on a Teufel Cinebar One

My previous DLNA renderer (a Philips Fidelio A3) started to become unreliable a few months ago and this is what I have replaced it with.

Hardware Used

The RasPi and case I had already but it was not in active use. The Cinebar was primarily purchased to output sound of my main workstation (games and videos), but since it has multiple inputs giving it a second task was kinda obvious to me.

While the RasPi 4 I had at hand has 4 GiB RAM, using a 2 GiB model is fine too. And since my setup is currently on a 32-bit OS, I could also have used an older RasPi model.

The following was collected while it was playing a song;

root@raspberrypi:~# uptime
 18:27:05 up  1:56,  3 users,  load average: 0,17, 0,09, 0,07
root@raspberrypi:~# free -h
               total        used        free      shared  buff/cache   available
Mem:           3,7Gi        79Mi       3,4Gi       0,0Ki       232Mi       3,5Gi
Swap:           99Mi          0B        99Mi

RasPi to Cinebar Connection

Since the RasPi’s built-in audio does not have the best reputation, I connected the Cinebar to the RasPi via USB. The Cinebar’s AUX input is anyway currently connected to my main workstation.

NOTE: I did quickly try audio over HDMI from the RasPi to the Cinebar out of curiosity, but the RasPi’s power LED stayed on when it was shut down but HDMI still wired. Since I run the RasPi headless, planned to use USB, and I had no inclination to try a dozen hdmi_… settings in /boot/config.txt, I saw no reason to look into RasPi ←HDMI→ Cinebar further.

Case Fan and Powerbutton Setup

The vendor of the case describes this in the article Argon ONE Pi 3 B+ Case Overview. The article also applies to their RasPi4 case.

Basically they recommend

pi@raspberrypi:~ $ curl https://download.argon40.com/argon1.sh | bash

You can do that if you feel brave (or stupid). But since curl … | bash is generally a bad idea, I do recommend you download the script and read it before running it.

Operating System Used

While I first had a try with Raspberry Pi OS (Lite) aka “Raspbian GNU/Linux 10 (buster)", the repo http://raspbian.raspberrypi.org/raspbian/ buster only gave me version 0.0.7-git of gmrender-resurrect. With that old version I got no time elapsed in Bubble UpnP (my DLNA control point) and when stopping a track it would immediately restart playing. :-(

But, gmrender-resurrect 0.0.9 was released on 2021-01-21, So I saw no point in wasting time on debugging an older version.

I upgraded to Raspbian GNU/Linux 11 (bullseye). Mainly because that has the wanted version available in the repos and this RasPi’s only job is to be a small headless UPnP media renderer and for that I want the latest upstream code and the luxury of retrieving binaries from maintained repos instead of building from source.

Debian 10 → 11 is described in greater detail in the post How to Upgrade Debian 10 Buster to 11 Bullseye Linux, but I went for repos hosted at raspberrypi.org since my buster install was also using that.

I adjusted from buster to bullseye in 2 files

pi@raspberrypi:/usr/share/doc/python-apt-doc/examples $ cat /etc/apt/sources.list
# try to go from debian 10 (buster) to 11 (bullseye)
# pcfe, 2021-06-12
#deb http://raspbian.raspberrypi.org/raspbian/ buster main contrib non-free rpi
deb http://raspbian.raspberrypi.org/raspbian/ bullseye main contrib non-free rpi
# Uncomment line below then 'apt-get update' to enable 'apt-get source'
#deb-src http://raspbian.raspberrypi.org/raspbian/ buster main contrib non-free rpi
pi@raspberrypi:/usr/share/doc/python-apt-doc/examples $ cat /etc/apt/sources.list.d/raspi.list 
#deb http://archive.raspberrypi.org/debian/ buster main
deb http://archive.raspberrypi.org/debian/ bullseye main
# Uncomment line below then 'apt-get update' to enable 'apt-get source'
#deb-src http://archive.raspberrypi.org/debian/ buster main

And then ran the upgrade

pi@raspberrypi:~ $ sudo apt update
[...] check for errors
pi@raspberrypi:~ $ sudo apt full-upgrade
[...] be sure to do as told on stdout, Debian calls interactive scripts on some package updates
pi@raspberrypi:~ $ sudo reboot

If by the time you read this Raspbian GNU/Linux 11 (bullseye) is available as arm64 install image, obviously go for latest and for 64bit, everything that follows should still work.

Setup

Is done with Ansible, many steps taken from Playing music on a Raspberry Pi using UPnP and DLNA (v3). Follow that post if you are not comfortable with Ansible.

Setup Notes

For the apt: module to work (the package: module calls it) I had to explicitly set

ansible_python_interpreter: /usr/bin/python3

With the default setting, the RasPi would try to run an autodetected /usr/bin/python3.7 and fail to import needed (and installed) python modules.

Playbook Used

This should be self explanatory and includes links to the various posts I consumed when settign this up.

####################################################################################
#  author: Patrick C. F. Ernzer <pcfe@pcfe.net>
#  description: Prepares the RasPi 4B (in an Argon ONE case) connected to the Teufel Cinebar One+
#  license: Creative Commons Attribution-ShareAlike 4.0 International License
#  min_ansible_version: 2.9
####################################################################################

# hardware specs at: 
# - https://teufel.de/cinebar-one-105396000 Teufel Cinebar One+
# - https://www.raspberrypi.org/products/raspberry-pi-4-model-b/ Raspberry Pi 4 Model B
# - https://www.argon40.com/learn/index.php/2020/03/10/argon-one-case-overview/ Argon ONE case

# 2021-06-12: had a armhf debian 10 (buster) that I pulled up to 11 (bullseye)
# as per https://www.how2shout.com/linux/how-to-upgrade-debain-10-buster-to-11-bullseye-linux/
---

- name: Ensure the Raspberry Pi 4 in the Argon ONE case is ready for DLNA renderer duties
  hosts: raspi4b
  become: yes

  vars:
    gmediarender_logging: false
    ansible_python_interpreter: /usr/bin/python3

  handlers:
    - name: newaliases | run
      command: newaliases
    - name: gmediarender | restart
      service:
        name: gmediarender.service
        state: restarted

  tasks:
    # enable persistent journal
    - name: ensure persistent logging for the systemd journal is possible
      file:
        path: /var/log/journal
        state: directory
        owner: root
        group: systemd-journal

    # ensure the needed packages are installed
    # Raspberry Pi OS (Lite) is "Raspbian GNU/Linux 10 (buster)"
    - name: ensure wanted packages (that are not part of Raspberry Pi OS Lite Debian 10) are present
      package:
        name:
          - gstreamer1.0-alsa
          - python-apt
        state: present
      when: ansible_distribution == "Debian" and ansible_distribution_major_version == "10"

    # Manually upgraded to Debian 11 (buster), specifically
    # http://raspbian.raspberrypi.org/raspbian bullseye
    # http://archive.raspberrypi.org/debian bullseye
    - name: ensure wanted packages (that are not part of Raspian 11) are present
      package:
        name:
          - gstreamer1.0-alsa
          - python3-apt
        state: present
      when: ansible_distribution == "Debian" and ansible_distribution_major_version == "11"

    - name: Ensure directory /home/pi/Downloads exists
      file:
        path: /home/pi/Downloads
        state: directory
        owner: pi
        group: pi
        mode: 0755

    - name: ensure the Argon ONE software installer is downloaded
      get_url:
        url: https://download.argon40.com/argon1.sh
        dest: /home/pi/Downloads/argon1.sh
        checksum: "sha256:f4dcf4a845e4022156ee73e6a97df144962597add4fba1ea45280033e0023aed"

    # USB audio, see all of the following links
    # https://learn.adafruit.com/usb-audio-cards-with-a-raspberry-pi/updating-alsa-config
    # https://raspberrypi.stackexchange.com/questions/80072/how-can-i-use-an-external-usb-sound-card-and-set-it-as-default
    # https://cweiske.de/tagebuch/gmediarenderer.htm (gstreamer1.0-alsa needs installing)
    #
    # defaults.ctl.card 0 and defaults.pcm.card 0 need to be set to 1
    # unless I disable the snd_bcm2835 audio, which I do not want to do.
    # https://raspberrypi.stackexchange.com/questions/39928/unable-to-set-default-input-and-output-audio-device-on-raspberry-jessie
    - name: ensure the default options snd-usb-audio index=-2 in /lib/modprobe.d/aliases.conf is overridden
      lineinfile:
        path:         /lib/modprobe.d/aliases.conf
        regexp:       '^options snd-usb-audio '
        line:         'options snd-usb-audio index=1'

    - name: alsa.conf, ensure defaults.ctl.card 1 is set
      lineinfile:
        path:         /usr/share/alsa/alsa.conf
        regexp:       '^defaults.ctl.card '
        line:         'defaults.ctl.card 1'

    - name: alsa.conf, ensure defaults.pcm.card 1 is set
      lineinfile:
        path:         /usr/share/alsa/alsa.conf
        regexp:       '^defaults.pcm.card '
        line:         'defaults.pcm.card 1'

    # Now the steps from http://blog.scphillips.com/posts/2014/05/playing-music-on-a-raspberry-pi-using-upnp-and-dlna-v3/
    # that are still applicable today (that post was with a way older Raspian release)
    - name: ensure all updates are applied
      package:
        update_cache: yes
        name: '*'
        state: latest
      tags: apply_errata

    - name: ensure gmrender-resurrect is present, package name is gmediarender
      package:
        name: gmediarender
        state: present

    - name: ensure gmediarender has no ALSA_DEVICE set, it should use the gstreamer default an earlier task set
      lineinfile:
        path:         /etc/default/gmediarender
        regexp:       '^ALSA_DEVICE'
        state:        absent
      notify: gmediarender | restart

    - name: ensure gmediarender has a more reasonable start volume of 50/100 than the default 75/100
      lineinfile:
        path:         /etc/default/gmediarender
        regexp:       '^INITIAL_VOLUME_DB='
        line:         'INITIAL_VOLUME_DB=-20'
      notify: gmediarender | restart

    # For logging on/off, use the variable gmediarender_logging (bool)
    - name: Ensure directory /var/log/gmediarender exists
      file:
        path: /var/log/gmediarender
        state: directory
        owner: root
        group: audio
        mode: 0775
      when: gmediarender_logging

    - name: ensure gmediarender DOES have logging enabled
      lineinfile:
        path:         /etc/default/gmediarender
        regexp:       '^DAEMON_EXTRA_ARGS='
        insertafter:  '^# DAEMON_EXTRA_ARGS='
        line:         'DAEMON_EXTRA_ARGS="--logfile /var/log/gmediarender/gmediarender.log"'
      notify: gmediarender | restart
      when: gmediarender_logging

    - name: ensure gmediarender does NOT have logging enabled
      lineinfile:
        path:         /etc/default/gmediarender
        regexp:       '^DAEMON_EXTRA_ARGS='
        insertafter:  '^# DAEMON_EXTRA_ARGS='
        line:         '# DAEMON_EXTRA_ARGS="--logfile /var/log/gmediarender/gmediarender.log"'
      notify: gmediarender | restart
      when: not gmediarender_logging

    - name: ensure gmediarender.service is enabled and started
      service:
        name:       gmediarender.service
        enabled:    true
        state:      started

    - name: check to see if we need a reboot, Debian style
      stat:
        path: /var/run/reboot-required
      register: reboot_required_file
      when: ansible_pkg_mgr == "apt"

    - name: reboot RasPi if Debian style hint file is present
      reboot:
      when:
        - reboot_required_file is defined
        - reboot_required_file.stat.exists is defined
        - reboot_required_file.stat.exists

Usage

Obvious if you used DLNA before, but if this is your first DLNA experience;

  1. If off, boot the RasPi by shortly pressing the power button on the case

  2. Ensure the Cinebar is on and set to Bluetooth input, LED is blue (USB and BT use the same input)

  3. Open your preferred DLNA control point on a device of your choice (on Android I use BubbleUPnP)

  4. select a media library of your choice (I use ReadyMedia (aka minidlna) on a Linux server)

  5. select your RasPi as renderer, by default it is called gmediarender on $(hostname)

  6. control all of

    • choice of song
    • playlist (n.b. the playlist resides on your control point, not the renderer)
    • volume
    • play, pause, seek, stop, next/previous track

    via your DLNA control point

  7. cleanly shut down the RasPi by pressing the power button ≥ 3 (but < 5) seconds

Usage Notes

Ideally you would shut down both the RasPi and the Cinebar when not in use, to save some energy.

The Argon ONE case comes with a nice power button, use it (RTFM on Argon ONE Power Button Functions here). You will need to install their software for the button and fan to work as advertised.

Pressing the button for ≥ 5 seconds does a hard poweroff. Avoid doing that, prefer the clean shutown.

The Cinebar will enter low power mode (LED is red) by itself if no audio is played for a while.

UPnP Tweaks

Bubble offers finer control than many other control points I tried, and it even does it on a per renderer basis.

Currently I am trying Gapless control for this DLNA renderer. But having lived without gapless playback on many DLNA renderers, I am not going to sink a lot of thought on this.

For now I’m mainly happy that I have once again a DLNA renderer to listen to music without any hassle when I sit in my work room.

Update 2021-08-20; WLAN and BT off

As I use neither WLAN nor Bluetooth, those were switched off as follows in /boot/config.txt;

# Disable WLAN and Bluetooth
# pcfe, 2021-08-20
dtoverlay=disable-bt
dtoverlay=disable-wifi

kudos to Robert G for the `disable-… overlays hint.