4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

リモートID受信機を自作する。(パート4)

Last updated at Posted at 2023-03-21

操縦者位置を追加したい

前回のリモートID受信機を自作する(パート3)に、操縦者の位置も表示させる様にして見たいと思います。
受信機側にもGNSS(GPS)モジュールを取り付けることで、位置情報を取得し、スマホに送信すると言う仕組みです。

用意するもの

① マイコン Seeed XIAO ESP32C3 (スイッチサイエンス 902円)
② GNSS(GPS)モジュール Flywoo GOKU GM10 Nano V3 GPS (HELIMONStER 2890円)

準備

RID送信機を作った要領で、GNSSモジュールを接続します。

オペレータ表示用のマークを用意します。
下の画像を「名前を付けて画像を保存」して、名前を[opmark.png]として下さい。
OPマーク.png
その他のマップやマークは前回を参照して用意して下さい。

プログラム

前回までのVSCodeプロジェクトを変更していきます。
platformio.iniを下記のように書き換えてください。GPS用のライブラリーを追加しています。

platformio.ini
[env:seeed_xiao_esp32c3]
platform = espressif32
board = seeed_xiao_esp32c3
framework = arduino
lib_deps = 
	links2004/WebSockets@^2.3.7
	ottowinter/ESPAsyncWebServer-esphome@^3.0.0
	mikalhart/TinyGPSPlus@^1.0.3	
monitor_speed = 115200
board_build.partitions = no_ota.csv

先ほどダウンロードした[opmark.png]を、dataフォルダにドラック&ドロップして追加します。

index.htmlを下記のように書き換えてください。

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta http-equiv="content-type" charset="UTF-8">
<title>RID Receiver</title>
<style>
	body	{background-color:rgba(128,128,128,0.322);}
	h1		{font-size: 2em;color:rgb(156,5,5); background-color: whitesmoke;}
	h2		{font-size: 1.5em;color:black;}
	span	{color:blue;}
	.pos	{position:absolute; left: 0px; top:0px; width: 1000px; height: 1000px;}
</style>
</head>
<!-----------------------------------------HTML----------------------------------------->
<body>
	<h1>RID受信情報</h1>
	<h2>
		名前: <span style="color:rgb(216, 3, 3)" id="name"> </span><br>
		MAC: <span id="addr"></span><br>
		RSSI: <span id="rssi">0</span><br>
		Primary PHY: <span id="pPhy"></span><br> 
		Secondary PHY: <span id="sPhy"></span><br>
		Tx Power: <span id="pw"></span><span>dBm</span><br>
		Counter: <span id="cnt"></span><br>
		---------------------------------<br>
		製造番号: <span id="SN"></span><br>
		登録記号: <span id="RN"></span><br>
		機体種別: <span id="UA"></span><br>
		緯度: <span id="lat"></span><br>
		経度: <span id="lng"></span><br>
		速度: <span id="speed"></span><br>
		高度: <span id="alti"></span><br>
		方角: <span id="dir"></span><br>		
		時刻:<span id="time"></span><br>
	</h2>
	<div style="position:relative;">
		<img src="map0" id="map0" class="pos" style="visibility: visible">
	 	<img src="map1" id="map1" class="pos" style="visibility: hidden;">
	 	<img src="map2" id="map2" class="pos" style="visibility: hidden;">
		<img src="map" id="map" class="pos" style="visibility: hidden;">
		<canvas id="cvs" class="pos" width="1000px" height="1000px"></canvas>
		<img src="opmark" id="opmark" style="position:absolute;">
		<img src="mark" id="mark" style="position:absolute;">

	</div>
