今週末の Python温泉で、最近買った、arduino を初めて触った。 Arduino+Pythonハッキング勉強会 の 資料 を見て、何か同じようなもの作れないかと思った。
Starter Kit を買ったので、arduino だけじゃなくて、いろなものが付いてきた。そのなかの一つはピエゾバザー。 バザーの動作を確認した後に、HTTPでbuzzer を操作できたらいいんじゃないかと思って、簡単な HTTP API を作った。
サーバーは最近作った namake ベースで、arduino との通信は PySerial でシリアルUSB。音のピッチと長さ(ms)をHTTP GETパラメターで受け取って、arduino に流す。
シリアルUSBはバイト通信なので、普通な整数でも通信が結構面倒くさいです。下のサーバーで struct モジュールで int データをバイナリに変換して、通信している (pack_int).
#:coding=utf-8:
import serial
import struct
from namake import Application, Response
def force_int(v, default=None):
try:
return int(v)
except (ValueError, TypeError):
return default
def pack_int(val):
return struct.pack('I', val)
def buzz(request):
pitch = force_int(request.GET.get('p'), 1000)
duration = force_int(request.GET.get('d'), 200)
# Validate
if not (50 <= pitch <= 4000):
return Response("50 < p < 4000", status=400)
if not (0 <= duration <= 1000):
return Response("0 <= d <= 1000", status=400)
# Write pitch and duration to arduino.
request.app.ser.write(pack_int(pitch))
request.app.ser.write(pack_int(duration))
# Wait for response.
if request.app.ser.readline().strip() == "ok":
return "OK"
else:
return "NG"
class BuzzApp(Application):
def __init__(self):
super(BuzzApp, self).__init__(__name__)
self.ser = serial.Serial("/dev/ttyACM0")
self.ser.baudrate = 115200
self.ser.timeout = 1
app = BuzzApp()
app.add_route('/$', buzz)
if __name__ == '__main__':
from namake.contrib.devserver import run_devserver
run_devserver(app)
arduino の方はこの感じ。ピエゾバザーを出力PIN 8 につながっていて、非常に簡単。
arduino のコードは以下の通り。 arduino のIDEで入力するコードは基本的に c++。2つの整数の8バイト(4バイトずつ) を受け取る。arduino が呼び出している loop() で Serial.read() して、u_tag の union データの埋めこむ。
8バイト読んだら、arduinoのtone()関数を呼び出して、音を鳴らす。読み込み中の間に、組み込まれている LEDをデバグの為、電気をつく。
// digital pin 2 has a pushbutton attached to it. Give it a name:
int LED1 = 13;
int i = -1;
union u_tag {
byte b[8];
struct {
unsigned long pitch_val;
unsigned long dur_val;
};
} u;
void startRead() {
u.pitch_val = 0;
u.dur_val = 0;
i = 0;
}
void finishRead() {
i = -1;
}
// the setup routine runs once when you press reset:
void setup() {
Serial.begin(115200);
pinMode(LED1, OUTPUT);
}
// the loop routine runs over and over again forever:
void loop() {
unsigned long pitch, dur;
if (i != -1) {
if (i < sizeof (union u_tag)) {
if (Serial.available()) {
u.b[i++] = Serial.read();
}
} else {
pitch = u.pitch_val;
dur = u.dur_val;
if (pitch >= 50 && pitch <= 4000 && dur >= 0 && dur <= 1000) {
tone(8, pitch, dur);
}
finishRead();
delay(dur);
digitalWrite(LED1, LOW);
Serial.println("ok");
}
} else if (Serial.available()) {
digitalWrite(LED1, HIGH);
startRead();
}
}
これで余裕で音楽鳴らせるべ!ってもりよしさんともりあがって、もりよしさんが JavaScript からHTTPで叩きまくった。