背景
Transystem 747proやHolux M-241などの古いMTK GPSロガーはBluetooth 2.1のSPP (Serial Port Profile)をサポートしており、そこでNMEAセンテンスのやりとりを行うことができます。
特定のコマンドを送ると記録されたデータをダウンロードすることができ、昔はAndroidアプリでGPX形式のログをダウンロードすることができていました。最近の端末はハードウェア側の仕様が新しくなりすぎてこれができなくなってきています。
いまどきGPSログなんてスマホでとれるじゃんというのは置いておいて、専用GPSロガー使いたいよね、出先でGPXファイルで落としたいよね、ってことで可能不可能の検証をやってみた結果です。
頑張れば今更ダウンローダーを作れそう(製作中)。
⇒できました。そのうちSmallStepの名前でオープンソースソフトウェアとして公開します。
サマリ
無印ESP32系のボード (普通のM5Stack)とArdino IDEなら行けることがわかった。シリアルを開く前にsetPinで"0000"を入れておかないとつながらない。AndroidのBluetoothSocketでSPP開くとここは必要ならOSとユーザ入力でやってくれてたところ。たぶん。
-
使える
- 無印ESP32 (Bluetooth 4.2→SPPが使える)
- Arduino (BluetoothClassic/LE両方使える)
-
使えない
- ESP32-S3, ESP32-C3, ESP8685 (Bluetooth 5→SPPは非サポート)
- ESP32-S2, ESP8266 (Bluetooth非搭載)
- MycroPython (BluetoothLEのみ。今後のサポート予定もない模様)
やり方
M5Stackに書き込んでHolux M-241と通信させてみた結果が以下の通り。
環境のセットアップはいろんな人が解説してくれてるので適当にやればなんとかなります。
コード (Arduino IDE)
#include <M5Stack.h>
#include <BluetoothSerial.h>
uint8_t gpsAddress[6] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; // 手持ちのロガーのBluetoothアドレスに置き換える
char *gpsPinCode = "0000"; // SPP接続時のPinコード。通常は"0000"で固定のはず (747pro, m-241はこれでいけた)
BluetoothSerial SerialBT;
bool gpsConnected;
// NMEAコマンドのチェックサム計算を行う
byte calcNmeaChecksum(char *cmd, byte len) {
byte chk = 0;
for (byte i=0; (i<len)&&(cmd[i]!=0); i++) {
chk ^= (byte)(cmd[i]);
}
return chk;
}
// NMEAコマンドにチェックサムをつけてSerialBTに書き出す
void sendNmeaCommand(char *cmd, byte len) {
word len2 = (word)(len) + 8;
char buf[len2];
byte chk = calcNmeaChecksum(cmd, len);
memset(buf, 0, len2);
sprintf(buf, "$%s*%X\r\n", cmd, chk);
Serial.write('>');
M5.Lcd.write('>');
for (short i=0; (i<len2)&&(buf[i]!=0); i++) {
SerialBT.write(buf[i]);
M5.Lcd.write(buf[i]);
}
}
// PMTK182,7コマンドを送る (ログのダウンロードコマンド)
void sendDownloadCommand(int startPos, int reqSize) {
char cmdstr[32];
sprintf(cmdstr, "PMTK182,7,%08X,%08X", startPos, reqSize);
sendNmeaCommand(cmdstr, sizeof(cmdstr));
}
void setup() {
// M5Coreの初期化
M5.begin();
M5.Power.begin();
M5.Lcd.setBrightness(LCD_BRIGHTNESS);
// SPPのマスターとして動作させる
SerialBT.begin(APP_NAME, true); // start bluetooth serial as a master
// LCDに接続中メッセージを表示
M5.Lcd.printf("Waiting for GPS device (%02X:%02X:%02X:%02X:%02X:%02X)...\n",
gpsAddress[0], gpsAddress[1], gpsAddress[2], gpsAddress[3], gpsAddress[4], gpsAddress[5]);
Serial.printf("Waiting for GPS device (%02X:%02X:%02X:%02X:%02X:%02X)...\n",
gpsAddress[0], gpsAddress[1], gpsAddress[2], gpsAddress[3], gpsAddress[4], gpsAddress[5]);
// SPP接続
SerialBT.setPin(gpsPinCode); // Pinコードをセット。これがないと繋がらない
gpsConnected = SerialBT.connect(gpsAddress); // 接続先アドレス指定でSPPを接続
// 接続に失敗したらLCDに失敗メッセージを出して無限ループ
if (!gpsConnected) {
M5.Lcd.printf("Connection failed, terminated.\n");
while (true) delay(1000);
}
// 接続成功メッセージ表示
M5.Lcd.printf("Connected!\n");
Serial.printf("Connected!\n");
// ログのダウンロードコマンドを送信 (実際には全体を参照するように何回か送る必要あり)
sendDownloadCommand(0, 0x8000);
}
void loop() {
delay(10);
// SerialBTの応答をシリアルに流す(LCDは即おかしな表示になる)
while (SerialBT.available()) {
char c = SerialBT.read();
Serial.print(c);
M5.Lcd.write(c);
}
}
実行結果 (Serialモニタ)
Waiting for GPS device (AA:BB:CC:DD:EE:FF)...
Connected!
>$PMTK182,7,00000000,00008000*2A ... 送信したダウンロードコマンド
$GPGGA,174536.000,xxxx.xxxx,N,xxxxx.xxxx,E,1,5,2.02,46.3,M,31.7,M,,*xx
$GPGSA,A,3,15,02,18,13,23,,,,,,,,2.24,2.02,0.97*04
$GPGSV,3,1,12,15,67,328,19,02,58,242,18,05,56,092,,13,55,032,16*77
$GPGSV,3,2,12,18,40,304,18,24,40,199,,20,30,116,,23,16,308,15*75
$GPGSV,3,3,12,30,11,041,,14,09,070,,29,08,233,,11,06,158,*7C
$GPRMC,174536.000,A,xxxx.xxxx,N,xxxxx.xxxx,E,1.54,292.07,180703,,,A*xx
$GPVTG,292.07,T,,M,1.54,N,2.85,K,A*3C
$GPGGA,174537.000,xxxx.xxxx,N,xxxxx.xxxx.3917,E,1,5,2.02,46.5,M,31.7,M,,*xx
$GPGSA,A,3,15,02,18,13,23,,,,,,,,2.24,2.02,0.97*04
$GPGSV,3,1,12,15,67,328,18,02,58,242,18,05,56,092,,13,55,032,16*76
$GPGSV,3,2,12,18,41,304,18,24,40,199,,20,30,116,,23,16,308,14*75
$GPGSV,3,3,12,30,11,041,,14,09,070,,29,08,233,,11,06,158,*7C
$GPRMC,174537.000,A,xxxx.xxxx,N,xxxxx.xxxx,E,2.95,326.66,180703,,,A*xx
$GPVTG,326.66,T,,M,2.95,N,5.48,K,A*3D
$PMTK182,8,00000000,FFFF1D00008004010A0000000000000000000000FFFFFFFFFFFFFFFFFF ... コマンド応答1
$PMTK182,8,00001000,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ... コマンド応答2
$PMTK182,8,00001800,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ... コマンド応答2
...
$PMTK182,8,00007800,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ... コマンド応答N
$PMTK001,182,7,3*20 ... コマンド終了メッセージ(1/2=エラー、3=正常終了)
...
参考ページ
ほぼ先人のやったことをMTK GPS相手にやっただけです。