2023年12月7日

手のひらサイズの地震計開発キットEQIS-1を作った話

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

前回の記事からピッタリ丸1年ぶりの記事です...

気が付けば2023年も残り1ヶ月を切っていて、そういえばアドベントカレンダーの季節だなぁ...と思い「防災アプリ開発 Advent Calendar」を覗いてみると たまたま去年と同じ12/7が空いていたので滑り込みで7日目として参加させていただきます。


現在、日本全国には地震を観測するための震度計を設置した地点「震度観測点」が4371地点存在しています。

奄美大島の瀬戸内町にある防災科研の震度観測点「瀬戸内強震観測施設」
KGS032 瀬戸内 (4652520 瀬戸内町古仁屋) / 2023.05.14 撮影

熊本県大津町にある防災科研の震度観測点「大津強震観測施設」
KMM005 大津 (4340320 大津町引水) / 2023.07.21 撮影

この震度観測点は気象庁、地方公共団体、そして防災科学技術研究所が設置したもので、各震度観測点での計測データを元に緊急地震速報や各地の震度などが発表されています。

この震度観測点ですが、人口の多い都市部を中心に全国を網羅するように整備されているものの、よく見ると疎な地域も存在します。
例えば、熊本地方気象台のある熊本地方合同庁舎の敷地内に気象庁の震度観測点「熊本西区春日」がありますが、地図を見ても分かるとおり目と鼻の先の白川を渡った先は熊本市中央区です。

気象庁 震度観測点のページより(注釈は筆者が記入)

熊本市西区は大半が山地(金峰山)であり、実際には区の東部から南西部にかけて住民が住んでいるため、ある意味で居住地域で見た時の中心にあるようにも見えます。
しかし、震度観測点から熊本西区役所までは直線で約3.9km、金峰山西側の河内町付近までは約10km離れています。(東京都心部には1〜3km間隔で設置されているのに...)

さて、そんな震度観測点から離れた場所に住んでいる方やマンションの高層階に住んでいる方、その他「気象庁発表の震度と体感が違う...」と感じる方、あるいは自宅の詳細な震度が知りたいという地震マニアの方向けに... コンパクトで、見た目が整っていて、データも取れて、なるべく安価な震度計を提供できないだろうか、と考えて作ったのが「地震計開発キット EQIS-1」です。

前置きが長くなりましたが... 今回はEQIS-1の開発から販売までのお話を記録も兼ねて書こうと思います。


色々ある(あった)けど決定版がない個人向けの地震計


かつて、家庭用地震計と銘打って販売された「グラグラフ」やオムロンが販売していた音声地震警報器「D7G-A01L 揺れっ太」など、製品として個人でも購入できるスタンドアローン動作(FM放送波やインターネット接続を要せず、搭載したセンサーで揺れを検出する)が可能な地震計が販売されていました。しかし、テレビや携帯電話で得られる緊急地震速報や精度の高い地震情報に押されたのか、これらの製品も販売終了してしまい、長いこと個人で購入できる手軽な地震計が市場に存在しない状態が続いていました。

昨年、同人ハードウェアとしてRaspberry Pi Picoを使用した「PiDAS」が登場していましたが、組み立ての手間が大きいことに加えて公式ファームウェアにバグが存在しており、メンテナンスされていない状況が続いています。

そこで、公式リポジトリにissueを立てていたNoneType1氏、PiDAS向けの改良ファームウェア(PiDAS Plus)を製作していたingen084氏、そして...

という私のツイートに対して

反応してもらったcompo031氏に声を掛け、地震計開発プロジェクトをスタートしました。

目標は前述の通り、小型、ケース入り、データが取れる、なるべく安価であり、個人でも購入できること、としました。また、文字を表示できる表示器(OLED)を搭載し、Wi-Fi接続とインターネットへのデータ送信に将来的に対応できる構成を目指しました。


加速度センサーの選定

当初、アナログ加速度センサとA/Dコンバータという構成を考えていましたが、手頃な12〜16bitのADCが軒並み在庫切れ&価格高騰しており、当初使用予定だったKionix社(現ローム)のアナログ加速度センサも生産終了で将来的な部材確保に懸念があったため、デジタル加速度センサを使用することにしました。
最初は手頃で入手性に優れるSTMicroelectronics製のLIS3DHを使用することを検討していましたが...

compo031氏による検証の結果、震度2以下を判別するのが厳しいということが分かり、NoneType1氏から提案されたSTMicroelectronics製のLSM6DSO(X)を検討に加え、最終的にコストと精度のバランスからLSM6DSOを採用しました。

この辺りの検証については、compo031氏による12/2の記事「地震観測を目的とした加速度センサ5種の性能比較」で詳細にまとめられていますので、併せて読んでいただければと思います。


XIAO拡張基板としてのボード設計

使用する加速度センサが決まったら、次はマイコンの選定です。

当初、Wi-Fi接続できるESP32シリーズやRaspberry Pi Pico RP2040などがワンボードになったものを作ろうと考えていましたが、枯れていて良さそうなESP32-WROOM-32はNRND(新規設計非推奨)で、出たばかりのESP32-S3はノウハウが足りていないため開発に時間が掛かることが予想され、RP2040は周辺回路が地味に面倒... ということで、マイコン周りの回路設計期間を短縮しつつ、これらの選択をユーザーに任せることができるよう、Seeed XIAOシリーズを搭載できる加速度センサ搭載拡張基板という建て付けで進めることにしました。

XIAO ESP32C3(左)とXIAO RP2040(右)

XIAOシリーズの拡張基板として設計することで、USB-Cコネクタや電源周りについて考える必要がなくなり、フォームファクタが統一されているため今後登場するかもしれない新しいマイコンを搭載したXIAOに対して(基本的に)特別なハード的変更を加えることなく自動的に対応する(別途ソフトの対応は必要)というメリットがあります。ただし、Seeed社がXIAOシリーズの販売を辞めてしまった場合は共倒れになる可能性もあるので諸刃の剣ではありますが... 今はXIAOが息の長い製品群であることを祈りながら乗っからせてもらいました。

既製品のマイコンボードを使ってしまえば、ここに繋がるセンサの回路だけを考えれば良いので非常に楽ちんです。
今回は、LSM6DSOをSPIで、震度を表示する表示器をI2Cで接続し、ケースの外からアクセスできる設定用ボタンとして2つのタクトスイッチも搭載することにしました。
表示器は制御チップSSD1306を搭載した0.96インチのOLEDを採用しました。

XIAOを使用したことで基板が2段重ねになり全体のフットプリントを小さくすることができたため、これに合うようなコンパクトかつ固定しやすい形状のケースを探します。

今回は、タカチ電機工業のプラケースの中から、固定に便利そうなフランジ足付きプラスチックケースTWF4-2-5を採用しました。TWFシリーズの中では最小サイズのものです。

ケースの図面PDFに記載の推奨基板形状を元にざっくりと基板外形をKiCadのEdge.Cutsレイヤーに描き、ざっくりと部品を配置、最終的にはケースの実寸をベースに調整をして基板形状を確定させました。

OLEDはデフォルトでピンヘッダがついているため、これを挿すだけで接続できるようにピンソケットを基板側に用意する形にしました。

あとは都合が良いように部品を配置しつつ配線し、シルク印刷がカッコ良くなるようにお絵描きしたら基板の設計は完了です。

3Dビューアーで見た時の完成図はこんな感じ。OLEDの基板も含めると3階建てのような内部構造となりました。


念の為、アートワークを実寸サイズで印刷してケースやXIAOと位置を合わせて確認し、問題がなかったのでガーバーデータを出力して基板製造サービスに発注しました。


ケースの加工

基板と部品配置からTWF4-2-5に開ける穴の位置を決めていきます。
タカチ電機工業のサイトからSTPファイルをダウンロード(要会員登録)し、KiCadから書き出したSTEPファイルと一緒に3D CADに読み込み、それぞれの位置を合わせた後に穴を開けます。

俯瞰

ケース上面のOLED部分(左)とケース側面のUSB-C部分(右)

当初、OLEDの窓部分は切削したままの形でしたが、3D CAD上でテーパーをつけてレンダリングしてみたところ、テーパー有りの方が見やすいのでは...?ということでテーパー加工を追加しています。

左はテーパー有り、右はテーパー無し
同じ角度から見た図で比較すると、テーパーがある方がOLED表面が見える面積が広い

このテーパー加工費が地味に高く、発注個数によりますが初回ロットでは1個あたり200円程度のコストが掛かっています... (千石電商で未加工品のTWF4-2-5が1個買えてしまう...)

また、内部で基板とXIAOの2階建てにしたことで、USB-Cコネクタ穴の位置がケース側面の中央付近になり、ケースにわずかな傾斜(抜き勾配)がついているためUSB-Cジャックの端面(差し込み口)がケース外側に向かって迫り出す形となって、結果的にUSB-Cケーブルのコネクタの長さを気にせずに接続できるようになりました。
当初、ケースの厚みが結構あるため、コネクタ部が長いUSB-Cケーブルを別途用意する必要があるかもしれない...と思っていたので、XIAOを採用して2段重ねにしたことが功を奏した形です。

ケースの加工図面が出来たら、マルツのプロトファクトリーにデータを送って見積もりしてもらいます。

前回記事で作ったGNSS受信機と違って今回はケース表面の印刷はありませんが、10個発注した場合の単価は未加工品の約10倍程度になりました。個数が増えると若干単価も下がっていきますが、マシニング加工は高い... ただし、仕上がりは美しいので届いた時の満足度も高いです。


完成〜販売に至るまで

発注していた基板とケースが届いたら、いよいよ組み立てです。

今回はXIAO RP2040を使用し、XIAO 6DoF Ext. 基板にXIAOとピンソケット等をはんだ付けしていきます。

組み立て方法はEQIS-1公式サイトに掲載しているため詳細は割愛しますが、設計中にKiCadの3Dビューアーで見た通りのものが組み上がります。(当たり前と言えば当たり前ですが...)

ファームウェアには、ingen084氏が開発した計測震度の計算とOLEDへの表示を行うプログラム(ingen084/seismometer)を使用し、これをXIAO RP2040に書き込んだら完成です。(以下の動画は開発中のものであり、現行のものと挙動が異なります。)

お好みのXIAOと青または白色のOLEDを購入して自分で組み立てるEQIS-1【基板+専用ケースセット】と、私が夜なべして組み立てている【OLED青/完成品】【OLED白/完成品】の3種類を、秋葉原のラジオデパート1階にある家電のケンちゃんにて8/20に販売開始しました。

また、ingen084氏のファームウェアを書き込み済みの【OLED青/ソフト書き込み済み完成品】【OLED白/ソフト書き込み済み完成品】を10/28から販売しています。

ちなみに、ingen084氏のファームウェアは計測震度を算出するためのフィルタに防災科研が持っている特許5946067号の仕組みを含んでおり、また、EQIS-1のハードウェアと共に使用する場合は装置の仕組みも含めた特許4229337号も絡んできます。このため、【ソフト書き込み済み完成品】として販売しているEQIS-1については防災科研とライセンス契約を締結して販売しています。

EQIS-1の組み立てキットと完成品(ソフト無し)で販売しているEQIS-1については、あくまで利用者が自らプログラムを開発する「地震計開発キット」という前提に従い、前述の特許に関係するライセンス契約は締結していません。(この見解についても確認済みです)
そのため、組み立てキットを作って、あるいは組み立て済みを買ってきてingen084氏のファームウェアを書き込んで転売したりすると特許権の侵害になる恐れがありますのでご注意ください。


昨年のGNSS受信機に続いて、2023年は手のひらサイズの地震計「EQIS-1」を製作しました。
年を追うごとにやる事がどんどん増えて、なかなか趣味のハード製作に割ける時間が確保できずに減りつつありますが、EQIS-1はセンサの検証やファームウェアの開発をしてくださる方がいらっしゃるおかげで世の中に出す事ができました。
仕事以外において役割分担したコミュニティ的な開発はほとんど初めてだったので、非常に有意義なプロジェクトだったと思います。

さて、来年は何を作りましょうか...

2022年12月7日

みちびき災危通報を受信するためにGNSS受信機を作った話

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

前回の記事から約4年3ヶ月ぶりの投稿です...

定期的に文章を書かないと書けなくなってしまうので何か書かなければ...と思っていたら、ちょうどタイミングよくアドベントカレンダーの季節だったので 防災アプリ Advent Calendar 2022の7日目として参加させていただきます。


現在、日本からオーストラリアにかけての上空に「みちびき(初号機〜4号機、初号機後継機)」と命名された準天頂衛星(quasi-zenith satellites)が4機(待機運用中の初号機も合わせると5機)飛んでいます。
この衛星を利用した準天頂衛星システムみちびき(QZSS)は既存のGPSやGLONASSといった衛星測位システムを補完し、常時1機は日本から高仰角で見通せる位置に滞空するように設計されています。

高層ビルの多い都市部や山間部において低仰角を飛んでいるGPSをうまく捕捉できない場合でも高仰角を飛ぶQZSSを合わせて利用することで測位精度を高めることができ、さらにサブメータ級(誤差1mレベル)やセンチメータ級(誤差数cm)での測位が可能になる補強情報の配信といった従来よりも更に高精度な測位を行うための仕組みも入っていたりします。

さて、そんな「みちびき」ですが、衛星測位以外のサービスとして、避難所の情報や状況を収集する衛星安否確認サービス「Q-ANPI」や、気象庁から発信された防災情報を配信する災害・危機管理通報サービス「災危通報」も提供されています。
Q-ANPIの送信には特殊な送信機(Q-ANPIターミナル)とソフトウェア、パソコン一式が必要なため一般人は利用できず、発災時の避難所や防災訓練、そしてNERV災対車の出動時などでしか実際の機材や使用している様子をお目にかかれませんが、災危通報は機材を揃えれば誰でも受信することができます。