</body>
<!-------------------------------------JavaScript--------------------------------------->
<script>
	const w = 1000;			// マップの幅
	const h = 1000;			// マップの高さ

	let lng0,lat0,lng1,lat1;
	let curMap = 0;
	let x0=0,y0=0;

	const ctx = document.getElementById('cvs').getContext('2d');	// キャンバスのコンテキスト
	const mk = document.getElementById('mark');						// マークのオブジェクト
	const opmk = document.getElementById('opmark');					// オペレータマークのオブジェクト

	let maps = [
		{ 
			obj: document.getElementById('map0'),	// 自宅周辺の地図
			lat0: 30.000000,   // <------ ここを書き換えてください
			lng0: 100.000000,  // <------ ここを書き換えてください
			lat1: 30.100000,   // <------ ここを書き換えてください
			lng1: 100.100000   // <------ ここを書き換えてください
		},
		{
			obj: document.getElementById('map1'),	// 良く行く場所の地図
			lat0: 31.000000,   // <------ ここを書き換えてください
			lng0: 101.000000,  // <------ ここを書き換えてください
		    lat1: 31.100000,   // <------ ここを書き換えてください
			lng1: 101.100000   // <------ ここを書き換えてください
		},
		{ 
			obj: document.getElementById('map2'),	// 遠征先の地図
			lat0: 32.000000,   // <------ ここを書き換えてください
			lng0: 102.000000,  // <------ ここを書き換えてください
			lat1: 32.100000,   // <------ ここを書き換えてください
			lng1: 102.100000   // <------ ここを書き換えてください
		},	
		{ 
			obj: document.getElementById('map'),	// 日本全体の地図(↑のどれにも当てはまら無い場合に表示する。これを一番最後に入れて下さい。)
			lat0: 23.584126,
			lng0: 122.058105,
			lat1: 45.874712,
			lng1: 149.523926
		}	
	];

	function drawLine(x,y){					// 移動経過のラインを描画
		if(x0 != 0){
			ctx.beginPath();
     	    ctx.moveTo(x0, y0);
     	    ctx.lineTo(x, y);									
			ctx.strokeStyle = '#FF0000';	// ラインの色
  		    ctx.lineWidth = 1;				// ラインの幅
			ctx.stroke();
		}
		x0 = x;
		y0 = y;
	}

	function setMap(n){						// マップ表示を切り替える
		for(let i=0; i < maps.length; i++){
			maps[i].obj.style.visibility = "hidden";
		}
		maps[n].obj.style.visibility = "visible";
		lng0 = maps[n].lng0;
		lat0 = maps[n].lat0;
		lng1 = maps[n].lng1;
		lat1 = maps[n].lat1;
		curMap = n;
		ctx.clearRect(0, 0, w, h);			// キャンバスのラインを消す
		x0 = 0;
	}
			
	function mapCheck(lng,lat){				// 経度、緯度から表示するマップを切り替える
		let n = maps.findIndex(m => m.lng0 <= lng && m.lat0 <= lat && lng <= m.lng1 && lat <= m.lat1 )
		if(n !== -1){
			if( n != curMap){
				setMap(n);
			}
		}
	}

	function InitWebSocket(){												// Websocketの初期設定
		websock = new WebSocket('ws://'+window.location.hostname+':81/');	// Websocket開始

		websock.onmessage = function(evt){									// データが送られて来た時の処理
			JSONobj = JSON.parse(evt.data);									// Json文字列を解析
			document.getElementById('name').innerHTML = JSONobj.name;
	        document.getElementById('addr').innerHTML = JSONobj.addr;
			document.getElementById('rssi').innerHTML = JSONobj.rssi;
			document.getElementById('cnt').innerHTML = JSONobj.cnt;
			document.getElementById('SN').innerHTML = JSONobj.SN;
			document.getElementById('RN').innerHTML = JSONobj.RN;
			document.getElementById('UA').innerHTML = JSONobj.UA;		
			document.getElementById('lat').innerHTML = JSONobj.lat;
			document.getElementById('lng').innerHTML = JSONobj.lng;
			document.getElementById('speed').innerHTML = JSONobj.speed;
			document.getElementById('alti').innerHTML = JSONobj.alti;
			document.getElementById('dir').innerHTML = JSONobj.dir;			
			document.getElementById('time').innerHTML = JSONobj.time;                
			document.getElementById('pPhy').innerHTML = JSONobj.pPhy;
			document.getElementById('sPhy').innerHTML = JSONobj.sPhy;
			document.getElementById('pw').innerHTML = JSONobj.pw;

			var lng = parseFloat(JSONobj.lng);								// RID 位置情報
			var lat = parseFloat(JSONobj.lat);
			mapCheck(lng,lat);												// 現在地に合わせて、マップを変更する

			var x = Math.trunc( w * (lng - lng0) / (lng1 - lng0) );			// 経度をx座標に変換
			var y = h - Math.trunc( h * (lat - lat0) / (lat1 - lat0) );	    // 緯度をy座標に変換

			var op_lng = parseFloat(JSONobj.oplng);							// オペレータ位置情報
			var op_lat = parseFloat(JSONobj.oplat);
			var ox = 0, oy = 0;
			if(op_lng != 0 && op_lat !=0){
				ox = Math.trunc( w * (op_lng - lng0) / (lng1 - lng0) );		// オペレータ経度をx座標に変換
				oy = h - Math.trunc( h * (op_lat - lat0) / (lat1 - lat0) );	// オペレータ緯度をy座標に変換
			}

			if(curMap == maps.length-1){
				y += 36;													// 日本地図では何故か縦づれするので補正
				oy += 36;
			}
			opmk.style.left = ox - 13 + 'px';								// オペレータマークの位置を変更(マークの左上角座標)	
			opmk.style.top = oy - 41 +'px';		
			mk.style.left = x - 13 + 'px';									// マークの位置を変更(マークの左上角座標)	
			mk.style.top = y - 42 +'px';
			drawLine(x, y);													// 軌跡を描画
		}
	}

	window.onload = function() {								// ページが読み込まれたら最初に実行する
		InitWebSocket();
		setMap(0);												// 最初に表示するマップ番号を指定
	}

