前回はこの ATOM GPS Kit(M8030-KT)を公式のサンプルコードだけで動かしましたが、今回はそのコードを弄って実用的に使えるように機能を追加したので、修正したコードも合わせて紹介します。
- Ambient(IoT クラウドサービス)で地図上へマッピング(GPS トラッカー)
リアルタイムで情報を送信する - 内蔵 microSD へ CSV 型式でデータを保存(GPS ロガー)
GPSAnalyse.h で取得できる $GNRMC の全てのデータを保存する - A ボタンで一時停止(スタート、ストップ)
- 30秒ごとにデータ処理
- UTC を JST に変換して保存
本 GPS Kit は、国内の正規代理店であるスイッチサイエンスで入手することができます。
microSD カードの保存データを Google マップでマッピング
Ambient でマップ表示
スマホのテザリング又はモバイルルータを介して IoT クラウドサービスの Ambient へ緯度・経度情報を投げることで、ブラウザを使ってリアルにマップで位置を確認することができます。
外部電源について
どうしてもこういった GPS は外に持ち出して使うことになるんで、電源をどうするかが悩みポイントで、車のダッシュボードに置くならモバイルバッテリでもいいんですが、徒歩の記録を取ろうとすると・・・どうしたもんかと、イマイチです。
外に出てる時間が1時間程度なら、こういうストラップを付けてぶら下げて使うのもいいんで・・・
そのうち ATOM GPS Kit + TailBAT が収まるホルダでも作って使い勝手を試してみようかと思います。
起動時のモードは2つ
電源投入時は microSD へのデータ保存のみ。
A ボタンを押しながら電源投入で、microSD へのデータ保存と、Wi-Fi 経由で Ambient へデータを送出します。
Ambient について
Ambient – IoTデーター可視化サービス
Ambient の登録はとっても簡単で、アカウントを作ってチャンネル登録するだけですぐに利用できます。
チャンネル登録ボタンで作成されるチャンネル ID とライトキーをプログラムコードへセットするだけです。
プログラムコード内の channelID に "チャンネル ID" を、writekey に "ライトキー" をセットします。
Ambient では ID9 に緯度が、ID10 に経度があらかじめ割り当てられているので、ここの設定はこのままで大丈夫です。
Ambient で地図を表示するために、チャート種類で ”地図” を選択します。(これだけ)
プログラムコード
公式の ATOM サンプル AtomicGPS.ino を弄って作ってるので、必ず以下の2つのファイルも同じフォルダに置いてコンパイル&書き込みをしてください、
- GPSAnalyse.cpp
- GPSAnalyse.h
Arduino IDE でサンプルの AtomicGPS.ino を開いてから、以下のコードをコピペするのが簡単です。
※仕事から帰ってここ数日で書いたもので、雨っぷりも重なってしっかりとフィールド試験ができない状態での公開となります。思い付きで機能をアップしたりしてるんで、キレイにコードを手直してないし、上手く動作しないところがあるかもなんですが、適当に修正して使ってみて下さい。
/*This is an example used SerialBT,you can can view gps data by connecting
* to Bluetooth assistant on your mobilephone or Serial Monitor
* the GPS log will be written to SD card
*
* Fix 2020/10/9 v.1.0 by JH1LHV
*
*/
#include "M5Atom.h"
#include "GPSAnalyse.h"
#include <SPI.h>
#include "FS.h"
#include <SD.h>
#include "Ambient.h"
GPSAnalyse GPS;
WiFiClient client;
Ambient ambient;
const char* ssid = "************";
const char* password = "************";
unsigned int channelId = *****;
const char* writeKey = "***************";
const char filename[] = "/GPSdata.csv";
File txtFile;
int p_time = 0;
int year, month, day, hour, minute, second;
bool wifi_select = false;
bool start = true;
String Jst;
String Utc; //1
char State; //2
double Latitude; //3
char LatitudeMark; //4
double Longitude; //5
char LongitudeMark; //6
float TrackSpeed; //7
float TrackAngle; //8
String Date; //9
float Magnetic; //10
char Declination; //11
int Mode; //12
bool writeLog(String filename) { //Write GPSdata to SDcard
txtFile = SD.open(filename, FILE_APPEND);
char buf[16];
if(txtFile){
txtFile.print(Date);
txtFile.print(", ");
txtFile.print(Jst);
txtFile.print(", ");
txtFile.print(State);
txtFile.print(", ");
dtostrf(Latitude, 9, 5, buf);
txtFile.print(buf);
txtFile.print(", ");
txtFile.print(LatitudeMark);
txtFile.print(", ");
dtostrf(Longitude, 9, 5, buf);
txtFile.print(buf);
txtFile.print(", ");
txtFile.print(LongitudeMark);
txtFile.print(", ");
dtostrf(TrackSpeed, 9, 5, buf);
txtFile.print(buf);
txtFile.print(", ");
dtostrf(TrackAngle, 9, 5, buf);
txtFile.print(buf);
txtFile.print(", ");
dtostrf(Magnetic, 9, 5, buf);
txtFile.print(buf);
txtFile.print(", ");
txtFile.print(Declination);
txtFile.print(", ");
txtFile.println(Mode);
txtFile.close();
}else{
return false;
}
return true;
}
void utc2jst(void) {
struct tm t, *rt;
time_t ti;
t.tm_year = ("20" + Date.substring(4, 6)).toInt() - 1900;
t.tm_mon = (Date.substring(2, 4)).toInt() - 1;
t.tm_mday = (Date.substring(0, 2)).toInt();
t.tm_hour = (Utc.substring(0, 2)).toInt() + 9;
t.tm_min = (Utc.substring(2, 4)).toInt();
t.tm_sec = (Utc.substring(4, 6)).toInt();
t.tm_wday = 0;
t.tm_yday = 0;
ti = mktime(&t);
rt = localtime(&ti);
year = rt->tm_year + 1900;
month = rt->tm_mon + 1;
day = rt->tm_mday;
hour = rt->tm_hour;
minute = rt->tm_min;
second = rt->tm_sec;
}
void setup() {
M5.begin(true,false,true);
M5.dis.fillpix(0x000000);
if (M5.Btn.isPressed()) {
wifi_select = true;
WiFi.begin(ssid, password);
while(WiFi.status()!=WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.print("\r\nWiFi connected\r\nIP address: ");
Serial.println(WiFi.localIP());
Serial.printf("### READY\n");
M5.dis.fillpix(0x004f00);
}
else {
M5.dis.fillpix(0x00004f);
}
SPI.begin(23,33,19,-1);
if(!SD.begin(-1, SPI, 40000000)){
Serial.println("initialization failed!");
}
sdcard_type_t Type = SD.cardType();
Serial.printf("SDCard Type = %d \r\n",Type);
Serial.printf("SDCard Size = %d \r\n" , (int)(SD.cardSize()/1024/1024));
Serial1.begin(19200,SERIAL_8N1,22,-1);
GPS.setTaskName("GPS");
GPS.setTaskPriority(2);
GPS.setSerialPtr(Serial1);
GPS.start();
ambient.begin(channelId, writeKey, &client);
}
void loop() {
M5.update();
if (M5.Btn.isPressed()) {
start = start ? false : true;
}
if (start == true)
if (wifi_select == true) M5.dis.fillpix(0x004f00);
else M5.dis.fillpix(0x00004f);
else M5.dis.fillpix(0x000000);
if(millis() - p_time >= 30000) { // 30sec
p_time = millis();
GPS.upDate();
Serial.println("");
Utc = GPS.s_GNRMC.Utc;
State =GPS.s_GNRMC.State;
Latitude = GPS.s_GNRMC.Latitude;
LatitudeMark = GPS.s_GNRMC.LatitudeMark;
Longitude = GPS.s_GNRMC.Longitude;
LongitudeMark = GPS.s_GNRMC.LongitudeMark;
TrackSpeed = GPS.s_GNRMC.TrackSpeed;
TrackAngle = GPS.s_GNRMC.TrackAngle;
Date = GPS.s_GNRMC.Date;
Magnetic = GPS.s_GNRMC.Magnetic;
Declination = GPS.s_GNRMC.Declination;
Mode = GPS.s_GNRMC.mode;
utc2jst();
char str1[5],str2[5],str3[5];
sprintf(str1, "%02d", month);
sprintf(str2, "%02d", day);
Date = String(year) + String(str1) + String(str2);
sprintf(str1, "%02d", hour);
sprintf(str2, "%02d", minute);
sprintf(str3, "%02d", second);
Jst = String(str1) + String(str2) + String(str3);
Serial.printf("JST= %s \r\n",Jst); //時刻
Serial.printf("State= %c \r\n",State); //ステータス
Serial.printf("Latitude= %.5f \r\n",Latitude); //緯度
Serial.printf("LatitudeMark= %c \r\n",LatitudeMark); //方位
Serial.printf("Longitude= %.5f \r\n",Longitude); //経度
Serial.printf("LongitudeMark= %c \r\n",LongitudeMark); //方位
Serial.printf("TrackSpeed= %.5f \r\n",TrackSpeed); //速度
Serial.printf("TrackAngle= %.5f \r\n",TrackAngle); //方向
Serial.printf("Date= %s \r\n",Date); //日付
Serial.printf("Magnetic= %.5f \r\n",Magnetic); //磁気
Serial.printf("Declination= %c \r\n",Declination); //赤緯
Serial.printf("Mode= %c \r\n",Mode); //方式
Serial.println("");
if (start==true) {
if ((Latitude != 0)&&(LongitudeMark != 0)&&(String(year).substring(0,2)=="20")) {
if (wifi_select == true) {
char buf[16];
dtostrf(Latitude, 9, 5, buf);
ambient.set(9, buf);
dtostrf(Longitude, 9, 5, buf);
ambient.set(10, buf);
ambient.send();
}
writeLog(filename);
}
}
}
}
基本的には Wi-Fi の設定と Ambient の設定、先頭部分にある ”***” を書き変えるだけで動作します。
〇 Wi-FI の設定
const char* ssid = "************";
const char* password = "************";
〇 Ambient の設定
unsigned int channelId = *****;
const char* writeKey = "***************";
--------------------------------------------------------------------
〇 シリアル通信速度について
Serial1.begin(19200,SERIAL_8N1,22,-1);
わたしは u-center を使って BN-200 の設定を変更しているので、デフォルト使用では ここの 19200 を 9600 に変更する必要があります。
〇 書き込みタイミングについて
if(millis() - p_time >= 30000) { // 30sec;
現在の設定では、microSD へのデータ書き込み、Ambient へのデータ送信は、30秒ごとに行われます。
人の歩行ならこの 30秒ごとの設定でもいいと思いますが、自転車や車両での使用をお考えなら、この数値をもっと小さくして時間当たりのデータ取得数を多くすることが必要かもしれませんので、適当に変更してお使いください。
Google マップを使う
Google マップで表示するのも簡単で、基本的には microSD に保存された csv ファイルをドラッグするだけで表示できます。
以下、csv ファイルをインポートして、軌跡をマップへプロットするまでの画面キャプチャとなります。
① Google マップのメニューから「マイプレイス」を選択
② 続いて、「マイマップ」を選択
③ 「地図を作成」をクリック
④ 「インポート」をクリック
⑤ microSD の GPSdata.csv をドラッグ
⑥ 緯度、経度をチェック
⑦ マーカーのタイトルとして、時間をチェック
⑧ 緯度・経度情報が地図にマッピング
このキャプチャ画像の地図は、プライバシー保護のため、わざと引きで表示しています。
掲載したプログラムに不具合や修正があれば、その都度アップデートしていきますので、よろしくお願いします。