В процессе отладки скетчей для Arduino принято вставлять в код команды Serial.println, помогающие увидеть значения переменных в разных точках программы и определить источники ошибок. Эти команды потребляют значительный объем флеш-памяти. Любое использование Serial.println требует включения в скетч примерно 500 байт библиотечного кода. Поэтому, убедившись в безупречной работе скетча, удалите или закомментируйте все такие команды.
Откажитесь от использования загрузчика
В главе 2 рассказывалось, как запрограммировать микроконтроллер непосредственно через контакты ICSP на плате Arduino с применением аппаратных программаторов. Такой подход поможет сэкономить пару килобайт, так как не требует установки загрузчика.
Статическое и динамическое размещение в памяти
Если вы, подобно автору книги, имеете опыт разработки крупномасштабных систем на таких языках, как Java или C#, вам наверняка приходилось создавать объекты во время выполнения и позволять сборщику мусора освобождать занимаемую ими память без вашего участия. Этот подход к программированию в принципе непригоден для программ, выполняющихся на микропроцессорах, которые имеют всего 2 Кбайт памяти. Ведь в Arduino просто нет никакого сборщика мусора, и, что более важно, в программах, которые пишутся для Arduino, выделение и освобождение памяти во время выполнения редко бывают необходимы.
Ниже приводится пример объявления статического массива, как это обычно делается в скетчах:
// sketch_06_04_static
int array[100];
void setup()
{
array[0] = 1;
array[50] = 2;
Serial.begin(9600);
Serial.println(array[50]);
}
void loop()
{
}
Объем памяти, занимаемой массивом, известен уже на этапе компиляции скетча, поэтому компилятор может зарезервировать для массива необходимый объем памяти. Второй пример, приведенный ниже, также создает массив того же размера, но выделяет память для него во время выполнения из пула доступной памяти. Обратите внимание на то, что версии Arduino IDE ниже 1.0.4 не поддерживают malloc.
// sketch_06_03_dynamic
int *array;
void setup()
{
array = (int *)malloc(sizeof(int) * 100);
array[0] = 1;
array[50] = 2;
Serial.begin(9600);
Serial.println(array[50]);
}
void loop()
{
}
В начале скетча определяется переменная int *array. Символ * сообщает, что это указатель на целочисленное значение (или в данном случае массив целых чисел), а не простое значение. Объем памяти, занимаемой массивом, неизвестен, пока не будет выполнена следующая строка в функции setup:
array = (int *)malloc(sizeof(int) * 100);
Команда malloc (
После выделения памяти массивом можно пользоваться точно так же, как если бы память для него была выделена статически. Динамическое распределение памяти позволяет отложить принятие решения о размере массива до фактического запуска скетча, и это единственное преимущество данного подхода.
Однако, используя прием динамического распределения памяти, легко оказаться в ситуации, когда память выделяется, но не освобождается, из-за чего скетч может быстро исчерпать имеющуюся память. Исчерпание памяти может вызвать зависание Arduino. Но если вся память выделяется статически, такого не происходит.
Обратите внимание на то, что даже мне, разработавшему не одну сотню проектов на Arduino, сложно найти вескую причину, оправдывающую прием динамического выделения памяти в Arduino.
Строки
Многие программы для Arduino вообще не нуждаются в текстовом представлении данных или используют его только в командах Serial.println для нужд отладки.
В Arduino поддерживаются два основных метода использования строк: старый метод — массивы элементов типа char и новый метод с применением библиотеки String Object.
Массивы элементов типа char
Когда в скетче определяется строковая константа, такая как
char message[] = "Hello World";