</script>
</html>

main.cppを下記のように書き換えてください。

main.cpp
#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <WebSocketsServer.h>
#include <SPIFFS.h>
#include <FS.h>
#include <TinyGPSPlus.h>

const char *ssid="RID_Receiver";
const char *pass="12345678";		   // パスワード(8Byte以上)
const IPAddress ip(192,168,5,1);	   // IPアドレス
const IPAddress subnet(255,255,255,0); // サブネットマスク

// ADタイプ
#define AD_TYPE_FLAG			0x01	// 発信モード
#define AD_TYPE_CMP_LOCAL_NAME	0x09	// 名前
#define AD_TYPE_TX_POWER		0x0A	// 送信出力値
#define AD_TYPE_SERVICE_DATA	0x16	// サービスデータ型 [16-bit UUID]

// メッセージ種別
#define MSG_TYPE_BASIC_ID		0x00	// 製造番号、登録記号
#define MSG_TYPE_LOCATION		0x10	// 位置情報
#define MSG_TYPE_AUTH			0x20	// 認証情報
#define MSG_TYPE_SELF_ID		0x30	// 操作目的 [文字列]
#define MSG_TYPE_SYSTEM			0x40	// リモートパイロットの位置情報
#define MSG_TYPE_OPERATOR_ID	0x50	// 操縦者ID [文字列]
#define MSG_TYPE_PACK  			0xF0	// パッケージ

// UAS ID Type
#define ID_TYPE_SerialNo		0x10	// 製造番号種別
#define ID_TYPE_ASSIGED_REG		0x20	// 登録番号種別
#define ID_TYPE_UUID			0x30	// UTM Assigned UUID

// 機体種別
#define UA_TYPE_NON						  0		// なし
#define UA_TYPE_AEROPLANE				  1		// 飛行機
#define UA_TYPE_HELICOPTER				  2		// ヘリコプター
#define UA_TYPE_GYROPLANE				  3		// ジャイロプレーン
#define UA_TYPE_HYBRID_LIFT				  4		// 垂直離陸固定翼機(ハイブリッドリフト)
#define UA_TYPE_ORINITHOPTER			  5		// 羽ばたき機(オルニソプター)
#define UA_TYPE_GLIDER					  6		// グライダー(滑空機)
#define UA_TYPE_KITE					  7		// カイト(凧)
#define UA_TYPE_FREE_BALLOON			  8		// 自由気球
#define UA_TYPE_CAPITIVE_BALLOON		  9		// 係留気球
#define UA_TYPE_AIRSHIP					  10	// 飛行船
#define UA_TYPE_FREE_FALL_PARACHUTE		  11	// パラシュート
#define UA_TYPE_ROCKET					  12	// ロケット
#define UA_TYPE_TETHERED_POWERED_AIRCRAFT 13	// テザー式動力航空機
#define UA_TYPE_GROUND_OBSTACLE			  14	// 地上障害物
#define UA_TYPE_OTHER					  15	// その他