災危通報では、緊急地震速報、震源、震度、南海トラフ地震、津波、北西太平洋津波、火山(噴火・噴火警戒レベル)、降灰予報(速報・詳細)、気象警報、土砂災害警戒情報、竜巻注意情報、洪水(河川氾濫警戒・発生)、台風、海上警報といった情報が配信されています。

例えば、緊急地震速報(12/6に配信された訓練/試験メッセージ)は以下のような感じ。

$QZQSM,58,9aaf8e1880000324000031000548c5e2c000000003dff8001c0000110a36474*78

これを読めるようにデコードすると...

防災気象情報(緊急地震速報)(発表)(訓練/試験)
*** これは訓練です ***
緊急地震速報
強い揺れに警戒してください。

発表時刻: 12月6日13時0分

震央地名: 日向灘
地震発生時刻: 6日13時0分
深さ: 10km
マグニチュード: 7.2
震度(下限): 震度6弱
震度(上限): 〜程度以上
島根、岡山、広島、山口、香川、愛媛、高知、福岡、佐賀、長崎、熊本、大分、宮崎、鹿児島、中国、四国、九州

以上のようになります。

ちなみに、過去に受信した配信データ試験データはGistで公開しているので、他の情報がどんな内容か気になる方や後述のデコーダを試したい方はご利用ください。

配信の頻度は4秒に1回のため緊急地震速報を早く受け取りたいといった用途には向かなかったり、地震の情報も都道府県単位かつ震度4以上の地域のみのため気象庁防災情報XML形式電文の受信を置き換えできたりする物ではありません。

しかし、空が見えればインターネットが繋がっていなくても防災情報を取得でき、情報を得るための契約や料金は不要です。
みちびきは国費で打ち上げられて運用されているプロジェクト、すなわち我々の払った税金が宇宙を飛び、防災情報を地上に降り注いでいる...と考えると受信してみたくなりませんか?


SPRESENSEによる災危通報の受信

災危通報の受信方法を検索するとよく出てくるのが、SONYが販売しているSPRESENSEというボードを利用した受信です。

みちびきから配信されている災危通報は、GPSやQZSSでの測位に利用しているL1C/A信号ではなく、サブメータ級測位補強に利用されるL1S信号で配信されており、このL1S信号に対応した受信機が必要です。
SPRESENSEはL1S信号の受信に対応していて、Arduino IDEでシリアルモニタにデータ(QZQSMセンテンス)を出力するサンプルスケッチも公式で提供されているため手っ取り早く災危通報の受信を試すことが可能です。

しかし、見晴らしの良い場所なら気にならないものの、オンボードのアンテナだと受信感度がイマイチだったり、USBケーブルで引き回して窓際や屋外に設置するのは面倒だったりするので外部アンテナを接続したくなります。
そんな人のために、外部アンテナの使用方法も公式で公開されているのですが... 使用するのは表面実装用のU.FLコネクタで、オンボードアンテナから外部アンテナに切り替えるために1005サイズのチップ抵抗を移設する必要があるという、ハンダづけに慣れている人でもかなり神経を使う作業。 

しかも、U.FLコネクタの嵌合サイクルは30回程度なので何度も着脱するのはお勧めされないため、ボードと付随するケーブルが入るようなケースを作って固定したり、ホットボンドで固定したりといった工夫が必要です。


他の受信機の検討

みちびき対応製品紹介(災害・危機管理通報サービス対応製品)のページに記載されているカーナビやウェアラブル機器以外の専用受信機(データをUSBやUARTなどで取得可能な物)で、個人が購入可能な物は安くても5万円程度、法人で購入可能な物の場合は10万円以上だったりします。

同じく対応製品として掲載されているu-blox社のGNSS受信モジュール「ZED-F9P」は、モジュールを採用したボードが各社から市販されていますが、RTK測位の基準局としても使用できる高機能なモジュールのため安くても4万円後半からとお高め。
昨年新たに対応製品として追加された、同じくu-blox社の「MAX-M10S」を採用しているボードは海外のSparkFunMIKROEGNSS OEMなどから販売されていますが、円安の影響もありいずれも7000円程度、アンテナはSMAコネクタで接続できますが、データ出力がUARTのためシリアル変換が別途必要でSPRESENSEと比較しても帯に短し襷に長し...

というわけで、理想のGNSS受信機を作ることにしました。


オリジナルのボード設計

手のひらサイズで、USB-C接続で、SMAコネクタを採用して外部アンテナを接続できる、そしてせっかくなら素のボード状態ではなくてケースに入っているもの、というのを目標に設計開始。

