#3 Re: Апаратні питання » Стенд для тестів FPV » 2025-06-14 16:59:42

strannik пише:

USB : посилаємо команду - вімикаємо два ланцюга data in/out з невеликою затримкою вимикаємо + та -.

Якою програмою будете посилати команди ?

#4 Re: Програмування Arduino » Вопрос по цифровому реле с питанием 12В » 2025-05-17 09:08:49

kepler пише:
jokeer пише:

Ніт
Ваші вподобання досить незвичні wink я сумніваюся, чи існує таке готове рішення. Саморобки повинні існувати.

Готового нет, а программируемое решение?
Так, вподобання незвичні, но сейчас это нужно..

Колись робив улучшайзінг по типу Hunter XCH-1200, поливалку на 12 каналів...

https://driver.top/blog/422159/


https://youtu.be/VsHSq5yDYUc?si=lt-yJOMSBwluPJAA

#5 Re: Проекти » Список исполнителей » 2025-05-08 17:00:04

Yuri57 пише:

hmm Необхідна допомога програміста у програмі на Arduino.
"Залізо" зібране (14-канальний пристрій), працює на 100%. Програма працює, але є недоліки, які необхідно прибрати. тел. 099 707 72 17

очень интересно но ничего не понятно ...

#6 Re: Програмування Arduino » парсинг NMEA **проблема » 2025-04-15 14:27:28

dimich пише:
renoshnik пише:
String nmeaSentence = ""; 		// Переменная для хранения NMEA-строки
...
nmeaSentence += c; 		// Добавить символ к строке NMEA
...

Навіщо зберігати символи в String, якщо потім їх копіювати в char[] ?

ось трохи змінив ...

  String nmeaSentence = "";
  boolean str_read = true;
  
void setup() { Serial.begin(9600); }

void serialEvent() { nmeaSentence = Serial.readStringUntil('n');
if (nmeaSentence.length() > 0 && str_read) { GPS_run(nmeaSentence); }
	}
  
void loop() {

	}

void GPS_run(String nmea) {
str_read = false;	
	// ********************************************
str_read = true;	
	}

#7 Re: Програмування Arduino » парсинг NMEA **проблема » 2025-04-12 17:45:12

dimich пише:
renoshnik пише:
char fullSpeed[5];
sprintf(fullSpeed, "%03d", speedKmh);

//   чтобы избежать переполнения 

sprintf(fullSpeed, sizeof(fullSpeed), "%03d", speedKmh);

Така конструкція чомусь виводить на екран якісь сторонні символи замість данних ... hmm

snprintf. Я ж поправився вище.
Взагалі комілятор мав би видати попередження. Чи у вас вони вимкнені?

Вже старий, не додивився ...  roll

#8 Re: Програмування Arduino » парсинг NMEA **проблема » 2025-04-12 16:05:35

char fullSpeed[5];
sprintf(fullSpeed, "%03d", speedKmh);

//   чтобы избежать переполнения 

sprintf(fullSpeed, sizeof(fullSpeed), "%03d", speedKmh);

Така конструкція чомусь виводить на екран якісь сторонні символи замість данних ... hmm

#9 Re: Програмування Arduino » парсинг NMEA **проблема » 2025-04-10 12:02:23

dimich пише:

Працює, не димить, не іскрить - це вже добре cool
Але перевірку контрольної суми я б таки додав. Хто зна, що там з UART'у може прийти. Так хоч якась валідація.

dimich пише:

sprintf(fullSpeed, sizeof(fullSpeed), "%03d", speedKmh);

Звісно ж, тут snprintf. То я при цитуванні забув поправити.

Я вже зробив заготовку, але поки не перевіряв .... 

	
String nmeaSentence = "$GPRMC,123456.00,A,4916.45,N,12311.12,W,0.05,89.60,030615,,,A*1C";

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

  if (checkChecksum(nmeaSentence)) {
    Serial.println("Контрольная сумма совпадает.");
  } else {
    Serial.println("Ошибка контрольной суммы.");
  }
}

void loop() {
  // Основной код программы
}

