PyPlay for Raspberry Pi

Ages ago I made a simple Python program that would play audio files out one at a time on a Mac. What’s the big deal with that? Don’t lots of programs do that? Well, I don’t think so, at least not on a Mac. The key concept here is playing a file from a list – and then stopping. In radio (or the theatre I guess) this is almost always what you want to do, not automatically play the next track.

So PyPlay was born. It was written in Python 2 and used Apple’s afplay command so it didn’t need any other software or modules installing – you download the file, chuck it in a folder with some audio files and run it in the terminal by typing in python pyPlay.py – If there isn’t already a standard format M3U playlist file in the folder, it makes one for you (in an arbitrary order).

You play files by typing in its number in the list and pressing enter. It gives you an out-time for the track, but the display is not updated with the time or the time remaining – you have to keep your eye on another clock such as the one on your computer. You can tweak the
Raspberry Pi desktop clock to show seconds by right-clicking on it > Digital Clock Settings > change Clock Format to %X

You can have two consoles open at the same time so you can segue tracks by playing a second one while the first is just ending. So you can DJ using a Raspberry Pi, some sound files and a bit of code!

This new Raspberry Pi version is a bit more polished than the Mac version as it adds support for .OGG files and fixes a few bugs. It still sticks to the original principle of not requiring any modules installing you shouldn’t find on a normal Raspbian install. You should launch it in Python 2 from the Terminal command line. It uses omxplayer instead of afplay to play the audio and allows some additional controls. While a track is playing you can use the keyboard to:

- Change the volume using + and – keys
- Pause a track with the space bar (this renders the out time meaningless, however)
- Skip around inside a track using arrow keys. Left & right seek back and forward 30 seconds, up and down by 600 seconds. You can also speed up and slow down the audio with 1 and 2 but I wouldn’t recommend it! These also render the displayed out time meaningless.

There’s lots still to do with this – it is inspired by the widely-used BBC audio playout Windows program CoolPlay (for which I designed the splash screen) but provides none of that program’s useful features like showing the progress of a track, allowing you to hop around a playlist and, indeed, build and re-order playlists. I may work on these but these features require me to delve into either curses programming or creating a proper GUI. It would, of course, be trivial to add some GPIO to control it from physical buttons or even fader starts on a mixing desk.

Anyway here’s the code. I hope it may be useful for someone making a dirt cheap radio station, DJ setup or for school dramas or assemblies! (I tried uploading it to Github, but Github is being… a git.)

#!/usr/local/bin/python

# 5 Aug 17 version
# Modified to run on Raspberry Pi using omxplayer instead of afplay
# Found bug - cannot cope with brackets in file names - FIXED!
# - cannot cope with ampersands either! - FIXED
# - make titles longer - FIXED
# - add support for .OGG audio files - FIXED

# If using GUI change clock display to show seconds with %X format.

# PyPlay radio playout script by Giles Booth @blogmwiki
# Written in Python2. Should not require any other modules to be installed.
# Requires audio files in same directory as this script.
# Original version ran on Mac using afplay.

# V 4.1 has horrible kludge to cope with multiple backslashes in array
# Version 4 fixes bugs replacing escaped spaces in display names and
# copes with filenames with apostrophes.
# Version 3 fixes bug playing files with spaces in file names
# makes an M3U file from audio files in directory if no playlist.m3u file found
# also fixes a bug with out time when crossing hour.

import os
import os.path
import time
import subprocess
import re
import datetime

playlist = ""

# \033[7m is code for inverse video
# \033[0m is normal video
# \033[31m is red video

# function to display tracks, durations, playing status & out time
def showTracks():
    os.system('clear')
    t = str(datetime.datetime.now().time())
    t = t[0:8]
    print '\033[37;44mWelcome to PyPlay!     \t\t\tUpdated ',t,'\033[0m'
    print ''
    print '\033[0;30;47m  # File name                           Length  Status  Out time \033[0m'
    for z in range(len(trackList)):
        highlightOn = ""
        highlightOff = ""
        if "PLAYING" in trackList[z][4]:
            highlightOn = "\033[31;103m"
            highlightOff = "\033[0m"
        print highlightOn, leadingSpace(str(z+1)), trackList[z][1], "\t", timeLeadingSpace(trackList[z][3]), "\t", trackList[z][4], highlightOff
    print '\033[97;42m    KEYS:    q stop | - + vol | space pause | arrows seek        \033[0m'

