GAミント至上主義

Web Monomaniacal Developer.

このブログについて(先頭固定)

【お約束】 投稿内容は個人の見解であり、所属する(していた)組織の公式見解ではありません。

名前:uyamazak(昔いた会社で上司が開発用Linuxサーバーのユーザー名に「yuyamazaki」が長いので勝手に作ってくれた。読み方わからないけどウヤマザク)

高校あたりからWEBサイトをやったり趣味の延長でWEB系を仕事にした感じの人間。

2019年3月から合同会社DMM.comでエンジニア。
でもまたGCPとかFirebaseとかVue.js使って新規サービス作りたいので、お仕事あったら教えてください。

コミケ3日目で10年以上売り子無欠勤
アズールレーン@竹敷でモバイルのUI、UX研究中(初嫁ジャベリン)

GitHub: https://github.com/uyamazak/

関わってる主なプロジェクト

続きを読む

LED8 子どもとユーザーテスト

LEDを消したり点けたりするだけのLED8ですが、1歳8ヶ月の子どもが一緒に遊んで楽しんでくれます。詳細は動画。

LED8 - You can L-chika on your browser

www.youtube.com

子供でも操作できるようにUIを考えたり、勉強になるのでおすすめです。

ハムスターホイールの回転をホールIC (SK8552G) とネオジム磁石とRaspberry Piで検知する

いろいろあるとハムスターの回転車の回転をラズパイから検知したくなると思います。

回転数さえプログラム側で検知できれば、あとはLinux & WEB系技術でいろいろできそうなので、まずはそこを試してみました。

ちなみに我が家にはハムスターはいないので、回転はエミュレーター(人間の手)を使用してテストしています(金魚は3匹います)。

f:id:uyamazak:20190806225345j:plain

要件

  • 早く回してもちゃんと計測できること
  • 回転車を選ばない設計(飼い主は好きなやつ使いたいよね)
  • シンプルがいいなぁ

考えたこと

ネットで調べて、下記のようないろいろ案を考えた。

1. 電極で物理スイッチ案

最初は、電極を車と土台につけて、回る時に接触させてスイッチにすればいいかと思ったけど、回転部分に電気通すの難しそうだし、回転車の種類を選びそう。
また回転時の接触が抵抗になったり、音もしそうなので却下。

2. フォトリフレクタ案

検索したところ、下記のようにフォトリフレクタを使っている人がいました。
光の反射を検知するので、無接触で良さそうだけど回路が難しそうっていうのと、外部の光を誤検知するっていう話もあったので保留。

https://ma-chanblog.com/raspi-ham-tacho/

3. 磁気センサー案

回転車に磁石を貼って、外側に何らかのセンサーを貼って検知する案。

とりあえずこれでいくことにした。

秋葉原秋月電子で購入。
http://akizukidenshi.com

磁気センサーには大きく2種類あったので、とりあえず2種類買ってきた。

3.1 リードスイッチ

http://akizukidenshi.com/catalog/c/creedsw/

名称的にはセンサーではなく、スイッチか。
ガラス管の中の線が、磁石が近づくと繋がり(なんで?)、線が繋がり電気が通るというシンプルなもの。
よくドアの開閉センサーとかに使われてるみたい。見たまんま線は2本なので、回路の途中に挟むだけ。

リードスイッチMKA-10110(5個入)
http://akizukidenshi.com/catalog/g/gP-03676/

5個200円くらいなので1個40円。
サイズ的にはガラス管の部分でだいたい10mm、太さ2mmぐらい。

動作時間

少数点以下のミリ秒ってよくわからんけど、十分早そう。

■動作時間:0.5ミリ秒
■復旧時間:0.3ミリ秒

3.2 ホールIC (ホール素子)

中身丸見えで、仕組みが分かりやすいリードスイッチと比べるとなんか高度なてくのろじーを感じる見た目。
iPadのカバーの閉じ検知とかもホールICを使っているらしい。

小さいくせして、下記のタイプはなんか抵抗も内蔵されており、単体で動きそうなのでこれにした。
電圧も3V - 20Vに対応しており、ラズパイの3.3V、5Vでそのまま使える。

ホールIC SK8552G SIP-3
http://akizukidenshi.com/catalog/g/gI-11029/

検索したら下記のような例もあったので安心。
https://raspberrypi.tokyo/sensor.html

だけど値段はリードスイッチと一緒で1個40円。とりあえず5個買った。
サイズは5mm * 3mm程度で実物見ると想像以上に小さい。足の間も狭いのでブレッドボードは大丈夫だけど、ジャンパワイヤーメスだと厳し目。
実際作るときは、ちゃんとハンダ付け(なるべくしたくない)して、何かケースとかカバーで周りを保護した方が良さげ。100均でなんか探すか。