bool checkChecksum(const String& nmea) {
  // Найти индекс символа '*'
  int asteriskIndex = nmea.indexOf('*');
  if (asteriskIndex == -1 || asteriskIndex < 1) {
    return false; // Нет контрольной суммы или неверный формат
  }

  // Извлечь контрольную сумму из строки (после '*')
  String checksumStr = nmea.substring(asteriskIndex + 1);
  int expectedChecksum = strtol(checksumStr.c_str(), nullptr, 16);

  // Вычислить контрольную сумму
  int calculatedChecksum = 0;
  for (int i = 1; i < asteriskIndex; i++) { // Пропускаем '$'
    calculatedChecksum ^= nmea[i];
  }

  return calculatedChecksum == expectedChecksum;
}	
	

#10 Re: Програмування Arduino » парсинг NMEA **проблема » 2025-04-10 11:30:56

dimich пише:
renoshnik пише:
String nmeaSentence = ""; 		// Переменная для хранения NMEA-строки
...
nmeaSentence += c; 		// Добавить символ к строке NMEA
...
char nmeaCStr[nmea.length() + 1];
nmea.toCharArray(nmeaCStr, nmea.length() + 1);
...

Навіщо зберігати символи в String, якщо потім їх копіювати в char[] ?

renoshnik пише:
if (fieldIndex == 1) {printFormattedTime(token);token = nullptr;}	// выводим время 1
if (fieldIndex == 2) {printFormattedValid(token);token = nullptr;}	// выводим валидность координат 2	
if (fieldIndex == 7) {printFormattedSpeed(token);token = nullptr;}// выводим скорость 7
if (fieldIndex == 9) {printFormattedDate(token);token = nullptr;}	// выводим дату 9

token = commaPos + 1;

Навіщо присвоювати token = nullptr, якщо відразу ж присвоюється token = commaPos + 1?
Раз на то пішло, то хоча б

switch (fieldIndex) {
    case 1: printFormattedTime(token); break;
    case 2: printFormattedValid(token); break;
    case 7: printFormattedSpeed(token); break;
    case 9: printFormattedDate(token); break;
    default: break;
}
token = commaPos + 1;
renoshnik пише:
float Kmh = atof(rawKnot) * 1.852;
int speedKmh = (int)Kmh;

Якщо вже так хочеться float, навіщо проміжна змінна і C-style кастинг? Такий же результат дасть

int speedKmh = atof(rawKnot) * 1.852;

А краще

int speedKmh = round(atof(rawKnot) * 1.8523);
renoshnik пише:
char fullSpeed[5];
sprintf(fullSpeed, "%03d", speedKmh);

У разі невалідних вхідних даних може бути переповнення буфера. Хоча б

sprintf(fullSpeed, sizeof(fullSpeed), "%03d", speedKmh);

Хоча у разі невалідних вхідних даних переповнення буфера буде ще на стадії отримання строки з UART.



1. - Згоден, що додає додатковий крок, але мені так було зручніше працювати з рядками на етапі побудови.
2. - про token = nullptr, то я шукав звідки лізуть деякі баги і забув потім видалити, дійсно то зайве.
3. - дякую виправив
4. - дякую виправив

#12 Re: Програмування Arduino » парсинг NMEA **проблема » 2025-04-09 22:16:59

Якось так ....

#include <Adafruit_GFX.h>    	// Core graphics library
#include <Adafruit_ST7735.h> 	// Hardware-specific library for ST7735
#include <SPI.h>
#define TFT_CS        10
#define TFT_RST        8 		// Or set to -1 and connect to Arduino RESET pin
#define TFT_DC         9
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
#include <SoftwareSerial.h>
#include <string.h>
SoftwareSerial gpsSerial(4, 3); // (RX, TX Arduino)

String nmeaSentence = ""; 		// Переменная для хранения NMEA-строки
const int timezoneOffset = 3;	// Часовой пояс (например, +3 для EEST)
boolean valid = false;			// флаг валидности координат
boolean drive = false;			// флаг движения
boolean overtime = false;		// флаг время/скорость

//	char tokenKnot[] = "10.5"; 		// Пример тестового значения


void setup() {
//								Serial.begin(9600);
gpsSerial.begin(9600);
tft.initR(INITR_BLACKTAB);   	// initialize a ST7735S chip, black tab
tft.setRotation(3);     		// Установка ориентации экрана
tft.fillScreen(ST77XX_BLACK);	// Установка начального цвета фона
}

