[Из песочницы] Программируемое реле на Ардуино

#include

#include
#include
#include
#include
#define LEFT 0
#define CENTER 1
#define RIGHT 2

#define RelayModesCount 4
#define KeyFirst 2
#define KeyLast 6

LiquidCrystal_I2C lcd (0×27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
RTC_DS1307 RTC; // RTC Modul
DHT dht (7, DHT21); // pin, type
volatile boolean Blinker = true;
volatile long BlinkerTime;
volatile byte ButtonPress[8];
const String RelayModeNames[] = {«OFF», «ON», «Once», «Daily»};

int aKey1 = 0;
int aKey2 = 0;

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.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 (2015, 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 *********************/

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);
Ddate = «Temp » + String (int (dht.readTemperature ())) + «C, Hum » + String (int (dht.readHumidity ())) + »%»;
LcdPrint (3, 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 ()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);
}
}
}

© Geektimes