できること

第30回 OSC通信でArduinoと他のアプリを連携させてみる。(前編)

DSC_0002

Arduinoは単体で開発してもとても面白いデバイスだということが、これまでいろいろなパーツやシールドを利用した例を紹介してきて見ていただいている方は、十分に感じているかとは思います。さらに一歩進んで、今回は、OSC通信という通信方法を利用して、Arduinoを外部のアプリケーションと通信連携する方法を試してみたいと思います。外部アプリケーションと連携することで、スイッチやボリュームなどの電子部品からのアクションを通じて、ビジュアル言語で画面を操作するなど、表現の幅がぐんっと広がることになります!

今回の電子工作レシピ

時間目安:90分
必要なパーツ

OSC通信ってなんだろう?

OSC通信とはどのような通信方式なのでしょうか?wikipediaで調べてみると…

Open Sound Control – wikipedia
OpenSound Control(OSC)とは、電子楽器(特にシンセサイザー)やコンピュータなどの機器において音楽演奏データをネットワーク経由でリアルタイムに共有するための通信プロトコルである。カリフォルニア大学バークレー校にある CNMAT(The Center for New Music and Audio Technologies)が開発した。

音楽演奏データをやりとりする目的で開発された通信方式ということがわかりました。音楽演奏データ通信といえば、聞いたことがある方も多いかもしれませんが、MIDI(Musical Instrument Digital Interface)が有名ですね。このMIDIは、1982年とはるか昔に開発されたこともあり、通信速度が低かったり、リアルタイム性に向いていないなどの理由から、昨今ではOSCが利用されるようになってきています。

では、なぜ今回音楽を扱うわけでもないのに、ArduinoでOSC通信なのでしょうか。実はこのOSC通信はその通信の柔軟さなどから音楽データ通信以外にもさまざまなケースに利用されるようになってきており、多くのソフトウェアでもOSCに対応したものが出てきています。

 

OSCのしくみ

さっそくOSCのしくみについて学んでいきましょう。OSCはとてもシンプルな構成です。OSCには大きく2つの種類のメッセージがあり、そのメッセージをアプリ間でやりとりしながら通信を行います。

図1 OSC通信の仕様

  • Message : メッセージの種別を示すもの。「/」で区切って階層構造を作ることが可能なので、複雑な通信でも見やすい形で通信フォーマットを作成することが可能です。
  • Arguments : メッセージの内容です。数値(int / float)や文字列(string)形式などのデータを受け渡すことができます。

 

ArduinoでOSCを利用するには?

ArduinoでOSCを利用するためには、ネットワークに繋がる環境を用意する必要がありますので、イーサネットシールドを使います。

写真1 Arduino イーサネットシールド

写真1 Arduino イーサネットシールド

また、ArduinoでOSC通信をするためには、ライブラリを必要とします。ArduinoでOSC通信をするためのライブラリはいくつかありますが、今回はArdOSCを利用します。(最新バージョンのArduinoの場合、エラーが出て正常に動作しない可能性がありますので、その場合は他のOSCライブラリをご利用ください。)ライブラリを追加後、下記の送信プログラムを記述します(ライブラリの追加の方法は以前の回を参考してください)。

OSC送信テストプログラム

#include <SPI.h>
#include <Ethernet.h>

#include <ArdOSC.h>

byte myMac[] = { 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX };  //イーサネットのMACアドレスを記述
byte myIp[] = { 192, 168, 0, 9 };  //Arduinoに割り当てるIPアドレスを記述
int destPort=12000;  //送信先のポート番号
byte destIp[] = { 192, 168, 0, 2 };  //送信先のIPアドレス

OSCClient client;
OSCMessage global_mes;

void setup(){
Ethernet.begin(myMac ,myIp);   //イーサネットの設定
}

void loop(){
global_mes.setAddress(destIp,destPort); //送信設定
global_mes.beginMessage("/temperature");  //Message
global_mes.addArgString("29.0");  //Argument
client.send(&global_mes);   //データの送信
global_mes.flush(); //データのクリア
delay(500);
}

Arduino側の準備ができたら、次に外部アプリケーションの準備をします。今回、外部のアプリケーションには、Processing(プロセッシング)を使います。Processingはオープンソースプロジェクトの、メディアアートやビジュアルデザインのためのプログラミング言語です。Arduino同様、初心者が手軽にあつかえるグラフィックに特化した言語と言えます。Processingのウェブサイトから「Download Processing」を選択して、Processingをダウンロードしてインストールしてください。

図2 Processingウェブサイト

図2 Processingウェブサイト

Processingのインストールが完了したら、起動します。

図3 Processingアイコン

図3 Processingアイコン

図4 Processing起動画面

図4 Processing起動画面

起動すると、Arduinoに似た画面が表示されますね。Arduino同様、中央の箇所にプログラムを記述して再生ボタンでプログラムの実行、停止ボタンでプログラムを停止することができます。スケッチの例やライブラリの扱い方などもArduinoとほとんど同じですので違和感なく使うことができるかと思います。

さっそくProcessingに下記のOSC受信プログラムを記述します。

OSC受信プログラム(Processing)


import oscP5.*;
import netP5.*;
 
OscP5 oscP5;
NetAddress myRemoteLocation;

void setup() {
 size(500,500);
 frameRate(25);
 oscP5 = new OscP5(this,12000); //受信するポートの設定
 myRemoteLocation = new NetAddress("127.0.0.1",12001); //送信する場合の送信IPとポート
}

void draw() {
 background(0);
}

//OSC
void oscEvent(OscMessage theOscMessage) {
theOscMessage.print();
}

