×

GPS受信機のデータをSubGHzで飛ばす!

2017-08-08

いつもハイテンションな「らずらいと兄貴」です!!

今回は、Lazurite HPのフォーラムでお客様からお問い合わせいただいた、シリアル接続のGPS受信機のデータをSubGHzで飛ばしたい!を、どうやったら実現できるかの回です。とても簡単に思えて実は奥が深い内容になっていますので、是非お読みください。

初めにお読みください

本プログラムはサンプルプログラムとして、新しいLazurite IDEに同梱をしました。
また、そして本プログラムを安定的に動作をさせるため、無線ドライバ内でもUARTのデータを取りこぼさないような修正を加えていますので、8/8に公開したLazuriteIDEをダウンロードしてご使用ください。

GYSFDMAXB_subghz

ACKの有無による動作の違い

LazuriteのSubGHz通信では、確実に届けたい場合のACKあり送信と、送達確認は不要でとにかくバンバン送りたいACKなし送信の2パターンがあり、次のような特徴があります。

ACK 送信先に届いたかどうか 送信完了までの時間
あり 分かる 送信が失敗した時の待ち時間が長い
なし 分からない 送信の成功/失敗に関わらず処理時間が一定

それぞれにメリットデメリットがありますので、用途に応じて使い分けが必要で、ACKありとなしの2パターンでサンプルコードを交えて説明します。

今回使用したGPS受信機は、GYSFDMAXBという太陽誘電製のGPSモジュールです。このモジュールからは、テキスト形式のNMEAフォーマットのデータをUART 9600bps(初期値)で受信するので、LazuriteのSerial2(14番ピンRX、15番ピンTX)に接続しました。

NMEAフォーマットとは、以下のような$GPから始まり、改行コードで終わるテキストデータです。

$GPGGA,020327.000,3530.5603,N,13936.9421,E,1,8,1.06,36.9,M,39.5,M,,*61
$GPGLL,3530.5603,N,13936.9421,E,020327.000,A,A*58
$GPGSA,A,3,30,15,05,20,13,21,28,24,,,,,1.34,1.06,0.83*05
...

SubGHzの送信設定は、ACKありなし共通で、100kbps、20mWにしています。

ACKなしで飛ばす

まずは簡単な方で、ACKなし(バンバン送る)の場合のサンプルコードからです。
初期値はACKありですので、setup()でSubGHz.setAckReq(false)を呼んでACKなしにしています。これで相手からのACKを待たず、一方的に送信するだけです。

void gps_output()
{
	digitalWrite(LED,LOW);								// LED ON
	Serial.println("send");
	SubGHz.send(SUBGHZ_PANID, HOST_ADDRESS, gps_buf, bufp, NULL);	// send data
	digitalWrite(LED,HIGH);								// LED off
}

void setup() {
  // put your setup code here, to run once:
	pinMode(LED,OUTPUT);								// setting of LED
	digitalWrite(LED,HIGH);								// setting of LED

	SubGHz.init();										// initializing Sub-GHz
	SubGHz.setAckReq(false);							// no ACK
	SubGHz.begin(SUBGHZ_CH, SUBGHZ_PANID, SUBGHZ_100KBPS, SUBGHZ_PWR_20MW);		// start Sub-GHz
	
	Serial.begin(115200);
	Serial2.begin(9600);
	Serial2.flush();
}

void loop() {
  // put your main code here, to run repeatedly:
	uint8_t data;

	if (Serial2.available() > 0) {
		data = (uint8_t) Serial2.read();
		if ((data == 0x0D) || (data == 0x0A)) {			// if end of line
			if ((bufp > 0) && (bufp < GPS_BUF_SIZE)) {
				gps_buf[bufp] = NULL;
				gps_output();							// output gps data
			}
			bufp = 0;
		} else if (bufp < GPS_BUF_SIZE) {
			gps_buf[bufp++] = data;
		}
	}
}

解説

