Arduinoのプログラム可能なリレー

アイデアは当たり前で、家の負荷を制御するためのコントローラーが必要でした。

1.暖房ボイラー。

2.給水用の貯蔵ボイラー。

3.井戸のポンプ。



ArduinoのXXのテーマに関する多くの興味深い記事を読んで、「Arduinoが欲しい」という考えを頭の中ではっきりと読みました。 コンポーネントとターンキーソリューションのコストを見積もり、Arduinoを実装することの明確な利点を検討しました。



画像



したがって、最小限のプログラム:



1. 4つのリレー、クロック(RTC)、LCDスクリーン。

2.各リレーの動作モード:オン、オフ、毎日のタイマー、1回限りの組み込み。

3.時間とリレーモードを設定するためのコントロールボタン。



家には2レートメーターがあるので、ボイラーは午前中に23から7に水を加熱します。 同様に、暖房:私の考えによれば、3 10のうち2つは夜間に点灯します。 温度制御はまだ標準のリモートコントロールにネイティブです。 予備としての1回限りのインクルージョンはポンプに送られます。たとえば、タンクを設定したり、井戸をポンプでくみ、その後リレーがオフモードになるように、インクルージョンをプログラムします。 主な機能:完成したデバイスが作成され、ボタンで制御され、PCへの接続を必要としません。



もちろん、暖房のために3つの動作モードを作成することをお勧めしますので、将来はコントローラーにすべてを掛けることを望みました:保存するために7から23の日、夜、5..6から7の朝のシャットダウンまでのウォームアップですが、これまでのところ最小プログラムが実装されています。



ハードウェア部分:



製造時のタスクは、できる限り安い製品を入手することでした。そのため、集団農場は可能な限り存在しています。 Aliは、arduino Uno R3用のスターターキット、4つのリレーモジュール、I2C 20 * 4 lcdスクリーン、RTC DS1307 I2Cクロック、Dht21デジタル温度および湿度センサーを注文しました。



これを初めて見たので、習得しなければなりませんでした。 Googleを使用した一般的な概念は、

http://habrahabr.ru/company/masterkit/blog/257747/

http://arduino.ru/Reference



美しい接続図を作成することはできません。 たとえば、コンポーネントのFritzingでは、マイクロコントローラー自体のみです。



リレーとボタンを接続しても問題は発生せず、プルアップ抵抗のみをオンにしました。 これはマニュアルに記載されています。 リンクhttps://arduino-info.wikispaces.com/LCD-Blue-I2C#v3は、LCD画面の接続に役立ちました。 チューニング抵抗器による調整が必要でした。「箱から出してすぐに」画面がまったく焼けなかったため、少し混乱しました。



クロックには、標準スキームhttp://zelectro.cc/RTC_DS1307_arduinoに従って接続されたバッテリーのみが必要でした



クロックをコンピューターと同期しませんでした。 起動時に、日付が2000未満または2100を超えているかどうかがチェックされ、クロック設定メニューが表示されます。



アナログ入力へのいくつかのボタンの接続については、 http: //arduino.net.ua/Arduino_articles/Arduino_proekty/Podkljuchenie%20knopok%20k%20odnomu%20analogovomu%20vhodu/で説明されています。プルアップ抵抗「pinMode(A2、 INPUT_PULLUP); "



コントロールは古典的な「モニター」です:「メニュー」、「+」、「-」、「設定」ボタン。



組み立てプロセス(写真付き)
私は6台のマシンの取り付けプレートを取りました:



画像



モジュールの下に取り付けラックを置きます。



画像



リレー、時計、コントローラーをねじ込みます:



画像



プリンターから、ローラーとスリーブをいくつか取りました。 スリーブは両面テープに接着されています。 別のボードがそれらに取り付けられます。詳細は以下を参照してください。



電源はいくつかのDlinkルーター、5V 2Aから取られましたが、USBケーブルを直接接続して困惑することはありませんでした。



画像



私はプラスチックからスクリーンを取り付けるためのパネルを切り取りました:



画像



