; *************************************************************************** ; copyright : (C) 2023 by Martin Preuss ; email : martin@libchipcard.de ; ; *************************************************************************** ; * This file is part of the project "AqHome". * ; * Please see toplevel file COPYING of that project for license details. * ; *************************************************************************** ; This module monitors up to two reed contacts to determine whether a windows ; door is open. ; ; It has two modes. ; ; Standard Mode ; ------------- ; ; In this mode up to two windows can be monitored. Each reed contact is reported on separately. ; If a window/door is open a value of 255 is reported, 0 otherwise. ; Tilt Detect Mode ; ---------------- ; ; In this mode both reed sensors are handled together to determine whether a window ; or door is closed, tilted or fully open. ; - if exactly one of two reed contacts is open the window/door is reported as tilted (value 128) ; - if both reed contacts are open the window/door is reported as fully open (value 255) ; - if both reed contacts are closed the window/door is reported as closed (value 0) ; ; ; The window state is reported via the VALUE message with a value id of VALUE_ID_REED1 for the ; first reed contact (or for the whole window/door if in tilt detection mode), for the second ; reed contact a value id of VALUE_ID_REED2 is used. Value type is always AQHOME_VALUETYPE_DOOR ; (i.e. for doors and windows). ; ; There are basically two types of reed contacts. ; - normally closed (NC): The contact is closed as long as the magnet is near the switch. ; If the magnet is removed the contact opens. ; This is the default working mode of this module. ; - normally open (NO): The contact is open as long as the magnet is near the switch. ; If the magnet is removed the contact closes. ; To account for those different types of working modes this mode is configurable. ; The state byte contains the state for two reed contacts (one in each nibble). ; ; bit meaning ; ------------------------------ ; 0 last measurement ; 1 measurement before last ; 2 change occurred ; 3 current stable state ; *************************************************************************** ; defs .equ REED_STATE_MASK_CURRENT = 0x8 .equ REED_STATE_MASK_CHANGED = 0x4 .equ REED_STATE_MASK_BEFORELAST = 0x2 .equ REED_STATE_MASK_LAST = 0x1 .equ REED_STATE_MASK_LASTVALUES = 0x3 ; bits 0 and 1 .equ REED_STATE_BIT_CURRENT = 3 .equ REED_STATE_BIT_CHANGED = 2 .equ REED_CONFIG_MASK_SET = 0x80 .equ REED_CONFIG_MASK_TILTDETECT = 0x40 ; use both reed contacts to detect tilted windows .equ REED_CONFIG_MASK_NO = 0x20 ; if set: normally open (e.g. disconnected when magnet near) .equ REED_CONFIG_BIT_TILTDETECT = 6 .equ REED_CONFIG_BIT_NO = 5 .equ REED_INITIAL_CONFIG = REED_CONFIG_MASK_SET .equ REED_CFGMOD_CMD_SETCONFIG = 1 ; only command for now ; *************************************************************************** ; data .dseg reedDataBegin: reedConfigFromEeprom: .byte 1 reedState: .byte 1 reedDataEnd: ; *************************************************************************** ; code .cseg ; --------------------------------------------------------------------------- ; @routine REED_Init ; ; Init module. ; ; @return CFLAG set if okay, cleared on error ; @clobbers r16, r17, X REED_Init: ; preset SRAM data area ldi xh, HIGH(reedDataBegin) ldi xl, LOW(reedDataBegin) clr r16 ldi r17, (reedDataEnd-reedDataBegin) rcall Utils_FillSram ; (r17, x) cbi REEDOUT_PORT, REEDOUT_PINNUM ; set common reed port to low sbi REEDOUT_DDR, REEDOUT_PINNUM ; set common reed output port as output cbi REED1_PORT, REED1_PINNUM ; disable internal pullup for DATA cbi REED1_DDR, REED1_PINNUM ; set reed1 port as input cbi REED2_PORT, REED2_PINNUM ; disable internal pullup for DATA cbi REED2_DDR, REED2_PINNUM ; set reed2 port as input rcall reedSetupConfig ; initial setup from EEPROM, if possible ret REED_Fini: sec ret REED_Run: ; clc ; debug ; ret ; debug push r15 in r15, SREG cli rcall reedHandleChanges out SREG, r15 pop r15 clc ret REED_Every100ms: push r15 in r15, SREG cli rcall reedUpdateState ; (r16, r17, r18, r19) out SREG, r15 pop r15 ret ; --------------------------------------------------------------------------- ; @routine REED_SetConfig ; ; Set config and write to EEPROM ; ; @return nothing ; @param r16 new configuration ; @clobbers r16, X, (R17) REED_SetConfig: sts reedConfigFromEeprom, r16 ldi xh, HIGH(EEPROM_OFFS_REED_CONF) ; write config to EEPROM ldi xl, LOW(EEPROM_OFFS_REED_CONF) ; rcall Utils_WriteEepromIncr ; (R17) ret ; @end ; --------------------------------------------------------------------------- ; @routine reedSetupConfig ; ; Read initial config from EEPROM or setup from new and write to EEPROM ; ; @clobbers r16, X reedSetupConfig: ldi xh, HIGH(EEPROM_OFFS_REED_CONF) ; read initial config from EEPROM ldi xl, LOW(EEPROM_OFFS_REED_CONF) ; rcall Utils_ReadEepromIncr ; (r16) sbiw xh:xl, 1 ; called routine increments X, undo tst r16 breq reedSetupConfig_noConfig cpi r16, 0xff brne reedSetupConfig_haveConfig reedSetupConfig_noConfig: ldi r16, REED_INITIAL_CONFIG ; disabled for now ; debug rcall Utils_WriteEepromIncr reedSetupConfig_haveConfig: sts reedConfigFromEeprom, r16 reedSetupConfig_end: ret ; @end ; --------------------------------------------------------------------------- ; @routine reedHandleChanges ; ; @clobbers all reedHandleChanges: lds r16, reedConfigFromEeprom sbrc r16, REED_CONFIG_BIT_TILTDETECT rjmp reedHandleChangesTiltDetectMode rjmp reedHandleChangesSingleMode ; @end ; --------------------------------------------------------------------------- ; @routine reedHandleChangesSingleMode ; ; @clobbers all reedHandleChangesSingleMode: lds r19, reedState ; reed 1 mov r16, r19 ldi r17, VALUE_ID_REED1 push r19 rcall reedSendValueIfChanged pop r19 andi r16, 0x0f andi r19, 0xf0 or r19, r16 ; reed 2 mov r16, r19 swap r16 ldi r17, VALUE_ID_REED2 push r19 rcall reedSendValueIfChanged pop r19 andi r16, 0x0f swap r16 andi r19, 0x0f or r19, r16 sts reedState, r19 ret ; @end ; --------------------------------------------------------------------------- ; @routine reedHandleChangesTiltDetectMode ; ; Handle changes in multi reed contact mode. ; This allows for detection of tilted windows/doors. ; ; @clobbers all reedHandleChangesTiltDetectMode: lds r19, reedState mov r16, r19 andi r16, (REED_STATE_MASK_CHANGED | (REED_STATE_MASK_CHANGED<<4)) breq reedHandleChangesTiltDetectMode_end ; no changes ; at least one contact changed, handle new state clr r16 sbrc r19, REED_STATE_BIT_CURRENT inc r16 sbrc r19, (REED_STATE_BIT_CURRENT+4) inc r16 ; no check for full state (0=closed, 1=tilted, 2=fully open) tst r16 breq reedHandleChangesTiltDetectMode_closed cpi r16, 1 breq reedHandleChangesTiltDetectMode_tilted ; otherwise fully open, fallthrough ldi r16, 255 rjmp reedHandleChangesTiltDetectMode_sendChanges reedHandleChangesTiltDetectMode_closed: ldi r16, 0 rjmp reedHandleChangesTiltDetectMode_sendChanges reedHandleChangesTiltDetectMode_tilted: ldi r16, 128 reedHandleChangesTiltDetectMode_sendChanges: ldi r17, VALUE_ID_REED1 push r19 rcall reedSendChange pop r19 brcc reedHandleChangesTiltDetectMode_end ; changes handled, clear change flags and write back andi r19, ~(REED_STATE_MASK_CHANGED | (REED_STATE_MASK_CHANGED<<4)) sts reedState, r19 reedHandleChangesTiltDetectMode_end: ret ; @end ; --------------------------------------------------------------------------- ; @routine reedSendValueIfChanged ; ; @return r16 updated state ; @param r16: current state nibble ; @param r17: value id ; @clobbers r16, r19 (all others) reedSendValueIfChanged: push r16 mov r18, r16 andi r16, REED_STATE_MASK_CHANGED breq reedSendValueIfChanged_l1 ; jmp if not changed ; value changed clr r16 sbrc r18, REED_STATE_BIT_CURRENT dec r16 ; if stable state=0 (meaning: closed) rcall reedSendChange brcc reedSendValueIfChanged_l1 pop r16 andi r16, ~REED_STATE_MASK_CHANGED ; clear change bit ret reedSendValueIfChanged_l1: pop r16 ret ; @end ; --------------------------------------------------------------------------- ; @routine reedSendChange ; ; @param r16 value ; @param r17 value id ; @clobbers r16, r18, r19, r20, r21, r22, r23, X (R3, R4, R6, R7, R8, R9, R10, R11, R12, R15, R16, R17, R18, R19, R20, R21, R22, X, Y) reedSendChange: lds r18, com2Address tst r18 brne reedSendChange_haveAddress clc ret reedSendChange_haveAddress: mov r18, r16 ; value to send in r19:r18 clr r19 ldi r16, 0xff ; dest addr: send to all ldi r20, 1 ; denominator in r21:r20 clr r21 ldi r22, AQHOME_VALUETYPE_DOOR ldi xl, LOW(com2SendBuffer) ldi xh, HIGH(com2SendBuffer) rcall CPRO_WriteReportValue ; (R3, R4, R6, R7, R8, R9, R10, R11, R12, R15, R16, R17, R18, R19, R20, R21, X, Y) rjmp COM2_SendPacket ; (r18, r19, r22, X) ; @end ; --------------------------------------------------------------------------- ; @routine reedUpdateState ; ; Updates SDRAM variable reedState according to current pin states. ; ; @clobbers r16, r17, r18, r19, r20 reedUpdateState: lds r19, reedState ; current reed state lds r20, reedConfigFromEeprom ; handle reed 1 (low nibble state) rcall reedReadValue1 ; (r16) sbrc r20, REED_CONFIG_BIT_NO com r16 ; invert reed data if "normally open" contact andi r16, 1 mov r17, r16 mov r16, r19 rcall reedUpdateStateNibble ; (r18) ; replace lower state nibble with new value andi r16, 0x0f ; only keep low nibble andi r19, 0xf0 ; clear out low nibble before or'ing new value in or r19, r16 ; handle reed2 (high nibble state) rcall reedReadValue2 ; (r16) sbrc r20, REED_CONFIG_BIT_NO com r16 ; invert reed data if "normally open" contact andi r16, 1 mov r17, r16 mov r16, r19 swap r16 rcall reedUpdateStateNibble ; (r18) swap r16 ; replace higher state nibble with new value andi r16, 0xf0 andi r19, 0x0f or r19, r16 ; write back reed state sts reedState, r19 ret ; @end ; --------------------------------------------------------------------------- ; @routine reedUpdateStateNibble ; ; @return r16 updated current state nibble ; @param r16 current state nibble ; @param r17 current pin status (o if clear, 1 if set) ; @clobbers r18 reedUpdateStateNibble: mov r18, r16 andi r18, REED_STATE_MASK_LAST ; clear everything but last measurement lsl r18 ; shift previous measurement to left andi r17, 1 or r18, r17 ; or current measurement into value ; store new value in state nibble andi r16, ~REED_STATE_MASK_LASTVALUES ; clear pin states or r16, r18 ; put updated measurements back into state nibble ; check for new stable state tst r18 ; last measurements all zero? breq reedUpdateStateNibble_stable0 cpi r18, 0x03 ; last measurements all one? breq reedUpdateStateNibble_stable1 ; not a stable state right now ret reedUpdateStateNibble_stable0: sbrs r16, REED_STATE_BIT_CURRENT ; new stable is 0, changed? ret ; ret if no change ori r16, REED_STATE_MASK_CHANGED ; flag change andi r16, ~REED_STATE_MASK_CURRENT ; set new stable value to 0 ret reedUpdateStateNibble_stable1: sbrc r16, REED_STATE_BIT_CURRENT ; new stable is 1, changed? ret ; ret if no change ori r16, (REED_STATE_MASK_CHANGED | REED_STATE_MASK_CURRENT) ; flag change, set new stable value to 1 ret ; @end ; --------------------------------------------------------------------------- ; @routine reedReadValue1 ; ; @return r16 current pin state reedReadValue1: clr r16 clc sbic REED1_PIN, REED1_PINNUM sec adc r16, r16 ret ; @end ; --------------------------------------------------------------------------- ; @routine reedReadValue2 ; ; @return r16 current pin state reedReadValue2: clr r16 clc sbic REED2_PIN, REED2_PINNUM sec adc r16, r16 ret ; @end