2015年4月5日日曜日

メディアキーで操作できる小型MPDクライアント minmpc

Raspberry Pi 2 とフリスクDACでオーディオメディアサーバーを作っていい感じに音楽垂れ流し環境が出来たのはいいのだけれど、ちょっと停止したい! とか 今の曲もう一回聴きたい! とか、そんな操作が少しばかり不便なことに気付きました。

最近のキーボードによくついてるメディアキー(再生/一時停止、次の曲、前の曲 とかのボタン)でさくっと操作できればいいなと思ったのですが、気に入るものは見つけられませんでした。
欲しいのは次のような感じ。

  • Windowsで動作
  • ウィンドウがアクティブでなくてもメディアキーから操作可能
  • ウィンドウは小さくて勝手に隠れてくれる
  • プレイリストの操作等は優秀なソフトが他にあるのでなくてもいい
というわけでRxの勉強がてら作ってみました。

minmpc (github)

  • メディアキーでホットキー的に操作できて再生中の曲が表示されるだけです。
  • インストーラやビルド済みの実行ファイルは準備してません。
  • Visual Studio Community 2013 で「NuGet パッケージの復元の有効化」がONになっていればビルドできると思います。

作りはかなり粗い&荒いものなのでソースの公開に留めておきます。
決してめんどくさがってるわけじゃないですよ?_(:3 」∠)_

2015年3月28日土曜日

Raspberry Pi 2 とフリスクDACでオーディオメディアサーバー

自宅サーバーをRaspberry pi 2 にしたら、メディアサーバーにしてみたくなって実際にやってみた記録です。
Raspberry Pi 2 はHDMIケーブルや3.5mmのミニジャックから音声出力が可能です。USBでDACを接続すれば音質の向上も期待できるはず。

今回は最近ちらほら話を聞くようになったMusic Player Daemon (MPD)を使います。 MPDとは、LinuxやUNIXで動作する、オーディオファイルを再生したりプレイリストを管理したりするデーモンです。 よく使われる FLAC, MP3, MP4/AAC, Ogg Vorbis, wave 辺りのサポートは万全なので安心。 DACがサポートしていればハイレゾ音源もいけるらしいですね。そんなモノ持ってないけど。

準備

音声の出力先から実際に音が出るようにしておきます。 いきなりUSB-DACを繋ぐと上手くいかなかったときに問題の切り分けが面倒なので、まずは Raspberry Pi 2 標準搭載の音声出力で試します。
NOOBSからのインストールで特に変更していなければ、デフォルトでHDMIか3.5mmのいずれかが自動で使われるようです。
設定の変更は raspi-config > 8 Advanced Options > A9 Audio から。
テレビとかにHDMIで繋いで作業しているなら何もしなくていいです。

MPDインストール

mpd と mpc をインストールします。mpcはCLIのmpdクライアント。動作の確認に便利なのでmpdと一緒にインストールします。

$ sudo apt-get install mpd mpc

mpdの設定ファイルを書き換えます。
/etc/mpd.conf

music_directory       "/mnt/data/music"
playlist_directory    "/mnt/data/playlists"
db_file               "/mnt/data/mpd/tag_cache"
log_file              "/mnt/data/mpd/mpd.log"
sticker_file          "/mnt/data/mpd/sticker.sql"
#bind_to_address      "localhost"
music_directory のフォルダーがメディアファイルの格納場所です。mpdが読めるようにパーミッションを設定します。 playlist_directory, db_file, log_file, sticker_file のファイル/フォルダーはmpdが書き込めるようにパーミッションを設定します。 bind_to_address の行はコメントアウトすればどこからでもTCP経由でコマンドを受け付けるようになります。

とりあえず動かすだけならこの設定だけで十分。mpd.confにはもっと細かい設定があるので man page は読んでおいた方がいいと思う。

mpdを起動します。

$ sudo service mpd start

mpcでmpdにコマンドを送ってみます。

$ mpc ls
music_directory 配下のフォルダーが表示されるはず。

プレイリストに音楽ファイルを登録します。

$ mpc add pops
pops フォルダー以下のすべての音楽ファイルをプレイリストに突っ込みました。

再生!

$ mpc play
簡単!
テンションあがりますね(・∀・)

これだけで、あとはLANに繋がっているマシンにクライアントソフトを入れておけばそこから音楽再生をコントロールできるようになります。 クライアントは Gnome Music Player Client がいいのかな? Windowsなら Auremo もよさげ。Android なら MPDroid とか Droid MPD Client とか。

USB-DACで音を出したい

なんでこんなことを始めたかというと、PU-2111(いわゆるフリスクDAC)が眠っていたからです。
価格の割に良い音が出ると聞いて買ってみて暫く使っていたのですが、音楽周りの環境が変わってから使わなくなってました。

