壁掛けCDプレイヤーもどき
introduction
壁掛けCDプレイヤー、そういうのもあるのか
オタグッズとしてのCD。基本データはリッピングして物理媒体はしまっちゃうが、見せつつ使う方法もあるようだ。
これをたくさん買ってもいいのだが、いくつも買えるほど財布事情も良くないし、一度セッティングしたらそのままだろうなという気もする。
そこで、①壁にCDを置く、②CD再生が可能、の2点を満たすものを作ろうかなと。
概要
さて、先述の発想からさらに要件を具体化してみる
・同時再生はしないので、再生機構は1つでよい
・CDのジャケを見える形で収納、再生
・壁に掛けたいのでできるだけ軽いといい
・CDの盤を見せるのはあきらめるとする
・再生/停止/曲飛ばし の操作
さらに高みを望むのならば
・リピート再生/シャッフル再生
・インスト曲のスキップ
とかか
再生するCDの選択は機器にCDケースをセットするとする。
CDを置いたらその中身が再生されるような。
処理はラズパイゼロを想定(ちょうど浮いてるので)
CDを置いたときの検知はRFIDかな~
画像とかバーコードもありかな。
手軽っぽいRFIDをメインに考えてみる
ラズパイゼロで音楽再生
ラズパイゼロにはオーディオ端子がない
そのため、USBスピーカーから鳴らすのがベターだが、そのままではならないらしい
このIDをいじくる必要があったりするらしいが、今回はなんか再生できたので次に行く
テスト音源はもとから存在していたもの。
しばらくしたら通らなくなったので、やっぱりデバイスの設定をします
cat /proc/asound/cards
これでデバイスの番号を確認する。
ラズパイゼロはデフォだとHDMIが0番に割り当てられている。
今回usbにさしたデバイスは1が割り振られている。
sudo nano /usr/share/alsa/alsa.conf
設定ファイルをいじる
defaults.ctl.card と defaults.pcm.card の値が0になっているのでデバイスに合わせる(ここでは1)
これでusbデバイスからおとが なるようになった!
Pythonからflacを再生
さて、いろんな方法があるらしいがomxplayerを使うのがいいらしい
したがって使おうと思ったけど、どうやらraspberry pi OSの32bit版には無いっぽい?インストールしようとしてもできなかった。
そのため、VLCで代用しよう。
import subprocess
import time
import vlc
def play_flac(file_path):
instance = vlc.Instance()
player = instance.media_player_new()
media = instance.media_new(file_path)
player.set_media(media)
player.play()
return player
def stop_music(player):
player.stop()
こんな感じでファイルの再生ができる
一時停止と次の曲へのスキップはそれぞれ以下
player.set_pause(1) # 一時停止
player.next() # スキップ
RFIDとラズパイ
使うのはM5stack用のRFIDモジュール
安価で技適大丈夫そうなのはこれくらいしかないから
https://www.switch-science.com/products/8301
これはI2C接続なので、その方法を探る
I2C通信は{SCL,SDA,5V,GND}のピンがある。
それぞれは次のように対応している
SCL : 5, SDA : 3, 5V : 2, GND : 6
トランシーバーバッファ:64 bytes
アドレス:0x28
さて、これを直接ラズパイpythonで扱う文献は探せなかったので、以前Arduinoで使った際の方法を参考にする。
その時はサンプルコードがあって、MFRC522というライブラリを使っていた。
じゃあこれがpythonにあればそれでいいんじゃないかということで調べるとある。
if __name__ == '__main__':
# read csv file uid:path
uid_list = []
with open('uid_list.csv') as f:
reader = csv.reader(f)
for line in reader:
uid_list.append(line)
#print(f'{uid_list}')
#print(f'1 uid:{uid_list[0][0]}, path:{uid_list[0][1]}')
# Hook the SIGINT
signal.signal(signal.SIGINT, end_read)
# Reader is located at Bus 1, adress 0x28
i2cBus = 1
i2cAddress = 0x28
# Create an object of the class MFRC522
MFRC522Reader = MFRC522(i2cBus, i2cAddress)
version = MFRC522Reader.getReaderVersion()
print(f'MFRC522 Software Version: {version}')
while continue_reading:
# Scan for cards
(status, backData, tagType) = MFRC522Reader.scan()
if status == MFRC522Reader.MIFARE_OK:
print(f'Card detected, Type: {tagType}')
# Get UID of the card
(status, uid, backBits) = MFRC522Reader.identify()
if status == MFRC522Reader.MIFARE_OK:
print('Card identified, UID: ', end='')
uid_str = ''
tmp = ''
print(f'{uid}')
ほぼサンプルだけど、こんな感じで埋め込んでる情報を読み取れる
タグはピカリ館のNCFシール
書き込みはAndroidアプリNFC Tools
テキストとしてCD毎にフォルダ分けしたパスを書き込む
組み合わせ
#!/usr/bin/env python3
# -*- coding: utf8 -*-
"""
Scans countinously for cards and prints the UID
"""
__author__ = "Christoph Pranzl"
__version__ = "0.0.5"
__license__ = "GPLv3"
# rfid
from mfrc522_i2c import MFRC522 # gpl
import signal
# csv
import csv
import pprint
import os
# player
import subprocess
import time
import vlc
# switch
import RPi.GPIO as GPIO
continue_reading = True
# switch GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_UP) # stop
GPIO.setup(26, GPIO.IN, pull_up_down=GPIO.PUD_UP) # skip
GPIO.setup(6, GPIO.IN, pull_up_down=GPIO.PUD_UP) # play
# 0 : play
# 1 : pouse
# 2 : skip -> prev flag
Play_flag = 0
def button_callback(channel):
print(f'Pushed {channel}')
# Capture SIGINT for cleanup when script is aborted
def end_read(signal, frame):
global continue_reading
print('Ctrl+C captured, ending read')
continue_reading = False
def play_flac(file_path):
instance = vlc.Instance()
player = instance.media_player_new()
media = instance.media_new(file_path)
player.set_media(media)
player.play()
return player
def stop_music(player):
player.stop()
def play_album(path):
print(f'album list')
file_names = os.listdir(path)
play_list = []
for name in file_names:
print(f'{name}')
tmp = path + '/' + name
play_list.append(tmp)
player = vlc.MediaListPlayer()
mediaList = vlc.MediaList(play_list)
player.set_media_list(mediaList)
player.set_playback_mode(vlc.PlaybackMode.loop) # looping
#player.set_playback_mode(vlc.PlaybackMode.default) # 1 set
player.play()
f = True
play_pause_flag = True # True : play , False : pause
while f:
b1 = GPIO.input(17)
b2 = GPIO.input(26)
b3 = GPIO.input(6)
#print(f'b1 : {b1}, b2 : {b2}, b3 : {b3}')
if b1 == 0:
print('STOP')
player.stop()
time.sleep(1)
break
elif b2 == 0:
print('skip')
player.next()
time.sleep(1)
elif b3 == 0:
print('play / pause')
if play_pause_flag:
print('pause')
player.set_pause(1)
play_pause_flag = False
else:
print('play')
player.play()
play_pause_flag = True
time.sleep(1)
time.sleep(0.05)
def callback(channel):
print('button pushed %s'%channel)
if __name__ == '__main__':
# read csv file uid:path
uid_list = []
with open('uid_list.csv') as f:
reader = csv.reader(f)
for line in reader:
uid_list.append(line)
#print(f'{uid_list}')
#print(f'1 uid:{uid_list[0][0]}, path:{uid_list[0][1]}')
# Hook the SIGINT
signal.signal(signal.SIGINT, end_read)
# Reader is located at Bus 1, adress 0x28
i2cBus = 1
i2cAddress = 0x28
# Create an object of the class MFRC522
MFRC522Reader = MFRC522(i2cBus, i2cAddress)
version = MFRC522Reader.getReaderVersion()
print(f'MFRC522 Software Version: {version}')
while continue_reading:
# Scan for cards
(status, backData, tagType) = MFRC522Reader.scan()
if status == MFRC522Reader.MIFARE_OK:
print(f'Card detected, Type: {tagType}')
# Get UID of the card
(status, uid, backBits) = MFRC522Reader.identify()
if status == MFRC522Reader.MIFARE_OK:
print('Card identified, UID: ', end='')
uid_str = ''
tmp = ''
#print(f'{uid}')
for i in range(0, len(uid) - 1):
print(f'{uid[i]:02x}:', end='')
tmp = format(uid[i],'02x')
uid_str = uid_str + tmp + ':'
print(f'{uid[len(uid) - 1]:02x}')
tmp = tmp = format(uid[len(uid) - 1],'02x')
uid_str = uid_str + tmp
print(f'uid_str : {uid_str}')
# リスト参照してパス読み出し
for line in uid_list:
if line[0] == uid_str:
print(f'Path ::: {line[1]}')
play_album(line[1])
現実に実装
おわりに
動いてよかった(小並感)
ラズパイゼロはポートが少なかったり32bitOSだったりといろいろ制約があったが、高度なことをしないならレガシーなものを使えば何とかなりそう
以上