// 位置情報
#define LOC_STA_NON			0x00	// 不明
#define LOC_STA_GROUND	    0x10	// 地面
#define LOC_STA_AIRBRONE	0x20	// 飛行中
#define LOC_FLAG_HT			0x04	// Height Type 0:Above Takeoff 1:AGL(Above Ground Level)地面からの高度
#define LOC_FLAG_EW 		0x02	// Eeast/West Direction 0:<180 1>=180
#define LOC_FLAG_SM 		0x01	// Speed Multiplier 0:x0.25 1:x0.75

#pragma pack(push,1)	// データを1バイト単位に詰めて配置
typedef struct{
	uint8_t type;				// Location(MSG_TYPE_LOCATION)
	uint8_t status;				// 飛行中、方角E/W、速度倍率などの状態
	uint8_t dir;				// 方角
	uint8_t speed;				// 速度
	uint8_t Ver_speed;			// 垂直速度
	uint32_t lat;				// 緯度
	uint32_t lng;				// 経度							
	uint16_t Pressur_Altitude;	// 気圧高度
	uint16_t Geodetic_Altitude;	// GPS高度
	uint16_t Height;			// 地面からの高さ
	uint8_t V_H_Accuracy;		// 垂直、水平速度精度
	uint8_t B_S_Accuracy;		// 気圧、速度精度
	uint16_t timestamp;			// 現在時間の分以下小数点第1位までの秒数X10
	uint8_t T_Accuracy;			// 時間精度
	uint8_t resv3;
} location;

typedef struct{
	uint8_t type;			// 認証情報
	uint8_t Auth_Type;		// Authentication Message [認証情報]
	uint8_t page_count;		// Page0
	uint8_t Length;        	// headからのサイズ
	time_t timestamp_auth;	// 現在時刻(2019.1.1からの秒数)
	uint8_t auth_head;      // ヘッダ
	char auth_data[16];		// 認証データ
}AUTH;
#pragma pack(pop)

TinyGPSPlus gps;
AsyncWebServer server(80);
WebSocketsServer webSocket = WebSocketsServer(81);

uint32_t scanTime = 100; //In 10ms (1000ms)
BLEScan* pBLEScan;

String phy[3] ={"1M PHY","2M PHY","Coded PHY"};
String ua[16] ={"なし","飛行機","ヘリコプター","ジャイロプレーン","ハイブリッドリフト","羽ばたき機","グライダー","カイト(凧)","自由気球","係留気球","飛行船","パラシュート","ロケット"," テザー式動力航空機","地上障害物","その他"};
uint8_t msg_size = 25;
location *loc;
AUTH *auth;
uint8_t counter, ua_type;
char name[30],serNo[23],regNo[23],addr[18];
char s_lat[20],s_lng[20],s_time[22],s_speed[20],s_alti[20], s_dir[6],op_lat[20],op_lng[20];
int8_t rssi;
uint8_t primary_phy, secondary_phy, tx_power;
String jsonTxt;
bool dataReady = false;
uint16_t ss=0;

void setJson(){
	jsonTxt ="{\"addr\":\""		+ String(addr) +	"\",";
	jsonTxt += "\"name\":\""	+ String(name) +	"\",";	
	jsonTxt += "\"rssi\":\""	+ String(rssi) +	"\",";
 	jsonTxt += "\"cnt\":\""		+ String(counter) + "\",";	
	jsonTxt += "\"SN\":\""		+ String(serNo) +	"\",";
	jsonTxt += "\"RN\":\""		+ String(regNo) +	"\",";
	jsonTxt += "\"UA\":\""		+ ua[ua_type] +	    "\",";
	jsonTxt += "\"lat\":\""		+ String(s_lat) + 	"\",";
	jsonTxt += "\"lng\":\""		+ String(s_lng) +	"\",";
	jsonTxt += "\"speed\":\""	+ String(s_speed) +	"\",";
	jsonTxt += "\"alti\":\""	+ String(s_alti) +	"\",";
	jsonTxt += "\"dir\":\""		+ String(s_dir) +	"\",";	
	jsonTxt += "\"time\":\""	+ String(s_time) +	"\",";
	jsonTxt += "\"pPhy\":\""	+ phy[primary_phy-1] +	"\",";
	jsonTxt += "\"sPhy\":\""	+ phy[secondary_phy-1] +"\",";
	jsonTxt += "\"pw\":\""		+ String(tx_power) +	"\",";
	jsonTxt += "\"oplat\":\""	+ String(op_lat) + 	"\",";
	jsonTxt += "\"oplng\":\""	+ String(op_lng) +	"\"}";	
	dataReady = true;
}

