make the smart speaker smarter 16

05/12

Index

USB Devices have reset

ときどき、KURO-RSが全く動かなくなるときが
あって、どうしてかなぁと調べてたら
USBコントローラ(?)でリセットが発生しており
USBデバイスがいつもは/dev/ttyUSB0で認識していたのが
それが発生したときに/dev/ttyUSB1として再認識していた
ようでKURO-RSへのコマンドが失敗するという事象があった。
同様の理由でPasoriも使えない。

こりゃどうしたもんかと。

今まではKURO-RS側は2度目のリセットが発生したときに
/dev/ttyUSB0で認識されいつの間にか直っていたのだが
Pasoriは頑固で元に戻らない。
ケーブル抜き差しで事なきを得ていた。

Watching kernel message

簡単な対処として監視プログラムを動かすことにした。
/var/log/kern.logにリセットのログが出力されたら
意図的にもう一度リセットさせて認識するデバイスファイルを
強引に書き換える。

sequenceDiagram
  Kernel ->> Kernel : Something occured
  Kernel ->> device file : ttyUSB0->ttsUSB1
  Kernel ->> kern.log : Reset event occured
  Watch Program ->> kern.log : Watching
  Watch Program ->> Watch Program : The event was found!
  Watch Program ->> Kernel : ioctl USBDEVFS_RESET
  Kernel ->> device file : ttyUSB1->ttsUSB0
  Kernel ->> kern.log : Reset event occured
  Watch Program ->> Watch Program : Was the device file changed into ttyUSB0?

こんな感じのものをつくる。
ただこれだとPasoriは救えないが
とりあえずはKURO-RSだけ救おうかなと。

Reset command

調べると既に同じ体験をされている方がいて
リセットコマンドを作っている先輩がいた。
ありがたく使わせていただく。

使い方は

$ usbdevicereset.sh 054c:06c3
Resetting USB device /dev/bus/usb/xxx/xxx
Reset successful
$

Watch Program

次に監視プログラムを。

#!/bin/bash

LOGFILE=/var/log/kern.log
MESSAGE='usb.*1\-1\.XXX.*reset' #トリガーとなるメッセージ。XXXにUSBポート番号を。
USBDEVCMD=`which lsusb`
AWKCMD=`which awk`
GREPCMD=`which grep`
CATCMD=`which cat`
SUDOCMD=`which sudo`
TAILCMD=`which tail`
FINDCMD=`which find`
RESETCMD=`which usbdevicereset.sh`
USBADDR='XXXX:XXXX'  #USBデバイスのID
USBTTYDEV='/dev/ttyUSB0' #期待するデバイスファイル
CHECKFILE=/tmp/${0}.check

#check environment
if [ "x$SUDOCMD" == "x" \
    -o "x$CATCMD" == "x" \
    -o "x$GREPCMD" == "x" \
    -o "x$USBDEVCMD" == "x" \
    -o "x$RESETCMD" == "x" \
    -o "x$AWKCMD" == "x" \
    -o "x$TAILCMD" == "x" \
    -o "x$FINDCMD" == "x" ];
then
    echo "some comand[s] was/were not found"
    exit 1
fi

if [ ! -f $LOGFILE ];
then
    echo "log file was not found"
    echo "log file is [${LOGFILE}]"
    exit 1
fi

#functions
checkaddress () {
    $USBDEVCMD |\
        $GREPCMD $USBADDR >/dev/null 2>&1 && \
        echo -n "OK" || echo -n "!"
}

watchcheckfile () {
    if [ ! -f $CHECKFILE ];
    then
        echo -n "OK"
    fi
    $FINDCMD `dirname $CHECKFILE` -type f -mtime +1 -print 2>/dev/null |\
        $GREPCMD `basename $CHECKFILE` >/dev/null 2>&1 && \
        echo -n "OK" || echo -n "!"
}

watchreset () {
    $CATCMD $LOGFILE |\
        $GREPCMD "${MESSAGE}" >/dev/null 2>&1  && \
        echo -n "OK" || echo -n "!"
}

usbreset () {
    $RESETCMD $USBADDR |\
        $GREPCMD "Reset successful" >/dev/null 2>&1 && \
        $CATCMD $LOGFILE |\
        $TAIL -1 |\
        $GREPCMD $USBTTYDEV >/dev/null 2>&1 && \
        echo -n "OK" || echo -n "!"
}

#main

#check usb address
ret=`checkaddress`
if [ "x$ret" == "x!" -o "x$ret" == "x" ];
then
    echo "usb device was not found"
    exit 1
fi

#watch checkfile
ret=`watchcheckfile`
if [ "x$ret" == "x!" -o "x$ret" == "x" ];
then
    exit 0
fi

#watch usbdevice reset
ret=`watchreset`
if [ "x$ret" == "x!" -o "x$ret" == "x" ];
then
    exit 0
fi

#reset usbdevice
ret=`usbreset`
if [ "x$ret" == "x!" -o "x$ret" == "x" ];
then
    ret=`usbreset`
    if [ "x$ret" == "x!" -o "x$ret" == "x" ];
    then
        echo "FAIL!!!"
        exit 1
    else
        touch $CHECKFILE
        exit 0
    fi
fi

これをwatchusbdevice.shのようなファイル名で保存。
使い方は

$ ./watchusbdevice.sh

これをcronにでも仕込んでおく。

10 0 * * * /srv/google-home-helper/watchusbdevice.sh

まだテストしてないけど
これでうまくいってもPasoriの方はどうしようも
ないので、こっちは別に考えなくてはいけないな。

Next is…

次は、不在時にあったことをまとめて帰宅時に
教えてくれる仕組みを作りたい。

私「OK Google,ただいま。」
ぐ「おかえりなさい。不在時の5月11日 7時37分から5月11日 22時05分までの出来事は、、、」
ぐ「11時15分、任天堂のつぶやきが更新されました。」
ぐ「19時2分、誰かが部屋に入っていました。」
ぐ「19時5分、電気が付きました。」
ぐ「19時10分、加湿器が付きました。」
ぐ「21時00分、兄者弟者の動画が更新されました。」

このくらいは言えるかな。
ちょっとやってみようかな。


コメント: