Raspberry PiでIoTなシステム開発:libconfigを使ってみる

Cから作ったバイナリの挙動をコンパイルせずに変えたいので、コンフィギュレーションファイルを読み込む仕様にしようと思いたちましが。が、自分でパーサーを書くのは面倒くさい。そこで調べてみたのですが、この類のパーサーは山ほど有るんですね。とりあえず頻出になっているlibconfigを試用してみました。

インストール

マニュアルに何故か普通のインストール手順が書かれていません。半分推測でインストールしてみました。apt-getには無かったです。

pi@raspberrypi:~ $ wget http://www.hyperrealm.com/libconfig/libconfig-1.5.tar.gzpi@raspberrypi:~ $ tar zxvf libconfig-1.5.tar.gz pi@raspberrypi:~ $ cd libconfig-1.5/pi@raspberrypi:~/libconfig-1.5 $ ./configurepi@raspberrypi:~/libconfig-1.5 $ makepi@raspberrypi:~/libconfig-1.5 $ sudo make install

サンプルファイルの実行

最初のmakeで既にビルドが終わっていました。

pi@raspberrypi:~ $ cd libconfig-1.5/examples/c/pi@raspberrypi:~/libconfig-1.5/examples/c $ ./example1Store name: Books, Movies & MoreTITLE                           AUTHOR                           PRICE   QTYTreasure Island                 Robert Louis Stevenson          $ 29.99    5Snow Crash                      Neal Stephenson                 $  9.99    8TITLE                           MEDIA        PRICE   QTYBrazil                          DVD         $ 19.99   11The City of Lost Children       DVD         $ 18.99    5Memento                         Blu-Ray     $ 24.99   20

サンプルのコンフィギュレーションファイルの中身がこちら。

// An example configuration file that stores information about a store.// Basic store information:name = ""Books, Movies & More"";// Store inventory:inventory ={  books = ( { title  = ""Treasure Island"";              author = ""Robert Louis Stevenson"";              price  = 29.99;              qty    = 5; },            { title  = ""Snow Crash"";              author = ""Neal Stephenson"";              price  = 9.99;              qty    = 8; }          );  movies = ( { title = ""Brazil"";               media = ""DVD"";               price = 19.99;               qty = 11; },             { title = ""The City of Lost Children"";               media = ""DVD"";               price = 18.99;               qty = 5; },             { title = ""Memento"";               media = ""Blu-Ray"";               price = 24.99;               qty = 20;             },             { title = ""Howard the Duck""; }           );};// Store hours:hours ={  mon = { open =  9; close = 18; };  tue = { open =  9; close = 18; };  wed = { open =  9; close = 18; };  thu = { open =  9; close = 18; };  fri = { open =  9; close = 20; };  sat = { open =  9; close = 20; };  sun = { open = 11; close = 16; };};

ファイルが存在しない場合のエラー処理

ファイルが無い場合のエラーに対応できます。

  config_t cfg;  config_setting_t *setting;  const char *str;  config_init(&cfg);  /* Read the file. If there is an error, report it and exit. */  if(! config_read_file(&cfg, ""example.cfg""))  {    fprintf(stderr, ""%s:%d - %s\n"", config_error_file(&cfg),            config_error_line(&cfg), config_error_text(&cfg));    config_destroy(&cfg);    return(EXIT_FAILURE);  }

ファイル名を変えてみたらこんな感じに。

pi@raspberrypi:~/libconfig-1.5/examples/c $ lsexample1    example1.o       example2    example2.o       example3    example3.o       example.cfg  Makefile.amexample1.c  example1.vcproj  example2.c  example2.vcproj  example3.c  example3.vcproj  Makefile     Makefile.inpi@raspberrypi:~/libconfig-1.5/examples/c $ mv example.cfg example.gggpi@raspberrypi:~/libconfig-1.5/examples/c $ lsexample1    example1.o       example2    example2.o       example3    example3.o       example.ggg  Makefile.amexample1.c  example1.vcproj  example2.c  example2.vcproj  example3.c  example3.vcproj  Makefile     Makefile.inpi@raspberrypi:~/libconfig-1.5/examples/c $ ./example1(null):0 - file I/O error

単一キーを探して値を取得

シンプルな記法ですね。配列ではない要素を取得する場合はconfig_lookup_string()。

  /* Get the store name. */  if(config_lookup_string(&cfg, ""name"", &str))    printf(""Store name: %s\n\n"", str);  else    fprintf(stderr, ""No 'name' setting in configuration file.\n"");