# returns duration of track in seconds
def getTrackLength(thing):
    bumf = ""
    bumfList = []
    trackLength = ""
    snog = trackArray[thing]
    foo = "omxplayer -i " + snog

    try:
        bumf = subprocess.check_output(foo, shell=True, stderr=subprocess.STDOUT)
    except Exception, e:
        bumf = str(e.output) # horrible kludge to get round exceptions but capture text
    bumfList = bumf.split('\n')

    for line in bumfList:
#        print line # debug line
        if line.startswith('  Duration'):
            line_mins = int(line[15:17])
            line_secs = int(line[18:20])
            trackSecs = line_secs + (line_mins*60)

    return trackSecs

# returns string with out time in HH:MM:SS format
def getEndTime(tk):
    tkDuration = trackList[tk-1][2]
    timeNow = str(datetime.datetime.now().time())
    timeHour = timeNow[0:2]
    timeMin = timeNow[3:5]
    timeSec = timeNow[6:8]
    tH = int(timeHour)
    tM = int(timeMin)
    tS = int(timeSec)
    tkS = tkDuration % 60
    tkM = (tkDuration - tkS) / 60
    endSec = int(round(tS+tkS, 0))
    endMin = int(tM + tkM)
    endHour = int(tH)
    if endSec > 59:
        endMin += 1
        endSec = endSec % 60
    if endMin > 59:
        endHour += 1
        endMin = endMin % 60
    endHourString = leadingZero(str(endHour))
    endMinString = leadingZero(str(endMin))
    endSecString = leadingZero(str(endSec))
    endTime = endHourString + ":" + endMinString + ":" + endSecString
    return str(endTime)

# adds a leading 0 to single character strings
def leadingZero(n):
    if len(n) == 1:
            n = '0' + n
    return n

# adds a leading space to single character strings
def leadingSpace(n):
    if len(n) == 1:
            n = ' ' + n
    return n

# adds a leading space to times shorter than 10 minutes
def timeLeadingSpace(n):
    if len(n) == 4:
            n = ' ' + n
    return n

# makes strings a fixed length
def colform(txt, width):
    if len(txt) > width:
        txt = txt[:width]
    elif len(txt) < width:
        txt = txt + (" " * (width - len(txt)))
    return txt

# returns the track length in a string M:SS format
def displayDuration(s):
    disTime = int(s)
    sec = disTime % 60
    m = (disTime - sec)/60
    secString = leadingZero(str(sec))
    t = str(m) + ":" + secString
    return t

# sets playing status for all tracks to empty string
def clearStatus():
    for y in range(len(trackList)):
        trackList[y][4] = ''

# Trying omxplayer on Raspberry Pi
def playTrack(track):
    song = trackArray[track-1]
    trackString = "omxplayer " + song + " > /dev/null"  # stop omxplayer output appearing on screen whilst playing
    os.system(trackString)

# if no playlist.m3u file found, make one from audio files found in directory
# edit audioFileTypes list to add more file types as needed (but don't add 'aif' because reasons)
if not os.path.exists('playlist.m3u'):
    audioFileTypes = ['.mp3','.MP3','.wav','.WAV','.m4a','.M4A','.aiff','.AIFF','.ogg','.OGG']
    os.system('clear')
    print "No playlist.m3u file found so I am making you one with these files:"
    print
    dirList = os.listdir(".")
    newDir = []
    for x in range(len(dirList)):
        for q in audioFileTypes:
            if q in dirList[x]:
                print(dirList[x])
                newDir.append(dirList[x])
    fo = open("playlist.m3u", "w")
    fo.write("#EXTM3U\n\n")
    for item in newDir:
#        print item
        fo.write("%s\n" % item)
    fo.close()
    time.sleep(2)

