REST API на Industrial 101 (семейство Yun) зависает, а затем выходит из строя

Я попытался настроить очень простой REST API на Arduino Industrial 101 (который, насколько я понимаю, эквивалентен Yun). Arduino подключен к датчику температуры, значение которого возвращается через интерфейс REST. Поначалу все работает нормально, но с течением дня устройство начинает реагировать все дольше и дольше, пока оно полностью не выйдет из строя и не перестанет отвечать на запросы. (В этот момент он также больше не отвечает на ping и ssh.)

На форуме Arduino есть несколько тем, сообщающих о подобных проблемах (например, эта), но все они Кажется, им уже как минимум несколько лет, и за это время они были решены. (Решение, представленное здесь, например, уже включено в мою версию Linino.)

Если я перезагрузлю ATMega, проблема останется, но если я перезагрузлю ОС, она будет решена за день, поэтому у меня есть основания подозревать, что что-то на стороне Линино не работает должным образом. Буду очень признателен за любую помощь, хотя я понимаю, что это довольно старая плата.

Мой скетч представляет собой упрощенную версию примера моста:

#include <Bridge.h>
#include <BridgeServer.h>
#include <BridgeClient.h>

BridgeServer server;

void setup() {
  pinMode(13, OUTPUT);
  digitalWrite(13, LOW);
  Bridge.begin();
  digitalWrite(13, HIGH);

  server.listenOnLocalhost();
  server.begin();
}

void loop() {
  // Получаем клиентов, приходящих с сервера
  BridgeClient client = server.accept();

  // Есть новый клиент?
  if (client) {
    // Обрабатываем запрос
    process(client);

    // Закрываем соединение и освобождаем ресурсы.
    client.stop();
  }

  delay(50); // Опрос каждые 50 мс
}

void process(BridgeClient client) {
  // независимо от команды, возвращаемое значение для A1 выбрано 4096 раз

  int pin = 1;
  float val = 0;
  for (int i = 0; i < 4096; i++){
    val += float(analogRead(pin))/4096.0;
  }
  client.println(val*5.0/1024.0, 5); //умножаем на 5/1024, чтобы получить вольты

}

, 👍2


3 ответа


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

0

Я проверил исходный код библиотеки Bridge. Это очень просто.

Динамическое распределение памяти отсутствует. Он использует String в client.connect(), но ваш скетч не использует его. Вы можете распечатать свободную память в ATmega с помощью библиотеки MemoryFree. Но я на 99% уверен, что утечки памяти не будет.

В библиотеке Bridge нет «фоновой» обработки, как предлагается в других ответах. Все команды и данные отправляются в WRT на Atheros и ждут результата или подтверждения. WRT-сторона библиотеки Bridge написана на Python.

Тот факт, что перезапуск WRT помогает продолжить работу, подтверждает, что проблема находится на стороне Python Bridge.

Python на WRT очень далек от базы знаний Arduino, поэтому я сомневаюсь, что вы найдете здесь решение.

,

Это модуль Wi-Fi, поэтому есть фоновая обработка, поддерживающая TCP или просто любое соединение., @Zunzulla alagaty

@Zunzullaalagaty, обработка Wi-Fi выполняется в чипе Atheros, а не в ATmega, и ввод проверяется только при вызовах библиотеки, таких как available или read., @Juraj


0

Прежде всего попробуйте изменить delay(50);. Попробуйте вместо этого использовать millis(); и посмотрите, сохраняется ли проблема.

int borderMillis = 50;
int startMillis = millis();

void loop()
{
  int nowMillis = millis();
  if(nowMillis >= borderMillis)
  {
    //запрашиваем нового клиента и т.д.

    startMillis = millis();
  }
}

Если это не помогает, попробуйте добавить метод yield(); после delay(50); / в конце цикла. Иногда совету директоров просто нужно время, чтобы выполнить второстепенные обязанности, и это может пригодиться.

Мое самое последнее предположение — утечка памяти. Вы можете попытаться получить оставшуюся часть кучи и посмотреть, будет ли она постоянно падать и не будет ли освобождена. Существуют библиотеки, которые помогут вам получить объем оставшейся памяти.

,

Я попробую это в ближайшее время, но просто чтобы убедиться, что я понимаю ваше предложение millis(): условие, которое используется в if, не должно ли оно быть (nowMillis - startMillis) >= borderMillis?, @Julius

@Юлиус, для меня это всегда работало без скобок. Но если это не сработает для вас, продолжайте, @Zunzulla alagaty

Меня не особо беспокоили скобки, мне просто кажется, что если вы определите nowMillis как millis() на каждой итерации, оно почти всегда будет больше 50, и поэтому почти всегда будет if. оценить как «истину»., @Julius

@Julius, я полагаю, метод yield() не помог?, @Zunzulla alagaty

Я попробовал, но библиотека планировщика недоступна для моей платы., @Julius


0

analogRead() возвращает число от 0 до 1023, поэтому в код функции process() можно внести некоторые улучшения.

Я добавил переменную unsigned long для накопления 4096 аналоговых показаний. После сложения всех показаний вы можете разделить сумму на 4096. До этого момента мы не использовали числа с плавающей запятой, да и в этом нет необходимости. Целочисленные математические вычисления гораздо менее «затратны», чем математические вычисления с плавающей запятой.

Наконец, я преобразую показания в число с плавающей запятой, которое будет использоваться в вычислениях с плавающей запятой client.println(val*5.0/1024.0, 5);.

У меня нет Arduino Yun или библиотек, которые вы используете для тестирования, поэтому вот простой скетч с предложенными мной изменениями в функции process().

unsigned long previousMillis = 0;
const long refreshRate = 50;

void setup(){
  Serial.begin(9600);
  process();
}   

void process(){
  // независимо от команды, возвращаемое значение для A1 выбрано 4096 раз
  int pin = 1;
  float val = 0;
  unsigned long temp = 0;
  for (int i = 0; i < 4096; i++){
    //val += float(analogRead(pin))/4096.0;
    temp += analogRead(pin);
  }
  temp = temp / 4096;
  val = float(temp);
  Serial.println(val*5.0/1024.0, 5);
  //client.println(val*5.0/1024.0, 5); //умножаем на 5/1024, чтобы получить вольты
}

void loop(){
  unsigned long currentMillis = millis();

  // Опрос каждые 50 мс
  if(currentMillis - previousMillis >= refreshRate){
    process();
    previousMillis = currentMillis;
  }
}

Зунзулла Алагати дает хорошее замечание по поводу использования delay(). Мой тестовый скетч удалил delay() и заменил его таймером millis(), основанным на скетче BlinkWithoutDelay, который поставляется с IDE.

,