OSC(Open Sound Control)とは,送信者(クライアント)から受信者(サーバ)へ,つぎのようなデータを送るための仕組みです.ひとつの通信データは,アドレスパターン(たとえば /kozPhone/uniVRtutorial2/message)とそれに続く引数の列(たとえば 320 150 "Game Over")からなります.アドレスパターンによって,ディレクトリ階層のように,データの意味づけを表示します.引数としては,整数(int32)・実数(float)・文字列などが使えます.
- /esp1/acc/x 5.67
- /esp1/acc3 5.67 2.11 -0.95
- /esp1/mess 320 150 "Game Over"
もともとは電子楽器のコンピュータ制御のためにつくられましたが,今ではさまざまな用途に使われています.通信の実装部分には UDP を利用することが多いようです.UDP とは,TCP と同様に,IP(Internet Protocol)を使ったデータ通信方法のひとつです.TCP/IP と比べて,UDP/IP は(確実性を一定犠牲にして)高速の通信ができます.ここでも UDP を使った OSC について解説します.
ESP 側のプログラム: ESP(Arduino)側では,IO4 に接続したスイッチを操作するたび,OSC メッセージ("/sw 1" または "/sw 0")を PC に送ります。PC はクライアントとなり,192.168.0.88 の IP アドレスを持つことを想定しています。通信に使用するポート番号は 8888 番とします。
#include <ESP8266WiFi.h> #include <WiFiUdp.h> #include <OSCMessage.h> const char *ssid = "xkozima"; // ご近所と重複ないように const char *pass = "dongbeino"; // 8文字以上 IPAddress ipServer(192, 168, 0, 1); // Dongbeino の固定アドレス IPAddress ipGateway(192, 168, 0, 1); // ゲートウェイ(上と同値) IPAddress subnet(255, 255, 255, 0); // サブネットマスク IPAddress ipClient(192, 168, 0, 88); // クライアント(PC)の固定アドレス WiFiUDP UDP; const unsigned int port = 8888; // OSC通信のポート番号 void setup() { // スイッチ入力 pinMode(4, INPUT_PULLUP); // シリアルモニタ(動作ログ) Serial.begin(115200); delay(100); Serial.println("\n*** Dongbeino ***"); // アクセスポイントの構成(固定アドレス) WiFi.mode(WIFI_AP); WiFi.softAP(ssid, pass); WiFi.softAPConfig(ipServer, ipGateway, subnet); Serial.print("network: "); Serial.println(ssid); Serial.print("address: "); Serial.println(WiFi.softAPIP()); Serial.print("client : "); Serial.println(ipClient); } void loop() { // PC への送信(スイッチ入力) int state = digitalRead(4); OSCMessage mess("/sw"); if (state == HIGH) { // スイッチは押されていない mess.add(1); Serial.println("/sw 1"); } else { // スイッチが押された mess.add(0); Serial.println("/sw 0"); } UDP.beginPacket(ipClient, port); mess.send(UDP); UDP.endPacket(); mess.empty(); // つぎへ delay(30); // 少し待つ(通信速度の調整) }
PC 側のプログラム: PC(openFrameworks)側のプログラムでは,8888番ポートで受信した OSC メッセージ("/sw 1" または "/sw 0")に応じて,赤丸または黒丸を描画します。
#pragma once #include "ofMain.h" #include "ofxOsc.h" #define PORT 8888 class ofApp : public ofBaseApp { private: ofxOscReceiver receiver; int state; public: void setup(); void update(); void draw(); ...(以下省略)... };
#include "ofApp.h" void ofApp::setup(){ ofSetWindowShape(600, 400); ofSetFrameRate(60); // OSC receiver.setup(PORT); // graphics ofBackground(255, 255, 255); // debug output printf("*** start server ***\n"); } void ofApp::update() { // OSC receive ofxOscMessage mess; while (receiver.getNextMessage(mess)) { if (mess.getAddress() == "/sw") { // get first(0-th) data state = mess.getArgAsInt(0); } } } void ofApp::draw(){ // graphics if (state == 0) { ofSetColor(255, 0, 0); } else { ofSetColor(0, 0, 0); } ofDrawCircle(300, 200, 120); } ...(以下省略)...
動かし方: ESP を動作させると SSID(上の例では xkozima)が見えるので,PC から接続します。このとき PC 側は固定 IP アドレス 192.168.0.88 とし,ゲートウェイは 192.168.0.1,サブネットマスクは 255.255.255.0 としてください。接続できたら,openFrameworks 側のプログラムを動かします。ESP 側でスイッチを操作すると,それに応じて PC 画面上のグラフィック描画が変化するはずです。
つぎに,PC から ESP にも OSC データを送る機能を加えたいと思います.ここでは,PC 上の openFrameworks プログラムから,キー操作に応じて,OSC メッセージ("/led 0" または "/led 1") を ESP に送信します.動作条件は,上にあげた「OSC でデータを ESP から PC に送る」と同様に,IP アドレスが 192.168.0.1 をもつ ESP がサブネット(SSID: xkozima) を提供し,PC はその中で 192.168.0.88 をもつようにします.ESP からの通信を受け取る PC 側のポート番号は 8888 で,逆に,PC からの通信を受け取る ESP 側のポート番号を 8889 とします.
ESP 側のプログラム: 前出のプログラムに加えて,PC からのデータ(OSC パケット)を受け取る処理を記述します.また,ESP の IO13 に(1kΩ の抵抗を介して)LED が接続されていることを確認してください.もし LED が無ければ,IO13〜1kΩ〜LED(A)/LED(K)〜GND と接続してください.
#include <ESP8266WiFi.h> #include <WiFiUdp.h> #include <OSCMessage.h> #include <OSCBundle.h> const char *ssid = "xkozima"; // ご近所と重複ないように const char *pass = "dongbeino"; // 8文字以上 IPAddress ipServer(192, 168, 0, 1); // Dongbeino の固定アドレス IPAddress ipGateway(192, 168, 0, 1); // ゲートウェイ(上と同値) IPAddress subnet(255, 255, 255, 0); // サブネットマスク IPAddress ipClient(192, 168, 0, 88); // クライアント(PC)の固定アドレス WiFiUDP UDP; const unsigned int port_out = 8888; // 送信先ポート番号 const unsigned int port_in = 8889; // 受信用ポート番号 void setup() { // スイッチ入力 pinMode(4, INPUT_PULLUP); // LED 出力 pinMode(13, OUTPUT); digitalWrite(13, LOW); // シリアルモニタ(動作ログ) Serial.begin(115200); delay(100); Serial.println("\n*** Dongbeino ***"); // アクセスポイントの構成(固定アドレス) WiFi.mode(WIFI_AP); WiFi.softAP(ssid, pass); WiFi.softAPConfig(ipServer, ipGateway, subnet); Serial.print("network: "); Serial.println(ssid); Serial.print("address: "); Serial.println(WiFi.softAPIP()); Serial.print("client : "); Serial.println(ipClient); // UDP 受信開始 UDP.begin(port_in); } void led_control(OSCMessage &mess) { // OSC メッセージに応じて LED を制御(オン・オフ) int val = mess.getInt(0); digitalWrite(13, val); } void loop() { // PC からの受信(LED に出力) OSCBundle bundle; int packetCount = UDP.parsePacket(); if (packetCount > 0) { while (packetCount--) { bundle.fill(UDP.read()); } if (! bundle.hasError()) { bundle.dispatch("/led", led_control); } } // PC への送信(スイッチ入力) int state = digitalRead(0); OSCMessage mess("/sw"); if (state == HIGH) { // スイッチは押されていない mess.add(1); Serial.println("/sw 1"); } else { // スイッチが押された mess.add(0); Serial.println("/sw 0"); } UDP.beginPacket(ipClient, port_out); mess.send(UDP); UDP.endPacket(); mess.empty(); // つぎへ delay(30); // 少し待つ(通信速度の調整) }
PC 側のプログラム: PC(openFrameworks)側のプログラムには,キー入力によって OSC メッセージ("/led 0" または "/led 1")を送信する機能を加えます.
#pragma once #include "ofMain.h" #include "ofxOsc.h" #define PORT_IN 8888 #define PORT_OUT 8889 class ofApp : public ofBaseApp { private: ofxOscReceiver receiver; ofxOscSenderr sender; int state; public: void setup(); void update(); void draw(); ...(以下省略)... };
#include "ofApp.h" void ofApp::setup(){ ofSetWindowShape(600, 400); ofSetFrameRate(60); // OSC receiver.setup(PORT_IN); sender.setup("192.168.0.1", PORT_OUT); // graphics ofBackground(255, 255, 255); // debug output printf("*** start server ***\n"); } void ofApp::update() { // OSC receive ofxOscMessage mess; while (receiver.getNextMessage(mess)) { if (mess.getAddress() == "/sw") { // get first(0-th) data state = mess.getArgAsInt(0); } } } void ofApp::draw(){ // graphics if (state == 0) { ofSetColor(255, 0, 0); } else { ofSetColor(0, 0, 0); } ofDrawCircle(300, 200, 120); } void ofApp::keyPressed(int key){ // OSC send "/led 1" ofxOscMessage mess; mess.setAddress("/led"); mess.addIntArg(1); sender.sendMessage(mess); mess.clear(); } void ofApp::keyReleased(int key){ // OSC send "/led 0" ofxOscMessage mess; mess.setAddress("/led"); mess.addIntArg(0); sender.sendMessage(mess); mess.clear(); } ...(以下省略)...
動かし方: 前出の例と同じように,ESP が提供するネットワークに PC を接続し,192.168.0.88 の固定アドレスを設定します.openFrameworks 側のプログラムを動作させ,ESP 側のスイッチを操作すると,それに応じて PC 画面上のグラフィックス描画が変化します.また,PC 上でいずれかのキーを押し下げると,ESP 上の LED が点灯し,キーから指を離すと消灯します.