まずデバイスの認識から

とりあえずPU-2111を直接繋いでみたらOSが突然の死。そして奇妙な再起動の連続でまともに起動しなくなりました。 挙動からなんとなく電圧不足のような印象。バスパワーに頼るのはやめて電源付きのUSBハブを経由して接続したら起動しました。 Raspberry Pi の電源に使っていた古いXperiaの付属品では出力不足っぽい? ケチった結果がこれだよ。

$ less /proc/asound/cards
0 [ALSA           ]: bcm2835 - bcm2835 ALSA
                     bcm2835 ALSA
1 [DAC            ]: USB-Audio - USB AUDIO    DAC
                     BurrBrown from Texas Instruments USB AUDIO    DAC at usb-bcm2708_usb-1.4.3, ful
認識されたっぽい。
$ sudo aplay -l
card 1: DAC [USB AUDIO    DAC], device 0: USB Audio [USB Audio]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
ALSAでも見えてる。いい感じ。
あ、これは sudo しないと見れません。ちょっとハマった。

デフォルトの再生デバイスにする

モジュール名を調べます

$ cat /proc/asound/modules
0 snd_bcm2835
1 snd_usb_audio

モジュールの優先順位を設定します(optionsの2行を追記)
/etc/modprobe.d/alsa-base.conf

options snd_usb_audio index=0
options snd_bcm2835 index=1

そして再起動してみて…

$ sudo reboot

優先順位が変わってたらOKなのかな?

$ cat /proc/asound/modules
0 snd_usb_audio
1 snd_bcm2835

再生してみます。

$ mpc play
キター!(・∀・)


Linuxのサウンドデバイス周りの知識は皆無でぐーぐる先生を頼りにやっただけなので、これでいいのかちょっと不安だったり……

おまけ: PU-2111でのノイズ

一通り組み上がって配線周りを整理したらピーって高音(4242Hz)のノイズが聞こえるようになりました。それもかなり目立つ音量で。 そういえばこのPU-2111、以前使っていた環境では小さい音ですがサーってタイプのノイズが気になってました。
試しにUSBハブのACアダプタを同じ出力の別のものに変えてみたら聞こえなくなりました。 電子回路はよく分からないけど電源からのノイズを拾いやすいのかもしれません。

2015年3月24日火曜日

自宅サーバーをRaspberry Pi 2 にした

古いタワー型PCで動いていた自宅サーバーを Raspberry Pi 2 に置き換えました。
GPIOを使ってどうこうとかそういう面白そうなことはやってません。
Raspberry Pi 2 を単なる省電力小型Linuxマシンとして使っているだけです。
このエントリは通常のタワー型PCサーバーではやることが無かった類の作業の記録です。

自宅サーバーなんてイマドキ外部のサービスに頼れば殆ど不要なのですが、データ量がTB単位になるような深い業を背負った人間のファイルサーバーはちょっと困ります。そこに一ヶ月の電気代が50円未満なんて言われる小型PCがあれば惹かれていくのは人の性というものです。

用途

NASが主な用途です。sambaです。
HDDは2台接続して、それぞれメインとバックアップにします。
RAIDではありません。個人用途では常時クローンされることよりも誤操作時の復元容易性の方を重視していいと思っています。
ついでにRSSリーダーにも使っています。Tiny Tiny RSS です。

ハードウェアの準備

Raspberry Pi 2 本体の他に幾つか準備するものがあります。

  • 電源 ― ACアダプタなどは付属していません。本体側はマイクロUSB端子なので、スマートフォン用の充電ACアダプタがお手軽にそれなりのパワーを供給できてよいです。ケチな電源を使うと外部機器を繋いだときにシステムがダウンします。
  • HDMIケーブル ― 画面がないと始まりません。
  • キーボード ― USB接続の標準的なやつでいいです。セットアップが終わったら取り外してもOK。
  • マウス ― なくてもいいけど、あればNOOBSの始めにちょっとだけストレスが減ります。
  • 本体ケース ― サーバー用途なので放置することになります。基盤剥き出しではホコリとか被ってあまりよろしくなさそうです。カメラのフラッシュでも死ぬし。
  • microSD ― OSをインストールするストレージです。4GB程度だと apt-get update が失敗したりと残念な気持ちになれます。16GBぐらいあればとりあえず安心じゃないでしょうか。
準備は計画的に。

OSインストール

Raspbianにします。
Debianをベースにした Raspberry Pi 用のOSで、Debian や Ubuntu をよく使っている人ならあまり悩まず使えそうです。
インストールにはNOOBSを使って手を抜きます。
ダウンロードしたファイルをmicroSDに書き込んで電源ケーブルを差し込むだけなので超楽ちん。
NOOBSでのRaspbianインストール方法については既に多くの方が記事にしているので省略。

