qnote Advent Calendar 9日目ですな。
順調に進んでおりますなぁー
年末の休み、楽しみですなぁー
よーし、んではマイペースに前回の続きいっちゃいましょうか!
前編はこちら
#今回の目的
メッセージカードの基板からArduinoで信号を読み取って、データ化できるか試してみます。
#準備するもの
- 前編でつくった(壊した)メッセージカード基板
- Arduino
- お手元のPC
#Arduinoについて
ご存知の方も多いかと思いますが、いちおー説明をば。
Arduinoは、数年前に流行り始めたマイコン(マイクロコンピュータ)の開発ボードです。
IoTはハードウェア(ボタンとかセンサーとか)と連携する必要があるわけですが、ハードウェアをうまく制御するためにはマイコンについて学ぶ必要が出てきます。
Arduinoが出るまでは、マイコンってプログラム書き込み機器の入手やC言語の習得とか、結構ハードルが高かったらしいです。
(一応IDEとかはあったみたいですけども)
そんなところに颯爽と登場したArduinoの特徴は
- Windows/Mac/Linux上で、専用のIDE環境でプログラミング出来る
- 言語はProcessing(C書くより格段に楽!)
- 書き込み機器が要らない(PCからUSB経由で直接書き込める)
- ビルド&書き込みはボタン押すだけ
- センサーからの入力値読み取りが簡単
- PCとの情報のやり取りもカンタン(読み取った値をPCに送ったりとか)
- USB電源繋いどけば単体で動く
などなど。
今からIoTを始めるのであれば、使わない手は無いといっても過言ではありません。
(ArduinoとRaspberry Piとの組み合わせもオススメ。C.H.I.P.も技適さえ通ればなぁ・・・とボヤいてみる)
#Arduinoの入手について
Arduinoには色々モデルがありまして、Arduino Unoってやつが一番基本的なモデルらしいです。
私もとりあえずUnoを購入しました。
こいつは公式で3000円くらいですが、Arduinoはハードウェアの仕様が公開されているので、もっとお安い互換品が出回っています。
Unoのモデルですと、Amazonでは最安450円くらいからという衝撃価格。
ただ、中国発送のモノは到着が恐ろしく遅いことがあるようなので、1台目はややお値段高くてもすぐ届くヤツ(大抵1000円程度)がオススメ。
互換品はクセがあったり品質がアレなものも多いようなので、Amazonで注文する場合はレビューに目を通してから自己責任で。w
(めんどくさければ、公式のArduino買っちゃいましょー
#Arduinoに繋いでみる
では、基板をメッセージカードに繋いでみます。
Arduinoには電圧を読み取れるAnalog Inが付いていますので、プラス側の端子をそこにプスッと繋げます。
A0でいいかな。
UnoのAnalogReadは5Vまでの電圧を0〜1023の値として読み取り可能です。
(大抵のArduinoは5Vっぽいですが、最大3.3Vのヤツもあるそうです
今回はボタン電池1個なので直で繋ぎましたが、最大電圧超える入力するとArduinoぶっ壊れますのでご注意をば。
#信号を取り出すプログラムを書いてみる
次はArduino IDEとProcessingでプログラム書きます。
DL・セットアップ・Arduinoへの書き込み方法は公式ページとか参照してくださいまし。
https://www.arduino.cc/
https://processing.org/
どうやって音声信号を取り出すか素人の頭で考えてみた結果、音声ファイルとか44.1kHzだったりするので、1秒間に44100回リード出来れば音声信号に出来るんじゃない?と推測してみた。(適当なので、コレが合ってるか間違ってるかは皆様ご判断ください)
ArduinoのコアであるATmega328Pとやらは動作クロックが20MHzらしいので、処理シンプルにすれば上手く回るんじゃないかなぁ?と。
Arduinoはloop処理はあるものの、定期実行はライブラリを入れないと出来ないみたい。
ってことで、処理を一定の間隔で実行するためにTimer1ってライブラリを入れました。
http://playground.arduino.cc/Code/Timer1
ライブラリのzipをダウンロードして、Arduinoのスケッチ→ライブラリをインクルード→.ZIP形式のライブラリをインクルード で取り込みOK。
あとは#includeで使うだけ。
最終的に、Arduino側のプログラムはこんな感じ。
サンプリングレートを44.1kHzじゃなくて8kHzにしているのは、シリアル通信の速度に限界があって、上手く音を取れなかったからです。w
#include <math.h>
#include <TimerOne.h>
const int PIN = 0;
void setup() {
// A0を入力モードに
pinMode(PIN, INPUT);
// Timer1はmicrotimeで指定可能なので、1秒を8kHzで割った値で定期実行。誤差はご愛嬌ってことで。
long time = 1000000 / 8000;
Timer1.initialize(time);
Timer1.attachInterrupt(readMelody);
// 転送速度MAXでシリアル通信開始
Serial.begin(115200);
}
void loop() {
// ループは何もしない
}
void readMelody() {
// デバッグ用。改行付きの文字列として出力
Serial.println(analogRead(PIN));
}
#Arduinoに書き込んでみる
こいつを書き込むのは、IDEのボタン押すだけ。ポチッとな。
IDEについてるシリアルモニタを起動すると、値はとれてるみたい。
(ちなみに、Macだとシリアルモニタ表示するとすぐフリーズする。Windowsだと平気だったのに・・・
普段は268で、メロディーの再生ボタン押すと0が混ざってくる。
中間値が無いので、0か1かのパルス的な信号っぽい。
ちなみに、1023が5Vってことは、268だと1.3Vくらい。
1.5Vのボタン電池だから、大体計算は合いますな。
#Processing
Processingで↑のデータを受け取って、WAVにしてみます。
まずはArduino側のプログラムを一部変更。
Serial.printlnだと文字列になっちゃうので、writeに。
void readMelody() {
// 値を渡す
Serial.write(analogRead(PIN));
}
で、Processing側はこんな感じ。
import processing.serial.*;
final int SAMPLE_RATE = 8000;
final int SIZE = SAMPLE_RATE * 15 + 48;
final String PORT = "/dev/cu.usbmodem1421";
final String FILE = "/Users/kako/Desktop/test.wav";
Serial serial;
int val;
byte[] data;
int index = 48;
boolean isExporting = false;
boolean isMelodyStarted = false;
void setup() {
println("Set PORT in below.");
printArray(Serial.list());
println("");
serial = new Serial(this, PORT, 115200);
data = new byte[SIZE];
}
void draw() {
}
void set4bytes(byte[] ary, int idx, int val) {
ary[idx ] = (byte)(val % 256);
ary[idx+1] = (byte)(val / 0x100 % 256);
ary[idx+2] = (byte)(val / 0x10000 % 256);
ary[idx+3] = (byte)(val / 0x1000000);
}
void set2bytes(byte[] ary, int idx, int val) {
ary[idx ] = (byte)(val % 256);
ary[idx+1] = (byte)(val / 0x100 % 256);
}
void exportIfNeeded() {
if (isExporting) {
return;
}
println("Export start.");
data[0] = 'R';
data[1] = 'I';
data[2] = 'F';
data[3] = 'F';
set4bytes(data, 4, SIZE - 8);
data[8] = 'W';
data[9] = 'A';
data[10] = 'V';
data[11] = 'E';
data[12] = 'f';
data[13] = 'm';
data[14] = 't';
data[15] = ' ';
set4bytes(data, 16, 16);
set2bytes(data, 20, 1);
set2bytes(data, 22, 1);
set4bytes(data, 24, SAMPLE_RATE);
set4bytes(data, 28, SAMPLE_RATE);
set2bytes(data, 32, 1);
set2bytes(data, 34, 8);
data[36] = 'd';
data[37] = 'a';
data[38] = 't';
data[39] = 'a';
set4bytes(data, 40, SIZE - 126);
saveBytes(FILE, data);
println("Export finished.");
exit();
}
void serialEvent(Serial p) {
int val = p.read();
if (!isMelodyStarted && val == 0) {
isMelodyStarted = true;
println("Recording start.");
}
if (!isMelodyStarted) {
return;
}
if (index < SIZE) {
data[index] = (byte)(val * 255 / 307);
index++;
} else if (!isExporting) {
exportIfNeeded();
}
}
お手元で試す場合は、
- PORTの指定を相応しいものに変更(Arduino IDEでArduinoのCOMポート見れるので、それに)
- メロディー開始のトリガを調整(手元の基板はメロディー中にしかAnalogRead 0が返ってこなかったのでそれをトリガにしているが、逆のパターンもあるかも)
あたりを調整してチャレンジしてみてください。
#録音できた音を聞いてみる
手元で録音した音源は↓こちら。
http://ijoru.com/arduino_melody_exported.wav
8kHzなので音質は酷いもんですが、Arduinoで電気信号取り出したって考えると味わい深いもんです。
#まとめ
プログラマーでもIoTは出来る!
出来るぞ!!