#open the playlist file and read its contents into a list
playlist = open('playlist.m3u')
trackArray = playlist.readlines()

# clean up the track list array of metadata and \n characters
# iterate over list in reverse order as deleting items from list as we go
for i in range(len(trackArray)-1,-1,-1):
    if trackArray[i].startswith('\n') or trackArray[i].startswith('#'):
        trackArray.pop(i)
    repl = {" ": "\ ", "\'": "\\'","&": "\&","(": "\(",")": "\)"} # define desired replacements here
    # use these three lines to do the replacement
    repl = dict((re.escape(k), v) for k, v in repl.iteritems())
    pattern = re.compile("|".join(repl.keys()))
    newArrayName = pattern.sub(lambda m: repl[re.escape(m.group(0))], trackArray[i])
    trackArray[i] = newArrayName
    temp = trackArray[i].strip()
    trackArray[i] = temp

# horrible kludge to strip out multiple backslashes
for i in range(len(trackArray)-1,-1,-1):
    repl = {"\\\\\\": "\\"} # define desired replacements here
    # use these three lines to do the replacement
    repl = dict((re.escape(k), v) for k, v in repl.iteritems())
    pattern = re.compile("|".join(repl.keys()))
    newArrayName = pattern.sub(lambda m: repl[re.escape(m.group(0))], trackArray[i])
    trackArray[i] = newArrayName

# read tracks into array to hold track info in format:
# filename - display name - duration as float - display duration - track status
print '\nScanning audio files to calculate durations:'
trackList = []
for a in range(len(trackArray)):
    rep = {"\ ": " ", "\\'": "\'", "\&": "&","\(": "(","\)": ")"} # define desired replacements here
    # use these three lines to do the replacement
    rep = dict((re.escape(k), v) for k, v in rep.iteritems())
    pattern = re.compile("|".join(rep.keys()))
    newName = pattern.sub(lambda m: rep[re.escape(m.group(0))], trackArray[a])
    print a+1,newName
    thisTrackLength = getTrackLength(a)
    trackList.append([trackArray[a],colform(newName,30),thisTrackLength,displayDuration(thisTrackLength),"status"])

# the main program loop
while True:
    clearStatus()
    showTracks()
    trackNo = raw_input('\nWhich track # would you like to play? q to quit ')
    if trackNo == 'q':
        break
    elif trackNo == "":
        print 'You must enter a track number or q to quit'
        time.sleep(2)
    elif trackNo.isalpha():
        print 'Must be a number'
        time.sleep(2)
    elif int(trackNo) <1 or int(trackNo) > len(trackArray):
        print 'Not a valid track number'
        time.sleep(2)
    else:
        eT = getEndTime(int(trackNo))
        trackList[int(trackNo)-1][4] = 'PLAYING ' + eT
        showTracks()
        playTrack(int(trackNo))
Posted in radio, Raspberry Pi | Tagged , , , , | Leave a comment

RFID card kit for Raspberry Pi

Two videos (one short, one long) exploring the Monk Makes Clever Card kit for the Raspberry Pi. I really recommend this RFID card kit, it is excellent value and the supplied software is very thoughtfully designed – I made an internet radio that plays different stations when you present different cards in mere minutes!

Here’s a quick demo of the radio:

And here’s an in-depth look at the kit and how I made the radio:

Posted in radio, Raspberry Pi | Tagged , , | 2 Comments

Tide times on Raspberry Pi Inky pHAT display

Now updated with weather info – see end of this blog post!

Someone once said ‘information wants to be free’. Well, yes and no. If you’re looking for publicly-funded academic research papers or live train running information or potentially life-saving data like tide times, then forget it. (Little bit of a rant, there. Sorry.)

After my radio, I fancied making a gizmo to show high and low tide times on my Raspberry Pi’s Inky pHAT e-ink display. This turned out to be way harder than I expected, chiefly because UK tide time data does not seem to be syndicated as an RSS or other accessible data feed. There is a privately-run web site that appears to have an RSS feed, but all the tide times are contained in one XML tag, so it’s not very useful. Magic Seaweed have an API but it requires a key – I have asked for one as they have loads more lovely surf data.

