Буфер символов для последовательного ввода: char str[2] vs char* str = (char*) malloc(2)
Я использую серийные номера программного обеспечения на моем Arduino nano для считывания данных с моего esp01 при функциональном тестировании прогрмаммного последовательного порта. Я просто делаю так, чтобы esp01 отправлял строку «ОК». к последовательному порту rx программного обеспечения Arduino (в данном случае контакт 9), а затем Arduino распечатывает его на мониторе через свой аппаратный последовательный порт (USB).
Я обнаружил, что когда я выделяю место для строки в Arduino с помощью char str[2]
, сообщение кажется правильным. Однако с char* str = (char*)malloc(2)
появляется неизвестный символ tai
введите строку "ОК"
Ниже приведен код, который я использую
#include<SoftwareSerial.h>
SoftwareSerial s(9,10);
void setup() {
pinMode(2,OUTPUT);
Serial.begin(9600);
s.begin(9600); // ссылка на esp01
}
void loop() {
if(s.available() > 0){
char txt[2];
int indx = 0;
// чтение ввода
while(s.available() > 0 && indx <=SIZE-1){
char buff;
buff= s.read();
txt[indx] = buff;
Serial.println(indx);
indx++;
}
// подключаем светодиод для отладки
if((String)txt == "OK"){
digitalWrite(2,HIGH);
delay(500);
digitalWrite(2,LOW);
delay(500);
}
// сбросить индекс
if(indx ==2){
Serial.println(txt);
Serial.print("Size of string:");
Serial.println(sizeof(txt));
indx = 0;
}
} // если(s.доступно())
}
вот распечатка серийного монитора
Для меня этот задний квадрат — загадка.
Буду признателен за любую помощь!!
Борис
2 ответа
Как уже отмечалось, строки C заканчиваются нулем. Итак, чтобы хранить 2 символа содержимого, вам нужен 3-байтовый массив. Ваш код, считывающий символы в буфер, также нуждается в изменении. (Этот код не добавляет обязательный завершающий нуль.)
Возможно, вам следует переписать свой код, чтобы использовать функцию strncat()
.
Во-первых, несколько общих замечаний:
- Разница между
char str[2]
иchar* str = (char*) malloc(2)
не имеет отношения к вашей проблеме. Это меняет место хранения массива, смыслsizeof str
иmalloc()
забьют оперативную память, если вы вызовете их несколько раз и забудьтеfree()
. - В вашем коде
sizeof(txt)
— это константа2
: фактический размер в байт массиваtxt
, независимо от того, сколько релевантных данных он содержит. держит. - Вы хотите, чтобы
txt
иindx
запоминали свое содержимое при вызовахloop()
. Вы должны либо сделать их глобальными, либо квалифицировать их какстатический
. - По возможности избегайте использования класса
String
. Здесь, так как вы используете его только для сравнения двух строк вы можете использовать традиционный Вместо этого используйте функциюstrcmp()
.
Теперь основная часть моего ответа посвящена расширению того, что уже было сказано о правильном завершении строк.
Строка представляет собой список символов переменной длины. Всякий раз, когда вы имея дело со списком переменной длины, вам нужно как-то сохранить трек такой длины. Есть два распространенных способа сделать это:
- либо вы сохраняете длину в переменной,
- или вы указываете значение дозорного (значение, которое будет недействительным в пределах список) после последнего элемента, чтобы обозначить конец списка.
Оба метода допустимы, и ниже я проиллюстрирую их оба. Однако в случае строк язык C++ использует метод 2, с символом ASCII «NUL» (числовое значение 0) в качестве индикатора.
Способ 1:
void loop() {
static char txt[2];
static size_t indx = 0;
while (s.available()) {
// чтение ввода
char c = s.read();
txt[indx++] = c;
// отчет
Serial.print("Received: '");
Serial.print(c);
Serial.print("', string: \"");
Serial.write(txt, indx);
Serial.print("\", size: ");
Serial.println(indx);
if (indx == 2 && memcmp(txt, "OK", 2) == 0) {
Serial.println("Correct message");
}
// сброс индекса
if (indx >= sizeof txt) {
indx = 0;
}
} // если(s.доступно())
}
На что следует обратить внимание:
- Переменная
indx
содержит длину строки. Бывает и такое быть индексом следующей доступной ячейки в массивеtxt
, если таковой имеется. - Строка печатается с использованием
Метод
write(const char *buffer, size_t size)
, который принимает размер в качестве второго аргумента. В отличие отprint(const char *)
, это не зависит в строке, заканчивающейся NUL. - Чтобы убедиться, что мы получили "ОК", мы должны проверить
правильная длина (
indx == 2
) и правильное содержимое. Содержимое проверяется с помощью функцииmemcmp()
, которая может сравнивать произвольные байтовые массивы и, опять же, не зависит от NUL-терминации. - Важным инвариантом цикла здесь является
idx < размер текста
. Это правда в инициализации и восстанавливается разделом «reset index» в конце петли. Раздел «чтение ввода» зависит от этого инварианта, чтобы не переполнить буфер.
Метод 2:
void loop() {
static char txt[3];
static size_t indx = 0;
while (s.available()) {
// чтение ввода
char c = s.read();
txt[indx++] = c;
txt[indx] = '\0';
// отчет
Serial.print("Received: '");
Serial.print(c);
Serial.print("', string: \"");
Serial.print(txt);
Serial.print("\", size: ");
Serial.println(indx);
if (strcmp(txt, "OK") == 0) {
Serial.println("Correct message");
}
// сброс индекса
if (indx >= sizeof txt - 1) {
indx = 0;
txt[indx] = '\0';
}
} // если(s.доступно())
}
И снова несколько замечаний:
- На самом деле это гибрид методов 1 и 2, так как переменная
indx
по-прежнему используется для отслеживания того, куда вставить следующий входящий символ. Это просто оказывается более удобным и эффективным, чем помещая этот символ в свою собственную строку длины 1 и используяstrcat()
. - Массив стал на одну ячейку длиннее, чтобы освободить место для НУЛ.
- Теперь появился еще один инвариант цикла: правильное завершение строки.
(
txt[indx] == '\0'
). Этот инвариант должен применяться как на чтение и сброс. - Строка завершается символом NUL, и теперь ее можно распечатать с помощью
print()
. и по сравнению сstrcmp()
. Строковый литерал"OK"
гарантируется компилятор завершает NUL.
Этот второй метод, как правило, безопаснее, поскольку функции и методы в Arduino и C++, которые ожидают, что строки будут NUL-окончание. Если ваши строки не являются, вы должны быть особенно осторожны, чтобы не используйте ни одну из этих строковых функций.
- AT-команда не отвечает на последовательный монитор
- Как отправить команду AT на sim800l с помощью SoftwareSerial
- Как остановить SoftwareSerial от получения данных и повторно включить его в какой-то другой момент?
- Ардуино для чтения с преобразователя RS232 в последовательный модуль TTL
- Не нашел датчик отпечатков пальцев :( Arduino Mega 2560 Adafruit Fingerprint Sensor
- Чтение SMS с помощью Arduino Uno и SIM800L и печать на LCD (16x2 буквенно-цифровых) с использованием последовательного соединения
- Как работает последовательная связь на Arduino?
- Как связаться с ESP8266 ESP01, отправив данные через программный сериал на Arduino Uno?
Чтобы сохранить два символа, вам нужно три слота в массиве. Вы забыли о нулевом терминаторе, обозначающем конец строки. Таким образом, в зависимости от того, где массив находится в памяти, за ним может быть или не быть дополнительный мусор, который печатается, потому что строка не завершается., @Delta_G
вам нужно 3 положения. 'О'К' '\0', @Juraj
Относительно «
while(s. available() > 0 && indx <=SIZE-1)
»: где вы определили «SIZE»?, @Edgar Bonet