time_t tm2019;	// 1900年から2019年までの秒数
void get_tm2019(){
  struct tm stm;
  stm.tm_year = 2019 - 1900;
  stm.tm_mon  = 0;
  stm.tm_mday = 1;
  stm.tm_hour = 0;
  stm.tm_min  = 0;
  stm.tm_sec  = 0;
  tm2019 = mktime( &stm );
}

void gps_read(void){				// GNSSのデータを読む
	float lat=0,lng=0;
  	
  if (gps.location.isValid()){
  	lat = gps.location.lat();
  	lng = gps.location.lng();
	}
	sprintf(op_lat,"%10.7f",lat);
	sprintf(op_lng,"%10.7f",lng);
}

uint8_t *decode_msg(uint8_t *data){
	uint8_t *adr = data;	
	uint8_t type,size,n;
	uint8_t id_type,status,dir;
	float lat,lng,alti;
    uint16_t h,m,s;
	uint16_t speed;
	time_t tim;
	struct tm t; 
	
	type = data[0] & 0xF0;
	
	switch(type){
		case MSG_TYPE_PACK:							// パケットデータ
			msg_size = data[1];
			n 	 = data[2];
			adr  = &data[3];
			for(int i=0;i<n;i++){
				adr = decode_msg(adr);
			}
			break;
			
		case MSG_TYPE_BASIC_ID:
			id_type = data[1] & 0xF0;
			if(id_type == ID_TYPE_SerialNo){		// 製造番号
				memcpy(serNo, &data[2], 20);
				ua_type = data[1] & 0x0F;			// 機体種別
				adr += msg_size;
			}
			if(id_type == ID_TYPE_ASSIGED_REG){	    // 登録記号
				memcpy(regNo, &data[2], 20);
				adr += msg_size;
			}
			break;
		
		case MSG_TYPE_LOCATION:						// 位置情報
			loc = (location *)&data[0];

			status = loc->status; 
			lat = (float)loc->lat/10000000;	        // 緯度
			lng = (float)loc->lng/10000000;	        // 経度
			sprintf(s_lat,"%10.7f",lat);
			sprintf(s_lng,"%10.7f",lng);

			h = loc->timestamp%36000;				// 時刻
			m = h/600;
			s = (h%600)/10;
			ss = h%10;
			s_time[20] = '0' + ss;

			speed = loc->speed;						// 速度
			if(speed<254){
				if(status & LOC_FLAG_SM){
					speed = speed * 0.75 + 255/4;
				}else{
					speed /= 4; 
				}
			}
			sprintf(s_speed,"%4dm/s",speed);

			alti = loc->Geodetic_Altitude;	        // 高度
			alti = alti/2-1000;
			sprintf(s_alti,"%7.1fm",alti);
			
			dir = loc->dir;							// 方向
			if(status & LOC_FLAG_EW){
				dir += 180;
			}
			sprintf(s_dir,"%3d°", dir);

			adr += msg_size;
			break;

		case MSG_TYPE_AUTH:							// 認証情報
			auth = (AUTH *)&data[0];
			tim = auth->timestamp_auth + tm2019;
			tim += 32400;	// 9*60*60(秒数で9時間進める。日本時間用)
			t = *localtime(&tim);
			sprintf(s_time,"%04d/%02d/%02d %02d:%02d:%02d.%1d", t.tm_year + 1900, t.tm_mon +1 , t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, ss);
			adr += msg_size;		
			break;

		default:
			adr += msg_size;
			break;			
	}
	return adr;
}