So I decided to find out how to do screen-scraping in Python. I used this very nice guide which uses two Python modules, ‘requests’ (which I already had on my Pi) and ‘Beautiful Soup’ which I installed with
sudo apt-get install python3-bs4

You can get tide times on many different sites, I chose the Met Office almost at random but partly because I want to add weather info. Screen-scraping is, I have to say, a Hideous Kludge. (I think I saw Hideous Kludge at the Bull and Gate once…) If the Met Office reconfigure their site it any way, this program will probably stop working. It could also do with some error handling…

DISCLAIMER: this beta is provided for amusement only!
Do not rely on the tide times presented as code may be buggy.

So here we go – put the background image file (mine is shamelessly modified from the supplied Pimoroni one but you could add one of your own making) and a font of your choice in the same folder as this script, install Beautiful Soup, find your local seaside place geohash code on the Met Office web site and replace mine with it, and off you jolly well go.

If you want to use my retro 68k Mac font (which does look lovely on the Inky pHAT) you can download it here.

You can set cron to run this automatically – I would suggest running it every morning at 5am; the current version of Raspbian allows you to do this easily on the desktop (Raspberry > System Tools > Scheduled tasks). I think this would be a great project to run on a PiZeroW to make a little tide time gizmo for your beach house!

# A program to display tide times on Raspberry Pi with Inky pHAT e-ink display
# by Giles Booth - www.suppertime.co.uk/blogmywiki
# Updated 1 Aug 17 to fix bug when only 3 tides in 24 hr period.

# DISCLAIMER: this beta is provided for amusement only!
# Do not rely on the tide times presented as code may be buggy.
# This screen-scrapes the Met Office web site, may break at any time.
# -------------------------------------------------------------
# I did not need to install 'requests', it was already on Pi.
# I installed Beautiful Soup with 'sudo apt-get install python3-bs4'
# using tutorial at https://www.dataquest.io/blog/web-scraping-tutorial-python/
# Download your own .ttf font eg ChiKareGo.ttf from
# http://www.pentacom.jp/pentacom/bitfontmaker2/gallery/?id=3778
# and place it in same folder as this program.

# To do: add weather, wind direction, custom backdrop, fix 3 tide bug.

import requests
from bs4 import BeautifulSoup
import inkyphat
from PIL import Image, ImageFont

# Fetch Gwithian tide times from Met Office web site
# Find other geohash location codes on the site, eg
# Godrevy, Cornwall gbujv26hb
# Walton-on-Naze u10yuyqrf
# Gt Yarmouth u135pr5sv
page = requests.get("http://www.metoffice.gov.uk/public/weather/tide-times/gbujtpnch")

soup = BeautifulSoup(page.content, 'html.parser')
html = list(soup.children)[2]
body = list(html.children)[3]

# print location name in console - you could print this straight to Inky pHAT
location = soup.find_all('h2')[1].get_text()
print(location)

# print 1st date (today!)
today_date = soup.find_all('h3', class_='dayTitle')[0].get_text()
print(today_date)

# print tide times - needs work as there are not always 4 tides in each 24 hour period
for i in range (4):
    print(soup.find_all('td')[i].get_text(),soup.find_all('span', class_='tideTime')[i].get_text())

# replace with font of your choice
font = ImageFont.truetype("ChiKareGo.ttf", 16)
inkyphat.set_image("emptier-backdrop.png")

title = 'Gwithian ' + today_date
w, h = font.getsize(title)
x = (inkyphat.WIDTH / 2) - (w / 2)
inkyphat.text((x, 0), title, inkyphat.WHITE, font=font)
inkyphat.line((31, 15, 184, 15)) # Horizontal  line

tide_y = 16

for i in range (4):
    tide_line = soup.find_all('td')[i].get_text() + ' tide: ' + soup.find_all('span', class_='tideTime')[i].get_text()
    if not tide_line[0].isdigit():  # filter out spurious lines when only 3 tides in 24 hrs
        inkyphat.text((50, tide_y), tide_line, inkyphat.WHITE, font=font)
        tide_y += 16

inkyphat.show()

