Open Sound Control

2020年12月26日 (土)

Wio-Terminal/M5Atom/ESP8622/ESP32ボードを共通のスケッチで動かす(v2)(OSC編)

2020/12/26+
初版

board3 OSC v2

board3 OSC v2

概要

Wio-Terminal/M5Atom/ESP8622/ESP32ボードを共通のスケッチで動かす(v2)(OSC編)
本記事は「Wio-Terminal/ESP8622/ESP32ボードを共通のスケッチで動かす(OSC編)」の改版にあたる。wio-terminalのWiFi/BLEファームウェアがバージョンアップしたので、それの対応方法を記載した。これにより、wio-terminalの古いファームウェアで原因不明で動作しなかったOSC受信が動作するようになった。

wio-terminalのファームウェアのアップデート方法については以下を参照のこと:
wio-terminalのファームウェア・アップデートについて(v2)(linux版)

OSCcodecライブラリ

OSC_codecライブラリとしてプロジェクトのsrcディレクトリに以下の2つのファイルを置く:

src/OSCmsgCodec.h

// OSC message Codec Header // 2013/10/28 #ifndef _OSCMSGCODEC_H #define _OSCMSGCODEC_H #include <string.h> int encOSCmsg(char *packet , union OSCarg *msg); // makes packet from OSC message and returns packet size void decOSCmsg(char *packet , union OSCarg *msg); // makes OSC message from packet union OSCarg { // char*, int and float are assumed four bytes char *address; char *typeTag; long int i; // int32 for Arduino(16bits) float f; char *s; struct { long int len; // is "int i" char *p; } blob; char m[4]; // for MIDI char _b[4]; // endian conversion temp variable }; #endif // _OSCMSGCODEC_H

src/OSCmsgCodec.cpp

/* <---------------------------------------------------------------------------------- OSC message Codec(encoder/decoder) version: 1.3 (2014/ 8/31) encoder bufix(enoceder returns byte length) version: 1.2 (2014/ 8/30) decoder bufix version: 1.1 (2013/11/20) support BIG_ENDIAN by #define version: 1.0 (2013/11/10) Copyright (C) 2011,2013,2014 S.Komatsu released under the MIT License: http://mbed.org/license/mit please refer to: http://opensoundcontrol.org/introduction-osc for OSC(Open Sound Control) The followings are supported: Features: Packet Parsing (Client) Packet Construction (Server) Bundle NOT Support Timetag NOT Support Type Support: i: int32 b: blob s: string f: float32 m: MIDI message(port id, status byte, data1, data2) // I don't know the detail Change Log: Bug(string length is not correct in encoding) Fix on 2013/11/10 (v0.9 -> v1.0) >---------------------------------------------------------------------------------- */ #include "OSCmsgCodec.h" //#define BIG_ENDIAN int lenAlign4B(int len) { if ((len % 4) == 0) {return len; } else {return len+4-(len % 4);} } int encOSCmsg(char *packet , union OSCarg *msg){ // *** important notice *** // output buffer must be cleared before call this char *p, *s, *d, *typeTag; char c; p=packet; d=p; s=msg[0].address; // address for(int i=0; i<strlen(msg[0].address); i++) *d++ = *s++; *d=0; // terminator // p += 4*((strlen(msg[0].address)+1)/4+1); p += lenAlign4B(strlen(msg[0].address)+1); // s=msg[1].typeTag; d=p; for(int i=0; i<strlen(msg[1].typeTag); i++) *d++ = *s++; *d=0; // terminator // p += 4*((strlen(msg[1].s)+1)/4+1); p += lenAlign4B(strlen(msg[1].typeTag)+1); // typeTag=msg[1].s+1; // skip ',' for(int n=0; n<strlen(typeTag); n++){ c = typeTag[n]; if (('s'==c)) { s=msg[n+2].s; d=p; for(int i=0; i<strlen(msg[n+2].s); i++) *d++ = *s++; *d=0; // terminater // p += 4*((strlen(msg[n+2].s)+1)/4+1); p += lenAlign4B(strlen(msg[n+2].s)+1); } else if (('i'==c)||('f'==c)) { #ifdef BIG_ENDIAN // no change endian (big to big) p[0]=msg[n+2]._b[0]; p[1]=msg[n+2]._b[1]; p[2]=msg[n+2]._b[2]; p[3]=msg[n+2]._b[3]; #else // change endian (little to big) p[0]=msg[n+2]._b[3]; p[1]=msg[n+2]._b[2]; p[2]=msg[n+2]._b[1]; p[3]=msg[n+2]._b[0]; #endif p +=4; } else if ('b'==c) { // put length of blog #ifdef BIG_ENDIAN // no change endian (big to big) p[0]=msg[n+2]._b[0]; p[1]=msg[n+2]._b[1]; p[2]=msg[n+2]._b[2]; p[3]=msg[n+2]._b[3]; #else // change endian (little to big) p[0]=msg[n+2]._b[3]; p[1]=msg[n+2]._b[2]; p[2]=msg[n+2]._b[1]; p[3]=msg[n+2]._b[0]; #endif p +=4; // get ponter of blog (copy to msg[n].blog.p) s=msg[n+2].blob.p; d=p; for(int i=0; i<msg[n+2].blob.len; i++) *d++ = *s++; p += 4*(msg[n+2].blob.len/4+1); } else if ('m'==c) { // get midi data (copy to msg[n].m[]) p[0]=msg[n+2].m[0]; p[1]=msg[n+2].m[1]; p[2]=msg[n+2].m[2]; p[3]=msg[n+2].m[3]; p +=4; } else { //printf("*** Not Supported TypeTag:%s ****\n",typeTag); } }; //return (p-packet); // return packet size // bugfix 2014/8/31 return sizeof(char)*(p-packet); // return byte length }; void decOSCmsg(char *packet , union OSCarg *msg){ // Caution: the returned result points to packet as blobs or strings (not newly allocatd) char *p, *typeTag; char c; int n; msg[0].address = packet; // address msg[1].typeTag = packet+4*((strlen(msg[0].s)+1)/4+1);//typeTag typeTag=msg[1].s+1; // skip ',' // bugfix 2014/8/30 if (strlen(typeTag)%2 == 0) p= msg[1].s+4*((strlen(msg[1].s)+1)/4); else p= msg[1].s+4*((strlen(msg[1].s)+1)/4+1); for(n=0; n<strlen(typeTag); n++){ c = typeTag[n]; if (('s'==c)) { msg[n+2].s=p; //p += 4*((strlen(msg[n+2].s)+1)/4+1); p += lenAlign4B(strlen(msg[n+2].s)+1); } else if (('i'==c)||('f'==c)) { #ifdef BIG_ENDIAN // no change endian (big to big) msg[n+2]._b[0]=p[0]; msg[n+2]._b[1]=p[1]; msg[n+2]._b[2]=p[2]; msg[n+2]._b[3]=p[3]; #else // change endian (big to little) msg[n+2]._b[3]=p[0]; msg[n+2]._b[2]=p[1]; msg[n+2]._b[1]=p[2]; msg[n+2]._b[0]=p[3]; #endif p +=4; } else if ('b'==c) { // get lenth of blog (copy to msg[n].blog.len) #ifdef BIG_ENDIAN // no change endian (big to big) msg[n+2]._b[0]=p[0]; msg[n+2]._b[1]=p[1]; msg[n+2]._b[2]=p[2]; msg[n+2]._b[3]=p[3]; #else // change endian (big to little) msg[n+2]._b[3]=p[0]; msg[n+2]._b[2]=p[1]; msg[n+2]._b[1]=p[2]; msg[n+2]._b[0]=p[3]; #endif p +=4; // get ponter of blog (copy to msg[n].blog.p) msg[n+2].blob.p=p; //p += 4*(msg[n+2].blob.len/4+1); p += lenAlign4B(msg[n+2].blob.len+1); } else if ('m'==c) { // get midi data (copy to msg[n].m[]) msg[n+2].m[0]=p[0]; msg[n+2].m[1]=p[1]; msg[n+2].m[2]=p[2]; msg[n+2].m[3]=p[3]; p +=4; } else { //printf("*** Not Supported TypeTag:%s ****\n",typeTag); } }; };

デモ・スケッチ

src/main.ino

// select board ////#define WIO_TERMINAL ////#define ESP8266 ////#define ESP32 ////#define M5ATOM /* OSC sender/receiver Forked for Wio-Terminal/ESP8266(ESP-WROOM-02)/ESP32 from: This sketch sends random data over UDP on a ESP32 device */ #ifdef M5ATOM #include "M5Atom.h" #define ESP32 #endif #ifdef WIO_TERMINAL //#include <AtWiFi.h> #include <rpcWiFi.h> #endif #ifdef ESP8266 #include <ESP8266WiFi.h> #endif #ifdef ESP32 #include <WiFi.h> #endif #include <WiFiUdp.h> // WiFi network name and password: const char* ssid = "your-ssid"; const char* passwd = "your-password"; //IP address to send UDP data to: const char* udpAddress = "192.168.0.18"; // target IP const int udpInPort = 8000; // incomming port const int udpOutPort = 9000; // outgoing port //<<<<<<<<<<<<<<<<<<<<< // OSC related #include "OSCmsgCodec.h" #define PACKET_SIZE 512 #define RECBUF_SIZE 256 union OSCarg msg[10], outmsg[10]; char buff[PACKET_SIZE],packet[PACKET_SIZE]; //>>>>>>>>>>>>>>>>>>>> char recbuf[128]; // buffer for incoming packets //The udp library class WiFiUDP udp; void setup() { Serial.begin(115200); Serial.println("OSC test program start..."); Serial.println(""); // delete old config WiFi.disconnect(true); Serial.printf("Connecting to %s ", ssid); WiFi.begin(ssid, passwd); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(" connected"); udp.begin(udpInPort); Serial.printf("Now listening at IP %s, UDP port %d\r\n", WiFi.localIP().toString().c_str(), udpInPort); } void loop() { int packetSize = udp.parsePacket(); if (packetSize>0) { Serial.printf("Received %d bytes from %s, port %d\r\n", packetSize, udp.remoteIP().toString().c_str(), udpInPort); int len = udp.read(recbuf, 128); if (len > 0) { decOSCmsg(recbuf, msg); Serial.printf("incomming OSC msg:\r\n%s %s", msg[0].address, msg[1].typeTag); for(int m=0; m < (strlen(msg[1].typeTag)-1); m++) { if (msg[1].typeTag[m+1]=='f') Serial.printf(" %f",msg[m+2].f); if (msg[1].typeTag[m+1]=='i') Serial.printf(" %d",msg[m+2].i); }; Serial.printf("\r\n"); Serial.printf("-------------------\r\n"); } } else { // test send // fader // send OSC message with random values (to touchOSC) // make OSC message for sending outmsg[0].address="/1/fader5"; outmsg[1].typeTag=",f"; outmsg[2].f= rand()%1000/1000.0; memset(packet,0,sizeof(packet)); // clear send buff for OSC msg int plen=encOSCmsg(packet,outmsg); // send it // Send a packet udp.beginPacket(udpAddress, udpOutPort); udp.write((const uint8_t*)packet, plen); udp.endPacket(); // //Serial.print("."); // indicate sending packet //Wait delay(10); // seems 'must' (for platformio) } }

wio-terminalのファームウェアのバージョン・アップにともない 以下のようにヘッダーが変更になっている:

//#include <AtWiFi.h> #include <rpcWiFi.h>

以下の#defineでボードを切り換えているがボードの種類を設定すると、 システムで設定されている定義が有効になるので特にソースを変更する必要はない。

#define WIO_TERMINAL #define ESP8266 #define ESP32 #define M5ATOM

以下については、自分の環境に合わせて変更すること:

// WiFi network name and password: const char* ssid = "your_ssid"; const char* passwd = "your_passwd"; //IP address to send UDP data to: const char* udpAddress = "192.168.0.18"; // target IP const int udpInPort = 8000; // incomming port const int udpOutPort = 9000; // outgoing port

udpAddressは、対向するOSC受信デバイス(touchOSCなど)のIPを設定する。

書き込み後に「picocom /dev/ttyACM0 -b115200」または「picocom /dev/ttyUSB0 -b115200」で通信ソフトを起動すると以下のような出力が表示される:

$ picocom /dev/ttyACM0 -b115200 ........... connected Now listening at IP 192.168.0.11, UDP port 8000

上の例ではTouchOSCから192.168.0.11:8000へOSCパッケットに送る(ようにTouchOSCを設定する)。

または、動作としては、touchOSCのsimpleレイアウトの画面で、最上行の受信アイコンがOSCを受信すると「赤」になり、fader5のfaderが送られてきたOSCの内容に従ってランダムに変化する。

platformio.ini

platformio.iniは、ボードに合わせて以下を使用する:

wio-terminalの場合