cfgの中のnameをnamuに変えてみると。

pi@raspberrypi:~/libconfig-1.5/examples/c $ ./example1No 'name' setting in configuration file.

連想配列である多次元配列

連想配列と通常の配列の多次元配列を取得。配列ではないデータはconfig_t型から直接参照していたけれども、配列は一次の名称で指定してconfig_setting_t型の変数をセットし、これをキーにしてデータを取得する。多次元配列なのでconfig_setting_t型の変数をまたセットして、中身にアクセスする。config_setting_length()で要素数が取得できるので繰り返し。配列中の行数で取得する場合はconfig_setting_get_elem()、要素名で取得する場合はconfig_setting_lookup_string()。

  /* Output a list of all books in the inventory. */  setting = config_lookup(&cfg, ""inventory.books"");  if(setting != NULL)  {    int count = config_setting_length(setting);    int i;    printf(""%-30s  %-30s   %-6s  %s\n"", ""TITLE"", ""AUTHOR"", ""PRICE"", ""QTY"");    for(i = 0; i < count; ++i)    {      config_setting_t *book = config_setting_get_elem(setting, i);      /* Only output the record if all of the expected fields are present. */      const char *title, *author;      double price;      int qty;      if(!(config_setting_lookup_string(book, ""title"", &title)           && config_setting_lookup_string(book, ""author"", &author)           && config_setting_lookup_float(book, ""price"", &price)           && config_setting_lookup_int(book, ""qty"", &qty)))        continue;      printf(""%-30s  %-30s  $%6.2f  %3d\n"", title, author, price, qty);    }    putchar('\n');  }

メモリ解放

config_init()で確保したメモリを、config_destroy()で解放。が、マニュアルには""deallocating all memory associated with the configuration, but does not attempt to deallocate theconfig_t structure itself.""と書かれていて解放しているのか解放していないのかよく分かりません。どういう意味だろう。

  config_destroy(&cfg);  return(EXIT_SUCCESS);

設定ファイルに加筆

example2.cが既存ファイルに加筆をするサンプル

  /* Find the 'movies' setting. Add intermediate settings if they don't yet  * exist.  */  root = config_root_setting(&cfg);  setting = config_setting_get_member(root, ""inventory"");  if(!setting)    setting = config_setting_add(root, ""inventory"", CONFIG_TYPE_GROUP);  setting = config_setting_get_member(setting, ""movies"");  if(!setting)    setting = config_setting_add(setting, ""movies"", CONFIG_TYPE_LIST);  /* Create the new movie entry. */  movie = config_setting_add(setting, NULL, CONFIG_TYPE_GROUP);  setting = config_setting_add(movie, ""title"", CONFIG_TYPE_STRING);  config_setting_set_string(setting, ""Buckaroo Banzai"");  setting = config_setting_add(movie, ""media"", CONFIG_TYPE_STRING);  config_setting_set_string(setting, ""DVD"");  setting = config_setting_add(movie, ""price"", CONFIG_TYPE_FLOAT);  config_setting_set_float(setting, 12.99);  setting = config_setting_add(movie, ""qty"", CONFIG_TYPE_INT);  config_setting_set_float(setting, 20);

実行するとupdated.cfgが生成される。さっきのexample1の対象のファイルを修正してからmake

  /* Read the file. If there is an error, report it and exit. */  if(! config_read_file(&cfg, ""updated.cfg""))

確かに追加されている。

pi@raspberrypi:~/libconfig-1.5/examples/c $ ./example1Store name: Books, Movies & MoreTITLE                           AUTHOR                           PRICE   QTYTreasure Island                 Robert Louis Stevenson          $ 29.99    5Snow Crash                      Neal Stephenson                 $  9.99    8TITLE                           MEDIA        PRICE   QTYBrazil                          DVD         $ 19.99   11The City of Lost Children       DVD         $ 18.99    5Memento                         Blu-Ray     $ 24.99   20Buckaroo Banzai                 DVD         $ 12.99    0

設定ファイルの中身を見ても確かに変更されている。フォーマッティングが変更されてしまうのが難かな。

