Как отследить ограничения ардуины или как ловить баги

axill
Offline
Зарегистрирован: 05.09.2011

Писал писал программу с постепенной отладкой и в итоге дописался до того, что она у меня неожиданно то зависает, то перезапускается. Так как использую  активно взаимодействие через XBee, много использую объекты String

1. Как отследить, что моей программе уже не хватает оператвной памяти контроллера? Скетч по размеру всего 18к

2. как лучше всего отлаживать программу? на ум приходит только постепенное исключение кусков кода с поиском того куска, который вызывает проблемы

mixail844
Offline
Зарегистрирован: 30.04.2012

ну во первых ваш подход правильный..но длинный.
но есть,как мне кажеться более эффективный способ,использовать отладчик  в AVR Studio,можно скачать с сайта Atmel или поискать в нете и там выбрав вашь контроллер пошагово запустить работу вашего кода.единственный момент - вам нужно будет почитать мануал как из среды Arduino IDE вытаскивать программу в виде .hex кода,что бы скормить его Avr Studio для эмулации и там уже смотреть когда виснет,какой регистр как изменяеться и прочее..
еще вариант,возможно более наглядный - использовать Proteus - там можно и вашу схемку приближенно нарисаовать и так же пошагово запустить код.

leshak
Offline
Зарегистрирован: 29.09.2011

>что она у меня неожиданно то зависает, то перезапускается.

Да. По симтомам это довольно сильно похоже на нехватку памяти. Врочем и просадки питания могут давать такую же бяку. Ну и мусорные импульсы в пины - опять тоже самое будет.

 > Как отследить, что моей программе уже не хватает оператвной памяти контроллера? Скетч по размеру всего 18к

"Скетч по размеру" - особо ничего не значит. Скетч может быть маленький, а объявить "пару буфферов побольше" и привет. Под переменные/объекты тоже память выделяется. Так что на "размер скетча" я вообще особо не смотрю. Если "вылезем за размеры", так еще на этапе заливки ArduinoIDE нафиг пошлет.

Отслеживать: начните отсюда http://www.arduino.cc/playground/Code/AvailableMemory

потом можно порытся на форумах arduino.cc

Насколько я понял, ни один подход не является точным. Все на уровне "шаманства". В лучшем случае дает цифры для "общего представления".

Я лично, перебирал "кто же из них работает". Просто сделал что-то типа Serial.println("My Test String");  и менял длину вот этой строки - смотрел "кто из этих функций показывает изменения более похожее на правду".

>как лучше всего отлаживать программу?

Ну опять-таки, сами понимаете если бы сущетсвовало "идеальное решение" - его бы просто автоматизировали. В каждом случае - отдельное решение. Особенно учитывая что может глючить не только программа но и железо.

  • Логгироване - Serial.print по делу и без дела :)  Можно отладочное сразу оборачивать в #ifdef DEBUG что-бы оптом его выключить одним движением (и освободить память) - мой основной метод, пока.
  • Мигать диодами в ключевых точках
  • Эмуляторы (VirtualBreadboard, Proteus и т.п.) - пока не пользуюсь активно, но думаю на него переходить.
  • Ну и "исключение кусков кода" - куда же от этого дется :) Тоже сильный прием.
  • Или наоборот. Делаешь "проверочный скетчик" и в нем крутишь "подозрительный кусочек", смотришь как он себя ведет.

P.S. По моему опыту главными "пожералками памяти", являются именно всякие Serial.println("My Test String") и проч. текстовые строки. Мне пока хватало просто все их аккуратно, когда памяти начинает не хватать, перевести в PROGMEM и PSTR и памяти сразу становится "много" ;)

 

 

 

axill
Offline
Зарегистрирован: 05.09.2011

закомментировал самый основной обмен по XBee и пока не виснет. Как оптимизировать использование памяти?

В обмене мне нужно вначале сформировать URL, а потом его отправить в серийный порт, вот мои функции:

// dir = 0 -> send dir = 1 - get
String beeEnquireString(String request, int dir) {
  String result = "";
  
  if(dir == 0) {
    // sending
    showChar('\x0');
  } else {
    // recieving
    showChar('\x1');
  }
  
  Serial.println(request);
  
  for (int i = 0; Serial.available() == 0 && i < 6000/10; i++) { delay(10); }
  
  if(Serial.available() == 0) {
    showChar('\x3');
    beepCount(40, 2);
    connDigi = 2; // timeout
    hideChar(500);
    return result;
  }
  
  showChar('\x4');
  while(Serial.available() > 0) {
    char c = Serial.read(); 
    if (c != '\n') {  result += (char) c; }
    delay(2);
  }
  hideChar(50);
  
  connDigi = 0;
  return result;
}
int beeEnquireInt(String request, int dir) {
  String str = beeEnquireString(request, dir);
  int result = -1;
  log("vera getting int");
  if(connDigi == 0 && str.length() > 0) { 
    log("will try convert string to int ");
    result = str.toInt(); 
  }
  return result;
}