void decode_data(uint8_t *data, uint8_t size){
	uint8_t *adr, len, type;

	adr = data;
	while(adr < data+size){
		len = adr[0];
		if(len==0)break;

		type = adr[1];
		switch(type){
			case AD_TYPE_CMP_LOCAL_NAME:	// 名前データ
				memcpy(name, &adr[2], 28);
				break;

			case AD_TYPE_SERVICE_DATA:		// サービスデータ
				counter = adr[5];
				decode_msg(&adr[6]);		// メッセージをデコードする
				break;

			default:
				break;
		}
		adr += len+1;
	}
}

/**
* @brief extend adv report parameters
*/
//typedef struct {
//    esp_ble_gap_adv_type_t event_type;              /*!< extend advertising type */
//    uint8_t addr_type;                              /*!< extend advertising address type */
//    esp_bd_addr_t addr;                             /*!< extend advertising address */
//    esp_ble_gap_pri_phy_t primary_phy;              /*!< extend advertising primary phy */
//    esp_ble_gap_phy_t secondly_phy;                 /*!< extend advertising secondary phy */
//    uint8_t sid;                                    /*!< extend advertising sid */
//    uint8_t tx_power;                               /*!< extend advertising tx power */
//    int8_t rssi;                                    /*!< extend advertising rssi */
//    uint16_t per_adv_interval;                      /*!< periodic advertising interval */
//    uint8_t dir_addr_type;                          /*!< direct address type */
//    esp_bd_addr_t dir_addr;                         /*!< direct address */
//    esp_ble_gap_ext_adv_data_status_t data_status;  /*!< data type */
//    uint8_t adv_data_len;                           /*!< extend advertising data length */
//    uint8_t adv_data[251];                          /*!< extend advertising data */
//} esp_ble_gap_ext_adv_reprot_t;

// BLEがアドバタイジングデータを受信したとき呼ばれる
class MyBLEExtAdvertisingCallbacks: public BLEExtAdvertisingCallbacks {
	void onResult(esp_ble_gap_ext_adv_reprot_t report) {
		if(report.event_type & ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY){
//        Serial.println("BLE4.2");
		}else{	// BLE 5.x の時
			esp_bd_addr_t &a = report.addr;			// BLE Address[6Bytes]
			sprintf(addr,"%02X:%02X:%02X:%02X:%02X:%02X",a[0],a[1],a[2],a[3],a[4],a[5]);
			rssi = report.rssi;
			primary_phy = report.primary_phy;
			secondary_phy = report.secondly_phy; 
			tx_power = report.tx_power;
			decode_data(report.adv_data, report.adv_data_len);
			gps_read();
			setJson();
		}
	}
};

void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
	switch(type) {
		case WStype_DISCONNECTED:
			break;
		case WStype_CONNECTED:
			break;
		case WStype_TEXT:
			break;
		case WStype_BIN:
			break;
		case WStype_ERROR:			
		case WStype_FRAGMENT_TEXT_START:
		case WStype_FRAGMENT_BIN_START:
		case WStype_FRAGMENT:
		case WStype_FRAGMENT_FIN:
			break;
	}
}

//----- GNSS( GPS ) 設定用 ---------------------------------------------
#define BAUDRATE	115200	// GNSS ボーレート
#define RATE		100		// GNSS 更新レート 100ms(10Hz)
#define RX_PIN 20
#define TX_PIN 21

#pragma pack(push,1)
typedef struct{
	uint8_t sync1 = 0xB5;
	uint8_t sync2 = 0x62;
	uint8_t cl = 0x06;
	uint8_t id = 0x00;
	uint16_t len = 20;	
	uint8_t port = 1;
	uint8_t rs = 0;
	uint16_t txReady = 0x0000;
	uint32_t mode = 0x000008D0;			// 8bit 1stopbit noPlity
	uint32_t baudrate = BAUDRATE;		// ボーレート
	uint16_t inProtoMaske = 0x0007;		// RTCM2,NMEA,UBX
	uint16_t outProtoMaske = 0x0003; 	// NMEA,UBX
	uint16_t flags = 0;
	uint16_t rs2 = 0;
	uint8_t ckA,ckB;
}UBX_CFG_PRT;

typedef struct {
	uint8_t sync1 = 0xB5;
	uint8_t sync2 = 0x62;
	uint8_t cl = 0x06;
	uint8_t id = 0x00;
	uint16_t len = 1;	
	uint8_t d = 1;
	uint8_t ckA = 0x08;
	uint8_t ckB = 0x22;
}UBX_CFG_PRT_POLL;