name = ""Books, Movies & More""inventory = {  books = ( {      title = ""Treasure Island""      author = ""Robert Louis Stevenson""      price = 29.99      qty = 5    }, {      title = ""Snow Crash""      author = ""Neal Stephenson""      price = 9.99      qty = 8    } )  movies = ( {      title = ""Brazil""      media = ""DVD""      price = 19.99      qty = 11    }, {      title = ""The City of Lost Children""      media = ""DVD""      price = 18.99      qty = 5    }, {      title = ""Memento""      media = ""Blu-Ray""      price = 24.99      qty = 20    }, {      title = ""Howard the Duck""    }, {      title = ""Buckaroo Banzai""      media = ""DVD""      price = 12.99      qty = 0    } )}hours = {  mon = {    open = 9    close = 18  }  tue = {    open = 9    close = 18  }  wed = {    open = 9    close = 18  }  thu = {    open = 9    close = 18  }  fri = {    open = 9    close = 20  }  sat = {    open = 9    close = 20  }  sun = {    open = 11    close = 16  }}

いずれにせよ自分でパーサーの仕様を考えて書くより200倍ぐらい楽なので使ってみることにします。"""

Raspberry PiでIoTなシステム開発:自分で書いたデーモンをinit.dで起動する

前回のシステムではrc.localにスクリプトを書いて起動していたのですが、これだと終了時に自動処理をさせることができきないのと、ランレベルに応じた処理ができません。それにinsservとかを使って登録できる方がなんとなく「システム管理」をしている感じがします。とりあえず最小限のスクリプトを先達の雛形に添って作成。ktdというのはこれから作成する予定のデーモンの名前です。実態は何もしない無限ループです。

#! /bin/sh### BEGIN INIT INFO# Provides:          ktd - kawacchi timer daemon# Required-Start:    $all# Required-Stop:# Default-Start:     2 3 4 5# Default-Stop:      0 1 6# Short-Description: daemon for detect gpio rise and report via unix domain socket### END INIT INFOcase ""$1"" in    start)        echo ""Starting ktd""        /home/pi/kt/ktd/ktd &        ;;    stop)        echo ""Stopping ktd""        killall ktd        ;;    *)        echo ""Usage: /etc/init.d/ktd start|stop""        exit 1        ;;esacexit 0

直接実行してみるとroot権限で実行されました。

pi@raspberrypi:~/kt/ktd $ sudo /etc/init.d/ktd startStarting ktdpi@raspberrypi:~/kt/ktd $ ps auUSER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMANDroot       634  0.0  0.1   4052  1740 tty1     Ss+  06:58   0:00 /sbin/agetty --noclear tty1 linuxroot       635  0.0  0.2   3872  1928 ?        Ss+  06:58   0:00 /sbin/agetty --keep-baud 115200 38400 9600 pi         703  0.0  0.4   6244  4472 pts/0    Ss   07:02   0:01 -bashroot      1214  0.3  0.1  10388  1204 pts/0    Sl   07:35   0:00 /home/pi/kt/ktd/ktdpi        1223  0.0  0.2   4740  2064 pts/0    R+   07:35   0:00 ps au

最初はrc*.dの何処にもスクリプトが入っていませんが、insservを実行するとリンクが登録されました。Required-startが反映されたようで実行順位が04になっています。

pi@raspberrypi:~/kt/ktd $ ls -l /etc/rc*.d/*ktdls: cannot access /etc/rc*.d/*ktd: No such file or directorypi@raspberrypi:~/kt/ktd $ sudo insserv -d ktdpi@raspberrypi:~/kt/ktd $ ls -l /etc/rc*.d/*ktdlrwxrwxrwx 1 root root 13 Feb 12 08:18 /etc/rc0.d/K01ktd -> ../init.d/ktdlrwxrwxrwx 1 root root 13 Feb 12 08:18 /etc/rc1.d/K01ktd -> ../init.d/ktdlrwxrwxrwx 1 root root 13 Feb 12 08:18 /etc/rc2.d/S04ktd -> ../init.d/ktdlrwxrwxrwx 1 root root 13 Feb 12 08:18 /etc/rc3.d/S04ktd -> ../init.d/ktdlrwxrwxrwx 1 root root 13 Feb 12 08:18 /etc/rc4.d/S04ktd -> ../init.d/ktdlrwxrwxrwx 1 root root 13 Feb 12 08:18 /etc/rc5.d/S04ktd -> ../init.d/ktdlrwxrwxrwx 1 root root 13 Feb 12 08:18 /etc/rc6.d/K01ktd -> ../init.d/ktdpi@raspberrypi:~/kt/ktd $ sudo insserv -r ktdpi@raspberrypi:~/kt/ktd $ ls -l /etc/rc*.d/*ktdls: cannot access /etc/rc*.d/*ktd: No such file or directory

削除も簡単にできるようです。もう1回登録をして再起動するとこんな感じでした。ちゃんと起動していますね。

pi@raspberrypi:~ $ ps axu | grep ktdroot       350  0.0  0.1  10388  1316 ?        Sl   08:23   0:00 /home/pi/kt/ktd/ktdpi         725  0.0  0.2   4276  1944 pts/0    S+   08:25   0:00 grep --color=auto ktd

次はstart-stop-daemonあたりを使用して、もっとそれっぽくしてみたいと思います。”””

Raspberry Pi でIoTなIT:preempt_rtが付いていない素のRaspbianの割り込み性能はWheezyとJessieで違いがあるのか

前日来実験している割り込みの性能テスト。Jessieの構築に成功したので、こちらでも実験してみました。JessieWheezyカーネルと基本パッケージが変わるだけでそれほど性能差は出ないだろうと思っていましたが、2割ぐらいレスポンスが違うように見えます。最適化されたということらしいので、そのあたりが影響しているのでしょうか。あと使っているJessieがLITEなので文字通り軽いのかもしれません。”””

Raspberry PiでIoTなシステム開発:echoのリダイレクトはsudoで実行してもリダイレクト部分だけsudoを実行したユーザーとして実行される

Raspberry PiのオンボードLEDをコントロールする方法が無いかと検索していたら、こちらの記事を発見。Can we control the on-board leds[コラム] Raspberry Pi 2のオンボードLEDを自由に光らせようどのキーワードに括弧書きを付けているかをechoで変更するので何かおかしいなと思っていたら、nanoで手動で書き換えても元に戻ります。これは一種のデバイスファイルで、読み取りが状態表示、キーワードの書き込みがデバイスに対する操作を行っているのだと思います。で、指示通りsuでrootになって作業をしたらちゃんとコントロールできたのですが、sudoで実行したらエラー。

pi@raspberrypi:~ $ sudo echo heartbeat > /sys/class/leds/led0/trigger -bash: /sys/class/leds/led0/trigger: Permission denied

suだとスクリプトから実行しにくいですし、rootになったまま他の作業を実施してしまいそうで怖いんです。で、調べてみるとリダイレクトするとリダイレクト部分だけがsudoの実行元のユーザーが実行されてしまうとのこと。なのでリダイレクトではなくパイプで渡して、渡された先でteeを使ってファイルに書き込むとエラーになりませんでした。

pi@raspberrypi:~ $ echo none | sudo tee /sys/class/leds/led0/trigger nonepi@raspberrypi:~ $ echo heartbeat | sudo tee /sys/class/leds/led0/trigger heartbeat

この通り実行してみるとチカッチカッと。GPIOに連動させる設定にしてサーバーアプリの状態監視に使う予定です。”””

Raspberry PiでIoTなシステム開発:オンボードLEDのコントロールはBCMナンバーで、WiringPiのコントロールは独自のナンバーで

当たり前のことではあるのですが、PiでGPIOを指定するときはコアであるBCMの番号が原則です。が、WiringPiは何故かGPIOに対して独自の付番をしているので、対照関係が結構ややこしいです。ヘッダピンの番号がさらにまた別になっています。で、今回オンボードLEDのコントロールのため、以下のコマンドでGPIOに対応させるようにしたのですが、このコマンドはBCMの番号で指定します。

    system(""echo gpio | sudo tee /sys/class/leds/led0/trigger"");    system(""echo 21 | sudo tee /sys/class/leds/led0/gpio"");

一方このGPIOをWiring PiでコントロールするときはWiring PiでコントロールするときはWiring Piの番号でコントロールします。

    while(1) {        digitalWrite(29, 1);        usleep( 50000);        digitalWrite(29, 0);        usleep(950000);   }

コード上ではWiring Piの世界とBCMの世界の区別がぱっと見ではわかりにくいので、気をつけた方がいいですね。”””

Raspberry PiでIoTなシステム開発:serviceコマンドはinit.dにスクリプトがあればrc*.dにスクリプトが無くても実行できる って話題がよくよく調べてみるとJessieはsystemdに変わっていたことに気づくという話題

service – Unix, Linux Commandserviceコマンドが環境変数の影響を排除してinit.dのスクリプトを実行できるということは色々なサイトに書かれていて概ね理解できていました。が、serviceが実態として、rc*.dのリンクを実行していて、その時点でのランレベルに応じた起動をするのか、とにかくinit.dにある方のスクリプトを直接実行しているのかが分からなかったのですが、このサイトによるとinit.dにある方を実行しているとのこと。で、実際にinsservでrc*.dのシンボリックを全削除してみたところ、それでもserviceコマンドでサービスのstart/stopができました。で更に実験としてinit.dの中のスクリプト名を変更したら、もちろん実行できなかったのですが、warningの中にあるsystemctlを実行すると、何故かktdが実行できるようになりました。

pi@raspberrypi:/etc/init.d $ sudo mv ktd tttdpi@raspberrypi:/etc/init.d $ sudo service ktd startWarning: Unit file of ktd.service changed on disk, 'systemctl daemon-reload' recommended.Job for ktd.service failed. See 'systemctl status ktd.service' and 'journalctl -xn' for details.pi@raspberrypi:/etc/init.d $ systemctl daemon-reloadFailed to execute operation: Access deniedpi@raspberrypi:/etc/init.d $ sudo systemctl daemon-reloadpi@raspberrypi:/etc/init.d $ sudo service ktd startpi@raspberrypi:/etc/init.d $ ps ax | grep ktd 1619 ?        Rl     0:10 /home/pi/kt/ktd/ktd 1622 ?        S      0:00 /home/pi/kt/ktd/ktd 1663 pts/0    S+     0:00 grep --color=auto ktd

そして名称変更したものを元の名前に戻しても実行できません。

pi@raspberrypi:~ $ cd /etc/init.d/pi@raspberrypi:/etc/init.d $ sudo mv tttd ktdpi@raspberrypi:/etc/init.d $ cd ~pi@raspberrypi:~ $ sudo service ktd startWarning: Unit file of ktd.service changed on disk, 'systemctl daemon-reload' recommended.Job for tttd.service failed. See 'systemctl status tttd.service' and 'journalctl -xn' for details.

その後、またsystemctlを実行して初めて実行できる様になりました。

pi@raspberrypi:~ $ sudo systemctl daemon-reloadpi@raspberrypi:~ $ sudo service ktd startpi@raspberrypi:~ $ sudo service ktd stop

一体何これといろいろと調べていると、Jessieからinit.dによるinitではなくsystemdによるinitに変わったようなのです。今までのinit.dにスクリプトを書くスタイルは今後obsoleteになるとのこと。systemd:wikipediainit – もわの書斎ここで疑問が出てくるのが、どうして既にsystemdに移行しているのに、init.dに沢山スクリプトが入っていて、実際にこれをいじるとサービスの起動なのかということ。systemdについて一番詳しいと思われる以下のページにも記述がありませんでした。Systemd入門(1) – Unitの概念を理解する – めもめも回答はこちらに。How does systemd use /etc/init.d scripts? – Unix & Linux Stack ExchangeRaspberry Pi • View topic – systemv init processing in jessie要はinit.dのスクリプトを起動時にsystemdがsystemdとしてのUnitに変換し、その上でsystemdの枠内で起動をしているとのこと。そうなるとこれからはinit.dのシェルスクリプトを書くと変換という一手間を経るわけで、最初からsystemdのUnitを書く方が効率が良さそうです。ただシェルスクリプトでかける方が、sleepで無理矢理タイミングをずらすような処理がやりやすく、魅力があります。とりあえず現時点ではinit.dで起動されるサービスが多数あり、自分のスクリプトだけsystemdに移行しても大勢に影響はなさそうですので、init.dのスタイルを続けることにします。が、並行してsystemdの流儀もマスターしていきたいですね。    “””

Raspberry PiでIoTなシステム開発:3.3Vマイコンで直接駆動できるN-ch FET

低電圧からの流れは世界的なトレンドのようで、PIC/AVR/H8の5Vから、ARM系の3.3Vに主流が移っていますね。周辺用のモジュールも3.3V対応のものが増えてきましたので、接続には困らないことが多いです。が、それでも困るのがMOS-FET。街中の電子部品店に出回っているMOS-FETは「エンハンスメントタイプ」とあってもゲート電圧が4Vを超えて初めてそれなりのIDが流せるものが多いです。そのため3.3V系のマイコンでは直接駆動できず、ゲートドライバを別途接続しないといけなくなります。ただ良く探してみると、MOS-FETにも3.3Vで直接駆動できるものがあります。私がSTM本の執筆の時によく使っていたのが、IRL2505。IRL2505 – International Rectifierデータシート上ではゲート電圧3V時に30A程流せます。最大電流が100Aありますので、モータードライブも楽勝です。オン抵抗わずか0.01Ω。難点は馬鹿高いこと。何と1コ400円もします。後継品も同じぐらいです。そこで他に使えるものが無いかと探してみて、これを入手してみました。共立電子で140円です。2SK975 – Renesas Electronicsゲート電圧3V時に1.5A程度流せます。オン抵抗0.4Ωぐらい。マイコンの周辺回路で1Aも食うものはそう無いので、実用上は充分かなと。TO-92みたいな感じでブレッドボードも楽勝。ただもうディスコンっぽいです。まだ入手できていないのですが気になっているのが秋月の掘り出し品のこちら。NchMOSFET 2SK2412 (60V20A)3V駆動で100Aまでドライブできます。パルスなら80Aまでいけるので、かなり大容量の負荷でも何とかなりそう。オン抵抗0.01Ωぐらい。あとはこの3点も使えるかもしれません。Nch MOSFET 2SK3134L-E (30V75A)NchパワーMOSFET EKI04036 40V80A次の秋月への発注の際には忘れずにカートに加えておきます。共立だとこれ。2SK4019 – Toshiba  “””

Raspberry PiでIoTなシステム開発:圧電振動子(ピエゾスピーカー)の昇圧ドライブ

バイクジムカーナのタイム計測機を作ろうとすると、スタートシグナルを鳴らす方法を考えないといけません。簡単な方法としては、音声出力にアンプとスピーカーをつなぐことなのですが、発想は簡単でも実装は大変です。バイクの爆音の中でヘルメットをしているライダーに音を伝えようとすると、相当なワット数のアンプが必要です。電源電圧が5Vに制限されていると、4Ωのスピーカーを使って出せる出力は理論上の最大でも5Wぐらい、実際に入手可能なD級アンプモジュールだと2wぐらいです。これではライダーにスタートシグナルを伝えることができません。そこで思いついたのが、防犯ブザーでよく使われている圧電振動子を昇圧して鳴らせる方法。ネットで検索するとそれほど多くは無いですがいくつか回路が出てきます。ただ受動素子の定数が合いませんので、トライアンドエラーで試してみます。電源電圧が5Vだとこんな感じですね。コードは最初はWiringPiのソフトウェアPWMライブラリを使いました。

#include #include #include int main(void){    wiringPiSetup();    pinMode(7,PWM_OUTPUT);    softPwmCreate(7,5,11);    int i = 0;    while(1){        softPwmWrite(7,10);        sleep(2);        /*        for (i=0;i<=11;i++)        {            printf(""rate is %d\n"",i);            softPwmWrite(7,i);            sleep(1);        }*/    }}

が、このライブラリは、途中でデューティ比を変えられるものの、PWM周期を変えられないことが判明。この昇圧回路の場合、デューティー比を変えるとインダクタへのチャージ量が変わりそれで音量調整ができるので、便利です。が、音程が変えられないとシグナルにならないので断念。出力を2本使う方法も有りだと思いますが、それは今度試して見ます。で、やむを得ずソフトウェアトーンライブラリで書いたのがこちら。

#include #include #include int main(void){    wiringPiSetup();    pinMode(7,OUTPUT);    softToneCreate(7);    int i = 0;    while(1){/*        softToneWrite(7,446);//446で実際の出力が439.98Hz        usleep(500000);        softToneWrite(7,0);        usleep(500000);        softToneWrite(7,446);//446で実際の出力が439.98Hz        usleep(500000);        softToneWrite(7,0);        usleep(500000);        softToneWrite(7,446);//446で実際の出力が439.98Hz        usleep(500000);        softToneWrite(7,0);        usleep(500000);        softToneWrite(7,904);//904で実際の出力が879.56Hz        usleep(1000000);        softToneWrite(7,0);        usleep(500000);*/        softToneWrite(7,446);//446で実際の出力が439.98Hz        usleep(3000000);        softToneWrite(7,904);//904で実際の出力が879.56Hz        usleep(3000000);        /*        for (i=0;i<=11;i++)        {            printf(""rate is %d\n"",i);            softPwmWrite(7,i);            sleep(1);        }*/    }}

以前rpi1で試したときもそうだったのですが、そのライブラリは音程がずれます。ソース内のコメントの通りですが、出力波形を実測して設定値を微調整する必要があります。で、5Vで慣らしてみたところ、かなりの音量で鳴らすことができました。計測してみるとピークトゥピークで25Vぐらいありそうです。しかしバイク用としては若干物足りません。そこで電源電圧を12Vにしてみたところ、ある程度満足のいく音量に達しました。ピークトゥピークで50Vぐらい出ています。ちなみにこれはピエゾスピーカーの仕様を超えた使い方ですので、いつ壊れても誰にも文句は言えません。ただし消費電流は440Hzで27mA、880Hzで60mAだけです。アンプをつないでスピーカーを鳴らすことを考えると大分とエコですね。出力のオンオフを繰り返していると、唐突にpiの電源が落ちることが何度かありました。恐らくですが、インダクタの起電力が電源側に回り込んでいます。繰り返しているとpiとかモバイルバッテリーを壊しそうなので、予防用にダイオードを入れました。使ったのは1N4148。最大で200mA流せます。電圧降下のロスがありますが、電源が12Vであればその1割程度なので許容範囲とします。イメージ動画がこちら。背景に聞こえている音楽は、「僕が普通にBGMを鳴らしている時の音量」です。なんとなく音量レベルが分かりますでしょうか。    """

