SensorCast ~ 指定IPアドレスにGPSデータをUDP送信
今までにスマートフォンで取得したGPSデータを指定IPアドレスに送信する記事を出してきましたが、そのスマートフォン側のアプリケーションをGoogle Playに登録しました。
というのも、Raspberry PiやM5StackでGPS受信機を付けるアプリケーションを組んでも、実際に使おうとすると面倒だったり、うまくリンクできなかったりして、あまり使わないことが多いのです。
そこで、Raspberry PiやM5Stackは表示専門に徹してもらって、元データはスマートフォンで処理してみたらと考えたわけです。また、今まで公開しているスマートフォン側のソースが断片的なため、記事を読んだ人が実際にプロジェクトを作ってアプリケーションを作るのは敷居が高いだろうということで、アプリケーションを一般公開することにしました。
とは言っても、IPアドレスとポート番号を指定するだけのアプリケーションです。そして、送信されるUDP送信文字列は
高度,速度,緯度,経度 ※カンマ区切り
という、超シンプルなものです。少しずつ、送信データ項目を増やそうとは思っていますが、需要も少ないと思うので気長にやっていきたいと思います。
Google Playへのリンクは以下のとおりです。
注意事項
(1).当アプリを利用した際の、直接的または間接的な損害・損失・不利益・精神的苦痛などに対して、kunimiyasoftは一切責任を負わないものとします。
(kunimiyasoft assume no responsibility whatsoever for any direct or indirect damage, loss, prejudice or emotional distress caused by use of this Application)
(2).当アプリは位置情報を利用します。位置情報は消費電力が比較的大きく、特にGPS機能がONになっていると多く電力を消費します。利用する際はバッテリー残量に注意してください。
(3).アイコンは、CoPilotにて作成しました。
SensorCastのプライバシーポリシー(privacy policy)
- 位置情報の使用
現在位置、高度、速度の取得に位置情報を使用します。 - インターネットへのアクセス
UDP送信のために、インターネットへアクセスします。データ通信が使われることをご了承ください。 - 個人情報
個人情報は取得しません。個人情報、位置情報をネットを介してkunimiyasoftへ通知する仕組みはありません。
android.os.NetworkOnMainThreadException
Google Playに登録作業をしているのですが、リリース前レポートで
android.os.NetworkOnMainThreadException
が出る機種が報告されて、登録が完了しません。これは、メインスレッドでネットワーク操作を実行しようとしたときに発生する例外です。非同期にしなければならないことは分かっているのですが、非同期のやり方に問題があるようです。しばらく、調査を行います。
⇒「InetAddress.getByName」が非同期である必要がありました。
広告 ID の申告が不完全です
これ、良く分かっていない。使用していないで申告しても、結局はじかれる。「さっぽろ周辺マップ ライト3」とかでは、使用していることにして登録していますが、今回のテスト的なプログラムでも必要なのでしょうか。⇒結局、SensorCastも広告IDを使う設定にしました(何か、いろいろやっているうちにそうなってしまったようです)
サンプルプログラム
SensorCast向けのサンプルプログラムを用意しました。ATOMS3 LITE用のプログラムです。表示器にSSD1306ドライバを使う128×64ドット表示のOLEDディスプレイを使います。接続に関しては、プログラムから判断するか、以下の記事を参照してください。
#include <M5AtomS3.h>
#include <FastLED.h>
#include <WiFi.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED 幅
#define SCREEN_HEIGHT 64 // OLED 高さ
#define OLED_RESET -1 // リセットピン
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Wi-Fi環境
const char* ssid = "XXXXXXXXXX";
const char* password = "XXXXXXXXXX";
WiFiClient client;
bool isSignal = false; // 1度でも信号が来たら、trueのまま
static int dispmode = 0; // 0 速度 / 1 高度
#include <WiFiUdp.h>
WiFiUDP udp;
const int port = 60000; // ポート番号
#define BUFSIZE 1024 // UDP受信最大サイズ
#define CBUTTON 41 // ATOMS3 LITE ボタンGP
void Wifi_connect() {
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
Serial.print('.');
delay(500);
}
Serial.println("\nWi-Fi connected");
AtomS3.dis.drawpix(0x0000ff);
AtomS3.update();
udp.begin(port);
}
void setup() {
// put your setup code here, to run once:
AtomS3.begin(true); // LED Enable
AtomS3.dis.setBrightness(100);
Wire.end(); // これをしないと動作しない場合がある
Wire.begin(38, 39); // SDA / SCL
AtomS3.dis.drawpix(0x00ff00);
AtomS3.update();
// Wi-Fiの接続
Wifi_connect();
// SSD1306 開始
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;)
;
}
delay(1000);
pinMode(CBUTTON, INPUT_PULLUP); // モード切替
delay(1000);
}
void split(String data, String* lines) {
int count = 0;
int datalength = data.length();
for (int i = 0; i < datalength; i++) {
char onec = data.charAt(i);
if (onec == ',') {
count++;
if (count == 4) {
break;
}
} else {
lines[count] += onec;
}
}
}
void loop() {
char buff[128];
char packetBuffer[BUFSIZE];
String lines[4];
// AtomS3.update();
int btn = digitalRead(CBUTTON);
if (btn == LOW) {
dispmode++;
if (dispmode >= 2) {
dispmode = 0;
}
} else {
// nop
}
if (WiFi.status() != WL_CONNECTED) {
// 接続が切れた
AtomS3.dis.drawpix(0x00ff00);
AtomS3.update();
WiFi.disconnect();
WiFi.reconnect();
while (WiFi.status() != WL_CONNECTED) {
Serial.print('R');
delay(500);
}
Serial.println("\nWi-Fi connected");
AtomS3.dis.drawpix(0x0000ff);
AtomS3.update();
}
switch (dispmode) {
case 0:
case 1:
{
int packetSize = udp.parsePacket();
if (packetSize) {
isSignal = true;
udp.read(packetBuffer, packetSize);
if (dispmode == 0) {
split(packetBuffer, lines);
Serial.println("received");
Serial.println(packetBuffer);
display.clearDisplay();
display.setTextSize(5);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
double wAltitude = lines[0].toDouble();
sprintf(buff, "%04d", (int)(wAltitude + 0.5));
display.println(buff);
display.setTextSize(2);
display.setCursor(96, 48);
display.println("m");
} else if (dispmode == 1) {
split(packetBuffer, lines);
Serial.println("received");
Serial.println(packetBuffer);
display.clearDisplay();
display.setTextSize(6);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
double wSpeed = lines[1].toDouble();
sprintf(buff, "%03d", (int)(wSpeed + 0.5));
display.println(buff);
display.setTextSize(2);
display.setCursor(80, 48);
display.println("km");
}
display.display();
} else {
// シグナルがない
if (isSignal) {
// nop
} else {
// UDP受信するまでは、IPアドレスを表示しておく
display.clearDisplay();
display.setTextSize(2); // Normal 1:1 pixel scale
display.setTextColor(SSD1306_WHITE); // Draw white text
display.setCursor(0, 0); // Start at top-left corner
display.println(WiFi.localIP());
}
display.display();
}
}
break;
default:
break;
}
delay(100);
}
高度と速度を表示するプログラムです。ATOMS3 LITEのボタンを押すごとに、切り替わります。こんな風に、SensorCastを使ってみてください。