Ви не увійшли.
Намагаюся розпарсити рядок данних NMEA. Зробив такий скетч.
#include <SoftwareSerial.h>
// Настраиваем пины для подключения GPS-модуля
const int RXPin = 4, TXPin = 3;
SoftwareSerial gpsSerial(RXPin, TXPin);
char nmeaBuffer[88]; // Буфер для строки NMEA
int bufferIndex = 0;
char token_1[88];
char token_2[88];
char token_3[88];
char token_4[88];
char token_5[88];
char token_6[88];
char token_7[88];
char token_8[88];
char token_9[88];
char token_10[88];
char token_11[88];
// $GPRMC,180708.000,A,4829.8325,N,03556.0990,E,4.21,66.09,040425,,,A*5F
// $GPRMC,180654.000,A,4829.8202,N,03556.0560,E,0.00,,040425,,,A*70
void setup() {
Serial.begin(115200); // Настройка монитора порта
gpsSerial.begin(9600); // Настройка GPS-модуля
Serial.println("Ожидание данных...");
}
void loop() {
while (gpsSerial.available()) {
char c = gpsSerial.read();
// Считываем строку, пока не достигнем конца строки
if (c == 'n') {
nmeaBuffer[bufferIndex] = ''; // Завершаем строку
processNMEA(nmeaBuffer); // Обрабатываем строку
bufferIndex = 0; // Сбрасываем индекс для следующей строки
} else if (bufferIndex < 87) {
nmeaBuffer[bufferIndex++] = c; // Сохраняем символ в буфер
}
}
}
void processNMEA(char* nmea) {
// Проверяем, является ли строка строкой $GPRMC
if (strncmp(nmea, "$GPRMC", 6) == 0) {
Serial.print("Строка протокола: ");
Serial.println(nmea); // Вывод всей строки $GPRMC
sscanf(nmea, "%[^','],%[^','],%[^','],%[^','],%[^','],%[^','],%[^','],%[^','],%[^','],%[^','],%[^',']",
token_1, token_2, token_3, token_4, token_5, token_6, token_7, token_8, token_9, token_10, token_11);
//Serial.print("Дата: ");
Serial.print(" token_1 = "); Serial.println(token_1);
Serial.print(" token_2 = "); Serial.println(token_2);
Serial.print(" token_3 = "); Serial.println(token_3);
Serial.print(" token_4 = "); Serial.println(token_4);
Serial.print(" token_10 = "); Serial.println(token_10);
Serial.print(" token_11 = "); Serial.println(token_11);
//printFormattedDate(token_10);
} }
// Функция для форматирования даты (ДД.ММ.ГГГГ)
void printFormattedDate(char* rawDate) {
if (strlen(rawDate) < 6) return; // Проверяем длину строки
char dd[3], mm[3], yy[5];
strncpy(dd, rawDate, 2); dd[2] = '';
strncpy(mm, rawDate + 2, 2); mm[2] = '';
strncpy(yy, rawDate + 4, 2); yy[2] = '';
Serial.print(dd); Serial.print(".");
Serial.print(mm); Serial.print(".");
Serial.print("20"); // Добавляем "20" для полного формата года
Serial.println(yy);
}
але результат незадовільний
Ожидание данных...
Строка протокола: $GPRMC,055655.062,V,4829.8313,N,03556.0391,E,,,050425,,,N*72
token_1 = $GPRMC
token_2 = 055655.062
token_3 = V
token_9 = 4829.8313
token_10 =
token_11 =
Строка протокола: $GPRMC,055656.062,V,4829.8313,N,03556.0391,E,,,050425,,,N*71
token_1 = $GPRMC
token_2 = 055656.062
token_3 = V
token_9 = 4829.8313
token_10 =
token_11 =
Строка протокола: $GPRMC,055657.062,V,4829.8313,N,03556.0391,E,,,050425,,,N*70
token_1 = $GPRMC
token_2 = 055657.062
token_3 = V
token_9 = 4829.8313
token_10 =
token_11 =
Строка протокола: $GPRMC,063155.062,A,4829.8269,N,03556.0546,E,0.30,48.66,050425,,,A*57
token_1 = $GPRMC
token_2 = 063155.062
token_3 = A
token_9 = 4829.8269
token_10 = 050425
token_11 =
Строка протокола: $GPRMC,063156.062,A,4829.8269,N,03556.0545,E,0.34,41.38,050425,,,A*51
token_1 = $GPRMC
token_2 = 063156.062
token_3 = A
token_9 = 4829.8269
token_10 = 050425
token_11 =
Строка протокола: $GPRMC,063157.062,A,4829.8270,N,03556.0545,E,0.41,33.89,050425,,,A*55
token_1 = $GPRMC
token_2 = 063157.062
token_3 = A
token_9 = 4829.8270
token_10 = 050425
token_11 =
Проблема в тому, що серія ком ( ,,, ) функцією sscanf обробляється не корректно ...
Остання редакція renoshnik (2025-04-05 09:33:22)
Неактивний
Проблема в тому, що серія ком ( ,,, ) функцією sscanf обробляється не корректно ...
![]()
![]()
sscanf() - невдалий вибір для парсингу такого синтаксиса. Його тут можна застосувати для парсингу окремого токена, але для рядка в цілому він погано підходить.
Токени розділені комами, так шукайте символ коми. Або за допомогою strchr(), або своєю реалізацією, що не набагато складніше. Потім парсіть вміст між комами.
Навіщо копіювати кожний токен в окремий буфер? Він же і так уже присутній в памʼяті. Якщо для парсинга самого токена він має закінчуватись нульовим символом, заміняйте кому на нульовий символ.
Якщо не помиляюсь, максимальна довжина рядка NMEA - 82 символи. Тобто буфера довжиною 83 символи буде достатньо. Чому 88?
Взагалі, ви спочатку накопичуєте всю строку в буфер, потім намагаєтесь її парсити. Звісно, можна і так. Але, мені здається, простіше парсити дані по мірі надходження.
Неактивний
Спробуйте без sscanf, якось так:
#include <stdio.h>
#define TOKEN_COUNT 11
#define TOKEN_SIZE 88
char token[TOKEN_COUNT][TOKEN_SIZE];
void process() {
int i;
for (i = 0; i < TOKEN_COUNT; i++)
printf("token[%i]='%s'\n", i, token[i]);
}
int main() {
int i = 0, j = 0;
for (;;) {
char c = getchar();
if (c == EOF) {
break;
} else if (c == '\n') {
while (i < TOKEN_COUNT) {
token[i++][j] = 0;
j = 0;
}
i = 0;
process();
} else if (c == ',') {
if (i < TOKEN_COUNT)
token[i++][j] = 0;
j = 0;
} else {
if (i < TOKEN_COUNT && j < TOKEN_SIZE - 1)
token[i][j++] = c;
}
}
}
Остання редакція Honey (2025-04-05 11:23:29)
Неактивний
scanf не вміє парсити порожні значення. Беріть strtok.
Я впевнений, що ця задача вирішена 100500 разів. Я б пошукав готовий парсер
Ось цим подивився що видає блок
#include <SoftwareSerial.h>
SoftwareSerial gpsSerial(4, 3); // RX (к Arduino), TX (к GPS-модулю)
void setup() {
Serial.begin(115200);
gpsSerial.begin(9600);
Serial.println("Начало чтения NMEA-строк...");
}
void loop() {
if (gpsSerial.available()) {
static char buffer[128];
static size_t index = 0;
char c = gpsSerial.read();
if (c == 'n') {
buffer[index] = '';
Serial.println(buffer);
index = 0; }
else if (index < sizeof(buffer) - 1) {buffer[index++] = c;}
}
}
отримав такі данні
Начало чтения NMEA-строк...
$GPGGA,085638.000,4829.8262,N,03556.0484,E,1,03,7.9,120.1,M,18.9,M,,0000*57
$GPGSA,A,2,31,29,28,,,,,,,,,,7.9,7.9,1.0*31
$GPRMC,085638.000,A,4829.8262,N,03556.0484,E,0.00,,050425,,,A*72
$GPGGA,085639.000,4829.8262,N,03556.0484,E,1,03,7.9,120.1,M,18.9,M,,0000*56
$GPGSA,A,2,31,29,28,,,,,,,,,,7.9,7.9,1.0*31
$GPGSV,3,1,10,25,82,177,,29,58,268,33,12,46,126,,11,39,054,17*75
$GPGSV,3,2,10,28,37,287,34,20,31,093,23,31,23,312,40,05,17,131,19*79
$GPGSV,3,3,10,06,10,040,,18,09,207,*7E
$GPRMC,085639.000,A,4829.8262,N,03556.0484,E,0.00,,050425,,,A*73
$GPGGA,085640.000,4829.8262,N,03556.0484,E,1,03,7.9,120.1,M,18.9,M,,0000*58
$GPGSA,A,2,31,29,28,,,,,,,,,,7.9,7.9,1.0*31
$GPRMC,085640.000,A,4829.8262,N,03556.0484,E,0.00,,050425,,,A*7D
$GPGGA,085641.000,4829.8262,N,03556.0484,E,1,03,7.9,120.1,M,18.9,M,,0000*59
$GPGSA,A,2,31,29,28,,,,,,,,,,7.9,7.9,1.0*31
$GPRMC,085641.000,A,4829.8262,N,03556.0484,E,0.00,,050425,,,A*7C
$GPGGA,085642.000,4829.8262,N,03556.0484,E,1,03,7.9,120.1,M,18.9,M,,0000*5A
$GPGSA,A,2,31,29,28,,,,,,,,,,7.9,7.9,1.0*31
$GPRMC,085642.000,A,4829.8262,N,03556.0484,E,0.00,,050425,,,A*7F
$GPGGA,085643.000,4829.8262,N,03556.0484,E,1,03,7.9,120.1,M,18.9,M,,0000*5B
$GPGSA,A,2,31,29,28,,,,,,,,,,7.9,7.9,1.0*31
$GPRMC,085643.000,A,4829.8262,N,03556.0484,E,0.00,,050425,,,A*7E
$GPGGA,085644.000,4829.8262,N,03556.0484,E,1,03,7.9,120.1,M,18.9,M,,0000*5C
$GPGSA,A,2,31,29,28,,,,,,,,,,7.9,7.9,1.0*31
$GPGSV,3,1,10,25,82,177,,29,58,268,33,12,46,126,,11,39,054,17*75
$GPGSV,3,2,10,28,37,287,33,20,31,093,22,31,23,312,40,05,17,131,17*71
$GPGSV,3,3,10,06,10,040,,18,09,207,*7E
$GPRMC,085644.000,A,4829.8262,N,03556.0484,E,0.00,,050425,,,A*79
переробив парсер
#include <SoftwareSerial.h>
SoftwareSerial gpsSerial(4, 3); // Пины для подключения GPS (RX, TX)
char value[32];
char buffer[128];
char subBuffer[128]; // Дополнительный буфер для разделения склеенных строк
void parseNMEA(const char* identifier, int commaIndex) {
while (gpsSerial.available()) {
memset(buffer, 0, sizeof(buffer)); // Очистка основного буфера
size_t len = gpsSerial.readBytesUntil('n', buffer, sizeof(buffer) - 1);
buffer[len] = 'n'; // Завершающий символ строки
// Проверка на склеенные строки
if (strstr(buffer, identifier) && strstr(buffer, "$GPGGA")) {
Serial.println("Обнаружена склеенная строка! Разделяем...");
char* secondPart = strstr(buffer, "$GPGGA"); // Находим начало второй строки
size_t firstPartLen = secondPart - buffer; // Вычисляем длину первой строки
// Копируем первую часть строки в отдельный буфер
strncpy(subBuffer, buffer, firstPartLen);
subBuffer[firstPartLen] = 'n';
Serial.print("Первая часть строки: ");
Serial.println(subBuffer);
// Обрабатываем первую часть строки
processLine(subBuffer, identifier, commaIndex);
// Обрабатываем вторую часть строки
Serial.print("Вторая часть строки: ");
Serial.println(secondPart);
processLine(secondPart, identifier, commaIndex);
continue;
}
// Фильтр: проверяем начало строки
if (buffer[0] != '$') {
Serial.println("Строка пропущена: некорректный формат");
continue;
}
Serial.print("Полученная строка: ");
Serial.println(buffer);
// Обработка строки с использованием идентификатора
processLine(buffer, identifier, commaIndex);
}
}
void processLine(const char* buffer, const char* identifier, int commaIndex) {
// Проверяем соответствие идентификатору
if (strncmp(buffer, identifier, strlen(identifier)) == 0) {
const char* ptr = buffer;
for (int i = 0; i < commaIndex; i++) {
ptr = strchr(ptr, ',');
if (!ptr) {
Serial.println("Ошибка: недостаточно запятых.");
return;
}
ptr++; // Переход к следующему символу после запятой
}
// Получаем значение после запятой
//char value[32];
size_t i = 0;
while (*ptr && *ptr != ',' && *ptr != 'r' && *ptr != 'n' && i < sizeof(value) - 1) {
value[i++] = *ptr++;
}
value[i] = 'n';
//Serial.print("Найдено значение: ");
//Serial.println(value);
return(value);
}
}
void setup() {
Serial.begin(115200);
gpsSerial.begin(9600);
Serial.println("Инициализация завершена. Ожидаю данные...");
}
void loop() {
parseNMEA("$GPGSA", 15); // Ищем данные $GPRMC после девятой запятой
Serial.print("-----------Найдено значение $GPGSA, 15 : ");
Serial.println(value);
delay(1000); // Увеличенный интервал для предотвращения переполнения
parseNMEA("$GPGGA", 4); // Ищем данные $GPRMC после девятой запятой
Serial.print("-----------Найдено значение $GPGGA, 4 : ");
Serial.println(value);
delay(1000); // Увеличенный интервал для предотвращения переполнения
parseNMEA("$GPRMC", 9); // Ищем данные $GPRMC после девятой запятой
Serial.print("-----------Найдено значение $GPRMC, 9 : ");
Serial.println(value);
delay(1000); // Увеличенный интервал для предотвращения переполнения
Serial.println("+++++++++++++++++++++++++++++++");
}
тепер отримую таке
Инициализация завершена. Ожидаю данные...
Найдено значение $GPGSA, 15 :
Обнаружена склеенная строка! Разделяем...
Первая часть строки:
Вторая часть строки: $GPGGA,095405.000,4829.8325,N,03556.0791,E,1,03,25.2,189.4,M,18$GPGGA,095406.000,4829.8319,N,03556.0755,E,1,03,25.1,187.5,M,18.
,M,,0000*6A
$GPGSA,A,2,26,31,28,,,,,,,,,,25.2,25.1,1.0*3D
$G.9⸮
Строка пропущена: некорректный формат
Полученная строка: $GPGSA,A,2,26,31,28,,,,,,,,,,25.2,25.1,1.0*3D
Полученная строка: $G829.8319,N,0355,E,23.78,275.81,050425,,,A*57
Найдено значение $GPGGA, 4 : 03556.0791
Полученная строка: $GPGGA,095407.000,4829.8315,N,03556.0725,E,1,03,25.1,185.7,M,18$GPGGA,095408.000,4829.8313,N,03556.0708,E,1,03,25.1,184.3,M,18.
A,2,26,31,3556.0708,E,1,03,25.1,184.3,M,18.9,M,,0000*63
$GPGSA,Y
Строка пропущена: некорректный формат
Полученная строка: $GPGSA,A,2,26,31,28,,,,,,,,,,25.1,25.1,1.0*3E
Полученная строка: $GPRMC,095408.000,A,4829.8313,N,03556.0708,E,21.39,276.20,050425,,,A*54
Найдено значение $GPRMC, 9 : 050425
791
+++++++++++++++++++++++++++++++
Полученная строка: $GPGGA,095409.000,4829.8307,N,03556.0723,E,1,03,25.1,183.0,M,1818,33,214,,28,31,257,20,31,31,287,26,12,22,139,20*7E
Полученная строка: $GPGSV,3,3,10,26,19,309,26,11,17,048,18*79
Полученная строка: $GPRMC,095409.000,A,4829.8307,N,03556.0723,E,18.97,274.54,050425,,,A*56
Найдено значение $GPGSA, 15 : 050425
791
Обнаружена склеенная строка! Разделяем...
Первая часть строки:
Вторая часть строки: $GPGGA,095410.000,4829.8299,N,03556.0744,E,1,03,25.1,181.8,M,18$GPGGA,095411.000,4829.8291,N,03556.0784,E,1,03,25.0,180.7,M,18.
,,,,,25.1,25.0,1.0*3F
$G.9,M,,0000*65
$GPGSA,A,2,26,31,28,,,,,.:
Строка пропущена: некорректный формат
Полученная строка: $GPGSA,A,2,26,31,28,,,,,,,,,,25.1,25.0,1.0*3F
Полученная строка: $G829.8291,N,0354,E,14.19,272.45,050425,,,A*50
Найдено значение $GPGGA, 4 : 03556.0744
Полученная строка: $GPGGA,095412.000,4829.8293,N,03556.0725,E,6,00,50.0,180.5,M,18$GPGGA,095413.000,4829.8294,N,03556.0665,E,6,00,50.0,180.2,M,18.
,M,18.9,M,,0000*6F
$GPGSA,A,2,,,,,,,,,,,,,50.0,,6,00,50.0,180.2⸮!
Строка пропущена: некорректный формат
Полученная строка: $GPGSA,A,2,,,,,,,,,,,,,50.0,50.0,50.0*06
Полученная строка: $GPRMC,095413.000,V,4829.8294,N,03556.0665,E,14.19,272.45,050425,,,E*4A
Найдено значение $GPRMC, 9 : 050425
744
+++++++++++++++++++++++++++++++
наче краще але купа якихось артифактів .....
якщо закоментувати перевірочні виводи в монітор то якась маячня виходить...
Инициализация завершена. Ожидаю данные...
Найдено значение $GPGSA, 15 :
Найдено значение $GPGGA, 4 :
Найдено значение $GPRMC, 9 :
+++++++++++++++++++++++++++++++
Найдено значение $GPGSA, 15 :
Найдено значение $GPGGA, 4 :
Найдено значение $GPRMC, 9 : 050425
+++++++++++++++++++++++++++++++
Найдено значение $GPGSA, 15 : 050425
Найдено значение $GPGGA, 4 :
50425
Найдено значение $GPRMC, 9 : 050425
+++++++++++++++++++++++++++++++
Найдено значение $GPGSA, 15 : 050425
Найдено значение $GPGGA, 4 :
50425
Найдено значение $GPRMC, 9 :
50425
+++++++++++++++++++++++++++++++
Найдено значение $GPGSA, 15 : 28
425
Найдено значение $GPGGA, 4 : 28
425
Найдено значение $GPRMC, 9 : 050425
+++++++++++++++++++++++++++++++
Найдено значение $GPGSA, 15 : 050425
Найдено значение $GPGGA, 4 : 050425
Найдено значение $GPRMC, 9 : 050425
+++++++++++++++++++++++++++++++
Неактивний
Візьміть готовий csv парсер.
Або візьміть приклад з man strtok і переробіть під себе.
Взагалі, ефективна реалізація парсера залежить від того, чи потрібно парсити якість конкретні типи повідомлень, які токени з них потрібні, в якому форматі вони мають бути для зручності подальшої обробки і т.д.
Це класична задача для реалізації у вигляді конечного автомата.
Неактивний
Сорян, дійсно
Ну, читайте посимвольно. Знайдете кому - смикайте за парсер. Знайдете кінець рядка - фіналізуйте запис. Лічильник полів потрібен.
jokeer пише:Або візьміть приклад з man strtok і переробіть під себе.
strtok() витягає тільки непусті токени. NMEA може містити пусті.
отож бо і воно....
#include <SoftwareSerial.h>
#include <string.h>
SoftwareSerial gpsSerial(4, 3);
String nmeaSentence = "";
void setup() {
Serial.begin(115200);
gpsSerial.begin(9600);
Serial.println("Начало чтения NMEA-строк...");
}
void loop() {
while (gpsSerial.available()) {
char c = gpsSerial.read();
if (c == 'n') {
GPS_run(nmeaSentence);
nmeaSentence = "";
} else if (c != 'r') {
nmeaSentence += c;
}
}
}
void GPS_run(String nmea) {
if (nmea.startsWith("$GPGSA")) { // Проверка на идентификатор строки
Serial.println("Обработка строки $GPGSA:");
Serial.println(nmea); // Вывод исходной строки
char nmeaCStr[nmea.length() + 1];
nmea.toCharArray(nmeaCStr, nmea.length() + 1);
char *token = strtok(nmeaCStr, ",");
int fieldIndex = 1;
for (int i = 1; i <= 18; i++) { // для теста обрабатываем ровно 18 полей
Serial.print("Поле ");
Serial.print(i);
Serial.print(": ");
Serial.println(token);
token = strtok(NULL, ","); // Переход к следующему значению
}
}
}
отримую таке
Начало чтения NMEA-строк...
Обработка строки $GPGSA:
$GPGSA,A,3,31,16,18,26,29,,,,,,,,3.1,1.9,2.4*3D
Поле 1: $GPGSA
Поле 2: A
Поле 3: 3
Поле 4: 31
Поле 5: 16
Поле 6: 18
Поле 7: 26
Поле 8: 29
Поле 9: 3.1
Поле 10: 1.9
Поле 11: 2.4*3D
Поле 12:
Поле 13:
Поле 14:
Поле 15:
Поле 16:
Поле 17:
Поле 18:
Обработка строки $GPGSA:
$GPGSA,A,3,31,16,18,26,29,,,,,,,,3.1,1.9,2.4*3D
Неактивний
Ставите вказівник на початок.
Шукаєте кому.
Якщо знайшли - міняєте на 0. Парсите отримане поле. Переставляєте вказівник на символ після 0. Повторюєте.
Перевіряєте краєві умови, щоб не вийти за границю масива.
Десь так
Пишіть функцію для парсера для x64. Можна скористатись нормальним ide з нормальним дебагером. Можна тестами обмазати. Як працюватиме правильно - перенесете на AVR.
Дякую всім, вже вийшло те що хотів
Тепер буду нарощувати код ...
renoshnik пише:отримую таке
Які поля з яких типів повідомлень вам потрібно витягти?
В мене GPS модуль і треба витягнути данні для роботи (час, дата, швидкість, координати і т.п.)
Неактивний
Якась ардуінофобія ... hmm
Навпаки
Якщо використовуєте Arduino Core - дивно не бути послідовними і не використовувати ардуїнівські ліби. Ви ж прямо в регістри UART не пишете? Хіба що вам ліцензійно чистий код потрібен..
Неактивний
Та ось вам іще одна наївна реалізація
class nmea
{
void (nmea::*context_)(char);
uint8_t buf_idx_;
uint8_t tok_idx_;
uint8_t tok_start_;
uint8_t checksum_;
bool ready_;
char buf_[80];
uint8_t tokens_[80];
void update_checksum(uint8_t c)
{
checksum_ ^= c;
}
void skip()
{
context_ = &nmea::ctx_skip;
}
void reset()
{
context_ = &nmea::ctx_reset;
}
void append_buf(char c)
{
if (buf_idx_ >= sizeof(buf_)/sizeof(*buf_)) {
skip();
return;
}
buf_[buf_idx_++] = c;
}
void append_tok()
{
if (tok_idx_ < sizeof(tokens_)/sizeof(*tokens_)) {
tokens_[tok_idx_++] = tok_start_;
}
}
void ctx_reset(char c)
{
ready_ = false;
if (c != '$') {
return;
}
context_ = &nmea::ctx_tokens;
buf_idx_ = 0;
tok_idx_ = 0;
tok_start_ = 0;
checksum_ = 0;
}
void ctx_skip(char c)
{
if (c == '\n') {
context_ = &nmea::ctx_reset;
}
}
void ctx_tokens(char c)
{
if (c == '*') {
context_ = &nmea::ctx_checksum;
append_tok();
append_buf('\0');
tok_start_ = buf_idx_;
return;
}
if (c == '\n') {
reset();
return;
}
update_checksum(c);
if (c == ',') {
append_tok();
append_buf('\0');
tok_start_ = buf_idx_;
return;
}
append_buf(c);
}
void ctx_checksum(char c)
{
if (c == '\r') {
if (buf_idx_ - tok_start_ != 2) {
skip();
}
return;
}
if (c == '\n') {
if (buf_idx_ - tok_start_ != 2) {
reset();
return;
}
append_buf('\0');
char *endptr;
uint8_t checksum = strtoul(buf_ + tok_start_, &endptr, 16);
if (*endptr == '\0' && checksum == checksum_) {
ready_ = true;
}
reset();
return;
}
append_buf(c);
}
public:
nmea() : context_ { &nmea::ctx_reset } {};
bool parse(char c)
{
(this->*context_)(c);
return ready_;
}
const char *token(uint8_t idx) const
{
return (idx < tok_idx_) ? (buf_ + tokens_[idx]) : "";
}
}
Детально на баги не тестив, на даних з оригінального поста наче працює.
Приклад використання:
nmea nmea;
void loop()
{
for (int c; (c = SoftwareSerial.read()) != -1;) {
if (!nmea.parse(c)) {
continue;
}
const char *type = nmea.token(0);
Serial.print(F("Parsed: "));
Serial.print(type);
if (strcmp(type, "GPGGA") == 0) {
Serial.print(F(" time: "));
Serial.print(nmea.token(1));
Serial.print(F("; location: "));
Serial.print(nmea.token(2));
Serial.print(nmea.token(3));
Serial.print(nmea.token(4));
Serial.print(nmea.token(5));
}
Serial.println();
}
}
Оффтоп: баг з бекслешами на цьому форумі колись пофіксять?
Неактивний
Кінцевий автомат воно концептуально. але ж букв дофіга і ніхто крім автора не розуміє як воно працює
Кінцевий автомат воно концептуально. але ж букв дофіга
і ніхто крім автора не розуміє як воно працює
Букв там дофіга бо написано для ардуіни на недо-C++. Для документації продакшн коду можна діаграму намалювати. Або взагалі взяти парсер-генератор (yacc чи bison) і не морочити голову.
Неактивний
Я програміст не настоящий як це взагалі працює? на вході опис даних на виході код С?
як це взагалі працює? на вході опис даних на виході код С?
Саме так. Але це має сенс для парсинга нерегулярних синтаксисів. Для регулярних, типу як в NEMA, достатньо КА.
Якби в arduino core була підтримка регулярних виразів, весь той КА можна було би описати однією строкою. Хіба що обчислення контрольної суми довелось би реалізовувати окремо.
Остання редакція dimich (2025-04-05 21:53:02)
Неактивний
Але суть запропонованої мною реалізації в тому, що дані частково парсяться по мірі надходження. UART працює повільно відносно процесора, тому виконання парсинга "розмазуються" в часі.
Якщо потрібно витягати тільки певні токени у певних типах повідомлень, то можна допиляти парсер, щоб він по закінченню просто випльовував готову структуру з float'ами, чи що там ще потрібно.
Неактивний