void loop() {
  while (gpsSerial.available()) {
    char c = gpsSerial.read(); 	// Считать один символ из GPS
    if (c == 'n') { 			// Проверить, является ли символ концом NMEA-строки
      GPS_run(nmeaSentence); 	// Передать строку в GPS_run() для обработки
      nmeaSentence = ""; 		// Очистить строку для следующей NMEA-строки
    } else if (c != 'r') { 	// Игнорировать символ возврата каретки
      nmeaSentence += c; 		// Добавить символ к строке NMEA
    }
  }
}

void GPS_run(String nmea) {
  if (nmea.startsWith("$GPRMC")) { 					// Проверка на идентификатор строки
//								Serial.println("______________________________"); 
//								Serial.println("_______ вход в $GPRMC ***1*** ");
    char nmeaCStr[nmea.length() + 1];
    nmea.toCharArray(nmeaCStr, nmea.length() + 1);
    int fieldIndex = 0;
    char *token = nmeaCStr;
    char *commaPos = strchr(token, ',');			// Найти первую запятую
    while (fieldIndex <= 13) {
    if (commaPos) {
    *commaPos = ''; 								// Разделяем строку на части

if (fieldIndex == 1) {printFormattedTime(token);token = nullptr;}	// выводим время 1
if (fieldIndex == 2) {printFormattedValid(token);token = nullptr;}	// выводим валидность координат 2	
if (fieldIndex == 7) {printFormattedSpeed(token);token = nullptr;}// выводим скорость 7
if (fieldIndex == 9) {printFormattedDate(token);token = nullptr;}	// выводим дату 9

token = commaPos + 1;
commaPos = strchr(token, ',');} 
else {break;}
      fieldIndex++;}
	}	
	
  if (nmea.startsWith("$GPGGA")) { 					// Проверка на идентификатор строки
//								Serial.println("______________________________");  
//								Serial.println("_______ вход в $GPGGA ***2*** ");
	char nmeaCStr[nmea.length() + 1];
    nmea.toCharArray(nmeaCStr, nmea.length() + 1);
    int fieldIndex = 0;
    char *token = nmeaCStr;
    char *commaPos = strchr(token, ',');			// Найти первую запятую
    while (fieldIndex <= 14) {
    if (commaPos) {
    *commaPos = ''; 								// Разделяем строку на части
 
if (fieldIndex == 7) {printFormattedSatelit(token);token = nullptr;}// выводим спутники 7

token = commaPos + 1;
commaPos = strchr(token, ',');} 
else {break;}
      fieldIndex++;}
	}	
}


// Функция контроля достоверности полученных координат
void printFormattedValid(char* rawValid) {
//									Serial.print("Полученная rawValid       : ");
//	Serial.print(valid); Serial.print(" - "); Serial.println(rawValid);
if (strcmp(rawValid, "V") == 0) {valid = false;}
if (strcmp(rawValid, "A") == 0) {valid = true;}
}


// Функция для форматирования количества спутников
void printFormattedSatelit(char* rawSatel) {
//									Serial.print("Полученная rawSatel       : ");
//									Serial.println(rawSatel);
	static char previousSatel[5];
	if (strcmp(rawSatel, previousSatel) != 0) {
	tft.fillRect(125, 1, 29, 20, ST77XX_BLACK);}
tft.setTextColor(ST7735_WHITE);
tft.setTextSize(1);
tft.setCursor(12, 10);
tft.print("active satellites");
if (valid == false) {tft.setTextColor(ST77XX_RED);}
if (valid == true) {tft.setTextColor(ST77XX_GREEN);}
tft.setTextSize(2); 
tft.setCursor(127, 5);
tft.print(rawSatel);
	strncpy(previousSatel, rawSatel, sizeof(previousSatel) - 1);
    previousSatel[sizeof(previousSatel) - 1] = '';
}


// Функция для форматирования скорости (knot -> km/h)
void printFormattedSpeed(char* rawKnot) {
//    Serial.print("Полученная rawKnot : ");
//    Serial.println(rawKnot);
    tft.setTextColor(ST77XX_ORANGE);
    tft.setTextSize(5);
    tft.setCursor(35, 45);
    float Kmh = atof(rawKnot) * 1.852;
  int speedKmh = (int)Kmh;
  if (speedKmh == 0) {drive = false; return;} 
  else {drive = true;}  
//    Serial.print("Конвертируем узлы в км/ч speedKmh : ");
//    Serial.println(speedKmh);
    char fullSpeed[5];
    sprintf(fullSpeed, "%03d", speedKmh);
	tft.fillRect(5, 40, 155, 45, ST77XX_BLACK);
//  tft.drawRect(5, 40, 155, 45, ST77XX_WHITE);
    tft.print(fullSpeed); 
//    Serial.print("Форматируем число с ведущими нулями fullSpeed: ");
//    Serial.println(fullSpeed);
}


