ESP32 - MicroPythonにて、GPS受信キットを制御する


はじめに

ESP32 - MicroPythonを使い、GPS受信キットから受信方法について記載します。

GPS受信機キット - 1PPS出力付き「みちびき」3機受信対応

今回、秋月電子さんで購入できるGPS受信機キット - 1PPS出力付き「みちびき」3機受信対応を使用します。

以下が主な仕様です。

  • GPSモジュール:GYSFDMAXB(太陽誘電)
  • 搭載GPS受信チップ:MT3339(MediaTek)
  • 受信周波数:1575.42MHz(L1,C/Aコード)
  • 受信チャネル数:66(アクイジション)、22(トラッキング)
  • 対応測位衛星システム:GPS(米国)、QZSS(日本)
  • 受信(トラッキング)感度:-164dBm(typ.)
  • 測位確度:2m(typ.緯経度の水平位置)@-135dBm
  • 出力データ形式:NMEA0183V3.01準拠
  • 測地系:WGS1984(デフォルト)
  • 電源電圧:DC5V(3.8V~12V)/・電源電流:40mA
  • 入出力信号レベル:C-MOSロジック(3.3V)レベル、非同期シリアル信号
  • UART通信速度:9600bps(デフォルト)、4800~115200bps
  • 出力データ更新レート:毎秒1回(デフォルト)、毎秒1~10回出力可
  • 1PPS出力:C-MOSロジック(3.3V)レベル、パルス幅:100mS(アクティブLow)
  • インジケータ:赤色LEDによる通電表示、衛星追尾中(1PPS)表示
  • 基板サイズ:30×30×13.5mm(電池ボックス実装時)
  • 重量:約11g(バックアップ電池装着時)

商品ページへのリンク

!!!本商品は、はんだ付けが必要です。!!!

開発環境

以下が確認した環境となります。

  • ホストPC
    • windows10 64bit Home
    • VSCode - 1.41.1
      • Pymakr 1.1.5
    • NodeJS - 12.14.1 LTS
  • ターゲット
    • MicroPython v1.12 on 2019-12-20; ESP32 module with ESP32

機材

※型名が購入先のリンクになっています。

項目 型名 備考
ESP32-WROOM-32 開発ボード NodeMCU-32S ESP32-WROOM-32 -
GPS受信機キット 秋月電子 -
ダイオード(1N4148) 秋月電子 GPS取説 - 推奨回路に記載
抵抗 10kΩ GPS取説 - 推奨回路に記載
ジャンパーケーブル 指定なし

接続図

※今回、「1PPS出力」は使用しないので、未接続になっています。
 評価基板によって、RX2,TX2は、D16, D17と記載されています。

モジュール

GPSモジュールからNMEAフォーマットで送られてきます。

制御文字などが入っているため、パースする必要があります。

調査したところ、micropyGPSというライブラリを使うとできそうなことがわかりました。

inmcm/micropyGPS

ただ、README.mdに下記の記載があります。

ESP32
You can follow the setup instructions for the pyboard.
The only difference is, that you shoud use micropyGPS as a frozen module.
Otherwise there will be exceptions, because there is not enough heap space available.

ヒープが足りなくなるから、"frozen module"として扱ってねとのこと。

コード

動作確認したコードは以下となります。

ファイル名:sample_gps.py

from machine import UART
from micropyGPS import MicropyGPS
import utime, gc, _thread

uart = UART(2, baudrate=9600, timeout=200)
my_gps = MicropyGPS(local_offset=0, location_formatting='dd')

def test():
    n = 0
    mem_free = gc.mem_free()
    tm_last = 0
    while True:
        len = uart.any()
        if len > 0:
            b = uart.read(len)
            for x in b:
                if 10 <= x <= 126:
                    stat = my_gps.update(chr(x))
                    if stat:
                        tm = my_gps.timestamp
                        tm_now = (tm[0] * 3600) + (tm[1] * 60) + int(tm[2])
                        if (tm_now - tm_last) >= 10:
                            print('=' * 20)
                            print(my_gps.date_string(), tm[0], tm[1], int(tm[2]))
                            print("latitude:", my_gps.latitude[0], ", longitude:", my_gps.longitude[0])
                            print("altitude:", my_gps.altitude)
                            print("satellites_used:", my_gps.satellites_used)
                            n += 1
                            tm_last = tm_now
                            if (n % 10) == 0:
                                print("Mem free:", gc.mem_free(), mem_free - gc.mem_free())
                                gc.collect()
        else:
            utime.sleep_ms(100)

testth=_thread.start_new_thread(test, ())

ファイル構成

>>> import os
>>> os.listdir()
['boot.py', 'project.pymakr', 'sample_gps.py', 'micropyGPS.py']

実行結果例

10秒ごとに、printのログが表示されます。
※xxxは伏せています。

>>> import sample_gps
I (58850) uart: ALREADY NULL
>>> ====================
00/00/00 12 59 11
latitude: 36.xxxxxx , longitude: 139.xxx
altitude: 139.5
satellites_used: []
====================
02/09/20 12 59 21
latitude: 36.xxxxxx , longitude: 139.xxx
altitude: 139.5
satellites_used: [3, 1, 193, 30, 8, 194]
====================
02/09/20 12 59 31
latitude: 36.xxxxxx , longitude: 139.xxx
altitude: 139.5
satellites_used: [3, 1, 193, 30, 194]
====================
02/09/20 12 59 41
latitude: 36.xxxxxx , longitude: 139.xxx
altitude: 139.5
satellites_used: [11, 3, 1, 17, 193, 30, 8, 194]

...

気づいたことと

最初の起動は時間がかかる

取説にも記載されていますが、時間がかかります
"40秒~数十分"と記載。
私の場合は、2分ほどかかりました。
受信できるとLEDの点滅で教えてくれます。

各項目の精度について

当たり前かもしれませんが、時刻はきっちり取れています。
緯度経度は、数十~100メートルぐらいといった感じでしょうか。
十分な精度だと思います。
ただ、ログ上は同じ値になっていますが、海抜は安定していない気がします。
40メートル ~ 180メートルの値をとってしまう。

ヒープ領域について

gc.collect()を実施しないと、利用可能なヒープが減っていきます。
上記のコードで、数時間ほど走らせてましたが、
とりあえず落ちることはなかったです。
もう少しエージングして確認しますが、
ダメなら別の方法が必要になるかもしれません。

参考にさせていただいたサイト