動作時間

リードスイッチと単位が違い、超早いってのは分かる。

・立ち上がり時間:5μS
・立ちさがり時間:1μS

とりあえずスペック的にホールICの方が良さそうなので、こちらから試すことにした。


磁石

磁石は100均で買おうと思ってたけど、千石電商の2階でネオジム磁石を見つけたので、サイズ別3種類購入し、それを使用。
とりあえず買った中で一番大きいのを使用してる。家に子どもがいると飲み込むのが怖いので養生テープで包んでおく。

https://www.sengoku.co.jp/shop_01.html

回転車

ハムスターの回転車は下記15cmのものを使用。シリーズで下から2番めのサイズだけど思ったより大きく、回転車もしっかりしてて結構重い。
磁石を外周に貼って回しても揺れたりしないので、問題なさそう。

SANKO サイレントホイール15

SANKO サイレントホイール15

動作確認用Pythonプログラム

GPIOの使用にはgpiozeroのDigitalInputDeviceを使用。もうほとんどのデバイスはこのモジュールでいいのでは?
ラズパイZero専用っぽい名前はちょっと問題かも。

gpiozero.readthedocs.io


いちいち反応を画面で確認するのも面倒なので、ホールICの動作に合わせてLEDも光らせることにした。
プログラムの動作確認も含め、ON, OFFは回路上で行わず、Pythonプログラム上で行う。
小さいブレッドボードで、+-ラインが無いためGNDはホールICと共用した。

from gpiozero import LED, DigitalInputDevice
from time import sleep
sensor = DigitalInputDevice(2, pull_up=True)
led = LED(3)
count = 0

def active_callback():
    global count
    count += 1
    led.blink(0.03, n=1)
    print('active! {}'.format(count))

sensor.when_activated = active_callback

while True:
    sleep(1)

回路

回路図の作り方がわからないので写真で。

f:id:uyamazak:20190806224208j:plain

赤が3.3V, 青がGND, 白がGPIO。

SK8552G SIP-3は下記データシートを見ると、本体を欠けている方を上にして、左から1, 2, 3にらしい。データシート1しか書いてないけど。

http://akizukidenshi.com/download/ds/unisonic/SK8552.pdf

1はVcc(なんだこれ?), 2はGND(わかる)、3はV OUT(3.3v繋げばいいかな?)という感想だったので、消去法的に1にGPIOの2番、2はGND、3は3.3Vを繋げたところ、1発で動きました。

3本だったからなんとかなったけど、ICとかは無理なので、データシートの読み方勉強したい。

動作について

磁石が近づいたらONになるかと思ったら、離れるときだけ反応するようです。手でバッと動かしても距離さえ近ければ反応してる。

動作の様子

結構距離がシビアで、手だと安定して反応させるのは難しい。
なので、回転車にテープで磁石を貼り、下にブレッドボードを置いて試してみました。
1歳8ヶ月の娘も回してくれました。

www.youtube.com

今後の課題

リードスイッチも試してみたい。距離がホールICより取れるなら選択の余地あり。

磁石は今買ったネオジム磁石より強いのは、サイズや金額が現実的ではないのでそのまま。

磁石の固定は瞬間接着剤も買ったけど、微調整を考えると強力なテープで良さそう。

センサー側の固定が一番難しそう。針金かなんかを付けて微調整できるといいかな。ハムスターの振動などでズレても戻せるようにしないといけない。

回転車の設置方法も柵に固定とか台で設置、吸盤でとかいろいろあるので取り付け場所も考えないといけない。

あとセンサーは下に付けると、磁石が重みで下に行き、ゆらゆらした時にたくさん反応してしまうので上の方に付けたほうが良さげ。

画面なしでRaspberry Pi Zeroだけで運用したいので、反応時に光るLEDはこのまま付けておいて、スイッチでON,OFFはあったほうがよさそう。

【解決】 Python3のfirebase_adminでCollectionをon_snapshot()してるとhread-ConsumeBidirectionalStream caught unexpected exception

下記記事でやっていた処理だけど、寝る前に実行して、朝起きるころ見るとエラーを吐いて止まっている。
まだ解決してないけど、メモ。


Raspberry PiでPython3を使ってFirestoreにクエリする - GAミント至上主義

マシンはRaspberry Pi Zero WH。
OSは

pi@raspberrypi:~/led8 $ lsb_release  -a
No LSB modules are available.
Distributor ID: Raspbian
Description:    Raspbian GNU/Linux 9.9 (stretch)
Release:        9.9
Codename:       stretch

関係ありそうなパッケージのバージョンは下記。

