リモートID受信機を自作する。（パート３）


https://qiita.com/kobatan/items/dc26c28c0e283ba6f4c3

https://qiita.com/kobatan/items/fc51a8ac0bd9e95946fa

<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2520758/ae74a408-5c0d-2820-cb14-f13363c0d0a1.jpeg">

[前回のリモートID受信機を自作する（パート２）](https://qiita.com/kobatan/items/dc26c28c0e283ba6f4c3)ではマップの表示は１つだけでしたが、今回は良く行くところのマップをいくつか用意し、その場所に行ったら、自動でそこのマップを表示させるようにして見たいと思います。
　ただ、SPIFFS領域が **２Mバイト**しかないので、マップデータはかなり圧縮する必要があります。

まずは[**前回の記事**](https://qiita.com/kobatan/items/dc26c28c0e283ba6f4c3)と、[**国土地理院のマップのダウンロード方法**](https://qiita.com/kobatan/items/fc51a8ac0bd9e95946fa)を理解して置いて下さい。
## マップの用意
今回は４つのマップを切り替えて表示させようと思います。
国土地理院のマップを４つ用意して下さい。

１つ目は、自宅周辺の地図     
２つ目は、良く行く場所の地図
３つ目は、遠征場所の地図
４つ目は、日本全国の地図（これは↑のどこにも該当しない場合の表示用です）

サイズは縦横が同じになるようにして下さい。例えば（1000ｘ1000）になるように。もっ大きくても大丈夫ですが、データサイズが大きくなるので程々に。
（ブラウザでの表示サイズは1000ｘ1000にしたので、表示はこのサイズに圧縮されます。もっと多く表示させたい場合は、index.htmlファイルを修正して下さい。）
ダウンロードしたマップの緯度経度は、それぞれメモって置いて下さい。
![国土地理院（日本）.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2520758/395ae0cd-46b0-8e46-a75a-bc0d7487699b.jpeg)

　ダウンロードしたファイルは**PNG形式**です。これはファイルサイズが大きいので、画像編集ソフトで**JPG**に変換して下さい。この時圧縮率を50%以下にして、ファイルサイズを300KB以下になるようにしましょう。（dataフォルダ内の容量の合計が**２MB以下**になるようにして下さい。）

ファイル名は以下のようにして下さい。
１つ目は、**map0.jpg**
２つ目は、**map1.jpg**
３つ目は、**map2.jpg**
４つ目は、**map.jpg**    （日本地図だけ、番号なしです）

**日本地図**と**マーク**は用意しました。下図を「名前を付けて画像を保存」して下さい。
名前は「**map.jpg**」と「**mark.png**」としてください。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2520758/ef8e3a54-3064-07f1-60e1-6294d9208e65.jpeg">
<img width="26" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2520758/165d9652-b4cf-abab-f54e-cd0c4d40ac07.png">
## プログラム
前回と同様にVSCodeを立ち上げ、プロジェクトを作って置いて下さい。
上で用意した**マップデータ**と**マーク**を[**data**]フォルダにドラッグ＆ドロップしてコピーして下さい。
![RID_REceiver.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2520758/ddf33ce9-8ede-332f-e938-081f2602a0d4.jpeg)

後は、[前回までのやり方](https://qiita.com/kobatan/items/dc26c28c0e283ba6f4c3)と同じです。

index.htmlは以下です。
**maps = []** の所の**緯度、経度**は、それぞれダウンロードしたマップに合わせて、**書き換えてください**。
```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>
		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>
		<div id="mark" style="position:absolute;">
			<img src="mark">
		</div>
	</div>
</body>
<!-------------------------------------JavaScript--------------------------------------->
<script>
	const w = 1000;			// マップの幅
	const h = 1000;			// マップの高さ

	let lng0,lat0,lng1,lat1;
	let curMap = 0;
	let x0=0,y0=0;
	const mk = document.getElementById('mark');							// マークのオブジェクト
	const ctx = document.getElementById('cvs').getContext('2d');		// キャンバスのコンテキスト

	let maps = [
		{ 
			"No": 0,
			"obj": document.getElementById('map0'),	// 自宅周辺の地図
			"lat0": 30.000000,   // <------ ここを書き換えてください
			"lng0": 100.000000,  // <------ ここを書き換えてください
			"lat1": 30.100000,   // <------ ここを書き換えてください
			"lng1": 100.100000   // <------ ここを書き換えてください
		},
		{
			"No": 1,
			"obj": document.getElementById('map1'),	// 良く行く場所の地図
			"lat0": 31.000000,   // <------ ここを書き換えてください
			"lng0": 101.000000,  // <------ ここを書き換えてください
		    "lat1": 31.100000,   // <------ ここを書き換えてください
			"lng1": 101.100000   // <------ ここを書き換えてください
		},
		{ 
			"No": 2,
			"obj": document.getElementById('map2'),	// 遠征先の地図
			"lat0": 32.000000,   // <------ ここを書き換えてください
			"lng0": 102.000000,  // <------ ここを書き換えてください
			"lat1": 32.100000,   // <------ ここを書き換えてください
			"lng1": 102.100000   // <------ ここを書き換えてください
		},	
		{ 
			"No": 3,
			"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 m = maps.find(m => m.lng0 <= lng && m.lat0 <= lat && lng <= m.lng1 && lat <= m.lat1 )
		if(m !== undefined){
			if( m.No != curMap){
				setMap(m.No);
			}
		}
	}

	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('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);
			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座標に変換

			if(curMap == maps.length-1) y += 36;						// 日本地図では何故か縦づれするので補正
			
			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
#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>

const char *ssid="RID_Receiver";
const char *pass="12345678";		   // パスワード（８Byte以上）
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)	// データを１バイト単位に詰めて配置
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;			// 現在時間の分以下小数点第１位までの秒数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)

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];
char s_lat[20],s_lng[20],s_time[22],s_speed[20],s_alti[20], s_dir[6];
int8_t rssi;
uint8_t primary_phy, secondary_phy, tx_power;
String jsonTxt;
bool dataReady = false;
uint16_t ss=0;

void setJson(){
	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) +	"\"}";
	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 );
}

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;
			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 + 9)%24, 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 の時
			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);
			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;
	}
}

void setup() {
//  Serial.begin(115200);

  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.begin();
	
  webSocket.begin();
  webSocket.onEvent(webSocketEvent);

}

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

[使い方は前回を参考にして下さい。](https://qiita.com/kobatan/items/dc26c28c0e283ba6f4c3)
以上です。