0からGNSS受信機を作るのは無理なので、先ほどの対応製品として掲載されていたu-blox社のGNSSモジュールMAX-M10Sを採用し、同社が公開しているMAX-M10S Integration Manualを参考にアンテナ周りの回路とバッテリーバックアップ用のコイン電池、USBシリアル変換ICのCH340、ステータス確認のためのインジケータLED、USB-Cコネクタ、SMAコネクタを追加。回路図をKiCadで書いていきます。

回路図ができたら、PCBエディターを開いて部品をざっくり配置し、全体のサイズ感とコネクタの大きさからケースを探し、今回はタカチ電機工業のポータブルプラスチックケースCS75Nを採用しました。あと、この時点で実装する部品も秋月電子通商とDigi-Keyで調達しておきます。

KiCadでケース図面の推奨基板形状の通りに外形をEdge.Cutsレイヤーに書いて、コネクタ、ジャック、部品を配置、配線していきます。

部品の3Dモデルを紐づけておくと3Dビューアーでボードの完成形が確認できるのでKiCad素晴らしい...

念の為、アートワークを等倍で印刷した紙に部品を載せてフットプリントがズレたりしていないか確認し、問題なさそうだったのでガーバーデータを出力して一式をzipにまとめて基板製造サービス、いつも使ってるElecrowに発注しました。


ケースの設計

基板が届くのを待っている間にケースの加工図面を書いていきます。

タカチのサイトから3D CAD用STPファイルをダウンロード(要会員登録)して、Fusion360に読み込み。
そして、KiCadからSTEPファイルを書き出して読み込みます。が、KiCadからエクスポートしたSTEPファイルをそのまま読み込んでも部品がほとんど載ってない状態になってしまうので、こちらの記事を参考にしてFreeCADとKiCadStepUpプラグインを使用して基板、配線、部品をひとまとめにしたSTEPファイルを作成します。

基板はver1.0の物(コネクタの位置が変わっていないので流用)

作成したSTEPファイルを改めてFusion360に読み込み、ケースの固定位置に基板を配置。
USB-CコネクタやSMAコネクタが通る穴をスケッチに起こし、押し出しでモデルに穴をあけます。

2D図面を作成して出力して、まずは図面を見ながらフライス盤で穴あけ(手作業)。

そんなこんなやってると基板が届いたので、1枚だけ手ハンダで部品を実装してケースに入れてみます。

この段階ではインジケーター用の穴は開けてませんが、USB-CもSMAコネクタも干渉せずにケースに収まることを確認。


動作テストと災危通報受信プログラム

アンテナを接続し、PCとも接続、u-blox社が提供しているu-center2を使用して動作確認と災危通報を受信できるように設定します。


CFG-MSGOUT-UBX_RXM_SFRBX_UART1を1(有効)に設定することで、UART1(シリアルポート)に衛星航法データが出力されるようになります。
これを有効にすると、みちびきから配信されている災危通報を取得できるようになります。

出力されるのはNMEAセンテンスではなくバイナリ形式のため、これを解析する必要があります。
災危通報のメッセージは、0xb5 0x62 から始まるUBXプロトコルのバイナリ形式で出力され、必ず以下のヘッダで始まります。

b5 -> UBX Preamble sync character 1
62 -> UBX Preamble sync character 2
02 -> Message Class (RBX)
13 -> Message ID (SFRBX)
2c 00 -> Payload Length (Little Endian, 44 bytes)
05 -> GNSS ID (5=QZSS)

NMEAセンテンスは \r\n の改行コードで終わりますが、UBXプロトコルのバイナリ形式メッセージはヘッダの Payload Length を読んでメッセージの終端を得る必要があります。

上記のヘッダに続き、メッセージごとのデータおよびチェックサムが入っています。

07 -> Satellite ID (7+182=189=QZS03)
01 -> Signal ID (1=L1S)
00 -> Frequency ID (Only used for GLONASS)
09 -> The number of data words contained in this message (8+9*4=44)
45 -> Tracking channel number
02 -> Message version (0x02)
00 -> Reserved 0
55f5ad9a170580110000008e00000000000000000000000010000000b1aa5aebff9483b2 -> QZQSM(災危通報データ)
e2 -> ck_a (Checksum)
cd -> ck_b (Checksum)

この仕様をもとに、シリアルポートを開き、みちびきのユーザインタフェース仕様書(災害・危機管理通報サービス, IS-QZSS-DCR-010) 4.3.1. Sentence format で示されている $QZQSM から始まるNMEA形式のセンテンスに変換して出力するプログラムをPythonで書きます。

import sys
import argparse
import operator
from functools import reduce
import serial

satellite_id = {
    184: '56',
    185: '57',
    189: '61',
    183: '55',
    186: '58',
}