画面を設定します。 キーボードの下の取り付けラックで固定-ネジ。 ラックの高さは、シールドカバーがラックに当接し、構造に剛性を与えることを期待して選択されます。 リレーブロックのスリーブは、ボタンが押されたときにボードが押し下げられるのを防ぎます。



画像



最初はボタンをデジタル入力に接続することを計画していましたが、突然モニターからキーボードモジュールを見つけました。これはネイティブモジュール( モニターからのボタンスキーム )として現れました。



キーボードをガスケットを介して両面テープに接着し、ボードを右下のネジの上に持ち上げました。 私は穴を通してマッチでボタンを押します。 理想的には、穴を開けてそこに通常のプッシャーを挿入する必要があります。 寒い冬の夕方に着くかもしれませんが、今では温水用のリレーを導入することが急務でした。



完成したデバイスの写真:

画像

画像



点滅するLEDもあります。





現時点では、リレーは「ノズルで」ハングし、ボイラーを制御し、加熱ボイラーの配線と接触器を設置した後に最終設置が行われます。 もちろん、配線中にブロックの位相も削除する必要があります。 今は時間がありません、屋外の家事をする必要があります。 部品のコストは約2,000ルーブルでした。



ソフトウェア部分:



ソフトウェアの部分は簡単ではありませんでした。時間の90%がメニューの作成に費やされ、使用可能なコードはファームウェアの3番目のバージョンでのみ習得されました。



最初のアプローチは、テストピースからテストパーツへと成長しました。 手続き型プログラミングの典型的な例は開発されていません。 その後の読み取りや編集に役立つ、有効で有効なコードを書くという原則を思い出さなければなりませんでした。



2番目のアプローチは、コードをOOP原則に変換することでした。 基本は特定のTMenuクラスで、そこからメニュー項目が直接継承されました。



要するに。 CurrentMenuポインターには、現在のメニュー項目のアドレスが割り当てられます。クラスの主な要素は、現在の項目がサブメニューまたは変数値であるかどうかを決定するItemIsValueビットと、OnKey()、Increment()、Decrement()、およびPrint()関数です。 メニュークラスには、親メニューへのポインターとポインターの配列も含まれます。 一般に、継承を使用することにより、任意のマルチレベルメニューを作成することができました。原則として、これは動的メニューであり、この実装では初期化中に1回だけ形成されます。 いずれの場合でも、コードは簡単に編集され、メニュー項目が追加されます。 残酷な現実は私を私の場所に置いた。 UNO R3には、このすべての豪華さが欠けています。



3番目のアプローチは、2番目のアプローチを削減することです。 主な違いは1つです-メニュークラスの具象オブジェクトにはサブメニューまたは変数のいずれかが含まれます-編集可能な値は、クラスによってタイプが設定されます。



そのため、クラスが定義されています:

class TMenu { public: byte _ItemsCount; TMenu *Parent; String *MenuName; boolean ItemIsValue; byte CurrentItem; TMenu **Items; String *ItemsName; byte ItemsCount(void) ; 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); };
      
      







クラスには以下が含まれます。

-メニュー項目(サブメニューまたは変数)の数、親メニューへのポインター(ポインターが0の場合、上部に到達します);

-MenuNameはメニュー名前です。

-上記のItemIsValue

-メニュー内のカーソル位置番号( CurrentItem );

- アイテムポインターの配列へのポインター。 サブメニューアドレス メニューに編集可能なアイテムが含まれる場合、この値は0です。

-Print ()関数は、現在のメニュー「CurrentMenu-> Print();」に代わってループサイクルから呼び出されます。これにより、目的のテキストを含む画面が描画されます。

-OnKey関数(バイトKeyNum)は 、接触チャッター抑制ユニットのループサイクルからも呼び出されます。これは、モニターのキーボードデコーダーです。

-関数ChangeItem(バイト値)仮想void Increment(void)仮想void Decrement( void)OnKey()から呼び出され、「+」および「-」ボタンを処理します。 ChangeItem()はメニュー項目の並べ替えIncrement()およびDecrement()は多態性で、現在の変数の値を並べ替えます。

-CheckDateTime関数(DateTime OldDate、int Increment、バイトDatePart)は、入力された日付と時刻をチェックします。 ビスコースの年と28 / 29、30、31の月の日数が認識されます。ロジックに基づいて、現在の日付、+ 1または-1、および日付/時刻の部分インデックス(0年、5秒)が関数に転送されます。



メニューナビゲーションは、オブジェクトのアドレスをCurrentMenuポインターに割り当てることで実装されます。

-CurrentMenu = CurrentMenu->アイテム[CurrentMenu-> CurrentItem]; 選択したメニューに入る

-CurrentMenu = CurrentMenu-> Parent; 前のメニューに移動



仕事の論理:


ループサイクルはキーボードを継続的にポーリングし、リレー設定をチェックし、LEDを点滅させます。



キーボードは基本としてポーリングされ、デジタル入力2〜6(メニュー、-、+、セット)では、アナログポートの値がこれらのコードにカウントされます。

-メニューの外側の「メニュー」ボタンをクリックすると、メニューが呼び出されます;それ以外の場合、メニューが上がります。

-「+」または「-」を押すと、メニュー項目が循環するか、現在のパラメーターが周期的に変更されます。 「 'set」ボタンを押すと、選択したメニューに入るか、変数の値を保存して、次の値の同時選択でフラッシュします。



Chatterはプログラムによって抑制されます。各ボタンには、押したり放したりするためのカウンターが割り当てられます。 調査は15ミリ秒間隔で3回実行されます。 プレスカウンタまたはリリースカウンタは、1ずつ増加するかリセットされます。 このようにして、押すと放す両方のがたつきが認識されます。 リリース状態は、ボタンを押したままの1回の作動で固定されます。



リレー設定では、動作モードがチェックされ、「毎日」モードでは、分単位の正確な時間が入力およびチェックされます。 点灯時間は、消灯時間より長く正しく認識されます。たとえば、23で点灯し、7で消灯します。「オン」モードでは、日付と時刻が設定されます。 便宜上、5番目のボタンを接続し、編集モードで現在の日付と時刻を設定する機能を設定する予定です。



これは簡単です。 小さいクラスの関数は、クラスを宣言するときに原則として宣言され、ヘッダーファイルとライブラリは使用されません。 コードはすでに小さくなっています。



プログラムコード
#include <EEPROM.h>



#include <DHT.h>

#include <Wire.h>

#include <LiquidCrystal_I2C.h>

#include <RTClib.h>

#define LEFT 0

#defineセンター1

#define RIGHT 2



#define RelayModesCount 4

#define KeyFirst 2

#define KeyLast 6



LiquidCrystal_I2C lcd(0x27、2、1、0、4、5、6、7、3、POSITIVE);

RTC_DS1307 RTC; // RTCモジュール

DHT dht(7、DHT21); //ピン、タイプ

volatile boolean Blinker = true;

揮発性の長いBlinkerTime;

揮発性バイトButtonPress [8];

const String RelayModeNames [] = {"OFF"、 "ON"、 "Once"、 "Daily"};



int aKey1 = 0;

int aKey2 = 0;



DateTime NowDate;



ブールDoBlink(void)

{

boolean Result = false;

long NBlinkerTime = millis();

if(ブリンカー)

{

if(NBlinkerTime-BlinkerTime> 200)

{

digitalWrite(8、高);

BlinkerTime = NBlinkerTime;

点滅= false;

結果= true;

}

}

他に

{

if(NBlinkerTime-BlinkerTime> 300)

{

digitalWrite(8、LOW);

BlinkerTime = NBlinkerTime;

点滅= true;

}



}

結果を返す;

}

文字列BlinkString(文字列、バイトCur、バイトItemsCount)

{

文字列結果=文字列;

バイトlen = string.length();

if(!Blinker && Cur == ItemsCount)

{

for(byte i = 0; i <len; i ++)result.setCharAt(i、 '');

}

結果を返す;

}



/ **************************************************** ******************************************************** ***** /

/ ************************************クラス宣言************* ********************************** /

/ **************************************************** *************************************************** ***** /



TMenuクラス