// Функция для форматирования времени с учётом часового пояса (UTC -> ЧЧ:ММ:СС)
void printFormattedTime(char* utcTime) {
//									Serial.print("Полученная utcTime       : ");
//									Serial.println(utcTime);
  char fullTime[9]; 								// Переменная для хранения строки времени в формате "HH:MM:SS"
  char hh[3], mm[3], ss[3];
  strncpy(hh, utcTime, 2); hh[2] = '';
  strncpy(mm, utcTime + 2, 2); mm[2] = '';
  strncpy(ss, utcTime + 4, 2); ss[2] = '';
  int hour = atoi(hh) + timezoneOffset;				// Коррекция часа в зависимости от timezoneOffset
  if (hour < 0) hour += 24; 						// Если час отрицательный, добавляем 24
  if (hour >= 24) hour -= 24; 						// Если час превышает 24, вычитаем 24
  sprintf(fullTime, "%02d:%s:%s", hour, mm, ss);	// Формируем строку времени
// время вверху
	if (drive == false) {
tft.fillRect(5, 40, 155, 45, ST77XX_BLACK);
tft.setTextColor(ST7735_YELLOW);
tft.setTextSize(3);
tft.setCursor(10, 50); tft.print(fullTime); } 
	if (drive == true) { 
tft.fillRect(15, 94, 130, 25, ST77XX_BLACK);
tft.setTextColor(ST7735_YELLOW);
tft.setTextSize(2);
tft.setCursor(30, 100); tft.print(fullTime); 
	overtime = true;}
}


// Функция для форматирования даты (ДД.ММ.ГГГГ)
void printFormattedDate(char* rawDate) {
//									Serial.print("Полученная drive d rawDate : ");
//									Serial.println(drive);
	if (drive == true) {return;}
//									Serial.print("Полученная rawDate       : ");
//									Serial.println(rawDate);
	static char previousDate[11];
	if (strcmp(rawDate, previousDate) != 0 || overtime == true) {
	tft.fillRect(15, 94, 130, 25, ST77XX_BLACK);}
	overtime = false;
char fullDate[11]; 									// Переменная для хранения полной строки даты (формат "DD.MM.YYYY")
char dd[3], mm[3], yy[3]; 							// Обратите внимание, что yy теперь размера 3, а не 5
strncpy(dd, rawDate, 2); dd[2] = '';
strncpy(mm, rawDate + 2, 2); mm[2] = '';
strncpy(yy, rawDate + 4, 2); yy[2] = '';
sprintf(fullDate, "%s.%s.20%s", dd, mm, yy);		// Формируем строку даты
tft.setTextColor(ST77XX_MAGENTA);
tft.setTextSize(2);
tft.setCursor(20, 100);
tft.print(fullDate);
	strncpy(previousDate, rawDate, sizeof(previousDate) - 1);
    previousDate[sizeof(previousDate) - 1] = '';
}

#13 Re: Апаратні питання » Реле для ардуіно 2+КВ » 2025-04-06 16:30:21

Треба робити схему реле + транзистор (паралельно) для такої потужності.

#14 Re: Програмування Arduino » парсинг NMEA **проблема » 2025-04-05 16:45:50

jokeR пише:

Це не наш шлях ...

arduino-way це використовувати по максимуму вже написаний код.
Ну, хіба що самому цікаво погратися в С wink

Якась ардуінофобія ... hmm

#15 Re: Програмування Arduino » парсинг NMEA **проблема » 2025-04-05 16:21:08

Дякую всім, вже вийшло те що хотів  big_smile  big_smile  big_smile

Тепер буду нарощувати код ...


dimich пише:
renoshnik пише:

отримую таке

Які поля з яких типів повідомлень вам потрібно витягти?

В мене GPS модуль і треба витягнути данні для роботи (час, дата, швидкість, координати і т.п.)

#16 Re: Програмування Arduino » парсинг NMEA **проблема » 2025-04-05 16:17:57

