Оптимизация кода для уменьшения размера используемой ОЗУ
- Войдите на сайт для отправки комментариев
День добрый.
Взываю к помощи. В программировании на Си - новичок, поэтому выдержу любые кидания помидорами.
Столкнулся с нехваткой ОЗУ для выполнения необходимых функций на arduino UNO:
Глобальные переменные используют 1 902 байт (92%) динамической памяти, оставляя 146 байт для локальных переменных. Максимум: 2 048 байт.
// Датчик DHT11 подключен к цифровому пину номер 2 // Реле модуль подключен к цифровому выводу 4 // ИК диод подключается на 3 пин //Часы подключены на 7,8,9 пины // DS1302: CE RST pin -> Arduino Digital 7 // I/O DATA pin -> Arduino Digital 8 // SCLK CLK pin -> Arduino Digital 9 //Подключение Blynk #define BLYNK_PRINT Serial #include <SPI.h> #include <Ethernet.h> #include <BlynkSimpleEthernet.h> #include <SimpleTimer.h> char auth[] = "1c69d3a8cb2446229cc900e7a7df6fa8"; SimpleTimer timer; //Подключение бибилиотеки DHT11 #include <dht11.h> // Добавляем библиотеку DHT11 dht11 DHT; // Объявление переменной класса dht11 #define DHT11_PIN 2 // Подключение библиотеки для работы с IR-светодиодом #include <IRremote.h> IRsend irsend; //Модуль реального времени #include <DS1302.h> // Часы DS1302 rtc(7, 8, 9); //Контакт реле int Relay = 4; //Контакт диода int LED_PIN = 6; //Переменные для управления кондиционером byte ConditionerON[349] = {5,180,30,90,5,5,5,15,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,15,5,5,5,5,5,15,5,5,5,5,5,15,5,15,5,15,5,15,5,15,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,15,5,15,5,15,5,15,5,30,30,90,5,15,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,15,5,5,5,5,5,15,5,5,5,15,5,15,5,15,5,15,5,15,5,15,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,30,30,90,5,15,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,15,5,5,5,5,5,5,5,5,5,15,5,15,5,5,5,15,5,15,5,15,5,15,5,15,5,15,5,15,5,15,5,5,5,5,5,5,5,15,5,15,5,15,5,5,5,5,5,5,5,5,5,5,5,15,5,15,5,15,5,5,5,15,5,5,5,5,5,5,5,15,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,15,5,15,5,15,5,15,5 }; byte ConditionerOff[349] = {5,180,30,90,5,5,5,15,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,15,5,5,5,5,5,15,5,15,5,5,5,15,5,15,5,15,5,15,5,15,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,15,5,15,5,30,30,90,5,15,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,15,5,5,5,5,5,15,5,5,5,15,5,15,5,15,5,15,5,15,5,15,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,30,30,90,5,15,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,15,5,5,5,5,5,5,5,15,5,15,5,15,5,5,5,15,5,15,5,15,5,15,5,15,5,15,5,15,5,15,5,5,5,5,5,5,5,15,5,15,5,15,5,5,5,5,5,5,5,5,5,5,5,15,5,15,5,15,5,5,5,15,5,5,5,5,5,5,5,15,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,15,5,15,5 }; boolean STATE_TEMP; /////////////////////////////////////////////////////////// //Переменные задания температуры unsigned int TEMP_MAX; unsigned int TEMP_MIN; BLYNK_WRITE(V3) { int widgetValue_MIN = param.asInt(); // Here we create variable to store incoming values from V1. Serial.print("MIN TEMP "); Serial.println(widgetValue_MIN); TEMP_MIN = widgetValue_MIN; } BLYNK_WRITE(V4) { int widgetValue_MAX = param.asInt(); // Here we create variable to store incoming values from V1. Serial.print("MAX TEMP "); Serial.println(widgetValue_MAX); TEMP_MAX = widgetValue_MAX; } /////////////////////////////////////////////////////////// //Установка интервала считывания данных long previousMillis = 0; long interval = 4000; //Виртуальные переменные void myTimerEvent(){ Blynk.virtualWrite(V1, DHT.temperature); Blynk.virtualWrite(V2, DHT.humidity); } //Для кириллицы в мониторе порта char* RUS(char* StrIn){char* StrOut=StrIn; uint8_t i[4]={0,0,StrIn[0],StrIn[1]}; while(i[2]>0&&i[0]<255){if(i[2]==0xd0&&i[3]>0x8F&&i[3]<0xC0){StrOut[i[1]]=i[3]+0x30;i[0]++;}else if(i[2]==0xd0&&i[3]==0x81){StrOut[i[1]]=0xA8;i[0]++;}else if(i[2]==0xd1&&i[3]>0x7F&&i[3]<0x90){StrOut[i[1]]=i[3]+0x70;i[0]++;}else if(i[2]==0xd1&&i[3]==0x91){StrOut[i[1]]=0xB8;i[0]++;}else{StrOut[i[1]]=i[2];} i[0]++; i[1]++; i[2]=StrIn[i[0]]; i[3]=StrIn[i[0]+1];} StrOut[i[1]]='\0'; return StrOut;} //////////////////////////////////////////////////////////////////////////////////////////////////////// void setup(){ // Set the clock to run-mode, and disable the write protection rtc.halt(false); rtc.writeProtect(false); //Вывод на монитор порта Serial.begin(9600); // Скорость работы порта Blynk.begin(auth); //Авторизация в Blynk // Setup a function to be called every second timer.setInterval(1000L, myTimerEvent); pinMode(Relay, OUTPUT); //назначение порта с Реле pinMode(LED_PIN, OUTPUT); // назначение порта с диодом } ////////////////////////////////////////////////////////////////////////////////////////////////////////// void loop(){ Blynk.run(); //Запуск Blynk в цикле timer.run(); // Initiates SimpleTimer // Выводим показания влажности и температуры датчика///////////////////////////////// unsigned long currentMillis = millis(); if(currentMillis - previousMillis > interval) { previousMillis = currentMillis; // Мониторинг ошибок датчика температуры int chk; chk = DHT.read(DHT11_PIN); // Чтение данных switch (chk){ case DHTLIB_OK: break; } //Часы // Send date Serial.print(RUS("Дата ")); Serial.print(rtc.getDateStr()); // Send time Serial.print(RUS(" Время ")); Serial.print(rtc.getTimeStr()); //Датчики ////int TEMP_MAX = pinValue; Serial.print(RUS(" Влажность = ")); Serial.print(DHT.humidity, 1); Serial.print(RUS(", Температура = ")); Serial.print(DHT.temperature,1); Serial.print(" ("); Serial.print(TEMP_MIN,1); Serial.print(")"); Serial.print(" - ("); Serial.print(TEMP_MAX,1); Serial.println(")"); Serial.print("Status "); Serial.println(STATE_TEMP,1); } ///////////////////////////////////////////////////////////////////// //Работа диода if (DHT.temperature >= TEMP_MAX) digitalWrite(LED_PIN, HIGH); else if (DHT.temperature <= TEMP_MIN) digitalWrite(LED_PIN, HIGH); else digitalWrite(LED_PIN, LOW); //Работа автоматики по температуре if (DHT.temperature >= TEMP_MAX && STATE_TEMP == LOW) { irsend.sendRaw(ConditionerON, 349, 38); STATE_TEMP = HIGH; } if (DHT.temperature <= TEMP_MIN && STATE_TEMP == HIGH) { irsend.sendRaw(ConditionerOff, 349, 38); STATE_TEMP = LOW; } }
Кириллица занимает 51 байт. Часы реального времени - 47 байт. Это все от чего я могу отказаться, но лучше от этого не становится. Blynk много берет себе, но его функционал мне понравился и отказываться от него в пользу html не хочу.
Пытался переносить переменные, ужимал все что мог, но знаний в массивах не хватает и понять как можно проще указать количество повторяющихся друг за другом переменных не могу...
Знаю, что надо еще что-то делать с массивами для передачи ИК-сигнала, но я сделал все что смог: поместил длительности интервалов из int в byte и в библиотеке подправил, чтобы умножалось значение на 100 и отправлялось на ИК передатчик.
Почитал этюды для начинающих и прочие форумы, но ничего не понял...
Надеюсь на Ваши советы или хотя бы на ссылки на понятные источники.
ConditionerOn & ConditionerOff разместить во флеше.
Эти же массивы "сжать". Самое тупое, хранить парами "значение, количество", даже это уже сократит массивы. Пятерок там море.
kisoft говорит дело :) Начните с разбора вот такого примера:
Спасибо огромное за пример и советы!
Буду вечером пробовать!
Отпишусь по количеству увеличенной свободной памяти!
Прошу прощения, но я, видимо, где то накосячил.
сделал все так, как в примере, но ОЗУ теперь занимает на 500 байт больше.
В моем коде, видимо, память резервируется и для переменных и для развернутой переменной commandON/OFF. Может если способ очищать ОЗУ после каждого разворачивания переменной command?
Уже было и не один раз разжовано, например, здесь: http://arduino.ru/forum/programmirovanie/perepolnyaetsya-sram-komandami-dlya-pulta-pomogite-razobratsya
Mariev
немножечко накосячили ))
byte
command[349]; // в оперативке должен остаться только он один
PROGMEM byte
RLEcmdOff[ RLE_LEN ] = { .. } // эти два массива можно объявить так для перемещения их во флеш, см. ссылку kisoft
PROGMEM byte
RLEcmdOn[ RLE_LEN2 ] = { .. } // и проверьте, на самом ли деле совпадает у вас размерность сжатых массивов, а то она скорее всего должна быть разной - RLE_LEN1 и RLE_LEN2
и перед отправкой irsend заполняйте буфер command[] уже из флеша как-то так (не проверял):
command[ ptr++ ] = pgm_read_byte_near(& RLEcmdOff[ i + 1 ] );
Спасибо.
Про занесение данных во флэш узнал из ссылки kisoft.
Уже сделал - теперь используется 75% памяти. 497 байт свободно. А главное можно внести еще несколько команд управления в память.
Архивирование массива в конечном итоге не делал, т.к. не вижу как это поможет в уменьшении используемой ОЗУ - все равно разворачивается переменная в 349 байт.
Если в чем то ошибаюсь - поправьте пожалуйста.
За советы всем спасибо
Архивирование массива в конечном итоге не делал, т.к. не вижу как это поможет в уменьшении используемой ОЗУ - все равно разворачивается переменная в 349 байт.
...
Это всего лишь твоя фантазия, не нужно ничего разворачивать в ОЗУ, коды из массива считываются в send побайтово и сразу отправляются.
1. Короче говоря, если нужно больше ОЗУ, переносить во флеш и оттуда считывать при отправке.
2. Если нужно больше флеша, то нужно делать "сжатие". При этом не нужно разворачивать сжатый массив в ОЗУ, а считывая по два байта из флеша формировать данные и сразу их отправлять.
3. Если нужно еще больше флеша, то нужно "закодировать" последовательность, хранить её во флеше в бинарном виде, а при отправке считывать из флеша в бинарном виде, после чего отправлять, например, как в sendNEC.
Возможно еще есть варианты, но третий, самый эффективный, в плане того, что можно море команд хранить во флеше. Именно третий я и буду использовать, когда буду свой DAIKIN окучивать :)
Угу. Именно п.3 - единственно верный. Мало того, если на него перейдете то только тогда поймете что ж и как на самом деле передаете в ИК на кондиционер и как установить любую температуру, режим и т.д. Читайте например http://radiokot.ru/articles/14/http://radiokot.ru/articles/14/ похоже первый описаный и ваш.
Еще для экономии ОЗУ строковые константы тоже в флеш засунуть.
Еще для экономии ОЗУ строковые константы тоже в флеш засунуть.
Вообще-то, с этого надо начинать.
Только, строго говоря, эти константы УЖЕ засунуты в флеш. А потом оттуда копируются в ОЗУ перед началом выполнения программы.
Есть у меня сомение, что такое количество (конкретных) библиотек можно запихнуть в Уно и все это будет стабильно работать.
Отбросьте сомнения. Запихнул переменные во флэш и убрал вывод информации в монитор порта. Теперь свободного ОЗУ 600 кб и все стабильно работает
Вы хотите сказать, что статические переменные занимают ровно -598 кб?
Нет. Некоторый объем был свободен и ранее. Потом при переносе переменных передачи сигнала по ИК освободилось 349 байт. И немного увеличил свободной памяти за счет исключения вывода информации в монитор порта, т.к. теперь это не надо - все выводится в blynk