FreeBSDでUSBデバイスのデバイス名を固定する

FreeBSDでUSBデバイスを使う際、それが同じ用途のデバイスであるとUSBポートの差し替えとか認識される順序によっては/dev/デバイス名○の数字の部分が変わってしまう。ヘタにデバイス名をアテにして設定しているとデバイス名が変わることで困ってしまう。そこで特定のUSBデバイスを接続したらそれに応じたデバイス名になるよう固定する。

参考: https://habr.com/ru/post/200330/

まず、USBデバイスの情報を表示した。
# usbconfig dump_device_desc

中略

ugen1.2: <Linux 4.19.68-sunxi with musb-hdrc Gadget Serial v2.4> at usbus1, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=ON (2mA)

  bLength = 0x0012 
  bDescriptorType = 0x0001 
  bcdUSB = 0x0200 
  bDeviceClass = 0x0002  <Communication device>
  bDeviceSubClass = 0x0000 
  bDeviceProtocol = 0x0000 
  bMaxPacketSize0 = 0x0040 
  idVendor = 0x0525 
  idProduct = 0xa4a7 
  bcdDevice = 0x0419 
  iManufacturer = 0x0001  <Linux 4.19.68-sunxi with musb-hdrc>
  iProduct = 0x0002  <Gadget Serial v2.4>
  iSerialNumber = 0x0000  <no string>
  bNumConfigurations = 0x0001

中略

ugen2.3: <u-blox AG - www.u-blox.com u-blox 7 - GPS/GNSS Receiver> at usbus2, cfg=0 md=HOST spd=FULL (12Mbps) pwr=ON (100mA)

  bLength = 0x0012 
  bDescriptorType = 0x0001 
  bcdUSB = 0x0110 
  bDeviceClass = 0x0002  <Communication device>
  bDeviceSubClass = 0x0000 
  bDeviceProtocol = 0x0000 
  bMaxPacketSize0 = 0x0040 
  idVendor = 0x1546 
  idProduct = 0x01a7 
  bcdDevice = 0x0100 
  iManufacturer = 0x0001  <u-blox AG - www.u-blox.com>
  iProduct = 0x0002  <u-blox 7 - GPS/GNSS Receiver>
  iSerialNumber = 0x0000  <no string>
  bNumConfigurations = 0x0001 

ugen1.2がUSBシリアル接続のデバイス(Linuxシングルボードコンピュータ)
ugen2.3がUSBシリアル接続のGPSモジュール

/etc/devd.confで使える変数 $device-nameは /dev/デバイス名 の「デバイス名」部分が入るのだろうと思っていたが、シリアルデバイスの一部(全部?)はデバイス名がttyU*ではなく、umodem*になるっぽい。/devにはそんなデバイス無いのにね。(「がとらぼ」で試した範囲では)
なので、umodemをttyUに置換して それを指定したデバイス名にシンボリックリンクする。

/usr/local/bin/ln_dev.sh (新規ファイル)
1
2
3
4
5
6
7
#!/bin/sh
# $1 置換前デバイス名 devd.confの$device-nameが入る前提
# $2 $device-nameのデバイス名で数字が付いていない部分下の例ではumodem
# $3 $device-nameのデバイス名の置換後
# $4 シンボリックリンク先 目的のデバイス名

/bin/ln -s $(echo /dev/$1 | /usr/bin/sed "s/$2/$3/g") $4
# chmod +x /usr/local/bin/ln_dev.sh
実行可能にしておく。
/etc/devd.conf (ファイル最後に追加)
 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
//USBシリアル通信デバイス (シングルボードコンピュータ)
attach 100 {
    match "vendor" "0x0525";
    match "product" "0xa4a7";
    match "release" "0x0419";
    action "/usr/local/etc/ln_dev.sh $device-name umodem ttyU /dev/hoge0";
};

detach 100 {
    match "vendor" "0x0525";
    match "product" "0xa4a7";
    match "release" "0x0419";
    action "/bin/rm /dev/hoge0";
};


//USB GPS module
attach 100 {
    match "vendor" "0x1546";
    match "product" "0x01a7";
    match "release" "0x0100";
    action "/usr/local/etc/ln_dev.sh $device-name umodem ttyU /dev/gps0";
};

detach 100 {
    match "vendor" "0x1546";
    match "product" "0x01a7";
    match "release" "0x0100";
    action "/bin/rm /dev/gps0";
};

attachがデバイス接続時。detachがデバイスの接続が切れたときに使用される。
先にusbconfigで見た内容のidVendorがdev.confで指定するvendorに対応する。同様にidProductがproductに、bcdDeviceがreleaseに対応する。上の例では3つがマッチした場合にactionするようにしたが、3つでなければならないわけではない。マッチさせる条件が多い方がより確実だろうけど、シリアル番号のようなものがあるなら例えばベンダー名とシリアルの2つで確実にユニークなデバイスとして識別できるかもしれない。
ただ、今回の機器は2つともiSerialNumber: 0x0000 <no string>でデバイス識別には役立ちそうになかったのでmatch条件にはしていない。

