ATOMIC GPS ベース(M8030-KT) を ATOMS3 につないで表示と MQTT 送信を整えたメモ
この記事は M5Stack Qiita Advent Calendar 2024 の 16 日目の記事です。
ATOMIC GPS ベース(M8030-KT) を ATOMS3 につないで表示と MQTT 送信を整えたメモです。
2024/12/15 の時点の情報で進めます。
ATOMIC GPS ベース(M8030-KT)

ATOMIC GPS ベース(M8030-KT) — スイッチサイエンス
ATOMシリーズ用のGPS測位モジュールです。M8030-KT測位ナビゲーションチップ、FLASHメモリを内蔵しています。また、ユーザー設定の電源オフ保持をサポートするボタン電池を内蔵しています。このモジュールは、地理座標の表示、バス停のアナウンス、車両や船舶のナビゲーション、軌跡の追跡などに応用できます。
GPSデータ通信の一般的な規格であるNMEA-0183プロトコルでデータを出力します。GPS、GLONASS、GALILEO、BDS、SBAS、QZSSを含む複数の衛星システムをサポートしています。72チャンネルのサーチ機能により、衛星信号を正確に捕捉・追尾し、正確な測位を実現します。
さらに、MicroSDカードスロットが含まれています。このスロットは、MicroSDカードからGPSやその他のファイルデータを読み込むことができます。例えば、地図ソフトで移動の軌跡を見るために、GPSデータを特定のフォーマットでエクスポートすることができます。また、通常のカードリーダーとしても機能し、ファイルの読み書きが可能です。
ATOM LITE、ATOM Matrix、ATOMS3 LITE、ATOMS3にマウントすることができます。
とのことで ATOM3S を使ってコンパクトに IoT 的に位置情報を扱うデバイスができます。
実際のプログラム
ATOM3S に書き込んだプログラムはこちらです。
Atomic GPS Base ドキュメント を参考にしつつ、
M5AtomS3/examples/AtomicBase/AtomicGPS/GPS/GPS.ino at main · m5stack/M5AtomS3
のサンプルコードで表示と MQTT 送信を整えて ATOM3S に以下のプログラムを書き込みます。
#include "M5AtomS3.h"
#include <TinyGPSPlus.h>
#include <WiFi.h>
#include <WiFiMulti.h>
WiFiMulti wifiMulti;
#include <ArduinoJson.h> // バージョンは v6 で
#include <WiFiClient.h>
#include <PubSubClient.h>
const char* ssid = "ssid";
const char* password = "password";
// MQTTの接続先のIP
const char *endpoint = "endpoint";
// MQTTのポート
const int port = 1880;
// 今回使いたい MQTT のユーザー名
const char *mqttUsername = "mqttUsername";
// 今回使いたい MQTT のパスワード
const char *mqttPassword = "mqttPassword";
// デバイスID
char deviceID[50];
// トピックを保存するためのバッファを確保
char pubTopic[50];
char subTopic[50];
// プログラム名
#define FW_NAME "AtomS3_GPS_Base_Wifi"
// ファームウェアバージョン
#define FW_VERSION "2024.1.0.2"
// ArduinoJson v6 の記述で JSON を格納する StaticJsonDocument を準備
StaticJsonDocument<512> root;
WiFiClient httpsClient;
PubSubClient mqttClient(httpsClient);
char pubJson[512];
// The TinyGPSPlus object
TinyGPSPlus gps;
int setupFlag = 0;
static void smartDelay(unsigned long ms) {
unsigned long start = millis();
do {
while (Serial2.available()) gps.encode(Serial2.read());
} while (millis() - start < ms);
}
void setup() {
auto cfg = M5.config();
AtomS3.begin(cfg);
wifiMulti.addAP(ssid, password);
AtomS3.Display.setTextDatum(TL_DATUM);
AtomS3.Display.setCursor(0,0);
AtomS3.Display.setTextSize(1);
AtomS3.Display.println("[GPS]");
AtomS3.Display.println(FW_NAME);
AtomS3.Display.println(FW_VERSION);
delay(1000);
Serial2.begin(9600, SERIAL_8N1, 5, -1);
WiFi.mode(WIFI_STA);
WiFi.setSleep(false);
Serial.print("Connecting...");
AtomS3.Display.println("WiFi ");
AtomS3.Display.print(".");
// Serial.println(ssid);
// Wait for connection
// while (WiFi.status() != WL_CONNECTED) {
while (wifiMulti.run() != WL_CONNECTED) {
delay(500);
Serial.print(".");
AtomS3.Display.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(WiFi.SSID());
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
Serial.print("MAC address: ");
Serial.println(WiFi.macAddress());
AtomS3.Display.setCursor(0,0);
AtomS3.Display.clear();
AtomS3.Display.println("[GPS]");
AtomS3.Display.println(FW_NAME);
AtomS3.Display.println(FW_VERSION);
AtomS3.Display.println("WiFi OK!");
// deviceID
String macStr = String(WiFi.macAddress());
macStr.replace(":", "");
sprintf(deviceID, "%s", macStr);
AtomS3.Display.print("ID ");
AtomS3.Display.println(deviceID);
AtomS3.Display.print("IP ");
AtomS3.Display.println(WiFi.localIP());
// メッセージを知らせるトピック
sprintf(pubTopic, "device/%s/pub", deviceID);
// メッセージを待つトピック
sprintf(subTopic, "device/%s/sub", deviceID);
AtomS3.Display.println("----");
AtomS3.Display.print("pubTopic ");
AtomS3.Display.println(pubTopic);
AtomS3.Display.println("----");
mqttClient.setServer(endpoint, port);
mqttClient.setCallback(mqttCallback);
mqttClient.setBufferSize(512);
connectMQTT();
delay(1000);
AtomS3.Display.print("... 5 ");
delay(1000);
AtomS3.Display.print("4 ");
delay(1000);
AtomS3.Display.print("3 ");
delay(1000);
AtomS3.Display.print("2 ");
delay(1000);
AtomS3.Display.print("1 > GO!");
delay(1000);
setupFlag = 1;
}
void connectMQTT() {
// 起動してからの経過時間でシードを設定
randomSeed(millis());
// MQTT clientID のランダム化(名称重複対策)
char clientID[40] = "clientID";
String rndNum = String(random(0xffffff), HEX);
String deviceIDRandStr = String("client-");
deviceIDRandStr.concat(deviceID);
deviceIDRandStr.concat("-");
deviceIDRandStr.concat(rndNum);
deviceIDRandStr.toCharArray(clientID, 40);
Serial.print("clientID ");
Serial.println(clientID);
AtomS3.Display.print("MQTT");
while (!mqttClient.connected()) {
if (mqttClient.connect(clientID,mqttUsername,mqttPassword)) {
Serial.println("MQTT Connected.");
int qos = 0;
mqttClient.subscribe(subTopic, qos);
Serial.println("MQTT Subscribed.");
// Connected message
DynamicJsonDocument doc(512);
doc["type"] = "connected";
doc["localIP"] = WiFi.localIP();
serializeJson(doc, pubJson);
mqttClient.publish(pubTopic, pubJson);
AtomS3.Display.println(" > OK!");
} else {
Serial.print("Failed. Error state=");
Serial.println(mqttClient.state());
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void mqttCallback (char* topic, byte* payload, unsigned int length) {
String str = "";
Serial.print("Received. topic=");
Serial.println(topic);
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
str += (char)payload[i];
}
Serial.print("\n");
// Deserialize the JSON dsocument
DeserializationError error = deserializeJson(root, str);
// Test if parsing succeeds.
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
//
}
void mqttLoop() {
if (!mqttClient.connected()) {
connectMQTT();
}
mqttClient.loop();
}
void loop() {
// 常にチェックして切断されたら復帰できるように
mqttLoop();
AtomS3.update();
AtomS3.Display.setCursor(0,0);
AtomS3.Display.clear();
AtomS3.Display.println("[GPS]");
AtomS3.Display.print("sate :");
AtomS3.Display.println(gps.satellites.value());
AtomS3.Display.println("lng :");
AtomS3.Display.println(gps.location.lat());
AtomS3.Display.println("lng :");
AtomS3.Display.println(gps.location.lng());
AtomS3.Display.println("alt :");
AtomS3.Display.println(gps.altitude.meters());
if (millis() > 5000 && gps.charsProcessed() < 10){
Serial.println(F("No GPS data received: check wiring"));
AtomS3.Display.println("[x] No GPS data received");
}
smartDelay(1000);
if (AtomS3.BtnA.wasPressed()) {
DynamicJsonDocument doc(512);
doc["type"] = "button";
doc["satellites"] = gps.satellites.value();
doc["lat"] = gps.location.lat();
doc["lon"] = gps.location.lng();
doc["alt"] = gps.altitude.meters();
serializeJson(doc, pubJson);
mqttClient.publish(pubTopic, pubJson);
AtomS3.Display.setCursor(0,0);
AtomS3.Display.clear();
AtomS3.Display.println("[GPS]");
AtomS3.Display.println("-> MQTT Send!");
AtomS3.Display.println("----------");
AtomS3.Display.print("sate :");
AtomS3.Display.println(gps.satellites.value());
AtomS3.Display.println("lng :");
AtomS3.Display.println(gps.location.lat());
AtomS3.Display.println("lng :");
AtomS3.Display.println(gps.location.lng());
AtomS3.Display.println("alt :");
AtomS3.Display.println(gps.altitude.meters());
Serial.println("Pressed");
delay(1000);
}
}
こちらを書き込んだら、

ATOMIC GPS ベース(M8030-KT) を ATOMS3 につないで電源を入れます。Wi-Fi を探して見つかると MQTT につないで GPS 捜索を開始します。

このように動きます。衛星数の sate が 0 なのは、一度ベランダで捉えてから家に戻って撮影しているためです。
画面クリックして位置情報が MQTT で送れる
if (AtomS3.BtnA.wasPressed()) {
DynamicJsonDocument doc(512);
doc["type"] = "button";
doc["satellites"] = gps.satellites.value();
doc["lat"] = gps.location.lat();
doc["lon"] = gps.location.lng();
doc["alt"] = gps.altitude.meters();
serializeJson(doc, pubJson);
mqttClient.publish(pubTopic, pubJson);
AtomS3.Display.setCursor(0,0);
AtomS3.Display.clear();
AtomS3.Display.println("[GPS]");
AtomS3.Display.println("-> MQTT Send!");
AtomS3.Display.println("----------");
AtomS3.Display.print("sate :");
AtomS3.Display.println(gps.satellites.value());
AtomS3.Display.println("lng :");
AtomS3.Display.println(gps.location.lat());
AtomS3.Display.println("lng :");
AtomS3.Display.println(gps.location.lng());
AtomS3.Display.println("alt :");
AtomS3.Display.println(gps.altitude.meters());
Serial.println("Pressed");
delay(1000);
}
だいたい、このあたりのコードですが、画面クリックして位置情報が MQTT で送れます。

このように Node-RED で MQTT を待ち受けます。

クリックすると MAC アドレスがをベースに MQTT トピックを作りつつ、現在の衛星数・緯度・経度・高度を送ってくれます!