jokeR пише:

https://docs.arduino.cc/libraries/nmeaparser/
Все вже кимсь написано wink

Це не наш шлях ...

#17 Re: Програмування Arduino » парсинг NMEA **проблема » 2025-04-05 14:27:33

dimich пише:
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

#18 Re: Програмування Arduino » парсинг NMEA **проблема » 2025-04-05 13:00:29

Ось цим подивився що видає блок

#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

+++++++++++++++++++++++++++++++

#19 Програмування Arduino » парсинг NMEA **проблема » 2025-04-05 09:06:17

renoshnik
відповідей: 65

Намагаюся розпарсити рядок данних 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 обробляється не корректно ...  sad sad sad

#20 Re: Програмування Arduino » рисуем график данных на TFT-дисплее » 2025-04-02 19:18:24

jokeer пише:

В мене до графіка зауважень і не було wink тут таке.. якщо чужий код працює якось не так, то або копати даташіти, або спробувати іншу реалізацію.

то я ще не відвик від служби..  smile

маю на увазі, що графік з цією лібою малюється одним кольором без змін ...
стара ліба пішла в смітник ...

#21 Re: Програмування Arduino » рисуем график данных на TFT-дисплее » 2025-04-02 17:52:44

jokeR пише:

https://github.com/adafruit/Adafruit-ST7735-Library/ не хочете розглянути?

Дякую, графік тепер без зауважень ...  smile  smile  smile

#22 Re: Програмування Arduino » рисуем график данных на TFT-дисплее » 2025-04-02 13:24:05

В прикладах бібліотеки взяв скетч аналогового годинника

/*
  An example analogue clock using a TFT LCD screen to show the time
 use of some of the drawing commands with the ST7735 library.
 For a more accurate clock, it would be better to use the RTClib library.
 But this is just a demo. 
 
 This examples uses the hardware SPI only. Non-hardware SPI
 is just too slow (~8 times slower!)
 
 Gilchrist 6/2/2014 1.0
 Updated by Bodmer
 */

#include <TFT_ST7735.h> // Graphics and font library for ST7735 driver chip
#include <SPI.h>

TFT_ST7735 tft = TFT_ST7735();  // Invoke library, pins defined in User_Setup.h

#define ST7735_GREY 0xBDF7

float sx = 0, sy = 1, mx = 1, my = 0, hx = -1, hy = 0;    // Saved H, M, S x & y multipliers
float sdeg=0, mdeg=0, hdeg=0;
uint16_t osx=64, osy=64, omx=64, omy=64, ohx=64, ohy=64;  // Saved H, M, S x & y coords
uint16_t x0=0, x1=0, y0=0, y1=0;
uint32_t targetTime = 0;                    // for next 1 second timeout

static uint8_t conv2d(const char* p) {
  uint8_t v = 0;
  if ('0' <= *p && *p <= '9')
    v = *p - '0';
  return 10 * v + *++p - '0';
}

uint8_t hh=conv2d(__TIME__), mm=conv2d(__TIME__+3), ss=conv2d(__TIME__+6);  // Get H, M, S from compile time

boolean initial = 1;

void setup(void) {
  tft.init();
  tft.setRotation(0);
  tft.fillScreen(ST7735_GREY);
  tft.setTextColor(ST7735_GREEN, ST7735_GREY);  // Adding a black background colour erases previous text automatically
  
  // Draw clock face
  tft.fillCircle(64, 64, 61, ST7735_BLUE);
  tft.fillCircle(64, 64, 57, ST7735_BLACK);

  // Draw 12 lines
  for(int i = 0; i<360; i+= 30) {
    sx = cos((i-90)*0.0174532925);
    sy = sin((i-90)*0.0174532925);
    x0 = sx*57+64;
    y0 = sy*57+64;
    x1 = sx*50+64;
    y1 = sy*50+64;

    tft.drawLine(x0, y0, x1, y1, ST7735_BLUE);
  }

  // Draw 60 dots
  for(int i = 0; i<360; i+= 6) {
    sx = cos((i-90)*0.0174532925);
    sy = sin((i-90)*0.0174532925);
    x0 = sx*53+64;
    y0 = sy*53+64;
    
    tft.drawPixel(x0, y0, ST7735_BLUE);
    if(i==0 || i==180) tft.fillCircle(x0, y0, 1, ST7735_CYAN);
    if(i==0 || i==180) tft.fillCircle(x0+1, y0, 1, ST7735_CYAN);
    if(i==90 || i==270) tft.fillCircle(x0, y0, 1, ST7735_CYAN);
    if(i==90 || i==270) tft.fillCircle(x0+1, y0, 1, ST7735_CYAN);
  }

  tft.fillCircle(65, 65, 3, ST7735_RED);

  // Draw text at position 64,125 using fonts 4
  // Only font numbers 2,4,6,7 are valid. Font 6 only contains characters [space] 0 1 2 3 4 5 6 7 8 9 : . a p m
  // Font 7 is a 7 segment font and only contains characters [space] 0 1 2 3 4 5 6 7 8 9 : .
  tft.drawCentreString("Time flies",64,130,4);

  targetTime = millis() + 1000; 
}