firebase-admin==2.17.0
google-api-core==1.13.0
google-api-python-client==1.7.9
google-auth==1.6.3
google-auth-httplib2==0.0.3
google-cloud-core==1.0.2
google-cloud-firestore==1.2.0
google-cloud-storage==1.16.1
google-resumable-media==0.3.2
googleapis-common-protos==1.6.0

googleapis.github.io

エラー出力は下記。

hread-ConsumeBidirectionalStream caught unexpected exception <_Rendezvous of RPC that terminated with:                  
        status = StatusCode.INTERNAL                                                                                     
        details = "Received RST_STREAM with error code 0"                                                                
        debug_error_string = "{"created":"@1562627421.743478058","description":"Error received from peer ipv6:[2404:6800$
4004:801::200a]:443","file":"src/core/lib/surface/call.cc","file_line":1046,"grpc_message":"Received RST_STREAM with err$
r code 0","grpc_status":13}"                                                                                             
> and will exit.                                                                                                         
Traceback (most recent call last):                                                                                       
  File "/usr/local/lib/python3.5/dist-packages/google/api_core/bidi.py", line 633, in _thread_main                       
    response = self._bidi_rpc.recv()                                                                                     
  File "/usr/local/lib/python3.5/dist-packages/google/api_core/bidi.py", line 544, in recv                               
    return self._recoverable(self._recv)                                                                                 
  File "/usr/local/lib/python3.5/dist-packages/google/api_core/bidi.py", line 503, in _recoverable                       
    raise exc                                                                                                            
  File "/usr/local/lib/python3.5/dist-packages/google/api_core/bidi.py", line 493, in _recoverable                       
    return method(*args, **kwargs)                                                                                       
  File "/usr/local/lib/python3.5/dist-packages/google/api_core/bidi.py", line 541, in _recv                              
    return next(call)
  File "/usr/local/lib/python3.5/dist-packages/grpc/_channel.py", line 364, in __next__
    return self._next()
  File "/usr/local/lib/python3.5/dist-packages/grpc/_channel.py", line 358, in _next
    raise self
grpc._channel._Rendezvous: <_Rendezvous of RPC that terminated with:
        status = StatusCode.INTERNAL
        details = "Received RST_STREAM with error code 0"
        debug_error_string = "{"created":"@1562627421.743478058","description":"Error received from peer ipv6:[2404:6800:
4004:801::200a]:443","file":"src/core/lib/surface/call.cc","file_line":1046,"grpc_message":"Received RST_STREAM with erro
r code 0","grpc_status":13}"
>
Exception in thread Thread-OnRpcTerminated:
Traceback (most recent call last):
  File "/usr/lib/python3.5/threading.py", line 914, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.5/threading.py", line 862, in run
    self._target(*self._args, **self._kwargs)
  File "/usr/local/lib/python3.5/dist-packages/google/cloud/firestore_v1/watch.py", line 290, in close
    raise reason
google.api_core.exceptions.InternalServerError: 500 Received RST_STREAM with error code 0

検索するとPub/Subなどでも同じようなエラーを出すようで、Firestore特有の問題ではなさそう。

https://github.com/googleapis/google-cloud-python/issues/4234

Threadingを使っているので、簡単にはtryでExceptionを受け取ることができない。

2019/7/10 追記

Firestore: what possible cause of this exception: InternalServerError: 500 Received RST_STREAM with error code 0 · Issue #282 · firebase/firebase-admin-python · GitHub
こちらのやり取りでwatchオブジェクトの_closedプロパティを見て、再接続をするハックを見つけたので試してみる。
みんなちょうど60分で切断されているらしい。

python - How to detect realtime listener errors in firebase firestore database? - Stack Overflow

ソースを確認したところ、_closed以外にもis_activeというプロパティもあったので、こちらも確認してみる。
google-cloud-python/watch.py at master · googleapis/google-cloud-python · GitHub

# 省略
def main():↲
    try:↲
        watcher = commands.collection_ref.on_snapshot(↲
            update_callback)↲
    except Exception as e:↲
        print(e)↲
        exit()↲
    while True:↲
        print(datetime.now())↲
        sleep(60)↲
↲
        if not watcher.is_active:↲
            print('is not active!!!')↲
            exit()↲
↲
        if watcher._closed:↲
            print('_closed!!!!!')↲
            exit()↲

7/11 解決。

_closedプロパティを確認して、Trueだったら再度開始することで数日以上動き続けている

# 省略
def main():↲
    try:↲
        watcher = collection_ref.on_snapshot(update_callback)↲
    except Exception as e:↲ 
        print(e)↲
        exit()↲
    while True:↲
        # print(datetime.now())↲
        sleep(60)↲
        if watcher._closed:↲
            print('_closed!!!!!')↲
            watcher = collection_ref.on_snapshot(update_callback