Pythonを使ってWiiリモコンから音を出す

果たして自分以外にこんなことやりたい人がいるのか分からないですけど他にあまりにも情報が少ないので一応残しておきます。 基本的にはhttp://wiibrew.org/wiki/Wiimoteの通りです。

必要なもの

フォーマットについて

Wiiリモコンのスピーカーで使える音声ファイルのフォーマットには8bit符号付きPCMか4bitADPCMのどちらかを選べるようです。

ただADPCMの方はヤマハのチップにしか使われてないフォーマットらしく変換できなかったのでPCMを選択しました。(ffmpegがサポートしてるらしいけど詳しくは知らない)

ファイルを変換するにはsoxを使って

sox --norm input.mp3 -b 8 out.raw channels 1 rate 2000  # 8bit, 2khz, mono

でできます。

Bluetoothソケット

PyBluezでどうやってWiiリモコンと通信したのか一応補足。

import bluetooth

ADDR = '00:19:1D:30:D2:2C'  # wiiリモコンのMACアドレス
socket = bluetooth.BluetoothSocket(bluetooth.L2CAP)
socket.connect((ADDR, 0x13))  # ここでWiiリモコンの①と②を同時押し

def write(value):
    socket.send(('a2' + value).decode('hex'))


def read(bytes):
    return socket.recv(bytes).encode('hex')

wiiリモコンMACアドレスhcitool scanとするかbluetooth.discover_devices(lookup_names=True)で調べられます。 write()、read()でWiiリモコンにデータ送ったり受け取ったりできます。

頭の0xa2はこちらから送信するときは必ず付けなくてはいけないようです。 また、Wiiリモコンから送られてくるデータの1バイト目は必ずa1になってます。

コレだけでとりあえず必要なものは揃いましたね。

スピーカー初期化

Wikiによると

  1. スピーカーの有効化(0x14へ0x04を送信)
  2. スピーカーのミュート(0x19へ0x04を送信)
  3. レジスタ0xa20009へ0x01を書き込み
  4. レジスタ0xa20001へ0x08を書き込み
  5. レジスタ0xa20001へ7-byte configurationを書き込む
  6. レジスタ0xa20008へ0x01を書き込み
  7. ミュート解除(0x19へ0x00を送信)

とあります。順番に行きましょう。

スピーカー有効化

write('1404')

スピーカーのミュート、ミュート解除

2. スピーカーのミュート(0x19へ0x04を送信)

write('1904')

7. ミュート解除(0x19へ0x00を送信)

write('1900')

7-byte configuration

スピーカーを鳴らすための設定は以下のような7バイトのデータになってます。 00 FF RR RR VV 00 00

FF: フォーマット。0x40なら8bitPCM、0x00ならADPCM

RR RR: サンプリングレート。 PCMの場合 12000000/RATEの値を16進数にしたものがRR RRになります。但しリトルエンディアンなので注意。

どうもBluetoothドライバ他との兼ね合いで2kHzより上はうまく音が出ないみたいです。

2kHzなら 12000000/2000 = 6000 = 0x1770 となるのでRR RRは 70 17となります。

VV: ボリューム 0x00から0xffの範囲

ボリュームを0x40とすると今回必要な7-byte configurationは 00 40 70 17 40 00 00 のようになります。

レジスタへの書き込み

レジスタに書き込みをするには以下のような23バイトのデータを送ります。 (a2) 16 MM FF FF FF SS DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD

MM: 書き込むレジスタの種類。今回は0x04

FF FF FF : アドレス

SS: サイズ(bytes)

DD: データ。20バイトに満たない場合はデータを左詰め?で0で埋めます。

以上を踏まえて

3. レジスタ0xa20009へ0x01を書き込み 4. レジスタ0xa20001へ0x08を書き込み 5. レジスタ0xa20001へ7-byte configurationを書き込む 6. レジスタ0xa20008へ0x01を書き込み は、

write('1604a200090101000000000000000000000000000000')
write('1604a200010108000000000000000000000000000000')
write('1604a200010700407017400000000000000000000000')
write('1604a200080101000000000000000000000000000000')