void loop() {
  if (targetTime < millis()) {
    targetTime = millis()+1000;
    ss++;              // Advance second
    if (ss==60) {
      ss=0;
      mm++;            // Advance minute
      if(mm>59) {
        mm=0;
        hh++;          // Advance hour
        if (hh>23) {
          hh=0;
        }
      }
    }

    // Pre-compute hand degrees, x & y coords for a fast screen update
    sdeg = ss*6;                  // 0-59 -> 0-354
    mdeg = mm*6+sdeg*0.01666667;  // 0-59 -> 0-360 - includes seconds
    hdeg = hh*30+mdeg*0.0833333;  // 0-11 -> 0-360 - includes minutes and seconds
    hx = cos((hdeg-90)*0.0174532925);    
    hy = sin((hdeg-90)*0.0174532925);
    mx = cos((mdeg-90)*0.0174532925);    
    my = sin((mdeg-90)*0.0174532925);
    sx = cos((sdeg-90)*0.0174532925);    
    sy = sin((sdeg-90)*0.0174532925);

    if (ss==0 || initial) {
      initial = 0;
      // Erase hour and minute hand positions every minute
      tft.drawLine(ohx, ohy, 65, 65, ST7735_BLACK);
      ohx = hx*33+65;    
      ohy = hy*33+65;
      tft.drawLine(omx, omy, 65, 65, ST7735_BLACK);
      omx = mx*44+65;    
      omy = my*44+65;
    }

      // Redraw new hand positions, hour and minute hands not erased here to avoid flicker
      tft.drawLine(osx, osy, 65, 65, ST7735_BLACK);
      tft.drawLine(ohx, ohy, 65, 65, ST7735_WHITE);
      tft.drawLine(omx, omy, 65, 65, ST7735_WHITE);
      osx = sx*47+65;    
      osy = sy*47+65;
      tft.drawLine(osx, osy, 65, 65, ST7735_RED);

    tft.fillCircle(65, 65, 3, ST7735_RED);
  }
}

секундна стрілка рандомно залишає артифакти жовтого кольору ....

можливо дійсно то якість самого екрану така ...

#23 Re: Програмування Arduino » рисуем график данных на TFT-дисплее » 2025-04-02 13:11:49

Для тесту запустив таку програмку

#include <TFT_ST7735.h> // Библиотека для работы с экраном
#include <SPI.h>
// Параметры подключения (замените на ваши пины)
#define CS_PIN 10
#define DC_PIN 9
#define RST_PIN 8
TFT_ST7735 tft = TFT_ST7735(); // Инициализация экрана

void setup(void) {
  tft.init();             // Инициализация экрана
  tft.setRotation(3);     // Установка ориентации экрана
  tft.fillScreen(TFT_NAVY);     // Установка начального цвета фона
  drawAxes();             // Построение осей координат
}