loop()では、Serial2のFIFOにデータがあったら読み込んで、改行コードだったらgps_output()で送信します。一行で最大7~80バイト程度のGPSデータを100kbpsで送信するので、SubGHz.send()の実行時間は十数ms(※)です。

※ 100kbpsをバイトあたりの送信時間に直すと、0.08ms/byte。80バイトだと80 * 0.08 = 6.4msですが、送信前のキャリアセンスなど必要な処理も加わるので、十数msという計算です。

GPSのUART転送速度は9600bps(初期値)ですので、1バイトあたりの送信時間は約1.0ms/byte。SubGHz.send()で送信している十数ms間に、Serial2のFIFOにバッファリングされるデータは最大でも約十数バイトとなり、128バイトのFIFOサイズ内に収まりますので、次のloop()のタイミングにSerial2.read()でFIFOから読み込めば、GPSの受信データを取りこぼすことはありません。

ACKありで飛ばす

ACKありの場合は送信先からACKが即座に受信できるとは限りませんので、送信に失敗したときの対策を考慮しておく必要があります。

実際のサンプルプログラムがこちらです。

void gps_output(void)
{
	SUBGHZ_MSG msg;
	uint32_t tmp;

	if (strncmp(gps_buf, NMEA_GPRMC, GPS_SEN_SIZE) == 0) {
		digitalWrite(LED,LOW);								// LED ON
		Serial.println("send");
		tmp = millis();
		msg = SubGHz.send(SUBGHZ_PANID, HOST_ADDRESS, gps_buf, bufp, NULL);	// send data
		Serial2.flush();
		digitalWrite(LED,HIGH);								// LED off
		if (msg == SUBGHZ_TX_ACK_FAIL) {
			Serial.print("Couldn't receive ACK even after ");
			Serial.print_long(millis()-tmp,DEC);
			Serial.println(" ms passed.");
		}
	}

}

void setup() {
  // put your setup code here, to run once:
	SUBGHZ_PARAM param;

	pinMode(LED,OUTPUT);								// setting of LED
	digitalWrite(LED,HIGH);								// setting of LED

	SubGHz.init();										// initializing Sub-GHz
	SubGHz.begin(SUBGHZ_CH, SUBGHZ_PANID, SUBGHZ_100KBPS, SUBGHZ_PWR_20MW);		// start Sub-GHz

	Serial.begin(115200);
	Serial2.begin(9600);
	Serial2.flush();
}

void loop() {
  // put your main code here, to run repeatedly:
	uint8_t data;

	if (Serial2.available() > 0) {
		data = (uint8_t) Serial2.read();
		if ((data == 0x0D) || (data == 0x0A)) {			// if end of line
			if ((bufp > 0) && (bufp < GPS_BUF_SIZE)) {
				gps_buf[bufp] = NULL;
				gps_output();
			}
			bufp = 0;
		} else if (bufp < GPS_BUF_SIZE) {
			gps_buf[bufp++] = data;
		}
	}
}

解説

ACK有りの場合、考慮しなくては行けないのが送信失敗時の対策になります。
920MHzの送信に失敗したとき、最大で約2秒の時間を要します。この時間は再送回数やACK待ち時間に応じた処理時間が発生しますので、デフォルトの設定では送信失敗の時に約2秒、成功した場合でも最大で1.5秒程度の時間がかかってしまうことがあります。一方、GPSモジュールはその間にもGPSのデータを送信してきますのでシリアル用バッファがオーバーフローしてGPSの値を取りこぼしてしまう可能性があります。
そのため、ACK有りの処理では送信後にUARTのバッファを初期化して、新たなGPSの信号を受信してから送信するというプログラムになっています。

最後に

接続してみるだけであれば簡単なのですが、システムを安定的に動かそうとすると以外に奥が深いGPSデータの送信でした。GPSからの入力と、920MHz無線による出力のスループットを考えて設計しないとシステムが破綻してしまいます。
どちらがおすすめということはありませんので、用途に合わせてご使用ください。