在Z-Wave上将Aquastorozh连接到智能家居



去年,我在发现Aquastorozh泄漏时购买了用于紧急关闭起重机的设备。 很长一段时间我无法忍受。 有一个想法将其集成到Z-Wave网络中并获得gidrolock模拟,但需要电池。 终于,手伸到...

Aquastorozh是带有插入式水龙头和泄漏传感器的底座。 该组合系统既可以通过适配器的220 V网络工作,也可以通过电池工作。 开发人员提供了连接到“智能家居”系统的功能。 通过关闭以太网插座中的一对触点,您可以打开分接头,而另一对则将它们闭合。 检测到泄漏时,继电器触点闭合1秒钟。 板上有一个非焊接的UART连接器,但是在本文中,我将讨论记录功能的实现。

开发任务


  • 远程开/关水龙头。
  • 泄漏信息。
  • 两个水表。
  • 不要中断“ Aquastorozh”的工作。

决定在ZUNo盾牌的基础上做。 它在带有压力密封的密封外壳中提供,板上有接线端子,并具有用于安装电池和其他电子组件的自由空间。 Arduino是类似的。

以太网连接器




可闭合导线之一是“水上看守人”的“地球”。 您可以结合使用ZUNo和Aqua-Watch的平台,并直接通过GPIO ZUNo控制起重机。 所以我做到了。 但是,如果ZUNo发生故障(例如,电池已用完),“ Aquastorozh”控制线将收到“零”信号,并开始循环重启。 这种连接方式极大地影响了整个系统的可靠性,因此,在电路稍微复杂的情况下,我切换到两个簧片继电器,这与Aquastorozh进行了电流隔离。 继电器导通时消耗约7 mA的电流。 要切换起重机,您需要打开一个继电器一秒钟,这完全可以接受。 电池电量足以进行数千次切换。 (现在我手里有电磁脉冲单线圈继电器。要切换它们,您需要施加一个1 ms的脉冲,这样能效更高。但是要控制该继电器,您需要4个晶体管和两个输入/输出脚)。

睡在Z波中


我将简要介绍Z-Wave设备的睡眠方式以及由此带来的问题。
Z波设备可能处于休眠状态或经常醒来。 睡眠设备是最节能的,但是无法发送命令(在我的情况下,是要切换起重机)。 第二种适合我。 FLiRS设备-经常收听路由从站。 配置为这种操作模式的设备每秒钟唤醒一次,如果在很短的时间内没有收到来自控制器的完全唤醒信号,它将进入睡眠状态。 例如:我发送命令打开起重机。 控制器了解到我的设备经常在监听,并在一秒钟内发出特殊的短唤醒光束,以便网络上的所有FLIRS设备都被唤醒。 一旦我的设备接受了这个数据包,它就会发送一个报告,表明它已经唤醒并准备接受命令。 获取关闭水龙头的命令。 再次入睡。 因此,每当设备受到控制时。 缺点是该设备在控制器广播的开始时和开始时都可以接收唤醒束。 控制器将其发送大约一秒钟。 在最坏的情况下,设备会在本新闻通讯的开头唤醒,并会等待将近一秒钟,直到命令到达。 但是由于通常不需要打开和关闭水龙头,因此这并不是一个严重的缺点。

实作


ZUNo Shield有一个小的面包板,您可以在上面放置必要的组件。



该电路包含两个继电器和两个晶体管进行控制。 一个简单的小草图。



关于能耗的几句话。

ZUNo屏蔽板包含一个用于RS-485协议的驱动器芯片和一个用于单线协议的底部模块上的引脚“ 11”的上拉电阻。 删除这些组件后,主要消费者仍然是ZUNo。



睡眠模式下的功耗约为5-10μA,活动模式下的功耗高达60 mA(继电器处于活动状态,ZUNa正在发送)。

不同工作模式下的电流消耗波形图


当前轴的方向是从上到下。

设备正在等待命令:



大约每秒钟可见一小段峰值,在此期间器件将被唤醒并检查唤醒光束是否到达。

设备收到命令:



首先,设备醒来,接收到唤醒光束,等待命令(从0到1秒),如果命令控制起重机,则它将打开相应的继电器1秒钟(在此阶段,您需要使控制器进入睡眠状态,同时保持腿处于当前状态,但是我我很害怕而且太懒了),剩下的时间都花在了芯片的内部操作上,之后ZUNo入睡了。 水龙头打开或关闭的每次操作总共将近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); } 

Source: https://habr.com/ru/post/zh-CN447616/


All Articles