{

公開:

byte _ItemsCount;

TMenu *親;

文字列* MenuName;

ブールItemIsValue;

バイトCurrentItem;



TMenu **アイテム;

文字列* ItemsName;

//バイトItemsCount(void);

バイトItemsCount(void){

return _ItemsCount;

};

bool AddItem(TMenu * NewItem);



仮想ボイドプリント(ボイド);

void OnKey(バイトKeyNum);

void ChangeItem(バイト値);



仮想void Increment(void);

仮想ボイド減少(ボイド);



仮想void OnSet(void);

DateTime CheckDateTime(DateTime OldDate、int Increment、バイトDatePart);

};



TNoMenuクラス:パブリックTMenu

{

公開:

void Print(void);

TNoMenu(TMenu * ParentMenu){

MenuName = 0;

CurrentItem = 0;

_ItemsCount = 0;

親= ParentMenu;

アイテム= 0;

ItemsName = 0;

ItemIsValue = false;

};

void Increment(void){};

void Decrement(void){};

void OnSet(void){};



};



TSelectMenuクラス:パブリックTMenu

{

公開:

void Print(void);

TSelectMenu(TMenu * ParentMenu、String NewName){

MenuName = new String(NewName);

CurrentItem = 0;

_ItemsCount = 0;

親= ParentMenu;

アイテム= 0;

ItemsName = 0;

ItemIsValue = false;

};

void Increment(void){};

void Decrement(void){};

void OnSet(void){};

};



TTimeMenuクラス:public TMenu

{

公開:

void Print(void);

DateTime * SetDateTime;

long OldDateTime;

TTimeMenu(TMenu * ParentMenu、String NewName、DateTime * ParamDate){

MenuName = new String(NewName);

CurrentItem = 0; _ItemsCount = 6; 親= ParentMenu; アイテム= 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;

};

};

};

TRelayMenuクラス:public TMenu

{

公開:

バイトRelayNumber;

バイト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; 親= ParentMenu; アイテム= 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);

};



ブールCheckDaily(void);



void OnSet(void){

/////ここで、リレーをメモリに書き込む必要があります



バイトp_address = RelayNumber * 16;

EEPROM.write(p_address、RelayMode);



EEPROM.write(p_address + 1、バイト(RelayOn.year()-2000));

EEPROM.write(p_address + 2、バイト(RelayOn.month()));

EEPROM.write(p_address + 3、バイト(RelayOn.day()));

EEPROM.write(p_address + 4、バイト(RelayOn.hour()));

EEPROM.write(p_address + 5、バイト(RelayOn.minute()));



EEPROM.write(p_address + 6、バイト(RelayOff.year()-2000));

EEPROM.write(p_address + 7、バイト(RelayOff.month()));

EEPROM.write(p_address + 8、バイト(RelayOff.day()));

EEPROM.write(p_address + 9、バイト(RelayOff.hour()));

EEPROM.write(p_address + 10、バイト(RelayOff.minute()));

};

};



/ **************************************************** ******************************************************** ***** /

/ **********************************クラス宣言の終了************** ***************** ....

/ *************************************************** *************************************************** ***** /



TMenu * CurrentMenu = 0;

TNoMenu * NoMenu = 0;

TSelectMenu * SelectMenu;

TTimeMenu * TimeMenu;



TRelayMenu * RelayMenu [4];



/ **************************************************** *************************************************** ******************************************* /

/ **************************************************** *************************************************** ******************************************* /

/ **************************************************** *************************************************** ******************************************* /

ボイド設定()