途中でマウスじゃないと選択しづらいボタンがあった気がします。マウスを使ったのはここだけ。

インストール中に日本語が化けて幾つかの項目を選ぶのが大変でした。
インストール後も sudo raspi-config で同じ設定画面を開けるので後回しにしてもいいです。

インストールが終わったらとりあえずOSのバージョン確認。

$ uname -a
Linux raspinas 3.18.7-v7+

$ cat /etc/debian_version
7.8

HDD接続

HDDは駆動部がある精密機械で、貴重なデータが詰まっています。裸で放置するのはちょっと躊躇われます。
はじめは段ボールで適当にHDDケースを作ったのですが、それなりに発熱する機械なので恐くなってやめました。
USB接続できるHDDケースに入れ、それを設置場所にしっかり固定。地震が来ても安心。
また電源のACアダプタが増え、ACアダプタが隣のコンセントを塞ぐ問題が予想されます。そのための短い延長コードがあると安心。

HDDを接続できたら、
マウントして、
パーミッションを設定して、
fstabで再起動時も自動でマウントされるようにして、
sambaを適当に設定してWindows機からの読み書きを確認し、
cron に rsync を仕込んで定期的にバックアップを取るようにして、
NAS完成。

SDカードの寿命を延ばしたい

このままだとログやスワップに使うストレージはSDカードなので、きっと1年ぐらいサーバーとして運用したら死ぬんじゃないかと思います。
予備のSDカードを用意して壊れたら入れ替えればいいじゃないという発想もありますが、出来ることなら寿命は延ばしたいですよね。
とりあえず2つの対策を実施します。

  • swapを無効に ― メモリが1GBもあるので無茶しなければswapなしでも暮らしていけます。
  • logをHDDに ― HDDは常時接続の前提なので。/var/log にHDDをマウントするだけです。一部書かれないログがありそうだけどまぁいいでしょ。
私はやってないですがRAMディスクを使うのもありだと思います。

swapを無効にする

まずは現状確認。

$ free -h
             total       used       free     shared    buffers     cached
Mem:          927M       916M        10M         0B        67M       758M
-/+ buffers/cache:        90M       837M
Swap:          99M         0B        99M
使われていないけど100MBぐらい予約されてる。
無効にしよう。
$ sudo swapoff --all
$ free -h
             total       used       free     shared    buffers     cached
Mem:          927M       914M        12M         0B        68M       755M
-/+ buffers/cache:        90M       836M
Swap:           0B         0B         0B
swapはいなくなったけど再起動したらきっと復活してくるので...
$ sudo apt-get remove dphys-swapfile
$ sudo reboot
再起動後
$ free -h
             total       used       free     shared    buffers     cached
Mem:          927M       652M       274M         0B        18M       574M
-/+ buffers/cache:        59M       867M
Swap:           0B         0B         0B
OK. swapいなくなってる。

他にも

/tmp をRAMディスクに逃がす とか
LAMPやLAPP環境を組むならデータベースファイルをHDDに置く とか
そんな風に書き込み頻度の高い部分を待避させていくのがよさそうです。
でもパフォーマンスは犠牲になります。
ご利用は計画的に。

2013年5月13日月曜日

アクリルLEDパネルお試し

とあるイベントでアクリルLEDパネルの展示&作成をやってたのを見て、自分もやってみたくなったので挑戦してみました。

とりあえず雰囲気を掴むため、出来るだけあり合わせの道具で。
アクリル板だけ東急ハンズで買ってきました。
  • 削る道具: ダイソールーター
  • 光る仕組み: Arduino + フルカラーLED (OSTA5131A)
  • アクリル板: 透明 2mm厚
  • パネルの固定: 洗濯バサミ:)
パネルの作成はシンプルに
  1. 紙に絵を下書き
  2. アクリル板を紙に乗せて下絵をペンでトレース
  3. アクリル板に書いた線をなぞってルーターで軽く削る
削る作業だけは集中力が要求されますが、あとは適当。

完成したパネルをLEDの上に垂直に立てて置いただけですが、ちゃんとLEDパネルっぽくなりました。
LEDひとつだと光量が足りないかも?とか、ちゃんと絵が光るのかな?とか不安でしたが杞憂だったみたい。

お手軽でなおかつ面白い。
道具を揃えてパネル台も含めてきちんと作るのもいいかなー。

シンプルな絵ということで、Minecraftで人気のCreeperさんにご登場願いました:)

2013年1月14日月曜日

one-liner時報

終日一人で自宅に引き篭っていると、時間の感覚が無くなってアレな感じです。
最近、職場で時報が鳴るようになったら時間が分かりやすくてよかったので、自宅でも導入してみました。