Updated version

I’ve since tweaked the code (inefficiently I know) to show wind direction, speed and general weather outlook. I’ve also made a new background image which allows for more text:

# A program to display tide times on Raspberry Pi with Inky pHAT e-ink display
# by Giles Booth - www.suppertime.co.uk/blogmywiki

# DISCLAIMER: this beta is provided for amusement only!
# Do not rely on the tide times presented as code may be buggy.
# This screen-scrapes the Met Office web site, may break at any time.
# -------------------------------------------------------------
# I did not need to install 'requests', it was already on Pi.
# I installed Beautiful Soup with 'sudo apt-get install python3-bs4'
# using tutorial at https://www.dataquest.io/blog/web-scraping-tutorial-python/
# Download your own .ttf font eg ChiKareGo.ttf from
# http://www.pentacom.jp/pentacom/bitfontmaker2/gallery/?id=3778
# and place it in same folder as this program.

import requests
from bs4 import BeautifulSoup
import inkyphat
from PIL import Image, ImageFont
import string

# Fetch Gwithian tide times from Met Office web site
# Find other geohash location codes on the site, eg
# Godrevy, Cornwall gbujv26hb
# Walton-on-Naze u10yuyqrf
# Gt Yarmouth u135pr5sv
page = requests.get("http://www.metoffice.gov.uk/public/weather/tide-times/gbujtpnch")

soup = BeautifulSoup(page.content, 'html.parser')
#html = list(soup.children)[2]
#body = list(html.children)[3]

# print location name in console - you could print this straight to Inky pHAT
location = soup.find_all('h2')[1].get_text()
print(location)

# print 1st date (today!)
today_date = soup.find_all('h3', class_='dayTitle')[0].get_text()
print(today_date)

# print tide times
for i in range (4):
    print(soup.find_all('td')[i].get_text(),soup.find_all('span', class_='tideTime')[i].get_text())

# replace with font of your choice
font = ImageFont.truetype("ChiKareGo.ttf", 16)
inkyphat.set_image("even-emptier-backdrop.png")

title = 'Gwithian ' + today_date
w, h = font.getsize(title)
x = (inkyphat.WIDTH / 2) - (w / 2)
inkyphat.text((x, 0), title, inkyphat.WHITE, font=font)
inkyphat.line((31, 15, 184, 15)) # Horizontal  line

tide_y = 16
for i in range (4):
    tide_line = soup.find_all('td')[i].get_text() + ' tide: ' + soup.find_all('span', class_='tideTime')[i].get_text()
    if not tide_line[0].isdigit():  # filter out spurious lines when only 3 tides in 24 hrs
        inkyphat.text((10, tide_y), tide_line, inkyphat.WHITE, font=font)
        tide_y += 16

# find wind speed and direction
wx = soup.find_all('div', class_="tideForecast")[0].get_text()
wxList = wx.splitlines()
windSpeed = wxList[17]
windDirection = wxList[13]
print('Wind',windSpeed,windDirection)
inkyphat.text((110, 16), 'Wind: '+windDirection+' '+windSpeed+' mph', inkyphat.WHITE, font=font)

# Find weather description
weather = soup.find(class_="tideForecast")
forecast_items = weather.find_all(class_="tideWxIcon")
outlook = forecast_items[0]
img = outlook.find("img")
desc = img['alt']
print('Outlook',desc)
inkyphat.text((110, 32), desc, inkyphat.WHITE, font=font)

inkyphat.show()
Posted in Raspberry Pi | Tagged , , , , | 2 Comments

InkyPhat Flotilla radio

I treated myself to a Pimoroni InkyPhat Raspberry Pi e-ink display – it’s small but good value compared with some other e-ink displays… and it has THREE colours: black, white – and red. It comes with some beautifully-designed examples such as a calendar, a local weather display and a badge-maker. Being an e-ink display, it retains its contents after the power goes and hence you can unplug it from the Pi and wear it!

Obviously the first thing I had to do was make a radio, so I tweaked my Flotilla radio so it only uses the rotary dial for volume and the 4-button touch panel to choose pre-set stations.