Raspberry PiでIoTなシステム開発:WiringPiはプログラムを終了させてもピンモードがそのままになるのでシグナルを捕捉してINPUTに戻す

WiringPiを使ってトーンやPWMを扱っていると、プログラムが終了した後も、GPIOがハイのままになっていることがありました。マイコン開発の感覚からすると違和感がありますが、汎用OS上で開発していると、GPIOの設定はそのままになるのが自然な流れのようですね。ただこれだと接続した先の回路が導通したままになり、LED点きっぱなし、モーター回りっぱなし、インダクタチャージしっぱなしになります。アウトになっているとちょっとしたことでショートさせてしまいそうで怖いです。それでは気持ち悪いので、プログラムの終了時に自動的にIOをインプットに戻すべく、シグナルの捕捉を実装します。参考にしたのはこちら。シグナル処理関数(シグナルハンドラ)を登録する | C言語入門講座

#include #include #include #include #include void setSignal(int p_signame);void signalHandler(int p_signame);int main(void){    setSignal(SIGINT);    wiringPiSetup();    pinMode(7,OUTPUT);    softToneCreate(7);    while(1){        softToneWrite(7,446);//446で実際の出力が439.98Hz        usleep(500000);        softToneWrite(7,0);        usleep(500000);        softToneWrite(7,446);//446で実際の出力が439.98Hz        usleep(500000);        softToneWrite(7,0);        usleep(500000);        softToneWrite(7,446);//446で実際の出力が439.98Hz        usleep(500000);        softToneWrite(7,0);        usleep(500000);        softToneWrite(7,904);//904で実際の出力が879.56Hz        usleep(1000000);        softToneWrite(7,0);        usleep(500000);    }}//シグナルの設定void setSignal(int p_signame){    if (signal(p_signame, signalHandler) == SIG_ERR) {    //エラー        printf(""Could not set signal handler\n"");        exit(1);    }    return;}//シグナル受信/処理void signalHandler(int p_signame){    printf(""I got signal. Number is %d.\n"", p_signame);    pinMode(7,INPUT);    exit(0);}

