Using A Raspberry Pi To Switch On Surround Sound Speakers

SpeakerIn a previous post, I talked about network booting a Raspberry Pi MythTV frontend. One issue that I had to solve was how to turn on my Onkyo surround sound speakers, but only if they are not already turned on.

I already had an MCE remote and receiver which can both transmit and receive, so it is perfect for controlling MythTV and switching the speakers on. There are plenty of tutorials out there, but the basic principle is to use irrecord to record the signals from the speaker’s remote control, so the Raspberry Pi can replay them to switch it on when the Pi starts up. In my case, I needed two keys, the power button and VCR/DVR input button. Once you’ve recorded the right signals, you can use irsend to repeat them.

Initially, I had it set up to always send the power button signal on boot. This had the unfortunate side-effect of switching the speakers off if they were already on, for example, if I had been listening to music through Sonos before deciding to watch TV.

To prevent this from happening I needed to determine whether the speakers were on or not. Fortunately, Raspberry Pi’s come with some useful tools to determine information about what is supported by the HDMI device it’s connected to. These tools are tvservice, which dumps the EDID information, and edidparser which turns the EDID into human-readable text.

You can use them as follows:

tvservice -d /tmp/edid.dump

edidparser /tmp/edid.dump > /tmp/edid.txt

This gives you a nice text file containing all of the resolutions and audio formats supported by the connected HDMI device. I took one output when the speakers were on, and one when they were off, and by diffing them I got this set of changes.

-HDMI:EDID found audio format 2 channels PCM, sample rate: 32|44|48 kHz, sample size: 16|20|24 bits
+HDMI:EDID found audio format 2 channels PCM, sample rate: 32|44|48|88|96|176|192 kHz, sample size: 16|20|24 bits
+HDMI:EDID found audio format 6 channels PCM, sample rate: 32|44|48|88|96|176|192 kHz, sample size: 16|20|24 bits
+HDMI:EDID found audio format 8 channels AC3, sample rate: 32|44|48 kHz, bitrate: 640 kbps
+HDMI:EDID found audio format 8 channels DTS, sample rate: 44|48 kHz, bitrate: 1536 kbps
+HDMI:EDID found audio format 6 channels One Bit Audio, sample rate: 44 kHz, codec define: 0
+HDMI:EDID found audio format 8 channels Dobly Digital+, sample rate: 44|48 kHz, codec define: 0
+HDMI:EDID found audio format 8 channels DTS-HD, sample rate: 44|48|88|96|176|192 kHz, codec define: 1
+HDMI:EDID found audio format 8 channels MLP, sample rate: 48|96|192 kHz, codec define: 0

Pretty obvious really – when the speakers are on they support a much greater range of audio formats!

Putting all this together I ended up with the following script. It grabs the EDID data, converts it into text, and if it doesn’t contain DTS-HD then turn the speakers on.

tvservice -d /tmp/edid.dump

edidparser /tmp/edid.dump > /tmp/edid.txt

if ! grep DTS-HD /tmp/edid.txt; then
 irsend SEND_ONCE speaker KEY_POWER
fi

Photo of Speaker by Ryann Gibbens.

Advertisements

Network Booting A Raspberry Pi MythTV Frontend

Network cables - mess :DWhen we moved house earlier in the year I wanted to simplify our home theatre setup. With my son starting to grow up, in a normal house he’d be able to turn on the tv and watch his favourite shows without needing us to do it for him, but with the overcomplicated setup that we had it would take him several years longer before he could learn the right sequence of buttons.

I’ve been a MythTV user for well over ten years, and all our TV watching is done through it. At this stage with our history of recorded shows and a carefully curated list of recording rules switching would be a big pain, so I wanted to try and simplify the user experience, even if it means complicating the setup somewhat.

I had previously tried to reduce the standby power consumption by using an Eon Power Down Plug, which monitors the master socket and switches off the slave sockets when the master enters standby mode. This works great as when the TV was off my Xbox and surround speakers would be switched off automatically. The downside is that if I want the use the speakers to listen to music (they are also connected to a Sonos Connect) then either the TV needs to be on, or I need to change the plug over. Lastly, because I was running a combined frontend and backend it wasn’t connected to the smart plug (otherwise it wouldn’t be able to turn on to record.) If you turned the TV off the frontend would still be on, preventing the backend from shutting down for several hours, until it went into idle mode.

