Преобразование массива символов в строку Flash

arduino-uno c++ sim808

Я хотел бы передать форматированную строку в метод, который принимает __FlashStringHelper. Вот сигнатура метода:

void myMethod(const __FlashStringHelper *str);

Я использовал snprintf для форматирования строки:

char cmd[30];
snprintf(cmd, 30, "There are %d seconds in a minute", 60);
myMethod(F(&cmd[0]));

Очевидно, что вышесказанное не работает, но как я могу отформатировать флэш-строку?

ПРАВКА: Мне нужно включить целочисленную переменную в мою флэш - строку. В приведенном выше примере "60" - это переменная.

uint8_t nSeconds = 60;
char cmd[30];
snprintf(cmd, 30, "There are %d seconds in a minute", nSeconds);
myMethod(F(&cmd[0]));

, 👍0

Обсуждение

Разве этот метод не имеет перегрузки, которая принимает " const char*`?, @Edgar Bonet


1 ответ


Лучший ответ:

3

Прямой ответ

F() берет неизменяемые строковые литералы, известные во время компиляции, и помещает их во flash ("PROGMEM") вместо оперативной памяти. функция snprintf() создает строки во время выполнения и помещает их в изменяемый массив. Эти две вещи принципиально несовместимы.

Ограниченная конкатенация во время компиляции

Очень ограниченная форма того, что вы пытаетесь сделать, вероятно, может быть сделана следующим образом:

#define MY_STRINGIZE(x) #x

myMethod(F("There are " MY_STRINGIZE(60) " seconds in a minute"));

Это расширяется до:

myMethod(F("There are " "60" " seconds in a minute"));

Соседние строковые литералы объединяются вместе, что делает следующее:

myMethod(F("There are 60 seconds in a minute"));

Все это говорит о том, что вы могли бы просто написать там прямо 60. И здесь нет никаких возможностей форматирования snprintf.

В Arduino нет версии snprintf для компиляции. И я не думаю, что функции языка C++ (или C) существуют для поддержки создания одного из них. Скорее всего, есть лучший способ достичь того, что вы действительно хотите сделать.

Работа над этим в целом

Часто нет необходимости связывать строки вообще, особенно если вы просто развернетесь и запишете их в потокили , в более общем случае, напечатаете подкласс , например , Serial, LCD, SDFile, SoftwareSerialи т. Д. Сцепление вещей для вас-это большая часть того, что они делают. Не обязательно делать это самому заранее.

Если у вас есть строковый литерал и константа (или переменная) и вы хотите сохранить как можно больше данных из оперативной памяти, использование двух отдельных .print()/.println() поможет вам сделать это:

printCapableThing.print(F("AT+CIPSTAT="));
printCapableThing.println(index);
  • F() помещает "AT+CIPSTAT=" во flash.
  • Если индекс является константой, он также будет ветром, не будучи резидентом в RAM.

Боковое примечание

Я понимаю, что вы спрашиваете о том, чтобы получить как можно больше во flash, но я также не могу не указать, что эта же основная техника применяется к этой ужасной идее по несколько иным причинам:

Serial.println(String(F("Key ")) + keyname + " : " + value);

Это создает ужасающую путаницу распределений и освобождений и ненужного копирования, когда вы просто позволяете потоку/объекту печати выполнять свою работу, вызывая print()/println() несколько раз. Результирующий код более надежен, меньше и там, где не связан ввод/вывод, быстрее, когда вы это делаете.

,

Я думаю, что ему действительно нужна не компилируемая версия "snprintf" (это было бы совершенно бесполезно), а "myMethod", который принимает " const char*`., @PMF

Я абсолютно вижу использование для форматирования строки во время компиляции. Хотя да, вероятно, дело дойдет до последнего., @timemage

Я имею в виду, что вы могли бы написать реализацию для snprintf(cmd, 30, F("Есть %d секунд в минуту"), 60); (не трудно сделать с временной копией). Но что-то вроде snprintf(F("..."), 30, F("Result is %d"), 60); не может работать, потому что результат snprintf, конечно, не может быть во flash., @PMF

Мог бы, но не нужно. Уже существует вариант snprintf_P, способный взять строку формата из PROGMEM. Вы, вероятно, использовали бы "PSTR" там вместо этого. Но я не вижу в этом смысла., @timemage

Спасибо всем за ответы! @timemage: "60 секунд в минуту" - это не мой точный случай использования, мой случай использования требует контекста, который я пытался опустить. В основном я пытаюсь понять, как можно отформатировать строку в памяти программы., @P. Avery

Я могу изменить ответ, если у вас есть добавить больше деталей к вашему вопросу. Но, если я воспринимаю вопрос довольно буквально, зная, что это пример, это то, что нет способа сделать это. Вы можете исходить *части* вашей в конечном счете скомпонованной строки *из* flash/progmem. Но *результат* формации должен будет оказаться в оперативной памяти. Часто нет необходимости конкатенировать строки вообще, особенно если вы просто развернетесь и запишете их в поток (например, Serial, LCD, SDFile и т. Д.)., @timemage

@timemage Как я могу использовать переменную внутри строки flash? Я добавил комментарии выше. Мне нужно подать AT-команду на GSM-щит. Команда требует индексной переменной. Я мог бы буквально написать множество методов для обработки этого, но ради понимания того, как это сделать, я предпочел бы написать один метод, который принимает в качестве входных данных целое число (индекс) и выдает следующую команду: AT+CIPSTAT={index}. mySoftwareSerial->println(F("AT+CIPSTAT={index}"));, @P. Avery

Я мог бы ответить на этот вопрос, но есть кое-что получше, о чем я упоминал выше. Вместо этого я исправлю ответ на то, что предложил бы там., @timemage

Отличный ответ, спасибо @timemage!, @P. Avery