Откуда берутся эти ненужные символы (из serial)?

В настоящее время у меня есть 2 Arduino, которые подключены к HC-12 и успешно установили с ними связь. Но сейчас я пытаюсь обработать отправленные данные и сохранить их в качестве переменной, чтобы проводить сравнения. Но я получаю ненужные данные, которые приходят вместе с кодом, что мешает мне проводить эти сравнения.

Например, из одного arduino, когда я отправил "стоп", я получил

stop<⸮=l⸮⸮⸮

Я не могу понять, из-за чего этот мусор, кроме того, я ограничил полученные символы размером 4, почему я получаю более 4 символов?

Код от отправителя

#include <SoftwareSerial.h>

SoftwareSerial HC12(3, 2); // HC-12 TX Pin, HC-12 RX Pin

void setup() {
  Serial.begin(9600);             // Serial port to computer
  HC12.begin(9600);               // Serial port to HC12
  Serial.print("Checking HC-12 module: ");
  HC12.write("AT");               //Check for response(this is sending data to the HC, the SET pin must be low for this to work)
  delay(1000);
  HC12.write("AT+RX");            //Gets configs of HC-12
}

void loop() {
  while (HC12.available()) {        // If HC-12 has data
    Serial.write(HC12.read());      // Send the data to Serial monitor
  }
  while (Serial.available()) {      // If Serial monitor has data
    HC12.write(Serial.read());      // Send that data to HC-12
  }
}

Упрощенный код получения

    #define BYTELENGTH 4 //Ограниченную 4 символами
       #define ENDMARKER '\n'  //тобы знать, когда остановить, остановить чтение последовательного
    
       SoftwareSerial HC12(3, 2); // HC-12 TX Pin, HC-12 RX Pin

  Serial.begin(9600); //Быстрее это может быть на нано
  HC12.begin(9600); //  Последовательный порт для HC12s
      
      char receivedChars[BYTELENGTH - 1];
      int byteIndex;
      char curChar;
      
      while (HC12.available()) {        //Если у HC-12 есть данные
        curChar = HC12.read();
        if(curChar != ENDMARKER){
          receivedChars[byteIndex] = curChar;
          byteIndex++;
        }
      }
      if(strcmp(receivedChars,"") != 0){
        Serial.println(receivedChars);
        receivedChars[0] = 0;
      }

, 👍0

Обсуждение

Кстати: 9600 бод-это не самое быстрое, что может получить Nano. Интерфейс HardwareSerial "Последовательный" также может работать со скоростью передачи данных до 2 Мегабод. Проблема с этими бодратами заключается только в том, что вы не можете предоставлять данные с такой скоростью. Но 9600 бод-это довольно медленно, даже для Nano. Настоящим узким местом здесь является библиотека программных материалов. Я думаю, что он может без проблем обрабатывать до 38400 бод. 11520 бод также может работать, хотя я думаю, что это очень сложно на пределе., @chrisl

Software serial является временным, только для тестирования. У меня действительно было все на скорости 115200 бод, но возникли проблемы. Это бы все объяснило!, @dka13


3 ответа


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

1

Я бы сказал, что вы печатаете данные за пределами своего массива символов, потому что вы не завершили в нем строку C, а размер вашего массива слишком мал.

У тебя есть

#define BYTELENGTH 4 //Ограничено 4 символами
char receivedChars[BYTELENGTH - 1];

таким образом, ваш массив символов состоит всего из 3 элементов. Когда вы хотите сохранить строку в этом массиве, вам всегда нужно завершить ее нулевым символом \0 (который также равен нулю в десятичном представлении). Таким образом, в этом массиве символов у вас есть место только для 2 символов. Но ваше сообщение "стоп" состоит из 4 символов, поэтому вам нужен массив символов, содержащий не менее 5 элементов. Если вы не находитесь в крайне стесненной ситуации с памятью, я бы предложил использовать буфер с небольшим запасом над вашим самым большим сообщением (чтобы быть уверенным, что ничего не сломается, если позже вы будете использовать сообщения немного длиннее). Так что больше похоже на это:

#define BYTELENGTH 10 //Ограничено 4 символами
char receivedChars[BYTELENGTH + 1]; // длина сообщения + окончание нулевого символа

А затем вам нужно изменить код получателя, чтобы добавить нулевой символ в конце сообщения. Также вы можете фактически использовать символ новой строки \n в качестве разделителя сообщений (чего вы в настоящее время не делаете):

bool full_message_received = false;

void loop(){
    while (HC12.available()) {        // Если у HC-12 есть данные
        curChar = HC12.read();
        if(curChar != ENDMARKER){
          receivedChars[byteIndex] = curChar;
          byteIndex++;
        } else {
          receivedChars[byteIndex] = '\0'; // завершение строки
          full_message_received = true; // пометка сообщения как полностью полученного
        }
    }
    if(full_message_received){
        Serial.println(receivedChars);
        receivedChars[0] = 0;
        full_message_received = false;
    }
}

