Pull to refresh

Конечный автомат или Arduino с GPS


Возникла задача подключить к Arduino GPS приемник EB-240 TD.

Как подключить расписывать не буду, это очевидно. На входе получаем из него поток данных в aормате NMEA0183:

$GPGGA,191933.219,2447.362460,N,12055.864119,E,0,0,,135.073,M,14.927,M,,*43
$GPGLL,2447.362460,N,12055.864119,E,191933.219,V,N*4B
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPGSV,1,1,00*79
$GPRMC,191933.219,V,2447.362460,N,12055.864119,E,0.00,0.00,040912,,,N*72
$GPVTG,0.00,T,,M,0.00,N,0.00,K,N*32
$GPZDA,191933.219,04,09,2012,,*50
$GPGGA,191934.219,2447.362460,N,12055.864119,E,0,0,,135.073,M,14.927,M,,*44
$GPGLL,2447.362460,N,12055.864119,E,191934.219,V,N*4C
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPGSV,1,1,00*79
$GPRMC,191934.219,V,2447.362460,N,12055.864119,E,0.00,0.00,040912,,,N*75
$GPVTG,0.00,T,,M,0.00,N,0.00,K,N*32
$GPZDA,191934.219,04,09,2012,,*57


Что характерно. при первом запуске определяются координаты N24 47,362460 E120 55,864119 — фабрика в Тайване, где было произведено устройство.

Теперь хотелось бы привести все это в удобный вид. Сначала я не хотел изобретать велосипед и обратился к Гуглу в поисках готового решения. Решений было несколько, но все они сводились к тому, что сперва мы читаем строку, а потом разбираем ее на части. Мне показалось, что этот алгоритм несколько избыточен, и вообще не очень как-то. Зачем сначала читать байты один за другим, соединять их в строку, а потом эту же строку разбирать на части?

Представим работу программы как конечный автомат: будем отслеживать его состояние после считывания каждого байта.
Допустим, нам нужно обнаружить и разобрать строку

$GPGGA,191933.219,2447.362460,N,12055.864119,E,0,0,,135.073,M,14.927,M,,*43

Начальное (нулевое) состояние автомата не определено. Читаем один символ, если это $, то переходим в следующее состояние (1); иначе — переходим обратно в (0). Если в состоянии (1) мы считываем G — переходим в состояние (2), иначе — обратно в (0). И так, пока мы не перейдем в состояние, когда распознаем подряд символы $GPGGA. Это наше распознающее состояние для этой строки. Далее формат NMEA подразумевает несколько значений, разделенных запятой и мы считываем эти значения опять же на лету функцией readVal().

#include <SoftwareSerial.h>
SoftwareSerial mySerial(2, 3); // RX, TX

char st = '0';
String time;
String lat;
char lat_s;
String lon;
char lon_s;
char qual;
String sats;
String alt;

boolean renew = false;

void setup()
{
Serial.begin(9600);
mySerial.begin(9600);
}

String readVal() {
boolean rot = true;
String buffer;
while(rot) {
int b = mySerial.read();
if ( (('0' <= (char)b) && ((char)b <= '9')) || ((char)b == '.') || (('A' <= (char)b) && ((char)b <= 'Z')) ) { // считываем значения, содержащие цифры, буквы и точку
buffer += (char)b;
}
if ( (char)b == ',' ) rot = false; // заканчиваем читать, когда встречаем запятую
delay(10);
}
return buffer;
}

void loop() // run over and over
{
int b = mySerial.read();
//Serial.write(b);
//Serial.write(st);

switch ( st ) { // перебираем состояния
case '0': if('$' == b) st = '1';
break;
case '1': if('G' == b) st = '2'; else st = '0';
break;
case '2': if('P' == b) st = '3'; else st = '0';
break;
case '3': if('G' == b) st = '4'; else st = '0';
break;
case '4': if('G' == b) st = '5'; else st = '0';
break;
case '5': if('A' == b) st = '6'; else st = '0';
break;
case '6': if(',' == b) { // распознали $GPGGA
time = readVal();
lat = readVal();
lat.replace('.',',');
lat_s = (char)readVal()[0];
lon = readVal();
lon.replace('.',',');
lon_s = (char)readVal()[0];
qual = (char)readVal()[0];
sats = readVal();
readVal(); // skip empty
alt = readVal();

renew = true;
st = '0';
} else st = '0';
break;

}

if (renew) {
Serial.print(«Time: » + time + " Lat/Lon: " + lat_s + lat + " " + lon_s + lon + "\r\n");
renew = false;
}
delay(10);

}


на выходе имеем строки типа:

Time: 190047.330 Lat/Lon: N2447.362460 E12055.864119


Таким же образом находим в потоке и другие данные, которые вам нужны.
Мне этот метод показался наиболее простым и эффективным.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.