; PlatformIO Project Configuration File ; ; Build options: build flags, source filter ; Upload options: custom upload port, speed and extra flags ; Library options: dependencies, extra library storages ; Advanced options: extra scripting ; ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html [env:seeed_wio_terminal] platform = atmelsam board = seeed_wio_terminal framework = arduino build_flags = -DWIO_TERMINAL upload_protocol = sam-ba monitor_speed = 115200 lib_ldf_mode = deep+ lib_deps = https://github.com/Seeed-Studio/Seeed_Arduino_mbedtls/archive/dev.zip https://github.com/Seeed-Studio/Seeed_Arduino_rpcUnified/archive/master.zip https://github.com/Seeed-Studio/Seeed_Arduino_rpcBLE/archive/master.zip https://github.com/Seeed-Studio/Seeed_Arduino_rpcWiFi/archive/master.zip https://github.com/Seeed-Studio/Seeed_Arduino_FreeRTOS/archive/master.zip https://github.com/Seeed-Studio/Seeed_Arduino_FS/archive/master.zip https://github.com/Seeed-Studio/Seeed_Arduino_SFUD/archive/master.zip # https://github.com/Seeed-Studio/Seeed_Arduino_LCD/archive/master.zip #

M5Atomの場合

; PlatformIO Project Configuration File ; ; Build options: build flags, source filter ; Upload options: custom upload port, speed and extra flags ; Library options: dependencies, extra library storages ; Advanced options: extra scripting ; ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html [env:esp32dev] platform = espressif32 #board = esp32dev board = m5stick-c framework = arduino monitor_speed = 115200 lib_ldf_mode = deep+ lib_deps = # use M5Atom lib 3113 # use "FastLED" 126

ESP32の場合

; PlatformIO Project Configuration File ; ; Build options: build flags, source filter ; Upload options: custom upload port, speed and extra flags ; Library options: dependencies, extra library storages ; Advanced options: extra scripting ; ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html [env:esp32dev] platform = espressif32 board = esp32dev framework = arduino

ESP8266の場合

; PlatformIO Project Configuration File ; ; Build options: build flags, source filter ; Upload options: custom upload port, speed and extra flags ; Library options: dependencies, extra library storages ; Advanced options: extra scripting ; ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html [env:huzzah] platform = espressif8266 #board = huzzah board = esp_wroom_02 framework = arduino

wio-terminalのファームウェア切り替えたときの注意

古いファームウェアのライブラリを動かした後は、古いライブラリがキャッシュで残っていると ビルド・エラーになるので以下を実行すること:

cd project_directory rm -r .pio/libdeps/seeed_wio_terminal/* rm -r .pio/build/seeed_wio_terminal/*

参考情報

TouchOSC - Modular touch control surface for OSC & MIDI
TouchOSC - iPhone
TouchOSC - Android

OSCmsgCode lib:
https://os.mbed.com/users/xshige/code/Sparkfun_CC3000_WiFi_OSCtranceiver//raw-file/e62251d890c1/OSCmsgCodec.h
https://os.mbed.com/users/xshige/code/Sparkfun_CC3000_WiFi_OSCtranceiver//raw-file/e62251d890c1/OSCmsgCodec.cpp
https://os.mbed.com/users/xshige/code/Sparkfun_CC3000_WiFi_OSCtranceiver//file/e62251d890c1/main.cpp/

Platform/Wio Terminal Network/Wi-Fi - example sketches

Wio Terminalをはじめよう(ピン配置、データシート、回路図など)

PlatformIO Core (CLI)

old version:
Wio-TerminalでWiFiで使う(その4:OSC)

以上

続きを読む "Wio-Terminal/M5Atom/ESP8622/ESP32ボードを共通のスケッチで動かす(v2)(OSC編)"

| | コメント (0)

2020年7月22日 (水)

Wio-Terminal/ESP8622/ESP32ボードを共通のスケッチで動かす(OSC編)

2020/7/22:
スケッチ改版

2020/7/21:
初版

board3 OSC

board3 OSC

概要

Wio-Terminal/ESP8622/ESP32ボードを共通のスケッチで動かす(OSC編)
(ホストPCとしてはubuntuを想定している)

OSCcodecライブラリ

OSC_codecライブラリとしてプロジェクトのsrcディレクトリに以下の2つのファイルを置く:

src/OSCmsgCodec.h

// OSC message Codec Header // 2013/10/28 #ifndef _OSCMSGCODEC_H #define _OSCMSGCODEC_H #include <string.h> int encOSCmsg(char *packet , union OSCarg *msg); // makes packet from OSC message and returns packet size void decOSCmsg(char *packet , union OSCarg *msg); // makes OSC message from packet union OSCarg { // char*, int and float are assumed four bytes char *address; char *typeTag; long int i; // int32 for Arduino(16bits) float f; char *s; struct { long int len; // is "int i" char *p; } blob; char m[4]; // for MIDI char _b[4]; // endian conversion temp variable }; #endif // _OSCMSGCODEC_H

src/OSCmsgCodec.cpp

/* <---------------------------------------------------------------------------------- OSC message Codec(encoder/decoder) version: 1.3 (2014/ 8/31) encoder bufix(enoceder returns byte length) version: 1.2 (2014/ 8/30) decoder bufix version: 1.1 (2013/11/20) support BIG_ENDIAN by #define version: 1.0 (2013/11/10) Copyright (C) 2011,2013,2014 S.Komatsu released under the MIT License: http://mbed.org/license/mit please refer to: http://opensoundcontrol.org/introduction-osc for OSC(Open Sound Control) The followings are supported: Features: Packet Parsing (Client) Packet Construction (Server) Bundle NOT Support Timetag NOT Support Type Support: i: int32 b: blob s: string f: float32 m: MIDI message(port id, status byte, data1, data2) // I don't know the detail Change Log: Bug(string length is not correct in encoding) Fix on 2013/11/10 (v0.9 -> v1.0) >---------------------------------------------------------------------------------- */ #include "OSCmsgCodec.h" //#define BIG_ENDIAN int lenAlign4B(int len) { if ((len % 4) == 0) {return len; } else {return len+4-(len % 4);} } int encOSCmsg(char *packet , union OSCarg *msg){ // *** important notice *** // output buffer must be cleared before call this char *p, *s, *d, *typeTag; char c; p=packet; d=p; s=msg[0].address; // address for(int i=0; i<strlen(msg[0].address); i++) *d++ = *s++; *d=0; // terminator // p += 4*((strlen(msg[0].address)+1)/4+1); p += lenAlign4B(strlen(msg[0].address)+1); // s=msg[1].typeTag; d=p; for(int i=0; i<strlen(msg[1].typeTag); i++) *d++ = *s++; *d=0; // terminator // p += 4*((strlen(msg[1].s)+1)/4+1); p += lenAlign4B(strlen(msg[1].typeTag)+1); // typeTag=msg[1].s+1; // skip ',' for(int n=0; n<strlen(typeTag); n++){ c = typeTag[n]; if (('s'==c)) { s=msg[n+2].s; d=p; for(int i=0; i<strlen(msg[n+2].s); i++) *d++ = *s++; *d=0; // terminater // p += 4*((strlen(msg[n+2].s)+1)/4+1); p += lenAlign4B(strlen(msg[n+2].s)+1); } else if (('i'==c)||('f'==c)) { #ifdef BIG_ENDIAN // no change endian (big to big) p[0]=msg[n+2]._b[0]; p[1]=msg[n+2]._b[1]; p[2]=msg[n+2]._b[2]; p[3]=msg[n+2]._b[3]; #else // change endian (little to big) p[0]=msg[n+2]._b[3]; p[1]=msg[n+2]._b[2]; p[2]=msg[n+2]._b[1]; p[3]=msg[n+2]._b[0]; #endif p +=4; } else if ('b'==c) { // put length of blog #ifdef BIG_ENDIAN // no change endian (big to big) p[0]=msg[n+2]._b[0]; p[1]=msg[n+2]._b[1]; p[2]=msg[n+2]._b[2]; p[3]=msg[n+2]._b[3]; #else // change endian (little to big) p[0]=msg[n+2]._b[3]; p[1]=msg[n+2]._b[2]; p[2]=msg[n+2]._b[1]; p[3]=msg[n+2]._b[0]; #endif p +=4; // get ponter of blog (copy to msg[n].blog.p) s=msg[n+2].blob.p; d=p; for(int i=0; i<msg[n+2].blob.len; i++) *d++ = *s++; p += 4*(msg[n+2].blob.len/4+1); } else if ('m'==c) { // get midi data (copy to msg[n].m[]) p[0]=msg[n+2].m[0]; p[1]=msg[n+2].m[1]; p[2]=msg[n+2].m[2]; p[3]=msg[n+2].m[3]; p +=4; } else { //printf("*** Not Supported TypeTag:%s ****\n",typeTag); } }; //return (p-packet); // return packet size // bugfix 2014/8/31 return sizeof(char)*(p-packet); // return byte length }; void decOSCmsg(char *packet , union OSCarg *msg){ // Caution: the returned result points to packet as blobs or strings (not newly allocatd) char *p, *typeTag; char c; int n; msg[0].address = packet; // address msg[1].typeTag = packet+4*((strlen(msg[0].s)+1)/4+1);//typeTag typeTag=msg[1].s+1; // skip ',' // bugfix 2014/8/30 if (strlen(typeTag)%2 == 0) p= msg[1].s+4*((strlen(msg[1].s)+1)/4); else p= msg[1].s+4*((strlen(msg[1].s)+1)/4+1); for(n=0; n<strlen(typeTag); n++){ c = typeTag[n]; if (('s'==c)) { msg[n+2].s=p; //p += 4*((strlen(msg[n+2].s)+1)/4+1); p += lenAlign4B(strlen(msg[n+2].s)+1); } else if (('i'==c)||('f'==c)) { #ifdef BIG_ENDIAN // no change endian (big to big) msg[n+2]._b[0]=p[0]; msg[n+2]._b[1]=p[1]; msg[n+2]._b[2]=p[2]; msg[n+2]._b[3]=p[3]; #else // change endian (big to little) msg[n+2]._b[3]=p[0]; msg[n+2]._b[2]=p[1]; msg[n+2]._b[1]=p[2]; msg[n+2]._b[0]=p[3]; #endif p +=4; } else if ('b'==c) { // get lenth of blog (copy to msg[n].blog.len) #ifdef BIG_ENDIAN // no change endian (big to big) msg[n+2]._b[0]=p[0]; msg[n+2]._b[1]=p[1]; msg[n+2]._b[2]=p[2]; msg[n+2]._b[3]=p[3]; #else // change endian (big to little) msg[n+2]._b[3]=p[0]; msg[n+2]._b[2]=p[1]; msg[n+2]._b[1]=p[2]; msg[n+2]._b[0]=p[3]; #endif p +=4; // get ponter of blog (copy to msg[n].blog.p) msg[n+2].blob.p=p; //p += 4*(msg[n+2].blob.len/4+1); p += lenAlign4B(msg[n+2].blob.len+1); } else if ('m'==c) { // get midi data (copy to msg[n].m[]) msg[n+2].m[0]=p[0]; msg[n+2].m[1]=p[1]; msg[n+2].m[2]=p[2]; msg[n+2].m[3]=p[3]; p +=4; } else { //printf("*** Not Supported TypeTag:%s ****\n",typeTag); } }; };

デモ・スケッチ

src/OSC_test.ino

// select board ////#define WIO_TERMINAL ////#define ESP8266 ////#define ESP32 /* OSC sender/receiver Forked for Wio-Terminal/ESP8266(ESP-WROOM-02)/ESP32 from: This sketch sends random data over UDP on a ESP32 device */ #ifdef WIO_TERMINAL #include <AtWiFi.h> #endif #ifdef ESP8266 #include <ESP8266WiFi.h> #endif #ifdef ESP32 #include <WiFi.h> #endif #include <WiFiUdp.h> // WiFi network name and password: const char* ssid = "your_ssid"; const char* passwd = "your_passwd"; //IP address to send UDP data to: const char* udpAddress = "192.168.0.21"; // target IP const int udpInPort = 8000; // incomming port const int udpOutPort = 9000; // outgoing port //<<<<<<<<<<<<<<<<<<<<< // OSC related #include "OSCmsgCodec.h" #define PACKET_SIZE 512 #define RECBUF_SIZE 256 union OSCarg msg[10], outmsg[10]; char buff[PACKET_SIZE],packet[PACKET_SIZE]; //>>>>>>>>>>>>>>>>>>>> char recbuf[128]; // buffer for incoming packets //The udp library class WiFiUDP udp; void setup() { Serial.begin(115200); Serial.println("OSC test program start..."); Serial.println(""); // delete old config WiFi.disconnect(true); Serial.printf("Connecting to %s ", ssid); WiFi.begin(ssid, passwd); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(" connected"); udp.begin(udpInPort); Serial.printf("Now listening at IP %s, UDP port %d\r\n", WiFi.localIP().toString().c_str(), udpInPort); } void loop() { int packetSize = udp.parsePacket(); if (packetSize>0) { Serial.printf("Received %d bytes from %s, port %d\r\n", packetSize, udp.remoteIP().toString().c_str(), udpInPort); int len = udp.read(recbuf, 128); if (len > 0) { decOSCmsg(recbuf, msg); Serial.printf("incomming OSC msg:\r\n%s %s", msg[0].address, msg[1].typeTag); for(int m=0; m < (strlen(msg[1].typeTag)-1); m++) { if (msg[1].typeTag[m+1]=='f') Serial.printf(" %f",msg[m+2].f); if (msg[1].typeTag[m+1]=='i') Serial.printf(" %d",msg[m+2].i); }; Serial.printf("\r\n"); Serial.printf("-------------------\r\n"); } } else { // test send // fader // send OSC message with random values (to touchOSC) // make OSC message for sending outmsg[0].address="/1/fader5"; outmsg[1].typeTag=",f"; outmsg[2].f= rand()%1000/1000.0; memset(packet,0,sizeof(packet)); // clear send buff for OSC msg int plen=encOSCmsg(packet,outmsg); // send it // Send a packet udp.beginPacket(udpAddress, udpOutPort); udp.write((const uint8_t*)packet, plen); udp.endPacket(); // //Serial.print("."); // indicate sending packet //Wait delay(10); // seems 'must' (for platformio) } }