Что произойдет, если я не завершу строку нулевым символом? Все функции, которые обрабатывают C-строки (например, strcmp() или также Serial.print ()), используют нулевой символ для определения, в какой момент строка находится в конце. Они не знают размер вашего массива, поэтому они просто читают байт за байтом, пока не достигнут нулевого символа. Они даже считывают больше данных, чем содержится в вашем массиве. Ненужные символы, скорее всего, являются именно тем, что в данный момент находится в памяти непосредственно после вашего массива. Serial.print() не знает, что они не принадлежат строке C, так как вы не использовали нулевой символ, чтобы сообщить ему об этом. Он просто продолжает печатать символы, пока не достигнет байта, то есть нуля.

Вы даже записали данные в память за пределами массива. В зависимости от того, что находится непосредственно после вашего массива в памяти, это может быть без каких-либо проблем, но вы также можете перезаписать некоторые другие данные, не зная, что это за данные. Это может привести к очень странным и трудным для отладки проблемам. Убедитесь, что вы никогда не пишете за пределами своего массива. Ты можешь ломать вещи странными способами.

,

Поэтому, когда достигается КОНЕЧНЫЙ МАРКЕР (в данном случае \n), вы говорите ему завершить остальную часть массива. Затем, когда я распечатываю его или что-то с ним делаю, я не должен обращаться к дополнительным пространствам памяти. Но тогда я даже не вижу \n, потому что длина массива слишком мала. Поэтому, чтобы исправить это, мне нужно убедиться, что мой массив может вместить не менее 4 символов + символ \n (который должен быть отправлен другим последовательным монитором ардуино, верно?), Затем мой код ищет его и завершает массив после него. Правильно ли я понимаю?, @dka13

почти правильно. Вам нужно, чтобы ваш массив соответствовал 4 символам + завершающий нулевой символ. В моем коде выше я не включаю \n в массив. Обычно вам не нужен этот символ, так как он используется только для разделения отдельных сообщений в последовательном интерфейсе. И \n также считывается и печатается, если ваш массив слишком мал. Но затем эти данные перезаписывают пространство памяти после массива, что может быть очень плохо., @chrisl

Поэтому для предотвращения записи и чтения за пределами массива вам нужно сделать массив достаточно большим для каждого возможного сообщения, которое вы можете получить. Вы читаете до тех пор, пока \n не прочитает только одно полное сообщение за раз (каждое отдельное сообщение разделено \n на последовательном интерфейсе). А для использования полученных данных в виде c-строки (например, для печати) вам необходимо завершить их нулевым символом., @chrisl


0

Похоже, у вас проблема со скоростью передачи данных. Я удивлен, что ты хоть что-то получаешь. Вам нужно программное обеспечение для запуска последовательного порта, такое как SoftwareSerial mySerial(rxPin, txPin), затем в программе установки mySerial.begin(9600); Вам нужно, чтобы в каждом приложении скорость передачи данных была одинаковой.

Я использовал устройства SC16IS750 I2C/SPI-to-UART в качестве внешнего UART, они отлично работают и имеют множество приятных функций, таких как 64 байта передачи и приема FIFOs. Некоторые версии этого приложения устарели, но они все еще доступны и работают нормально. Вы также получаете 8 дополнительных операций ввода-вывода.

,

У меня он есть, просто исключил его, чтобы не публиковать весь свой код. Только что внес правку. Я предполагаю, что есть что-то о последовательном или массиве символов, которого мне не хватает, что добавляет дополнительный мусор, @dka13

Программное обеспечение работает в полудуплексном режиме. Это вызвано прерыванием, и если возникают другие прерывания, которые могут вызвать проблемы. Вот ссылка/обсуждение, которое может вам очень помочь. https://electronics.stackexchange.com/questions/23265/arduino-software-serial-full-duplex. Чем больше информации дается в целом, тем лучше. Если подключено дополнительное оборудование, опубликуйте схему со ссылками, показывающими техническую информацию об устройствах., @Gil

Это на самом деле проблема, которую я заметил, я надеюсь полностью избавиться от серийного программного обеспечения. На данный момент это просто упростило программирование, но, похоже, это вызывает некоторые проблемы, поэтому мне, возможно, потребуется ускорить переход., @dka13


-1

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

Все, что я могу сказать, это то, что в моем случае удаление программного обеспечения для использования аппаратного последовательного порта defaut решило возникшие у меня проблемы.

В качестве альтернативы вы можете использовать I2C ⇒ UART IC, например MAX3100, для обеспечения другого аппаратного UART (с MAX3100 вам нужно будет добавить кристалл 3,6468 или 1,8234 МГц и конденсаторы 2×22pf) - Я тоже еще не пробовал (хотя чип и кристалл заказаны. Или вы также сможете выполнить преобразование i2c ⇒ UART с помощью еще одного Arduino.

,

Программное обеспечение работает нормально на скорости 9600 бод. принятый ответ объясняет истинную проблему., @Juraj

Это неправда, @Juraj. Это во многом зависит от скорости передачи. Даже при скорости 9600 бит / с я получил потери персонажа, когда не добавил задержек, чтобы замедлить его. Это не связано с аппаратным портом., @Camion