I decided to solve these problems by using a Raspberry Pi 3 as a separate frontend, and switching the plugs around. As they run Linux, and have hardware decoding of MPEG2 and h264 they work great as MythTV frontends.

A common issue with Raspberry Pis is that if you don’t shutdown them down correctly then their SD cards become corrupt. If I connected the Pi to the slave plug socket as planned then it would be uncleanly shut down every time the TV was switched off, risking regular corruption. Fortunately Raspberry Pis support network booting, which means you can have the root filesystem mounted from somewhere else, and you don’t even need the SD card at all. I already had a Synology NAS, which I love, and is a perfect host for the filesystem.

Sadly the network code that is built into the Pis ROM (and therefore isn’t updatable) is very specific and buggy. My router’s DNS server doesn’t support the options required to make the Pi boot, so I switched to using a DNS server on the Synology. While you can’t set the right options in the web frontend you can edit the config files directly to make it work. The bugs in the Pis firmware are that the DNS responses must be received at the right time. Too quick or too slow and the Pi will fail to boot. One of the aspects I like the most about my Synology is that it has a very low power suspend more. When it is in this mode it takes a little while to wake up and respond the network event. Waking up takes too long for the Pi, which would give up waiting for a response. While I wouldn’t have been happy about it, I could have disabled the low power mode to make the Pi work. Unfortunately the second time the Pi boots the DNS server responds too quickly (the first time it has to check whether the IP address it is about to hand out is in use.) This response is too quick for the Pi, which again will fail to boot.

The other option is to use an SD card with a kernel and a few supporting files on it to start the boot, and then use Linux’s built-in NFS root filesystem support. While this does require an SD card, it’s read only and after the kernel has been loaded the card will be accessed very rarely, if ever, so the risk of corruption is minimal. After running with this set up for a few months, and being switched off several times per day we’ve not had a single corruption of the SD card so far.

Setting this up is pretty straightforward, I just extracted a Minibian tarball to my NAS and shared it via NFS. Next I copied the contents of /boot to my SD card, and modified cmdline.txt to include the following:

root=/dev/nfs nfsroot=192.168.1.72:/volume1/pi/minibian rw ip=dhcp

With this added it boots up reliably and can be shut down uncleanly with little or no risk of corruption.

Next up is making the MythTV frontend start up automatically. This is was done by adding the following to /etc/rc.local

modprobe rc_rc6_mce
/usr/bin/ir-keytable -c -p RC-5,RC-6 -w /etc/rc_keymaps/rc6_mce
echo "performance" > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
su -c "/home/andrew/autostart.sh" andrew &

The first two lines are required to set up my MCE IR receiver. The third line is needed to ensure that the Pi’s performance remains consistent and the CPU isn’t throttled down while you’re in the middle of an episode of Strictly. The final line just triggers another script that actually runs the frontend, but run as me, and not root.

#!/bin/bash

/home/andrew/wake_speakers &
startx /home/andrew/start_myth 2>&1 > ~/mythtv.log

I’ll cover the first line in another post, but it just turns on the surround speakers and makes sure they in the right mode. The second line starts X, and runs my custom start script. This final script looks like this:

#!/bin/bash
QT_QPA_PLATFORM=xcb /usr/bin/mythfrontend -O libCECEnabled=0

While I managed to solve my key issues of making it easier to switch the open and off, and I can listen to music without the TV being on and still have most devices switched fully off, I still have a few issues still to solve. The main two are that bootup speed is not as fast as I would like, and the backend doesn’t cope well with the frontend exiting uncleanly (and it waits 2.5 hours before turning off). I will cover these issues, and some others that I had to solve in a future post.


Photo of Network cables – mess 😀 by jerry john.

Links to Amazon contain an affiliate code.

Custom Podcasts With MythTV

I love listening to both BBC Radio 4 and BBC 6 Music. Like the rest of the BBC radio stations a significant proportion of the shows are available as a podcast. Unfortunately this is not true of all the shows, and for those that feature music such as Adam & Joe or Steve Lamacq the podcasts are talking only.