void beeSetVSwitch(int id, int status){
  String enq = vera3URL+"data_request?id=lu_action&output_format=text&DeviceNum="
    + String(id)
    + "&serviceId=urn:upnp-org:serviceId:VSwitch1&action=SetTarget&newTargetValue="
    + String(status);
  beeEnquireString(enq, 0);
}
int beeGetVSwitch(int id) {
  // запрашиваю значение виртуального выключателя по id
  String enq = vera3URL+"data_request?id=lu_variableget&serviceId=urn:upnp-org:serviceId:VSwitch1&Variable=Status&DeviceNum="
    + String(id);
  return beeEnquireInt(enq, 1);
}
String beeSetViewValue(int id, String variable, String value) {
  String enq = vera3URL+"data_request?id=lu_variableset&serviceId=urn:upnp-org:serviceId:VContainer1&Variable="
    + variable
    + "&DeviceNum="
    + String(id)
    + "&Value="
    + value;
  return beeEnquireString(enq, 0);
}
String beeGetViewValue(int id, String variable) {
  String enq = vera3URL+"data_request?id=lu_variableget&serviceId=urn:upnp-org:serviceId:VContainer1&Variable="
    + variable
    + "&DeviceNum="
    + String(id);
  return beeEnquireString(enq, 1);
}

 

axill
Offline
Зарегистрирован: 05.09.2011

leshak пишет:

Мне пока хватало просто все их аккуратно, когда памяти начинает не хватать, перевести в PROGMEM и PSTR и памяти сразу становится "много" ;) 

Можно на пальцах разъяснить?)

leshak
Offline
Зарегистрирован: 29.09.2011

Ну первое - я бы наверное отказался от использования объекта String. А пользовался обычными строками - string. Во первых за ними тоже, скорее всего, тянутся куча функций, во вторых не известно как он внутри себя с памятью обращается, плюс большая вероятсно что на каждой конкатенации строк - образуется новый объек типа строка.

Вообщем открывать какую-нибудь книги по C и смотреть раздел "работа со стороками".

Для того что-бы не грузить строки в оперативку, а работать прямо с флеша нужно прочитать http://www.arduino.cc/playground/Main/PROGMEM 

Ну или просто погуглить слова, которые я упоминал, PROGMEM и PSTR со словом ардуино. Я уж не помню где именно это читал, когда разбирался, но нагугливалось без проблем.

В простейшем виде я пользуюсь написал вот такие две функции хелпера.

 

// аналоги Serial.print и Serial.println, но строки берут из флеша.
void print (PGM_P s) {
        char c;
        while ((c = pgm_read_byte(s++)) != 0)
            Serial.print(c);
    }

void println(PGM_P s){
	print(s);
	Serial.println();
}

Ну, а потом, просто глобальной заменой или руками меняю Serial.print("Hello world") на print(PSTR("Hello world")) и получают "профит". Аналогичная замена для println.

Пока мне даже этого хватало. 

Возможно эти функции и не нужны. Где-то, краем глаза, видел что в новой arduinoIDE добавили какую-то поддержку к Stream (а Serail от него наследуется), для работы со стороками из флеша. Как-то через синтаксис << , но не вникал и не уверен в этом. Небыло надобности.

 

axill
Offline
Зарегистрирован: 05.09.2011

leshak пишет:

Ну или просто погуглить слова, которые я упоминал, PROGMEM и PSTR со словом ардуино. Я уж не помню где именно это читал, когда разбирался, но нагугливалось без проблем.

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

Написал пока себе простую функцию по загрузки строки из памяти программ в оъект String по запросу. вроде все стало стабильным

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

leshak пишет:

...

 

Возможно эти функции и не нужны. Где-то, краем глаза, видел что в новой arduinoIDE добавили какую-то поддержку к Stream (а Serail от него наследуется), для работы со стороками из флеша. Как-то через синтаксис << , но не вникал и не уверен в этом. Небыло надобности.

 

Стало интересно - проверил. Кажется, действительно добавили :)

работает в виде

PGM_P msg="Чего-то там";
Serial.print(msg);

 Только с русскими буквами засада - посылает "как есть", то есть по 2 байта на букву (UTF-8)

leshak
Offline
Зарегистрирован: 29.09.2011

> PGM_P msg="Чего-то там";

А оно так понимает что это строка именно из флеша? в PSTR оборачивать строку не нужно? С одной стороны вроде PGM_P об этом говорит, с другой ....

А вот такое не затруднит проверить? (нет счас ардуины под рукой, только вечером доберусь,  а любопытно сейчас): 

Serial.println(PSTR("Hello Word!"));

 

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Такую конструкцию "кушает", но в порт ничего не посылает, только перевод строки.

leshak
Offline
Зарегистрирован: 29.09.2011

AlexFisher пишет:

Такую конструкцию "кушает", но в порт ничего не посылает, только перевод строки.

Спасибо.

Нужно будет покопатся с этим подробней. Посмотреть исходинки Stream и где как объявлять. Хотя-бы что-бы быть уверенным где оно действительно разместило во флеш, а где проигнорировало это.

Что-бы не получилось как с PROGMEM для локальных переменных. Тупо его игнорит, и ты думаешь что "разместил во флеше и работает", а на самом деле оно так в памяти и осталось (потому и работает).

xzeus
Offline
Зарегистрирован: 15.07.2015
Serial.println(F("Hello Word!"));

 

Logik
Offline
Зарегистрирован: 05.08.2014

Своевременный ответ.