以下の#defineでボードを切り換えているがボードの種類を設定すると、 システムで設定されている定義が有効になるので特にソースを変更する必要はない。

#define WIO_TERMINAL #define ESP8266 #define ESP32

以下については、自分の環境に合わせて変更すること:

// WiFi network name and password: const char* ssid = "your_ssid"; const char* passwd = "your_passwd"; //IP address to send UDP data to: const char* udpAddress = "192.168.0.15"; // target IP const int udpInPort = 8000; // incomming port const int udpOutPort = 9000; // outgoing port

udpAddressは、対向するOSC受信デバイス(touchOSCなど)のIPを設定する。

書き込み後に「picocom /dev/ttyACM0 -b115200」または「picocom /dev/ttyUSB0 -b115200」で通信ソフトを起動すると以下のような出力が表示される:

$ picocom /dev/ttyACM0 -b115200 ........... connected Now listening at IP 192.168.0.11, UDP port 8000

上の例ではTouchOSCから192.168.0.11:8000へOSCパッケットに送る(ようにTouchOSCを設定する)。

または、動作としては、touchOSCのsimpleレイアウトの画面で、最上行の受信アイコンがOSCを受信すると「赤」になり、fader5のfaderが送られてきたOSCの内容に従ってランダムに変化する。

Arduino-IDEのボードマネージャーのURL

以下のURLを設定することで3つのボード(Wio-Terminal/ESP8266/ESP32)が使用可能になる。

https://files.seeedstudio.com/arduino/package_seeeduino_boards_index.json http://arduino.esp8266.com/stable/package_esp8266com_index.json https://dl.espressif.com/dl/package_esp32_index.json

問題点

Wio-Terminalでは、OSC受信は、動作しなかった。(原因不明)

参考情報

TouchOSC - Modular touch control surface for OSC & MIDI
TouchOSC - iPhone
TouchOSC - Android

OSCmsgCode lib:
https://os.mbed.com/users/xshige/code/Sparkfun_CC3000_WiFi_OSCtranceiver//raw-file/e62251d890c1/OSCmsgCodec.h
https://os.mbed.com/users/xshige/code/Sparkfun_CC3000_WiFi_OSCtranceiver//raw-file/e62251d890c1/OSCmsgCodec.cpp
https://os.mbed.com/users/xshige/code/Sparkfun_CC3000_WiFi_OSCtranceiver//file/e62251d890c1/main.cpp/

Platform/Wio Terminal Network/Wi-Fi - example sketches

Wio Terminalをはじめよう(ピン配置、データシート、回路図など)

Platform/Wio Terminal/Network/Overview
download url:
https://files.seeedstudio.com/wiki/Wio-Terminal/res/ameba-image-Tool-v2.4.1.zip
https://files.seeedstudio.com/wiki/Wio-Terminal/res/20200601-rtl8720d-images-v2.2.0.0.zip

PlatformIO Core (CLI)

以上

続きを読む "Wio-Terminal/ESP8622/ESP32ボードを共通のスケッチで動かす(OSC編)"

| | コメント (0)

2020年7月13日 (月)

Wio-TerminalでWiFiで使う(その4:OSC)

2020/7/19
platformio.iniの変更

2020/7/15:
スケッチ改版

2020/7/13:
初版

PlatformIO Wio Terminal WiFi4

PlatformIO Wio Terminal WiFi4

概要

Wio-TerminalでWiFiで使う(その4:OSC)
これは「Wio-TerminalでWiFiで使う(その3: REST-API)」の続きでOSCデモプログラムについて記載する
(ホストPCとしてはubuntuを想定している)

OSCcodecライブラリ

OSC_codecライブラリとしてプロジェクトのsrcディレクトリに以下の2つのファイルを置く:

src/OSCmsgCodec.h

// OSC message Codec Header // 2013/10/28 #ifndef _OSCMSGCODEC_H #define _OSCMSGCODEC_H #include <string.h> int encOSCmsg(char *packet , union OSCarg *msg); // makes packet from OSC message and returns packet size void decOSCmsg(char *packet , union OSCarg *msg); // makes OSC message from packet union OSCarg { // char*, int and float are assumed four bytes char *address; char *typeTag; long int i; // int32 for Arduino(16bits) float f; char *s; struct { long int len; // is "int i" char *p; } blob; char m[4]; // for MIDI char _b[4]; // endian conversion temp variable }; #endif // _OSCMSGCODEC_H

src/OSCmsgCodec.cpp

/* <---------------------------------------------------------------------------------- OSC message Codec(encoder/decoder) version: 1.3 (2014/ 8/31) encoder bufix(enoceder returns byte length) version: 1.2 (2014/ 8/30) decoder bufix version: 1.1 (2013/11/20) support BIG_ENDIAN by #define version: 1.0 (2013/11/10) Copyright (C) 2011,2013,2014 S.Komatsu released under the MIT License: http://mbed.org/license/mit please refer to: http://opensoundcontrol.org/introduction-osc for OSC(Open Sound Control) The followings are supported: Features: Packet Parsing (Client) Packet Construction (Server) Bundle NOT Support Timetag NOT Support Type Support: i: int32 b: blob s: string f: float32 m: MIDI message(port id, status byte, data1, data2) // I don't know the detail Change Log: Bug(string length is not correct in encoding) Fix on 2013/11/10 (v0.9 -> v1.0) >---------------------------------------------------------------------------------- */ #include "OSCmsgCodec.h" //#define BIG_ENDIAN int lenAlign4B(int len) { if ((len % 4) == 0) {return len; } else {return len+4-(len % 4);} } int encOSCmsg(char *packet , union OSCarg *msg){ // *** important notice *** // output buffer must be cleared before call this char *p, *s, *d, *typeTag; char c; p=packet; d=p; s=msg[0].address; // address for(int i=0; i<strlen(msg[0].address); i++) *d++ = *s++; *d=0; // terminator // p += 4*((strlen(msg[0].address)+1)/4+1); p += lenAlign4B(strlen(msg[0].address)+1); // s=msg[1].typeTag; d=p; for(int i=0; i<strlen(msg[1].typeTag); i++) *d++ = *s++; *d=0; // terminator // p += 4*((strlen(msg[1].s)+1)/4+1); p += lenAlign4B(strlen(msg[1].typeTag)+1); // typeTag=msg[1].s+1; // skip ',' for(int n=0; n<strlen(typeTag); n++){ c = typeTag[n]; if (('s'==c)) { s=msg[n+2].s; d=p; for(int i=0; i<strlen(msg[n+2].s); i++) *d++ = *s++; *d=0; // terminater // p += 4*((strlen(msg[n+2].s)+1)/4+1); p += lenAlign4B(strlen(msg[n+2].s)+1); } else if (('i'==c)||('f'==c)) { #ifdef BIG_ENDIAN // no change endian (big to big) p[0]=msg[n+2]._b[0]; p[1]=msg[n+2]._b[1]; p[2]=msg[n+2]._b[2]; p[3]=msg[n+2]._b[3]; #else // change endian (little to big) p[0]=msg[n+2]._b[3]; p[1]=msg[n+2]._b[2]; p[2]=msg[n+2]._b[1]; p[3]=msg[n+2]._b[0]; #endif p +=4; } else if ('b'==c) { // put length of blog #ifdef BIG_ENDIAN // no change endian (big to big) p[0]=msg[n+2]._b[0]; p[1]=msg[n+2]._b[1]; p[2]=msg[n+2]._b[2]; p[3]=msg[n+2]._b[3]; #else // change endian (little to big) p[0]=msg[n+2]._b[3]; p[1]=msg[n+2]._b[2]; p[2]=msg[n+2]._b[1]; p[3]=msg[n+2]._b[0]; #endif p +=4; // get ponter of blog (copy to msg[n].blog.p) s=msg[n+2].blob.p; d=p; for(int i=0; i<msg[n+2].blob.len; i++) *d++ = *s++; p += 4*(msg[n+2].blob.len/4+1); } else if ('m'==c) { // get midi data (copy to msg[n].m[]) p[0]=msg[n+2].m[0]; p[1]=msg[n+2].m[1]; p[2]=msg[n+2].m[2]; p[3]=msg[n+2].m[3]; p +=4; } else { //printf("*** Not Supported TypeTag:%s ****\n",typeTag); } }; //return (p-packet); // return packet size // bugfix 2014/8/31 return sizeof(char)*(p-packet); // return byte length }; void decOSCmsg(char *packet , union OSCarg *msg){ // Caution: the returned result points to packet as blobs or strings (not newly allocatd) char *p, *typeTag; char c; int n; msg[0].address = packet; // address msg[1].typeTag = packet+4*((strlen(msg[0].s)+1)/4+1);//typeTag typeTag=msg[1].s+1; // skip ',' // bugfix 2014/8/30 if (strlen(typeTag)%2 == 0) p= msg[1].s+4*((strlen(msg[1].s)+1)/4); else p= msg[1].s+4*((strlen(msg[1].s)+1)/4+1); for(n=0; n<strlen(typeTag); n++){ c = typeTag[n]; if (('s'==c)) { msg[n+2].s=p; //p += 4*((strlen(msg[n+2].s)+1)/4+1); p += lenAlign4B(strlen(msg[n+2].s)+1); } else if (('i'==c)||('f'==c)) { #ifdef BIG_ENDIAN // no change endian (big to big) msg[n+2]._b[0]=p[0]; msg[n+2]._b[1]=p[1]; msg[n+2]._b[2]=p[2]; msg[n+2]._b[3]=p[3]; #else // change endian (big to little) msg[n+2]._b[3]=p[0]; msg[n+2]._b[2]=p[1]; msg[n+2]._b[1]=p[2]; msg[n+2]._b[0]=p[3]; #endif p +=4; } else if ('b'==c) { // get lenth of blog (copy to msg[n].blog.len) #ifdef BIG_ENDIAN // no change endian (big to big) msg[n+2]._b[0]=p[0]; msg[n+2]._b[1]=p[1]; msg[n+2]._b[2]=p[2]; msg[n+2]._b[3]=p[3]; #else // change endian (big to little) msg[n+2]._b[3]=p[0]; msg[n+2]._b[2]=p[1]; msg[n+2]._b[1]=p[2]; msg[n+2]._b[0]=p[3]; #endif p +=4; // get ponter of blog (copy to msg[n].blog.p) msg[n+2].blob.p=p; //p += 4*(msg[n+2].blob.len/4+1); p += lenAlign4B(msg[n+2].blob.len+1); } else if ('m'==c) { // get midi data (copy to msg[n].m[]) msg[n+2].m[0]=p[0]; msg[n+2].m[1]=p[1]; msg[n+2].m[2]=p[2]; msg[n+2].m[3]=p[3]; p +=4; } else { //printf("*** Not Supported TypeTag:%s ****\n",typeTag); } }; };

OSC_send_test

OSC送信プログラム:
src/OSC_send_test.ino

// select board #define WIO_TERMINAL //#define ESP8266 #include <SPI.h> // forced /* OSC sender/receiver Forked for Wio-Terminal/ESP-WROOM-02 from: This sketch sends random data over UDP on a ESP32 device */ #ifdef WIO_TERMINAL #include <AtWiFi.h> #endif #ifdef ESP8266 #include <ESP8266WiFi.h> #endif #include <WiFiUdp.h> // WiFi network name and password: const char* ssid = "your_ssid"; const char* passwd = "your_password"; //IP address to send UDP data to: const char* udpAddress = "192.168.0.15"; // target IP const int udpInPort = 8000; // incomming port const int udpOutPort = 9000; // outgoing port //<<<<<<<<<<<<<<<<<<<<< // OSC related #include "OSCmsgCodec.h" #define PACKET_SIZE 512 #define RECBUF_SIZE 256 union OSCarg msg[10], outmsg[10]; char buff[PACKET_SIZE],packet[PACKET_SIZE]; //>>>>>>>>>>>>>>>>>>>> char recbuf[128]; // buffer for incoming packets //The udp library class WiFiUDP udp; void setup() { Serial.begin(115200); Serial.println("OSC test program start..."); Serial.println(""); // delete old config WiFi.disconnect(true); Serial.printf("Connecting to %s ", ssid); WiFi.begin(ssid, passwd); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(" connected"); udp.begin(udpInPort); Serial.printf("Now listening at IP %s, UDP port %d\r\n", WiFi.localIP().toString().c_str(), udpInPort); } void loop() { int packetSize = udp.parsePacket(); if (packetSize>0) { Serial.printf("Received %d bytes from %s, port %d\r\n", packetSize, udp.remoteIP().toString().c_str(), udp.remotePort()); int len = udp.read(recbuf, 128); if (len > 0) { decOSCmsg(recbuf, msg); Serial.printf("incomming OSC msg:\r\n%s %s", msg[0].address, msg[1].typeTag); for(int m=0; m < (strlen(msg[1].typeTag)-1); m++) { if (msg[1].typeTag[m+1]=='f') Serial.printf(" %f",msg[m+2].f); if (msg[1].typeTag[m+1]=='i') Serial.printf(" %d",msg[m+2].i); } Serial.printf("\r\n"); Serial.printf("-------------------\r\n"); } } else { // test send // fader // send OSC message with random values (to touchOSC) // make OSC message for sending outmsg[0].address="/1/fader5"; outmsg[1].typeTag=",f"; outmsg[2].f= rand()%1000/1000.0; memset(packet,0,sizeof(packet)); // clear send buff for OSC msg int plen=encOSCmsg(packet,outmsg); // send it // Send a packet udp.beginPacket(udpAddress, udpOutPort); udp.write((const uint8_t*)packet, plen); udp.endPacket(); // //Serial.print("."); // indicate sending packet //Wait //delay(100); } }

