AMBL新卒2年目、ビジネスインテグレーション部の小堤(おつつみ)です。好きな色はお召茶(おめしちゃ。日本の伝統色・染色名。御召の着物のようなくすんだ緑みの青色、深い灰みの青)です。
現在私は、システムエンジニアとして金融機関の基盤システムの改修、画面実装などを行っています。
本記事を執筆しようと思ったきっかけは、実生活に役立つものを仕事で得た自分のスキルを使って作ることに面白さを感じたからです。
私は趣味で自転車に乗っており、防犯の目的で位置情報を遠隔から確認できるようなものを購入しようと考えていました。ただそういった製品は非常に高価であり、また拡張性があまりなく、私が望む機能がなかったため、購入まで踏み切れずにいました。
そんな中でGPSモジュールが格安で販売されていることを知り、自身でも作成できるのではないかと思い、自分で制作してみることにしました。
この記事では、シングルボードコンピュータとGPSモジュールを使用したGPSロガーのプログラムを詳しくご紹介します。
使用言語、開発環境
・使用言語
C#, HTML, PHP, JavaScript
・開発環境
Windows11
Arduino IDE(C#), Visual Studio Code(HTML,PHP,JavaScript), XAMPP(Webサーバ),Termux(仮想Linuxアプリ)
構成
このシステムは、自転車に搭載したコンピュータがGPS情報を取得し、その情報をAPサーバに送信します。
APサーバに送信されたGPSの情報をGoogle Maps APIを用いて地図上にピンや移動履歴を表示できるようにしています。
そのAPサーバにスマホなどでアクセスし、ページを開くことで現在地の地図を画面上で確認することができます。
・ESP32
ESP32はArduinoというシングルボードコンピュータの互換機として使用できます。
IOポートからデータの入出力を行え、単体でWIFIやBluetoothといった無線でのデータ送受信を行えます。
高機能な割に値段が安く、1200円ほどで購入できます。
開発はArduino IDEという開発環境を用いて行います。サンプルプログラムなども豊富にあるため初心者の方でも簡単に開発が行えます。
・GPSモジュール
様々な種類のGPSモジュールが販売されており、なんと500円で販売されているものがあったため、今回はそれを購入し使用しています。とても安価だったため、テスト段階で基盤に印刷されているPIN配列と実際の配列が逆になっていることに気づいたり、測位に時間がかかったりと不安が残る状態でしたが、後にこの不安が的中することに。。。。
・Google Maps API
Google Maps APIはGoogleが提供している地図表示用のAPIです。位置情報を送ることで簡単に地図を表示させることができます。今回は現在地にピンを出す画面と移動履歴表示画面を出す際に使用しています。
本来は使用するのに料金がかかってしまうのですが、今回は初回トライアルの枠を使用し無償で利用しています。
・Webサーバ
今回は時間がなかったため、TermuxというAndroid用のLinux仮想環境アプリを用いてApacheサーバを立ち上げており、スマホのテザリング機能を用いてAPサーバとESP32を同じネットワークに入れることで運用しています。そのため今回は遠隔で位置情報は確認できず、ローカル環境でのみ確認ができる状態になっています。今後はネット上にAPサーバを立て、ESP32にLTE通信モジュール等を搭載し通信をさせたいと思っています。
表示画面イメージ
・位置情報表示ページ
※上記画像の位置情報はサンプルを用いて表示させています。
・移動履歴表示ページ
※上記画像の位置情報はサンプルを用いて表示させています。
ソースコード
ESP32には位置情報取得とサーバにその情報を渡す処理を記載していきます。
以下に実装コードを記載します。
#include <WiFi.h>
#include <HTTPClient.h>
#include <TinyGPS++.h>
TinyGPSPlus gps;
const char* ssid = “******”;
const char* password = “********”;
int cnt = 0;
char c;
float gps_lat; //緯度
String S_gps_lat;
float gps_longt; //経度
String S_gps_longt;
void setup() {
Serial.begin(115200);
Serial2.begin(9600);
// WiFi接続
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println(“Connecting to WiFi…”);
}
Serial.println(“Connected to WiFi”);
}
void loop() { // HTTPリクエスト
if(Serial2.available()){
c = Serial2.read();
gps.encode(c);
gps_lat = gps.location.lat();
gps_longt = gps.location.lng();
Serial.print(“LAT: “); Serial.println(gps_lat,9);
S_gps_lat = String(gps_lat,9);
Serial.print(“LONG: “); Serial.println(gps_longt,9);
S_gps_longt = String(gps_longt,9);
}
HTTPClient http;
http.begin(“http://192.168.137.1/post.php”); // PHPファイルのURLを指定
http.addHeader(“Content-Type”, “application/x-www-form-urlencoded”); // POSTデータの形式を指定
String data =”message=ESP32&LAT=”+S_gps_lat+”&LONG=”+S_gps_longt;
int httpResponseCode = http.POST(data); // リクエストを送信
if (httpResponseCode > 0) {
Serial.println(“HTTP Response code: ” + String(httpResponseCode));
String response = http.getString(); // サーバーからのレスポンスを取得
Serial.println(“Response: ” + response);
} else {
Serial.println(“Error on HTTP request”);
}
http.end(); // 終了処理
delay(1000);
}
上記コードは上部、setup、loopの3つに分けられており、上部でincludeや変数の定義を行い、setup内で起動時に一度だけ実行させる処理を記載し、loop内に実際の処理を記載します。loop内はその名の通りESP32に電源が入っている間は処理がループされます。
setup内でデバック用とGPSモジュールの通信用にシリアル通信を開始させています。そのあとにWIFI接続処理を記載し、成功するまでループ処理を行っています。
loop内ではGPSモジュールから受信した情報を変数に格納しデバッグで受信した値を表示させています。その後APサーバとhttp通信を行うための処理を記載しpost.phpに取得した位置情報を送信しています。
・Webサーバ
Webサーバ上にはESP32からのデータ受信用ページ、現在の位置表示ページ、移動履歴表示ページの3つのプログラムを作成しています。
・データ受信用ページ
<?php
if ($_SERVER[‘REQUEST_METHOD’] === ‘POST’) {
// POSTデータの取得
$postData = $_POST[‘message’];
$postLat = $_POST[‘LAT’];
$postLong = $_POST[‘LONG’];
// 取得したPOSTデータを処理する
// レスポンスの送信
echo “Received data: ” . $postData;
echo ” “;
echo “LAT: ” . $postLat;
echo ” “;
echo “Long: ” . $postLong;
echo ” “;
//値を連想配列に格納
$json = array(
“LAT”=>$postLat,
“LONG”=>$postLong
);
if($postLat!=0.000000000){
//Json形式に変換
$decoded_json = json_encode($json);
//現在地をJson形式でファイルに上書き
$file = fopen(“GPSData.txt”, “w”);
fwrite($file, $decoded_json);
fclose($file);
//現在地を追記
$file = fopen(“GPSLog.txt”, “a”);
fwrite($file, “|”.$postLat.”,”.$postLong);
fclose($file);
}
} else {
// POSTリクエスト以外の場合には、エラーを返す
http_response_code(405);
echo “Only POST requests are allowed.”;
}
?>
上記コードではESP32から送られてきたデータを変数に格納し、配列に格納しています。
今回はテキストファイルで現在の位置情報と移動のログの保存を行うためファイルの編集を行うコードを記載しています。また現在地表示ページではJavaScriptを用いて位置情報を扱うためJson形式に変換しテキストに保存しています。
・現在地表示ページ
<!DOCTYPE html>
<html>
<head>
<title>ESP32 Data Display</title>
</head>
<body>
<h1>ESP32 Data Display</h1>
<div id=”data-display”></div>
<a href=’GPSLogViewer.php’”>GPSLog </a>
<div style=”width:1260px;height:470px;” id=”maps”></div>
<script>
var GPSData= “”;
// AJAXを使用して、PHPからデータを取得し、表示する
function updateData() {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (this.readyState === 4 && this.status === 200) {
GPSData = this.responseText;
}
};
xhr.open(“POST”, “GPSData.txt”, true);
xhr.send();
}
function ShowJson(){
var data = JSON.parse(GPSData||”null”);
GPSLat = data.LAT;
GPSLong = data.LONG;
}
function initMap() {
console.log(GPSLat);
var mapPosition = {lat: Number(GPSLat), lng: Number(GPSLong)}
var mapArea = document.getElementById(“maps”);
var mapOptions = {
center: mapPosition,
zoom: 16,
};
var map = new google.maps.Map(mapArea, mapOptions);
const marker = new google.maps.Marker({
position: mapPosition,
map: map
});
}
function googleMap(){
var ApiKey = “*******************”;
var script = document.createElement(‘script’);
script.src = ‘https://maps.googleapis.com/maps/api/js?key=’+ApiKey+’&callback=initMap’;
script.async = true;
document.head.appendChild(script);
}
setInterval(updateData, 2500);
setInterval(ShowJson, 2500);
setInterval(googleMap, 5000);
</script>
</body>
</html>
上記コードではJavaScript内でテキストファイルに保存している位置情報を読み出し、処理して画面上に表示させています。
updateData内ではテキスト内に記載されている現在の位置情報を読み出し、変数に格納しています。
ShowJson内では上記で格納したものをJsonで読み込んで緯度経度をそれぞれ変数に格納しています。
そしてGoogleMap内でGoogle Maps APIで使用するキーと表示させる要素の指定、APIのURLとキー、緯度経度の情報を引き渡し、APIの結果を表示させています。
<!DOCTYPE html>
<html>
<head>
<title>GPSLogViewer</title>
</head>
<body>
<h1>GPSLogViewer</h1>
<a href=’Viewer.php’”>Viewer </a><br>
<img src=”” id=”GPSLogimg” alt=””>
<?php
//GPSLogファイルから変数に格納
$fp = fopen(“GPSLog.txt”, “r”);
$i = 0;
$phpGPSData =””;
while (($line = fgets($fp, 22))) {
$phpGPSData = $phpGPSData.$line;
$i += 1;
}
fclose($fp);
?>
<script>
console.log(‘<?php echo substr($phpGPSData, 0, 21*($i-1)) ?>’ );
console.log(‘<?php echo $i ?>’ );
console.log(‘<?php echo substr($phpGPSData, 0, 21) ?>’ );
//GoogleMapApi使用時に必要なKey
var ApiKey = “************************”;
//GPSLogデータをPHPからJavaScriptに渡す
var GPSLogs = (‘<?php echo substr($phpGPSData, 0, 21*($i-1)) ?>’);
//地図画像表示処理
function googleMap(){
var GPSLogimg = document.getElementById(“GPSLogimg”);
var Apisrc = ‘https://maps.googleapis.com/maps/api/staticmap?
size=1080×1920&path=weight:3|color:red’+GPSLogs+’&key=’+ApiKey;
GPSLogimg.setAttribute(‘src’, Apisrc);
console.log(“True”);
}
googleMap();
</script>
</body>
</html>
上記コードでPHP内でテキストファイルに保存されている位置情報のログを読み出し、移動履歴を線で表示させています。
PHP内で移動のログを緯度経度を一対一で読み出し、それをすべての情報を読み出すまで行います。
JavaScript内ではPHP内で読み出した位置情報を変数に格納しています。そしてGoogleMap内で表示する要素の指定を行い、位置情報とキーをAPIに送信し、帰ってきた情報を指定の要素内に表示しています。
実際に使ってみた
・実行風景
現状はモバイルバッテリーにてESP32を動作させています。
またスマホからテザリングを行い、ESP32とサーバを同じネットワークで動作させており、ESP32からの情報をAPサーバが受信しています。この状態でバッグに一式を入れ、赤羽駅から王子駅まで自転車に乗ってみました。
結果からお伝えすると思った動作をさせることができませんでした。
以下が実際のページのキャプチャになります。
・位置情報表示ページ
・移動履歴表示ページ
もっと連続的にGPSを測位できると思っていたのですが、移動中実際に測位できていた箇所は4か所程度でした。また王子駅到着後なかなかGPSの受信ができず、地図上だと王子駅に到着できていません。
自宅で動作確認をした時には特に問題なかったのですが、実際に移動すると頻繁にGPSが受信できないようになってしまっているようでした。
次回は、精度の高いGPSモジュールを買い直して、連続的に測位ができるようにしていきたいです。
さいごに
この記事では、ESP32とWebサーバとのやり取りについて記載しました。
今回は、本来作りたかったものを完成させることができなかったため、次回完成形の記事を披露したいと思います。
結果は改善の余地有りでしたが、このように自身が作りたい、と思ったものに対して自分で情報を集めて、自分で形にしていくことの楽しさを知ることができ、自身が行っている仕事のモチベーションを上げることができました。
この記事を読んだ方が、自分もなにか作ってみたいと思っていただけたら幸いです。