AquastorozhをZ-Waveのスマートホームに接続する





昨年、Aquastorozhからの漏れが検出されたときに、クレーンの緊急遮断用の機器を購入しました。 長い間、私はそれを置くことができませんでした。 Z-Waveネットワークに統合してgidrolockアナログを入手するというアイデアがありましたが、バッテリーで動作します。 最後に、手が届きました...



Aquastorozhは、プラグインタップと漏れセンサーを備えたベースです。 この複合体は、アダプターを介した220 Vネットワークとバッテリーの両方で機能します。 開発者は、「スマートホーム」のシステムに接続する機能を提供しています。 イーサネットアウトレットの1組の接点を閉じることにより、タップを開き、もう1組を閉じることができます。 漏れが検出されると、リレー接点が1秒間閉じます。 ボードにははんだ付けされていないUARTコネクタがありますが、この記事では、文書化された機能の実装について説明します。



開発タスク





ZUNoシールドに基づいて行うことにしました。 圧力シール付きの密閉された筐体に納品され、ボード上に端子台があり、バッテリーと追加の電子部品を取り付けるための空きスペースがあります。 Arduinoも同様です。



イーサネットコネクタ







閉鎖可能なワイヤの1つは、「アクアウォッチマン」の「アース」です。 ZUNoとAqua-Watchの土地を組み合わせて、GPIO ZUNoから直接クレーンを制御できます。 だから私はやった。 しかし、ZUNo障害(たとえば、バッテリーが切れた)の場合、「Aquastorozh」制御ラインに「ゼロ」が供給され、周期的に再起動し始めます。 このような接続オプションはシステム全体の信頼性に大きく影響するため、回路が少し複雑になったため、2つのリードリレーに切り替えて、Aquastorozhからガルバニック絶縁を提供しました。 リレーはオンのときに約7 mAを消費します。 クレーンを切り替えるには、1つのリレーを1秒間オンにする必要がありますが、これはまったく問題ありません。 バッテリーの充電は数千回の切り替えに十分でした。 (今、私の手に電磁パルスシングルコイルリレーがあります。それらを切り替えるには、エネルギー効率がはるかに高い1 msパルスを適用する必要があります。しかし、リレーには4つのトランジスタと2つの入力/出力レッグが必要です)。



Z波で眠る



Z-Waveデバイスがどのようにスリープするかと、結果として生じる問題について少しお話します。

Z-waveデバイスは休止状態になるか、頻繁に目覚めます。 睡眠装置は最もエネルギー効率が良いですが、コマンドを送信することはできません(私の場合、クレーンを切り替えます)。 2番目のタイプは私に合っています。 FLiRSデバイス-頻繁にリスニングするルーティングスレーブ。 この動作モード用に構成されたデバイスは毎秒起動し、短時間コントローラーから完全に起動する信号を受信しないと、スリープ状態になります。 例:クレーンを開くコマンドを送信します。 コントローラーは、デバイスが頻繁にリッスンしていることを理解し、1秒以内に特別な短いパケット(ウェイクアップビーム)を送信して、ネットワーク上のすべてのFLIRSデバイスがウェイクアップするようにします。 デバイスがこのパケットを受け入れるとすぐに、デバイスはウェイクアップし、コマンドを受け入れる準備ができているというレポートを送信します。 タップを閉じるコマンドを取得します。 再び眠りに落ちる。 そのため、デバイスが制御されるたびに。 欠点は、デバイスがコントローラーによるブロードキャストの終了時と開始時の両方でウェイクアップビームを受信できることです。 コントローラーは約1秒間送信します。 最悪の場合、デバイスはこのニュースレターの最初に起動し、コマンドが到着するまでほぼ1秒待機します。 しかし、タップを開閉する必要がないことが多いため、これは重大な欠点ではありません。



実装



ZUNo Shieldには小さなブレッドボードがあり、その上に必要なコンポーネントを配置できます。







回路には、制御用の2つのリレーと2つのトランジスタが含まれています。 簡単な小さなスケッチ。







エネルギー消費について一言。



ZUNoシールドには、RS-485プロトコル用のドライバーチップと、One Wireプロトコル用の下部ブロックのピン「11」用のプルアップ抵抗が含まれています。 これらのコンポーネントを削除した後、主な消費者はZUNoのままです。







スリープモードでの消費量は約5〜10μAで、アクティブモードでは最大60 mA(リレーがアクティブで、ZUNaが送信中)です。



異なる動作モードでの消費電流のオシログラム



現在の軸の方向は上から下です。



デバイスはコマンドを待っています:







ほぼ毎秒、デバイスが起動し、ウェイクアップビームが到着したかどうかを確認する短いピークが表示されます。



デバイスはコマンドを受信しました:







最初に、デバイスは目覚め、ウェイクアップビームを受信し、コマンドを待機し(0から1秒まで)、クレーンを制御するコマンドが1秒間対応するリレーをオンにします(この段階では、脚を現在の状態に保ちながらコントローラーをスリープ状態にする必要がありますが、私は怖くて怠zyでした)、残りの時間はチップの内部動作に費やされ、その後ZUNoは眠りに落ちます。 タップによる開閉の1回の操作につき合計で約3.5秒。 ひどく長いですが、そのような操作が非常にまれに実行されるという事実のために、最適化は無視できます。 はい、Arduino ideのスケッチはこの小さなマイクロコントローラで「投げる」ことのほんの一部であり、メーカーが好奇心から確実に隠しているものであるため、ほとんど意味がありません。



Aquastorozhの接続図







おわりに



Aquastorozhを既存のZ-Waveネットワークにかなり正確に追加することが判明しました。 主な欠点は、Aquastorozhからのフィードバックがないことです。 この段階では、ZUNoライブラリの新しいバージョンを待っています。このバージョンでは、ZUNoが正常にスリープできないバグが修正されるため、Aquastorozhがインストールおよび接続された写真の代わりに、デバッグプロセスの写真があります。







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



スケッチ
//#define _DEBUG #define OPEN_PIN 11 #define CLOSE_PIN 12 #define LEAK_PIN 19 #define INT1 18 uint8_t valve_action = 0; #ifdef _DEBUG uint8_t const METER1_PIN = 8; #else uint8_t const METER1_PIN = 7; #endif #define METER2_PIN 8 #include "EEPROM.h" #define MAGIC_VALUE 42 #define ADDR_ACTION 1 #define CH_METER_1 4 #define CH_METER_2 8 #define CNT_ON_OFF_CICL 12 // Global variables byte pin7SwitchBinaryState = 0; DWORD eeprom_buf = 0; #define LEAK_CHANNEL 3 DWORD meter_cnt1; DWORD meter_cnt2; #define ADR_MET1 4 #define ADR_MET2 5 uint8_t alarm_clr = LOW; // Z-Wave channels ZUNO_SETUP_CHANNELS( ZUNO_SWITCH_BINARY(pin7SwitchBinaryGetter, pin7SwitchBinarySetter), ZUNO_SWITCH_BINARY(alarmGetter, alarmSetter), ZUNO_SENSOR_BINARY(ZUNO_SENSOR_BINARY_TYPE_WATER, getterSensorBinary), ZUNO_METER(ZUNO_METER_TYPE_WATER, METER_RESET_ENABLE , ZUNO_METER_WATER_SCALE_PULSECOUNT, 4, 0, getterMETER1, resetterMETER1), ZUNO_METER(ZUNO_METER_TYPE_WATER, METER_RESET_ENABLE , ZUNO_METER_WATER_SCALE_PULSECOUNT, 4, 0, getterMETER2, resetterMETER2) ); ZUNO_SETUP_BATTERY_LEVELS(2700, 3300); ZUNO_SETUP_SLEEPING_MODE(ZUNO_SLEEPING_MODE_FREQUENTLY_AWAKE); void close_water() { #ifdef _DEBUG Serial1.println("close"); #endif digitalWrite(CLOSE_PIN, HIGH); delay(1000); digitalWrite(CLOSE_PIN, LOW); //delay(1000); } void open_water() { #ifdef _DEBUG Serial1.println("open"); #endif digitalWrite(OPEN_PIN, HIGH); delay(1000); digitalWrite(OPEN_PIN, LOW); //delay(1000); } #define LEAK_DETECTED LOW #define LEAK_END HIGH #define ADDR_LEAK_ST_LAST 2 #define ADR_B1_F 3 #define ADR_B2_F 4 #define NZ_ADR_LEAK 5 uint8_t last_leak_st; #define EEPROM_MAGIC 0x11223342 #define EEPROM_ADR_MAGIC 0 void setup() { #ifdef _DEBUG Serial1.begin(9600); Serial1.println("serial init"); #else pinMode(METER1_PIN, INPUT); pinMode(METER2_PIN, INPUT); #endif pinMode(CLOSE_PIN, OUTPUT); pinMode(OPEN_PIN, OUTPUT); pinMode(LEAK_PIN, INPUT_PULLUP); pinMode(INT1, INPUT_PULLUP); digitalWrite(CLOSE_PIN, LOW); digitalWrite(OPEN_PIN, LOW); byte n; NZRAM.get(0x0, &n, 1); if (n == MAGIC_VALUE) { // correct magic value after wake up from sleep mode // trust NZRAM data } else { // incorrect magic, first boot after battery insert ot rebooted due to low battery // initialize NZRAM magic n = MAGIC_VALUE; NZRAM.put(0x0, &n, 1); NZRAM.write(ADDR_ACTION, LOW); NZRAM.write(ADDR_LEAK_ST_LAST, LEAK_END); NZRAM.write(ADR_B1_F, HIGH); NZRAM.write(ADR_B2_F, HIGH); } EEPROM.get(EEPROM_ADR_MAGIC, &eeprom_buf, sizeof(DWORD)); if(eeprom_buf != EEPROM_MAGIC) { eeprom_buf = EEPROM_MAGIC; EEPROM.put(EEPROM_ADR_MAGIC, &eeprom_buf, sizeof(DWORD)); resetterMETER1(); resetterMETER2(); eeprom_buf = 0; EEPROM.put(CNT_ON_OFF_CICL, &eeprom_buf, sizeof(DWORD)); } } uint8_t last_btn_st; void check_btn(uint8_t meter_pin, uint8_t NZ_adr_st) { last_btn_st = NZRAM.read(NZ_adr_st); if(digitalRead(meter_pin) == LOW) { if(last_btn_st != LOW) { for(uint8_t i=0; i<3; ++i) { if(digitalRead(meter_pin) == LOW) delay(5); else return; } last_btn_st = LOW; NZRAM.write(NZ_adr_st, last_btn_st); } } else { if(last_btn_st == LOW) { for(uint8_t i=0; i<3; ++i) { if(digitalRead(meter_pin) == HIGH) delay(5); else return; } last_btn_st = HIGH; NZRAM.write(NZ_adr_st, last_btn_st); if(NZ_adr_st == ADR_B1_F) inc_met(ADR_MET1); else inc_met(ADR_MET2); } } } //=----------------------------------------------------------- void loop() { if(digitalRead(LEAK_PIN) == LEAK_DETECTED) { if(NZRAM.read(ADDR_LEAK_ST_LAST) != LEAK_END) { zunoSendReport(LEAK_CHANNEL); NZRAM.write(ADDR_LEAK_ST_LAST, LEAK_END); } } check_btn(METER1_PIN, ADR_B1_F); check_btn(METER2_PIN, ADR_B2_F); if(zunoGetWakeReason() == ZUNO_WAKEUP_REASON_RADIO) { uint32_t start_time=0; #define ACTION_T_OUT 2000 start_time = millis(); //while(NZRAM.read(ADDR_ACTION)== 0) while(valve_action== 0) if((millis() - start_time) >= ACTION_T_OUT) { #ifdef _DEBUG Serial1.println("T_OUT"); #endif break; } else delay(50); #ifdef _DEBUG Serial1.println(millis() - start_time); #endif if(NZRAM.read(ADDR_ACTION)) { valve_action = 0; NZRAM.write(ADDR_ACTION, LOW); if(pin7SwitchBinaryState == LOW) close_water(); else open_water(); } if(alarm_clr) //      { alarm_clr == LOW; zunoSendReport(LEAK_CHANNEL); } } if(digitalRead(INT1)==0) { zunoSetWUOptions(ZUNO_WUPFLAGS_INT1_HIGH); } else { zunoSetWUOptions(ZUNO_WUPFLAGS_INT1_LOW); } zunoSendDeviceToSleep(); } //----------------------------------------------------------- // Getters and setters void inc_met(uint8_t num_chennel) { uint8_t eeprom_adr_met; if(num_chennel == ADR_MET1) eeprom_adr_met = CH_METER_1; else eeprom_adr_met = CH_METER_2; EEPROM.get(eeprom_adr_met, &eeprom_buf, sizeof(DWORD)); eeprom_buf++; EEPROM.put(eeprom_adr_met, &eeprom_buf, sizeof(DWORD)); zunoSendReport(num_chennel); } DWORD getterMETER1() { EEPROM.get(CH_METER_1, &eeprom_buf, sizeof(DWORD)); return eeprom_buf; } DWORD getterMETER2() { EEPROM.get(CH_METER_2, &eeprom_buf, sizeof(DWORD)); return eeprom_buf; } void resetterMETER1() { eeprom_buf = 0; EEPROM.put(CH_METER_1, &eeprom_buf, sizeof(DWORD)); } void resetterMETER2() { eeprom_buf = 0; EEPROM.put(CH_METER_2, &eeprom_buf, sizeof(DWORD)); } void pin7SwitchBinarySetter(byte value) { valve_action = 1; NZRAM.write(ADDR_ACTION, HIGH); pin7SwitchBinaryState = value; if(value == 255) // if open valve, then off leak alarm { NZRAM.write(ADDR_LEAK_ST_LAST, LOW); zunoSendReport(LEAK_CHANNEL); } } byte pin7SwitchBinaryGetter() { return pin7SwitchBinaryState ? 0xFF : 0; } byte getterSensorBinary() { return digitalRead(LEAK_PIN) ? 0 : 0xFF; } byte alarmGetter() { uint8_t ret; ret = NZRAM.read(ADDR_LEAK_ST_LAST); return ret ? 0xFF : 0; } void alarmSetter(byte value) { alarm_clr = HIGH; NZRAM.write(ADDR_LEAK_ST_LAST, value); }
      
      








All Articles