その辺で公開されているソフトを使ってもいいのですが、折角なので自前で。

サウンドの再生自体は.NETでお手軽にできるので、PowerShellから一発。
(New-Object Media.SoundPlayer("C:\time-signal\signal.wav")).PlaySync()
これをタスクスケジューラで毎時59分56秒とかに実行するよう登録すると、一応時報になります。

ですがこのままではタスク実行時にコマンドプロンプトウィンドウが表示されてちょっと残念。

こういうときはWshShellオブジェクトからRun。
第2引数IntWindowStyleを0にすればウィンドウは表示されません。

こんな.vbsファイルをタスクスケジューラに登録すれば目的達成。
CreateObject("WScript.Shell").Run "powershell (New-Object Media.SoundPlayer(\""C:\time-signal\signal.wav\"")).PlaySync()",0
one-linerといいつつ .vbsファイルをタスクスケジューラから呼ぶという邪道な感じになってしまいました。

サウンドデータは好きなものを.WAV形式で調達すれば良いです。

2013年1月4日金曜日

P/Invokeの規定の呼び出し規約

自前のunmanagedなdllを、.NETなアプリケーションからP/Invokeで使おうとした際に  pInvokeStackImbalance で躓いたメモです。
環境は Visual Studio 2010 / .NET Framework 4.0

unmanaged DLL 側はC++でこんな感じ。
extern "C" {
 __declspec(dllexport) bool
 ReadValue(wchar_t* buffer, unsigned int bufferLength) {
  wcscpy_s(buffer, bufferLength, L"hogehoge");
  return true;
 }
}
それをこんなC#コードで呼び出すと、デバッグ実行時に pInvokeStackImbalance で怒られます。
[DllImport("foo.dll", CharSet = CharSet.Unicode)]
public static extern bool ReadValue(StringBuilder buffer, uint bufferLength);
MSDN: DllImportAttribute.CallingConvention フィールド によると
DllImportAttribute.CallingConvention の規定値は Winapi。
これはプラットフォーム標準の呼び出し規約を使用することを示す。Windows なら StdCall。
よくP/InvokeされるWin32APIの場合はすべてStdCallなので規定値でOKですね。