The display update is slow, I guess to ensure maximum quality – there’s none of the ghosting of old images I experienced in early e-book readers. It also forces you to listen to a few seconds of each station before changing channel. And I quite like the drama of the thing.

Making the artwork was a bit of a drag – you have to save images as PNG files with a very specific colour palette – 3 colours only and they must be in the right order. I hate GIMP at the best of times but it drove me mad as I kept getting images that looked ok on screen but swapped colours on the InkyPhat. Eventually it all came good.

I’m really looking forward to making more projects with this neat little colourful e-ink display.

Here’s the code – it assumes you have installed Flotilla and InkyPhat and needs Flotilla Touch and Dial modules. You will also need to have installed mpc and mpd and added 4 radio stations as described in my Flotilla radio project.

#!/usr/bin/env python

# simple internet radio with Flotilla and InkyPhat
# Script by Giles Booth x
# www.suppertime.co.uk/blogmywiki
# Always stop flotilla daemon before running Python with Flotilla
# with 'sudo service flotilla stop'

import os
import sys
import flotilla
import inkyphat
from PIL import Image, ImageFont

# Looks for the dock, and all of the modules we need
# attached to the dock so we can talk to them.

dock = flotilla.Client()
print("Client connected...")

while not dock.ready:
    pass

print("Finding modules...")
touch = dock.first(flotilla.Touch)
dial = dock.first(flotilla.Dial)

if touch is None or dial is None:
    print("Some Flotilla modules required were not found...")
    dock.stop()
    sys.exit(1)
else:
    print("Found. Running...")

os.system("mpc load")
dial_val = 0

inkyphat.set_image(Image.open("splash.png"))
inkyphat.show()

# Starts the loop going so it keeps working until we stop it
try:
    while True:

# volume control using dial
        new_dial_val = int(float(dial.data[0])/10.23)
        if new_dial_val != dial_val:
            dial_val = new_dial_val
            os.system("mpc volume " + str(dial_val))

# Looks for a Touch module and listens for an input

        if touch.one:
            os.system("mpc play 1")
            inkyphat.set_image(Image.open("logo-6music.png"))
            inkyphat.show()

        if touch.two:
            os.system("mpc play 2")
            inkyphat.set_image(Image.open("bbc-ws.png"))
            inkyphat.show()

        if touch.three:
            os.system("mpc play 3")
            inkyphat.set_image(Image.open("fip.png"))
            inkyphat.show()

        if touch.four:
            os.system("mpc play 4")
            inkyphat.set_image(Image.open("radio4.png"))
            inkyphat.show()

# This listens for a keyboard interrupt, which is Ctrl+C and can stop the program
except KeyboardInterrupt:
    os.system("mpc stop")
    inkyphat.paste(inkyphat.Image.new('P', (inkyphat.WIDTH, inkyphat.HEIGHT)))
    font = ImageFont.truetype(inkyphat.fonts.FredokaOne, 36)
    print(dir(inkyphat.fonts))
    message = "goodbye"
    w, h = font.getsize(message)
    x = (inkyphat.WIDTH / 2) - (w / 2)
    y = (inkyphat.HEIGHT / 2) - (h / 2)
    inkyphat.text((x, y), message, inkyphat.RED, font)
    inkyphat.show()
    print("Stopping Flotilla...")
    dock.stop()

Here are the image files I made:

Posted in Raspberry Pi | Tagged , , , , , | Leave a comment

Raspberry Pi Freeview PVR and TV streamer

I’ve had a cheap USB SDR stick sitting in my Amazon basket for… about 2 years. I finally got round to buying the thing.

What is an SDR? It’s a Software Defined Radio – a radio tuner that allows you to listen to analogue broadcasts over a wide range of frequencies and – with the right software – watch and record digital TV. As soon as I got my chunky blue stick – a NooElec R820T2 – I plugged in its little antenna and installed the gqrx radio receiving software on my MacBook. Having grown up in a ham radio-filled household, this was quite nostalgic tuning different bands looking for stuff. The USB stick uses a common chipset and works as a normal FM radio tuner with RDS, but pretty soon I had stumbled upon Heathrow Approach and could listen to radio chat from the cabins of planes flying over my house and later I found some OB frequencies used by ITN (nicely idented) and I assume the BBC (at least I think that’s why I was hearing Radio 2 on some VERY strange frequencies!). You can do all sorts of other clever stuff with SDRs like receiving weather maps and plotting aeroplane and boats on maps. VERY annoyingly I can’t find any Mac software for decoding DAB radio, though you can do this on Windows and Linux machines.

