Raspberry Pi 3 JukeBox with RFID Music Selection and Gesture Control

A couple of weeks ago friends mentioned a nice project idea. Their daughter is quite young and already a huge music fan. Until she is grown enough to use CDs or small MP3 players (or whatever is en vogue when she is old enough) she could use RFID tagged somethings to select her choice of music in a simple way.
The idea of an RFID controlled Raspberry Pi 3 Music Player is not new. Several examples like this cool looking music robot already exist.  So here I want to add the description of my little prototype JukeBox which uses gesture control to adjust the volume.

Hardware

Raspberry Pi 3 with Jessie
Speaker with 3.5 mm jack
RFID Reader and Cards/Tags
APDS-9960 Gesture Control Chip

Wiring

RFID Reader

RFID Reader Pin # Pin name
SDA 24 GPIO8
SCK 23 GPIO11
MOSI 19 GPIO10
MISO 21 GPIO9
IRQ None None
GND Any Any Ground
RST 22 GPIO25
3.3V 1 3.3V

Gesture Control Sensor

Board Pin Name Remarks Pin # RPi Function
1 VIN +3.3V Power 1 3.3V
2 GND Ground 6 GND
3 SCL Clock 5 BCM 3 (SCL)
4 SDA Data 3 BCM 2 (SDA)
5 INT Interrupt 16 BCM 23 (SDA)

Setting up the Sound

sudo raspi-config

In the advanced options select the audio settings and set audio output to 3.5 mm audo jack.
In /etc/boot.config the parameter dtparam=audio=on should not be commented.

Volume

Adjusting the volume via the command line is possible with

amixer cset numid=1 -- 80%

See this page for more information on using audio on a Raspberry Pi.

Software

A simple python script loaded in /etc/rc.local controls the music being played. It uses the MFRC522 library for reading RFID tags and the VLC python bindings for playing music.

RFID

To use the MFRC522 python library first enable SPI and install the SPI library.

sudo raspi-config
# Interfacing Options > P4 SPI > enable

sudo apt-get install git python-dev --yes
git clone https://github.com/lthiery/SPI-Py.git
cd SPI-Py
sudo python setup.py install
cd ..
git clone https://github.com/mxgxw/MFRC522-python.git

Once the libraries are installed RFID tags can be read with the example program:

cd MFRC522-python
sudo python Read.py

These RFID tag IDs are used in the example python script below to select the desired MP3s.

VLC

For playing MP3s I used the VLC python bindings. Numerous other possibilities exist as well, but I chose VLC because of its documented API. The python binding can be found in the VLC git repository. Simply place the file vlc.py beneath the own python script.

Gesture Control with APDS-9960

For detecting gestures with the APDS-9960 sensor I found these sources on github:

https://github.com/rm-hull/apds9960

https://github.com/liske/python-apds9960

The first repository provides a setup script for the library. The second repository contains an example python script for detecting gestures.

Python Scripts

Adjusting the Volume with Gesture Control

A simple way for adjusting the volume is running a python script dedicated to detecting gestures in the background. Such a script can be launched in /etc/rc.local . The volume is adjusted with a system call.

import os
import time

from apds9960.const import *
from apds9960 import APDS9960
import RPi.GPIO as GPIO
import smbus

port = 1
bus = smbus.SMBus(port)
apds = APDS9960(bus)

def intH(channel):
  print("INTERRUPT")

GPIO.setmode(GPIO.BOARD)
GPIO.setup(7, GPIO.IN)

dirs = {
    APDS9960_DIR_NONE: "none",
    APDS9960_DIR_LEFT: "left",
    APDS9960_DIR_RIGHT: "right",
    APDS9960_DIR_UP: "up",
    APDS9960_DIR_DOWN: "down",
    APDS9960_DIR_NEAR: "near",
    APDS9960_DIR_FAR: "far",
}

volume = 50   # 0..100 %
def adjustVolume(value):
  global volume
  volume += value
  if volume < 0.0:     volume = 0   elif volume > 100.0:
    volume = 100
  if volume >= 0.0 and volume <= 100.0:
    print('Adjust volume to ' + str(volume) + ' %')
    cmd = 'amixer cset numid=1 -- ' + str(volume) + '%'
    os.system(cmd)
  else:
    print('Volume value out of bounds: ' + str(volume) + ' (0.0 .. 100.0 %)')

def run():
  # Add interrupt event: rising edge
  GPIO.add_event_detect(7, GPIO.FALLING, callback = intH)
  apds.setProximityIntLowThreshold(50)
  apds.enableGestureSensor()

  while True:
    time.sleep(0.5)
    if apds.isGestureAvailable():
      motion = apds.readGesture()
      gesture = dirs.get(motion, "unknown")
      print("Gesture={}".format(gesture))

      if gesture == 'up':
        adjustVolume(10)
      elif gesture == 'down':
        adjustVolume(-10)

