🗐 電装工芸日記 - 舞台照明機器の製作とか -

能登半島地震で被災された方々にお見舞い申し上げます。

or 管理画面へ

2023年1月 この範囲を時系列順で読む この範囲をファイルに出力する

Icon of admin
 ANSI-CというかLinuxのC言語ではキー入力は「待ち」が基本です。何かキーが入力されるとか、Enterを押されるまで処理が一時停止します。それでも構わないこともありますが、ダメな時もあります。
 WindowsならDxLib.hを使って様々な入力を直に得ることが出来ますが、ANSI-Cの標準ライブラリには同様の物がありません。
 そこで、キー入力があれば受け付け、無いなら飛ばして次に行く処理を作ってみました。キー入力はOSがバッファしているので、これを覗く形です。

Raspberry Pi 4B / Rasbian11_32bit(blueseye) / OS標準gcc
/* ---------------------------------
  リアルタイムにキー入力をチェックする
  --------------------------------- */
/* getcharはキー入力のキャッシュが空だと入力があるまで待つ。
  キャッシュが無いなら無いでそのまま抜けたいが、タイムアウトを設定しても
  抜けない。環境に寄るのかもしれないが、ioctl.hを用いることで
  タイムアウトが実現できた。
*/

/* ライブラリ読み込み */
#include <stdio.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>

/* 定数設定 */
#define SET 1        // ioctl.readのモードをRawにするフラグ
#define RESET 0       // ipctl.readのモードをCanonicalに戻すフラグ

/* プロトタイプ宣言 */
int get_inkey( char *get_char ) ;  // キー入力を取得する関数
  // char *get_char  取得したキーコードを返すポインタ
int set_inkey( int mode_flag ) ;  // キー入力のモードを設定する関数
  // int mode_flag  モードの設定フラグ 1(SET)=Rawモードにする / 0(CLR)=モードを戻す

/* テスト用main */
int main(void) {
  char in_char = 0 ;   // 入力されたキーコードを保存する変数
  int ret_inkey = 0 ;   // get_inkeyからの戻り値を保存する変数
  /* コンソールの設定を初期化 */
  set_inkey( SET ) ;
  /* 無限ループで入力したキーを表示する */
  for(;;) {
    // get_inkeyから1文字取得
    in_char = 0x00 ;          // 事前初期化
    ret_inkey = get_inkey( &in_char ) ; // キー取得
    /* get_inkeyの戻り値を処理 */
    // 戻り値が-1ならエラーなのでループから抜けて終了
    if( ret_inkey == -1 ) {
      break ;
    // エンターキーが押されたならループを抜けて終了
    } else if( in_char == 0x0A ) {
      break ;
    // 入力された文字を出力する・・・表示可能な文字
    } else if( in_char >= 0x20 && in_char <= 0x7F ) {
      printf( " %c<%02X>", in_char, in_char ) ;
    // 入力された文字を出力する・・・表示不可能な文字
    } else if( in_char > 0x00 && in_char <= 0xFF ) {
      printf( " <%02X>", in_char ) ;
    }
    fflush( stdout ) ; // 画面表示のスタックを吐き出す   
  }
  /* 正常終了 */
  printf( "\n" ) ;    // 念のための改行
  set_inkey( RESET ) ;  // コンソールの設定を元に戻す
  return 0 ;
}

/* キー入力を取得する */
int get_inkey( char *get_char) {
  char in_char = 0 ;     // 入力されたキーを保持
  char read_length = 0 ;   // 読み込んだバイト数

  // readを使って標準入力から1文字取得
  read_length = read( 0, &in_char, 1 ) ;
  // read_lengthが-1ならエラー、それ以外なら正常
  if( read_length != -1 ) {  // 正常なので取得値を返す
    *get_char = in_char ;
  } else {          // エラーなので以上通知
    *get_char = 0x00 ;
    set_inkey( RESET ) ;
    return -1 ;
  }
  return 0;
}