def nmea_checksum(sentence):
    data = sentence.strip("$").split('*', 1)[0]
    cksum = reduce(operator.xor, (ord(s) for s in data), 0)
    return cksum

def ubx_checksum(message):
    ck_a = 0
    ck_b = 0
    i = 0
    while i < len(message):
        ck_a = (ck_a + message[i]) & 0xff
        ck_b = (ck_b + ck_a) & 0xff
        i += 1
    return ck_a, ck_b

def ubx2qzqsm(line):
    if line[:7] == b'\xB5\x62\x02\x13\x2C\x00\x05': # UBX-RXM-SFRBX, 44 bytes, QZSS
        satId = satellite_id[line[7] + 182] # PRN -> Satellite ID
        data = b''
        for i in range(9):
            data += bytes((line[14+3+i*4], line[14+2+i*4], line[14+1+i*4], line[14+0+i*4]))
        if data[1] >> 2 == 43 or data[1] >> 2 == 44: # Message Type 43=JMA-DC Report, 44=Other
            dcr_message = (data[:31] + bytes((data[31] & 0xC0,))).hex()[:-1] # 256-4=252 bit
            sentence = '$QZQSM,' + satId + ',' + dcr_message + '*'
            return sentence + format(nmea_checksum(sentence), 'x')


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Print QZQSM NMEA format sentence')  
    parser.add_argument('port', help='serial port. ex: /dev/ttyUSB0')
    parser.add_argument('baudrate', help='baudrate. ex: 115200')
    parser.add_argument('-n', '--nmea', help='print other standard NMEA sentence', action='store_true')
    args = parser.parse_args()

    with serial.Serial(args.port, args.baudrate) as ser:
        while True:
            line = b''
            nmea_flag = False
            ubx_flag = False
            count = 0
            payload_length = 0
            while True:
                if ubx_flag:
                    if count > 4 and payload_length == 0:
                        payload_length = int.from_bytes(line[4:5], "little")
                    if payload_length > 0 and count == payload_length + 8: # header 6 bytes + checksum 2 bytes
                        break
                b = ser.read()
                if b == b'$' and not ubx_flag:
                    nmea_flag = True
                if b == b'\x62' and line == b'\xB5':
                    ubx_flag = True
                if b == b'\n':
                    if line.endswith(b'\r'):
                        line += b
                        break
                    else:
                        line += b
                else:
                    line += b
                count += 1

            if args.nmea and nmea_flag:
                sentence = line.decode().strip('\r\n')
                ck = nmea_checksum(sentence)
                if format(ck, 'x') == sentence.split('*', 1)[1]:
                    print(sentence)

            if ubx_flag:
                ck_a, ck_b = ubx_checksum(line[2:payload_length+6])
                if line[-2] == ck_a and line[-1] == ck_b:
                    sentence = ubx2qzqsm(line)
                    if sentence:
                        print(sentence)

pySerialをインストールして、シリアルポートとボーレートを指定して実行。

pip install pyserial
python read.py /dev/tty.usbserial1410 115200

以下のようにNMEA形式のセンテンスが得られます。

$QZQSM,57,9aadf5b1118002c3f2587f8b101962082c41a588acb1181623500012b979380*20

上記のセンテンスをデコーダにかけると災危通報の内容を人間が読める形で出力できます。

git clone https://github.com/9SQ/azarashi.git
cd azarashi
python setup.py install

import azarashi
sentence = '$QZQSM,57,9aadf5b1118002c3f2587f8b101962082c41a588acb1181623500012b979380*20'
report = azarashi.decode(sentence, msg_type='spresense')
print(report)
以下のような出力を得られます。

防災気象情報(海上)(発表)(通常)
海上警報が発表されました。

発表時刻: 11月12日17時35分

警報等情報要素: 海上濃霧警報
サハリン東方海上

警報等情報要素: 海上濃霧警報
サハリン西方海上

警報等情報要素: 海上濃霧警報
網走沖

警報等情報要素: 海上濃霧警報
宗谷海峡

警報等情報要素: 海上濃霧警報
北海道西方海上

警報等情報要素: 海上濃霧警報
北海道東方海上

警報等情報要素: 海上濃霧警報
釧路沖

警報等情報要素: 海上濃霧警報
日高沖

今回はデコードにオープンソースのライブラリを使いましたが、災危通報の仕様書は公開されているので自分で1から実装することも可能です。

いくつかの場所で受信テストをしてみると、どうやらロケーションとタイミングが良ければ運用中のみちびき4機すべてから受信できそうなことが分かったため、衛星が現在どこを飛んでいるのか確認できるトラッカーを作って確認しながら受信テストを実施したりしました。