音声データ送信

スピーカーの初期化が終わったら実際にデータを送信します。

音声データをWiiリモコンへ送信するときのフォーマットは

(a2) 18 LL DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD

DDはデータ。LLはバイト長…と思いきやビット長らしいです、ここで数時間詰まりました(よく見たらWikiにもちゃんと書いてある)

あとは実際にガンガン送信するだけなんですけどもう一つ重要な事があって、それは「設定したサンプルレートに合わせた間隔でデータを送る」ということです。つまり8bit、サンプルレートが2kHzなら2kB/s、Wiiリモコン送れる音声データは一度に20バイト。

ということは結局10ミリ秒間隔で送信すれば良いことになります。 この間隔がちょっとでも違うと全く音が出ません。ここでも数時間取られましたよ…。

まとめると

こうなります。書き殴りの汚いコードなのは許してね。

import time

import bluetooth


def write(value):
  r.send(('a2' + value).decode('hex'))


def recv():
  return r.recv(25).encode('hex')


def init_speaker():
  write('1404')
  write('1904')

  write('1604a200090101000000000000000000000000000000')
  write('1604a200010108000000000000000000000000000000')
  write('1604a200010700407017400000000000000000000000')
  write('1604a200080101000000000000000000000000000000')

  write('1900')


def send_song(songname, dur=0.01):
  f = open(songname).read().encode('hex')
  for i in range(len(f)/20):
    write('18a0' + f[i*40:(i+1)*40])
    time.sleep(dur)

ADDR = '00:19:1D:30:D2:2C'

r = bluetooth.BluetoothSocket(bluetooth.L2CAP)

r.connect((ADDR, 0x13))


write('11f0')  # LEDの点滅を止める

init_speaker()
send_song('out.raw')

実際に動いてるとこ

うーん……楽しい!!!!!!!!!!!!!

たまに何故か音が出ない時があって、どうもスピーカーの初期化に失敗してるみたいです。そういうときはinit_speaker()を何回か繰り返すと音が出るようになります。

ちなみにPythonWiiリモコン使いたいだけならcwiidってライブラリがあるのでそっち使ったほうがいいです。ただ自分でWiiリモコンぽちぽち押しながらWikiとにらめっこしてコード書くのも楽しいですね。

Skype4Pyで作ったBotが反応しなくなった

前にSkype4Pyで特定のワードに反応して発言するBotを作ったのを思い出して久々に起動してみたら反応しなくなってた。

Skype4PyでBotを作る時はだいたい

import Skype4Py

skype = Skype4Py.Skype()


def hoge(msg, event):

  if event == Skype4Py.enums.cmsReceived: # "RECEIVED"

    pass# ほげほげ


skype.OnMessageStatus = hoge

みたいにするけど、最近のSkypeだと'RECEIVED'ってイベントが起こらない。どうもSkypeP2Pベースからクラウドベースに移行したのが原因らしい。

とはいえ今でもP2Pベースのチャットルームは作成できるのでそこにBotを参加させれば以前と同じように動作する。

どこでも良いので(グループでなくても可)チャットを開いて

/createmoderatedchat


と入力すると参加者が自分ひとりのグループが作成される。このグループは以前のようにP2Pベースなのであとは適当に参加者を追加して煮るなり焼くなり。ただこれもいつサポートが打ち切られるかわからないけど。

CD50のオイル交換

CD50のオイルを交換したのでメモ。 オイル交換は原付きだろうがリッターバイクだろうが基本的にやることは同じ。下から抜いて上から入れるだけ(上から抜く時もある)

用意するもの

  • 廃油受け

自分のバイクのオイルの容量に合ったものを選ぶ。ホームセンターとかバイク用品店行けば売ってます。 ケチりたい人は同じようなサイズの箱にビニールをかぶせてトイレットペーパーを敷き詰めれば代用できる。

CD50の場合は17mm