/* キー入力のモードを設定する */
int set_inkey( int mode_flag ) {
  static struct termio tty_backup;    // 変更前の設定を保持
  static struct termio tty_change;    // 変更後の設定を保持
  // モードを設定する
  if( mode_flag == SET ) {
    // 現在の設定をスタック
    ioctl(0, TCGETA, &tty_backup);
    tty_change = tty_backup;
    // 設定を変更
    tty_change.c_lflag &= ~( ECHO | ICANON ) ;   // エコーを止め、RAW モードへ変更
    tty_change.c_cc[VMIN]= 0 ;          // 0文字入力された時点で入力を受け取る
    tty_change.c_cc[VTIME] = 0.01 ;        // 何も入力がなくても1msec待つ (1 = 1/10sec)
    ioctl( 0, TCSETAF, &tty_change ) ;       // ここで設定を反映
  // モードを戻す
  } else if( mode_flag == 0 ) {
    // スタックしていた設定に戻す
    ioctl( 0, TCSETAF, &tty_backup ) ;
  }
  return 0;
}


 同じ感じでgetcharを使ってもいいような情報が多かったのですが、RaspberryPiのblueseyeではこの方法でないとキー入力があるまで一時停止してしまいます。
 Pythonでも同様の処理を書いたことがありますが、C言語の方が圧倒的にレスポンスがいいです。
 矢印キーがANSIエスケープシーケンスのコードで取得できたことには驚きましたが、ファンクションキーも含め、getcharでは取得できないこともあるキーも取得出来て満足。
 ただし、シフトキーなどの装飾キーを単体で取得することは出来ません。

#C言語
Icon of admin
 棚上げしていた本業に取り組んでおりますが、ここ10日ほどは残業することも精神的に追われることもない日常を送っているので心身共に回復しております。

 VSCodeのオカゲですが、Pythonと同じ感覚でC言語を使えています。覚えなきゃならないことはまだまだ沢山ありますが、実行ファイルの完成まで1クリックですし、コンパイルエラーの説明がとても丁寧でわかりやすいし、コンパイルの時間もPythonのインタプリタ翻訳と大差ありませんので、方言違いのPythonを書いている様な使い勝手です。相当昔の記憶によりますが、リンカーの処理を勝手にやってくれるだけでも楽ってもんです。
 C言語の取り扱いにはローレベルのコンピュータの知識と摩訶不思議な記述の習得が不可欠ですが、慣れてくるとPythonよりも書いてて楽しいですね。コンピュータを動かしているって実感がPICのアセンブラ並に強いからでしょうか。何をしているかも所要時間もわからない便利なだけのコマンドでハードウェアの制御をするとすべてが遠くに思えて面白くありません。この感覚が変人たる所以かもしれませんが、私の基本はハードウェアを直に動かすPICのアセンブラなんだと改めて思います。

 この先数日は棚上げしていた本業(某劇場の舞台資料)の仕上げをせねばなりませんが、これが終われば半月くらいは自由研究に没頭できそうです。新規のご相談も頂いているので進捗次第ではわかりませんが。。。
 もちろん、息抜きにC言語の学習と試作はしますよ。

#C言語
Icon of admin
 ダンプ表示のライブラリは出来ました。何の変哲もない16進数のカラム表示です。
 データ配列のポインタ、表示データ数、列数、行数、最上行値のポインタなどを引数にして関数を呼ぶと表示します。
 地味ですが、こういったライブラリがあると開発の効率が良くなるのでは?という想いで作ってみました。後でDMXデータを表示する画面を作る際にも同様のことをするので習作でもあります。

 以下オレメモです。
 ANSIエスケープシーケンスにおいてパラメータに変数を用いたい場合。

 // カーソルをx=10,y=5にして文字列を表示する
