Как стать автором
Обновить

Конечный автомат или 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


Таким же образом находим в потоке и другие данные, которые вам нужны.
Мне этот метод показался наиболее простым и эффективным.
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.