ドレンボルトは鬼のような力で締まってる事が多いのでメガネレンチを使ったほうが舐めにくい。

  • オイル

VENT VERT 10W-50

今回は余りがあったから↑のオイルを入れた。

普通はCD50にこんな良いオイルを入れる必要は無いのでホンダのG1で十分。

  • ドレンワッシャー

ワッシャー自体が潰れて変形することでオイル漏れを防ぐものなので普通のワッシャーでは代用不可。 使いまわせるけど高いものではないので毎回交換がおすすめ。 CD50のドレンワッシャーは内径12mm

  • パーツクリーナー 必須ではないけどオイルが垂れた場所に吹きかけるとよく落ちる。

オイルを抜く

エンジンが冷えきってるとオイルが硬くてうまく抜けないので5分くらい暖気してからだとちょうど良い。逆にエンジンが熱いときにオイルを抜こうとするとヤケドするので注意。

クランクケースの下で真下を向いてるのがドレンボルト。あらかじめ径がわかっていれば間違えることはないでしょう。

あとは外すだけだけど稀に回す方向を間違える人がいるので気をつけること。 舐めるだけならまだしもネジ切ったりすると悲惨。 閉めるときは時計回し、緩めるときは反時計回り。

ドレンボルトを外す前に廃油受けをセットしておく。

ここで大事なのが使い終わった廃油受けは厳重にビニール袋に入れること! コレを言いたいが為にこのエントリーを書いたようなモン。 私は一度ちゃんと処理せずに悲惨な目にあいました。

新しいオイルを入れる

オイルが抜けきったらドレンボルトに新しいドレンワッシャーを付けて締める。必要以上に締め付けるとボルトを舐めたりネジ切ったりするので自信がない人はトルクレンチを使うのが吉。 締め付けトルクはだいたい20~25Nm

オイルジョッキ(ダイソーの麺つゆいれで代用)があるとオイルの容量も測れて入れる時も楽。 CD50はオイル交換時600ml必要。

カワサキのオイルってちゃんとライムグリーンなのね。

キャップを外してオイルを入れる。

おわり

エンジンが掛かるか、しばらくアイドリングして問題ないか、オイル漏れがないか、ボルトの緩みがないかを確認しておわり。

Raspberry PiをAirPlayサーバにする

Raspberry PiにShairport Syncをインストールした時のメモ。 AirPlayはBluetoothのようにペアリングが必要なく家に帰ってきてWifiに接続するだけでワイアレスでスマホから音楽をかけられるのでたいへん便利。

Shairportには色々な派生バージョンがあるけど、評判のいいShairport Syncをチョイス。 長いので以下Shairportと書くことにする。

Shairport Syncのインストール

基本的にはREADME.mdの通り。

必要なパッケージのインストール

sudo apt-get install libshairport2 autoconf libtool libdaemon-dev libasound2-dev libpopt-dev libconfig-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev

libsoxr-dev(The SoX Resampler)は音を良くする(?)ためのライブラリ。あってもなくても良いけど使うとCPU負荷が高くなるらしい。

ビルド

git clone https://github.com/mikebrady/shairport-sync.git
cd shairport-sync
autoreconf -i -f
./configure --with-alsa --with-avahi --with-ssl=openssl --with-metadata --with-systemd
make

SoXを使いたい人は./configureのオプションに --with-soxr を付け加える。

ユーザーの追加

Shairport用のユーザーを追加する必要があるらしいのでユーザーとグループを追加。

sudo getent group shairport-sync &>/dev/null || sudo groupadd -r shairport-sync >/dev/null
sudo getent passwd shairport-sync &> /dev/null || sudo useradd -r -M -g shairport-sync -s /usr/bin/nologin -G audio shairport-sync >/dev/null

インストール

sudo make install

設定

/etc/shairport-sync.confが設定ファイル。

