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

ESP8266: Switch WiFi Connections using RFID

It took me some time to find an example for using RFID (Radio Frequency IDentification) on an Arduino. RFID is commonly used for identification, tracking, etc. . RFID senders (tags) are so small that they can be implanted. I heard rumours that some humans already started to wear RFID senders under their skin! Brave new world… Imagine to turn on the coffee machine contactless by pointing at it with the index finger. Such a thought gives me the creeps.

However, the example I describe here switches the WiFi connection only depending on the RFID tag discovered. Easily expandable for different purposes!

Components used

Adafruit Feather Huzzah
Monochrome OLED display
RFID-RC522 receiver + matching RFID tags

Wiring

Adafruit Feather Huzzah RFID-RC522 OLED Display
3,3 V 3,3 V  3,3 V
GND GND GND
SDA (4) SDA
SCL (5) SCL
2 (Arduino Uno: 10) SDA
SCK/14 (Arduino Uno: 11) SCK
MO/13  MOSI
MI/12 MISO
16 * (Arduino Uno: 9)  RST

For SDA and Reset two free IO pins should be used on the ESP8266 module. These two pins will be initialized during setup.

The wiring of the RFID receiver to the ESP8266 module is described here as well, including a wiring diagram.

Software

The RFID Arduino library https://github.com/miguelbalboa/rfid can be used. The library can be downloaded from the github repository. The download folder rfid-master should be renamed to rfidmaster and should be copied to the Arduino IDE’s library directory.

During compilation the rfid library issues a warning that it is incompatible with STM32F1 architecture. But this seems to have no influence, the software is working in the end.

Sample Sketch

In this example the WiFi connection will be switched depending on the RFID tag that was recognized. The tags that can be used are hard coded.

#include <ESP8266WiFi.h>  // http://esp8266.github.io/Arduino/versions/2.0.0/doc/libraries.html
#include <WiFiClient.h>   // https://www.arduino.cc/en/Reference/WiFiClient

#include <SPI.h>
#include <MFRC522.h>

// OLED ESP_ssd1306_128x64_I2C
#include <ESP_SSD1306.h>    // Modification of Adafruit_SSD1306 for ESP8266 compatibility
#include <Adafruit_GFX.h>   // Needs a little change in original Adafruit library (See README.txt file)
#include <SPI.h>            // For SPI comm (needed for not getting compile error)
#include <Wire.h>           // For I2C comm, but needed for not getting compile error

boolean debug=true;

// Arduino Uno
//#define SS_PIN 10 // SDA an Pin 10
//#define RST_PIN 9 // RST an Pin 9

// ESP8266 (Adafruit Feather Huzzah)
#define RST_PIN 15 // RST-PIN for RC522 - RFID - SPI - Modul GPIO15
#define SS_PIN  2  // SDA-PIN for RC522 - RFID - SPI - Modul GPIO2 

MFRC522 mfrc522(SS_PIN, RST_PIN); // RFID-Empfänger benennen

const unsigned long BAUD_RATE = 115200;  // serial connection speed
const unsigned long HTTP_TIMEOUT = 10000;   // max respone time from server

void initSerial();
void connectWiFi(const char* ssid, const char* password);
void disconnectWifi();
WiFiClient client;

const char* ssid1 = "SSID1";
const char* password1 = "PASSWORD1";
const char* ssid2 = "SSID2";
const char* password2 = "PASSWORD2";

const char* RFIDTAG1 = "IDe7b5f653";
const char* RFIDTAG2 = "ID2e6432";

#define OLED_RESET  16  // Pin 16 -RESET digital signal
ESP_SSD1306 display(OLED_RESET); // FOR I2C

void initOLEDDisplay();
void showConnection(const char* ssid);

void setup() {

  initSerial();
  if( debug ) Serial.println("setup");

  initOLEDDisplay();

  // start SPI connection
  // initialize RFID receiver
  SPI.begin();
  mfrc522.PCD_Init();
}

void loop() {

  // RFID-TAG is close
  if ( ! mfrc522.PICC_IsNewCardPresent() ) {
    return; // gehe weiter...
  }

  // RFID-TAG was detected
  if ( ! mfrc522.PICC_ReadCardSerial()) {
    return; // gehe weiter...
  }

  String tag = "ID";
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    // UID of RFID-TAG is read, consists of 4 single blocks
    tag += String(mfrc522.uid.uidByte[i], HEX);
  }
  if( debug ) {
    Serial.print("ID of RFID-TAG: ");
    Serial.print(tag.c_str());
    Serial.println();
  }

  // decide what to do for a certain tag
  if( tag == RFIDTAG1 ) {
    disconnectWifi();
    connectWiFi(ssid1, password1);
  }
  if( tag == RFIDTAG2 ) {
    disconnectWifi();
    connectWiFi(ssid2, password2);
  }
}

// initialize serial port
void initSerial() {
  Serial.begin(BAUD_RATE);
  while (!Serial) {
    ;  // wait for serial port to initialize
  }
  if( debug ) Serial.println("Serial ready");
}

void initOLEDDisplay() {
  // SSD1306 Init
  display.begin(SSD1306_SWITCHCAPVCC);  // Switch OLED
  display.clearDisplay();
  // Show image buffer on the display hardware.
  // Since the buffer is intialized with an Adafruit splashscreen
  // internally, this will display the splashscreen.
  display.display();
  delay(2000);

  // Clear the buffer.
  display.clearDisplay();
}

// attempt to connect to WiFi
void connectWiFi(const char* ssid, const char* password) {
  WiFi.mode(WIFI_STA);
  // connect to the WiFi network
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    if( debug ) Serial.print(".");
  }
  if( debug ) {
    Serial.println("");
    Serial.println("WiFi connected");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
  }
  if( WiFi.status() == WL_CONNECTED ) {
    showConnection(ssid);
  } else {
    showConnection("-");
  }
}

void disconnectWifi() {
  WiFi.disconnect();
}

void showConnection(const char* ssid) {
  display.clearDisplay();

  display.setTextSize(2);
  display.setTextColor(WHITE);

  display.setCursor(0, 0);
  display.println("SSID");

  display.setCursor(0, 15);
  display.println(ssid);

  display.display();
}

This is how the test setup looks like:
RFID switch