F() мешает программе
Я работаю над библиотекой обработчиков команд, и у меня возникла большая проблема с функцией F()
.
Пример скетча для библиотеки использует довольно много оперативной памяти, потому что ему приходится печатать много текста по UART. Как и прежде, я использовал F()
, чтобы уменьшить использование оперативной памяти. Сначала сработало!
Lib работает с помощью функции strtok
. Команда fomar:
cmd1,..,paramN;..;cmdN,..,paramN
Команды разделены ;
, а параметры команды (params) разделены ,
Библиотека работает без проблем, возвращает адрес памяти (char*
) любой команды и каждого параметра.
Когда я пытаюсь сравнить параметр/команду с strcmp
, это работает, только если все команды в верхнем регистре.
Возьмем, к примеру, команду LED
:
Он работает только с Led
, LED
или любым случаем, где буква d находится в верхнем регистре. led
не будет работать, и будет сказано, что команда le
не существует. Да, le
, он не будет печатать led
. Но после команды DURATION
он как-то заработал.
Память для последовательного буфера динамически выделяется в конструкторе объекта в глобальной области видимости.
Почему? Все эти проблемы связаны с тем, что F()
. Может кто-нибудь объяснить, почему? Вот код и скриншот последовательного монитора.
void execCmd(const char *set)
{
char *cmdParam = CMDHandler.find(set, CMD_PARAM);
if (!strcmp(cmdParam, "LED"))
{
// ОДИН СПОСОБ ПРОВЕРИТЬ, ИМЕЕТ ЛИ КОМАНДА ПАРАМЕТРЫ - ПЕРЕД ПОЛУЧЕНИЕМ ПАРАМЕТРОВ == ЛУЧШИЙ СПОСОБ
if (!CMDHandler.count(CMDHandler.getNext(CMD_PARAM), CMD_PARAM))
{
Serial.print(F("->> Command LED: Current LED status is "));
Serial.println(digitalRead(LED_PIN));
return;
}
cmdParam = CMDHandler.find(nullptr, CMD_PARAM);
LEDStatus = 0;
uint8_t status = atoi(cmdParam);
digitalWrite(LED_PIN, status);
Serial.print(F("->> Command led: LED Status is "));
Serial.println(status, DEC);
}
else if (!strcmp(cmdParam, "LOOP"))
{
cmdParam = CMDHandler.find(nullptr, CMD_PARAM);
// ВТОРОЙ СПОСОБ ПРОВЕРИТЬ, ИМЕЕТ ЛИ КОМАНДА ПАРАМЕТРЫ - ПОСЛЕ ПОЛУЧЕНИЯ КАЖДОГО ПАРАМЕТРА
if (cmdParam == nullptr)
{
Serial.println(F("->> Command LOOP: Expected one parameter!"));
return;
}
uint8_t status = atoi(cmdParam);
if (status)
{
LEDStatus = 2;
digitalWrite(LED_PIN, HIGH);
Serial.println(F("->> Command LOOP: LED Blink Loop is on!"));
delay(blinkDuration);
}
else
{
LEDStatus = 0;
digitalWrite(LED_PIN, LOW);
Serial.println(F("->> Command LOOP: LED Blink Loop is off!"));
}
}
else if (!strcmp(cmdParam, "DURATION"))
{
if (!CMDHandler.count(CMDHandler.getNext(CMD_PARAM), CMD_PARAM))
{
Serial.print(F("->> Command DURATION: Current blink duration is "));
Serial.print(blinkDuration, DEC);
Serial.println("ms");
return;
}
cmdParam = CMDHandler.find(nullptr, CMD_PARAM);
blinkDuration = atoi(cmdParam);
Serial.print(F("->> Command DURATION: New blink duration is "));
Serial.print(blinkDuration, DEC);
Serial.println("ms!");
}
else if (!strcmp(cmdParam, "HELP"))
{
Serial.println(F("------ HELP ------\n-> LED [0/1] - Turns off/on LED on pin 13\n-> LOOP [0/1] - Stops/starts blink loop with LED on pin 13\n-> DURATION [X] - Changes duration of LED loop blink. Recommended values is 50-500ms\n"));
}
else
{
Serial.print(F("->> Command "));
Serial.print(cmdParam);
Serial.println(F(" does not exist!"));
}
char *CMDHand::find(const char *input, uint8_t const type)
{
char *found = nullptr;
char separator = delimiter[type];
if (input != nullptr) // ПЕРВЫЙ ВЫЗОВ
{
constrain[type] = input + strlen(input) - 1;
next[type] = input;
}
else if (next[type] > constrain[type]) return (nullptr);
found = strtok(next[type], &separator); // ПО НЕКОТОРЫМ ПРИЧИНАМ ЭТО НЕ РАБОТАЕТ, ЕСЛИ Я ПОСТАВЛЯЮ &delimiter[type] ВМЕСТО &separator
last[type] = next[type];
next[type] += strlen(found) + 1;
return (found);
}
@Pararera, 👍1
1 ответ
Лучший ответ:
Для сравнения со строками, заключенными в макрос F()
, необходимо использовать специальные формы строковых функций, которые имеют суффикс _P
. Вам также необходимо привести строковые литералы к правильному типу (PGM_P
).
Для сравнения без учета регистра следует использовать strcasecmp
вместо strcmp
.
Чтобы собрать их вместе, вы получите следующее:
if (strcasecmp_P(cmdParam, (PGM_P)F("help")) == 0) {
...
}
Здесь возникла проблема:
found = strtok(next[type], &separator)
strtok
принимает строку C как список разделителей (должен заканчиваться NULL). Вы передаете ему адрес одного символа. В следующем байте в памяти может быть или не быть (вероятно, нет) NULL, но все остальное до первого найденного в памяти NULL будет восприниматься как символы-разделители.
- Как разделить входящую строку?
- Как вывести несколько переменных в строке?
- В чем разница между Serial.write и Serial.print? И когда они используются?
- Загрузка Arduino Nano дает ошибку: avrdude: stk500_recv(): programmer is not responding
- Программы построения последовательных данных
- Как узнать частоту дискретизации?
- Что такое Serial.begin(9600)?
- Очистить существующий массив при получении новой последовательной команды
Все эти конкретные функции «_P» где-то задокументированы? Я знаю несколько, но не всем из них нравится strcasecmp_P., @Michel Keijzers
@MichelKeijzers https://nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html, @Majenko
Нет, я использовал
F()
дляSerial.print()
иSerial.println()
, а не для ˙strcmp(). Почему тогда я должен использовать его в
strcmp()`?, @ParareraПо тем же причинам, что и в Serial.print. Для экономии оперативной памяти., @Majenko
Большое спасибо (я уже проголосовал за ваш ответ)., @Michel Keijzers
@Majenko извините, но нет. Это не работает так, как должно.
led
не работает, потому что каким-то образомled
становитсяle
(я знаю, потому что там написано, чтоcommand le не существует
).светодиод
работает. Я не знаю, почему букваd
(строчная d) вызывает все эти проблемы. Независимо от того, какую команду я набираю, если в ней естьd
, это вызовет проблемы. БезF()
вSerial.print/ln
все работает нормально. Почему?, @Pararera@SilvioCro Тогда в методе find загадочного класса CMDHandler происходит что-то странное. Если ваша строка не анализируется правильно, вы должны смотреть на синтаксический анализатор, а не на структуру сравнения., @Majenko
@Majenko добавил метод «найти». Код работает нормально, когда я не использую
F()
, даже если я пытался использовать его как программу на C++. Похоже, чтоF()
переопределяет часть буфера для Serial, поскольку, если я печатаю текст, используя методnext
изfind
, я могу увидеть часть текста, которую я не писал., @ParareraУ вас есть критическая ошибка в вашем методе поиска. См. мое редактирование выше., @Majenko
Большое спасибо, теперь это работает., @Pararera