{

NoMenu = new TNoMenu(0);

SelectMenu = new TSelectMenu(NoMenu、 "NoMenu");

TimeMenu = new TTimeMenu(SelectMenu、 "Time Setup"、およびNowDate);



SelectMenu-> AddItem(TimeMenu);



バイトp_address;

DateTime DTFlesh;

for(int i = 0; i <4; i ++)

{

//ここで、フラッシュからのパラメータのロードを追加する必要があります

RelayMenu [i] =新しいTRelayMenu(SelectMenu、i、「Relay」+文字列(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(バイトi = KeyFirst; i <KeyLast; i ++)

{

pinMode(i、INPUT); //キーパッド2- "menu" 3-"-" 4-"+" 5- "SET"

digitalWrite(i、HIGH); //抵抗入力2Vccをセットアップ

ButtonPress [i] = true;

}

pinMode(8、出力); // LED

pinMode(9、出力);

for(バイトi = 10; i <14; i ++)

{

pinMode(i、OUTPUT); //リレーi

digitalWrite(i、HIGH);

}



pinMode(A2、INPUT_PULLUP);

pinMode(A3、INPUT_PULLUP);



Serial.begin(9600); //文字の入力に使用

digitalWrite(8、LOW);

digitalWrite(9、高);



lcd.begin(20、4); // 20文字4行のlcdを初期化し、バックライトをオンにします

RTC.begin();



lcd.noBacklight();

遅延(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;

}

他に

{

lcd.setCursor(2、1);

lcd.print( "Clock Failure!");

遅延(700);

RTC.adjust(DateTime(2015、1、1、00、00、00));

CurrentMenu = TimeMenu;

}



}



ボイドループ()

{

/ ********* KEYPAD BUNCLE、5キー、2〜6 ********* /

バイトNButtonPress [8] = {0、0、0、0、0、0、0、0、0};

バイトNButtonRelease [8] = {0、0、0、0、0、0、0、0};

const byte ButtonTry = 3;



aKey1 = analogRead(2);

aKey2 = analogRead(3);

バイトaKeyNum = 0;



/ ****************キーが押されたか、離されたかを確認***************** /

for(バイトi = 0; i <3; i ++)

{

遅延(15);

if(aKey1 <64)aKeyNum = 2; // AnalogKey 1 = Dig2

else if(aKey1 <128)aKeyNum = 6; //アナログキー3 = D4

else if(aKey1 <256)aKeyNum = 4; //キー5 = d6

else if(aKey2 <64)aKeyNum = 1; //キー6 =メニュー

else if(aKey2 <128)aKeyNum = 3; // analogkey 2 = D3

else if(aKey2 <256)aKeyNum = 5; //キー4 = d5

それ以外の場合、aKeyNum = 0; //キーなし



for(byte j = KeyFirst; j <KeyLast; j ++)//読み取りポート2 ... 6

{

if(digitalRead(j)== LOW || aKeyNum == j)

{

NButtonPress [j] ++;

NButtonRelease [j] = 0;

}

他に

{

NButtonPress [j] = 0;

NButtonRelease [j] ++;

遅延(5);

}

}



}

/ ***************キープロセスを実行******************* /

//バイトm;



for(バイトj = KeyFirst; j <KeyLast; j ++)

{

if(NButtonPress [j]> = ButtonTry && ButtonPress [j] == false)

{

ButtonPress [j] = true;

CurrentMenu-> OnKey(j);

}

他に

{

if(NButtonRelease [j]> = ButtonTry && ButtonPress [j] == true)

{

ButtonPress [j] = false;

}

}

}

/ *****************リレーチェック********************* /



CurrentMenu-> Print();

DoBlink();

}



void LcdPrint(バイト文字列、文字列str、バイト整列)

{

バイトStrTrim1;

バイトStrTrim2;

lcd.setCursor(0、文字列); //行0の文字0から開始

スイッチ(位置合わせ)

{

ケース右:



休憩;



ケースセンター:

StrTrim1 = byte((20-str.length())/ 2);

StrTrim2 = 20-str.length()-StrTrim1;

for(byte k = 0; k <StrTrim1; k ++)lcd.print( "");

lcd.print(str);

for(バイトk = 0; k <StrTrim2; k ++)lcd.print( "");

休憩;



デフォルト:

lcd.print(str);

StrTrim1 = 20-str.length();

for(byte k = 0; k <StrTrim1; k ++)lcd.print( "");

}

}



void TNoMenu :: Print(void)

{

NowDate = RTC.now();

文字列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())+ " : "+文字列(NowDate.minute())+": "+文字列(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();

文字列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(バイトKeyNum)

{

スイッチ(KeyNum)

{

case 3://-if(ItemIsValue)Decrement();

その他のChangeItem(-1);

休憩;

ケース4:// +

if(ItemIsValue)Increment();

その他のChangeItem(1);

休憩;

case 5:// SET

if(ItemIsValue)

{

発症();

ChangeItem(+1);

}

それ以外の場合//サブメニューに入る

{

if(Items && ItemsCount())

{

if(CurrentMenu-> ItemsCount())

{

CurrentMenu = CurrentMenu->アイテム[CurrentMenu-> CurrentItem];

CurrentMenu-> CurrentItem = 0;

}

}

}

休憩;

デフォルト:// 2 -menu

if(親)CurrentMenu = CurrentMenu->親; //(TMenu *)&NoMenu;

他に

{

CurrentMenu = SelectMenu;

CurrentMenu-> CurrentItem = 0;

}

}

}



void TMenu :: ChangeItem(バイト値)

{

CurrentItem + = value;

if(CurrentItem> 128)CurrentItem = ItemsCount()-1;

else if(CurrentItem> ItemsCount()-1)CurrentItem = 0;

}



ブールTMenu :: AddItem(TMenu * NewItem)

{

if(!Items)Items = new TMenu * [_ ItemsCount = 1];

else Items =(TMenu **)realloc((void *)Items、(_ItemsCount = _ItemsCount + 1)* sizeof(void *));

アイテム[_ItemsCount-1] = NewItem;

}



DateTime TMenu :: CheckDateTime(DateTime OldDate、int Increment、バイト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] +インクリメント;



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;

それ以外の場合、DTmax [2] = 28。

}

それ以外の場合、DTmax [2] = 30;



for(バイト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();

バイトシフト= 0;

if(CurrentItem> 3)shift = CurrentItem-3;

for(バイトi = 0; i <4; i ++)

{

if((CurrentItem-shift)== i)// && Blinker)

{

LcdPrint(i、 ">>" + *(アイテム[i + shift]-> MenuName)+ "<<"、CENTER);

}

else LcdPrint(i、*(Items [i + shift]-> MenuName)、CENTER);

}

RelayCheck();

}



void TRelayMenu ::印刷(void)

{



文字列DData;

NowDate = RTC.now();

LcdPrint(0、(* MenuName)+ "[" + BlinkString(RelayModeNames [RelayMode]、CurrentItem、0)+ "]"、CENTER);

DData = "On:";

スイッチ(RelayMode)

{

ケース3://毎日

// DData = DData + "";

if(CurrentItem> 0 && CurrentItem <4)CurrentItem = 4;

休憩;

デフォルト:

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:";

スイッチ(RelayMode)

{

ケース3://毎日

// DData = DData + "";

if(CurrentItem> 5 && CurrentItem <9)CurrentItem = 9;

休憩;

デフォルト:

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);

}



ブール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());

ブール結果 // true =オフ時間より長いオン時間

if(TimeOn> TimeOff)

{

if(NowTime <= TimeOff || NowTime> = TimeOn)result = true;

else result = false;

}

他に

{

if(NowTime <= TimeOff && NowTime> = TimeOn)result = true;

else result = false;

};

結果を返す;



}



void RelayCheck(ボイド)

{

boolean OnceBitCheck;

for(バイトi = 0; i <4; i ++)

{

スイッチ(RelayMenu [i]-> RelayMode)

{

case 1://リレー0n

digitalWrite(i + 10、LOW);



休憩;

case 2://一度;

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;

バイトp_address = RelayMenu [i]-> RelayNumber * 16;

EEPROM.write(p_address、RelayMenu [i]-> RelayMode);

}

digitalWrite(i + 10 ,! OnceBitCheck);

休憩;

ケース3://毎日

digitalWrite(i + 10 ,!(RelayMenu [i]-> CheckDaily()));

休憩;

デフォルト://リレー0ff

digitalWrite(i + 10、HIGH);

}

}

}





これは、トグルスイッチをクリックすることを忘れずに、朝7時に起きて23時に寝る必要がなくなるような仕事です。 私は工業規格のふりをしません。コードにデータをカプセル化しませんでした。グローバル変数も残しました。



時計の精度には懸念がありましたが、これまでのところ、大きな逸脱に気付きませんでした。



ご清聴ありがとうございました。



All Articles