Уважаемые знатоки. Помогите переделать скеч. Сейчас 4 реле. Нужно 8 реле чтобы включались и выключались по времени. И самое трудное для меня это переделать меню. Может ка кто упростить выкинуть лишнее. Код (C++): #include <EEPROM.h> #include <Wire.h> #include <LiquidCrystal_I2C.h> #include <RTClib.h> #define LEFT 0 #define CENTER 1 #define RIGHT 2 #define RelayModesCount 4 #define KeyFirst 2 #define KeyLast 6 //--------------------- LiquidCrystal_I2C lcd (0x3F, 20, 4); RTC_DS1307 RTC; // RTC Modul volatile boolean Blinker = true; volatile long BlinkerTime; volatile byte ButtonPress[8]; const String RelayModeNames[] = {"OFF", "ON", "Once", "Daily"}; int aKey1 = 0; int aKey2 = 0;
Код (C++): DateTime NowDate; boolean DoBlink(void) { boolean Result = false; long NBlinkerTime = millis(); if (Blinker) { if (NBlinkerTime - BlinkerTime > 200) { digitalWrite(8, HIGH); BlinkerTime = NBlinkerTime; Blinker = false; Result = true; } } else { if (NBlinkerTime - BlinkerTime > 300 ) { digitalWrite(8, LOW); BlinkerTime = NBlinkerTime; Blinker = true; } } return Result; } String BlinkString(String string, byte Cur, byte ItemsCount) { String result = string; byte len = string.length(); if (!Blinker && Cur == ItemsCount) { for (byte i = 0; i < len; i++) result.setCharAt(i, ' '); } return result; } class TMenu { public: byte _ItemsCount; TMenu *Parent; String *MenuName; boolean ItemIsValue; byte CurrentItem; TMenu **Items; String *ItemsName; // byte ItemsCount(void); byte ItemsCount(void) { return _ItemsCount; }; bool AddItem(TMenu *NewItem); virtual void Print(void); void OnKey(byte KeyNum); void ChangeItem(byte value); virtual void Increment(void); virtual void Decrement(void); virtual void OnSet(void); DateTime CheckDateTime(DateTime OldDate, int Increment, byte DatePart); }; class TNoMenu: public TMenu { public: void Print(void); TNoMenu(TMenu *ParentMenu) { MenuName = 0; CurrentItem = 0; _ItemsCount = 0; Parent = ParentMenu; Items = 0; ItemsName = 0; ItemIsValue = false; }; void Increment(void) {}; void Decrement(void) {}; void OnSet(void) {}; }; class TSelectMenu: public TMenu { public: void Print(void); TSelectMenu(TMenu *ParentMenu, String NewName) { MenuName = new String(NewName); CurrentItem = 0; _ItemsCount = 0; Parent = ParentMenu; Items = 0; ItemsName = 0; ItemIsValue = false; }; void Increment(void) {}; void Decrement(void) {}; void OnSet(void) {}; }; class TTimeMenu: public TMenu { public: void Print(void); DateTime *SetDateTime; long OldDateTime; TTimeMenu(TMenu *ParentMenu, String NewName, DateTime *ParamDate) { MenuName = new String(NewName); CurrentItem = 0; _ItemsCount = 6; Parent = ParentMenu; Items = 0; ItemsName = 0; ItemIsValue = true; OldDateTime = millis(); SetDateTime = ParamDate; }; void Increment(void) { *SetDateTime = CheckDateTime (*SetDateTime, 1, CurrentItem); }; void Decrement(void) { *SetDateTime = CheckDateTime (*SetDateTime, -1, CurrentItem); }; void OnSet(void) { RTC.adjust(*SetDateTime); }; void SecondTimer(void) { long TmpDateTime = millis(); if (TmpDateTime - OldDateTime > 1000) { OldDateTime = TmpDateTime; *SetDateTime = *SetDateTime + 1; }; }; }; class TRelayMenu: public TMenu { public: byte RelayNumber; byte RelayMode; // byte Shedule=0; boolean OnceBit; DateTime RelayOn; DateTime RelayOff; TRelayMenu(TMenu *ParentMenu, byte NewNumber, String NewName) { MenuName = new String(NewName); CurrentItem = 0; _ItemsCount = 11; Parent = ParentMenu; Items = 0; ItemsName = 0; ItemIsValue = true, OnceBit = false; RelayNumber = NewNumber; RelayMode = 0; RelayOn = DateTime(2015, 1, 1, 23, 00, 00); RelayOff = DateTime(2015, 1, 1, 07, 00, 00); }; void Print(void); void Increment(void) { if (!CurrentItem) { RelayMode++; if ( RelayMode >= RelayModesCount) RelayMode = 0; } else if (CurrentItem < 6) RelayOn = CheckDateTime (RelayOn, 1, CurrentItem - 1); else RelayOff = CheckDateTime (RelayOff, 1, CurrentItem - 6); }; void Decrement(void) { if (!CurrentItem) { RelayMode--; if ( RelayMode > 127) RelayMode = RelayModesCount - 1; } else if (CurrentItem < 6) RelayOn = CheckDateTime (RelayOn, -1, CurrentItem - 1); else RelayOff = CheckDateTime (RelayOff, -1, CurrentItem - 6); }; boolean CheckDaily(void); void OnSet(void) { //--------------------------------------- byte p_address = RelayNumber * 16; EEPROM.write(p_address, RelayMode); EEPROM.write(p_address + 1, byte(RelayOn.year() - 2000)); EEPROM.write(p_address + 2, byte(RelayOn.month() )); EEPROM.write(p_address + 3, byte(RelayOn.day() )); EEPROM.write(p_address + 4, byte(RelayOn.hour() )); EEPROM.write(p_address + 5, byte(RelayOn.minute() )); EEPROM.write(p_address + 6, byte(RelayOff.year() - 2000)); EEPROM.write(p_address + 7, byte(RelayOff.month() )); EEPROM.write(p_address + 8, byte(RelayOff.day() )); EEPROM.write(p_address + 9, byte(RelayOff.hour() )); EEPROM.write(p_address + 10, byte(RelayOff.minute() )); }; }; //--------------------------------- TMenu *CurrentMenu = 0; TNoMenu *NoMenu = 0; TSelectMenu *SelectMenu; TTimeMenu *TimeMenu; TRelayMenu *RelayMenu[4]; void setup() { NoMenu = new TNoMenu(0); SelectMenu = new TSelectMenu (NoMenu, "NoMenu"); TimeMenu = new TTimeMenu(SelectMenu, "Time Setup", &NowDate); SelectMenu->AddItem(TimeMenu); byte p_address; DateTime DTFlesh; for (int i = 0; i < 4; i++) { // здесь надо добавить загрузку параметров из флеша RelayMenu[i] = new TRelayMenu (SelectMenu, i, "Relay " + String(i + 1)); SelectMenu->AddItem(RelayMenu[i]); p_address = i * 16; RelayMenu[i]->RelayMode = EEPROM.read(p_address); DTFlesh = DateTime(int(EEPROM.read(p_address + 1) + 2000), EEPROM.read(p_address + 2), EEPROM.read(p_address + 3), EEPROM.read(p_address + 4), EEPROM.read(p_address + 5), 0 ); RelayMenu[i]->RelayOn = RelayMenu[i]->CheckDateTime(DTFlesh, 0, 0); DTFlesh = DateTime(int(EEPROM.read(p_address + 6) + 2000), EEPROM.read(p_address + 7), EEPROM.read(p_address + 8), EEPROM.read(p_address + 9), EEPROM.read(p_address + 10), 0 ); RelayMenu[i]->RelayOff = RelayMenu[i]->CheckDateTime(DTFlesh, 0, 0); } for (byte i = KeyFirst; i < KeyLast; i++) { pinMode(i, INPUT); //Keypad 2-«menu» 3-"-" 4-"+" 5-«SET» digitalWrite(i, HIGH); //setup Resistor input2Vcc ButtonPress[i] = true; } pinMode(8, OUTPUT); //LED pinMode(9, OUTPUT); for (byte i = 10; i < 14; i++) { pinMode(i, OUTPUT); // relay i digitalWrite(i, HIGH); } pinMode (A2, INPUT_PULLUP); pinMode (A3, INPUT_PULLUP); //Serial.begin(9600); // Used to type in characters digitalWrite(8, LOW); digitalWrite(9, HIGH); lcd.begin(20, 4); // initialize the lcd for 20 chars 4 lines and turn on backlight RTC.begin(); lcd.init(); lcd.noBacklight(); delay(150); lcd.backlight(); NowDate = RTC.now(); //проверка времени if ( NowDate.year() > 2000 && NowDate.year() < 2114 && NowDate.month() > 0 && NowDate.month() < 13 && NowDate.day() > 0 && NowDate.day() < 32 && NowDate.hour() >= 0 && NowDate.hour() < 24 && NowDate.minute() >= 0 && NowDate.minute() < 60 && NowDate.second() >= 0 && NowDate.second() < 60 ) { CurrentMenu = NoMenu; } else { lcd.setCursor(2, 1); lcd.print("Clock Failure!"); delay(700); RTC.adjust(DateTime(2017, 1, 1, 00, 00, 00)); CurrentMenu = TimeMenu; } } void loop() { /********* KEYPAD BUNCLE, 5 keys, from 2 to 6 *********/ byte NButtonPress[8] = {0, 0, 0, 0, 0, 0, 0, 0}; byte NButtonRelease[8] = {0, 0, 0, 0, 0, 0, 0, 0}; const byte ButtonTry = 3; aKey1 = analogRead (2); aKey2 = analogRead (3); byte aKeyNum = 0; /**************** check for key pressed or released ****************/ for (byte i = 0; i < 3; i++) { delay(15); if (aKey1 < 64) aKeyNum = 2; //AnalogKey 1 = Dig2 else if (aKey1 < 128) aKeyNum = 6; //Analog key 3 = D4 else if (aKey1 < 256) aKeyNum = 4; //key 5=d6 else if (aKey2 < 64) aKeyNum = 1; //key 6 = menu else if (aKey2 < 128) aKeyNum = 3; //analogkey 2 = D3 else if (aKey2 < 256) aKeyNum = 5; //key 4 =d5 else aKeyNum = 0; // no key for (byte j = KeyFirst; j < KeyLast; j++) // Read ports 2...6 { if (digitalRead(j) == LOW || aKeyNum == j) { NButtonPress[j]++; NButtonRelease[j] = 0; } else { NButtonPress [ j ] = 0; NButtonRelease[j]++; delay(5); } } /*************** Do key process ******************/ // byte m; for (byte j = KeyFirst; j < KeyLast; j++) { if (NButtonPress[j] >= ButtonTry && ButtonPress[j] == false) { ButtonPress[j] = true; CurrentMenu->OnKey(j); } else { if (NButtonRelease[j] >= ButtonTry && ButtonPress[j] == true) { ButtonPress[j] = false; } } } /***************** Relay Check *********************/
Код (C++): CurrentMenu->Print(); DoBlink(); } } void LcdPrint(byte string, String str, byte Align) { byte StrTrim1; byte StrTrim2; lcd.setCursor(0, string); //Start at character 0 on line 0 switch (Align) { case RIGHT: break; case CENTER: StrTrim1 = byte((20 - str.length()) / 2); StrTrim2 = 20 - str.length() - StrTrim1; for (byte k = 0; k < StrTrim1; k++) lcd.print(" "); lcd.print(str); for (byte k = 0; k < StrTrim2; k++) lcd.print(" "); break; default: lcd.print(str); StrTrim1 = 20 - str.length(); for (byte k = 0; k < StrTrim1; k++) lcd.print(" "); } } void TNoMenu::Print(void) { NowDate = RTC.now(); String Ddate; Ddate = " R1-" + RelayModeNames[RelayMenu[0]->RelayMode] + " R2-" + RelayModeNames[RelayMenu[1]->RelayMode]; LcdPrint(0, Ddate, CENTER); Ddate = " R3-" + RelayModeNames[RelayMenu[2]->RelayMode] + " R4-" + RelayModeNames[RelayMenu[3]->RelayMode]; LcdPrint(1, Ddate, CENTER); Ddate = String (NowDate.year()) + "/" + String(NowDate.month()) + "/" + String(NowDate.day()) + " " + String (NowDate.hour()) + ":" + String(NowDate.minute()) + ":" + String(NowDate.second()); LcdPrint(2, Ddate, CENTER); RelayCheck(); } void TTimeMenu::Print(void) { SecondTimer(); String Ddate = BlinkString(String((*SetDateTime).year()), CurrentItem, 0) + "/" + BlinkString(String( (*SetDateTime).month()), CurrentItem, 1) + "/" + BlinkString(String((*SetDateTime).day()), CurrentItem, 2) + " "; LcdPrint(1, Ddate, CENTER); Ddate = BlinkString(String ((*SetDateTime).hour()), CurrentItem, 3) + ":" + BlinkString(String((*SetDateTime).minute()), CurrentItem, 4) + ":" + BlinkString(String((*SetDateTime).second()), CurrentItem, 5); LcdPrint(2, Ddate, CENTER); LcdPrint(3, " ", CENTER); RelayCheck(); } void TMenu::OnKey(byte KeyNum) { switch (KeyNum) { case 3: if (ItemIsValue) Decrement(); else ChangeItem(-1); break; case 4: // + if (ItemIsValue) Increment(); else ChangeItem(1); break; case 5: // SET if (ItemIsValue) { OnSet(); ChangeItem(+1); } else // вход в подменю { if (Items && ItemsCount()) { if (CurrentMenu->ItemsCount()) { CurrentMenu = CurrentMenu->Items[CurrentMenu->CurrentItem]; CurrentMenu->CurrentItem = 0; } } } break; default: // 2 -menu if (Parent) CurrentMenu = CurrentMenu->Parent; //(TMenu *) &NoMenu; else { CurrentMenu = SelectMenu; CurrentMenu->CurrentItem = 0; } } } void TMenu::ChangeItem(byte value) { CurrentItem += value; if (CurrentItem > 128) CurrentItem = ItemsCount() - 1; else if (CurrentItem > ItemsCount() - 1) CurrentItem = 0; } boolean TMenu::AddItem(TMenu * NewItem) { if (!Items) Items = new TMenu *[_ItemsCount = 1]; else Items = (TMenu **)realloc((void *)Items, (_ItemsCount = _ItemsCount + 1) * sizeof(void *)); Items[_ItemsCount - 1] = NewItem; } DateTime TMenu::CheckDateTime(DateTime OldDate, int Increment, byte DatePart) { int DTmin[6] = {2000, 1, 1, 0, 0, 0}; int DTmax[6] = {2199, 12, 31, 23, 59, 59}; int DT[6]; int diff; DT[0] = OldDate.year(); DT[1] = OldDate.month(); DT[2] = OldDate.day(); DT[3] = OldDate.hour(); DT[4] = OldDate.minute(); DT[5] = OldDate.second(); DT[DatePart] = DT[DatePart] + Increment; if (DT[1] == 1 || DT[1] == 3 || DT[1] == 5 || DT[1] == 7 || DT[1] == 8 || DT[1] == 10 || DT[1] == 12) DTmax[2] = 31; else if (DT[1] == 2) { if ((DT[0] % 4 == 0 && DT[0] % 100 != 0) || (DT[0] % 400 == 0)) DTmax[2] = 29; else DTmax[2] = 28; } else DTmax[2] = 30; for (byte i = 0; i < 6; i++) { if (DT[i] > DTmax[i]) DT[i] = DTmin[i]; else if (DT[i] < DTmin[i]) DT[i] = DTmax[i]; } return DateTime(DT[0], DT[1], DT[2], DT[3], DT[4], DT[5]); } void TSelectMenu::Print(void) { NowDate = RTC.now(); byte shift = 0; if (CurrentItem > 3) shift = CurrentItem - 3; for (byte i = 0; i < 4; i++) { if ((CurrentItem - shift) == i) //Blinker { LcdPrint(i, ">> " + * (Items[i + shift]->MenuName) + " <<", CENTER); } else LcdPrint(i, *(Items[i + shift]->MenuName), CENTER); } RelayCheck(); } void TRelayMenu::Print(void) { String DData; NowDate = RTC.now(); LcdPrint(0, (*MenuName) + "[" + BlinkString(RelayModeNames[RelayMode], CurrentItem, 0) + "]", CENTER); DData = "On:"; switch (RelayMode) { case 3: //Daily // DData = DData + " "; if (CurrentItem > 0 && CurrentItem < 4) CurrentItem = 4; break; default: DData = DData + BlinkString(String(RelayOn.year(), DEC), CurrentItem, 1) + "/" + BlinkString(String( RelayOn.month(), DEC), CurrentItem, 2) + "/" + BlinkString(String( RelayOn.day(), DEC), CurrentItem, 3); } DData = DData + " " + BlinkString(String (RelayOn.hour(), DEC), CurrentItem, 4) + ":" + BlinkString(String(RelayOn.minute(), DEC), CurrentItem, 5); LcdPrint(1, DData, CENTER); DData = "Off:"; switch (RelayMode) { case 3: //Daily // DData = DData + " "; if (CurrentItem > 5 && CurrentItem < 9) CurrentItem = 9; break; default: DData = DData + BlinkString(String(RelayOff.year(), DEC), CurrentItem, 6) + "/" + BlinkString(String( RelayOff.month(), DEC), CurrentItem, 7) + "/" + BlinkString(String( RelayOff.day(), DEC), CurrentItem, 8); } DData = DData + " " + BlinkString(String (RelayOff.hour(), DEC), CurrentItem, 9) + ":" + BlinkString(String(RelayOff.minute(), DEC), CurrentItem, 10); LcdPrint(2, DData, CENTER); LcdPrint(3, " ", CENTER); } boolean TRelayMenu::CheckDaily(void) { int TimeOn = 60 * int(RelayOn.hour()) + int(RelayOn.minute()); int TimeOff = 60 * int(RelayOff.hour()) + int(RelayOff.minute()); int NowTime = 60 * int(NowDate.hour()) + int(NowDate.minute()); boolean result; // true = время включения больше времени выключения if ( TimeOn > TimeOff ) { if (NowTime <= TimeOff || NowTime >= TimeOn ) result = true; else result = false; } else { if (NowTime <= TimeOff && NowTime >= TimeOn ) result = true; else result = false; }; return result; } void RelayCheck (void) { boolean OnceBitCheck; for (byte i = 0; i < 4; i++) { switch (RelayMenu[i]->RelayMode) { case 1: //relay 0n digitalWrite(i + 10, LOW); break; case 2: //Once; OnceBitCheck = (NowDate.unixtime() > RelayMenu[i]->RelayOn.unixtime() && NowDate.unixtime() < RelayMenu[i]->RelayOff.unixtime()); if (OnceBitCheck) RelayMenu[i]->OnceBit = true; else if (RelayMenu[i]->OnceBit) { RelayMenu[i]->RelayMode = 0; byte p_address = RelayMenu[i]->RelayNumber * 16; EEPROM.write(p_address, RelayMenu[i]->RelayMode); } digitalWrite(i + 10, !OnceBitCheck); break; case 3: //Daily digitalWrite(i + 10, !(RelayMenu[i]->CheckDaily())); break; default: //relay 0ff digitalWrite(i + 10, HIGH); } } } [/code]