Сбой Arduino во время последовательной печати без очевидной причины

Я написал код Arduino, который продолжал падать, и я не мог понять, почему. Затем я максимально сократил код, чтобы увидеть проблему. Сокращенный код в настоящее время не делает ничего экстраординарного, кроме вывода некоторых строк в последовательной строке.

Это сокращенный код:

void setup() 
{
  Serial.begin(9600);
  Serial.print("Begin\r\n");
  send_wifi_beacon();
}
void loop()
{  
  delay(1000);
}
void send_wifi_beacon()
{
  send_wlan_chip_command("AT+CIFSR");
  Serial.print("notreached"); // эта часть никогда не достигается
  send_data_over_network(); // нельзя удалить эту строку, иначе не произойдет сбой, что странно, так как это никогда не вызывается
}
void read_response_from_serial()
{  
  delay(2000);
  Serial.print("==-1\r\n"); // здесь происходит сбой
}
void send_wlan_chip_command(char* command)
{
  Serial.print("Sending command to wifi module: ");
  Serial.print(command);
  read_response_from_serial();
  Serial.print("Command response: ");
}
// эта функция никогда не вызывается, так как сбой происходит до вызова, но ее нельзя удалить, иначе сбоя не произойдет
// и тогда его нельзя будет переделать, краха тоже не будет.
void send_data_over_network()
{
  char number[3] = "12";
  // номер символа[3] = "";
  char length_of_payload[20] = "0123456789012345678"; // 20 цифр, если предположить, что 64-битное целое число является максимальным, на котором этот код когда-либо будет работать
// char length_of_payload[20] = "";
  char *command;

  command = malloc(26);
  command = "AT+CIPSSEND";
  strcat(command, number);
  strcat(command, ",");
  strcat(command, length_of_payload);
  send_wlan_chip_command(command);
  free(command);
}

Это соответствующий вывод:

Начать отправку команды на модуль Wi-Fi: AT+CIFSR==Начать
Отправка команды на модуль Wi-Fi: AT+CIFSR==Begin
Отправка команды на модуль Wi-Fi: AT+CIFSR==Begin
Отправка команды модулю Wi-Fi: AT+CIFSR==Begin

Это означает, что Arduino продолжает сбоить и перезагружаться. Это будет продолжаться вечно.

Странно то, что если я изменю следующие две строки на: (в функции "send_data_over_network", как в примере выше)

char number[3] = "1"; // изменено с => номер символа [3] = "";
char length_of_payload[20] = "123"; // изменено с => char length_of_payload[20] = "0123456789012345678";

Теперь вывод изменится на:

точный вывод

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

Варианты устранения неполадок, которые я уже пробовал:

  • Переустановив Arduino IDE, я пробовал несколько версий 1.8, 1.6, 1.4. 1,0
  • Различные платы Arduino (Arduino Uno и Arduino Mega)
  • Разные ноутбуки (поскольку я думал, что это может быть связано с проблемами питания моего ноутбука)

Возможно, это что-то очевидное, и я просто не вижу этого, но я уже потратил +16 часов на отладку, поэтому помощь будет очень признательна.

, 👍2


2 ответа


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

1

Этот код выглядит неправильно:

Команда
  command = malloc(26);
  command = "AT+CIPSSEND";
  strcat(command, number);
  strcat(command, ",");
  strcat(command, length_of_payload);
  send_wlan_chip_command(command);

Сначала вы выделяете 26 байт, затем создается новая строка, которую вы направляете к ней. Это происходит в памяти, которая не выделена (или перезаписывает другие данные).

Вместо

  command = "AT+CIPSSEND";

использовать

  strcpy(command, "AT+CIPSSEND");

Также убедитесь, что 26 байт достаточно, измените его на 128 байт, чтобы быть уверенным (проверьте нужный объем позже).

,

Если вы решите использовать malloc() и строки, обязательно инициализируйте память нулями, иначе strcat не будет работать должным образом., @jose can u c

Не "подозрительно". Это не правильно". Нельзя добавить к строковой константе... Кроме того, вы хотите использовать strcpy, а не strcat в своем исправлении., @Johnny Mopp

Ах, я не знал, что я не могу добавить к строковой константе. Но это имеет смысл, поскольку находится в другой области памяти. Одна вещь, которую я до сих пор не понимаю, это то, как именно это привело к сбою, поскольку функция на самом деле так и не была достигнута. Или было? Поэтому я бы предположил, что он рухнет после этого, а не до вызова функции. Кто-нибудь может это объяснить? Может ли это быть связано с оптимизацией компилятора? Я принял ваш ответ как решение, но голосование еще не засчитывается. Спасибо вам обоим., @KoKlA

@JohnnyMopp Спасибо за хорошие замечания, я улучшил ответ., @Michel Keijzers

@KoKIA, вы уверены, что функция не вызывалась? Самый простой способ - добавить оператор печати до и после неправильного кода ... и желательно добавить немного после каждой команды печати, чтобы убедиться, что информация была сброшена (не уверен, что задержка абсолютно необходима). Но операторы печати очень помогают (если отладка невозможна, как в Arduino)., @Michel Keijzers

@KoKlA Кроме того, даже если бы ваш код работал, у вас возникла бы утечка памяти, поскольку вы немедленно переназначили бы значение, возвращаемое из malloc. Эта память потеряна. Кроме того, из-за переназначения вы получите ошибку в free()., @Johnny Mopp

@MichelKeijzers круто. вы правы, я просто добавил задержку (x) между моими последовательными выходами, и теперь я мог видеть, что на самом деле происходит сбой в функции, и функция на самом деле вызывается. Это означает, что Arduino вылетает быстрее, чем серийный номер может печатать свои символы. Теперь все имеет смысл, я бы никогда не подумал об этом. Большое спасибо, @KoKlA

Рассмотрите возможность использования alloca() вместо malloc(). Он размещается в стеке, а не в куче, и автоматически освобождается при выходе из функции и отбрасывании кадра стека., @Majenko

@KoKIA Добро пожаловать ... добавление операторов печати занимает в основном время, но это может иметь значение между догадкой и знанием., @Michel Keijzers

@Majenko спасибо за совет alloca(), я обязательно им воспользуюсь., @KoKlA

@Majenko и @KoKlA: alloca() наиболее полезна, когда вы заранее не знаете требуемый размер. Если этот размер является константой времени компиляции, проще просто объявить char command[26]., @Edgar Bonet


1

У вас почти наверняка не хватает памяти, и у вас есть коллизия стека с кучей. Динамическое выделение и освобождение памяти не является хорошей идеей, если у вас меньше 2 КБ ОЗУ для работы. Если вы используете malloc(), используйте его только для выделения буферного пространства, а затем повторно используйте буферы. Поместите свой тестовый код полностью в настройку и посмотрите, что произойдет. Также проверьте объем ОЗУ, выделенный во время компиляции, он может быть больше, чем вы думаете.

char *buf1;
char *buf2;

void setup() {
   buf1 = (char *) malloc(10);
   buf2 = (char *) malloc(10);
   .
   .
   // тестовый код здесь
}
,