Arduino + Processing + Max / MSPで双方向にごにょごにょする
■ 仕様
- 全般
コミュニケーション方法:Arduino <-(Serial)-> Processing <-(OSC)-> Max / Msp
- Arduinoの仕様
ボーレート: 9600
センサ1: A5(可変抵抗)
センサ2: D2(チルトスイッチ)
LED1: D9(赤)
LED2: D10(緑)
- Processing側の仕様
シリアルのボーレート: 9600
OSC受信ポート: 8001
- Max / MSP側の仕様
IPアドレス: 127.0.0.1
OSC受信ポート: 8000
/** * 一定間隔でセンサの値をおくりつつ、シリアルで値を受信したらその値に応じてLEDの色を変えるプログラム */ #define BAUD_RATE 9600 #define RESISTOR_PIN A5 #define SWITCH_PIN 2 #define RED_LED_PIN 9 #define GREEN_LED_PIN 10 #define SEND_INTERVAL 1000 char messageValue[2][10]; int messagePosIndex = 0; int messageValueIndex = 0; unsigned long sendTimer = 0; void setup() { Serial.begin(BAUD_RATE); } void loop () { getSerialData(); // Send sensing data if (millis() - sendTimer > SEND_INTERVAL) { Serial.print(analogRead(RESISTOR_PIN)); // Potention Metor Serial.print("\t"); Serial.print(digitalRead(SWITCH_PIN) == HIGH ? 255 : 0); // Tilt Switch Serial.println("\t"); } } void getSerialData() { while (Serial.available() > 0) { int serialData = Serial.read(); // Space if(serialData == 32) { if (messagePosIndex < 3) { messageValueIndex = 0; messagePosIndex++; } } // 0-9 else if (serialData >= 48 && serialData <= 57) { if (messageValueIndex < 10) { messageValue[messagePosIndex][messageValueIndex++] = serialData; } } // Suffix(:) else if(serialData == 58) { int val0 = atoi(messageValue[0]); int val1 = atoi(messageValue[1]); // Change LED brightness analogWrite(RED_LED_PIN, val0); analogWrite(GREEN_LED_PIN, val1); for(int i=0;i<messagePosIndex;i++) { for(int j=0;j<10;j++) { messageValue[i][j] = 0; } } messagePosIndex = 0; messageValueIndex = 0; } } }
■ Processing側のソースコード
/** * Arduinoからの信号をOSC経由でMaxに送るプログラム * いちおう、MaxからOSC経由で信号が来た場合、Arduinoに送り返すようにもなっている */ import processing.serial.*; import processing.net.*; import oscP5.*; import netP5.*; String COM_PORT = ""; String OSC_TARGET_IP_ADDRESS = "127.0.0.1"; int BAUD_RATE = 9600; // ArduinoのBaud Rate int OSC_MY_PORT = 8001; // Processingの受信用ポート int OSC_TARGET_PORT = 8000; // Max側の受信用ポート Serial serialPort; OscP5 oscP5; NetAddress maxLocation; String recievedMessage = ""; int dumpY = -15; int lastDatas[]; void setup() { size(640, 480); background(0); smooth(); println(Serial.list()); // OSC try { // increase the datagram size to 10000 bytes // by default it is set to 1536 bytes OscProperties oscProperties = new OscProperties(); oscProperties.setDatagramSize(10000); oscProperties.setListeningPort(OSC_MY_PORT); oscP5 = new OscP5(this, oscProperties); maxLocation = new NetAddress(OSC_TARGET_IP_ADDRESS, OSC_TARGET_PORT); } catch (Exception e) { dump("OSC initialized error:" + e); } // Serial try { // シリアルポートをしていない場合、8番当たりを狙ってみる if (COM_PORT.equals("")) { COM_PORT = Serial.list()[8]; } serialPort = new Serial(this, COM_PORT, BAUD_RATE); } catch (Exception e) { dump("Serial initialized error:" + e); } // とりあえず5個くらい要素用意しとく lastDatas = new int[5]; } void draw() { } /** * OSCの信号受信時のイベント * 受信フォーマットは、「/sensor ,ii 0 1」といった感じを想定。 */ void oscEvent(OscMessage receivedMessage) { // OSCのフォーマット確認 String message = "### received an osc message." + " addrpattern: " + receivedMessage.addrPattern() + " typetag: " + receivedMessage.typetag() + " timetag: " + receivedMessage.timetag(); dump(message); // OSCから受け取ったデータをArduinoに送る String cmd = receivedMessage.get(0).intValue() + " " + receivedMessage.get(1).intValue() + ":"; dump(cmd); serialPort.write(cmd); } /** * シリアル(Arduinoから来る想定)の信号受信時のイベント * 受信フォーマットは、「100\t200:\r\n」といった感じを想定。 */ void serialEvent(Serial serialData) { String val = serialPort.readString(); if (val.equals("\r")) { parseData(recievedMessage); recievedMessage = ""; } else if (val.equals("\n")) { } else { recievedMessage = recievedMessage + val; } } /** * 受信したシリアルデータをパースして、OSCでMax / MSPに送る * 送信フォーマットは、「/msg , ii 10 20」といった感じを想定。 */ void parseData(String message) { dump(recievedMessage); try { String[] datas = recievedMessage.split("\t"); OscMessage msg = new OscMessage("/msg"); for (int i=0;i<datas.length;i++) { int v = int(datas[i]); msg.add(v); lastDatas[i] = v; } oscP5.send(msg, maxLocation); } catch (Exception e) { dump("[Error][parseData]" + e); } } ////////////////////////////////// utils ////////////////////////////////// /** * ダンプ */ void dump(String val) { // GUIウィンドウに表示する場合 //int y = _incrementDumpY(); //text(val + "\n", 0, y, WINDOW_WIDTH, y + 20); println(val); } /** * ウィンドウに表示する際のテキストの位置を指定 */ int _incrementDumpY() { dumpY += 15; if (dumpY > 480) { background(0); dumpY = -15; } return dumpY; } /** * 日付を取得 */ String getTime() { return String.format("%04d/%02d/%02d %02d:%02d:%02d", year(), month(), day(), hour(), minute(), second()); }
■ Max / MSP側のソースコード(json形式でexportしてみた)
{ "patcher" : { "fileversion" : 1, "appversion" : { "major" : 6, "minor" : 1, "revision" : 3, "architecture" : "x86" } , "rect" : [ 29.0, 69.0, 965.0, 600.0 ], "bglocked" : 0, "openinpresentation" : 0, "default_fontsize" : 12.0, "default_fontface" : 0, "default_fontname" : "Arial", "gridonopen" : 0, "gridsize" : [ 15.0, 15.0 ], "gridsnaponopen" : 0, "statusbarvisible" : 2, "toolbarvisible" : 1, "boxanimatetime" : 200, "imprint" : 0, "enablehscroll" : 1, "enablevscroll" : 1, "devicewidth" : 0.0, "description" : "", "digest" : "", "tags" : "", "boxes" : [ { "box" : { "fontname" : "Arial", "fontsize" : 12.0, "frgb" : 0.0, "id" : "obj-7", "maxclass" : "comment", "numinlets" : 1, "numoutlets" : 0, "patching_rect" : [ 503.0, 40.0, 157.0, 20.0 ], "text" : "P5からの信号モニタリング" } } , { "box" : { "fontname" : "Arial", "fontsize" : 12.0, "id" : "obj-61", "maxclass" : "newobj", "numinlets" : 1, "numoutlets" : 0, "patching_rect" : [ 575.0, 209.0, 33.0, 20.0 ], "text" : "s v3" } } , { "box" : { "fontname" : "Arial", "fontsize" : 12.0, "id" : "obj-59", "maxclass" : "newobj", "numinlets" : 1, "numoutlets" : 0, "patching_rect" : [ 539.0, 209.0, 33.0, 20.0 ], "text" : "s v2" } } , { "box" : { "fontname" : "Arial", "fontsize" : 12.0, "id" : "obj-36", "maxclass" : "newobj", "numinlets" : 1, "numoutlets" : 0, "patching_rect" : [ 503.0, 209.0, 33.0, 20.0 ], "text" : "s v1" } } , { "box" : { "fontname" : "Arial", "fontsize" : 12.0, "id" : "obj-8", "maxclass" : "newobj", "numinlets" : 1, "numoutlets" : 0, "patching_rect" : [ 646.0, 117.0, 79.0, 20.0 ], "text" : "print oscmsg" } } , { "box" : { "fontname" : "Arial", "fontsize" : 12.0, "id" : "obj-5", "maxclass" : "newobj", "numinlets" : 1, "numoutlets" : 3, "outlettype" : [ "int", "int", "int" ], "patching_rect" : [ 503.0, 164.0, 79.0, 20.0 ], "text" : "unpack 1 1 1" } } , { "box" : { "fontname" : "Arial", "fontsize" : 12.0, "id" : "obj-3", "maxclass" : "newobj", "numinlets" : 2, "numoutlets" : 2, "outlettype" : [ "", "" ], "patching_rect" : [ 503.0, 128.0, 67.0, 20.0 ], "text" : "route /msg" } } , { "box" : { "id" : "obj-2", "maxclass" : "button", "numinlets" : 1, "numoutlets" : 1, "outlettype" : [ "bang" ], "patching_rect" : [ 588.0, 117.0, 20.0, 20.0 ] } } , { "box" : { "fontname" : "Arial", "fontsize" : 12.0, "id" : "obj-6", "maxclass" : "newobj", "numinlets" : 1, "numoutlets" : 1, "outlettype" : [ "" ], "patching_rect" : [ 503.0, 78.0, 99.0, 20.0 ], "text" : "udpreceive 8000" } } , { "box" : { "fontname" : "Arial", "fontsize" : 12.0, "frgb" : 0.0, "id" : "obj-1", "maxclass" : "comment", "numinlets" : 1, "numoutlets" : 0, "patching_rect" : [ 121.0, 34.0, 157.0, 20.0 ], "text" : "これおすと信号おくられる" } } , { "box" : { "fontname" : "Arial", "fontsize" : 12.0, "frgb" : 0.0, "id" : "obj-29", "maxclass" : "comment", "numinlets" : 1, "numoutlets" : 0, "patching_rect" : [ 379.5, 34.0, 82.0, 20.0 ], "text" : "green led" } } , { "box" : { "fontname" : "Arial", "fontsize" : 12.0, "frgb" : 0.0, "id" : "obj-28", "maxclass" : "comment", "numinlets" : 1, "numoutlets" : 0, "patching_rect" : [ 304.5, 34.0, 53.5, 20.0 ], "text" : "red led" } } , { "box" : { "fontname" : "Arial", "fontsize" : 12.0, "id" : "obj-27", "maxclass" : "flonum", "numinlets" : 1, "numoutlets" : 2, "outlettype" : [ "float", "bang" ], "parameter_enable" : 0, "patching_rect" : [ 308.0, 242.0, 50.0, 20.0 ] } } , { "box" : { "fontname" : "Arial", "fontsize" : 12.0, "id" : "obj-26", "maxclass" : "flonum", "numinlets" : 1, "numoutlets" : 2, "outlettype" : [ "float", "bang" ], "parameter_enable" : 0, "patching_rect" : [ 383.0, 242.0, 50.0, 20.0 ] } } , { "box" : { "id" : "obj-22", "maxclass" : "gain~", "numinlets" : 2, "numoutlets" : 2, "outlettype" : [ "signal", "int" ], "parameter_enable" : 0, "patching_rect" : [ 379.5, 64.0, 22.0, 140.0 ], "size" : 255 } } , { "box" : { "id" : "obj-21", "maxclass" : "gain~", "numinlets" : 2, "numoutlets" : 2, "outlettype" : [ "signal", "int" ], "parameter_enable" : 0, "patching_rect" : [ 304.5, 64.0, 22.0, 140.0 ], "size" : 255 } } , { "box" : { "fontname" : "Arial", "fontsize" : 12.0, "id" : "obj-18", "maxclass" : "message", "numinlets" : 2, "numoutlets" : 1, "outlettype" : [ "" ], "patching_rect" : [ 228.0, 64.0, 50.0, 18.0 ], "text" : "/sensor" } } , { "box" : { "fontname" : "Arial", "fontsize" : 12.0, "id" : "obj-16", "maxclass" : "newobj", "numinlets" : 4, "numoutlets" : 1, "outlettype" : [ "" ], "patching_rect" : [ 301.0, 292.0, 68.0, 20.0 ], "text" : "pack s i i i" } } , { "box" : { "fontname" : "Arial", "fontsize" : 12.0, "id" : "obj-13", "maxclass" : "newobj", "numinlets" : 1, "numoutlets" : 0, "patching_rect" : [ 322.0, 327.0, 92.0, 20.0 ], "text" : "print osc_msg" } } , { "box" : { "fontname" : "Arial", "fontsize" : 9.0, "id" : "obj-4", "maxclass" : "newobj", "numinlets" : 1, "numoutlets" : 0, "patching_rect" : [ 301.0, 365.0, 113.0, 17.0 ], "text" : "udpsend 127.0.0.1 8001" } } ], "lines" : [ { "patchline" : { "destination" : [ "obj-13", 0 ], "disabled" : 0, "hidden" : 0, "midpoints" : [ 310.5, 324.0, 331.5, 324.0 ], "source" : [ "obj-16", 0 ] } } , { "patchline" : { "destination" : [ "obj-4", 0 ], "disabled" : 0, "hidden" : 0, "midpoints" : [ 310.5, 312.0, 310.5, 312.0 ], "source" : [ "obj-16", 0 ] } } , { "patchline" : { "destination" : [ "obj-16", 0 ], "disabled" : 0, "hidden" : 0, "midpoints" : [ 237.5, 279.0, 310.5, 279.0 ], "source" : [ "obj-18", 0 ] } } , { "patchline" : { "destination" : [ "obj-27", 0 ], "disabled" : 0, "hidden" : 0, "midpoints" : [ 317.5, 204.0, 317.5, 204.0 ], "source" : [ "obj-21", 1 ] } } , { "patchline" : { "destination" : [ "obj-26", 0 ], "disabled" : 0, "hidden" : 0, "midpoints" : [ 392.5, 204.0, 392.5, 204.0 ], "source" : [ "obj-22", 1 ] } } , { "patchline" : { "destination" : [ "obj-16", 2 ], "disabled" : 0, "hidden" : 0, "midpoints" : [ 392.5, 279.0, 343.166656, 279.0 ], "source" : [ "obj-26", 0 ] } } , { "patchline" : { "destination" : [ "obj-16", 1 ], "disabled" : 0, "hidden" : 0, "midpoints" : [ 317.5, 279.0, 326.833344, 279.0 ], "source" : [ "obj-27", 0 ] } } , { "patchline" : { "destination" : [ "obj-5", 0 ], "disabled" : 0, "hidden" : 0, "midpoints" : [ 512.5, 155.5, 512.5, 155.5 ], "source" : [ "obj-3", 0 ] } } , { "patchline" : { "destination" : [ "obj-36", 0 ], "disabled" : 0, "hidden" : 0, "midpoints" : [ 512.5, 186.0, 512.5, 186.0 ], "source" : [ "obj-5", 0 ] } } , { "patchline" : { "destination" : [ "obj-59", 0 ], "disabled" : 0, "hidden" : 0, "midpoints" : [ 542.5, 195.0, 548.5, 195.0 ], "source" : [ "obj-5", 1 ] } } , { "patchline" : { "destination" : [ "obj-61", 0 ], "disabled" : 0, "hidden" : 0, "midpoints" : [ 572.5, 195.0, 584.5, 195.0 ], "source" : [ "obj-5", 2 ] } } , { "patchline" : { "destination" : [ "obj-2", 0 ], "disabled" : 0, "hidden" : 0, "midpoints" : [ 512.5, 114.0, 597.5, 114.0 ], "source" : [ "obj-6", 0 ] } } , { "patchline" : { "destination" : [ "obj-3", 0 ], "disabled" : 0, "hidden" : 0, "midpoints" : [ 512.5, 99.0, 512.5, 99.0 ], "source" : [ "obj-6", 0 ] } } , { "patchline" : { "destination" : [ "obj-8", 0 ], "disabled" : 0, "hidden" : 0, "midpoints" : [ 512.5, 99.0, 655.5, 99.0 ], "source" : [ "obj-6", 0 ] } } ], "dependency_cache" : [ ] } }