ケースのデザインとマシニング加工発注

問題なく災危通報の受信と解析ができたので、GNSS受信機として売り物になるように仕上げていきます。

先ほどのFusion360のモデルにインジケーターLEDの穴を3つ追加であけて、2D図面を出力。
ついでにDXFファイルも出力してIllustratorに持っていき1:1で開き、ケース表面の窪みと3つの穴のアウトラインだけ残してお絵描きをします。

出力した2D図面とIllustratorファイルをマルツのプロトファクトリーに送って見積もりをしてもらいます。

詳細の費用は伏せますが、費用感としては千石電商で1個180円で買えるCS75N-Wが、インクジェット印刷とマシニング加工をすると90倍くらいの価格になります(白目
1個の場合なので、10個、100個と数が増えると単価は下がるため、今回はひとまず10個で発注しました。

並行して、チップLEDの表面からケース表面までの長さをFusion360で測って、インジケーターLED直上の穴にはめるための導光棒をAliexpressで発注しておきます。

届くのを待つ間に、基板に部品を実装します。
流石に何個も手ハンダでやるのはつらいので、基板と一緒に注文しておいたステンシルとペーストはんだを使い、ホットプレートリフローで一気に実装します。
一気にやると言ってもチップ部品はピンセットを使って1個ずつ乗せ、プレートの面積が10 x 10cmのET-10を使ったので同時に焼けるのは2枚ずつで、1時間で3〜4個できるかなといったところ。(実家に帰ればチップマウンターオーブンがあるので多少早いんだけれど...)

加工ケースとLED導光棒は2週間程度で届き、届いたケースに部品を実装した基板と導光棒を固定して...

加工精度も問題なく、Fusion360でシミュレーションした通りに完成です。


というわけで、自分の欲しいGNSS受信機を自分で作ってみた話でした。

ちなみに、この受信機を利用してNERV災対車に搭載する防災情報サイネージも作ったりしています。

表面に印刷のない零号機を使用中

ものづくりは様々な要素が組み合わさったパズルを作っていくような感じで面白いし、自分のこだわりを詰め込みながら形ある物を作っていくのは楽しいですね。

今回設計したGNSS受信機は近日発売予定です。
詳細はドキュメントも兼ねたサイトにて公開しますので、興味のある方は買ってもらえると嬉しいです。

追記:こちらで販売中です。https://prioris.booth.pm/items/4308281

追記2:ケース無しのMicroUSB版とRaspberry Pi向け版の2種も作りました。秋葉原の家電のケンちゃん(店頭&オンライン)にて販売中です。

GNSS受信基板 GR-M10-B/S-B45

https://www.kadenken.com/view/item/000000001535

GNSS受信基板 GR-M10-RP (for Raspberry Pi)

https://www.kadenken.com/view/item/000000001536

2018年9月11日

続・気象庁防災情報XMLの受信と周辺サービスをGCPに移行した話

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

まず、北海道胆振東部地震で被災された方にお見舞い申し上げます。
今回の地震は熊本地震と同様の最大震度7が観測された為、自分自身が2年前に体験した熊本地震の時の様子を思い出しながら、今必要な情報は何か?を考えて仕事をしていました。

それにしても、まさか札幌にお天気カメラを設置しに行った3日後に大地震が発生するとは思ってませんでしたが...

設置の翌日、台風が北上してくる中、15時の便にフライトを振り替えて新千歳空港から飛び立ち、17時ごろに羽田についたら以降の便が全便欠航していた&翌日(5日)も台風による悪天候でダイヤが乱れていたので、振り替えていなければ人生で2度目の震度7クラス地震を体験するかもしれないところでした...

さて、今回は、気象庁防災情報XMLの受信と周辺サービスをGCPに移行した話 の続きのお話です。
※これは防災情報デザインと配信に関する個人的な研究の一環として私費でやっている物の話です。

あれから半年程度運用を続けてきましたが、全体の運用に毎月1500円〜1700円の費用が掛かっていました。(それでも十分に安い)

この「全体」というのは、気象庁XML受信のためのサブスクライバ&閲覧のためのビューアをホストしているAppEngine、XMLやJSON、画像保存のためのStorage、QUAKE.ONEをホストしているFirebase、その他内部的な処理に使っているCloud Functions (15個の関数)、Pub/Sub (5個のトピック)、JMAXMLのメタデータや地震情報、気象警報・注意報のステートを保存しているDatastoreを含みます。

例えば、2018年6月に掛かった実際の費用はこんな感じ。


全体の費用として一番大きいのが、Compute Engine g1-smallインスタンスx1で毎月2000円から継続利用の割引で約1400円と実に全体の80%以上。
このインスタンスは、Twitterへの投稿やPushbulletでのプッシュ通知の際に添付される震源・震度マップ画像を、headless Chrome&PuppeteerでレンダリングしてCloud Storageに保存するためにだけに使用されているものです。
f1-microインスタンスでは、CPUバーストが効いた状態でレンダリング(960x540)に10秒以上掛かる上に、地震が頻発すると何度目かのレンダリングでCPUバーストが効かずに実行がタイムアウトしてしまうという残念なことが発生したため、g1-smallインスタンスで運用していました。

ところが、2018年8月のアップデートで、Cloud FunctionsにてNode.js 8系が利用できるようになると共にheadless Chromeが利用可能であることがアナウンスされました。

Introducing headless Chrome support in Cloud Functions and App Engine | Google Cloud Blog

これに伴って、構成を以下のように変更。


(クリックで拡大してご覧ください)

大きな変更点は以下の通りです。

1. 画像のレンダリングをCloud Functionsで実行
2. 画像レンダリング用のg1-smallインスタンスを廃止
3. WebSocket配信サーバをConoHaからf1-microインスタンスに変更

これにて、毎月の利用額がCloud Storageのオペレーションとデータの保管に掛かる費用のみとなり、200円以下となりました。
(f1-microインスタンス1台はAlways-freeなので0円)

Cloud Functionsでのレンダリングは、asia-northeast1のメモリ2GBで最短3900ミリ秒(ページの読み込みのために入れたwait 3000ミリ秒を含む)、最大でも7秒程度とブラウザを利用した画像の生成(かつ無料枠範囲内)の方法としては優秀。
もちろん、スピードが重視されるような防災情報配信用途としてはとてもとても遅い(ゲヒルンで製作・運用している描画エンジンはフルHD1枚200ミリ秒程度)ので、あくまで実験・実証としての利用、またはスピードが求められない場面での利用に限られるのですが...。

ひとまず、ConoHaに全てを載せていた時の価格(毎月1000円程度)と比較して1/5くらいになったので、クラウドは上手く使えば安くなりますよ事例として記しておきます。
ただし、これはアクセス数に大きく依存するので、今くらいの利用者数であれば200円で済むものが、利用者が数倍になればGCPに支払う費用も数倍、数十倍以上になり得るということです。
あくまで個人的な実験・試行というところでの運用を続けているので、流石にそういう状況になったら続けることはできませんが、こういった複数の要素を使ったサービスを数十円、数百円のレベルから始めることが出来るのがGCPやAWS、Azureといったクラウドのメリットということでしょう。

2018年7月14日

HDピコレーザープロジェクター 自作キットを組み立ててみた

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
つい先日、Twitterのタイムラインを眺めていたら、こんなものを発見。


これは試してみるしかない、と1台買ったので組み立ててみました。
ちなみに、お値段は購入時点で税込15,120円。限定数販売後は値上げするようです。


届いた中身はこんな感じ(写真には写ってませんが、レーザー出力部とHDMI信号を受ける基板を繋ぐフレキも入っています)


ひとまず、ベースとなるアルミプレートに基板とレーザー出力部を仮置きしてみる。
基板はそのまま「1」の番号が振ってあるネジで5箇所を固定。


レーザー出力部に付属のフレキを「TOP」の刻印がある向きで接続。
反対側を基板に繋ぎつつ、固定場所に位置合わせします。


レーザー出力部を「1」の番号が振ってあるネジで3箇所 固定。


トップのアクリルパネルを固定するためのシャフト(3番の部品)を、アルミプレートの裏側から「2」の番号が振ってあるネジで5箇所 固定。


最後に、アクリルパネルの保護紙を剥がして、「4」の番号が振ってあるネジで5箇所 固定。

なかなかコンパクトで、サイズはiPod Classicと同じくらい、重さは87gでした。


同じくレーザー走査型のプロジェクターで、SONYが海外にて販売しているMP-CL1Aとサイズ比較。
MP-CL1Aは個人輸入で1台5万円くらいだった記憶...


少し明るい環境だったので、はっきりとした比較にはなりませんが、レーザークラス3RのMP-CL1A(左)と比較して、HDピコレーザープロジェクター(右)はクラス1にしては健闘していると感じました。

ちなみに、MacとHDMIで接続したところ、YPbPrの信号には対応してないようで、画面全体がピンク色に...
手元にあるRaspberry PiやPINE64では問題なく表示されました。

次は真っ暗な環境で試してみようと思います。(続報はTwitterに書くかも

追記:真っ暗な部屋で映してみました。



投影先は天井、距離は2mくらいで、正確に測ってないけど50インチくらいはあるはず。
小さい文字はボケるけど、動画を見る分には悪くないかなというところです。