general = {
  name = "%H";

nameを変更するとAirPlayサーバーの表示名が変わる。お好みで変えると良いです。

USBから音声を出力する

デフォルトだと3.5mmのジャックから音声が出力されるけど音はあまりよくないのでUSBに繋いだDACから音を出すことにする。 DACを繋いでから

aplay -l

無事認識されてればcard 0以外のデバイスが表示される。

pi@raspberrypi:~ $ aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: ALSA [bcm2835 ALSA], device 0: bcm2835 ALSA [bcm2835 ALSA]
  Subdevices: 8/8
  Subdevice #0: subdevice #0
  Subdevice #1: subdevice #1
  Subdevice #2: subdevice #2
  Subdevice #3: subdevice #3
  Subdevice #4: subdevice #4
  Subdevice #5: subdevice #5
  Subdevice #6: subdevice #6
  Subdevice #7: subdevice #7
card 0: ALSA [bcm2835 ALSA], device 1: bcm2835 ALSA [bcm2835 IEC958/HDMI]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: CODEC [USB Audio CODEC], device 0: USB Audio [USB Audio]
  Subdevices: 1/1
  Subdevice #0: subdevice #0

上の例だとcard 1のSubdevice #0がUSBDAC。 このcard と Subdeviceの番号を覚えとく。

/etc/shairport-sync.confの中に

alsa = {
  output_device = "default";

とあるのでdefaultをhw:1,0と変える。数字は上で出てきたもの。

systemctl start shairport-sync

でShairport起動。

AirPlay出力先の設定

LAN内にAirPlayサーバーがある状態でコントロールセンター(名前合ってたっけ?)を開くとボリュームの下に見慣れない物がある。 設定がうまくいってればタップして出力先にAirPlayサーバーを選択できるはず。 後はテキトーに音楽を流してスピーカーから音が出れば終了です。お疲れ様でした。

ちなみにShairport "Sync"というだけあって映像と音楽の同期を取ってくれるのでYoutubeとか見てもちゃんと音ズレせずに再生される。すごい

CBR250R(MC41)のタイヤ交換

 走行距離15000kmでタイヤの溝が無くなったので交換することに。

 

選んだタイヤはIRCのRX02

 

 現行250ccクラスのメーカ指定であるRX01の一つ上のグレードのタイヤ。

前後で14000円ちょい。

 

さて、タイヤを交換するにはメンテナンススタンドっつー物を使ってタイヤを地面から浮かせる訳だけどコレが結構いい値段する上に大きくてかさばる。そこで今回は車用のパンタジャッキで前後のタイヤを交換してみた。

 

という訳で今回はメンテスタンドは買いたくないけどタイヤ交換は自分でやりたい人向けのお話。

 

 

 ↓使ったジャッキ。

【アストロプロダクツ】AP 1.5TON パンタグラフジャッキ

【アストロプロダクツ】AP 1.5TON パンタグラフジャッキ

 

 まずはフロントタイヤが動かないように輪ゴムでフロントブレーキを固定する。

次にサイドスタンドにカマボコ板くらいの厚さの何かを噛ませてバイクを水平に近づける。 

 

リアを浮かせる場合はリアサスの下の部分にウエスを噛ませてリフト。

f:id:pu2x:20160813200921j:image 

f:id:pu2x:20160813215400j:image

フロントの場合はエキパイにジャッキを噛ませる。

f:id:pu2x:20160813201230j:image

f:id:pu2x:20160813215410j:image

 

チューブレスタイヤの交換に関しては他のブログ等に山ほど情報があるので割愛。

チューブレスタイヤの交換は初めてだったけど思ったよりは簡単にできた。ただホイール外したことがない人は練習しといたほうがいいと思う。

 

ビードを落とす際タイヤレバーを使うとどうしてもホイールに傷が入るので頻繁にタイヤ交換をする人はビードブレーカーを買ったほうがいいかも。3000円くらいで買えるし。

 

ビード上げは原付にホイールを乗せて近所のガソリンスタンドで空気入れを拝借して一発。

タイヤの中に可燃性のガスを入れて爆発させることによってビードを上げる「爆発ビード上げ」なるものもあるらしい。よく思いつくなこんなの。

爆発ビード上げのやり方 ~引っ張りタイヤ~ - YouTube

シールチェーンの交換にえらく手間取った話

「チェーン交換なんて一瞬で終わるでしょー。」

 

 CBR250R(MC41)のチェーンが走行距離15000kmを超えたあたりで伸びきって波打ってしまったので交換することに。

 

選んだチェーンはEK(江沼)チェーンの520SR-X2のスクリュージョイント

 

 

 一般的に二輪車に使われているチェーンはカシメジョイントといって取り付けに専用工具が必要になるがスクリュージョイントは専用工具なしでカシメジョイントと同様の強度を発揮できる、らしい。

 

 交換にあたって最初に古いチェーンを切らなくてはいけないが、最初にピンの頭を削ってカシメてある部分を平らにしないといけないらしい。

普通はディスクグラインダーを使って一瞬で削ってしまうらしいけどそんなモン持ってないのでダイソーの棒ヤスリでひたすら削る。

カシメてある部分は穴の径より大きいのでそのままでは抜けないf:id:pu2x:20160813181021j:image

 

ピンの頭とプレートが同じくらいの高さになったらチェーンカッターでピンを抜く…が、全く抜けない

 アレコレやってるうちに3つあったチェーンカッターを全部駄目にしてしまった。

どうもピンを完全に削りきる前に何度か試しにチェーンカッターを使ったせいでピンを圧入してしまった模様。

 

何が言いたいかと言うと、

  • 棒ヤスリで削る場合はヤスリが負けてすぐ駄目になるのでたくさん用意すべき
  • 多分最初からディスクグラインダーを買ったほうが安くて楽(アマゾンで3000円くらい)
  • ピンを削りきる前にチェーンカッターを使ってはいけない

 

それにしても新品のEKチェーンは簡単に抜けたのにDIDチェーンはなんであんなに硬いのか。

 

追記

ディスクグラインダー使ったら一発でチェーン切れた。

テレビが見れるようになった

押し入れで眠ってた地デジチューナー(plex PX-S1UD)を久しぶりに引っ張り出してきてサーバーと格闘すること数十分、あっさり地デジの受信に成功。録画も問題なし。

ドライバのインストールはここを参考にした。
http://blog.lwlv.net/archives/tag/px-s1ud


自室の環境で受信できた物理チャンネルとチャンネル名はそれぞれ
16: MX
21: フジテレビ
22: TBS
23: テレ東
24: テレビ朝日
25: 日テレ
26: NHK教育
27: NHK
の8つだった。千葉テレビがギリギリ映らないのが残念。


番組表の管理・録画はChinachuに任せることにした。

リアルタイム視聴

リアルタイムで視聴するには、
PX-S1UDで使える録画コマンドのrecdvbを使って、

recdvb --b25 --strip --sid hd --http 8000

とすると8000番ポートでhttpデーモンが立ち上がる。
後は vlc http://localhost:8000/<物理CH>とするとrecdvbデーモンから配信されてくるのでリアルタイムで視聴できる。

一々コマンドを打つのは大変なのでショートカットを作成。

function channel() {DISPLAY=:0 vlc http://localhost:8000/$1 & ; sleep 5; xte -x :0 'key f'}
_channel() {
  _values \
    'channels' \
    '16[TOKYO MX]' \
    '21[フジテレビ]' \
    '22[TBS]' \
    '23[テレビ東京]' \
    '24[テレビ朝日]' \
    '25[日本テレビ]' \
    '26[NHK教育]' \
    '27[NHK]'
}

compdef _channel channel

DISPLAY=:0としているのはssh越しにコマンドを打った時にVLCCUIモードで立ち上がらないようにするため。
xte -x :0 'key f'の部分はVLCを起動した後fキーを送信してフルスクリーンになるようにしている。
_channel()はzshの補完関数。

ひとまずこれでそこそこ快適な地デジ環境が手に入った。



あとこれは夢の話だけど、BCASカードを失くしちゃったからsoftcasを導入したら問題なく見れた。夢の話。