#define ESC 0x1B
int main(void) {
 int x = 10 ;
 int y = 5 ;
 printf("%c[%d:%dHカーソルを指定の位置に", ESC, x, y ) ;
 return 0 ;
}

 ※ このブログシステムでは#や[が機能文字扱いなので、上記ではこれらを全角文字で書いています。

 などとします。大昔のN-BASICの「LOCATE + PRINT」みたいなことをしています。
 printfの文字列中のエスケープを\eと直で書くと後に続く%dが機能しませんが、#defineで定義したESC(0x1B)を%cで読み込めば機能します。
 もちろん、printfのESCの位置に0x1Bと直書きしてもいいのですが、#defineした方が読みやすいと思います。

 C言語ではこういった不思議な記述が多いです。正規表現的でもありますが、バイナリ的な指向が各所に出てきます。
 アセンブラに慣れた身に違和感はありませんが、理解に苦しむ要素かもしれません。

#C言語
Icon of admin
 本業では社内のサーバーやパソコンのメンテをしていますが、処理の待ち時間でC言語のお勉強。
 時間についてです。
 DMXを扱うには無受信1秒でタイムアウトしなければなりませんし、Delayを構成するには1/30~1/50秒程度のインターバルタイムで受信値をスタックしなければなりません。
 ですので、msec(1/1000)クラスの時間評価が必要です。

 システムが持つ時間情報を扱うには<time.h>を用い、エポック秒やPOSIX時間などと呼ばれる基準日時(1970年1月1日0時0分0秒(UTC))からの経過時間(secとnsec)を使うのがよいと思います。有効な精度は10usec程度の様ですが、今現在書いている処理では十分です。これを処理の節目でスタックして経過時間を評価するのですが、年月日時でパラメータが分かれているよりもトータル秒数の方が扱いが簡単です。23時59分59秒の1秒後に0時0分0秒にならないタイマで継続した時間評価をしたいのでこの方がいいと思います。
 ただ、スタックする変数のバイト長には気を付けないといけません。secもnsecもそれぞれ4バイト数なので評価をするには8バイト数で考えないといけないからです。64bitOSならint型でも8バイト数なことが多いので問題ありませんが、私はRaspberryPiで32bitOSを使っているのでint型は4バイト数になります。対策には8バイト数である「(unsigned) long long int」型を使います。ちょっとクセがある型なので計算やprintfで注意しないといけませんケドね。

#C言語
Icon of admin
 オレメモであります。
 参考になりそうな先達のページ

● UDP/IP通信全体の参考
UDP / IP でパケットの送受信を行う
● broadcast通信の参考
UDPブロードキャスト送信サンプル
● ネットワークデバイスを指定した通信の参考
Ethernetインターフェース(eth0, eth1)を指定してソケットを作成する (Linux, C, Raspberry Pi)
● 受信タイムアウトの参考
selectを使用してタイムアウト付き受信を実現する <= recvもrecvfromも受信があるまでひたすら待ち続けるので、タイムアウトさせることは必須。
UDP通信におけるselectの使い方。 <= recvの例が多い中、recvfromの書式を確認出来る。recvfromを使う理由は送信元アドレスを取得したいから。
● 時間の扱い方
時間情報の取得方法と扱い方 <= これの5ページ目の「POSIX環境」が結構大事。DelayをするにもDMXのタイムアウトをするにもこれが必要。
● ターミナルの行長と行数を取得する
ターミナルのサイズを取得する
ターミナルの幅と高さを得る
● ANSIエスケープシーケンス
ANSIエスケープシーケンス チートシート

 この辺りを参考にすれば「ネットワークデバイスを指定してbroadcastで通信する」が出来そうな気がします。
 ただし、1行々々読み込んで使われている構造体をよく把握することが大切です。構造体の中に必要な情報が隠れていることが多いからです。構造体の中に構造体があったりもするので、十分に読み込まないといけません。

 実験は受信が当面の課題となり、
