Ви не увійшли.
Працює, не димить, не іскрить - це вже добре
Але перевірку контрольної суми я б таки додав. Хто зна, що там з UART'у може прийти. Так хоч якась валідація.
sprintf(fullSpeed, sizeof(fullSpeed), "%03d", speedKmh);
Звісно ж, тут snprintf. То я при цитуванні забув поправити.
Неактивний
Працює, не димить, не іскрить - це вже добре
![]()
Але перевірку контрольної суми я б таки додав. Хто зна, що там з 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;
}
Неактивний
Дійсно, динамічно виділяти пам'ять під масив можна Вік живи.. Але я б такого категорично не рекомендував.В мікроконтролері, де пам'яті небагато, треба по максимуму виділяти пам'ять статично -: якщо її не вистачить - програма тупо не скомпілюється. А якщо не вистачить пам'яті при роботі.. Дрон впаде, регулятор обертів спалить двигун, самогонний апарат перегріється.. Воно вам треба?
Дійсно, динамічно виділяти пам'ять під масив можна
Це не динамічне виділення в сенсі з купи, як malloc() або оператор new. Тут памʼять виділяється на стеку, як для звичайної локальної змінної. Компілятору нема різниці, чи віднімати від вказівника вершини стека константу, чи значення якого-небудь регістра.
Те ж саме можна зробити "класичним" методом:
char *array = alloca(strlen(str) + 1);
А якщо не вистачить пам'яті при роботі..
Проблема не стільки в тому, що памʼяті може не вистачити, а в тому, що нема можливості це перевірити навіть в рантаймі. При динамічному виділенні є можливіть перевірити успішність результата (і грамотно написаний код має це перевіряти). Хоча в ардуіно фреймворку це теж не дає 100% гарантії, бо хто знає, скільки байтів ще напхає в стек який-небуть обробник переривання умовної бібліотеки Wire. Може і для ардуіни є якісь реалізації runtime stack guard, але мені не зустрічались.
Неактивний
Для перевірки, можливо, підійде sizeof. Але це стрьомне. Ну, і якщо пам'яті таки не вистачило, то навіть незрозуміло що гірше, просто ребутнутися, чи щось там обробляти.
Для перевірки, можливо, підійде sizeof.
Ем, оператор sizeof повертає розмір обʼєкта під час компіляції. Яким він тут боком?
Ну, і якщо пам'яті таки не вистачило, то навіть незрозуміло що гірше, просто ребутнутися, чи щось там обробляти.
Так це одна з умов постановки задачі - як обробляти ту чи іншу ситуацію. Можна показати повідомлення, можна ребутнутись, можна "зависнути", але поведінка має бути визначена. Гірше (точніше, неприпустимо), коли поведінка не визначена. Наприклад, виконається довільний код, який перемкне піни в недопустимий стан або перепише важливі дані в EEPROM.
Неактивний
Для динамічно створеного масиву sizeof теж щось повертає. і це не відомо під час компіляції.
Ну і автоматика, яка в деяких умовах сама ребутиться, виглядає стрьомно. Це трохи краще ніж перезапис eeprom
Для динамічно створеного масиву sizeof теж щось повертає. і це не відомо під час компіляції.
Він повертає те ж значення, що вказане в квадратних дужках при обʼявлені масиву, помножене на розмір елемента. Це ніяк не відображає, чи не виліз стек за дозволені межі, і чи не вилізе при подальшому виклику функцій або обробників переривань.
Ну і автоматика, яка в деяких умовах сама ребутиться, виглядає стрьомно.
Від постановки задачі залежить. Іноді ребут - єдина правильна дія. Іноді має сенс додатково зберігати діагностичну інформацію, яка саме умова спричинила фолт.
Остання редакція dimich (2025-04-11 13:26:43)
Неактивний
Ок. Ну тоді це досить стрьомна конструкція
char fullSpeed[5];
sprintf(fullSpeed, "%03d", speedKmh);
// чтобы избежать переполнения
sprintf(fullSpeed, sizeof(fullSpeed), "%03d", speedKmh);
Така конструкція чомусь виводить на екран якісь сторонні символи замість данних ...
Неактивний
ця конструкція щось виводить у буфер. А що там у вас виводиться на екран - звідси не видно
char fullSpeed[5]; sprintf(fullSpeed, "%03d", speedKmh); // чтобы избежать переполнения sprintf(fullSpeed, sizeof(fullSpeed), "%03d", speedKmh);
Така конструкція чомусь виводить на екран якісь сторонні символи замість данних ...
snprintf. Я ж поправився вище.
Взагалі комілятор мав би видати попередження. Чи у вас вони вимкнені?
Неактивний
Arduino IDE, як проект для навчання, мав би компілювати з -Wall -Wextra -Werror -pedantic-errors. А у них навіть "Show verbose output during: compile" за замовчуванням вимкнене. Бояться, що багатобуков злякає школярів?
Неактивний
renoshnik пише:char fullSpeed[5]; sprintf(fullSpeed, "%03d", speedKmh); // чтобы избежать переполнения sprintf(fullSpeed, sizeof(fullSpeed), "%03d", speedKmh);
Така конструкція чомусь виводить на екран якісь сторонні символи замість данних ...
snprintf. Я ж поправився вище.
Взагалі комілятор мав би видати попередження. Чи у вас вони вимкнені?
Вже старий, не додивився ...
Неактивний
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;
}
Остання редакція renoshnik (2025-04-15 14:28:07)
Неактивний
ось трохи змінив ...
Не дуже розумію, навіщо взагалі використовувати String. Це обгортка над динамічним масивом char. Вона зручна, коли потрібно обʼєднувати рядки та маніпулювати літералами що зберігаються на флеші. У вас рядок фіксованої максимальної довжини, читайте одразу в статичний масив.
Вище я наводив приклад реалізації токенайзера, який видає char* на токен по індексу, або на пустий рядок, якщо токена з таким індексом нема.
Неактивний