最後のあたりがシグナルに対応する処理の部分です。ハンドラが呼び出されるときにシグナル種別が分かるのに最初に捕捉するシグナルを指定する必要性が今ひとつ分かりませんが、これで正常に動作しました。このロジックを入れていないときは、プログラムの終了時にピン7がアウトになったままです。

pi@raspberrypi:~/kt/ktd $ gpio readall +-----+-----+---------+------+---+---Pi 2---+---+------+---------+-----+-----+ | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM | +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+ |     |     |    3.3v |      |   |  1 || 2  |   |      | 5v      |     |     | |   2 |   8 |   SDA.1 |   IN | 1 |  3 || 4  |   |      | 5V      |     |     | |   3 |   9 |   SCL.1 |   IN | 1 |  5 || 6  |   |      | 0v      |     |     | |   4 |   7 | GPIO. 7 |  OUT | 0 |  7 || 8  | 1 | ALT0 | TxD     | 15  | 14  | |     |     |      0v |      |   |  9 || 10 | 1 | ALT0 | RxD     | 16  | 15  | |  17 |   0 | GPIO. 0 |   IN | 0 | 11 || 12 | 0 | IN   | GPIO. 1 | 1   | 18  | |  27 |   2 | GPIO. 2 |   IN | 0 | 13 || 14 |   |      | 0v      |     |     | |  22 |   3 | GPIO. 3 |   IN | 0 | 15 || 16 | 0 | IN   | GPIO. 4 | 4   | 23  | |     |     |    3.3v |      |   | 17 || 18 | 0 | IN   | GPIO. 5 | 5   | 24  | |  10 |  12 |    MOSI |   IN | 0 | 19 || 20 |   |      | 0v      |     |     | |   9 |  13 |    MISO |   IN | 0 | 21 || 22 | 0 | IN   | GPIO. 6 | 6   | 25  | |  11 |  14 |    SCLK |   IN | 0 | 23 || 24 | 1 | IN   | CE0     | 10  | 8   | |     |     |      0v |      |   | 25 || 26 | 1 | IN   | CE1     | 11  | 7   | |   0 |  30 |   SDA.0 |   IN | 1 | 27 || 28 | 1 | IN   | SCL.0   | 31  | 1   | |   5 |  21 | GPIO.21 |   IN | 1 | 29 || 30 |   |      | 0v      |     |     | |   6 |  22 | GPIO.22 |   IN | 1 | 31 || 32 | 0 | IN   | GPIO.26 | 26  | 12  | |  13 |  23 | GPIO.23 |   IN | 0 | 33 || 34 |   |      | 0v      |     |     | |  19 |  24 | GPIO.24 |   IN | 0 | 35 || 36 | 0 | IN   | GPIO.27 | 27  | 16  | |  26 |  25 | GPIO.25 |   IN | 0 | 37 || 38 | 0 | IN   | GPIO.28 | 28  | 20  | |     |     |      0v |      |   | 39 || 40 | 0 | OUT  | GPIO.29 | 29  | 21  | +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+ | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM | +-----+-----+---------+------+---+---Pi 2---+---+------+---------+-----+-----+