上の例ではシングルボードコンピュータは/dev/hoge0、GPSモジュールは/dev/gps0となる。それぞれ最後の0は上のようにdevd.confで指定したから付くというだけであって、数字を付けなければならないというものではない。今後GPSモジュールを追加で接続するならgps1とかgps2とかにするという目論見での命名。

# service devd restart
devdを再起動。

これで、USBデバイスのケーブルを抜き差ししたりシステムを再起動したりで/dev/ttyU0だとか/etc/ttyU1だとかの数字が変わっても、その内容から識別してデバイス名のシンボリックリンクが作られるので安心。

Grafana AlertingやAlert Managerと連携して警告灯を点灯

これまでいくつか警告灯の記事を書いたけど、Grafana Alertingから、またはPrometheusのAlert Managerからwebhookで通知を受けると警告灯が点灯するようにした。
前回はwebhookで受けた内容をログファイルに書き出すようにしたが、今回はwebhookで受けた内容は一切見ない。webhookを受けたのをトリガーにしてGrafana Alerter(とAlert Manager この記事では省略)からアラートのステータスを取りに行って、その内容で警告灯を光らせるにようにした。でないと、通知されたwebhookの内容を見たところで、その時点での他の状態がわからずどのように光らせるか非常に処理が難しくなる。それ用のDBを用意して状態を管理するのもバカらしいし。

長いのを出しても意味がないので超はしょり版

 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
<?php

// 自身のGrafanaのAlertステータス取得URL
$url = "http://grafana_user:grafana_password@grafana_server:3000/api/alerts/";
$logFile = __DIR__ . '/warn.log';

if ($_SERVER["REQUEST_METHOD"] == "POST") {
    //$body = file_get_contents('php://input');
    //$json = json_decode($body);

    $json = json_decode(get_curl($url), true);
    //file_put_contents($logFile, print_r($json), FILE_APPEND);

    $warn = false;
    $alert = false;

    foreach($json as $ealert) {
        $log =  date("Y-m-d H:i:s "). $ealert['name'] . " " . $ealert['state'] . "\n";

        //Systel Load alertがalertingならアラートではなく警告とする
        if ($ealert['name'] =='System Load alert'){
            if ($ealert['state'] =='alerting'){
                $warn = true;
            }
        }

        file_put_contents($logFile, $log, FILE_APPEND);
    }

    //ライト点灯
    if ($alert) {
        light(3);
    } elseif ($warn) {
        light(2);
    } else {
        light(1);
    }
}

function get_curl($url) {
    $c = curl_init();
    curl_setopt($c, CURLOPT_URL, $url);
    curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
    $res =  curl_exec($c);
    //$res = var_dump($res);
    curl_close($c);

    return $res;
}

function light($c) {
    switch ($c) {
        case 0:
            //警告灯 消灯
            $code = "0f";
            break;
        case 1:
            //警告灯 緑
            $code = "0b";
            break;
        case 2:
            //警告灯 黄
            $code = "0d";
            break;
        case 3:
            //警告灯 赤
            $code = "0e";
            break;
    }

    $cmd = 'echo "' .  $code . '" | xxd -r -p > /dev/cuaU0';
    exec($cmd , $error);
    //echo $error;
}
?>

上の例は超簡略版なのでSystem Load alertの警告パターン1つだけしか存在しない。(21〜25行)
処理としては、Grafana Alertingで得られるステータスが1つでもアラート(異常相当)なら赤、アラートが無くて警告相当が1つでもあれば黄、アラートも警告も無ければ緑を光らせるもの。
警告状態から正常に復帰した場合は何事もなかったかのように緑点灯(正常)というのは問題ないとすることもできるが、アラートから警告あるいは正常に復帰した場合にまるで問題が起きていなかったかのように黄・緑が光って見えるというのは良くないという考え方もある。上の例には書いていないが、実際にはアラート状態が発生した場合はアラート名と発生したインスタンスを名前とするファイルを一つ書き出すようにしている。その(それらの)ファイルが存在するならGrafana Alertingのステータスではアラートが無い状態に戻っているとしても赤を点灯し続けるようにしている。本当に問題が解決した・解決を確認したなら管理者がそのファイルを手動で消す。

また、警告灯とは別に各インスタンスのステータスをcurlでPrometheusから取得し、Amazon Pollyに読み上げさせるような処理も作っている。今回はせっかくなので警告とアラートも読み上げさせるようにした。ただし、そちらはコードは割愛。