準備ができたら、Processingを実行した後に、Arduinoを実行すると、Processingのプログラムを記述する画面下のconsole枠にArduinoから送られてきたメッセージが表示されれば成功です。

図5 Processing実行結果

図5 Processing実行結果

気温湿度を外部アプリケーションに受け渡してみる

OSC通信を使ってArduinoで気温湿度を計測結果を、外部のアプリケーションに渡してみます。

Arduino側の準備として、気温湿度パーツのDHT11を利用して計測します。DHT11はArduinoのライブラリが用意されていますので、DHT11のライブラリを追加します。

写真2 気温湿度センサDHT11

写真2 気温湿度センサDHT11

ダウンロードしたDHT11のライブラリには、サンプルファイルが用意されていますので、そのサンプルをもとにDHT11で気温湿度が正常に計測できるかどうか確認します。

図6 気温湿度センサ回路

図6 気温湿度センサ回路

気温湿度計測のプログラム


#include "DHT.h"
#define DHTPIN 2
#define DHTTYPE DHT11   // DHT 11

DHT dht(DHTPIN, DHTTYPE);

void setup() {
  Serial.begin(9600);
  Serial.println("DHT11");
  dht.begin();
}

void loop() {
  delay(2000);

  float h = dht.readHumidity();
  float t = dht.readTemperature();
  float f = dht.readTemperature(true);

  if (isnan(h) || isnan(t) || isnan(f)) {
    Serial.println("Failed to read from DHT sensor!");
    return;
  }

  float hif = dht.computeHeatIndex(f, h);
  float hic = dht.computeHeatIndex(t, h, false);

  Serial.print("Humidity: ");
  Serial.print(h);
  Serial.print(" %\t");
  Serial.print("Temperature: ");
  Serial.print(t);
  Serial.print(" *C ");
  Serial.print(f);
  Serial.print(" *F\t");
  Serial.print("Heat index: ");
  Serial.print(hic);
  Serial.print(" *C ");
  Serial.print(hif);
  Serial.println(" *F");
}
図7 実行結果

図7 実行結果

正常に計測が確認できたら、OSC通信用にプログラムを修正します。

気温湿度OSC送信プログラム

#include &amp;lt;SPI.h&amp;gt;
#include &amp;lt;Ethernet.h&amp;gt;
#include &amp;lt;ArdOSC.h&amp;gt;
#include "DHT.h"
#define DHTPIN 2
#define DHTTYPE DHT11 // DHT 11

DHT dht(DHTPIN, DHTTYPE);
byte myMac[] = { 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX };
byte myIp[] = { 192, 168, 0, 9 }; 

int destPort=12000;
byte destIp[] = { 192,168,0,10 };

OSCClient client;
OSCMessage global_mes;
 
void setup(){ 
 Ethernet.begin(myMac ,myIp); 
 dht.begin();
}
 
void loop(){ 
 delay(2000);

 float h = dht.readHumidity();
 float t = dht.readTemperature();
 float f = dht.readTemperature(true);

 if (isnan(h) || isnan(t) || isnan(f)) {
 Serial.println("Failed to read from DHT sensor!");
 return;
 }

// float hif = dht.computeHeatIndex(f, h);
// float hic = dht.computeHeatIndex(t, h, false);

 global_mes.setAddress(destIp,destPort);
 //温度を送信
 global_mes.beginMessage("/temperature");
 global_mes.addArgFloat(t); 
 client.send(&amp;amp;global_mes);
 global_mes.flush(); //object data clear
 delay(5000);
 
 //湿度を送信
 global_mes.flush(); //object data clear
 global_mes.setAddress(destIp,destPort);
 global_mes.beginMessage("/humidity");
 global_mes.addArgFloat(h); 
 client.send(&amp;amp;global_mes);
 delay(1000);

 global_mes.flush(); //object data clear
 
}

受信側プログラム(Processing)

import oscP5.*;
import netP5.*;
 
OscP5 oscP5;
NetAddress myRemoteLocation;

void setup() {
 size(500,500);
 frameRate(25);
 oscP5 = new OscP5(this,12000);
 myRemoteLocation = new NetAddress("127.0.0.1",12001);
}

String str1 = "";
String str2 = "";
float val = 0;

void draw() {
 background(0);
 fill(256,256,256);
 text(str1,50,20);
 text(str2,50,40);
 
 fill(val,256,256);
 rect(20,10,20,32);
}

//OSC
void oscEvent(OscMessage theOscMessage) {
 /* print the address pattern and the typetag of the received OscMessage */
 print("### received an osc message.");
 theOscMessage.print();
 str1 = "OSC Message : "+theOscMessage.addrPattern();
 str2 = "OSC Arguments : "+theOscMessage.get(0).floatValue();
 val = theOscMessage.get(0).floatValue();
}

両方のプログラムを実行すると、Processingの画面に下記のように温度・湿度が繰り返し表示されれば完成です。

図8 OSC通信実行結果

図8 OSC通信実行結果

まとめ

今回はArduinoと外部アプリケーションを連携する方法の一つであるOSC通信を紹介しました。OSC通信を利用しなくても、独自に相互のアプリケーション同士で通信規約などを決めて通信することも可能ですが、OSC通信を利用することで、世にある数多くのソフトウェアやデバイスと連携することが可能になります。次回は、今回の基礎を発展させてOSC通信を利用することで広がるArduinoの使い方を紹介したいと思います。

アバター画像

電子工作や新しいデバイスをこよなく愛するエンジニア。日常生活のちょっとしたことを電子工作で作って試して、おもしろく過ごしたいと日々考えています。

RoboMaster 2019 参戦ファーストステップガイド