try:
  print('Gesture Control')
  print('Press Ctrl-C to stop.')
  run()
except KeyboardInterrupt:
  print "Ctrl+C captured, ending read."
  continue_reading = False
  GPIO.cleanup()
  exit()

Playing Music with RFID Tags

import vlc
import RPi.GPIO as GPIO
import MFRC522
import datetime
import os
import time

MIFAREReader = MFRC522.MFRC522()

mp3path = '/home/pi/Music/'
mp3dict = {
'123-234-456-678' :'A.mp3',	# 1
'123-234-456-679' : 'B.mp3',	# card
'123-234-456-670' : 'C.mp3'	# 2
}
isPlaying = False
continue_reading = True
currentUID = '-1'
lastUID = '-1'

PLAYERS = {}

volume = 50 # 0..100 %
def adjustVolume(value):
  global volume
  volume += value
  if volume < 0.0:     volume = 0   elif volume > 100.0:
    volume = 100
  if volume >= 0.0 and volume <= 100.0:
    print('Adjust volume to ' + str(volume) + ' %')
    cmd = 'amixer cset numid=1 -- ' + str(volume) + '%'
    os.system(cmd)
  else:
    print('Volume value out of bounds: ' + str(volume) + ' (0.0 .. 100.0 %)')

def isMP3playing():
  global isPlaying
  print('Is MP3 playing? ' + str(isPlaying))
  return isPlaying

def playMP3(currentUID):
  global isPlaying
  if not isMP3playing() and str(currentUID) != '-1':
    print('Play MP3 ' + mp3path + mp3dict[currentUID])
    p = vlc.MediaPlayer(mp3path + mp3dict[currentUID])
    p.play()
    lastUID = currentUID
    PLAYERS[currentUID] = p
    isPlaying = True
    print( 'Playing: ' + str(lastUID))
    #while pygame.mixer.music.get_busy() == True:
    #    continue
  else:
    print('Error: Play MP3 ' + str(currentUID))

def pauseMP3(currentUID):
  global isPlaying
  if isMP3playing() and str(currentUID) != '-1':
     print('Pause MP3 ' + mp3path + mp3dict[currentUID])
     if PLAYERS[currentUID] != None:
       PLAYERS[currentUID].pause()
       isPlaying = False
     else:
       print('Error: Pause MP3 ' + str(currentUID))

def stopMP3(currentUID):
  global isPlaying
  if isMP3playing() and str(currentUID) != '-1':
    print('Stop MP3 ' + mp3path + mp3dict[currentUID])
    if PLAYERS[currentUID] != None:
      PLAYERS[currentUID].stop()
      isPlaying = False
    else:
      print('Error: Stop MP3 ' + str(currentUID))

def run():
    global isPlaying
    a = None
    b = None

    while continue_reading:
        (status,TagType) = MIFAREReader.MFRC522_Request(MIFAREReader.PICC_REQIDL)
        if status == MIFAREReader.MI_OK:
            print('Tag detected')

        # Get the UID of the card
        (status,uid) = MIFAREReader.MFRC522_Anticoll()
        print('Status: ' + str(status) + ' [OK = ' + str(MIFAREReader.MI_OK) + ']')

        if status == MIFAREReader.MI_OK:
            a = datetime.datetime.now()
            if isMP3playing() == False:
                currentUID = str(uid[0]) + '-' + str(uid[1]) + '-' + str(uid[2]) + '-' + str(uid[3])
                print('Current UID: ' + str(currentUID) + ' / Last UID: ' + lastUID)

                if lastUID != currentUID:
                    print('Start playing MP3: ' + str(mp3dict[currentUID]))
                    playMP3(currentUID)

        elif status == MIFAREReader.MI_ERR:
            # check timestamps, this status is detected just after reading a tag successfully
            b = datetime.datetime.now()
            if a != None:
                print('Check time delta ' + str(a))
                c = b-a
                print('Time delta: ' + str(c) + ' ' + str(c.seconds))
                if c.seconds == 0:
                    print('Do not stop the music')
                    pass
                else:
                    print('Stop the music')
                    if isMP3playing() == True:
                        stopMP3(currentUID)

try:
  print('My little JukeBox')
  print('Press Ctrl-C to stop.')
  adjustVolume(30) # default is 50
  run()
except KeyboardInterrupt:
  print('Ctrl+C captured, ending read.')
  continue_reading = False
  GPIO.cleanup()
  exit()

Comments

Technically the same techniques described here could be used to play videos on a connected display. Perhaps this is a nice extension of such a project…

Raspberry Pi RFID Jukebox Prototype
Raspberry Pi RFID Jukebox Prototype
Advertisements

Audio Book: Raspberry Pi Zero Internet Radio with pHAT BEAT

