Ошибка компиляции. Недостаточно оперативной памяти Arduino NANO
- Войдите на сайт для отправки комментариев
Друзья приветствую!
Познакомился недавно с ардуинками и мой мозг тут же начал генерацию проектов на базе этого контроллера.
Взявшись за проект управления теплофикационными системами уперся в проблему нехватки памяти (RAM) в ардуинке.
Суть проекта:
Есть 4 датчика ds18b20;
Есть 6 реле;
Интернет модуль enc28j60;
И собственно сам котроллер ардуино.
Объекты управления: Насос отопления, насос ГВС, котел отопления, электрокотел ГВС и два электроклапана (на гвс).
Задача СИСТЕМЫ состоит в автоматизации работы отопления и гвс в различных обстоятельствах.
Например: т.к. дом загородный, перед выездом, (в направлении дома) через интернет можно включить котлы и приехать в уже прогретый дом с подготовленной горячей водой.
Или когда дом не посещается зимой, не дать ему замерзнуть путём включения Электрокотла гвс, открытия двух клапанов и включения насоса ЦО. Всё это долго описывать на самом деле, если кого заинтересует, выложу все схемы и опишу условия, а как сам запилю всю систему и проверю сделаю статью)
Т.к. я не программист, а в портфолио имеется лишь работа с html кодами, и то по примерам, то пришлось собирать скетч-франкенштейн.
Все строки которые понимал подписал. Проблема ещё в том что в этом виде всё работает нестабильно. Буфер для интернета аж "1300". Страница перезагружается каждые 5 секунд а потом уходит в "401 Unauthorized"
Если умельцы отзовутся, будет очень круто "допилить" эту СИСТЕМУ, уверен, что многим пригодится!
//CS - D10 // //ST(SI) - D11 // //SO - D12 // //SCK - D13 // //LNT(INT) - D2 //не обязателен //5V - 5V // //GND - GND // #include <EtherCard.h> // Библиотека enc28j60 #include <EEPROM.h> // Библиотека памяти #include <OneWire.h> // Библиотека датчика ds18b20 #include <Adafruit_BMP280.h> // Библиотека датчика давл. и темп. BMP280 #define BMP_CS 10 Adafruit_BMP280 bme(BMP_CS); // hardware SPI static byte myip[] = { 192,168,0,200 }; // адрес сервера ардуино (192.168.0.любой) static byte gwip[] = { 192,168,0,1 }; // адрес админ роутера static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 }; // мак адрес должен быть уникальный (любой) static uint16_t port = 80; // порт-80 стандартный для браузеров byte Ethernet::buffer[1250]; // буффер, увеличивать при нестабильности (1280) BufferFiller bfill; int PinSSR1 = 6; // Реле 1 (Электрокотел ГВС) на пине 6 int PinSSR2 = 7; // Реле 2 (Клапан ГВС 7) на пине 7 int PinSSR3 = 8; // Реле 3 (Клапан ГВС 13) на пине 8 int PinSSR4 = 9; // Реле 4 (Насос ЦО обр.) на пине 9 char chartemp1[10]; //DHT22 Датчик температуры наружного воздуха Тн.в. char chartemp2[10]; //ds18b20 Датчик температуры ds18b20 установленном на ЦО Т1 char chartemp3[10]; //ds18b20 Датчик температуры ds18b20 установленном на ЦО Т2 int TargetTemp1; //предел температуры Тн.в. int TargetTemp2; //предел температуры ЦО Т1 int TargetTemp3; //предел температуры ЦО Т2 float temp1; //DHT22 Датчик температуры наружного воздуха Тн.в. OneWire ds(2); void setup () { if (ether.begin(sizeof Ethernet::buffer, mymac, 10) == 0); //на 10-ый пин подключен (CS) ENC28J60 ether.staticSetup(myip); pinMode(PinSSR1, OUTPUT); //Назначение пина на выход. Реле 1 (Электрокотел ГВС) на пине 6 pinMode(PinSSR2, OUTPUT); //Назначение пина на выход. Реле 2 (Клапан ГВС 7) на пине 7 pinMode(PinSSR3, OUTPUT); //Назначение пина на выход. Реле 3 (Клапан ГВС 13) на пине 8 pinMode(PinSSR4, OUTPUT); //Назначение пина на выход. Реле 3 (Насос ЦО обр.) на пине 9 TargetTemp1 = EEPROM.read(0); // Чтение "предельной" темп-ры в ячейку 1 (Тн.в.) TargetTemp2 = EEPROM.read(2); // Чтение "предельной" темп-ры в ячейку 2 (ЦО Т1) TargetTemp3 = EEPROM.read(4); // Чтение "предельной" темп-ры в ячейку 3 (ЦО Т2) } const char http_OK[] PROGMEM = // Начало HTML страницы "HTTP/1.0 200 OK\r\n" "Content-Type: text/html\r\n" "Pragma: no-cache\r\n\r\n" "<!DOCTYPE html>" "<html><head>" "<meta http-equiv='refresh' content='5'/>" "<meta charset='utf-8'>" "<title>Дом, милый дом</title>" "<body>" "<center><font size=\"4\">" "<table border=\"1\" cellpadding=\"5\" style=\"width: 800px; border-collapse: collapse; border: 1px solid black; align: center;\">" "<tr style=\"background-color: silver\"><td>Нагрузка:</td>" "<td>Тн.в.:</td>" "<td>Т1:</td>" "<td>Т2:</td>"; const char http_Found[] PROGMEM = "HTTP/1.0 302 Found\r\n" "Location: /\r\n\r\n"; const char http_Unauthorized[] PROGMEM = "HTTP/1.0 401 Unauthorized\r\n" "Content-Type: text/html\r\n\r\n" "<h1>401 Unauthorized</h1>"; static word homePage() { bfill.emit_p(http_OK); bfill.emit_p(PSTR( "<tr><td>Температура:</td>" "<td>$S °C</td>" "<td>$S °C</td>" "<td>$S °C</td></tr>"), chartemp1, chartemp2, chartemp3, bme.readTemperature(), bme.readPressure()/133.322); // Датчики в ячейках таблицы (Тн.в., ЦО Т1, ЦО Т2) bfill.emit_p(PSTR("<tr><td>Питание:</td>")); if ( digitalRead(PinSSR1) == LOW ) { // Реле (Насос ЦО обр.) bfill.emit_p(PSTR("<td><font color=green><b>ВКЛ</b></font></td>")); } else { bfill.emit_p(PSTR("<td><font color=red><b>ВЫКЛ</b></font></td>")); } if ( digitalRead(PinSSR2) == LOW ) { // Реле (Клапан ГВС 13) bfill.emit_p(PSTR("<td><font color=green><b>ВКЛ</b></font></td>")); } else { bfill.emit_p(PSTR("<td><font color=red><b>ВЫКЛ</b></font></td>")); } if ( digitalRead(PinSSR3) == LOW ) { // Реле (Клапан ГВС 7) bfill.emit_p(PSTR("<td><font color=green><b>ВКЛ</b></font></td>")); } else { bfill.emit_p(PSTR("<td><font color=red><b>ВЫКЛ</b></font></td>")); } if ( digitalRead(PinSSR4) == LOW ) { // Реле (Электрокотел) bfill.emit_p(PSTR("<td><font color=green><b>ВКЛ</b></font></td>")); } else { bfill.emit_p(PSTR("<td><font color=red><b>ВЫКЛ</b></font></td></tr>")); } bfill.emit_p(PSTR("<tr><td>Предел:</td>")); bfill.emit_p(PSTR("<td>$D °C</td>"), TargetTemp1); // Вывод инф из ячейки с "пределом" темп-ры (Тн.в.) bfill.emit_p(PSTR("<td>$D °C</td>"), TargetTemp2); // Вывод инф из ячейки с "пределом" темп-ры (Т2) bfill.emit_p(PSTR("<td>$D °C</td></tr>"), TargetTemp3); // Вывод инф из ячейки с "пределом" темп-ры (Т1) bfill.emit_p(PSTR("<tr><td>Установить:</td>" "<td><form><input type=text name=ttemp1 size=15> <input type=submit value=ОК> </form></td>" // Кнопка ОК (ЦО) "<td><form><input type=text name=ttemp2 size=15> <input type=submit value=ОК> </form></td>" // Кнопка ОК (ГВС) "<td><form><input type=text name=ttemp3 size=15> <input type=submit value=ОК> </form></td></tr>" // Кнопка ОК (Комната) "<tr><td>Режим:</td>" "<td>Rbr</td>" "<td>Кек</td>" "<td>Кек</td>" "</table></font></center></body></html>")); // Конец HTML страницы return bfill.position(); } void loop(void) { float temp2; // Присваивает запятые данным температуры (ЦО Т1) float temp3; // Присваивает запятые данным температуры (ЦО Т2) char TargetTemperarureTextbox[10]; byte sensor1[8] = {0x28, 0xFF, 0x06, 0x3D, 0x82, 0x16, 0x05, 0x55}; // Адрес датчика температуры Т1 (ПИН 5) (1 полоска) (28 FF 6 3D 82 16 5 55) byte sensor2[8] = {0x28, 0xFF, 0x8A, 0x99, 0x84, 0x16, 0x05, 0xC9}; // Адрес датчика температуры Т2 (ПИН 5) (3 полоски) (28 FF 8A 99 84 16 5 C9) byte sensor3[8] = {0x28, 0xFF, 0x4E, 0x70, 0x84, 0x16, 0x05, 0x2E}; // Адрес датчика температуры Тн.в. (ПИН 5) (2 полоски) (28 FF 4E 70 84 16 5 2E) //DHT22 //temp1 = dht22.readTemperature(); // Датчик температуры наружного воздуха //dtostrf(temp1, 4, 2, chartemp1); //DS18B20 temp1 = tempread(sensor2); // Что-то формируется с датчиком (ЦО Т1) dtostrf(temp1, 4, 2, chartemp1); temp2 = tempread(sensor1); // Что-то формируется с датчиком (Тн.в.) dtostrf(temp2, 4, 2, chartemp2); temp3 = tempread(sensor3); // Что-то формируется с датчиком (ЦО Т2) dtostrf(temp3, 4, 2, chartemp3); //Если Тн.в. <= "Предельной" температуре 1 и Т2 <= "Предельной" температуре 3 (15С), //то реле (1,2,3,4) замыкаются; включается Электрокотел ГВС, открываеются клапаны 7 и 13 на ГВС и //включается насос ЦО. //Если Тн.в. <= "Предельной" температуре 1 и Т2 >= "Предельной" температуре 3 (25С), //то реле (1,2,3,4) размыкаются; //Если Т2 <= "Предельной" температуре 3 (25С-10С), (т.е. если температура Т2 упадет до 15С) //то реле (1,2,3,4) замыкаются; включается Электрокотел ГВС, открываеются клапаны 7 и 13 на ГВС и //включается насос ЦО. if (temp1<=TargetTemp1 && temp3<=TargetTemp3-10) //Условие для датчика температуры DHT22 на (Улице) { digitalWrite(PinSSR1, LOW); // Реле 1 вкл (Насос ЦО обр.) на пине 6 digitalWrite(PinSSR2, LOW); // Реле 1 вкл (Клапан ГВС 13) на пине 7 digitalWrite(PinSSR3, LOW); // Реле 1 вкл (Клапан ГВС 7) на пине 8 digitalWrite(PinSSR4, LOW); // Реле 1 вкл (Электрокотел ГВС) на пине 9 } else if (temp3>=TargetTemp3) { digitalWrite(PinSSR1, HIGH); // Реле 1 выкл (Насос ЦО обр.) на пине 6 digitalWrite(PinSSR2, HIGH); // Реле 1 выкл (Клапан ГВС 13) на пине 7 digitalWrite(PinSSR3, HIGH); // Реле 1 выкл (Клапан ГВС 7) на пине 8 digitalWrite(PinSSR4, HIGH); // Реле 1 выкл (Электрокотел ГВС) на пине 9 } // if (temp2<TargetTemp2-5) //Условие для датчика температуры ds18b20 на (ГВС) (Реле 2, пин 8) // { // digitalWrite(PinSSR2, LOW); //При факт. темп. < (пред.-5) - реле включено // } // else if (temp2>TargetTemp2+1) // { // digitalWrite(PinSSR2, HIGH); //При факт. темп. < (пред.+1) - реле выключено // } // if (temp3<TargetTemp3-1) //Условие для датчика температуры ds18b20 на (ЦО) (Реле 1, пин 9) // { // digitalWrite(PinSSR3, LOW); //При факт. темп. < (пред.-1) - реле включено // } // else if (temp3>TargetTemp3+1) // { // digitalWrite(PinSSR3, HIGH); //При факт. темп. < (пред.+1) - реле выключено // } word len = ether.packetReceive(); //Проверить ethernet пакеты word pos = ether.packetLoop(len); //Проверить TCP пакеты if (pos) { bfill = ether.tcpOffset(); char *data = (char *) Ethernet::buffer + pos; if (strncmp("GET /", data, 5) != 0) { bfill.emit_p(http_Unauthorized); } else { data += 5; if (data[0] == ' ') { homePage(); //Если обнаружено изменение на станице, запускаем функцию } else if (strncmp( "?ttemp1=" , data , 8 ) == 0) { if (ether.findKeyVal(data + 1, TargetTemperarureTextbox , sizeof TargetTemperarureTextbox , "ttemp1") > 0) { byte value = atoi(TargetTemperarureTextbox); // команды для преобразования текста в число if ((value >=0) && (value <=10)) // Предел Тн.в. 0-10 { TargetTemp1 = value; EEPROM.write(0, value); } } bfill.emit_p(http_Found); } else if (strncmp( "?ttemp2=" , data , 8 ) == 0) { if (ether.findKeyVal(data + 1, TargetTemperarureTextbox , sizeof TargetTemperarureTextbox , "ttemp2") > 0) { byte value = atoi(TargetTemperarureTextbox); // команды для преобразования текста в число if ((value >=10) && (value <=95)) // Предел Т1 10-95 { TargetTemp2 = value; EEPROM.write(2, value); } } bfill.emit_p(http_Found); } else if (strncmp( "?ttemp3=" , data , 8 ) == 0) { if (ether.findKeyVal(data + 1, TargetTemperarureTextbox , sizeof TargetTemperarureTextbox , "ttemp3") > 0) { byte value = atoi(TargetTemperarureTextbox); // команды для преобразования текста в число if ((value >=10) && (value <=75)) // Предел Т2 10-75 { TargetTemp3 = value; EEPROM.write(4, value); } } bfill.emit_p(http_Found); } } ether.httpServerReply(bfill.position()); } } float tempread(byte sensoraddr[]) { byte i; byte present = 0; byte data[12]; // Сюда попадают данные из датчиков byte addr[8]; // Здесь хранятся адреса датчиков for ( i = 0; i < 8; i++) { addr[i]=sensoraddr[i]; } ds.reset(); // pulse the pins and wait for a response to reset the DS1820 ds.select(addr); // 0x55 (MATCH_ROM) followed by the address of the 1820 to talk to. ds.write(0x44,0); //PARASITE POWER OFF present = ds.reset(); ds.select(addr); ds.write(0xBE); // Read Scratchpad for ( i = 0; i < 9; i++) { // we need 9 bytes data[i] = ds.read(); } int16_t raw = (data[1] << 8) | data[0]; float temp = (float)raw / 16.0; return temp; }
ergeykl, конкретных советов у мея нет, могу поделиться только общими мыслями.
Мысль 1-я: У Ардуино вообще как-то маловато памяти для И-нета. А Вы еще и выбрали самую убогонькую модель с 2к памяти. В Меге 2560 или Due все как-то немного попросторнее.
Мысль 2-я: коль памяти для буферов не хватает, может, вообще обойтись без буферов? На выход передавать, сразу читая из PROGMEM по одному символу, и точно также парсить сразу входящий поток, не откладывая его в буфер.
Читал Вашу 2-ю мысль с улыбкой, потому что для меня это как читать китайские символы, ничего непонятно))
Самое печальное, что мой мозг генерирует безумное количество "проектов" по автоматизации различных инженерных систем, а выполнить я их сам не могу, потому как примеров нет как и опытного программиста под рукой. Как собака, всё понимаю, но сказать не могу.
Сегодня нарвался на esp8266, все его величают "Убийцей Ардуино" У самого есть esp-01, но нет usb ttl конвертора, и ещё заказал esp-12 который nodemcu.
Хотел поинтересоваться, с тем что 12-ый у него там 32кб памяти и если его прошить под Arduino IDE я смогу запилить подобную систему?
мой мозг генерирует безумное количество "проектов" по автоматизации различных инженерных систем
Прелесть какая )))
Да не, это такой "вирусный маркетинг" .. ща пофлудят слегонца, а потом начнут заяснять что надо всем и срочно переходить на СТМ или Клюбничку с Малинкой .. закупил попкорна. ;)
Arhat109-2, а тут выбор-то не очень велик: при работе с html нужно либо очень тщательно все проектировать и программировать (для чего нужна соответствующая квалификация), либо действовать "по-ардуиновски" - т.е. восполнять недостатки умения за счет перехода на более мощную технику.
Собственно, получается, что выбор малинки или чего-либо аналогичного экономически целесообразен, т.к. позволяет существенно сэкономить на квалификации исполнителя.
Вам не кажется, что использование "html" в вопросах автоматизации несколько нелогичным? Почему тогда сразу не ЛИСП или SQL? :)