以下については、自分の環境に合わせて変更すること:

// WiFi network name and password: const char* ssid = "your_ssid"; const char* passwd = "your_password"; //IP address to send UDP data to: const char* udpAddress = "192.168.0.15"; // target IP const int udpInPort = 8000; // incomming port const int udpOutPort = 9000; // outgoing port

udpAddressは、対向するOSC受信デバイス(touchOSCなど)のIPを設定する。

留意点: OSC受信も実装したが、実際には動作しない。(原因不明) #defineでESP8266ボードに切り替えた場合、受信できるが、一回、受信した後、リセットする(原因不明)

書き込み後に「picocom /dev/ttyACM0 -b115200」で通信ソフトを起動すると以下のような出力が表示される:

$ picocom /dev/ttyACM0 -b115200 ........... connected Now listening at IP 192.168.0.11, UDP port 8000

上の例ではTouchOSCから192.168.0.11:8000へOSCパッケットに送る。(実際には動作せず)

または、動作としては、touchOSCのsimpleレイアウトの画面で、最上行の受信アイコンがOSCを受信すると「赤」になり、fader5のfaderが送られてきたOSCの内容に従ってランダムに変化する。

追記情報(改善)

試行錯誤の結果、ESP8266ボードに関しては、platformio.iniに以下のように「lib_ldf_mode = deep+」を追加することにより、正常にOSC受信が正常に動作するようになった。wio-terminalに関しては「lib_ldf_mode = deep+」を追加しても改善しなかった。

; PlatformIO Project Configuration File ; ; Build options: build flags, source filter ; Upload options: custom upload port, speed and extra flags ; Library options: dependencies, extra library storages ; Advanced options: extra scripting ; ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html [env:huzzah] platform = espressif8266 #board = huzzah board = esp_wroom_02 framework = arduino lib_ldf_mode = deep+

正常に動作した際の出力例:

$ picocom /dev/ttyUSB0 -b115200 ...... connected Now listening at IP 192.168.0.17, UDP port 8000 # 以下、TouchOSCのfaderを操作してOSCパケットを # ESP8266(この例ではIPは192.168.0.17:8000)に送る Received 20 bytes from 192.168.0.23, port 9000 incomming OSC msg: /1/fader3 ,f 0.531646 ------------------- Received 20 bytes from 192.168.0.23, port 9000 incomming OSC msg: /1/fader3 ,f 0.516878 ------------------- Received 20 bytes from 192.168.0.23, port 9000 incomming OSC msg: /1/fader3 ,f 0.514768 ------------------- Received 20 bytes from 192.168.0.23, port 9000 incomming OSC msg: /1/fader3 ,f 0.514768 ------------------- Received 20 bytes from 192.168.0.23, port 9000 incomming OSC msg: /1/fader2 ,f 0.483122 -------------------

「lib_ldf_mode = deep+」の解説: https://docs.platformio.org/en/latest/librarymanager/ldf.html#ldf

参考情報

TouchOSC - Modular touch control surface for OSC & MIDI
TouchOSC - iPhone
TouchOSC - Android

OSCmsgCode lib:
https://os.mbed.com/users/xshige/code/Sparkfun_CC3000_WiFi_OSCtranceiver//raw-file/e62251d890c1/OSCmsgCodec.h
https://os.mbed.com/users/xshige/code/Sparkfun_CC3000_WiFi_OSCtranceiver//raw-file/e62251d890c1/OSCmsgCodec.cpp
https://os.mbed.com/users/xshige/code/Sparkfun_CC3000_WiFi_OSCtranceiver//file/e62251d890c1/main.cpp/

Platform/Wio Terminal Network/Wi-Fi - example sketches

Wio Terminalをはじめよう(ピン配置、データシート、回路図など)

Platform/Wio Terminal/Network/Overview
download url:
https://files.seeedstudio.com/wiki/Wio-Terminal/res/ameba-image-Tool-v2.4.1.zip
https://files.seeedstudio.com/wiki/Wio-Terminal/res/20200601-rtl8720d-images-v2.2.0.0.zip

PlatformIO Core (CLI)

以上

続きを読む "Wio-TerminalでWiFiで使う(その4:OSC)"

| | コメント (0)

2020年6月23日 (火)

Wio_Lite_RISC-VボードでWiFiを動かす(その3:OSC)

2020/6/23:
初版

PlatformIO Wio Lite RISC-V WiFi3

PlatformIO Wio Lite RISC-V WiFi3

概要

Wio_Lite_RISC-VボードでWiFiを動かす(その3:OSC)。 これは「Wio_Lite_RISC-VボードでWiFiを動かす(その2)」の続きになる。ここでは、OSC(Open Sound Control)のデモプログラムについて記載する
開発ツール(PlatformIOなど)のインストールについては「開発ツールPlatformIOをcliで使う(Wio_Lite_RISC-V版)」を参照のこと、ここでは、WiFiを動かすためのプログラムについて記載する。 (ホストPCとしてはubuntuを想定している)

接続

wio-lite-rvボードのPA9(TX). PA10(RX)、GNDをUSBシリアルに接続する。
ちなみに、XIAOをUSBシリアルとして使用する場合は以下のように接続する:

wio-lite-rv XIAO
PA10(RX) D6(TX)
PA9(TX) D7(RX)
GND GND

OSCcodecライブラリ

OSC_codecライブラリとしてプロジェクトのsrcディレクトリに以下の2つのファイルを置く:

src/OSCmsgCodec.h

// OSC message Codec Header // 2013/10/28 #ifndef _OSCMSGCODEC_H #define _OSCMSGCODEC_H #include <string.h> int encOSCmsg(char *packet , union OSCarg *msg); // makes packet from OSC message and returns packet size void decOSCmsg(char *packet , union OSCarg *msg); // makes OSC message from packet union OSCarg { // char*, int and float are assumed four bytes char *address; char *typeTag; long int i; // int32 for Arduino(16bits) float f; char *s; struct { long int len; // is "int i" char *p; } blob; char m[4]; // for MIDI char _b[4]; // endian conversion temp variable }; #endif // _OSCMSGCODEC_H

src/OSCmsgCodec.cpp

/* <---------------------------------------------------------------------------------- OSC message Codec(encoder/decoder) version: 1.3 (2014/ 8/31) encoder bufix(enoceder returns byte length) version: 1.2 (2014/ 8/30) decoder bufix version: 1.1 (2013/11/20) support BIG_ENDIAN by #define version: 1.0 (2013/11/10) Copyright (C) 2011,2013,2014 S.Komatsu released under the MIT License: http://mbed.org/license/mit please refer to: http://opensoundcontrol.org/introduction-osc for OSC(Open Sound Control) The followings are supported: Features: Packet Parsing (Client) Packet Construction (Server) Bundle NOT Support Timetag NOT Support Type Support: i: int32 b: blob s: string f: float32 m: MIDI message(port id, status byte, data1, data2) // I don't know the detail Change Log: Bug(string length is not correct in encoding) Fix on 2013/11/10 (v0.9 -> v1.0) >---------------------------------------------------------------------------------- */ #include "OSCmsgCodec.h" //#define BIG_ENDIAN int lenAlign4B(int len) { if ((len % 4) == 0) {return len; } else {return len+4-(len % 4);} } int encOSCmsg(char *packet , union OSCarg *msg){ // *** important notice *** // output buffer must be cleared before call this char *p, *s, *d, *typeTag; char c; p=packet; d=p; s=msg[0].address; // address for(int i=0; i<strlen(msg[0].address); i++) *d++ = *s++; *d=0; // terminator // p += 4*((strlen(msg[0].address)+1)/4+1); p += lenAlign4B(strlen(msg[0].address)+1); // s=msg[1].typeTag; d=p; for(int i=0; i<strlen(msg[1].typeTag); i++) *d++ = *s++; *d=0; // terminator // p += 4*((strlen(msg[1].s)+1)/4+1); p += lenAlign4B(strlen(msg[1].typeTag)+1); // typeTag=msg[1].s+1; // skip ',' for(int n=0; n<strlen(typeTag); n++){ c = typeTag[n]; if (('s'==c)) { s=msg[n+2].s; d=p; for(int i=0; i<strlen(msg[n+2].s); i++) *d++ = *s++; *d=0; // terminater // p += 4*((strlen(msg[n+2].s)+1)/4+1); p += lenAlign4B(strlen(msg[n+2].s)+1); } else if (('i'==c)||('f'==c)) { #ifdef BIG_ENDIAN // no change endian (big to big) p[0]=msg[n+2]._b[0]; p[1]=msg[n+2]._b[1]; p[2]=msg[n+2]._b[2]; p[3]=msg[n+2]._b[3]; #else // change endian (little to big) p[0]=msg[n+2]._b[3]; p[1]=msg[n+2]._b[2]; p[2]=msg[n+2]._b[1]; p[3]=msg[n+2]._b[0]; #endif p +=4; } else if ('b'==c) { // put length of blog #ifdef BIG_ENDIAN // no change endian (big to big) p[0]=msg[n+2]._b[0]; p[1]=msg[n+2]._b[1]; p[2]=msg[n+2]._b[2]; p[3]=msg[n+2]._b[3]; #else // change endian (little to big) p[0]=msg[n+2]._b[3]; p[1]=msg[n+2]._b[2]; p[2]=msg[n+2]._b[1]; p[3]=msg[n+2]._b[0]; #endif p +=4; // get ponter of blog (copy to msg[n].blog.p) s=msg[n+2].blob.p; d=p; for(int i=0; i<msg[n+2].blob.len; i++) *d++ = *s++; p += 4*(msg[n+2].blob.len/4+1); } else if ('m'==c) { // get midi data (copy to msg[n].m[]) p[0]=msg[n+2].m[0]; p[1]=msg[n+2].m[1]; p[2]=msg[n+2].m[2]; p[3]=msg[n+2].m[3]; p +=4; } else { //printf("*** Not Supported TypeTag:%s ****\n",typeTag); } }; //return (p-packet); // return packet size // bugfix 2014/8/31 return sizeof(char)*(p-packet); // return byte length }; void decOSCmsg(char *packet , union OSCarg *msg){ // Caution: the returned result points to packet as blobs or strings (not newly allocatd) char *p, *typeTag; char c; int n; msg[0].address = packet; // address msg[1].typeTag = packet+4*((strlen(msg[0].s)+1)/4+1);//typeTag typeTag=msg[1].s+1; // skip ',' // bugfix 2014/8/30 if (strlen(typeTag)%2 == 0) p= msg[1].s+4*((strlen(msg[1].s)+1)/4); else p= msg[1].s+4*((strlen(msg[1].s)+1)/4+1); for(n=0; n<strlen(typeTag); n++){ c = typeTag[n]; if (('s'==c)) { msg[n+2].s=p; //p += 4*((strlen(msg[n+2].s)+1)/4+1); p += lenAlign4B(strlen(msg[n+2].s)+1); } else if (('i'==c)||('f'==c)) { #ifdef BIG_ENDIAN // no change endian (big to big) msg[n+2]._b[0]=p[0]; msg[n+2]._b[1]=p[1]; msg[n+2]._b[2]=p[2]; msg[n+2]._b[3]=p[3]; #else // change endian (big to little) msg[n+2]._b[3]=p[0]; msg[n+2]._b[2]=p[1]; msg[n+2]._b[1]=p[2]; msg[n+2]._b[0]=p[3]; #endif p +=4; } else if ('b'==c) { // get lenth of blog (copy to msg[n].blog.len) #ifdef BIG_ENDIAN // no change endian (big to big) msg[n+2]._b[0]=p[0]; msg[n+2]._b[1]=p[1]; msg[n+2]._b[2]=p[2]; msg[n+2]._b[3]=p[3]; #else // change endian (big to little) msg[n+2]._b[3]=p[0]; msg[n+2]._b[2]=p[1]; msg[n+2]._b[1]=p[2]; msg[n+2]._b[0]=p[3]; #endif p +=4; // get ponter of blog (copy to msg[n].blog.p) msg[n+2].blob.p=p; //p += 4*(msg[n+2].blob.len/4+1); p += lenAlign4B(msg[n+2].blob.len+1); } else if ('m'==c) { // get midi data (copy to msg[n].m[]) msg[n+2].m[0]=p[0]; msg[n+2].m[1]=p[1]; msg[n+2].m[2]=p[2]; msg[n+2].m[3]=p[3]; p +=4; } else { //printf("*** Not Supported TypeTag:%s ****\n",typeTag); } }; };

OSC_recv_test

OSC受信プログラム:
src/OSC_recv_test.cpp