I watch almost all of TV through MythTV which records all of my favourite shows automatically while on my way to work I like to listen to podcasts that are downloaded automatically by iTunes. Would it be possible to automatically record shows with MythTV that aren’t available as podcasts and sync them to my iPhone automatically?

Recording a radio show with MythTV is no different to recording a TV show so that’s not a problem. MythTV also provides the ability to run a script after certain shows have been recorded. All that is required is a script that converts the recording into an mp3 file and to build an RSS feed which can be read by iTunes.

First we need to convert the recorded file into an mp3, which is easy to do with the ffmpeg program.

#!/usr/bin/python
# -*- coding: utf-8 -*-

from datetime import date, datetime
import glob
import MySQLdb
import os
import sys

input = sys.argv[1]
input_filename = input.split("/")[-1]
output_filename = input_filename.split(".")[0] + ".mp3"

os.system("ffmpeg -y -i %s -acodec libmp3lame -ab 128k /var/www/localhost/htdocs/podcasts/%s > /dev/null" % (input, output_filename))

Next up we need write out the RSS feed that iTunes will read. We start off by opening the file and writing out some boiler plate code.

fp = open("/var/www/localhost/htdocs/podcasts/feed.rss", "w")
fp.write("""<?xml version="1.0" encoding="UTF-8"?>

<rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" version="2.0">
  <channel>
       <title>MythTV Recorded Radio</title>
       <description>Radio Recorded By MythTV</description>
       <link>http://192.168.0.8/podcasts/</link>
       <language>en-us</language>
       <lastBuildDate>%(datetime)s</lastBuildDate>
       <pubDate>%(datetime)s</pubDate>
       <webMaster>andrewjwilkinson@gmail.com</webMaster>

       <itunes:image href="http://192.168.0.8/podcasts/stevelamacq.jpg"/>

       <itunes:category text="Technology">
           <itunes:category text="Podcasting"/>
       </itunes:category>
""" % { "datetime": datetime.now().ctime() })

Finally we need to write out a small bit of XML for each file that’s in our directory waiting to be downloaded. We do this by looking at each mp3 file in the podcasts directory and looking for the appropriate entry in MythTV’s recorded table. If an entry doesn’t exist then recording has been deleted and we delete the mp3 file.

db = MySQLdb.connect(user="mythtv", passwd="mythtv", db="mythconverg")

for radio_file in glob.glob("/var/www/localhost/htdocs/podcasts/*.mp3"):
    output = radio_file.split("/")[-1]
    size = len(open(radio_file, "rb").read())

    c = db.cursor()
    c.execute("SELECT title, description, starttime FROM recorded WHERE basename=%s", (output.split(".")[0] + ".mpg", ))
    row = c.fetchone()
    if row is None:
        os.remove(radio_file)
        continue

    title, description, starttime = row

    fp.write("""       <item>
           <title>%(title)s - %(datetime)s</title>
           <link>http://192.168.0.8/podcasts/%(output)s</link>
           <guid>http://192.168.0.8/podcasts/%(output)s</guid>
           <description>%(description)s</description>
           <enclosure url="http://192.168.0.8/podcasts/%(output)s" length="%(output_size)s" type="audio/mpeg"/>
           <category>Podcasts</category>
           <pubDate>%(datetime)s</pubDate>
       </item>""" % { "title": title, "description": description, "datetime": starttime, "output": output, "output_size": size })

fp.write("""
    </channel>
</rss>
""")

To use this put all three bits of code into one file, save it somewhere and mark it as executable. Next set up Apache to serve the directory /var/www/localhost/htdocs/podcasts/ as /podcasts. Finally you need to set up the script to run automatically after a program you want to create a podcast from has been recorded. To do this run mythtv-setup and select the ‘general’ menu option. Move through the screens until you reach ‘Job Queue (Job Commands)’. Add a brief description of the script in the ‘description’ field then enter the <path to script> %s. Then use the normal MythTV frontend and edit the recording schedules to make the correct User Script run.

Point iTunes as http://you.ip.address/podcasts/feed.rss and it’ll automatically download any new recordings.