2025年6月 この範囲を時系列順で読む この範囲をファイルに出力する
Art-Netの扱いにアイデアを一つ。
送信元を識別するにはIPv4アドレスがキーになると思います。運が悪くなければ送信元毎にユニークなハズです。これの扱い。
IPv4アドレスは4バイト長(32bit)で構成され、文字列で表しても7~15文字です。
これを一つの整数にまとめてしまうアイデアです。一般的にint型は4バイト長ですから、丸めてしまえばint型にしても情報は欠落しません。
こうすれば、4つの数字や7~15文字のテキストよりも扱いが簡単で軽くなると思うのです。IPアドレスとしての情報は別途残すとして、識別IDにこれを使うのです。
ユニバースも同じ考え方でいいでしょう。15bit長ですからshort型でも収まりますが、噂に聞くところではint型の方が処理が軽いらしいのでこの型にしておこうかなと。
この両者を合わせた8バイトのlong型もしくはLongLong型を併記しておいてもいいかもしれません。なぜこの様なことを考えるかと言いますと、複数のユニバースを保管する配列から特定のものを探し出す処理を軽くしたいからです。処理の方針にもよりますが、受信したものを一旦スタックして一定の時間間隔で取り出そうというのが今の考え方ですので、クエリに相当するインデックスを作るのは当然としてもキーワードが簡素なことは重要だと思うのです。一定の時間間隔で作れらたLoop配列ならばその並びが時間情報となりますので、Delay を求めても受信日時と現在日時を比較する必要がありません。
Art-Netのモニターも兼ねたいので、使う使わないはともかく、受信したデータを全て一時保管するつもりです。
かといってモニターのための保管とパッチのための保管を別々にするのは気に入らないので、受信をすべて保管してそこから必要なモノを取り出したいのです。
仮に、送信元8、それぞれ8ユニバースとするなら、最大44fpsとして1秒間に2,816件のスタックをしなければなりません。1パケット当たりのデータ長は540バイトくらいですから1.5MB/秒くらいです。2秒分のスタックをしても3MBです。動画の1フレーム当たりのデータ長は640x480の16bitカラーで1.8MBくらいですので55MB/秒です。動画に比べたらArt-Netの情報量は余裕っしょ。
#[Art-Net] #C言語
送信元を識別するにはIPv4アドレスがキーになると思います。運が悪くなければ送信元毎にユニークなハズです。これの扱い。
IPv4アドレスは4バイト長(32bit)で構成され、文字列で表しても7~15文字です。
これを一つの整数にまとめてしまうアイデアです。一般的にint型は4バイト長ですから、丸めてしまえばint型にしても情報は欠落しません。
こうすれば、4つの数字や7~15文字のテキストよりも扱いが簡単で軽くなると思うのです。IPアドレスとしての情報は別途残すとして、識別IDにこれを使うのです。
ユニバースも同じ考え方でいいでしょう。15bit長ですからshort型でも収まりますが、噂に聞くところではint型の方が処理が軽いらしいのでこの型にしておこうかなと。
この両者を合わせた8バイトのlong型もしくはLongLong型を併記しておいてもいいかもしれません。なぜこの様なことを考えるかと言いますと、複数のユニバースを保管する配列から特定のものを探し出す処理を軽くしたいからです。処理の方針にもよりますが、受信したものを一旦スタックして一定の時間間隔で取り出そうというのが今の考え方ですので、クエリに相当するインデックスを作るのは当然としてもキーワードが簡素なことは重要だと思うのです。一定の時間間隔で作れらたLoop配列ならばその並びが時間情報となりますので、Delay を求めても受信日時と現在日時を比較する必要がありません。
Art-Netのモニターも兼ねたいので、使う使わないはともかく、受信したデータを全て一時保管するつもりです。
かといってモニターのための保管とパッチのための保管を別々にするのは気に入らないので、受信をすべて保管してそこから必要なモノを取り出したいのです。
仮に、送信元8、それぞれ8ユニバースとするなら、最大44fpsとして1秒間に2,816件のスタックをしなければなりません。1パケット当たりのデータ長は540バイトくらいですから1.5MB/秒くらいです。2秒分のスタックをしても3MBです。動画の1フレーム当たりのデータ長は640x480の16bitカラーで1.8MBくらいですので55MB/秒です。動画に比べたらArt-Netの情報量は余裕っしょ。
#[Art-Net] #C言語
しばらく前に書いた Art-Net を扱うコードです。
今だったらこうするのにってところは多いですが、これをたたき台にしようかなと。
表示の都合で一部の文字を全角にしています。コピペしてお使いになる際はご注意ください。オレオレライブラリーやオレオレヘッダーが必要なのでこのソースだけでは動きませんけど、要点の参考になれば幸いかと。
/* --------------
Art-Net Engine
ae_main.c
-------------- */
// 汎用ライブラリをインクルード
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <sys/shm.h>
//#include <netinet/in.h>
//#include <sys/ipc.h>
// 自作ライブラリをインクルード
#include "ae_main.h"
#include "std_inkey.h"
#include "std_dump.h"
// Main
int main( int argc, char *argv〔〕 ) {
// 汎用変数
// int i ;
// int ret = 0 ;
// 共有メモリ変数
ae_sharemem_t *ae_sharemem ;
int ae_sharememsize = sizeof( ae_sharemem_t ) ;
int ae_sharememID ;
//【開発用】共有メモリ領域の確保 上位モジュールが出来たらそちらで作る
ae_sharememID = shmget( IPC_PRIVATE, ae_sharememsize, SHM_R|SHM_W ) ;
// 共有メモリ定義
ae_sharemem = shmat( ae_sharememID, 0, SHM_R|SHM_W ) ;
// nsec時変数
struct timespec now ; // 現在POSIX時を得る構造体
unsigned long long int start_nsec ; // 処理開始nsec時を保存
//【開発用】処理時間変数
unsigned long long int lap_nsec ; // 処理終了nsec時
static unsigned long long int max_nsec = 0 ; // 経過したnsecの最大値
// Art-Net 受信変数
int an_rx ; // 受信socket番号
struct sockaddr_in an_rx_addr ; // 受信先指定構造体
int an_rx_length ; // recvfromで得たデータ長
char an_rx_nic〔〕 = { "eth0" } ; // 受信NICの名称
char an_rx_buf〔 ADMX_BUF_SIZE 〕 ; // 受信バッファ ArtDMXの最大バイト長で設定
socklen_t an_rx_sin_size = ADMX_BUF_SIZE ; // recvfromに渡す受信バッファサイズ
struct sockaddr_in an_rx_from_addr ; // 送信元情報の構造体
artdmx_t artdmx_rx ; // 受信したArtDMXを保存する構造体
// Art-Net 送信変数
int an_tx ; // 送信socket番号
struct sockaddr_in an_tx_addr ; // 送信先指定構造体
char an_tx_nic〔〕 = { "eth1" } ; // 受信NICの名称
int yes = 1 ; // 送信をブロードキャストに設定する際のフラグ変数
artdmx_t artdmx_tx〔 ADMX_TX_UNIVRESES 〕 ; // 送信するArtDMXを保存する構造体配列
char an_tx_buf〔 ADMX_BUF_SIZE 〕 ; // 送信バッファ Art-DMXの最大バイト長で設定
int an_tx_count = 0 ; // 送信ユニバースカウンタ
unsigned long long int an_tx_nsec = 0 ; // 送信nsec時を保存
float an_tx_fps = 29.97 ; // 送信フレームレート
unsigned int an_tx_interval =
( unsigned int )( 1e9 / ADMX_TX_UNIVRESES / an_tx_fps ) ;
// Art-Netの受信ソケットを作成
an_rx = socket( AF_INET, SOCK_DGRAM, 0 ) ; // 受信ソケットのインスタンスを作成
setsockopt( an_rx, SOL_SOCKET, SO_BINDTODEVICE, // 受信NICを指定する
&an_rx_nic, sizeof( an_rx_nic ) );
an_rx_addr.sin_family = AF_INET ; // 受信をUDPに設定
an_rx_addr.sin_port = htons( AN_PORT ) ; // 受信ポート番号を設定
an_rx_addr.sin_addr.s_addr = INADDR_ANY ; // 送信元アドレスを無指定にする
bind( an_rx, ( struct sockaddr * ) &an_rx_addr, // 上記をインスタンスに設定する
sizeof( an_rx_addr ) ) ;
// Art-Netの送信ソケットを作成
an_tx = socket( AF_INET, SOCK_DGRAM, 0 ) ; // 送信ソケットのインスタンスを作成
an_tx_addr.sin_family = AF_INET ; // 送信をUDPに設定
an_tx_addr.sin_port = htons( AN_PORT ) ; // 送信ポート番号を設定
an_tx_addr.sin_addr.s_addr = inet_addr( "255.255.255.255" ) ; // 送信先アドレスを無指定にする
setsockopt( an_tx, SOL_SOCKET, SO_BINDTODEVICE, // 送信NICを指定する
&an_tx_nic, sizeof( an_tx_nic ) ) ;
setsockopt( an_tx, SOL_SOCKET, SO_BROADCAST, // 送信をブロードキャストに設定
( char * )&yes, sizeof( yes ) ) ;
// std_inkey 用変数
int key_code_length = 0 ;
int key_code_length_st = 0 ;
char key_code〔 32 〕 = { 0x00 } ;
char key_code_st〔 32 〕 = { 0x00 } ;
// std_inkey を開始
set_inkey( SET ) ;
// ***** Art-Netの受送信と表示 *****
printf( "\e〔2J\e〔?25l" ) ; // 画面を消去 カーソル非表示 \e〔?25l カーソル非表示
while( strcmp( key_code, _KEY_F12 ) != 0 ) {
// ------ 開始nsec時を取得 ------
clock_gettime( CLOCK_REALTIME, &now ) ; // 現在POSIX時間値取得
start_nsec = ( unsigned long long int )now.tv_sec * 1e9 // unsigned long long int型(8バイト長int)変数に
+ now.tv_nsec ; // 取得値をひとまとめにする(nsec)
// --------- キー入力 ----------
key_code_length = get_inkey( key_code ) ; // キー入力を取得
if( strcmp( key_code, "\x20" ) == 0 ) max_nsec = 0 ; //【開発用】キー入力が〔SP〕なら実行時間計測の最大値をクリア
if( key_code_length > 0 ) { // キー入力を別関数に渡すためのスタック
key_code_length_st = key_code_length ;
memcpy( key_code_st, key_code, sizeof( key_code ) ) ;
}
// --------- 受 信 -----------
memset( an_rx_buf, 0, ADMX_BUF_SIZE ) ; // 受信バッファのクリア
an_rx_length = recvfrom( an_rx, an_rx_buf, ADMX_BUF_SIZE, MSG_DONTWAIT, // an_rx_from_addrには送信元情報が入る
( struct sockaddr * )&an_rx_from_addr, &an_rx_sin_size ) ;
// 受信がある場合
if( an_rx_length > 0 ) { // an_rx_lengthは受信値のバイト長
// 送信元IPアドレスとポートを抽出し、構造体artdmx_rxに保存
inet_ntop( AF_INET, &an_rx_from_addr.sin_addr, // 受信データから送信元IPアドレスを抽出保存
artdmx_rx.senderip, sizeof( artdmx_rx.senderip ) ) ;
strcpy( artdmx_rx.senderip, inet_ntoa( an_rx_from_addr.sin_addr ) ) ;
artdmx_rx.senderport = ntohs( an_rx_from_addr.sin_port ) ; // 受信データから送信元ポートを抽出保存
artdmx_rx.recv_nsec = start_nsec ; // 受信日時を保存
// 受信したArtDMXをデコード
if( artdmx_decode( an_rx_buf, &artdmx_rx ) >= 0 ) // ArtDMXなら送信スタックに保存
memcpy( &artdmx_tx〔 artdmx_rx.universe 〕, // ArtDMX以外なら読み飛ばし
&artdmx_rx, sizeof( artdmx_t ) ) ;
//【開発用】終了nsec時を取得
clock_gettime( CLOCK_REALTIME, &now ) ; // 現在POSIX時間値取得
lap_nsec = ( unsigned long long int )now.tv_sec * 1e9 // unsigned long long int型(8バイト長int)変数に
+ now.tv_nsec - start_nsec ; // 取得値をひとまとめにする(nsec)
if( max_nsec < lap_nsec ) max_nsec = lap_nsec ; // ここまでの最大処理時間を得る
}
// --------- 送 信 -----------
else if( start_nsec - an_tx_nsec > ( unsigned int )( an_tx_interval ) ) {
artdmx_encode( an_tx_buf, &artdmx_tx〔 an_tx_count 〕 ) ; // 送信データをエンコード
sendto( an_tx, an_tx_buf, ADMX_BUF_SIZE, MSG_DONTWAIT, // 送信実行
( struct sockaddr * )&an_tx_addr, sizeof( an_tx_addr ) ) ;
/*
//【開発用】終了nsec時を取得
clock_gettime( CLOCK_REALTIME, &now ) ; // 現在POSIX時間値取得
lap_nsec = ( unsigned long long int )now.tv_sec * 1e9 // unsigned long long int型(8バイト長int)変数に
+ now.tv_nsec - start_nsec ; // 取得値をひとまとめにする(nsec)
if( max_nsec < lap_nsec ) max_nsec = lap_nsec ; // ここまでの最大処理時間を得る
*/
//【開発用】受信データの表示
artdmx_check_disp( &artdmx_tx〔 an_tx_count 〕, // 送信データの仮表示
key_code_st, &key_code_length_st, &lap_nsec, &max_nsec ) ;
// 送信事後処理
an_tx_nsec = start_nsec ; // 送信インターバル・次回用のnsec取得
if( ++an_tx_count > 7 ) an_tx_count = 0 ; // 送信ルートカウンタインクリメント
}
// 待ち時間 無駄にループしても処理負荷が増えるだけなので少しお休み
usleep( 1 ) ;
}
// --------- 終 了 ----------
set_inkey( RESET ) ; // std_inkeyを終了
close( an_rx ) ; // 受信ソケットをクローズ
close( an_tx ) ; // 送信ソケットをクローズ
printf( "\e〔?25h\e〔0m\n" ) ; // コンソール画面を戻す \e〔?25h カーソルの表示
shmdt( ae_sharemem ) ; // 共有メモリの切り離し
//【開発用】上位モジュールが出来たらそちらで処理する
shmctl( ae_sharememID, IPC_RMID, 0) ; // 共有メモリ領域開放
return 0 ;
}
// ArtDMXのデコード関数
int artdmx_decode( char *an_rx_buf, artdmx_t *artdmx_rx ) {
int i ;
char int_st〔 sizeof( int ) 〕 ;
// IDを抽出
for( i = 0; i < 8; i++ ) { // bufの先頭から8バイト
artdmx_rx->id〔 i 〕 = an_rx_buf〔 i 〕 ;
}
// OpCodeを抽出
int_st〔 0 〕 = an_rx_buf〔 8 〕 ;
int_st〔 1 〕 = an_rx_buf〔 9 〕 ;
int_st〔 2 〕 = 0x00 ;
int_st〔 3 〕 = 0x00 ;
artdmx_rx->opcode = *( ( int * )&int_st〔 0 〕) ;
if( ! (strcmp( artdmx_rx->id, "Art-Net" ) == 0 // ArtDMXかチェックし、そうでなければエラーを返して終了
&& artdmx_rx->opcode == 0x5000 ) ) {
return -1 ;
}
// ProVerを抽出
int_st〔 0 〕 = an_rx_buf〔 11 〕 ;
int_st〔 1 〕 = an_rx_buf〔 10 〕 ;
int_st〔 2 〕 = 0x00 ;
int_st〔 3 〕 = 0x00 ;
artdmx_rx->prover = *( ( int * )&int_st〔 0 〕) ;
// Sequenceを抽出
artdmx_rx->sequence = an_rx_buf〔 12 〕 ;
// Physicalを抽出
artdmx_rx->physical = an_rx_buf〔 13 〕 ;
// Universeを抽出
artdmx_rx->subnet = an_rx_buf〔 14 〕 / 0x10 ;
artdmx_rx->universe = an_rx_buf〔 14 〕 % 0x10 ;
artdmx_rx->net = an_rx_buf〔 15 〕 ;
// Lengthを抽出
int_st〔 0 〕 = an_rx_buf〔 17 〕 ;
int_st〔 1 〕 = an_rx_buf〔 16 〕 ;
int_st〔 2 〕 = 0x00 ;
int_st〔 3 〕 = 0x00 ;
artdmx_rx->length = *( ( int * )&int_st〔0〕) ;
// Dataを抽出
for( i = 0; i < artdmx_rx->length; i++ ) {
artdmx_rx->data〔 i 〕 = an_rx_buf〔 i + 18 〕 ;
}
if( artdmx_rx->length < 512 ) {
for( i = artdmx_rx->length; i < 512; i++ ) {
artdmx_rx->data〔 i 〕 = 0x00 ;
}
}
return 0 ;
}
// ArtDMXのエンコード関数
int artdmx_encode( char *an_tx_buf, artdmx_t *artdmx_tx ) {
int i ;
// IDを設定
for( i = 0; i < 8; i++ ) {
an_tx_buf〔 i 〕 = artdmx_tx->id〔 i 〕 ;
}
// OpCodeを設定
an_tx_buf〔 8 〕 = (char)( artdmx_tx->opcode % 0x100 ) ;
an_tx_buf〔 9 〕 = (char)( artdmx_tx->opcode / 0x100 ) ;
// ProVerを設定
an_tx_buf〔 10 〕 = (char)( artdmx_tx->prover / 0x100 ) ;
an_tx_buf〔 11 〕 = (char)( artdmx_tx->prover % 0x100 ) ;
// Sequenceを設定
an_tx_buf〔 12 〕 = (char) artdmx_tx->sequence ;
// Physicalを設定
an_tx_buf〔 13 〕 = (char) artdmx_tx->physical ;
// SubUniを設定
an_tx_buf〔 14 〕 = (char)( artdmx_tx->subnet * 0x100 + artdmx_tx->universe ) ;
// Netを設定
an_tx_buf〔 15 〕 = (char)artdmx_tx->net ;
// Lengthを設定
an_tx_buf〔 16 〕 = (char)( artdmx_tx->length / 0x100 ) ;
an_tx_buf〔 17 〕 = (char)( artdmx_tx->length % 0x100 ) ;
// Dataを設定
memcpy( an_tx_buf + 18, artdmx_tx->data, 512 ) ;
return 0 ;
}
// ArtDMXのチェック用画面表示
int artdmx_check_disp( artdmx_t *artdmx, char *key_code_st, int *key_code_length_st,
unsigned long long int *val1, unsigned long long int *val2 ) {
// 汎用変数
//int i ;
// タイムカウント変数
struct timespec now ; // 現在POSIX時を得る構造体
unsigned long long int now_nsec ; // 現在nsecを保存
static unsigned long long int prev_nsec = 0 ; // 過去nsecを保存
// データ管理用
static artdmx_t artdmx_disp ;
// std_dump 用変数
static int first_line = 0 ; // 表示開始行数
// 表示制御変数
struct winsize ws ;
static int col = 20 ;
static int row = 100 ;
static int net = 0 ;
static int subnet = 0 ;
static int universe = 0 ;
// カーソルキー入力
if( *key_code_length_st > 0 ) {
if( strcmp( key_code_st, _KEY_RIGHT ) == 0 ) {
if( ++universe > 7 ) universe = 7 ;
}
if( strcmp( key_code_st, _KEY_LEFT ) == 0 ) {
if( --universe < 0 ) universe = 0 ;
}
if( strcmp( key_code_st, _KEY_UP ) == 0 ) {
first_line-- ;
}
if( strcmp( key_code_st, _KEY_DOWN ) == 0 ) {
first_line++ ;
}
*key_code_length_st = 0 ;
}
// 対象ユニバースのデータをスタック
if( ( artdmx->net == net ) && ( artdmx->subnet == subnet ) && ( artdmx->universe == universe ) ) {
memcpy( &artdmx_disp, artdmx, sizeof( artdmx_t ) ) ;
}
// タイムカウント
clock_gettime( CLOCK_REALTIME, &now ) ; // 現在POSIX時間値取得
now_nsec = ( unsigned long long int )now.tv_sec * 1e9 // unsigned long long int型(8バイト長int)変数に
+ now.tv_nsec ; // 取得値をひとまとめにする(nsec)
if( now_nsec - prev_nsec < 5e7) return -1 ; // 経過時間が不足なら何もせずリターン
prev_nsec = now_nsec ; // 過去nsecを現在値としておく
// 画面のテキスト表示サイズを取得
ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) ; // 画面サイズを取得 幅=ws.ws_col 高=ws.ws_row
if( row + 4 > ws.ws_row ) row = ws.ws_row - 4 ; // dumpの行数制限
// artdmxを表示
printf( "\e〔0;0H" ) ; // カーソルを左上に
printf( "Sender: %s:%d", artdmx_disp.senderip, artdmx_disp.senderport ) ;
printf( "\e〔28GID: %s", artdmx_disp.id ) ;
printf( "\e〔41GOpCode: 0x%04X", artdmx_disp.opcode ) ;
printf( "\e〔57GProVer: 0x%04X", artdmx_disp.prover ) ;
printf( "\e〔0K\nSequence: 0x%04X", artdmx_disp.sequence ) ;
printf( "\e〔19GPhysical: 0x%04X", artdmx_disp.physical ) ;
printf( "\e〔37GLength: %d", artdmx_disp.length ) ;
printf( "\e〔50G< Net:%d SubNet:%d Universe:%d >\e〔0K\n", artdmx_disp.net, artdmx_disp.subnet, artdmx_disp.universe ) ;
dump_print( artdmx_disp.data, artdmx_disp.length, col, row, &first_line, 0 ) ;
printf( "%8lld /%8lld nsec", *val1, *val2 ) ;
//fflush( stdout ) ;
// 終了
return 0 ;
}
#[Art-Net] #C言語
今だったらこうするのにってところは多いですが、これをたたき台にしようかなと。
表示の都合で一部の文字を全角にしています。コピペしてお使いになる際はご注意ください。オレオレライブラリーやオレオレヘッダーが必要なのでこのソースだけでは動きませんけど、要点の参考になれば幸いかと。
/* --------------
Art-Net Engine
ae_main.c
-------------- */
// 汎用ライブラリをインクルード
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <sys/shm.h>
//#include <netinet/in.h>
//#include <sys/ipc.h>
// 自作ライブラリをインクルード
#include "ae_main.h"
#include "std_inkey.h"
#include "std_dump.h"
// Main
int main( int argc, char *argv〔〕 ) {
// 汎用変数
// int i ;
// int ret = 0 ;
// 共有メモリ変数
ae_sharemem_t *ae_sharemem ;
int ae_sharememsize = sizeof( ae_sharemem_t ) ;
int ae_sharememID ;
//【開発用】共有メモリ領域の確保 上位モジュールが出来たらそちらで作る
ae_sharememID = shmget( IPC_PRIVATE, ae_sharememsize, SHM_R|SHM_W ) ;
// 共有メモリ定義
ae_sharemem = shmat( ae_sharememID, 0, SHM_R|SHM_W ) ;
// nsec時変数
struct timespec now ; // 現在POSIX時を得る構造体
unsigned long long int start_nsec ; // 処理開始nsec時を保存
//【開発用】処理時間変数
unsigned long long int lap_nsec ; // 処理終了nsec時
static unsigned long long int max_nsec = 0 ; // 経過したnsecの最大値
// Art-Net 受信変数
int an_rx ; // 受信socket番号
struct sockaddr_in an_rx_addr ; // 受信先指定構造体
int an_rx_length ; // recvfromで得たデータ長
char an_rx_nic〔〕 = { "eth0" } ; // 受信NICの名称
char an_rx_buf〔 ADMX_BUF_SIZE 〕 ; // 受信バッファ ArtDMXの最大バイト長で設定
socklen_t an_rx_sin_size = ADMX_BUF_SIZE ; // recvfromに渡す受信バッファサイズ
struct sockaddr_in an_rx_from_addr ; // 送信元情報の構造体
artdmx_t artdmx_rx ; // 受信したArtDMXを保存する構造体
// Art-Net 送信変数
int an_tx ; // 送信socket番号
struct sockaddr_in an_tx_addr ; // 送信先指定構造体
char an_tx_nic〔〕 = { "eth1" } ; // 受信NICの名称
int yes = 1 ; // 送信をブロードキャストに設定する際のフラグ変数
artdmx_t artdmx_tx〔 ADMX_TX_UNIVRESES 〕 ; // 送信するArtDMXを保存する構造体配列
char an_tx_buf〔 ADMX_BUF_SIZE 〕 ; // 送信バッファ Art-DMXの最大バイト長で設定
int an_tx_count = 0 ; // 送信ユニバースカウンタ
unsigned long long int an_tx_nsec = 0 ; // 送信nsec時を保存
float an_tx_fps = 29.97 ; // 送信フレームレート
unsigned int an_tx_interval =
( unsigned int )( 1e9 / ADMX_TX_UNIVRESES / an_tx_fps ) ;
// Art-Netの受信ソケットを作成
an_rx = socket( AF_INET, SOCK_DGRAM, 0 ) ; // 受信ソケットのインスタンスを作成
setsockopt( an_rx, SOL_SOCKET, SO_BINDTODEVICE, // 受信NICを指定する
&an_rx_nic, sizeof( an_rx_nic ) );
an_rx_addr.sin_family = AF_INET ; // 受信をUDPに設定
an_rx_addr.sin_port = htons( AN_PORT ) ; // 受信ポート番号を設定
an_rx_addr.sin_addr.s_addr = INADDR_ANY ; // 送信元アドレスを無指定にする
bind( an_rx, ( struct sockaddr * ) &an_rx_addr, // 上記をインスタンスに設定する
sizeof( an_rx_addr ) ) ;
// Art-Netの送信ソケットを作成
an_tx = socket( AF_INET, SOCK_DGRAM, 0 ) ; // 送信ソケットのインスタンスを作成
an_tx_addr.sin_family = AF_INET ; // 送信をUDPに設定
an_tx_addr.sin_port = htons( AN_PORT ) ; // 送信ポート番号を設定
an_tx_addr.sin_addr.s_addr = inet_addr( "255.255.255.255" ) ; // 送信先アドレスを無指定にする
setsockopt( an_tx, SOL_SOCKET, SO_BINDTODEVICE, // 送信NICを指定する
&an_tx_nic, sizeof( an_tx_nic ) ) ;
setsockopt( an_tx, SOL_SOCKET, SO_BROADCAST, // 送信をブロードキャストに設定
( char * )&yes, sizeof( yes ) ) ;
// std_inkey 用変数
int key_code_length = 0 ;
int key_code_length_st = 0 ;
char key_code〔 32 〕 = { 0x00 } ;
char key_code_st〔 32 〕 = { 0x00 } ;
// std_inkey を開始
set_inkey( SET ) ;
// ***** Art-Netの受送信と表示 *****
printf( "\e〔2J\e〔?25l" ) ; // 画面を消去 カーソル非表示 \e〔?25l カーソル非表示
while( strcmp( key_code, _KEY_F12 ) != 0 ) {
// ------ 開始nsec時を取得 ------
clock_gettime( CLOCK_REALTIME, &now ) ; // 現在POSIX時間値取得
start_nsec = ( unsigned long long int )now.tv_sec * 1e9 // unsigned long long int型(8バイト長int)変数に
+ now.tv_nsec ; // 取得値をひとまとめにする(nsec)
// --------- キー入力 ----------
key_code_length = get_inkey( key_code ) ; // キー入力を取得
if( strcmp( key_code, "\x20" ) == 0 ) max_nsec = 0 ; //【開発用】キー入力が〔SP〕なら実行時間計測の最大値をクリア
if( key_code_length > 0 ) { // キー入力を別関数に渡すためのスタック
key_code_length_st = key_code_length ;
memcpy( key_code_st, key_code, sizeof( key_code ) ) ;
}
// --------- 受 信 -----------
memset( an_rx_buf, 0, ADMX_BUF_SIZE ) ; // 受信バッファのクリア
an_rx_length = recvfrom( an_rx, an_rx_buf, ADMX_BUF_SIZE, MSG_DONTWAIT, // an_rx_from_addrには送信元情報が入る
( struct sockaddr * )&an_rx_from_addr, &an_rx_sin_size ) ;
// 受信がある場合
if( an_rx_length > 0 ) { // an_rx_lengthは受信値のバイト長
// 送信元IPアドレスとポートを抽出し、構造体artdmx_rxに保存
inet_ntop( AF_INET, &an_rx_from_addr.sin_addr, // 受信データから送信元IPアドレスを抽出保存
artdmx_rx.senderip, sizeof( artdmx_rx.senderip ) ) ;
strcpy( artdmx_rx.senderip, inet_ntoa( an_rx_from_addr.sin_addr ) ) ;
artdmx_rx.senderport = ntohs( an_rx_from_addr.sin_port ) ; // 受信データから送信元ポートを抽出保存
artdmx_rx.recv_nsec = start_nsec ; // 受信日時を保存
// 受信したArtDMXをデコード
if( artdmx_decode( an_rx_buf, &artdmx_rx ) >= 0 ) // ArtDMXなら送信スタックに保存
memcpy( &artdmx_tx〔 artdmx_rx.universe 〕, // ArtDMX以外なら読み飛ばし
&artdmx_rx, sizeof( artdmx_t ) ) ;
//【開発用】終了nsec時を取得
clock_gettime( CLOCK_REALTIME, &now ) ; // 現在POSIX時間値取得
lap_nsec = ( unsigned long long int )now.tv_sec * 1e9 // unsigned long long int型(8バイト長int)変数に
+ now.tv_nsec - start_nsec ; // 取得値をひとまとめにする(nsec)
if( max_nsec < lap_nsec ) max_nsec = lap_nsec ; // ここまでの最大処理時間を得る
}
// --------- 送 信 -----------
else if( start_nsec - an_tx_nsec > ( unsigned int )( an_tx_interval ) ) {
artdmx_encode( an_tx_buf, &artdmx_tx〔 an_tx_count 〕 ) ; // 送信データをエンコード
sendto( an_tx, an_tx_buf, ADMX_BUF_SIZE, MSG_DONTWAIT, // 送信実行
( struct sockaddr * )&an_tx_addr, sizeof( an_tx_addr ) ) ;
/*
//【開発用】終了nsec時を取得
clock_gettime( CLOCK_REALTIME, &now ) ; // 現在POSIX時間値取得
lap_nsec = ( unsigned long long int )now.tv_sec * 1e9 // unsigned long long int型(8バイト長int)変数に
+ now.tv_nsec - start_nsec ; // 取得値をひとまとめにする(nsec)
if( max_nsec < lap_nsec ) max_nsec = lap_nsec ; // ここまでの最大処理時間を得る
*/
//【開発用】受信データの表示
artdmx_check_disp( &artdmx_tx〔 an_tx_count 〕, // 送信データの仮表示
key_code_st, &key_code_length_st, &lap_nsec, &max_nsec ) ;
// 送信事後処理
an_tx_nsec = start_nsec ; // 送信インターバル・次回用のnsec取得
if( ++an_tx_count > 7 ) an_tx_count = 0 ; // 送信ルートカウンタインクリメント
}
// 待ち時間 無駄にループしても処理負荷が増えるだけなので少しお休み
usleep( 1 ) ;
}
// --------- 終 了 ----------
set_inkey( RESET ) ; // std_inkeyを終了
close( an_rx ) ; // 受信ソケットをクローズ
close( an_tx ) ; // 送信ソケットをクローズ
printf( "\e〔?25h\e〔0m\n" ) ; // コンソール画面を戻す \e〔?25h カーソルの表示
shmdt( ae_sharemem ) ; // 共有メモリの切り離し
//【開発用】上位モジュールが出来たらそちらで処理する
shmctl( ae_sharememID, IPC_RMID, 0) ; // 共有メモリ領域開放
return 0 ;
}
// ArtDMXのデコード関数
int artdmx_decode( char *an_rx_buf, artdmx_t *artdmx_rx ) {
int i ;
char int_st〔 sizeof( int ) 〕 ;
// IDを抽出
for( i = 0; i < 8; i++ ) { // bufの先頭から8バイト
artdmx_rx->id〔 i 〕 = an_rx_buf〔 i 〕 ;
}
// OpCodeを抽出
int_st〔 0 〕 = an_rx_buf〔 8 〕 ;
int_st〔 1 〕 = an_rx_buf〔 9 〕 ;
int_st〔 2 〕 = 0x00 ;
int_st〔 3 〕 = 0x00 ;
artdmx_rx->opcode = *( ( int * )&int_st〔 0 〕) ;
if( ! (strcmp( artdmx_rx->id, "Art-Net" ) == 0 // ArtDMXかチェックし、そうでなければエラーを返して終了
&& artdmx_rx->opcode == 0x5000 ) ) {
return -1 ;
}
// ProVerを抽出
int_st〔 0 〕 = an_rx_buf〔 11 〕 ;
int_st〔 1 〕 = an_rx_buf〔 10 〕 ;
int_st〔 2 〕 = 0x00 ;
int_st〔 3 〕 = 0x00 ;
artdmx_rx->prover = *( ( int * )&int_st〔 0 〕) ;
// Sequenceを抽出
artdmx_rx->sequence = an_rx_buf〔 12 〕 ;
// Physicalを抽出
artdmx_rx->physical = an_rx_buf〔 13 〕 ;
// Universeを抽出
artdmx_rx->subnet = an_rx_buf〔 14 〕 / 0x10 ;
artdmx_rx->universe = an_rx_buf〔 14 〕 % 0x10 ;
artdmx_rx->net = an_rx_buf〔 15 〕 ;
// Lengthを抽出
int_st〔 0 〕 = an_rx_buf〔 17 〕 ;
int_st〔 1 〕 = an_rx_buf〔 16 〕 ;
int_st〔 2 〕 = 0x00 ;
int_st〔 3 〕 = 0x00 ;
artdmx_rx->length = *( ( int * )&int_st〔0〕) ;
// Dataを抽出
for( i = 0; i < artdmx_rx->length; i++ ) {
artdmx_rx->data〔 i 〕 = an_rx_buf〔 i + 18 〕 ;
}
if( artdmx_rx->length < 512 ) {
for( i = artdmx_rx->length; i < 512; i++ ) {
artdmx_rx->data〔 i 〕 = 0x00 ;
}
}
return 0 ;
}
// ArtDMXのエンコード関数
int artdmx_encode( char *an_tx_buf, artdmx_t *artdmx_tx ) {
int i ;
// IDを設定
for( i = 0; i < 8; i++ ) {
an_tx_buf〔 i 〕 = artdmx_tx->id〔 i 〕 ;
}
// OpCodeを設定
an_tx_buf〔 8 〕 = (char)( artdmx_tx->opcode % 0x100 ) ;
an_tx_buf〔 9 〕 = (char)( artdmx_tx->opcode / 0x100 ) ;
// ProVerを設定
an_tx_buf〔 10 〕 = (char)( artdmx_tx->prover / 0x100 ) ;
an_tx_buf〔 11 〕 = (char)( artdmx_tx->prover % 0x100 ) ;
// Sequenceを設定
an_tx_buf〔 12 〕 = (char) artdmx_tx->sequence ;
// Physicalを設定
an_tx_buf〔 13 〕 = (char) artdmx_tx->physical ;
// SubUniを設定
an_tx_buf〔 14 〕 = (char)( artdmx_tx->subnet * 0x100 + artdmx_tx->universe ) ;
// Netを設定
an_tx_buf〔 15 〕 = (char)artdmx_tx->net ;
// Lengthを設定
an_tx_buf〔 16 〕 = (char)( artdmx_tx->length / 0x100 ) ;
an_tx_buf〔 17 〕 = (char)( artdmx_tx->length % 0x100 ) ;
// Dataを設定
memcpy( an_tx_buf + 18, artdmx_tx->data, 512 ) ;
return 0 ;
}
// ArtDMXのチェック用画面表示
int artdmx_check_disp( artdmx_t *artdmx, char *key_code_st, int *key_code_length_st,
unsigned long long int *val1, unsigned long long int *val2 ) {
// 汎用変数
//int i ;
// タイムカウント変数
struct timespec now ; // 現在POSIX時を得る構造体
unsigned long long int now_nsec ; // 現在nsecを保存
static unsigned long long int prev_nsec = 0 ; // 過去nsecを保存
// データ管理用
static artdmx_t artdmx_disp ;
// std_dump 用変数
static int first_line = 0 ; // 表示開始行数
// 表示制御変数
struct winsize ws ;
static int col = 20 ;
static int row = 100 ;
static int net = 0 ;
static int subnet = 0 ;
static int universe = 0 ;
// カーソルキー入力
if( *key_code_length_st > 0 ) {
if( strcmp( key_code_st, _KEY_RIGHT ) == 0 ) {
if( ++universe > 7 ) universe = 7 ;
}
if( strcmp( key_code_st, _KEY_LEFT ) == 0 ) {
if( --universe < 0 ) universe = 0 ;
}
if( strcmp( key_code_st, _KEY_UP ) == 0 ) {
first_line-- ;
}
if( strcmp( key_code_st, _KEY_DOWN ) == 0 ) {
first_line++ ;
}
*key_code_length_st = 0 ;
}
// 対象ユニバースのデータをスタック
if( ( artdmx->net == net ) && ( artdmx->subnet == subnet ) && ( artdmx->universe == universe ) ) {
memcpy( &artdmx_disp, artdmx, sizeof( artdmx_t ) ) ;
}
// タイムカウント
clock_gettime( CLOCK_REALTIME, &now ) ; // 現在POSIX時間値取得
now_nsec = ( unsigned long long int )now.tv_sec * 1e9 // unsigned long long int型(8バイト長int)変数に
+ now.tv_nsec ; // 取得値をひとまとめにする(nsec)
if( now_nsec - prev_nsec < 5e7) return -1 ; // 経過時間が不足なら何もせずリターン
prev_nsec = now_nsec ; // 過去nsecを現在値としておく
// 画面のテキスト表示サイズを取得
ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) ; // 画面サイズを取得 幅=ws.ws_col 高=ws.ws_row
if( row + 4 > ws.ws_row ) row = ws.ws_row - 4 ; // dumpの行数制限
// artdmxを表示
printf( "\e〔0;0H" ) ; // カーソルを左上に
printf( "Sender: %s:%d", artdmx_disp.senderip, artdmx_disp.senderport ) ;
printf( "\e〔28GID: %s", artdmx_disp.id ) ;
printf( "\e〔41GOpCode: 0x%04X", artdmx_disp.opcode ) ;
printf( "\e〔57GProVer: 0x%04X", artdmx_disp.prover ) ;
printf( "\e〔0K\nSequence: 0x%04X", artdmx_disp.sequence ) ;
printf( "\e〔19GPhysical: 0x%04X", artdmx_disp.physical ) ;
printf( "\e〔37GLength: %d", artdmx_disp.length ) ;
printf( "\e〔50G< Net:%d SubNet:%d Universe:%d >\e〔0K\n", artdmx_disp.net, artdmx_disp.subnet, artdmx_disp.universe ) ;
dump_print( artdmx_disp.data, artdmx_disp.length, col, row, &first_line, 0 ) ;
printf( "%8lld /%8lld nsec", *val1, *val2 ) ;
//fflush( stdout ) ;
// 終了
return 0 ;
}
#[Art-Net] #C言語
TaitanやETCでは正常なのにMA系の卓からの出力を正常に受信出来ないノードがあります。MAの卓は値が変化している最中は30fpsくらいですが、値の変化が無いと10fps位に落ちるのでコレが原因かなと妄想しています。DoctorMXでの計測ですが、レガシーDMXだけでなくArt-Netもしかりです。
原因は定かではありませんが繋がらないのは困ります。ArtNetPatchではどうしましょう。
fpsを一定にすることは外さないとして、ArtNetPatchの処理フェーズにも関係しそうな気がしてます。
#[Art-Net]
原因は定かではありませんが繋がらないのは困ります。ArtNetPatchではどうしましょう。
fpsを一定にすることは外さないとして、ArtNetPatchの処理フェーズにも関係しそうな気がしてます。
#[Art-Net]
ArtNetPatch は一時スタックに格納した後にどう処理するかが課題です。
一定以上の処理速度を確保しなければなりません。DMX512 は最大44fps ですので1フェーズあたり23msec.以下で動かす必要があります。4卓、各8ユニバースと想定するなら、受信1パケットあたりの総処理時間を710usec.以下にしなければなりません。RaspberryPi でも一時スタックに格納するまでの所要時間は150usec.くらいかと予想しますが、全体の処理が納まるかは感覚として微妙です。
Delay の扱いをどうするが鍵です。Delay を構成するには FIFO と呼ばれるループ型のスタックを使いますが、Art-Net を受信する度に FIFO に持って行くか、一旦一時スタックに留めて一定間隔で FIFO に持って行くか悩んでいます。前者ならソースコードは簡単ですがパケットの受信時刻を評価する回数が多く重くなり、後者ならソースコードが複雑になりますがスタックポインタの現在値からの単純なオフセットで処理出来るので軽くなります。簡単で重いか、複雑で軽いか、この両者の良し悪しは簡単には決められません。
後工程を考えずに前工程を決めてしまうと難儀することがあるので、工程全体をある程度イメージしてから進めなければなりません。
#[Art-Net]
一定以上の処理速度を確保しなければなりません。DMX512 は最大44fps ですので1フェーズあたり23msec.以下で動かす必要があります。4卓、各8ユニバースと想定するなら、受信1パケットあたりの総処理時間を710usec.以下にしなければなりません。RaspberryPi でも一時スタックに格納するまでの所要時間は150usec.くらいかと予想しますが、全体の処理が納まるかは感覚として微妙です。
Delay の扱いをどうするが鍵です。Delay を構成するには FIFO と呼ばれるループ型のスタックを使いますが、Art-Net を受信する度に FIFO に持って行くか、一旦一時スタックに留めて一定間隔で FIFO に持って行くか悩んでいます。前者ならソースコードは簡単ですがパケットの受信時刻を評価する回数が多く重くなり、後者ならソースコードが複雑になりますがスタックポインタの現在値からの単純なオフセットで処理出来るので軽くなります。簡単で重いか、複雑で軽いか、この両者の良し悪しは簡単には決められません。
後工程を考えずに前工程を決めてしまうと難儀することがあるので、工程全体をある程度イメージしてから進めなければなりません。
#[Art-Net]
ArtNetPatch は受信データを得るところまで考えてみました。
卓が1枚でユニバースも指定ならシンプルですが、有効なすべての卓とすべてのユニバースを得たいので少し複雑になりそうです。
構成の概略はこんな感じ。
Art-Net
↓
[Art-Net受信] UDP/IP、ポート番号0x1936のブロードキャストを受信。
↓
[IPアドレス、受信時刻、パケットを取得]
↓
[Art-Netフィルタ] パケットのIDを見てArt-Netパケットか判断。そうでないなら破棄しBreak。
↓
[Art-Net送信機リストに登録] IPアドレスをキーワードに送信機をリストに登録。
↓
[OpCodeフィルタ] Art-NetパケットのOpCodeで振り分け。とりあえずはArtDmxだけ扱い、その他なら破棄しBreak。
↓
[ArtDmxデコーダ] ArtDmxを変換し構造体に保存
↓
Loop
さらに別プロセスに送信機リストとArtDmxを表示する画面も作り、共有メモリとセマフォの機能を習得します。
#[Art-Net]
卓が1枚でユニバースも指定ならシンプルですが、有効なすべての卓とすべてのユニバースを得たいので少し複雑になりそうです。
構成の概略はこんな感じ。
Art-Net
↓
[Art-Net受信] UDP/IP、ポート番号0x1936のブロードキャストを受信。
↓
[IPアドレス、受信時刻、パケットを取得]
↓
[Art-Netフィルタ] パケットのIDを見てArt-Netパケットか判断。そうでないなら破棄しBreak。
↓
[Art-Net送信機リストに登録] IPアドレスをキーワードに送信機をリストに登録。
↓
[OpCodeフィルタ] Art-NetパケットのOpCodeで振り分け。とりあえずはArtDmxだけ扱い、その他なら破棄しBreak。
↓
[ArtDmxデコーダ] ArtDmxを変換し構造体に保存
↓
Loop
さらに別プロセスに送信機リストとArtDmxを表示する画面も作り、共有メモリとセマフォの機能を習得します。
#[Art-Net]
急ぎの用件が無かったので台車を仕上げました。仕掛品が邪魔だったからです。
しかし、気が付くと昼飯を除き約10時間ほど作業を続けてしまい体がヤバイ・・・。
半世紀以上使ってきた体ですからもうちっと労わらんとイカンですね。
ただ、こういう作業をした後のお風呂は妙に気持ちいい(笑
#器具の製作
しかし、気が付くと昼飯を除き約10時間ほど作業を続けてしまい体がヤバイ・・・。
半世紀以上使ってきた体ですからもうちっと労わらんとイカンですね。
ただ、こういう作業をした後のお風呂は妙に気持ちいい(笑
#器具の製作
2025年5月 この範囲を時系列順で読む この範囲をファイルに出力する
アタマがヒマな時にArtNetPatch の処理構成を考えています。
今日は現地照明の卓番でしたから時間はありました。居なきゃいけない、トラブル無ければやることない、もちろん寝てもいけない。こんな置きダヌキは妄想にふけるに限ります。
今のところ、Art-Net に流れているデータを表示するモニターから作ってみようかと。必要になるテクニックのほとんどを使うことになるので習作になりますし処理負荷が見えます。卓を何枚相手に出来るか、何ユニバース相手に出来るかの検討材料になります。
RaspberryPi-CM4 を母体に使う予定ですが、高性能を求めるのではなく RaspberryPi で作れる範囲の最高スペックを形にすることが課題です。昨年の試作では RaspberryPi4で卓3枚、入力合計12ユニバース、出力4ユニバースくらいならいけそうでした。ミキサーとしても使えるプロファイルカーブ・ディレイ付きのパッチマシンが欲しいワケですが、自分の仕事ではこの規模で御の字です。RaspberryPi を使う理由は安くて小さくて電気喰わなくて PIC などのマイコンと協調しやすいからです。PCレベルのマザーボードにSPIやI2Cが付いていればそれでもいいのですけど、それが期待できる工業用のマザーボードは一般のPCマザーボードに比べてお高いのです。
RaspberryPi上のDebianベースで作っておけば、ほぼそのまま工業用マザーボードに持って行ける期待感があります。Windowsベースでは難しいことですが、Debian(Linux)ベースでの製作はメリットが多いのです。
#[Art-net]
今日は現地照明の卓番でしたから時間はありました。居なきゃいけない、トラブル無ければやることない、もちろん寝てもいけない。こんな置きダヌキは妄想にふけるに限ります。
今のところ、Art-Net に流れているデータを表示するモニターから作ってみようかと。必要になるテクニックのほとんどを使うことになるので習作になりますし処理負荷が見えます。卓を何枚相手に出来るか、何ユニバース相手に出来るかの検討材料になります。
RaspberryPi-CM4 を母体に使う予定ですが、高性能を求めるのではなく RaspberryPi で作れる範囲の最高スペックを形にすることが課題です。昨年の試作では RaspberryPi4で卓3枚、入力合計12ユニバース、出力4ユニバースくらいならいけそうでした。ミキサーとしても使えるプロファイルカーブ・ディレイ付きのパッチマシンが欲しいワケですが、自分の仕事ではこの規模で御の字です。RaspberryPi を使う理由は安くて小さくて電気喰わなくて PIC などのマイコンと協調しやすいからです。PCレベルのマザーボードにSPIやI2Cが付いていればそれでもいいのですけど、それが期待できる工業用のマザーボードは一般のPCマザーボードに比べてお高いのです。
RaspberryPi上のDebianベースで作っておけば、ほぼそのまま工業用マザーボードに持って行ける期待感があります。Windowsベースでは難しいことですが、Debian(Linux)ベースでの製作はメリットが多いのです。
#[Art-net]
電装工芸の本体ページを見たら昨日のアクセス数が564でした。これまでは多くても100前後でしたので驚いた。自分で言うのもナンですが誰が見てんだ?
#雑記
#雑記
この後しばらく日程が緩いので、仕掛かり品の台車とDMX切替機の仕上げをする予定です。
DMX切替機はフェスなどで複数の調光卓を使う際に卓を選択する装置です。DMX1ユニバースの製品なので複数ユニバース使う場合に複数を1台の選択スイッチに連動する機能とHOLD機能の追加です。
HOLDは自分が未熟だったのもありますが、2kBのメモリを持ったPIC16を安価に入手出来る様になってようやく作れるようになったのです。DMXの512バイトのスタックは不整合を防止するには2面必要ですが、PIC16系のメモリは最近まで1024バイトが頭打ちだったので手が止まっていました。こんなPICマイコンが200円以下で買えるのですから良い時代になったものです。
あとは、出来ることならArt-Netパッチの製作も進めたいですね・・・。パッチ、プロファイルカーブ、ディレイが主題ですが、RaspberryPiでもC言語でプログラムすれば8ユニバースくらい扱えそうです。DMXミックスやアクティブにするArt-Netの入力経路の選択機能も入れる予定なので対象調光卓の選択も可能です。私の感覚で全てのことが出来るパッチマシンが目標です。こんなのを1万円程度で買えるRaspberryPi上で動かしたらメーカーさんに嫌がられそうですが、カスタマーサポートをしきれないので自社使用と理解してくれる同業者にしか売るつもりはありません。カスタマーサポートがどんだけ大変で面倒かを体験するとどんだけ便利で間違いなく売れるモノを作っても自分で使う範囲に留めたくなるのです。50万円の粗利があっても100万円分の手間がかかっては損得以前に心が折れます。
みなさん、メーカーさんや販売店さんに過剰な要求をしてませんか?お客さま、購買者は神様ではありません。三波春夫先生の聴衆に対するリップサービスであって、売る側が買う側に忖度する常識論・責任論ではありません。適正な対価も払わんで文句を言うだけならクレーマーであってお客ぢゃありません。持ちつ持たれつの同格の気持ちをどうかお忘れになりませんように。。。
#器具の製作
DMX切替機はフェスなどで複数の調光卓を使う際に卓を選択する装置です。DMX1ユニバースの製品なので複数ユニバース使う場合に複数を1台の選択スイッチに連動する機能とHOLD機能の追加です。
HOLDは自分が未熟だったのもありますが、2kBのメモリを持ったPIC16を安価に入手出来る様になってようやく作れるようになったのです。DMXの512バイトのスタックは不整合を防止するには2面必要ですが、PIC16系のメモリは最近まで1024バイトが頭打ちだったので手が止まっていました。こんなPICマイコンが200円以下で買えるのですから良い時代になったものです。
あとは、出来ることならArt-Netパッチの製作も進めたいですね・・・。パッチ、プロファイルカーブ、ディレイが主題ですが、RaspberryPiでもC言語でプログラムすれば8ユニバースくらい扱えそうです。DMXミックスやアクティブにするArt-Netの入力経路の選択機能も入れる予定なので対象調光卓の選択も可能です。私の感覚で全てのことが出来るパッチマシンが目標です。こんなのを1万円程度で買えるRaspberryPi上で動かしたらメーカーさんに嫌がられそうですが、カスタマーサポートをしきれないので自社使用と理解してくれる同業者にしか売るつもりはありません。カスタマーサポートがどんだけ大変で面倒かを体験するとどんだけ便利で間違いなく売れるモノを作っても自分で使う範囲に留めたくなるのです。50万円の粗利があっても100万円分の手間がかかっては損得以前に心が折れます。
みなさん、メーカーさんや販売店さんに過剰な要求をしてませんか?お客さま、購買者は神様ではありません。三波春夫先生の聴衆に対するリップサービスであって、売る側が買う側に忖度する常識論・責任論ではありません。適正な対価も払わんで文句を言うだけならクレーマーであってお客ぢゃありません。持ちつ持たれつの同格の気持ちをどうかお忘れになりませんように。。。
#器具の製作
修理した Antari F1-FAZER を小ぶりな野外フェスで使っています。風には負けますが、煙量はアホかと思うくらい。
まだ確信の段階ではありませんが、使用の度に洗浄液を通すのは効果がありそうです。
#器具の修理
まだ確信の段階ではありませんが、使用の度に洗浄液を通すのは効果がありそうです。
#器具の修理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132