#include <Arduino.h> #include <stdio.h> #include <stdarg.h> #include <math.h> // OSC related #include "OSCmsgCodec.h" #define PACKET_SIZE 512 #define RECBUF_SIZE 256 union OSCarg msg[10], outmsg[10]; char buff[PACKET_SIZE],packet[PACKET_SIZE]; #define MY_SSID "your_ssid" #define MY_PASSWD "your_passwd" #define TARGET_IP "192.168.0.23" #define OUT_PORT 9000 #define IN_PORT 8000 char ipAddress [20]; static void wio_serial_init(void) { // enable GPIO clock for USART0 rcu_periph_clock_enable(RCU_GPIOA); // enable USART clock rcu_periph_clock_enable(RCU_USART0); /* connect port to USARTx_Tx(PA9) */ gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); /* connect port to USARTx_Rx(PA10) */ gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10); // enable GPIO clock for USART1 rcu_periph_clock_enable(RCU_GPIOA); // enable USART clock rcu_periph_clock_enable(RCU_USART1); /* connect port to USARTx_Tx(PA2) */ gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2); /* connect port to USARTx_Rx(PA3) */ gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_3); // USART0 configure usart_deinit(USART0); usart_baudrate_set(USART0, 115200U); usart_word_length_set(USART0, USART_WL_8BIT); usart_stop_bit_set(USART0, USART_STB_1BIT); usart_parity_config(USART0, USART_PM_NONE); usart_hardware_flow_rts_config(USART0, USART_RTS_DISABLE); usart_hardware_flow_cts_config(USART0, USART_CTS_DISABLE); usart_receive_config(USART0, USART_RECEIVE_ENABLE); usart_transmit_config(USART0, USART_TRANSMIT_ENABLE); usart_enable(USART0); // USART1 configure usart_deinit(USART1); usart_baudrate_set(USART1, 115200U); usart_word_length_set(USART1, USART_WL_8BIT); usart_stop_bit_set(USART1, USART_STB_1BIT); usart_parity_config(USART1, USART_PM_NONE); usart_hardware_flow_rts_config(USART1, USART_RTS_DISABLE); usart_hardware_flow_cts_config(USART1, USART_CTS_DISABLE); usart_receive_config(USART1, USART_RECEIVE_ENABLE); usart_transmit_config(USART1, USART_TRANSMIT_ENABLE); usart_enable(USART1); } int _get0_char(void) { while ( usart_flag_get(USART0, USART_FLAG_RBNE)== RESET){ } return (uint8_t)usart_data_receive(USART0); } int _get1_char(void) { while ( usart_flag_get(USART1, USART_FLAG_RBNE)== RESET){ } return (uint8_t)usart_data_receive(USART1); } int _put0_char(int ch) { usart_data_transmit(USART0, (uint8_t) ch ); while ( usart_flag_get(USART0, USART_FLAG_TBE)== RESET){ } return ch; } int _put1_char(int ch) { usart_data_transmit(USART1, (uint8_t) ch ); while ( usart_flag_get(USART1, USART_FLAG_TBE)== RESET){ } return ch; } void usart0_printf(const char *fmt, ...) { char buf[100]; va_list args; va_start(args, fmt); vsprintf(buf, fmt, args); va_end(args); char *p = buf; while( *p != '\0' ) { _put0_char(*p); p++; } } void usart0_print(const char *s) { while( *s != '\0' ) { _put0_char(*s); s++; } } void usart0_println(const char *s) { while( *s != '\0' ) { _put0_char(*s); s++; } _put0_char('\r'); _put0_char('\n'); } void usart1_printf(const char *fmt, ...) { char buf[100]; va_list args; va_start(args, fmt); vsprintf(buf, fmt, args); va_end(args); char *p = buf; while( *p != '\0' ) { _put1_char(*p); p++; } } void usart1_print(const char *s) { while( *s != '\0' ) { _put1_char(*s); s++; } } void usart1_println(const char *s) { while( *s != '\0' ) { _put1_char(*s); s++; } _put1_char('\r'); _put1_char('\n'); } int usart1_available(void) { return ( usart_flag_get(USART1, USART_FLAG_RBNE)== SET); } //------------------------------------------------------- char res[500]; void get_res(void) { int cpos = 0; for(int cc=0; cc<24000000; cc++) { while ( usart_flag_get(USART1, USART_FLAG_RBNE)== SET) { //res[cpos] = (char) _get1_char(); cpos++; res[cpos] = (char) usart_data_receive(USART1); cpos++; }; }; res[cpos] = 0; } void get_res2(void) { int cpos = 0; for(int cc=0; cc<500000; cc++) { while ( usart_flag_get(USART1, USART_FLAG_RBNE)== SET) { //res[cpos] = (char) _get1_char(); cpos++; res[cpos] = (char) usart_data_receive(USART1); cpos++; }; }; res[cpos] = 0; } void setup() { wio_serial_init(); //Serial.begin(115200); //serial port of GD32 //Serial1.begin(115200); //serial port of ESP8266 pinMode(LED_BUILTIN, OUTPUT); delay(3000); usart0_print("\r\n\r\n\r\n\r\n\r\n"); // rest ESP8266 usart0_println("SEND:"); usart0_print("AT+RST\r\n"); usart1_print("AT+RST\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); // get firmware version usart0_println("SEND:"); usart0_print("AT+GMR\r\n"); usart1_print("AT+GMR\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_println("SEND:"); usart0_print("AT+CWQAP\r\n"); usart1_print("AT+CWQAP\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_println("SEND:"); usart0_print("AT+CWMODE=1\r\n"); usart1_print("AT+CWMODE=1\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); //Serial1.println("AT+CWJAP=\"Your WiFi SSID\",\"Password\""); // add your own wifi usart0_println("SEND:"); usart0_print("AT+CWJAP=\""); usart0_print(MY_SSID); usart0_print("\",\""); usart0_print(MY_PASSWD); usart0_print("\"\r\n"); usart1_print("AT+CWJAP=\""); usart1_print(MY_SSID); usart1_print("\",\""); usart1_print(MY_PASSWD); usart1_print("\"\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_println("SEND:"); usart0_print("AT+CIFSR\r\n"); usart1_print("AT+CIFSR\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); //-- get IP Address -- int len = strlen( res ); bool done=false; bool error = false; int pos = 0; while (!done) { if ( res[pos] == '\"') { done = true;} pos++; if (pos > len) { done = true; error = true;} } if (!error) { int buffpos = 0; done = false; while (!done) { if ( res[pos] == '\"' ) { done = true; } else { ipAddress[buffpos] = res[pos]; buffpos++; pos++;} } ipAddress[buffpos] = 0; } else { strcpy(ipAddress,"ERROR"); } //usart0_printf("ipAddress:%s\r\n",ipAddress); //----------------- usart0_println("SEND:"); usart0_print("AT+CIPMUX=1\r\n"); usart1_print("AT+CIPMUX=1\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_println("SEND:"); usart0_print("AT+CIPSERVER=1,80\r\n"); usart1_print("AT+CIPSERVER=1,80\r\n"); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); usart0_printf("\r\nipAddress: %s\r\n\r\n",ipAddress); // UDP access // for UDP usart0_printf("AT+CIPSTART=1,\"UDP\",\"%s\",%d,%d,0\r\n",TARGET_IP,OUT_PORT,IN_PORT); usart1_printf("AT+CIPSTART=1,\"UDP\",\"%s\",%d,%d,0\r\n",TARGET_IP,OUT_PORT,IN_PORT); get_res(); usart0_println("RES:"); usart0_println(res); usart0_println("-----------"); } //------------ void dispatch(void) { int datlen; int cp1,cp2,cp3; int len = (int)strlen(res); // keep length of res[] for (int i=0; i<len; i++) { if ((res[i]=='+') && (res[i+1]=='I') && (res[i+2]=='P') && (res[i+3]=='D') ) { // check +IPD // skip 1st ',' for (cp1 = i+4; cp1<len; cp1++) { if (res[cp1]==',') break; } // find 2nd ',' for (cp2 = cp1+1; cp2<len; cp2++) { if (res[cp2]==',') break; } char *dp = &res[cp2+1]; // find ':' for (cp3 = cp2+1; cp3<len; cp3++) { if (res[cp3]==':') break; } res[cp3]=0; // make string terminator datlen = atoi(dp); int d = 0; for (int s=cp3+1; s<(cp3+1+datlen); s++) { packet[d] = res[s]; d++; }; packet[d]=0; //usart0_printf("datalen: %d\r\n",datlen); // // decode OSC messeage decOSCmsg(packet, msg); usart0_printf("incomming OSC msg:\r\n%s %s\r\n", msg[0].address, msg[1].typeTag); for(int m=0; m < ((int)strlen(msg[1].typeTag)-1); m++) { if (msg[1].typeTag[m+1]=='f') usart0_printf(" %d/1000", (int)floor(1000*msg[m+2].f)); // %f not supported, replace with %d if (msg[1].typeTag[m+1]=='i') usart0_printf(" %d",msg[m+2].i); } usart0_printf("\r\n"); usart0_printf("Received packet from: %s \r\n", TARGET_IP); usart0_printf("-------------------\r\n"); } }; } //------------ void loop() { digitalWrite(LED_BUILTIN, HIGH); // debug: for indicate in-loop /** // test loop while(true) { if ( usart_flag_get(USART1, USART_FLAG_RBNE)== SET) _put0_char(_get1_char()); if ( usart_flag_get(USART0, USART_FLAG_RBNE)== SET) _put1_char(_get0_char()); }; **/ while(true) { get_res2(); if ((int)strlen(res) != 0) { //usart0_print("\r\nRES:\r\n"); //usart0_println(res); dispatch(); } else { //usart0_print("."); // indicating input wait } } }

以下については、自分の環境に合わせて変更すること:

#define MY_SSID "your_ssid" #define MY_PASSWD "your_passwd" #define TARGET_IP "192.168.0.23" #define OUT_PORT 9000 #define IN_PORT 8000

TARGET_IPは、対向するOSC送信デバイス(touchOSCなど)のIPを設定する。

書き込み後に「picocom /dev/ttyACM0 -b115200」で通信ソフトを起動すると以下のような出力が表示される:

$ picocom /dev/ttyACM0 -b115200 SEND: AT+GMR RES: AT+GMR AT version:2.1.0.0-dev(d25f6d2 - Oct 15 2019 12:03:04) SDK version:v3.2-192-g81655f39 compile time(525fbfe):Oct 17 2019 05:39:13 Bin version:2.0.0(WROOM-02) ... <省略> ... ----------- ipAddress: 192.168.0.26 # この時点でtouchOSCのfaderなどを動かす。 # (この例では、Wio-LiteのIPの"192.168.0.26"へOSCメッセージを送る) # 以下のように受信したOSCメッセージが表示される: incomming OSC msg: /1/fader2 ,f 983/1000 Received packet from: 192.168.0.23 ------------------- incomming OSC msg: /1/fader2 ,f 670/1000 Received packet from: 192.168.0.23 ------------------- incomming OSC msg: /1/fader2 ,f 554/1000 Received packet from: 192.168.0.23 ------------------- incomming OSC msg: /1/fader2 ,f 776/1000 Received packet from: 192.168.0.23 ------------------- incomming OSC msg: /1/fader2 ,f 700/1000 Received packet from: 192.168.0.23 ------------------- incomming OSC msg: /1/fader2 ,f 837/1000 Received packet from: 192.168.0.23 ------------------- incomming OSC msg: /1/fader2 ,f 575/1000 Received packet from: 192.168.0.23 ------------------- incomming OSC msg: /1/fader2 ,f 814/1000 Received packet from: 192.168.0.23 -------------------

printfの%fがサポートされていないので、整数の%dで代用している。(例:「814/1000」は、0.814を意味する)

OSC_send_test

OSC送信プログラム:
OSC受信プログラムのloop()関数を以下のものに置き換える:

void loop() { digitalWrite(LED_BUILTIN, HIGH); // debug: for indicate in-loop /** // test loop while(true) { if ( usart_flag_get(USART1, USART_FLAG_RBNE)== SET) _put0_char(_get1_char()); if ( usart_flag_get(USART0, USART_FLAG_RBNE)== SET) _put1_char(_get0_char()); }; **/ while(true){ // test send // fader // send OSC message with random values (to touchOSC) // make OSC message for sending outmsg[0].address="/1/fader5"; outmsg[1].typeTag=",f"; outmsg[2].f= rand()%1000/1000.0; memset(packet,0,sizeof(packet)); // clear send buff for OSC msg int plen=encOSCmsg(packet,outmsg); // send it ////usart0_printf("send: AT+CIPSEND=1,%d\r\n",plen); usart1_printf("AT+CIPSEND=1,%d\r\n",plen); delay(10); get_res2(); for(int i=0; i<plen; i++) _put1_char(packet[i]); delay(10); get_res2(); ////usart0_printf("res: %s\r\n",res); usart0_print("."); // indicating OSC sending } }

以下については、OSC受信でもプログラムと同様に自分の環境に合わせて変更すること:

#define MY_SSID "your_ssid" #define MY_PASSWD "your_passwd" #define TARGET_IP "192.168.0.14" #define OUT_PORT 9000 #define IN_PORT 8000

TARGET_IPは、対向するOSC受信デバイス(touchOSCなど)のIPを設定する。

書き込み後に「picocom /dev/ttyACM0 -b115200」で通信ソフトを起動すると以下のような出力が表示される:

$ picocom /dev/ttyACM0 -b115200 SEND: AT+RST RES: AT+RST OK I (1337827) wifi: state: 5 -> 0 (0) WIFI DISCONNECT rl ----------- SEND: AT+GMR RES: AT+GMR AT version:2.1.0.0-dev(d25f6d2 - Oct 15 2019 12:03:04) SDK version:v3.2-192-g81655f39 compile time(525fbfe):Oct 17 2019 05:39:13 Bin version:2.0.0(WROOM-02) OK ... <省略> ... ----------- ipAddress: 192.168.0.28 AT+CIPSTART=1,"UDP","192.168.0.14",9000,8000,0 RES: AT+CIPSTART=1,"UDP","192.168.0.14",9000,8000,0 local port:8000 change type:0 1,CONNECT OK ----------- #以下、OSC送信のたびに「.」が出力される。 .........................

動作としては、touchOSCのsimpleレイアウトの画面で、最上行の受信アイコンがOSCを受信すると「赤」になり、fader5のfaderが送られてきたOSCの内容に従ってランダムに変化する。

参考情報

TouchOSC - Modular touch control surface for OSC & MIDI
TouchOSC - iPhone
TouchOSC - Android

OSCmsgCode lib:
https://os.mbed.com/users/xshige/code/Sparkfun_CC3000_WiFi_OSCtranceiver//raw-file/e62251d890c1/OSCmsgCodec.h
https://os.mbed.com/users/xshige/code/Sparkfun_CC3000_WiFi_OSCtranceiver//raw-file/e62251d890c1/OSCmsgCodec.cpp
https://os.mbed.com/users/xshige/code/Sparkfun_CC3000_WiFi_OSCtranceiver//file/e62251d890c1/main.cpp/

Wio Lite RISC-V(GD32VF103)

Wio Lite RISC V GD32VF103 with ESP8266
Blink with a Wio Lite RISC-V with ESP8266

PlatformIO Core (CLI)

ESP8266をTCPサーバとTCPクライアントにするATコマンド実例 (1/4)
esp8266_at_command_examples_en.pdf

ESP8266をWifiモデムとして使う - ATコマンドによるMQTT通信
MQTT_via_ESP01
Arduino WiFi library for ESP8266 modules

espressif/ESP8266_AT - examples
Espressif Documentation

以上

続きを読む "Wio_Lite_RISC-VボードでWiFiを動かす(その3:OSC)"

| | コメント (0)

2020年4月19日 (日)

プログラミング言語GOでOSC(Open Sound Control)を動かす

2020/4/19

GO Install OSC

GO Install OSC

概要

プログラミング言語GOでOSC(Open Sound Control)を動かす。実行するプログラムはOSCライブラリのexamplesを流用している。 (ホストPCとしてはubuntuを想定している)

OSCライブラリのインストール

go get github.com/hypebeast/go-osc # 作業用ディレクトリを設定する cd $GOPATH/src mkdir OSC-go cd OSC-go

OSC受信プログラム

OSCRecv_demo.go

package main //import "github.com/hypebeast/go-osc/osc" import ( "fmt" "github.com/hypebeast/go-osc/osc" ) func main() { addr := "0.0.0.0:8000" d := osc.NewStandardDispatcher() d.AddMsgHandler("/1/fader1", func(msg *osc.Message) { osc.PrintMessage(msg) }) d.AddMsgHandler("/1/fader2", func(msg *osc.Message) { osc.PrintMessage(msg) }) d.AddMsgHandler("/1/fader3", func(msg *osc.Message) { osc.PrintMessage(msg) }) d.AddMsgHandler("/1/fader4", func(msg *osc.Message) { osc.PrintMessage(msg) }) d.AddMsgHandler("/1/fader5", func(msg *osc.Message) { osc.PrintMessage(msg) }) d.AddMsgHandler("/1/toggle1", func(msg *osc.Message) { osc.PrintMessage(msg) }) d.AddMsgHandler("/1/toggle2", func(msg *osc.Message) { osc.PrintMessage(msg) }) d.AddMsgHandler("/1/toggle3", func(msg *osc.Message) { osc.PrintMessage(msg) }) d.AddMsgHandler("/1/toggle4", func(msg *osc.Message) { osc.PrintMessage(msg) }) fmt.Println("Listening at:",addr) server := &osc.Server{ Addr: addr, Dispatcher:d, } server.ListenAndServe() }

入力ポートとして8000として、OSC受信する。

OSC送信プログラム

OSCSend_demo.go

package main import "github.com/hypebeast/go-osc/osc" func main() { client := osc.NewClient("localhost", 8000) msg := osc.NewMessage("/1/fader1") msg.Append(int32(111)) msg.Append(true) msg.Append("hello") client.Send(msg) msg1 := osc.NewMessage("/1/fader2") msg1.Append(float32(0.789)) client.Send(msg1) }

出力ポートとして8000として、OSC送信する。

OSC message dump

任意のOSCメッセージを受信できるプログラム:

OSCdump.go

package main import ( "bufio" "fmt" "net" "os" "github.com/hypebeast/go-osc/osc" ) func main() { addr := "0.0.0.0:8000" server := &osc.Server{} conn, err := net.ListenPacket("udp", addr) if err != nil { fmt.Println("Couldn't listen: ", err) } defer conn.Close() fmt.Println("OSC msg Dump") fmt.Println("Press \"q\" to exit") go func() { fmt.Println("Start listening on", addr) for { packet, err := server.ReceivePacket(conn) if err != nil { fmt.Println("Server error: " + err.Error()) os.Exit(1) } if packet != nil { switch packet.(type) { default: fmt.Println("Unknow packet type!") case *osc.Message: fmt.Printf("-- OSC Message: ") osc.PrintMessage(packet.(*osc.Message)) case *osc.Bundle: fmt.Println("-- OSC Bundle:") bundle := packet.(*osc.Bundle) for i, message := range bundle.Messages { fmt.Printf(" -- OSC Message #%d: ", i+1) osc.PrintMessage(message) } } } } }() reader := bufio.NewReader(os.Stdin) for { c, err := reader.ReadByte() if err != nil { os.Exit(0) } if c == 'q' { os.Exit(0) } } }

入力ポートとして8000として、OSC受信する。

build

以下でビルドする:

go build OSCRecv_demo.go go build OSCSend_demo.go go build OSCdump.go

実行と出力例

./OSCRecv_demo # TouchOSCからOSCを受信したときの出力 Listening at: 0.0.0.0:8000 /1/fader1 ,f 0.5864979 /1/fader2 ,f 0.53164554 /1/fader2 ,f 1 /1/fader2 ,f 0.98734176 /1/fader2 ,f 0.9704641 /1/fader3 ,f 0.46202534 ... ./OSCdump # TouchOSCからOSCを受信したときの出力 OSC msg Dump Press "q" to exit Start listening on 0.0.0.0:8000 -- OSC Message: /1/fader2 ,f 0.6413502 -- OSC Message: /3/xy ,ff 0.33333334 0.32771537 -- OSC Message: /3/xy ,ff 0.6479401 0.6479401 -- OSC Message: /2/push6 ,f 1 -- OSC Message: /2/push6 ,f 0 -- OSC Message: /4/multitoggle/6/2 ,f 1 -- OSC Message: /4/multitoggle/6/6 ,f 1 -- OSC Message: /4/multitoggle/3/4 ,f 1 ...

引用情報

Open Sound Control (OSC) library for Golang. Implemented in pure Go.

TouchOSC
TouchOSC iPhone

以上

続きを読む "プログラミング言語GOでOSC(Open Sound Control)を動かす"

| | コメント (0)

2020年4月 9日 (木)

プログラミング言語RustでOSC(Open Sound Control)を動かす

2020/4/9

Rust Install OSC

Rust Install OSC

概要

プログラミング言語RustでOSC(Open Sound Control)を動かす。 ここでは、開発ツールのインストールから説明する。実行するプログラムはOSCライブラリのexamplesを流用している。 (ホストPCとしてはubuntuを想定している)

開発ツールのインストール

mkdir rust_ws cd rust_ws curl https://sh.rustup.rs -sSf | sh

ツールの動作確認:

# プロジェクトhello_cargo を作る cargo new hello_cargo --bin cd hello_cargo # build&run cargo build cargo run # 以下のような出力が出ればOKとなる: Finished dev [unoptimized + debuginfo] target(s) in 0.02s ~/rust_ws/hello_cargo$ cargo run Finished dev [unoptimized + debuginfo] target(s) in 0.02s Running `target/debug/hello_cargo` Hello, world!

OSC受信プログラム

(1)プロジェクトを作る:

cargo new osc_recv --bin cd osc_recv

(2)Cargo.tomlを編集する:
[dependencies]に以下を追加する:

[dependencies] rosc = "~0.3"

(3)src/main.rsを以下のように編集する:

extern crate rosc; use rosc::OscPacket; use std::env; use std::net::{SocketAddrV4, UdpSocket}; use std::str::FromStr; fn main() { let args: Vec<String> = env::args().collect(); let usage = format!("Usage {} IP:PORT", &args[0]); if args.len() < 2 { println!("{}", usage); ::std::process::exit(1) } let addr = match SocketAddrV4::from_str(&args[1]) { Ok(addr) => addr, Err(_) => panic!(usage), }; let sock = UdpSocket::bind(addr).unwrap(); println!("Listening to {}", addr); let mut buf = [0u8; rosc::decoder::MTU]; loop { match sock.recv_from(&mut buf) { Ok((size, addr)) => { println!("Received packet with size {} from: {}", size, addr); let packet = rosc::decoder::decode(&buf[..size]).unwrap(); handle_packet(packet); } Err(e) => { println!("Error receiving from socket: {}", e); break; } } } } fn handle_packet(packet: OscPacket) { match packet { OscPacket::Message(msg) => { println!("OSC msg: {} {:?}", msg.addr, msg.args); } OscPacket::Bundle(bundle) => { println!("OSC Bundle: {:?}", bundle); } } }

(4)build&run

cargo build cargo run 0.0.0.0:8000

入力ポートとして8000として、OSC受信する。

出力例:

# TouchOSCからOSCを受信したときの出力 Received packet with size 20 from: 192.168.0.10:9000 OSC msg: /1/fader1 [Float(0.5843882)] Received packet with size 20 from: 192.168.0.10:9000 OSC msg: /1/fader1 [Float(0.5822785)] Received packet with size 20 from: 192.168.0.10:9000 OSC msg: /1/fader1 [Float(0.5822785)] Received packet with size 20 from: 192.168.0.10:9000 OSC msg: /1/fader1 [Float(0.5801688)] Received packet with size 20 from: 192.168.0.10:9000 OSC msg: /1/fader1 [Float(0.5801688)] # 本記事のOSC_sendのOSCを受信したときの出力 Received packet with size 20 from: 127.0.0.1:9000 OSC msg: /3/xy2 [Float(0.99939775), Float(0.4754662)] Received packet with size 20 from: 127.0.0.1:9000 OSC msg: /3/xy1 [Float(0.5), Float(1.0)] Received packet with size 20 from: 127.0.0.1:9000 OSC msg: /3/xy2 [Float(1.0), Float(0.5)] Received packet with size 20 from: 127.0.0.1:9000 OSC msg: /3/xy1 [Float(0.52453387), Float(0.99939775)] Received packet with size 20 from: 127.0.0.1:9000 OSC msg: /3/xy2 [Float(0.99939775), Float(0.52453387)]

OSC送信プログラム

(1)プロジェクトを作る:

cargo new osc_send --bin cd osc_send

(2)Cargo.tomlを編集する: [dependencies]に以下を追加する:

[dependencies] rosc = "~0.3"

(3)src/main.rsを以下のように編集する:

use rosc::encoder; use rosc::{OscMessage, OscPacket, OscType}; use std::net::{SocketAddrV4, UdpSocket}; use std::str::FromStr; use std::time::Duration; use std::{env, f32, thread}; fn get_addr_from_arg(arg: &str) -> SocketAddrV4 { SocketAddrV4::from_str(arg).unwrap() } fn main() { let args: Vec<String> = env::args().collect(); let usage = format!( "Usage: {} HOST_IP:HOST_PORT CLIENT_IP:CLIENT_PORT", &args[0] ); if args.len() < 3 { panic!(usage); } let host_addr = get_addr_from_arg(&args[1]); let to_addr = get_addr_from_arg(&args[2]); let sock = UdpSocket::bind(host_addr).unwrap(); // switch view let msg_buf = encoder::encode(&OscPacket::Message(OscMessage { addr: "/3".to_string(), args: vec![], })) .unwrap(); sock.send_to(&msg_buf, to_addr).unwrap(); // send random values to xy fields let steps = 128; let step_size: f32 = 2.0 * f32::consts::PI / steps as f32; for i in 0.. { let x = 0.5 + (step_size * (i % steps) as f32).sin() / 2.0; let y = 0.5 + (step_size * (i % steps) as f32).cos() / 2.0; let mut msg_buf = encoder::encode(&OscPacket::Message(OscMessage { addr: "/3/xy1".to_string(), args: vec![OscType::Float(x), OscType::Float(y)], })) .unwrap(); sock.send_to(&msg_buf, to_addr).unwrap(); msg_buf = encoder::encode(&OscPacket::Message(OscMessage { addr: "/3/xy2".to_string(), args: vec![OscType::Float(y), OscType::Float(x)], })) .unwrap(); sock.send_to(&msg_buf, to_addr).unwrap(); thread::sleep(Duration::from_millis(20)); } }

(4)build&run

cargo build cargo run 0.0.0.0:9000 127.0.0.1:8000

出力ポートを8000に設定してOSC送信する。

出力例:

$ cargo run 0.0.0.0:9000 127.0.0.1:8000 Finished dev [unoptimized + debuginfo] target(s) in 0.03s Running `target/debug/osc_send '0.0.0.0:9000' '127.0.0.1:8000'`

参照情報

rosc is an implementation of the OSC 1.0 protocol in pure Rust.

UbuntuにRustをインストールする

TouchOSC
TouchOSC iPhone

以上

続きを読む "プログラミング言語RustでOSC(Open Sound Control)を動かす"

| | コメント (0)

2020年4月 7日 (火)

FT232Hボードにneopixelsを接続しOSC(OpenSoundControl)で制御する

2020/4/7

FT232H OSC neopixels(spi)

FT232H OSC neopixels(spi)

概要

以下のFT232Hボードにneopixelsを接続しOSC(OpenSoundControl)で制御する。

FT232H使用USB⇔GPIO+SPI+I2C変換モジュール
Adafruit FT232H Breakout
PCのpython3でCircuitPythonのAPIを使用する

接続

D1(MOSI)をneopixelsのDINに接続する。

OSCモジュールのインストールと動作確認

Module Install:

pip3 install python-osc

Test Script:
python-osc_test.py

from pythonosc import osc_message_builder from pythonosc import osc_message builder = osc_message_builder.OscMessageBuilder(address="/1/fader1") builder.add_arg(0.76) msg = builder.build() print(msg._dgram) # osc_msg = osc_message.OscMessage(msg._dgram) print(osc_msg.address) print(osc_msg.params)

実行(動作確認):

$ python3 python-osc_test.py #以下が出力されればOK b'/1/fader1\x00\x00\x00,f\x00\x00?B\x8f\\' /1/fader1 [0.7599999904632568]

OSC/neopixelデモ

Module Install:

pip3 install python-osc

FT232Hボードにneopixelsを接続する」にある以下のスクリプトもインストール(コピー)する。
ws2812_spi0_cpy.py
(表示が速いので、Adafruit製でないほうのモジュールを使用する)

Demo Script:
OSC_neopixel_demo.py

# FT232H board # connect D1 to DIN of neopixels from pythonosc import osc_message from socket import socket, AF_INET, SOCK_DGRAM #--------------------------------------------- import math from time import sleep import board #import neopixel_spi as neopixel from ws2812_spi0_cpy import WS2812 #--------------------------------------------- NUM_PIXELS = 64 spi = board.SPI() np = WS2812(led_count=NUM_PIXELS, intensity=1) #------------------------------------------------ HOST = '' PORT = 8000 s = socket(AF_INET, SOCK_DGRAM) s.bind((HOST, PORT)) def np_show(r,g,b,l): pixels = [(0, 0, 0) for i in range(NUM_PIXELS)] for n in range(NUM_PIXELS): if (n < int(l*NUM_PIXELS)): pixels[n] = (r, g, b) #print(pixels) np.show(pixels) r = 0 g = 0 b = 0 l = 1 while True: msg, address = s.recvfrom(128) osc_msg = osc_message.OscMessage(msg) #print(f"message: {osc_msg.address} {osc_msg.params} from: {address}") if osc_msg.address == '/1/fader1': r = int(255*osc_msg.params[0]) if osc_msg.address == '/1/fader2': g = int(255*osc_msg.params[0]) if osc_msg.address == '/1/fader3': b = int(255*osc_msg.params[0]) if osc_msg.address == '/1/fader5': l = osc_msg.params[0] #print('R:{} G:{} B:{} L:{}'.format(r,g,b,l)) np_show(r,g,b,l) sleep(0.01) s.close() #---------------------------------

実行:

$ export BLINKA_FT232H=1 $ python3 OSC_neopixel_demo.py

コントローラとしてTouchOSCを使用する。 fader1,fader2,fader3,fader5が、それぞれr,g,b,lに対応しているので fader1,fader2,fader3でneopixelの色を制御し、neopixelの光る長さを fader5で制御する。制御データを取りこぼしやすいので、faderは、スライドするよりも タップするように操作する。

参照情報

python-osc:
https://python-osc.readthedocs.io/en/latest/index.html
https://github.com/attwad/python-osc.git

TouchOSC | Control reference
Open Sound Control
ESP32のMicroPythonでOSC(Open Sound Control)で通信する

FT232Hボードにneopixelsを接続する

FT232H Blinka PINOUT

CircuitPython Libraries on any Computer with FT232H

FT232H使用USB⇔GPIO+SPI+I2C変換モジュール
Adafruit FT232H Breakout

CircuitPython on Linux and Raspberry Pi
CircuitPython 5.0.x - I2C

以上

続きを読む "FT232Hボードにneopixelsを接続しOSC(OpenSoundControl)で制御する"

| | コメント (0)

2020年2月17日 (月)

CC3200のMicroPythonでOSC(Open Sound Control)受信する

2020/2/17

CC3200 MicroPython OSC recv

CC3200 MicroPython OSC recv

概要

CC3200のMicroPythonでOSC(Open Sound Control)で受信する方法について述べる。これはESP32のもの同様のものであるが、CC3200の場合、floatが実装されていないので、そこが差分になる。float32は、符号1ビット、指数部8ビット、仮数部23ビットで構成されるが、その32ビットが、そのまま整数32ビットに読み替えられる。

準備

ツールをインストール前に環境整備として以下を設定する:
(1)書き込みツール(esptool)のインストール
以下のURLの参照のこと;
esptool.py

(この記事のなかでは使用しない)

(2)ampyのインストール
AMPY_PORTは、自分の環境に合わせる。

pip install adafruit-ampy export AMPY_PORT=/dev/ttyUSB0 export AMPY_BAUD=115200

(3)picocomのインストール

sudo apt-get install picocom

以上のうち、exportしているものは、.bashrcに登録することを勧める。

送信側アプリの準備

ここでは、iPhoneのTouchOSCを使用する。
以下、インストール用URL:
iPhone/iPad TouchOSC
Android TouchOSC

OSCモジュールのインストール

以下の方法でインストールした:
(ホストPC上で以下を行なう)

# OSCモジュールのインストール git clone https://github.com/SpotlightKid/micropython-osc.git cd micropython-osc/uosc ampy mkdir /flash/lib ampy mkdir /flash/lib/uosc ampy put client.py /flash/lib/uosc/client.py ampy put server.py /flash/lib/uosc/server.py ampy put threadedclient.py /flash/lib/uosc/threadedclient.py ampy put common.py /flash/lib/uosc/common.py ampy put socketutil.py /flash/lib/uosc/socketutil.py # インストールしたモジュールの確認 ampy ls /flash/lib/uosc /flash/lib/uosc/client.py /flash/lib/uosc/common.py /flash/lib/uosc/server.py /flash/lib/uosc/socketutil.py /flash/lib/uosc/threadedclient.py

エラー回避した追加モジュールのインストール

# 追加モジュールのインストール ampy put socketutil.py /flash/lib/uosc/OSCmsg.py # インストールしたモジュールの確認 ampy ls /flash/lib/uosc /flash/lib/uosc/OSCmsg.py ...

実際のソース:
OSCmsg.py
(server.pyからエラー回避のためにlogging関連のコードなどを削除したもの)

# -*- coding: utf-8 -*- # # uosc/OSCmsg.py # 2020/2/9: forked from server.py # """ OSCmsg """ from ustruct import unpack from uosc.common import Impulse, to_time MAX_DGRAM_SIZE = 1472 def split_oscstr(msg, offset): end = msg.find(b'\0', offset) return msg[offset:end].decode('utf-8'), (end + 4) & ~0x03 def split_oscblob(msg, offset): start = offset + 4 size = unpack('>I', msg[offset:start])[0] return msg[start:start + size], (start + size + 4) & ~0x03 def parse_timetag(msg, offset): """Parse an OSC timetag from msg at offset.""" return to_time(unpack('>II', msg[offset:offset + 4])) def parse_message(msg, strict=False): args = [] addr, ofs = split_oscstr(msg, 0) if not addr.startswith('/'): raise ValueError("OSC address pattern must start with a slash.") # type tag string must start with comma (ASCII 44) if ofs < len(msg) and msg[ofs:ofs + 1] == b',': tags, ofs = split_oscstr(msg, ofs) tags = tags[1:] else: errmsg = "Missing/invalid OSC type tag string." if strict: raise ValueError(errmsg) else: tags = '' for typetag in tags: size = 0 if typetag in 'ifd': size = 8 if typetag == 'd' else 4 args.append(unpack('>' + typetag, msg[ofs:ofs + size])[0]) elif typetag in 'sS': s, ofs = split_oscstr(msg, ofs) args.append(s) elif typetag == 'b': s, ofs = split_oscblob(msg, ofs) args.append(s) elif typetag in 'rm': size = 4 args.append(unpack('BBBB', msg[ofs:ofs + size])) elif typetag == 'c': size = 4 args.append(chr(unpack('>I', msg[ofs:ofs + size])[0])) elif typetag == 'h': size = 8 args.append(unpack('>q', msg[ofs:ofs + size])[0]) elif typetag == 't': size = 8 args.append(parse_timetag(msg, ofs)) elif typetag in 'TFNI': args.append({'T': True, 'F': False, 'I': Impulse}.get(typetag)) else: raise ValueError("Type tag '%s' not supported." % typetag) ofs += size return (addr, tags, tuple(args)) def parse_bundle(bundle, strict=False): """Parse a binary OSC bundle. Returns a generator which walks over all contained messages and bundles recursively, depth-first. Each item yielded is a (timetag, message) tuple. """ if not bundle.startswith(b'#bundle\0'): raise TypeError("Bundle must start with b'#bundle\\0'.") ofs = 16 timetag = to_time(*unpack('>II', bundle[8:ofs])) while True: if ofs >= len(bundle): break size = unpack('>I', bundle[ofs:ofs + 4])[0] element = bundle[ofs + 4:ofs + 4 + size] ofs += size + 4 if element.startswith(b'#bundle'): for el in parse_bundle(element): yield el else: yield timetag, parse_message(element, strict) def handle_osc(data, src, dispatch=None, strict=False): try: head, _ = split_oscstr(data, 0) if head.startswith('/'): messages = [(-1, parse_message(data, strict))] elif head == '#bundle': messages = parse_bundle(data, strict) except Exception as exc: return try: for timetag, (oscaddr, tags, args) in messages: if dispatch: dispatch(timetag, (oscaddr, tags, args, src)) except Exception as exc: print("Exception in OSC handler: %s", exc)

受信テスト

送信側になるTouchOSCを起動してレイアウトSimpleのPanel#1を表示して 待ち受けにして以下のコマンドを実行する:

# プログラムを書き込む ampy put CC3200_OSCrecvBundled_test.py picocom /dev/ttyUSB0 -b115200 # REPLに入る import CC3200_OSCrecvBundled_test.py # WiFiが接続されIPアドレスが表示される # そのIPアドレスをTouchOSCのHostのIPアドレスとして設定する # TouchOSCの画面のfaderやtoggleを動かすと # そのOSCパケットを受信してREPL画面に表示される

実際のソース:
CC3200_OSCrecvBundled_test.py
以下の部分を自分の環境に合わせる;
MYSSID="your_ssid"
MYKEY="your_passwd"

# CC3200 WiFi setup MYSSID="your_ssid" MYKEY="your_passwd" import machine from network import WLAN # configure the WLAN subsystem in station mode (the default is AP) wlan = WLAN(mode=WLAN.STA) # go for fixed IP settings #wlan.ifconfig(config=('192.168.0.107', '255.255.255.0', '192.168.0.1', '8.8.8.8')) #wlan.scan() # scan for available networks wlan.connect(ssid=MYSSID, auth=(WLAN.WPA2, MYKEY)) while not wlan.isconnected(): pass print(wlan.ifconfig()) #----------------------------- from uosc.OSCmsg import parse_message, parse_bundle, split_oscstr import usocket as socket import network incoming_port = 8000 # get my IP myIP, netmask, gateway, dns = wlan.ifconfig() s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM) #s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind(('0.0.0.0',incoming_port)) print('myIP:'+myIP) print('listening...') s.setblocking(True) while True: data,addr=s.recvfrom(1024) print('received:',data,'from',addr) head, _ = split_oscstr(data, 0) if head.startswith('/'): msg = parse_message(data) print(msg) # '#bundle' does not work # elif head == '#bundle': # for x in parse_bundle(data): # timetag, msg = x # print(msg) print('=======================')

REPL出力例:

>>> import CC3200_OSCrecvBundled_test ('192.168.0.23', '255.255.255.0', '192.168.0.1', '192.168.0.1') myIP:192.168.0.23 listening... received: b'/1/fader1\x00\x00\x00,f\x00\x00\x00\x00\x00\x00' from ('192.168.0.16', 9000) ('/1/fader1', 'f', (0,)) # 0 は、 0.0 に対応している ======================= received: b'/1/fader5\x00\x00\x00,f\x00\x00?\x80\x00\x00' from ('192.168.0.16', 9000) ('/1/fader5', 'f', (1065353216,)) # 1065353216 は、 1.0 に対応している =======================

参考情報

TouchOSC | Control reference
OpenSound Control
ESP32のMicroPythonでOSC(Open Sound Control)で通信する

CC3200 MicroPython examples
CC3200 MicroPython インストール
micropython - Quick reference for the WiPy

ampyを用いたMicroPythonのファイル操作とプログラム実行

以上

続きを読む "CC3200のMicroPythonでOSC(Open Sound Control)受信する"

| | コメント (0)

2020年2月 9日 (日)

ESP32のMicroPythonでOSC(Open Sound Control)で通信する

2020/2/9

ESP32 MicroPython OSC send/recv

ESP32 MicroPython OSC send/recv

概要

ESP32のMicroPythonでOSC(Open Sound Control)で通信する方法について述べる。受信のほうは、足りない内蔵モジュールがあるせいでエラーして動かなかった。エラー回避のためにOSC_messageのデコーダ専用のOSCmsg.pyを作り、それをインストールした。 ここでは、linux環境でのインストール方法について説明する。

準備

ツールをインストール前に環境整備として以下を設定する:
(1)書き込みツール(esptool)のインストール
以下のURLの参照のこと;
esptool.py

(この記事のなかでは使用しない)

(2)ampyのインストール
AMPY_PORTは、自分の環境に合わせる。

pip install adafruit-ampy export AMPY_PORT=/dev/ttyUSB0 export AMPY_BAUD=115200

(3)picocomのインストール

sudo apt-get install picocom

以上のうち、exportしているものは、.bashrcに登録することを勧める。

受信側アプリの準備

ここでは、iPhoneのTouchOSCを使用する。
以下、インストール用URL:
iPhone/iPad TouchOSC
Android TouchOSC

WiFi接続モジュールのインストール

ampy put wlan.py

wlan.py

# wlan.py def wlan_connect(ssid='SSID', password='PASSWD'): import network wlan = network.WLAN(network.STA_IF) if not wlan.active() or not wlan.isconnected(): wlan.active(True) print('connecting to:', ssid) wlan.connect(ssid, password) while not wlan.isconnected(): pass

OSCモジュールのインストール

本来ならば以下をREPLで実行することで該当モジュールのインストールができるはずだが sslでエラーが出るようでインストールできなかった。

# connect wlan SSID="your_ssid" PASSWORD="your_password" from wlan import wlan_connect wlan_connect(SSID, PASSWORD) import uos import uos.mkdir("/lib") #-------------------------- import upip upip.install("micropython-osc")

そこで以下の方法でインストールした:
(ホストPC上で以下を行なう)

# OSCモジュールのインストール git clone https://github.com/SpotlightKid/micropython-osc.git cd micropython-osc/uosc ampy mkdir /lib ampy mkdir /lib/uosc ampy put client.py /lib/uosc/client.py ampy put server.py /lib/uosc/server.py ampy put threadedclient.py /lib/uosc/threadedclient.py ampy put common.py /lib/uosc/common.py ampy put socketutil.py /lib/uosc/socketutil.py # インストールしたモジュールの確認 ampy ls /lib/uosc /lib/uosc/client.py /lib/uosc/common.py /lib/uosc/server.py /lib/uosc/socketutil.py /lib/uosc/threadedclient.py

エラー回避した追加モジュールのインストール

# 追加モジュールのインストール ampy put socketutil.py /lib/uosc/OSCmsg.py # インストールしたモジュールの確認 ampy ls /lib/uosc /lib/uosc/OSCmsg.py ...

実際のソース:
OSCmsg.py
(server.pyからエラー回避のためにlogging関連のコードなどを削除したもの)

# -*- coding: utf-8 -*- # # uosc/OSCmsg.py # 2020/2/9: forked from server.py # """ OSCmsg """ from ustruct import unpack from uosc.common import Impulse, to_time MAX_DGRAM_SIZE = 1472 def split_oscstr(msg, offset): end = msg.find(b'\0', offset) return msg[offset:end].decode('utf-8'), (end + 4) & ~0x03 def split_oscblob(msg, offset): start = offset + 4 size = unpack('>I', msg[offset:start])[0] return msg[start:start + size], (start + size + 4) & ~0x03 def parse_timetag(msg, offset): """Parse an OSC timetag from msg at offset.""" return to_time(unpack('>II', msg[offset:offset + 4])) def parse_message(msg, strict=False): args = [] addr, ofs = split_oscstr(msg, 0) if not addr.startswith('/'): raise ValueError("OSC address pattern must start with a slash.") # type tag string must start with comma (ASCII 44) if ofs < len(msg) and msg[ofs:ofs + 1] == b',': tags, ofs = split_oscstr(msg, ofs) tags = tags[1:] else: errmsg = "Missing/invalid OSC type tag string." if strict: raise ValueError(errmsg) else: tags = '' for typetag in tags: size = 0 if typetag in 'ifd': size = 8 if typetag == 'd' else 4 args.append(unpack('>' + typetag, msg[ofs:ofs + size])[0]) elif typetag in 'sS': s, ofs = split_oscstr(msg, ofs) args.append(s) elif typetag == 'b': s, ofs = split_oscblob(msg, ofs) args.append(s) elif typetag in 'rm': size = 4 args.append(unpack('BBBB', msg[ofs:ofs + size])) elif typetag == 'c': size = 4 args.append(chr(unpack('>I', msg[ofs:ofs + size])[0])) elif typetag == 'h': size = 8 args.append(unpack('>q', msg[ofs:ofs + size])[0]) elif typetag == 't': size = 8 args.append(parse_timetag(msg, ofs)) elif typetag in 'TFNI': args.append({'T': True, 'F': False, 'I': Impulse}.get(typetag)) else: raise ValueError("Type tag '%s' not supported." % typetag) ofs += size return (addr, tags, tuple(args)) def parse_bundle(bundle, strict=False): """Parse a binary OSC bundle. Returns a generator which walks over all contained messages and bundles recursively, depth-first. Each item yielded is a (timetag, message) tuple. """ if not bundle.startswith(b'#bundle\0'): raise TypeError("Bundle must start with b'#bundle\\0'.") ofs = 16 timetag = to_time(*unpack('>II', bundle[8:ofs])) while True: if ofs >= len(bundle): break size = unpack('>I', bundle[ofs:ofs + 4])[0] element = bundle[ofs + 4:ofs + 4 + size] ofs += size + 4 if element.startswith(b'#bundle'): for el in parse_bundle(element): yield el else: yield timetag, parse_message(element, strict) def handle_osc(data, src, dispatch=None, strict=False): try: head, _ = split_oscstr(data, 0) if head.startswith('/'): messages = [(-1, parse_message(data, strict))] elif head == '#bundle': messages = parse_bundle(data, strict) except Exception as exc: return try: for timetag, (oscaddr, tags, args) in messages: if dispatch: dispatch(timetag, (oscaddr, tags, args, src)) except Exception as exc: print("Exception in OSC handler: %s", exc)

送信テスト

受信側になるTouchOSCを起動してレイアウトSimpleのPanel#1を表示して 待ち受けにして以下のコマンドを実行する:

ampy run ESP32_OSCsend_test.py

実行するとOSCのパケットがTouchOSCに届き、TouchOSC画面のfaderやtoggleが動けば テストOKになる。

使用したプログラムは以下になる:
ESP32_OSCsend_test.py

以下の部分は自分のWiFi環境に合わせて変更すること:
SSID = "your_ssid"
PASSWORD = "your_passwd"

以下は、TouchOSCの設定に合わせて修正する:
IP='192.168.0.20' # TouchOSCのLocal IP address
OUT_PORT=9000 # TouchOSCのPort(incomming)

# connect wlan SSID = "your_ssid" PASSWORD = "your_passwd" [ from wlan import wlan_connect wlan_connect(SSID, PASSWORD) #------------------------------------- # test for TouchOSC(Layout:Simple) from uosc.client import Bundle, Client, create_message IP='192.168.0.20' OUT_PORT=9000 osc = Client(IP, OUT_PORT) # panel#1 # fader#1 for n in range(1,100): osc.send('/1/fader1', n/100) for n in range(1,100): osc.send('/1/fader1', 1-n/100) # fader#2 for n in range(1,100): osc.send('/1/fader2', n/100) for n in range(1,100): osc.send('/1/fader2', 1-n/100) # fader#3 for n in range(1,100): osc.send('/1/fader3', n/100) for n in range(1,100): osc.send('/1/fader3', 1-n/100) # fader#4 for n in range(1,100): osc.send('/1/fader4', n/100) for n in range(1,100): osc.send('/1/fader4', 1-n/100) # fader#5 for n in range(1,100): osc.send('/1/fader5', n/100) for n in range(1,100): osc.send('/1/fader5', 1-n/100) # toggles osc.send('/1/toggle1', 1) osc.send('/1/toggle2', 1) osc.send('/1/toggle3', 1) osc.send('/1/toggle4', 1) osc.send('/1/toggle1', 0) osc.send('/1/toggle2', 1) osc.send('/1/toggle3', 0) osc.send('/1/toggle4', 1) #---------------------------------- # end of panel#1

「ampy run」でエラーになって動作しない場合、同じプログラムをREPLでコピー&ペーストして実行すると動作することがある。

受信テスト

送信側になるTouchOSCを起動してレイアウトSimpleのPanel#1を表示して 待ち受けにして以下のコマンドを実行する:

# プログラムを書き込む ampy put ESP32_OSCrecvBundled_test.py picocom /dev/ttyUSB0 -b115200 # REPLに入る import ESP32_OSCrecvBundled_test.py # WiFiが接続されIPアドレスが表示される # そのIPアドレスをTouchOSCのHostのIPアドレスとして設定する # TouchOSCの画面のfaderやtoggleを動かすと # そのOSCパケットを受信してREPL画面に表示される

実際のソース:
ESP32_OSCrecvBundled_test.py

# handling bundled message # connect wlan SSID="your_ssid" PASSWORD="your_password" from wlan import wlan_connect wlan_connect(SSID, PASSWORD) #----------------------------- from uosc.OSCmsg import parse_message, parse_bundle, split_oscstr import usocket as socket import network incoming_port = 8000 sta_if = network.WLAN(network.STA_IF) myIP, netmask, gateway, dns = sta_if.ifconfig() s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind(('0.0.0.0',incoming_port)) print('myIP:'+myIP) print('listening...') s.setblocking(True) while True: data,addr=s.recvfrom(1024) print('received:',data,'from',addr) head, _ = split_oscstr(data, 0) if head.startswith('/'): msg = parse_message(data) print(msg) elif head == '#bundle': for x in parse_bundle(data): timetag, msg = x print(msg) print('=======================')

REPL出力例:

myIP:192.168.0.14 listening... received: b'/1/fader2\x00\x00\x00,f\x00\x00?\x05\xf0\xe0' from ('192.168.0.2', 9000) ('/1/fader2', 'f', (0.5232067,)) ======================= received: b'/1/fader4\x00\x00\x00,f\x00\x00?E!|' from ('192.168.0.2', 9000) ('/1/fader4', 'f', (0.7700422,)) ======================= received: b'/1/fader5\x00\x00\x00,f\x00\x00>\x84HT' from ('192.168.0.2', 9000) ('/1/fader5', 'f', (0.2583643,)) ======================= received: b'/1/fader5\x00\x00\x00,f\x00\x00>\x98D\x85' from ('192.168.0.2', 9000) ('/1/fader5', 'f', (0.2973978,)) ======================= # 以降、TouchOSCの「Option/Bundle Messages」をオンにする received: b'#bundle\x00\xe1\xeb1\x93=[W>\x00\x00\x00\x14/1/fader2\x00\x00\x00,f\x00\x00?9\xc9\xfe' from ('192.168.0.2', 9000) ('/1/fader2', 'f', (0.7257384,)) ======================= received: b'#bundle\x00\xe1\xeb1\x94\x9b\xa7\xda\xa4\x00\x00\x00\x14/1/fader4\x00\x00\x00,f\x00\x00>\x98M\xc6' from ('192.168.0.2', 9000) ('/1/fader4', 'f', (0.2974684,)) ======================= received: b'#bundle\x00\xe1\xeb1\x94\xb8P\x04\xfa\x00\x00\x00\x14/1/fader4\x00\x00\x00,f\x00\x00>\xab\xbf0' from ('192.168.0.2', 9000) ('/1/fader4', 'f', (0.335443,)) =======================

デコーダ関数parse_messageの値の返し方

oscaddr, tag, values = parse_message(data) >>> oscaddr '/1/fader1' >>> tag 'f' >>> values (0.7362869,) >>> values[0] 0.7362869 >>> >>> oscmsg = parse_message(data) >>> oscmsg ('/1/fader1', 'f', (0.7362869,)) >>> oscmsg[0] '/1/fader1' >>> oscmsg[1] 'f' >>> oscmsg[2] (0.7362869,) >>> oscmsg[2][0] 0.7362869 >>>

参考情報

TouchOSC | Control reference
OpenSound Control

ESP32-DevKitC ESP-WROOM-32開発ボード
MicroPython - Quick reference for the ESP32
Streaming Data from ESP32 using MicroPython and MQTT
ampyを用いたMicroPythonのファイル操作とプログラム実行

以上

続きを読む "ESP32のMicroPythonでOSC(Open Sound Control)で通信する"

| | コメント (0)