--============================================================================== -- ILLUMRA by Ad Hoc Electronics -- For support contact: support@ILLUMRA.com or 801-225-2226 -- Filename: AHD0345C -- Multiple Occupancy Sensors with Single Daylighting Sensor using 4CH SLT -- Controls Wireless Relays -- Copyright 2010 Ad Hoc Electronics --============================================================================== if adhoc_defined == nil then -- *** Variables and Functions *** adhoc_defined = true learnable = 0 -- 1 makes gateway able to be taught occ sensors and SLT -- learnable = 0 -- 0 makes gateway unable to be taught occ sensors and SLT vm.eo_flush() -- clear the RX queue -- Occ sensor variables and functions -- Number of occupancy sensors available for this script (maximum 39): sensor_loop = 39 -- Sensor 1 is AV1/BV1, sensor 2 is AV2/BV2, etc. any_occupied = 0 -- This variable keeps track of whether any learned occ sensor -- is sensing motion. 0=no motion, 1=motion. occ_timeout = 120 -- occupancy sensor timeout in seconds (approximately) occ_timer = occ_timeout -- initialize --SLT variables count_down_timeout=60 -- light sensor time out in seconds (approx.) count_down_to_off=count_down_timeout -- initialize heard_from_slt = 0 -- initialize timer_expired = 0 -- initialize -- SLT ID is always stored in location 40 slt_sensor_id = ME.AV40_Present_Value -- SLT Sensor ID (received by teach-in) slt_sensor_on = ME.BV40_Present_Value -- SLT Sensor Value (1 means slt is on) -- relay variables -- wireless relay is hardcoded to relay #21 wireless_relay_state = ME.BO21_Present_Value wireless_relay_press = ME.BO41_Present_Value -- This function returns a matching occ sensor ID and its value. -- If no match is found, it returns an available zero ID, -- or if all available IDs are already taken, it returns nil. sensor_find = function(check_id) for index=1,39,1 do object = ME["AV" .. index .. "_Present_Value"] if object.value == check_id then -- Found a matching sensor ID, return both objects: return object, ME["BV" .. index .. "_Present_Value"] end end for index=1,39,1 do object = ME["AV" .. index .. "_Present_Value"] if object.value == 0 then -- Found a matching sensor ID, return both objects: return object, ME["BV" .. index .. "_Present_Value"] end end -- No matching ID and all available IDs are already taken: return nil, nil -- Return both the ID and value objects as nil end else -- *** Program Execution *** -- decrement SLT counter when the most recent SLT packet was an ON packet (~1 per second) -- if this gateway misses an OFF packet from the SLT, then this counter will eventaully -- cause this this counter to expire. SLT ON packets reset the counter. if last_rx_packet == 1 then count_down_to_off=count_down_to_off-1 end -- print some status messages if last_rx_packet == 0 then print("SLT OFF", "OCC", occ_timer) elseif last_rx_packet == 1 then print("SLT ON", count_down_to_off, "OCC", occ_timer) elseif countdown_expired == 1 then print("SLT EXPIRED") end -- decrememnt occ sensor timer so long as it hasn't already expired. if occ_timer >= 0 then occ_timer = occ_timer-1 end -- check to see of occ sensor timer already expired. if occ_timer < (0) then any_occupied = 0 print("OCC TIMEOUT EXPIRED") end eo_packet = vm.eo_read(-1) -- Retrieve oldest packet from RX queue if eo_packet ~= nil then -- Check if a valid packet was received -- Check if the received packet is occ sensor teach-in telegram: if (learnable == 1) and (eo_packet.bytes[6] == 0x80) -- DB0 and (eo_packet.bytes[7] == 0xFF) -- DB1 and (eo_packet.bytes[8] == 0x0F) -- DB2 and (eo_packet.bytes[9] == 0x1C) then -- DB3 -- A teach-in was received, look for an empty sensor ID: sensor_id, sensor_on = sensor_find(eo_packet.enocean_id) if sensor_id ~= nil then if sensor_id.value == 0 then sensor_id.value = eo_packet.enocean_id print("Learned Occ: ", sensor_id.value) else print("Occ Already Learned: ", sensor_id.value) end else print("No room for another Occ:", sensor_id.value) end else -- Otherwise, check if for a matching sensor ID: sensor_id, sensor_on = sensor_find(eo_packet.enocean_id) if sensor_id ~= nil then if sensor_id.value ~= 0 then -- A sensor value was received, keep it: if eo_packet.bytes[7] >= 127 then -- DB1: PIR is ON sensor_on.value = 1 -- Sensor is occupied any_occupied = 1 -- sensor detected motion occ_timer = occ_timeout -- just heard from an occ sensor, reset timer print("Occupied", sensor_id.value) else -- DB1: PIR is OFF sensor_on.value = 0 -- Sensor is not occupied any_occupied = 0 print("Unoccupied", sensor_id.value) end end end end -- Check if the received packet is a SLT teach-in telegram: if (learnable == 1) and (eo_packet.bytes[6] == 0x80) -- DB0 and (eo_packet.bytes[7] == 0x08) -- DB1 and (eo_packet.bytes[8] == 0x08) -- DB2 and (eo_packet.bytes[9] == 0xFC) then -- DB3 -- A teach-in was received, check if link exists: if slt_sensor_id.value == 0 then -- No sensor linked yet, keep sensor ID: slt_sensor_id.value = eo_packet.enocean_id print("Learned SLT: ", slt_sensor_id.value) end -- Otherwise, check if packet ID matches kept ID: elseif eo_packet.enocean_id == slt_sensor_id.value then -- A sensor value was received, keep it: -- if eo_packet.bytes[9] == 0x74 then -- DB3 is an ON packet for a single CH SLT if (eo_packet.bytes[9] == 0x87) and (eo_packet.bytes[6] == 0xEB) then -- an action ON packet for CH 1 of 4 CH SLT slt_sensor_on.value = 1 -- SLT is on heard_from_slt = 1 -- we heard from the SLT countdown_expired = 0 -- countdown didn't expire last_rx_packet = 1 -- last RX packet was an ON command count_down_to_off=count_down_timeout -- reset the counter print("SLT COMMAND ON") -- for debugging only -- print("Time=", scl.clock()) elseif (eo_packet.bytes[9] == 0x00) and (eo_packet.bytes[6] == 0xEF) then -- an action OFF packet for CH 1 of 4 CH SLT slt_sensor_on.value = 0 -- SLT is off heard_from_slt = 1 -- we heard from the SLT last_rx_packet = 0 -- last RX packet was an OFF command print("SLT COMMAND OFF") elseif (eo_packet.bytes[9] == 0x87) and (eo_packet.bytes[6] == 0xFB) then -- a status ON packet for CH 1 of 4 CH SLT slt_sensor_on.value = 1 -- SLT is on heard_from_slt = 1 -- we heard from the SLT countdown_expired = 0 -- countdown didn't expire last_rx_packet = 1 -- last RX packet was an ON packet count_down_to_off=count_down_timeout -- reset the counter print("SLT STATUS ON") elseif (eo_packet.bytes[9] == 0x87) and (eo_packet.bytes[6] == 0xFF) then -- a status OFF packet for CH 1 of 4 CH SLT slt_sensor_on.value = 0 -- SLT is off heard_from_slt = 1 -- we heard from the SLT last_rx_packet = 0 -- last RX packet was an OFF packet print("SLT STATUS OFF") end end -- check to see if we've ever heard from the slt if heard_from_slt == 0 then print ("NO SLT YET") count_down_to_off = -1 end if count_down_to_off <= 0 then -- has the countdown timer expired? if countdown_expired == 0 then -- was it already expired? countdown_expired = 1 -- no it was not already expired slt_sensor_on.value = 0 -- haven't heard from SLT so assume it off count_down_to_off = -1 -- coerce so counter doesn't overflow with unpredictable outcome last_rx_packet = -1 -- coerce print ("SLT COUNTDOWN EXPIRED") else count_down_to_off = -1 -- yes it was already expired end --[[ Print each packet ID and content (for debug purposes only): print("Debug:", eo_packet.enocean_id, eo_packet.bytes[9], eo_packet.bytes[8], eo_packet.bytes[7], eo_packet.bytes[6]) --]] end end -- packet received -- manage the wireless relay(s) -- only wireless relays which are taught to the gateway will respond. -- the relay(s) will only be modified when necessary, -- i.e. on change, and refreshed/forced every X seconds. if slt_sensor_on.value == 1 then new_state = 0 debug_txt = "Sunlight present, so turn OFF lights" elseif any_occupied == 1 then new_state = 1 debug_txt = "Sunlight NOT present, but occupied, so turn ON lights" else new_state = 0 debug_txt = "Sunlight NOT present, NOT occupied, so turn OFF lights" end -- checking the conditions before sending a telegram to the relay(s)... ME.AV38_Present_Value.value = ME.AV38_Present_Value.value + 1 if (new_state ~= old_state) -- either the state needs to be changed, or (ME.AV38_Present_Value.value >= 10) then -- Or X seconds elapsed: wireless_relay_press.value = 0 -- transmit the state with a press print("========") if (new_state ~= old_state) then print("The state changed") end if (scl.sysclock() - wireless_relay_state.time > 10) then print("The clock expired") ME.AV38_Present_Value.value = 0 end print("Clock Time: ", scl.sysclock()) print("Last Change Time: ", wireless_relay_state.time) print("========") old_state = new_state -- keeping the last sent state for comparison wireless_relay_state.value = new_state wireless_relay_press.value = 1 -- transmit the state with a press print(debug_txt) end end -- *** End of Script ***