gqrx is available in an experimental version for the Raspberry Pi but I haven’t got it working very well yet – it seems much noisier than it did on the Mac (possibly because I’m in a different room), I had to run it using a -r command line switch to get round a bug where the waveform didn’t show and RDS doesn’t seem to work. Nonetheless on the left here you can see me listening to planes on the Heathrow approach frequency. The ‘waterfall’ is useful for spotting sporadic transmissions on adjacent frequencies.

What I did get working was a Freeview TV tuner and server on the Raspberry Pi. I can now watch live TV & radio off air on my Pi (admittedly not full screen) – and possibly even more usefully, the Pi will stream TV to other devices on my home network, so I can watch live TV in a browser or using VLC on my laptop anywhere in the house.

I even managed to schedule a recording on my MacBook which was recorded as a .ts (transmission stream) file on the Pi – this is the format used by PVRs and can be played in VLC (and converted to a more compact, portable format using other apps too.)

How did I get this working? Glad you asked me that. I finished at about 1am and I didn’t take notes. It involved a lot of fiddling in a studio-managerly fashion. Here’s roughly what I did…

First I installed tvheadend on a RaspberryPi 3. I used this guide – but I had to change quite a lot when it came to configuring it as they set up a satellite TV receiver and I am just using Freeview. For example, I chose DVB-T as my network type:

Tvheadend comes pre-populated with a list of digital TV networks all over the world – luckily I live close enough to Crystal Palace to be able to receive Freeview with a damp piece of string for an aerial (as my late dad G3NXU would say). I suspect if you live further from a transmitter you may find the supplied telescopic antenna pretty useless.

I didn’t have to add any muxes manually as they did in the guide I followed, but I did have to go back to the TV adaptor page and re-enable it before it started scanning muxes and allowed me to then add channels.

Once I’d added the channels I was a bit stuck. I knew tvheadend is a server so on my MacBook I pointed a web browser at the IP address of the Pi on my home network using http://IPADDRESSOFPI:9981/playlist/channels - and straight away BBC1 started playing on the MacBook!

I subsequently found you can view the EPG by browsing to http://localhost:9981/ on the Pi, schedule recordings, and even watch live TV in a browser window.

I found it more useful to use VLC, however. On the Pi just install and use VLC to open a network stream and use http://localhost:9981/playlist/channels as the URL – on another machine on your LAN use http://IPADDRESSOFPI:9981/playlist/channels.

VLC allows you to de-interlace video (improves the appearance of movement), switch audio soundtracks (for example to hear audio description) and turn subtitles on and off. On the Pi itself it couldn’t render video full screen at the same time as decoding and streaming, but clients on the LAN like my MacBook could. The largest I got the picture to play reliably on the Pi was a bit bigger than quarter size, but smaller than half size. (These cheap USB SDR sticks don’t decode HDTV by the way).

I think the NooElec stick provides a great deal of fun for £15 – £20 – there may be better ones you can buy now however. It’s also VERY chunky and blocks all but 1 USB port on the Pi3! I love the idea that such a cheap device opens up a world of radio and data reception, giving me access to TV channels that can’t easily be streamed online as well as schedule recordings and play live TV on laptops and mobile devices anywhere in the house. It could also bring TV and radio to situations without internet access. And there must be some exciting possibilitiesfor all that EPG data – a bot that tweets automatically when a certain program is about to start?

As I type this on the same Raspberry Pi I am listening to BBC 6music off Freeview on the same device – not how the processor is being thrashed… ;)

Posted in BBC, computers, radio, Raspberry Pi, Raspbian, TV | Tagged , , , , , , , , | Leave a comment