Буфер символов для последовательного ввода: 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.доступно())
  
}

вот распечатка серийного монитора

Для меня этот задний квадрат — загадка.

Буду признателен за любую помощь!!

Борис

, 👍0

Обсуждение

Чтобы сохранить два символа, вам нужно три слота в массиве. Вы забыли о нулевом терминаторе, обозначающем конец строки. Таким образом, в зависимости от того, где массив находится в памяти, за ним может быть или не быть дополнительный мусор, который печатается, потому что строка не завершается., @Delta_G

вам нужно 3 положения. 'О'К' '\0', @Juraj

Относительно «while(s. available() > 0 && indx <=SIZE-1)»: где вы определили «SIZE»?, @Edgar Bonet


2 ответа


0

Как уже отмечалось, строки C заканчиваются нулем. Итак, чтобы хранить 2 символа содержимого, вам нужен 3-байтовый массив. Ваш код, считывающий символы в буфер, также нуждается в изменении. (Этот код не добавляет обязательный завершающий нуль.)

Возможно, вам следует переписать свой код, чтобы использовать функцию strncat().

,

1

Во-первых, несколько общих замечаний:

  • Разница между char str[2] и char* str = (char*) malloc(2) не имеет отношения к вашей проблеме. Это меняет место хранения массива, смысл sizeof str и malloc() забьют оперативную память, если вы вызовете их несколько раз и забудьте free().
  • В вашем коде sizeof(txt) — это константа 2: фактический размер в байт массива txt, независимо от того, сколько релевантных данных он содержит. держит.
  • Вы хотите, чтобы txt и indx запоминали свое содержимое при вызовах loop(). Вы должны либо сделать их глобальными, либо квалифицировать их как статический.
  • По возможности избегайте использования класса String. Здесь, так как вы используете его только для сравнения двух строк вы можете использовать традиционный Вместо этого используйте функцию strcmp().

Теперь основная часть моего ответа посвящена расширению того, что уже было сказано о правильном завершении строк.

Строка представляет собой список символов переменной длины. Всякий раз, когда вы имея дело со списком переменной длины, вам нужно как-то сохранить трек такой длины. Есть два распространенных способа сделать это:

  1. либо вы сохраняете длину в переменной,
  2. или вы указываете значение дозорного (значение, которое будет недействительным в пределах список) после последнего элемента, чтобы обозначить конец списка.

Оба метода допустимы, и ниже я проиллюстрирую их оба. Однако в случае строк язык 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-окончание. Если ваши строки не являются, вы должны быть особенно осторожны, чтобы не используйте ни одну из этих строковых функций.

,