A couple of days ago I laid my hands on a pHAT BEAT and two small speakers. Together with a Raspberry Pi Zero (and an Internet connection of course) this makes building an internet radio easily possible. And yes, inspiration for this project was also the Pirate Radio Kit.
The pHAT BEAT comes along with stereo output, an amplifier, a couple of buttons for adjusting the volume, playing/pause, forward/backward and powering off and a number of bright and shiny LEDs. Just the perfect audio hardware component for an internet radio.

Hardware

Raspberry Pi Zero with Micro SD card and up-to-date OS
USB WiFi stick (not needed if a Raspberry Pi Zero W is used)
pHAT BEAT
2
small speakers
some cables
USB power supply

Assemble the hardware as required. This implies some soldering for the headers of the Raspberry Pi Zero and the pHAT BEAT as well as the connections to the speakers. This tutorial is a good guideline to see what to do.

Software

Once the Raspberry Pi Zero is accessible headless in the local WLAN network (see this blog post for setup instructions) install the pHAT BEAT Python library.

Luckily the software for an internet radio project already exists. The setup is really made simple by running the setup script only. The setup script installs the required software and adjusts the whole configuration on the Raspberry Pi Zero. See https://github.com/pimoroni/phat-beat/tree/master/projects/vlc-radio for further reference.

Once the installation is complete, reboot. After reboot the internet radio will be automatically started and will play some example music.

The pHAT BEAT’s buttons directly work with the example projects software. Adjusting the volume or switching between different items on a configurable playlist (see configuration below) is directly possible. Even the off button immediately works: it turns off the radio and fully shuts down the Raspberry Pi Zero.

Configuration

Configure Internet Radio Streams

Collect the URLs of your favourite internet radio streams. Create the file /home/pi/.config/vlc/playlist.m3u . Insert the URLs into the playlist as in this example:

Example playlist.m3u
#EXTM3U
#EXTINF:0,station1
#EXTVLCOPT:network-caching=1000
http://station1.net/.../...
#EXTINF:0,station2
http://station2.com/.../.../mp3/...
#EXTINF:0,station3
http://station3.something/...

Alternatively create a playlist containing the radio stream URLs of your choice in VLC and save the playlist to a file. This file can be copied to the Raspberry Pi Zero to /home/pi/.config/vlc/playlist.m3u.

After reboot the forward/backward buttons of the pHAT BEAT can be used to switch between the different internet radio streams.

Wrapping: The Result

The wrapping was simple in this case: an old book became a nice „audio book“! Similar to my ‚book book shelves‘ an old book is hollowed inside with a sharp knife so the hardware fits in.
Surprisingly well is the sound of the speakers inside the book!
All I need now is to find a way to operate the small buttons of the pHAT BEAT…

Info & Links

https://github.com/pimoroni/phat-beat

 

sleepy pillow

Quite a while ago I had the idea to create a pillow with a Lilypad MP3 . The „Sleepy Pillow“ should play songs at random for approx. 45 minutes and then power down as good as possible to save battery.

The Lilypad MP3 is an Arduino board that takes an SD card with audible files and plays them with speakers attached.

This is how the Sleepy Pillow was realized:

components used

Lilypad MP3
speakers
conductive thread, needle for sewing
LiPo, additional JST connectors
Lilypad switch
fabric
pillow filling
for testing: alligator clips
mini SD card with MP3 songs

make it happen

First I set up the main components with alligator clips before sewing. That way the source code could be conveniently tested.

IMG_20151003_141823_sleepypillow_setup

I put something below the speakers to prevent them from vibrating too much while testing.

From my song collection I chose several songs in MP3 format and placed them on the mini SD card using an adapter. The filenames are consecutive numbers.

When the source code was working so far I started to sew the components on the fabric I selected as a pillowcase. For the LiPo I created a small bag.

I added a switch for turning the power completely off manually.

to consider

The Lilypad MP3 can not play songs when attached via USB to a computer. Only serial output can be seen! The player starts to play the files but the playback stops after a few seconds. The reason is that there is not enough power to drive the speakers via USB. An additional power supply such as a LiPo is required. The program can only be fully tested attaching a battery to the Lilypad.

The audio files on the SD card may be of the type MP3, WAV, MP4, FLA, OGG, WMA or AAC. The filenames should be in the format 8.3 which is up to 8 digits for the filename and 3 digits for the file extension. I went for consecutive numbers as filenames to ease random selection.

the source code

The source code is based on the examples that can be found for the Lilypad MP3. The sleep code originates from the Arduino playground.

SleepyPillow.ino

I used the latest Arduino Software from https://www.arduino.cc/en/Main/Software. Great tutorials to get started with the Lilypad MP3 are at the sparkfun website.

how it works

When the Sleepy Pillow is turned on it starts to play songs from the SD card at random. The music stops after the desired time limit is reached. Then the Lilypad MP3 is „put to sleep“ to save battery. Additionally it is possible to use the power switch…

The volume is automatically turned down approx. 10 s before the time limit is reached.

IMG_20151007_194819_sleepypillow