typedef struct{
	uint8_t sync1 = 0xB5;
	uint8_t sync2 = 0x62;
	uint8_t cl = 0x06;
	uint8_t id = 0x08;
	uint16_t len = 6;
	uint16_t measRate = RATE;	// 更新時間(ms)
	uint16_t navRate = 1;		// 比率
	uint16_t timeRef = 1;		// GPS time	
	uint8_t ckA,ckB;
}UBX_CFG_RATE;
#pragma pack(pop)

int set_cs(uint8_t *cmd){	// チェックサムを計算
	int i,n;
	uint16_t len;
	uint8_t ca=0;
	uint8_t cb=0;
	len = cmd[4]+cmd[5]*16;
	for(i=2; i<len+4+2; i++){
		ca = (ca + cmd[i]) & 0xFF;
		cb = (cb + ca) & 0xFF;
	}
 	cmd[i++]= ca;
 	cmd[i] = cb;
 	return len+8;	// 全体の長さを返す
}

void send_UBX(uint8_t *buf){
	int len;
	len = set_cs(buf);		// チェックサムを計算
	for(int i=0; i<len; i++){
		Serial1.write(buf[i]);
	}
	Serial1.println("");
	delay(300);
}

UBX_CFG_PRT 		cfg_prt;
UBX_CFG_PRT_POLL	cfg_prt_poll;
UBX_CFG_RATE		cfg_rate;
const  uint32_t init_speed[5] = { 115200, 57600, 38400, 19200, 9600 };

void gnss_setup(){
	// GNSSのボーレートを設定する 
	for (int i = 0; i < 5; i++){	// 現在の速度が判らないので、可能性のある速度でコマンドを送信する。どれかで通じるはず。
		Serial1.begin(init_speed[i], SERIAL_8N1, RX_PIN, TX_PIN);
		while(!Serial1){}
		send_UBX((uint8_t *)&cfg_prt);						// ボーレート設定コマンド
		send_UBX((uint8_t *)&cfg_prt_poll);					// 確認用コマンド
		Serial1.end();
		delay(300);
	}
	Serial1.begin(BAUDRATE, SERIAL_8N1, RX_PIN, TX_PIN);
	while(!Serial1){}
	send_UBX((uint8_t *)&cfg_rate);							// 更新レートを10Hz(100ms間隔)に設定
}
//-------------------------------------------------------------------------------------------

void setup() {
//  Serial.begin(115200);
  gnss_setup();				// GNSSモジュールの初期設定

  if(!SPIFFS.begin(true)){      // SPIFFSのセットアップ
		return;
  }
  get_tm2019(); // 2019年までの秒数を計算しておく

  BLEDevice::init("");
  pBLEScan = BLEDevice::getScan();
  pBLEScan->setExtendedScanCallback(new MyBLEExtAdvertisingCallbacks());	// BLE コールバックルーチンを指定
  pBLEScan->setExtScanParams();
  delay(1000);
  pBLEScan->startExtScan(scanTime, 1); // scan duration in n * 10ms, period - repeat after n seconds (period >= duration)

  WiFi.softAP(ssid,pass);
  WiFi.softAPConfig(ip,ip,subnet);

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/index.html");
  });
  	server.on("/map", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/map.jpg", "image/jpg");
  });
	server.on("/map0", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/map0.jpg", "image/jpg");
  });
	server.on("/map1", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/map1.jpg", "image/jpg");
  });
	server.on("/map2", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/map2.jpg", "image/jpg");
  });
  server.on("/mark", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/mark.png", "image/png");
  });
  server.on("/opmark", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/opmark.png", "image/png");
  });

  server.begin();
  webSocket.begin();
  webSocket.onEvent(webSocketEvent);

}

void loop() {
	webSocket.loop();
	
	while(Serial1.available()){				// UARTにGNSSのデータが来てたら全て読み込む
		if(gps.encode(Serial1.read()))
			gps_read();
	}

	if(dataReady){
		webSocket.broadcastTXT(jsonTxt);	// WebSocketでデータ送信
		dataReady = false;
	}
}

以上です。
使い方は前回を参考にして下さい。

4
3
10

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?