Ошибка компиляции. Недостаточно оперативной памяти Arduino NANO

ergeykl
Offline
Зарегистрирован: 20.04.2017

Друзья приветствую!

Познакомился недавно с ардуинками и мой мозг тут же начал генерацию проектов на базе этого контроллера.

Взявшись за проект управления теплофикационными системами уперся в проблему нехватки памяти (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 &deg;C</td>" 
  "<td>$S &deg;C</td>"
  "<td>$S &deg;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 &deg;C</td>"), TargetTemp1);                    // Вывод инф из ячейки с "пределом" темп-ры (Тн.в.)
      bfill.emit_p(PSTR("<td>$D &deg;C</td>"), TargetTemp2);                    // Вывод инф из ячейки с "пределом" темп-ры (Т2)
      bfill.emit_p(PSTR("<td>$D &deg;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; 
}

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

ergeykl, конкретных советов у мея нет, могу поделиться только общими мыслями.

Мысль 1-я: У Ардуино вообще как-то маловато памяти для И-нета. А Вы еще и выбрали самую убогонькую модель с 2к памяти. В Меге 2560 или Due все как-то немного попросторнее.

Мысль 2-я: коль памяти для буферов не хватает, может, вообще обойтись без буферов? На выход передавать, сразу читая из PROGMEM по одному символу, и точно также парсить сразу входящий поток, не откладывая его в буфер.

ergeykl
Offline
Зарегистрирован: 20.04.2017

Читал Вашу 2-ю мысль с улыбкой, потому что для меня это как читать китайские символы, ничего непонятно))

Самое печальное, что мой мозг генерирует безумное количество "проектов" по автоматизации различных инженерных систем, а выполнить я их сам не могу, потому как примеров нет как и опытного программиста под рукой. Как собака, всё понимаю, но сказать не могу.

Сегодня нарвался на esp8266, все его величают "Убийцей Ардуино" У самого есть esp-01, но нет usb ttl конвертора, и ещё заказал esp-12 который nodemcu.

Хотел поинтересоваться, с тем что 12-ый у него там 32кб памяти и если его прошить под Arduino IDE я смогу запилить подобную систему?

Araris
Offline
Зарегистрирован: 09.11.2012

ergeykl пишет:

мой мозг генерирует безумное количество "проектов" по автоматизации различных инженерных систем

Прелесть какая )))

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Да не, это такой "вирусный маркетинг" .. ща пофлудят слегонца, а потом начнут заяснять что надо всем и срочно переходить на СТМ или Клюбничку с Малинкой .. закупил попкорна. ;)

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Arhat109-2, а тут выбор-то не очень велик: при работе с html нужно либо очень тщательно все проектировать и программировать (для чего нужна соответствующая квалификация), либо действовать "по-ардуиновски" - т.е. восполнять недостатки умения за счет перехода на более мощную технику.

Собственно, получается, что выбор малинки или чего-либо аналогичного экономически целесообразен, т.к. позволяет существенно сэкономить на квалификации исполнителя.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Вам не кажется, что использование "html" в вопросах автоматизации несколько нелогичным? Почему тогда сразу не ЛИСП или SQL? :)