Не хватает памяти на про мини. Должно быть подключено 4 сервы, DS1307, экран 1602 по I2C, но хватило только на 3 сервы. Работа с таймером,выбор режимов работы. Каким способом лучше соединить ардуины, чтобы разделить работу сервоприводов? Или лучше использовать ардуину с бОльшим объемом памяти?
Из той периферии, что написана - памяти должно хватать с запасом. Значит, дело в скетче - оптимизировать скетч - освободится память Где код?
Бегло просмотрел код, не знаю, что там за "OneButton.h". Предварительный вердикт - код поддаётся сильной оптимизации, можно сократить объём занимаемой оперативки достаточно сильно. Например, вместо bool для каждого флага юзать битовые поля, вместо bool для каждого дня недели - также битовые поля, прошерстить размерность всех переменных и оставить только такую размерность, которая на самом деле нужна (возможно, где-то можно обойтись двухбайтовыми переменными, вместо четырёхбайтовых). Выкинуть жирные библиотеки типа DS1307 и самому написать код работы с часами. Ну и т.д. В общем, работать есть над чем. Но в том виде, в котором код сейчас - он трудноподдерживаем и малосопровождаем, к сожалению.
@issida, если я правильно понял не хватает SRAM , вы четыре переменных servoХ_hmsv объявляете как long, а используете их как byte Код (C++): //время срабатывания первого сервопривода long servo1_hmsv[30]; .................................... for (byte s = 0;s < 30; s++){ //считывание с 0 по 29 eeprom для расписания сервы1 servo1_hmsv[s]=EEPROM.read(s); .................................... unsigned long servo1timeon = ((servo1_hmsv[3*a]*3600)+(servo1_hmsv[(3*a)+1]*60)); //пересчет времени в секунды сервы 1 }
Собственно, вот я о чём: Код (C++): int servo2_week [7];//первое значение - понедельник, последнее - воскресенье Имеем 14 занятых байт для проверки, в какой день недели срабатывать. Четырнадцать, Карл! Вместо одного: Код (C++): byte servo2_week; if(servo2_week & 1) // работаем в понедельник if(servo2_week & 2) // работаем во вторник if(servo2_week & 4) // работаем в среду if(servo2_week & 8) // работаем в четверг if(servo2_week & 16) // работаем в пятницу if(servo2_week & 32) // работаем в субботу if(servo2_week & 64) // работаем в воскресенье Или, если библиотека возвращает dayOfTheWeekначиная с 0 (т.е. 0 - это понедельник): Код (C++): if( servo2_week & (1 << myTime.dayOfTheWeek()) )
Когда ставил byte или int считало бессмыслицу, появляются отрицательные значения или вовсе не то, что нужно. И это сбивало с толку. Код (C++): byte servo1_hmsv[30]; ---------------------- for (byte a = 0; a < 10; a++){ long h = servo1_hmsv[3*a]; int m = servo1_hmsv[(3*a)+1]; long servo1timeon = (h*3600)+(m*60); //пересчет времени в секунды сервы 1 так немного сэкономило памяти. "OneButton.h" - обработка нажатий на кнопку, одиночное, двойное, длинное С днями недели и битовыми полями таким способом не понял . С кнопок устанавливаются дни, когда должно срабатывать, и в процессе должна быть возможность изменения. Исправил на byte servo2_week
И что мешает битовым полям изменяться, не подскажете? С кнопок сколько угодно можно устанавливать дни, просто записывать флаг для в нужный бит, и всё. Например, если нажата кнопка "3" (в среду работать), то запись Код (C++): dayMask |= 1 << 3; установит третий бит в байте. Проверить, установлен ли этот бит (т.е., работаем ли мы в среду), можно тоже крайне просто: Код (C++): if(dayMask & (1 << 3)) { // Урра, мы работаем в среду! } Просто у вас аж 14 байт используется для маски дней недели, когда достаточно всего лишь одного. Итого, на одну серву экономия - 13 байт оперативы, немало, знаете ли.
@issida, еще замечания Код (C++): unsigned long utime = myTime.unixtime(); //сохраняем в переменную - время в формате UNIX utime %= 86400; //Сохраняем в этой же переменной остаток деления на кол-во секнд в сутках, //Это дает количество секунд с начала текущих суток myTime.unixtime() - даст количество секунд прошедших с 1970 года Код (C++): unsigned long z = EEPROM.read(160);//считывание последних часов с памяти unsigned long x = EEPROM.read(161);//считывание последних минут с памяти unsigned long time_last_1 = (z*3600)+(x*60); //перевод в секунды запишите проще, компилятор сам разберется где хранить временную переменную Код (C++): //считывание последних часов и минут с памяти и перевод в секунды unsigned long time_last_1 = (EEPROM.read(160)*3600)+(EEPROM.read(161)*60); Я у вас не увидел начальных установок EEPROM, EEPROM.read(s) вернет 255, если в ячейку ранее не занесли значение. Я бы занес значения по умолчанию в PROGMEM Код (C++): static const uint8_t servo1_hmsv_default[30] PROGMEM = { 08,00,08,30,09,00,09,30,............16,00,16,30 }; и если EEPROM.read(s) вернет невалидные значения, использовал значения по умолчанию (или предупреждал пользователя, или как-то что-то делал)
Спасибо за подсказку. Сделал, заработало Код (C++): servo11_week ^= 1 << trash; с возможностью инвертирования. ОЗУ убавилось, но выросла флеш-память, с которой у меня напряг. для одной сервы на 40-50 байт кажется. Не работает подомным методом Код (C++): byte a = 15; byte b = 35; void setup() { Serial.begin(9600); long c = (a*3600)+(b*60); Serial.println(c); int d=a; int e=b; long f = (d*3600)+(e*60); Serial.println(f); long g = a; long h = b; long i = (g*3600)+(h*60); Serial.println(i); } void loop() {} Простой пример для наглядности. Ввполняет одинаковые операции, а ответы разные. С первой записью в EEPROM не критично, когда код будет готов запишу нужные значения в нужные ячейки
@issida, 3600L записывать как long Код (C++): //считывание последних часов и минут с памяти и перевод в секунды unsigned long time_last_1 = (EEPROM.read(160)*3600L)+(EEPROM.read(161)*60);
Такой кусок ода можно запихнуть в класс? Слишком много одинаковых частей для четырех серв Код (C++): //--------------АВТОМАТИЧЕСКИЙ РЕЖИМ СЕРВЫ1---------------- if(servo1_week[dayOfWeek-1]==1 && servo1_always_on){ //если совпадает день недели и серва должна работать for (byte a = 0; a < 10; a++){ unsigned long servo1timeon = (servo1_hmsv[3*a]*3600L)+(servo1_hmsv[(3*a)+1]*60); //пересчет времени в секунды сервы 1 if (utime==servo1timeon ){ servo1_setvol= servo1_hmsv[(3*a)+2]; //запоминаем объем as1=100;// запуск режима } } } switch (as1) { case 100: if(servo1_late && servo1_on){//если не работает никакой из режимов servo1_on=false;//ручной режим не запустится if(!servo1_on){ as1=1; break; } } case 1: if(utime-prev_mixer_time>120)as1=2;//если с момента остановки миксера прошло 120сек, входим в режим включения миксера if(utime-prev_mixer_time<120)as1=4; //если меньше 120сек, то минуем этот режим break; case 2: analogWrite(11, mixer_speed);// запуск миксера s1t=utime;//запоминаем время включения миксера as1=3;//переход в следующий break; case 3: if(utime-s1t>=mixer_time){ //если мисер отработал заданное время analogWrite(11, 0);//выключаем prev_mixer_time = utime;//запоминаем время выключения миксера } if(utime-s1t>=mixer_time+5){//ждем 5 сек as1=6;//переходим в следующий } break; break; case 4: s1t=utime; as1=5; break; case 5: if(utime-s1t>1)as1=6; break; case 6: servo1_current_vol = servo1_current_vol + servo1_setvol; // вычисление текущего угла сервы 1 as1=7; break; case 7: if(servo1_current_vol<180 && servo1_current_vol>=0){//если текущий угол находится от 0 до 179 as1=11;//переходим в 11 break; } if(servo1_current_vol>=180){//если текущий угол от 180 и больше s1t=utime;//запоминаем это время as1=8; //переходим в 8 break; } case 8: if(utime-s1t>=1){//ждем одну секунду и выполняем servo1_current_vol=servo1_current_vol-180;// вычисляем нужный угол myservo1.attach(3); myservo1.write(180);//поворачиваем в 180 as1=9; } break; case 9: if(utime-s1t>=3){//ждем myservo1.write(0);//поворачиваем в 0 as1=10; } break; case 10: if(utime-s1t>=5){//ждем as1=7;// переходим в 5 } break; case 11: myservo1.attach(3);//включаем серву myservo1.write(servo1_current_vol);//записывем текущий угол s1t=utime;//запоминаем время as1=12; break; case 12: if(utime-s1t>=2){//ждем 2 сек myservo1.detach();//отключаем серву servo1_on=true; EEPROM.update(160, hour ); EEPROM.update(161, minute ); as1=0; //выход break; } }
Любой кусок кода можно запихнуть в класс Первый признак для введения слоя абстракции, как вы правильно заметили - наличие дублирующегося кода. Самого корёжит в своих исходниках такое, если честно Иногда забиваю до лучших времён, когда жать в подмышках не начинает. Но чаще, конечно - лучше сразу разобраться