Ошибка компиляции. Недостаточно оперативной памяти 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? :)