一方、こちらはシグナルを捕捉したもの。INになっているのが分かります。

 pi@raspberrypi:~/kt/ktd $ gpio readall +-----+-----+---------+------+---+---Pi 2---+---+------+---------+-----+-----+ | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM | +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+ |     |     |    3.3v |      |   |  1 || 2  |   |      | 5v      |     |     | |   2 |   8 |   SDA.1 |   IN | 1 |  3 || 4  |   |      | 5V      |     |     | |   3 |   9 |   SCL.1 |   IN | 1 |  5 || 6  |   |      | 0v      |     |     | |   4 |   7 | GPIO. 7 |   IN | 1 |  7 || 8  | 1 | ALT0 | TxD     | 15  | 14  | |     |     |      0v |      |   |  9 || 10 | 1 | ALT0 | RxD     | 16  | 15  | |  17 |   0 | GPIO. 0 |   IN | 0 | 11 || 12 | 0 | IN   | GPIO. 1 | 1   | 18  | |  27 |   2 | GPIO. 2 |   IN | 0 | 13 || 14 |   |      | 0v      |     |     | |  22 |   3 | GPIO. 3 |   IN | 0 | 15 || 16 | 0 | IN   | GPIO. 4 | 4   | 23  | |     |     |    3.3v |      |   | 17 || 18 | 0 | IN   | GPIO. 5 | 5   | 24  | |  10 |  12 |    MOSI |   IN | 0 | 19 || 20 |   |      | 0v      |     |     | |   9 |  13 |    MISO |   IN | 0 | 21 || 22 | 0 | IN   | GPIO. 6 | 6   | 25  | |  11 |  14 |    SCLK |   IN | 0 | 23 || 24 | 1 | IN   | CE0     | 10  | 8   | |     |     |      0v |      |   | 25 || 26 | 1 | IN   | CE1     | 11  | 7   | |   0 |  30 |   SDA.0 |   IN | 1 | 27 || 28 | 1 | IN   | SCL.0   | 31  | 1   | |   5 |  21 | GPIO.21 |   IN | 1 | 29 || 30 |   |      | 0v      |     |     | |   6 |  22 | GPIO.22 |   IN | 1 | 31 || 32 | 0 | IN   | GPIO.26 | 26  | 12  | |  13 |  23 | GPIO.23 |   IN | 0 | 33 || 34 |   |      | 0v      |     |     | |  19 |  24 | GPIO.24 |   IN | 0 | 35 || 36 | 0 | IN   | GPIO.27 | 27  | 16  | |  26 |  25 | GPIO.25 |   IN | 0 | 37 || 38 | 0 | IN   | GPIO.28 | 28  | 20  | |     |     |      0v |      |   | 39 || 40 | 0 | OUT  | GPIO.29 | 29  | 21  | +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+ | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM | +-----+-----+---------+------+---+---Pi 2---+---+------+---------+-----+-----+

   “””