void loop() {
 delay(50);
 tft.drawLine(75, 0, 75, 75, TFT_GREEN); // цвет изменяется 
 delay(50);
 tft.drawLine(55, 120, 55, 20, TFT_GREEN); // цвет изменяется 
 delay(50);
 tft.drawFastVLine(25, 0, 90, TFT_GREEN); // стабильный цвет
 delay(50);
 drawAxes();               // Построение осей координат
}
//  ФУНКЦИЯ ОТРИСОВКИ БАЗОВОГО ЭКРАНА
void drawAxes() {
  // Начало координат в нижнем левом углу
  int originX = 1;      // Начало координат по X (левый край экрана)
  int endX = 158;       // Окончание координат по X (правый край экрана)
  int originY = 125;    // Начало координат по Y (нижний край экрана)
  int endY = 0;       // Окончание координат по Y (верхний край экрана)  
  // Рисуем оси X и Y
  tft.drawFastHLine(0, originY, tft.width(), TFT_WHITE); // Горизонтальная линия вправо
  tft.drawFastVLine(originX, 0, tft.height(), TFT_WHITE); // Вертикальная линия вверх
  // Добавляем стрелки на концах осей
  tft.drawLine(endX, originY, endX - 3, originY - 3, TFT_WHITE);  // Стрелка вправо
  tft.drawLine(originX, endY, originX + 3, endY + 3, TFT_WHITE);    // Стрелка вверх
  // Подписи осей
  tft.setTextColor(TFT_CYAN);
  tft.setTextSize(1);
  tft.setCursor(139, 115);  // Подпись оси X
  unsigned long _x = tft.width();
  tft.print(_x);
unsigned long _y = tft.height();  
  tft.setCursor(5, 1);    // Подпись оси Y
  tft.print(_y);
}

tft.drawLine(75, 0, 75, 75, TFT_GREEN); // цвет изменяется
tft.drawLine(55, 120, 55, 20, TFT_GREEN); // цвет изменяется
tft.drawFastVLine(25, 0, 90, TFT_GREEN); // стабильный цвет

якщо за коментувати рядок    tft.setRotation(3);     // Установка ориентации экрана

результат не змінюється  hmm  hmm  hmm

#24 Re: Програмування Arduino » рисуем график данных на TFT-дисплее » 2025-04-02 09:58:25

dimich пише:
renoshnik пише:

Так FAST_LINE застосовується для прямих ліній, дійсно так правильніше було малювати осі координат...

Я маю на увазі, що в залежності від значення макроса FAST_LINE під час компіляції використовується або одна, або інша реалізація TFT_ST7735::drawLine().

renoshnik пише:

Що до розміру екрану, то кінцеві точки я визначив візуально підбором, бо параметрів екрану я не знав.

Там розмір екрана задається параметрами конструктора:

  TFT_ST7735(int16_t _W = ST7735_TFTWIDTH, int16_t _H = ST7735_TFTHEIGHT);

і за замовчуванням це 128x160.

Враховуючи всю ту "магію" з MADCTL та іншими параметрами, а також зоопарком варіантів модулів з клонами ST7735, при некоректній ініціалізації можуть невірно розраховуватись адреси пікселів, які попадають в середину пікселя, що виглядає як спотворення кольорів.
Я би для початку повиводив прості тестові зображення, щоб побачити закономірність.

З розміром екрану розібрався.

  tft.drawFastHLine(0, originY, tft.width(), TFT_WHITE); // Горизонтальная линия вправо
  tft.drawFastVLine(originX, 0, tft.height(), TFT_WHITE);	// Вертикальная линия вверх
//  tft.drawLine(endX, originY, endX - 3, originY - 3, TFT_WHITE);	// Стрелка по X
//  tft.drawLine(originX, endY, originX + 3, endY + 3, TFT_WHITE); 	// Стрелка по Y

FAST використовується тільки для горизонтальних і вертикальних ліній у графіку мені не вдалося використати FAST hmm

***  може проблеми з кольором через орієнтацію екрану hmm

  tft.setRotation(3);       // Установка ориентации экрана

#25 Re: Програмування Arduino » рисуем график данных на TFT-дисплее » 2025-04-01 23:29:54

dimich пише:
renoshnik пише:

В лібі поки нічого підозрілого не знайшов...

Ця ліба? Там два варіанта реалізації drawLine(), з FAST_LINE і без.

Якщо у вас 158x125, то

   int endX = 158;       			// Окончание координат по X (правый край экрана)
   int originY = 125;    			// Начало координат по Y (нижний край экрана)

мало би бути

   int endX = 157;
   int originY = 124;

хіба ні?


Так FAST_LINE застосовується для прямих ліній, дійсно так правильніше було малювати осі координат...

    tft.drawFastHLine(0, y, w, color1);
    tft.drawFastVLine(x, 0, h, color2);

Що до розміру екрану, то кінцеві точки я визначив візуально підбором, бо параметрів екрану я не знав.

Підвал форуму