いい歳したオッサンが中学生かよという恥ずかしさはある。うん。

関連記事:

クルマのバックライトと前ポジションのLEDを差し替えた

正月休みにクルマのバックランプをT10の明るいLEDに変更した。それで光り方は明るくはなったが、期待したほどは後ろが照らされるものではなかった。T16ソケットにT10が挿さるとはいえ、T10ではバルブが細いので光らせるLEDチップを沢山は積めない。そこで改めてT16(実際はT15)のLEDを購入した。

SMD(表面実装)な4014チップが45個というT15のLEDバルブ。T16ではないが、ほぼ同じ。AliExpressの商品ページには900lmと書かれている。それは1つで900lmなのか2個セットでなのかは不明。片面18個のLEDが付いた2枚の基板の間にヒートシンクが入っていて、一応は放熱も考えられてはいるみたい。
2個セットで200円ちょっと。

T16 LED 1
全長40mmなので写真の印象ほどは大きくはない。基板はそんなに綺麗ではない感じ。1個100円程度というのは納得。

T16 LED 2
スマホで撮ったら色が飛んだ。スミマセン
ソケットに挿さる側。厚すぎず薄すぎずでソケットに負担をかけずにグラグラしないでしっかり挿さりそう。

T16 LED 3
ソケットの端子を起こしてしっかり挿した。基板がソケットに当たった部分の縁が少しひび割れた。劣化してるのかな。

バックランプ 電球時
バックライトを純正の電球に戻して撮影した。その純正球の光り方はかなり黄色い。
ここから3枚はカメラの設定は同じ。夕方の日没前に撮影しているので周囲が真っ暗ではない。

バックランプ T10 LED時
この前挿したT10のLED。目で見ると純正球より明るいが、この写真では何故か明るくなった様には見えなくて色が白になっただけの感じ。

バックランプ T16 LED時
今回購入したT15のLED。光り方が明らかに強烈になっている。夜にこのバックランプを見ると爆光に近い眩しさではないだろうか。ただし、眩しくはなっても周囲、特に地面側を照らしてくれるのかは不安。バックランプユニットの形状が地面側を照らす用になってないように思えるのよね。

2020年2月25日追記
先週末に夜間にクルマを動かしたので感想。思ったより周囲を照らしていた。目視でもそれなりに近くの範囲は見えるが、純正ナビのバックカメラとドラレコ兼デジタルインナーミラーのバックカメラではどちらも非常に明るく見える。ヘッドライトの様に数十メートル先まで照らしてくれるわけではないが、暗夜でもノロノロでなら目視で後退が十分できる程度になっていた。めでたし。

さて、これで正月休みにバックランプ用に挿したT10の明るいLEDが余った状態になってしまった。フロントのポジション球は正月に挿した3030チップ1つのLED T10バルブで満足してたが、そういえばバックランプにしていた明るいT10バルブはポジション球としては一度も挿していなかった。そこで試しに交換することにした。

明るいT10 LEDバルブはこれ。白は6000Kで12V/5Wで550lmということになっている。(本当かどうかは不明) ちょっとお高くて2個で300円超えで、この記事最初のT15のLEDの1.5倍もする。使わないのはもったいない。

ポジションLED交換 1
手前がフロントのポジションランプ用に挿していた3030チップ1つのT10 LED。奥側が明るいT10 LEDバルブ(とソケット)。
ちょっと長い上に全体がアルミで重いのでヘッドライトユニット内で抜け落ちると困るが、これもソケットの端子起こしをしたらしっかり挿さった。なので今回は抜け落ち防止のアルミテープは使わないことにした。

ポジションLED交換 2
ヘッドライトユニット内で光らせるとこんな感じ。純正球と比べるとだいぶぴょこっと飛び出ている。ちなみにこの写真はシャッター速度を速めて暗く写している。

ポジションLED交換 3
目で見るとこんな感じに見える。もはやポジション球ではなくヘッドライト。バックライトとしてはそんなに明るいとは思わなかったけどヘッドライトユニット内では爆光。商品説明ページに書かれた550lmが本当ならヘタな小型バイクのハロゲンのヘッドライトより光量が多いということになる。そして、ヘッドライトから80cm離れた壁にヘッドライトのロービームを点灯するとポジション交換前はカットラインがはっきり見えていたのが消えた(笑) つまり、カットラインの影が消えるほど光っている。ポジションの光が水平より上方向にだいぶ飛んでいるということなので対抗車のドライバーに眩しがられるかも。離れるとそうでもないのかな?

予想はしてたけど、ポジションとして「点灯を認識できる」レベルを通り越して眩しすぎるので普通に使うのはどうかしら。正月に挿した3030が1つのLEDの方がポジションには良さそう。ヘッドライトロービーム時にポジションが消灯してくれたら問題ないんだけどなぁ。