Как лучше оптимизировать скетч для работы с majordomo по MQTT?
- Войдите на сайт для отправки комментариев
Уважаемые форумчане, конечно тема оптимизации кода всплывает на поверхность с завидным постоянством, написано множество статей, переломано куча копий, сломано масса судЕп... и все же, не пинайте сильно ногами :)
На этом форуме в трех ветках "Этюды для начинающих: Память" очень подробно разобраны методы программирования. Разумеется их читал, но, пока что в голове не все устаканилось. Но я работаю над собой!
Суть в следующем:
Arduino Uno.
За основу был взят пример из библиотеки "Adafruit MQTT Library Ethernet Example", в который добавлялись, так же из примеров по работе с датчиками участки кода. Все было прекрасно и безоблачно, пока память под переменные не закончилась :(
Разумеется код не оптимален. От слова "абсолютно". Возможно нужно подобрать менее прожорливую библиотеку MQTT. Возможно опрос однотипных датчиков засунуть в цикл, сократив таким образом число переменных. Возможно лучше будет если в некоторых местах работать напрямую а не через переменные. Само собой нужно выкинуть апендикс оставшийся от "Adafruit MQTT Library Ethernet Example" с выводом счетчика циклов, но я намеренно его пока держу. Суть то не в нем, а в принципе.
В общем, посмотрите пожалуста код и просто посоветуйте в каком направлении лучше копать и насколько реально можно оптимизировать код? А то ног у Ардуинки задействовано мало, еще куча свободных а вся память кончилась :(
Понятное дело, что на голом С++ все получится в разы компактней, но я еще к сему подвигу не готов. Не все сразу...
Кхм... Не нашел как код засунуть под спойлер... :( Извините...
/*************************************************** Adafruit MQTT Library Ethernet Example Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! Written by Alec Moore Derived from the code written by Limor Fried/Ladyada for Adafruit Industries. MIT license, all text above must be included in any redistribution ****************************************************/ #include <SPI.h> #include <Wire.h> #include "Adafruit_MQTT.h" #include "Adafruit_MQTT_Client.h" #include <Ethernet.h> #include <EthernetClient.h> #include <Dns.h> #include <Dhcp.h> #include "DHT.h" //Датчик температуры и влажности //#include <BMP085.h> // Датчик давления и температуры #include <Adafruit_BMP085.h> // Датчик давления и температуры #include <BH1750-HD.h> // Датчик освещённости в Люксах /************************* Ethernet Client Setup *****************************/ // Мажардомо Контроллер №1 установлен в комнате byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; //Uncomment the following, and set to a valid ip if you don't have dhcp available. //IPAddress iotIP (192, 168, 0, 42); //Uncomment the following, and set to your preference if you don't have automatic dns. //IPAddress dnsIP (8, 8, 8, 8); //If you uncommented either of the above lines, make sure to change "Ethernet.begin(mac)" to "Ethernet.begin(mac, iotIP)" or "Ethernet.begin(mac, iotIP, dnsIP)" /************************* Описание параметров брокера *********************************/ #define AIO_SERVER "192.168.1.15" //Адресс брокера #define AIO_SERVERPORT 1883 //Порт брокера #define AIO_USERNAME "user" //Пользователь брокера #define AIO_KEY "12345" //Пароль пользователя брокера /************ Global State (you don't need to change this!) ******************/ // Датчики температуры и влажности начало //#define DHTTYPE DHT11 // DHT 11 #define DHTTYPE DHT22 // DHT 22 (AM2302) //#define DHTTYPE DHT21 // DHT 21 (AM2301) #define DHTPIN1 6 // Пин подключения датчика температуры и влажности 1 #define DHTPIN2 7 // Пин подключения датчика температуры и влажности 2 #define DHTPIN3 8 // Пин подключения датчика температуры и влажности 3 // Датчики температуры и влажности конец // Датчики газа и дыма начало int GazPin1 = A0; //Добавим MQ-2 к аналоговому пину A0 // Датчики газа и дыма конец // Датчики напряжения начало int VoltPin1 = A1; //Добавим датчик напряжения к аналоговому пину A1 // Датчики Напряжения конец //Датчики Давления и температуры начало //BMP085 dps = BMP085(); Adafruit_BMP085 dps = Adafruit_BMP085(); //Датчики Давления и температуры конец //Датчики Освещённости Люкс начало BH1750 lightMeter; //Датчики Освещённости Люкс конец //Set up the ethernet client EthernetClient client; Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY); //Соединение с брокером // You don't need to change anything below this line! #define halt(s) { Serial.println(F( s )); while(1); } /****************************** Feeds ***************************************/ //Создаем топики датчиков влажности и температуры // Setup a feed called 'photocell' for publishing. // Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname> Adafruit_MQTT_Publish photocell = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/photocell"); // Setup a feed called 'onoff' for subscribing to changes. Adafruit_MQTT_Subscribe onoffbutton = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff"); /* Adafruit_MQTT_Publish Toph1 = Adafruit_MQTT_Publish(&mqtt, "/Sensor_humidity01/ "); // Создаем топик датчика влажности №1 Adafruit_MQTT_Publish Topt1 = Adafruit_MQTT_Publish(&mqtt, "/Sensor_temp01/ "); // Создаем топик датчика температуры №1 Adafruit_MQTT_Publish Toph2 = Adafruit_MQTT_Publish(&mqtt, "/Sensor_humidity02/ "); // Создаем топик датчика влажности №2 Adafruit_MQTT_Publish Topt2 = Adafruit_MQTT_Publish(&mqtt, "/Sensor_temp02/ "); // Создаем топик датчика температуры №2 Adafruit_MQTT_Publish Toph3 = Adafruit_MQTT_Publish(&mqtt, "/Sensor_humidity03/ "); // Создаем топик датчика влажности №3 Adafruit_MQTT_Publish Topt3 = Adafruit_MQTT_Publish(&mqtt, "/Sensor_temp03/ "); // Создаем топик датчика температуры №3 Adafruit_MQTT_Publish Topg1 = Adafruit_MQTT_Publish(&mqtt, "/Sensor_Smoke01/ "); // Создаем топик датчика газа/дыма №1 Adafruit_MQTT_Publish Topdps1 = Adafruit_MQTT_Publish(&mqtt, "/Sensor_pressure01/ "); // Создаем топик датчика давления и температуры №1 */ Adafruit_MQTT_Publish Toph1 = Adafruit_MQTT_Publish(&mqtt, "/SH01/"); // Создаем топик датчика влажности №1 Adafruit_MQTT_Publish Topt1 = Adafruit_MQTT_Publish(&mqtt, "/ST01/"); // Создаем топик датчика температуры №1 Adafruit_MQTT_Publish Toph2 = Adafruit_MQTT_Publish(&mqtt, "/SH02/"); // Создаем топик датчика влажности №2 Adafruit_MQTT_Publish Topt2 = Adafruit_MQTT_Publish(&mqtt, "/ST02/"); // Создаем топик датчика температуры №2 Adafruit_MQTT_Publish Toph3 = Adafruit_MQTT_Publish(&mqtt, "/SH03/"); // Создаем топик датчика влажности №3 Adafruit_MQTT_Publish Topt3 = Adafruit_MQTT_Publish(&mqtt, "/ST03/"); // Создаем топик датчика температуры №3 Adafruit_MQTT_Publish Topg1 = Adafruit_MQTT_Publish(&mqtt, "/SS01/"); // Создаем топик датчика газа/дыма №1 Adafruit_MQTT_Publish Topdps1 = Adafruit_MQTT_Publish(&mqtt, "/SP01/"); // Создаем топик давления датчика BMP085 №1 Adafruit_MQTT_Publish Topbmpt1 = Adafruit_MQTT_Publish(&mqtt, "/STb01/"); // Создаем топик температуры датчика BMP085 №1 Adafruit_MQTT_Publish Toplux1 = Adafruit_MQTT_Publish(&mqtt, "/SL01/"); // Создаем топик освещённости датчика BH1750 №1 //Adafruit_MQTT_Publish TopVolt1 = Adafruit_MQTT_Publish(&mqtt, "/SV01/"); // Создаем топик освещённости датчика BH1750 №1 /*************************** Sketch Code ************************************/ // Объявляем глобальные переменные float dhtx ; //Для датчика темпервтуры и влажности DHT //float t ; // Температура Для датчика темпервтуры и влажности DHT char temp[8]; long p_100; //int SignBit, Whole, Fract; int Whole, Fract; int Pressure = 0; // void setup() { Serial.begin(115200); Serial.println(F("Adafruit MQTT demo")); // Initialise the Client Serial.print(F("\nInit the Client...")); Ethernet.begin(mac); delay(1000); //give the ethernet a second to initialize mqtt.subscribe(&onoffbutton); Подписываемся на топик } uint32_t x=0; void loop() { // Начало главного цикла программы // Ensure the connection to the MQTT server is alive (this will make the first // connection and automatically reconnect when disconnected). See the MQTT_connect // function definition further below. delay(1000); // Пауза отправки данных в брокер MQTT_connect(); // this is our 'wait for incoming subscription packets' busy subloop Adafruit_MQTT_Subscribe *subscription; while ((subscription = mqtt.readSubscription(1000))) { if (subscription == &onoffbutton) { Serial.print(F("Got: ")); Serial.println((char *)onoffbutton.lastread); } } // Now we can publish stuff! Serial.print(F("\nSending photocell val ")); Serial.print(x); Serial.print("..."); if (! photocell.publish(x++)) { Serial.println(F("Failed")); } else { Serial.println(F("OK!")); } // ping the server to keep the mqtt connection alive if(! mqtt.ping()) { mqtt.disconnect(); } //------------- Датчик температуры и влажности 1 начало процедуры // Reading temperature or humidity takes about 250 milliseconds! // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) // MQTT_connect(); DHT dht1(DHTPIN1, DHTTYPE); // Присваиваем датчику №1 пин и тип dht1.begin(); // Инициализируем // check if returns are valid, if they are NaN (not a number) then something went wrong! // if (isnan(t1) || isnan(h1)) { // Serial.println("Failed to read from DHT"); // } else { { dhtx = dht1.readHumidity(); // Счмтываем значения влаги с датчика в переменную Serial.print("Humidity №1: "); // Печатаем надпись в порт Serial.print(dhtx); // Печатаем в порт значения с датчика влажности №1 Toph1.publish (dhtx); // Отправляем значения с датчика влажности №1 в брокер dhtx = dht1.readTemperature(); // Счмтываем значения температуры с датчика в переменную Serial.print(" Temperature №1: "); // Печатаем надпись в порт Serial.print(dhtx); // Печатаем в порт значения с датчика температуры №1 Serial.println(" *C"); Topt1.publish(dhtx); // Отправляем значения с датчика температуры №1 в брокер } //------------- Датчик температуры и влажности 1 конец процедуры //------------- Датчик температуры и влажности 2 начало процедуры // Reading temperature or humidity takes about 250 milliseconds! // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) // MQTT_connect(); DHT dht2(DHTPIN2, DHTTYPE); // Присваиваем датчику №1 пин и тип dht2.begin(); // Инициализируем // check if returns are valid, if they are NaN (not a number) then something went wrong! // if (isnan(t1) || isnan(h1)) { // Serial.println("Failed to read from DHT"); // } else { { dhtx = dht2.readHumidity(); // Счмтываем значения влаги с датчика в переменную Serial.print("Humidity №2: "); // Печатаем надпись в порт Serial.print(dhtx); // Печатаем в порт значения с датчика влажности №2 Toph2.publish (dhtx); // Отправляем значения с датчика влажности №2 в брокер dhtx = dht2.readTemperature(); // Счмтываем значения температуры с датчика в переменную Serial.print(" Temperature №2: "); // Печатаем надпись в порт Serial.print(dhtx); // Печатаем в порт значения с датчика температуры №2 Serial.println(" *C"); Topt2.publish(dhtx); // Отправляем значения с датчика температуры №2 в брокер } //------------- Датчик температуры и влажности 2 конец процедуры //------------- Датчик температуры и влажности 3 начало процедуры // Reading temperature or humidity takes about 250 milliseconds! // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) // MQTT_connect(); DHT dht3(DHTPIN3, DHTTYPE); // Присваиваем датчику №1 пин и тип dht3.begin(); // Инициализируем // check if returns are valid, if they are NaN (not a number) then something went wrong! // if (isnan(t1) || isnan(h1)) { // Serial.println("Failed to read from DHT"); // } else { { dhtx = dht3.readHumidity(); // Счмтываем значения влаги с датчика в переменную Serial.print("Humidity №3: "); // Печатаем надпись в порт Serial.print(dhtx); // Печатаем в порт значения с датчика влажности №3 Toph3.publish (dhtx); // Отправляем значения с датчика влажности №3 в брокер dhtx = dht3.readTemperature(); // Счмтываем значения температуры с датчика в переменную Serial.print(" Temperature №3: "); // Печатаем надпись в порт Serial.print(dhtx); // Печатаем в порт значения с датчика температуры №3 Serial.println(" *C"); Topt3.publish(dhtx); // Отправляем значения с датчика температуры №3 в брокер } //------------- Датчик температуры и влажности 3 конец процедуры //------------- Датчик Газа и дыма 1 начало процедуры int GazLevel = analogRead(GazPin1); itoa(GazLevel, temp, 10); Serial.print(" Газ/Дым №1: "); Serial.println (temp); Topg1.publish(temp); // Отправляем значения с датчика Газ/Дым №1 в брокер // Adafruit_MQTT_Publish(&mqtt, "/Sensor_Smoke01/ ").publish(temp); // Создаем топик датчика газа/дыма №1 и тправляем данные // Adafruit_MQTT_Publish(&mqtt, "/Sensor_Smoke01/ ".publish(temp)); // Создаем топик датчика газа/дыма №1 и тправляем данные //------------- Датчик Газа и дыма 1 конец процедуры //------------- Датчик Давления и температуры BMP085 1 начало процедуры Wire.begin(); dps.begin(); dhtx = (dps.readTemperature()); // Получаем значение температуры с датчика давления и температуры bmp80 Serial.print("Temperature = "); Serial.print(dhtx); Serial.println(" *C"); itoa(dhtx, temp, 10); Topbmpt1.publish(temp); // Отправляем значения температуры с датчика атмосферного давления bmp80 в брокер Pressure = dps.readPressure()/133.1; //получаем значение атмосферного давления в паскалях и переводим в мм ртутного столба Serial.print(Pressure); Serial.println(" mm "); itoa(Pressure, temp, 10); Topdps1.publish(temp); // Отправляем значения с датчика атмосферного давления bmp80 в брокер //------------- Датчик Давления и температуры BMP085 1 конец процедуры //------------- Датчик Освещённости 1 начало процедуры //Adafruit_MQTT_Publish Toplux1 = Adafruit_MQTT_Publish(&mqtt, "/SL01/"); // Создаем топик освещённости датчика BH1750 №1 lightMeter.begin(BH1750_CONTINUOUS_HIGH_RES_MODE); uint16_t lux = lightMeter.readLightLevel(); // Serial.print("Light: "); // Serial.print(lux); // Serial.println(" lx"); itoa(lux, temp, 10); Toplux1.publish(temp); // Отправляем значения с датчиа освещённости №1 в брокер //------------- Датчик Освещённости 1 конец процедуры } // Конец главного цикла программы // Function to connect and reconnect as necessary to the MQTT server. // Should be called in the loop function and it will take care if connecting. void MQTT_connect() { int8_t ret; // Stop if already connected. if (mqtt.connected()) { return; } Serial.print("Connecting to MQTT... "); while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected Serial.println(mqtt.connectErrorString(ret)); Serial.println("Retrying MQTT connection in 5 seconds..."); mqtt.disconnect(); delay(5000); // wait 5 seconds } Serial.println("MQTT Connected!"); }
1. Просто убило: " А то ног у Ардуинки задействовано мало, еще куча свободных а вся память кончилась". У Вас какая задача: сделать реальный проект или использовать все ноги ардуинки? Если послдеднее - пустите по ним бегущие огни и заканчивайте на этом проект.
2. Одиннадцать !!! подключаемых библиотек. Вы всерьез хотите использовать в таком проекте Уно?
3. Ну и конкретные советы, как исправить Вашу явно неоптимальную работу с памятью: все текстовые константы вынесите в PROGMEM. После этого сразу станет заметно легче. Но, в целом, следует соразмерять свои хотелки с возможностями конкретного контроллера. Т.е. сначла мы формулируем задачу и только протом подбираем для нее подходящий контроллер, а не наоборот, как это сделано у Вас.
1. У Вас какая задача: сделать реальный проект или использовать все ноги ардуинки?
Проект реален, просто он "вырос" и другого проекта. Сначала я реализовал погодную станцию описанную в статье
http://out.arduino.ru/?redirect=http%3A%2F%2Fstudent-proger.ru%2F2014%2F...
Постепенно допиливал скетч, оброс датчиками, которые распихал по всему дому(живу в частном доме и хочется контролировать все и вся). Проект прекрасен своей относительной простотой, но это "вещь в себе", которая только отсылает данные на сайт "народный мониторинг" и только. А хочется не просто смотреть на циферки и строить графики, а еще на их основе и управлять различной автоматикой. А сие реализовывать привязываясь к "народному мониторингу" - не самая удачная, на мой взгляд идея. Поскольку дома есть сервачек на Linux Mint поставил туда Majordomo + Mosquitto. И начал осваивать связку этого хозяйства с тем колхозом, что уже построен. Тут то и выяснилось, что не хватает памяти для подключения всех тех датчиков, которые у меня уже прекрасно работают с другим скетчем.
2. Одиннадцать !!! подключаемых библиотек. Вы всерьез хотите использовать в таком проекте Уно?
На сегодняшний момент задача суметь оптимизировать скетч таким образом, что бы задействовать все те датчики которые у меня уже подключены. И не переделовать то, что уже смонтировано. Пусть себе работает. Но уже в новом качестве. А дальше расширять функционал буду добавлением контроллеров для локальных задачь.
Ну и прокачать свой скил, разумеется, научившись более рационально работать с имеющимися ресурсами.
3. Ну и конкретные советы, как исправить Вашу явно неоптимальную работу с памятью: все текстовые константы вынесите в PROGMEM. После этого сразу станет заметно легче. Но, в целом, следует соразмерять свои хотелки с возможностями конкретного контроллера. Т.е. сначла мы формулируем задачу и только протом подбираем для нее подходящий контроллер, а не наоборот, как это сделано у Вас.
Да, про PROGMEM я тоже думал, начал изучать. Спасибо что подтвердили мои мысли. А почему только текстовые? Из за их "объёма"? Числовые не имеет особого смысла, дабы не усложнять код?
А на счет выбора контроллера под задачу - Вы тут совершенно правы. Но откуда ноги растут я уже повествовал чуть выше.
Спасибо Вам большое!
Сначала бы вам нужно рассказать, что подразумевается под "закончилась память под переменные" - RAM, Progspace?
Сначала бы вам нужно рассказать, что подразумевается под "закончилась память под переменные" - RAM, Progspace?
Наверное так будет наглядней?
По мне так нормально, раз работает.
А дальше расширять функционал буду добавлением контроллеров для локальных задачь.
Для этого, минимум, надо, чтобы было куда расширять. А если по принципу: У меня есть готовый проект, и он помещается в Уно. У меня также есть другой проект, и он также помещается в Уно. Вот я и хочу объединить эти два проекта и добавить к ним следующий функционал... то какого-то из ресурсов (оперативной памяти, флэш-памяти, EEPROM, выводов, таймеров, прерываний...) может и не хватить.
Скетч использует 25362 байт (78%) памяти устройства. Всего доступно 32256 байт.
После того, как Вы уберете строки в PROGMEM, у Вас остантся примерно по 20% обоих видов памяти на дополнение функциональности.
Не совсем так. Я хочу заменить скетч на свой и отправлять данные в majordomo и уже оттуда, те, что нужно, отсылать на народный мониторинг. Благо что в majordomo есть нужный функционал.
И из majordomo управлять другими контроллерами, которые будут рулить исполнительными устройствами.
Можно конечно перейти на мегу, но тогда придется переделывать то, что уже смонтировано в коробочку и подсоеденино с кучей датчиков. К тому же это лишь отсрочит проблему но не решит ее. На сегодняшний момент мой корявый скетч не позволяет использовать все датчики, которые уже смонтированы и прекрасно работают со старым скетчем.
Правильно ли я понимаю, что начать нужно с засовыванием вот этих переменных в массив строк?
Уж больно много каждая отжирает памяти отведённой для переменных :(
В описании PROGMEM это выглядит следующим образом:
Но, если честно, я, пока что, не очень представляю как это сделать в моем случае... Я ведь только только хоть что то начал понимать что к чему...
Экземпляр класса в PROGMEM не засунуть.
Просто продолжите использовать макрос F() в Serial.println(). Или вообще не выводите ничего в Serial, отключив этот функционал.
Далее... когда решите перейти к радикальным мерам, можете начать избавляться от float-переменных, но тогда придется переписывать часть библиотек. Выкиньте DNS, он вам не нужен. DHCP так просто не отключить, необходимо править библиотеку Ethernet. Много оперативки жрет Wire. Переходом на альтернативную библиотечку поэкономите слегонца. Но адафрутовские рутины работают только со стандартным Wire. И вновь - все придется переписать.
Некоторые функции могут напрямую работать с PROGMEM, некоторые - нет.
Например, println/print - может.
Поэтому начните с того, что все строки вида
замените на
этим Вы уже достигнете некоторого эффекта. А дальше можно попытаться проделать то же самое с другими функциями, принимающими строку - вдруг получится.
Экземпляр класса в PROGMEM не засунуть.
Я думал засунуть "/SH01/"... Тоже никак не получится?
Просто продолжите использовать макрос F() в Serial.println(). Или вообще не выводите ничего в Serial, отключив этот функционал.
Да, я и хотел все, что выводится в сериал порт поотключать. Оно ведь нужно только на этапе подключения датчика, что бы глянуть работает или нет. По большому счету достаточно будет сообщения о соединении с брокером.
Далее... когда решите перейти к радикальным мерам, можете начать избавляться от float-переменных, но тогда придется переписывать часть библиотек. Выкиньте DNS, он вам не нужен. DHCP так просто не отключить, необходимо править библиотеку Ethernet. Много оперативки жрет Wire. Переходом на альтернативную библиотечку поэкономите слегонца. Но адафрутовские рутины работают только со стандартным Wire. И вновь - все придется переписать.
Да, DNS мне тоже не нужен. Я работаю с локальной сеткой. DHCP можно и оставить. Остается найти менее прожорливую MQTT библиотеку.... Их я нашел вагон и маленькую тележку, но, для меня самым понятным оказалась именно адафрутовская. Отказаться от float - на сегодняшний момент непосильная для меня задача. недорос. Не подскажите менее прожорливые библиотеки?
p.s. Что то глюкнуло и предыдущий комментарий запостился невероятное количество раз. Как удалить лишнее - что то не обнаружил :( Не ругайте слишком сильно, сам не рад:(
Я думал засунуть "/SH01/"... Тоже никак не получится?
Попробуйте. Но я смысла не вижу, 60 байтов наэкономите может. Я с MQTT не сражался, не смогу посоветовать. Общие рекомендации написал выше.
найти менее прожорливую MQTT библиотеку.... Их я нашел вагон и маленькую тележку, но, для меня самым понятным оказалась именно адафрутовская.
https://github.com/256dpi/arduino-mqtt лучше будет и попроще.
Пущай тут лежит )
интересно девки пляшут...esp8266 для mqtt видимо более правильный выбор