1)ネットワークデバイスを指定せずArt-Netを受信する。<= とにかく受信する
2)ネットワークデバイスを指定してArt-Netを受信する。<= ネットワークデバイスを制限して受信する。
3)送信元IPアドレスを取得し、IPアドレスをもとに受信値を振り分ける。<= 複数の送信元(卓)がある場合の不確実性排除とミックス処理のためには不可欠。
 といった手順で進めることになろうかと思います。
 受信値を確認するため、習作を兼ね、バイナリをダンプ表示をするライブラリも作ってみましょう。

#C言語 #[Art-Net]
Icon of admin
 RTCを装着したRaspberryPiは正常のようです。
 今後製作する装置にもRTCを装着したいのですが、電池切れをどうやって拾うか思案中です。

 socketの実験は連休中に時間が取れず未対応です。
 Art-Netの受信と送信を1台でこなすにはIPアドレス的には同じゾーンで装置は別という変な構成になります。その上、broadcastで受けてbroadcastで送るというこれもまた変な構成です。ですので、コピペで使える先例がありそうでありません。
 RaspberryPiのPythonでは成功していることですから可能だと思いますが、C言語のsocketの方が設定すべきパラメータが多いため、Pythonには無かった設定をどうするかが難問なのです。難しいというより、「ネットワークデバイスを指定してbroadcastで通信する」という前提の先例が少なく、沢山のデータシートを継ぎハギしないと見えないことが多いために苦心しているところです。

#RaspberryPi #C言語 #[Art-Net]
Icon of admin
 珍しく明日から3連休。すべてを自由に使えるワケではありませんがscocketのテストが出来そう。
 Art-Netを扱える卓に繋いで実験しなければなりませんので場所が限られるのです。

#[Art-Net]
 
Icon of admin
 RaspberryPiにRTC(リアルタイムクロック)を入れているのに起動の度に時間がずれる。
 チェックしたところRTCモジュールの電池がダメになっています。定格3.3vのところ0.6vしかない。
 RTCのチップはDS3231ですが、電池には充電タイプのLIR2032(LRではなくLIR)を使えばいいらしい。
 amazonにあったのでポチる。先ほど届いたので取り付けたところRTCの機能が回復。
 Art-Netエンジンのテストプログラムを書いていて気持ち悪かったのでスッキリしました。

追記
 時刻合わせは
$ sudo ntpdate ntp.nict.jp
 ネットワーク経由で現在時を取得し、
$ sudo hwclock -w
 DS3231に書き込みます。
 この手順をした後、電源を完全に切ってから起動すると時刻が合っているハズです。
 時刻が初期化されてしまうならDS3231の電池がNGかと。

#RaspberryPi
Icon of admin
 秋月電子通商さんでRaspberryPi4Bが販売されていました。
 特需ですぐに無くなってしまう?
 お金は辛いですが、とりあえず3個発注しました。

#RaspberryPi
Icon of admin
 本業が落ち着いたので所属会社の作業部屋を片付けをしています。
 コンセプトマシンで使うオンジナオイルの20Lの空き缶2個の処分で悩む。捨てようと思えば捨てられますが、一般家庭ゴミでは出せませんので面倒。
 ならば、イスにしてしまいましょう。
 上面に丸く切ったコンパネを貼り、床を傷つけないように底面の縁にゴムのエッジ材を取り回しました。すべて余り材とジャンク品で済んだので目先のお金はゼロ。
 しばらくは作業部屋とジャンク材の片付けです。キレイ好きとは真逆ですが、次の作業のためにもやっておかないといけません。

#本業

■当面の課題

桜のライトアップの季節です。花粉症の季節でもあります。
自分は平気ですが、花粉症の部下は死にそうな顔をしています。

編集

■複合検索:

  • 投稿者名:
  • 投稿年月:
  • #タグ:
  • カテゴリ:
  • 出力順序:

■日付検索:

■カレンダー:

2023年1月
1234567
891011121314
15161718192021
22232425262728
293031

■カテゴリ:

■最近の投稿:

最終更新日時:
2024年4月19日(金) 06時10分48秒