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から始まり、改行コードで終わるテキストデータです。
[code lang=text]
$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
…
[/code]
SubGHzの送信設定は、ACKありなし共通で、100kbps、20mWにしています。
ACKなしで飛ばす
まずは簡単な方で、ACKなし(バンバン送る)の場合のサンプルコードからです。
初期値はACKありですので、setup()でSubGHz.setAckReq(false)を呼んでACKなしにしています。これで相手からのACKを待たず、一方的に送信するだけです。
[c]
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;
}
}
}
[/c]
解説
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が即座に受信できるとは限りませんので、送信に失敗したときの対策を考慮しておく必要があります。
実際のサンプルプログラムがこちらです。
[c]
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;
}
}
}
[/c]
解説
ACK有りの場合、考慮しなくては行けないのが送信失敗時の対策になります。
920MHzの送信に失敗したとき、最大で約2秒の時間を要します。この時間は再送回数やACK待ち時間に応じた処理時間が発生しますので、デフォルトの設定では送信失敗の時に約2秒、成功した場合でも最大で1.5秒程度の時間がかかってしまうことがあります。一方、GPSモジュールはその間にもGPSのデータを送信してきますのでシリアル用バッファがオーバーフローしてGPSの値を取りこぼしてしまう可能性があります。
そのため、ACK有りの処理では送信後にUARTのバッファを初期化して、新たなGPSの信号を受信してから送信するというプログラムになっています。
最後に
接続してみるだけであれば簡単なのですが、システムを安定的に動かそうとすると以外に奥が深いGPSデータの送信でした。GPSからの入力と、920MHz無線による出力のスループットを考えて設計しないとシステムが破綻してしまいます。
どちらがおすすめということはありませんので、用途に合わせてご使用ください。