というわけで今回は次ように呼び出し規約をきちんと指定してあげないといけない。
[DllImport("foo.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern bool ReadValue(
    StringBuilder buffer, uint bufferLength);
ちなみにこのエラーを検出している MDA (Managed Debugging Assistant) は、.NET Framework 3.5 では規定値で無効になっているらしいです。

2012年8月26日日曜日

AndroidとPC(Winamp)の楽曲同期

AndroidとPC(Winamp)の楽曲プレイリスト同期に手こずった記録です。

前提

  • 自宅では楽曲ファイルの再生とプレイリスト管理をWindows PC上のWinampで行う
  • 楽曲ファイルはすべてUbuntu PCに保存されていて、Windows PCからはsamba経由でアクセス
  • 外出時にはAndroid (NEC N-01D) で音楽を聴く

やりたいこと

  • Winampの任意のプレイリストのファイルをAndroidで同期・聴けるようにしたい
  • 対象のプレイリストは時々更新するので出来る限り手軽に同期したい
  • USBケーブルでPCとAndroid機を繋ぐのはめんどくさいのでWi-Fiで済ませたい

まずはWinampの世界で


Android版Winampを使えばWi-Fi経由で任意のプレイリストを同期できるらしい! 素敵!

  • PCのWinampからAndroidデバイスが見つからない
    1. とりあえずUSBで繋いでみると当然見つかります
    2. 無線LAN接続のノートPCのWinampからもOK(・∀・
    3. 有線LANで接続されたPCから無線LAN接続のAndroid機は見つけられない……?
    4. ルーターが無線LANポートへのマルチキャストパケットの転送を制限していたので解除
    5. 同期できた!(・∀・
  • 文字化け
    • 同期できた楽曲をAndroidで見るといくつか文字化けが見つかる
    • Webで検索したら、ID3タグがUnicode系でないとAndroidでは文字化けするという情報多数
    • ID3Uniで全部Unicode化
    • だいぶ改善されました。が、まだ文字化けしている楽曲が残っています…
  • ファイル名が文字化け or 失われる
    • ファイル名が失われていた
    • Winampは転送時にファイル名の変更をしている?
    • ダメなファイル名の特徴を調べればわかるかもしれませんがここで気力が尽きました
  • Wi-Fiでの転送が不安定
    • 同期中に途中で通信が切れる
    • 症状は、Android側のWinampがクラッシュするか、Wi-Fi接続が切れる
    • どちらが発生するかは不定

…Winampの世界で完結させるのは諦めることにしました。


次の戦略 - rsync backup for android で同期

  1. WinampからプレイリストをM3U形式で出力
    • Winampのプレイリストビューで保存(Ctrl+S)するだけです
  2. Ubuntu PCでM3Uを解釈して必要な楽曲ファイルのシンボリックリンクを同期用フォルダに作る
    • 同期用フォルダには、楽曲フォルダのツリーをそのままコピーして、必要なファイルのみシンボリックリンクを作ります
    • cronで固定のパスのm3uを読み、実行
  3. rsync backup for android で同期
    • シンボリックリンクを解決してコピーする設定

M3Uを解釈して同期用フォルダを構築
  • Winampが吐き出すM3Uは次の通りということにしています。
    • Shift-JIS (CP932)
    • '#' から始まる行以外がファイルのリスト
  • 波ダッシュ問題
  • ファイルパスと正規表現
    • '/' で囲むとわけわかんなくなります。
    • '|' がいいと思います。
#!/bin/bash

if [ ! -f "$1" ]; then
  echo "no playlist is specified."
  exit
fi

WINDIRARR=(S:/sound/ T:/sound2/)
SRCDIRARR=(/home/library/sound1/ /home/library/sound2/)
OUTDIRARR=(/home/users/android-rsync/data/1/ /home/users/android-rsync/data/2/)
TMPFILE=$1.tmp

for (( i = 0; i < ${#OUTDIRARR[*]}; i++ )); do
  find ${OUTDIRARR[$i]} -type l -exec rm {} \;
done

sed -e 's|\\|\/|g' $1 > $TMPFILE

while read LINE; do
  LINE=`echo $LINE | nkf --ic=CP932 --oc=UTF-8 | sed 's|\\s*$||g'`
  
  if test 0 -ne ${#LINE} && ! echo $LINE | grep -sq '^#'; then
    RELPATH=$LINE
    for (( i = 0; i < ${#WINDIRARR[*]}; i++ )); do
      WINDIR=${WINDIRARR[$i]}
      SRCDIR=${SRCDIRARR[$i]}
      OUTDIR=${OUTDIRARR[$i]}
      RELPATH=`echo "$LINE" | perl -pe "s|$WINDIR||i"`
      if [ "$LINE" != "$RELPATH" ]; then break; fi
    done
    
    if [ "$LINE" != "$RELPATH" ]; then
      RELDIR=${RELPATH%/*}
      if [ ! -d "$OUTDIR$RELDIR" ]; then mkdir -p "$OUTDIR$RELDIR"; fi
      ln -s "$SRCDIR$RELPATH" "$OUTDIR$RELPATH"
    else
      echo "ERROR: $RELPATH"
    fi
  fi
done < $TMPFILE

rsync backup for android
  • Ubuntu側にコレ専用のユーザを作り、鍵を登録します
    •  鍵は rsync backup for android で作ったものを使います
  • Additional rsync options
    • 同期方向は Ubuntu→Android への一方通行になるように設定
    • シンボリックリンクを解決してコピー
      • -l    copy symlinks as symlinks    ←初期状態ではこちら
      • -L    transform symlink into referent file/dir    ←これに変更
Rescan Media
  • Androidはメディアストレージサービスというプロセスでプレイリストを管理しています。
  • 通常はOS起動時やストレージのマウント時にスキャンしてプレイリストを構築するらしいです。
  • なので rsync で引いてきただけでは楽曲はプレイリストに入りません。
  • メディアストレージサービスのスキャンを手軽に手動実行できるのがこのアプリ。
  • 初回起動時は "Auto quit?" が off になっています。チェックをONにしたあと手動でアプリケーションを明示的に終了させないと、2度目以降rescanされていないようでした。
  • アプリを起動したタイミングでrescanが実行されるのかな?

他の戦略 - FUSE


シンボリックリンクではなく Filesystem in Userspace(FUSE) がいいのでは? というアドバイスも貰いました。FUSEというものは知らなかったのですが面白そうです。

2012年8月22日水曜日

エアコンのWeb UIを作った

エアコンのリモコンとか作った そのご。

Arduino IDE のシリアルモニタからしか操作出来ないのはつまんないのでUIを作ったの巻。
  1. Arduinoに赤外線LEDと温度センサーを繋げて
  2. Ubuntuに接続して
  3. PHPでWebインターフェイスを作った
という話です。

ハードウェア


  • Arduino UNO R3
  • 赤外線LED : OSI5LA5113A
  • 温度センサー : LM61CIZ
  • UbuntuがインストールされたPC

前回まではWindowsで作業していたのですが、WebサーバはUbuntuで動いているため今回はそちらで。
温度センサーの LM61CIZ は、-30℃ ~ +100℃ を 300mV ~ 1600mV で出力します。

スケッチ


温度を常時出力しながらシリアルの入力 (リモコン信号送信指示) を待ちます。
void loop() {
  digitalWrite(PIN_IR_OUT, LOW);
 
  if (Serial.available()) {
    int c = Serial.read();
    for (int i = 0; i < signalMap[i].chr != '\0'; i++) {
      if (signalMap[i].chr == c) {
        sendSignal(signalMap[i].signal);
        Serial.println("send");
        break;
      }
    }
  } else {
    // analog input value
    float aIn = analogRead(PIN_ANALOG_IN);
   
    // input voltage (mV)
    float vIn = (5000 * aIn) / 1024;
   
    // temperature (C)
    float t = (vIn - 600) / 10;
   
    Serial.println(t);
  }
 
  delay(1000);
}

ArduinoをUbuntuに接続


ドライバとかは不要です。USBケーブルを挿すだけなのでお手軽。
screen /dev/ttyACM0 9600
でシリアル通信を確認。

PHPでWebインターフェイスを作る


はじめに作ったものはこんな感じでした。

やっつけ感が溢れる

機能は、温度を表示し、エアコン ON/OFF の指示を受け付けるだけです。

シリアル通信には php-serial という ライブラリを使いました。
通常 Apache のユーザは /dev/ttyACM0 などへはアクセス出来ないと思います。
サーバの運用ポリシーに合わせて対処を考える話ですね。

さて、これでやってみたかったことは一通り出来ました。
帰宅3分前にスマートフォンで室温を見て「うわー;´д`) 」って思ったらエアコンをつけておく、なんてことも可能です!……が、スマートフォンで見てみると悲惨なことに (左上に小さく表示) なったので jQuery Mobile を使ってみました。
いい感じ

2012年8月20日月曜日

Arduinoから赤外線信号を送信

エアコンのリモコンとか作った そのよん。

ひとつ前のエントリで読み取ったリモコンの信号を、自前で赤外線LEDをコントロールして発信してみます。
受信のときとは異なり、キャリア周波数の変調 (38kHzで点滅させる処理) を自分でプログラムする必要があります。
…受信はセンサーが高機能なお陰で楽できました。

赤外線LEDには OSI5LA5113A を使いました。

送信ビットデータの生成


受信のときの逆処理を行うだけなので詳細は割愛。

信号データはArduino内に持つことにします。
#define SIGNAL_LEN 7

typedef struct _SignalMap {
  char chr;
  byte signal[SIGNAL_LEN];
} SignalMap;

SignalMap signalMap[] = {
  // 電源ON 冷房 風量自動 28℃
  {'C', { 0x28, 0x61, 0x3d, 0x10, 0xef, 0xbc, 0x43 } },
  // 電源OFF 冷房 風量自動 28℃
  {'c', { 0x28, 0x61, 0x3d, 0x10, 0xef, 0xac, 0x53 } },
  // terminator
  {'\0', { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } }
};
シリアルへ送られてきた文字に反応して、対応する信号を発信する仕様です。

キャリア周波数の変調


38kHzで点滅ということは、1周期 26.32マイクロ秒。
そんな厳密ではないと思うのですが、ON/OFFが半々だとしてもそれぞれ13マイクロ秒程度ということです。
unsigned long endTime = micros() + highTimeSpan;
while (endTime > micros()) {
  digitalWrite(PIN_IR_OUT, HIGH);
  delayMicroseconds(9);  // ※a
  digitalWrite(PIN_IR_OUT, LOW);
  delayMicroseconds(6);  // ※b
}
delayMicroseconds(lowTimeSpan);
待ち時間 (delayMicroseconds) は手探り。
各命令がどれぐらいの時間を使うのか調べるのが正しい方法かもしれません。。。
とりあえずこれでうちのエアコンは受け付けてくれました。

なお事前に友人が作った赤外線送信のスケッチを見せてもらっていたのですが、それと同じ時間(上記の a=9, b=7)ではうちのエアコンは反応しませんでした。ここらへん機器によって微妙に差がありそうです。

OSI5LA5113Aの指向性


かなり狭い角度にしか信号が届きません。
データシートを見ると ±3°ぐらい。体感でも 10°ずらすとダメな感じ。
これに気付かずハマったorz

テスト


このままでは実際に発信している信号が元のリモコンと同じものなのか、確認する手段がありません。このまま手探りのテストははっきり言って現実的ではありません。ここまでそれに気付かなかった私って。。。
どうしようもないのでもう一つ Arduino を買って、前回のエントリと同じ受信モジュールを組んでしまいました。

完成


シリアルモニタから 'C' を入力したらエアコンがぴぴっと動き出す!
エアコン斜め横に配置。奥側はつけっぱだった温度センサー

2012年8月19日日曜日

Arduinoで赤外線信号を読取

エアコンのリモコンとか作った そのさん。

室温がわかるようになってもそこから発展しないのはつまんないし、室温をコントロール出来れば面白いなと思ったわけです。
つまり、エアコン操作。

友人がArduinoと赤外線LEDでリモコンを自作しているのを見ていたので、その組み合わせでどうにかなりそうなことは分かっていました。
しかし肝心の送信する信号を知る方法がありません。
PC-OP-RS1 (パソコン用学習リモコン) みたいなので調べればいいじゃないかという意見もありますが、安いものではないしどうせなら自作してみたほうが面白いかな、と。

まず赤外線リモコンの信号とはどんなものなのか検索。
分かりやすく解説してくれているサイトがあったので、この知識を前提に進めます。

ハードウェア

  • Arduino UNO R3
  • PL-IRM-2161-C438
    • 安価で入手可能なセンサーですが、これがなかなか高機能。赤外線リモコン特有の38kHzで点滅する赤外線の有無を出力してくれます。この手の工作では定番ぽい?

赤外線センサーの Vout を、Arduinoのデジタルピンに繋ぎます。残りの電源とGNDも適当に。

ソフトウェア


自宅のエアコンのリモコン信号がどのフォーマットなのかを知るため、赤外線信号のON/OFF時間を取得します。

sketchはGistで公開
Raw IR-signal receiver for Arduino with PL-IRM-2161-C438 — Gist

結果はちょっと見づらいですが、信号のON/OFFが切り替わったタイミングがマイクロ秒単位で取得できます。
リーダー信号の時間から、おそらく家製協フォーマットだろうと推測。

フォーマットが分かれば16進表記にするのは難しくないですね。

IR-signal receiver for Arduino with PL-IRM-2161-C438 — Gist


というわけでリモコン信号の値を受け取ることが出来ました。
ビット順序が昇順か降順かだとかは機器ごとに変わると思いますが、大概の機器はこんな感じでいけそうです。

本当にこれで正しいのかどうかは実際に信号送信してみなければ分からないのですが、それはまた後ほど。

キーボード G510 のLCDアプレット作成

エアコンのリモコンとか作った そのに。

Arduinoが検知した温度をどこに表示させようかと考えて思い立ったのが、Logicool Gaming Keyboard G510 のLCD。

軽く調べてみて分かったことは
  • Logitech/Logicool の G-Series と呼ばれる機器の幾つかはLCD(と幾つかのボタンのセット)を搭載しており、このLCDはWindows上で動くAppletと呼ばれるソフトウェアから制御する。
  • Applet は G-Series のユーティリティソフトウェアから制御する。
  • G-Series のユーティリティソフトウェアのインストール先にある LCDSDK フォルダ内に、AppletのSDKがある

なんだかいい感じです。

Applet開発はどうやるのか


取り敢えずSDKを覗いてみると、プログラミングガイドライン、APIリファレンス、サンプルコードなど情報は揃っていました。開発言語はC/C++, ビルド環境は Visual Studio でいいみたい。

SDKは大きく二つに分かれています。
  • LCDSDK
    • テキスト表示、ビットマップ表示、プログレスバーなど、便利なライブラリが用意されたセット
  • LCDSDKCore
    • LCDとの接続、LCD横のボタン操作イベント、LCDのドット単位操作など、必要最小限のAPIセット

ぱっと見で単純そうだったので LCDSDKCore の方を使うことにしました。

SDKに含まれるサンプル ColorAndMono を参考にします。
このサンプルは、接続されているLCDがカラーかモノクロかを判断して、ランダムなノイズのような画像をLCDに表示するだけのものですが、LCDの接続/切断、LCD横についているボタンのイベント、その他各種イベントについても一通り書かれているので参考にするにはうってつけ。
ソースを見れば分かりますが、WndProcに処理を実装していくタイプの標準的なWin32アプリケーションに、LCD用の初期化処理とイベント処理が追加されたようなものです。

まず、私が持っている G-Series 機器は G510 だけなので、カラー判別処理とカラーLCD用の処理はバッサリ無視。
G510のLCDは、横160 × 縦43 ドット、単色です。


LCDの表示更新処理には lgLcdBitmap160x43x1 というスゴい名前の構造体を使います。
メンバは2つ。
  • hdr … ビットマップのフォーマットを指定。
  • pixels … 表示させる画像データ。unsigned char の配列。
pixels の各Byteがドットを表し、先頭ビットが 1 だと対応するLCDのドットが明るくなる、といった感じ。lgLcdUpdateBitmap 関数に hdr のアドレスを渡すと、ヘッダの後ろに続く pixels が読まれてLCDに反映されるみたいです。

ここまでわかればAppletを作る最低限の情報は揃っています。

製作

こんなアプリケーションを作ることにしました。 

  • G-Seriesのユーティリティソフト起動時に自動起動
  • 1秒ごとにLCDを更新する (WM_TIMER 使用)
  • USBシリアルポートへArduinoが送り続けてくる温度情報を読み取る
  • 温度情報をLCDに表示
  • 日時も表示
  • 余ったスペースに何か絵を表示できればいいな


LCDのための定型処理は ColorAndMono を参考に必要な処理を実装。
ColorAndMonoサンプルのソースを読めば分かることばかりなのでここでは割愛。

絵はモノクロ2値ビットマップを読み込みます。

文字描画関数なんてものは用意されていないのでそこは何とかする必要があります。
素直に LCDSDK を使うか、別途DIBに描いてコピーするか、自前でビットマップフォントを作るか、まぁどれでもいけると思います。
今回は絵の表示処理が使い回せるのでビットマップフォントを作りました。
日時と温度を表示するだけなので数字+αがあればいいのです。


ちょっとハマったところ その1

Windowsでは fopen("COM3", "r") とかやってもシリアルポートからの入力を読めないみたいです。
この制限を書いた正式なドキュメントは見つからなかったのだけれど、とりあえずダメなものはダメなのだと諦めて Win32 API から読むことにしました。

HANDLE hCom = CreateFile(
    port,            // シリアルポート。"COM3" みたいな文字列
    GENERIC_READ | GENERIC_WRITE,
    0,
    NULL,
    OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL,
    NULL
);

if (hCom == INVALID_HANDLE_VALUE) {
    // このへんでエラー処理
    return -1;
}

DCB dcb;
GetCommState(hCom, &dcb);

dcb.BaudRate = 9600;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;

SetCommState(hCom, &dcb);

// あとは ReadFile でごにょごにょ。

ちょっとハマったところ その2

2値ビットマップのピクセルデータは 1bit = 1dot なのですが、横ライン1行が 32bit 境界で揃っています。

こちらの説明が分かりやすかったです。
BMP ファイルフォーマット

MSDNには 16bit 境界みたいに書いてあるけど違うと思うの。。。

こんなの出来ました

LCDでネギを振るミクさん

LCDでネギを振るミクさん

Arduinoで温度検知

エアコンのリモコンとか作った そのいち。

まずはこちらの記事を参考にさせて頂きました。
Arduinoで温度計(LM35DZ) - 思い出はいつまでも

使ったもの

  • Arduino UNO R3
  • LM35DZ
    • マルツパーツ館で購入したのですが、センサーではなくオペアンプのエリアに置いてあって探し回ったり。
  • 作業PCは Windows 7 (x64) 

やったこと


まずハード側。

LM35の3つのピンがそれぞれ何なのか調べます。データシートはこういうパーツを販売している店がWEBで公開してくれていて、すぐに見つかりました。

+Vs を Arduino の 5V へ
Vout を Arduino の A0 へ
GND を Arduino の GND へ
それぞれ繋ぎます。

ハードは以上。 これだけ。

次にArduinoのソフトウェア一式をダウンロード。IDEやドライバが含まれます。
 Arduino - Software

バージョンは 1.0.1 でした。

作業PCにドライバをインストール。
  1. USBケーブルを接続するとドライバのインストールに失敗と表示される
    • デバイス マネージャで見ると「不明なデバイス」
  2. 手動でドライバをインストールすることにする
    • 「不明なデバイス」のドライバの更新
    • "drivers\Arduino UNO REV3.inf" を選択
  3. デバイス マネージャの "ポート(COMとLPT)" に "Arduino UNO R3 (COM3)" みたいなのが追加されました

Arduino IDE の設定は次の2点。
  • マイコンボードの選択で Arduino UNO
  • シリアルポートを選択


スケッチはとりあえず上記の参考記事からそのまんまコピペ。

IDEの「マイコンボードに書き込む」をクリックすると、コンパイルしてArduinoに書き込まれ、Arduino付属のシリアルモニタで温度の表示ができました。



Arduino お手軽すぎる!
知識無しでもこんなことが数千円で出来てしまうなんていい時代です。

2012年8月18日土曜日

エアコンのリモコンとか作った

元々はPC周りの温度を常時確認したいと思って弄り始めたArduino。
モバイル環境から室温確認&エアコンを操作が出来るようになったので一区切り。
しかし節電が叫ばれるこのご時世、全力で活躍するような機会は少なそうです。

いろいろと手を出したので途中でやったことなどを少しずつ書き起こしてみます。

やったことは大まかにいうと
電子工作経験ゼロ、電気回路の知識は中学校レベルの人間がやったことなのでおかしなところが多いかもしれません。

Blogger

日々やったことを纏めて書き残したいときに書いていきます。
月一回ぐらいのゆるい継続を目指して。