project: reorder
Signed-off-by: Thomas Klaehn <thomas.klaehn@u-blox.com>
This commit is contained in:
21
gate_guard/__init__.py
Normal file
21
gate_guard/__init__.py
Normal file
@@ -0,0 +1,21 @@
|
||||
'''
|
||||
Created on Dec 19, 2016
|
||||
|
||||
@author: klaehn
|
||||
'''
|
||||
import time
|
||||
import gate
|
||||
|
||||
def main():
|
||||
gate_state = gate.Gate()
|
||||
try:
|
||||
while True:
|
||||
gate_state.poll()
|
||||
time.sleep(0.1)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print "key exit"
|
||||
return None
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
29
gate_guard/data_buffer.py
Normal file
29
gate_guard/data_buffer.py
Normal file
@@ -0,0 +1,29 @@
|
||||
'''
|
||||
Created on Mar 30, 2017
|
||||
|
||||
@author: tkla
|
||||
'''
|
||||
|
||||
class DataBuffer(object):
|
||||
def __init__(self, length):
|
||||
self.__max = length
|
||||
self.__data = []
|
||||
|
||||
def push(self, element):
|
||||
if self.__max == 0:
|
||||
return False
|
||||
if len(self.__data) == self.__max:
|
||||
_ = self.__data.pop(0)
|
||||
self.__data.append(element)
|
||||
return True
|
||||
|
||||
def average(self):
|
||||
if len(self.__data) is 0:
|
||||
return None
|
||||
return sum(self.__data) / len(self.__data)
|
||||
|
||||
def length(self):
|
||||
return len(self.__data)
|
||||
|
||||
def clear(self):
|
||||
self.__data = []
|
29
gate_guard/engine.py
Normal file
29
gate_guard/engine.py
Normal file
@@ -0,0 +1,29 @@
|
||||
'''
|
||||
Created on Mar 30, 2017
|
||||
|
||||
@author: tkla
|
||||
'''
|
||||
import gpio
|
||||
|
||||
class Engine(object):
|
||||
def __init__(self, gpio_1 = 13, gpio_2 = 19):
|
||||
self.gpio_1 = gpio.Gpio(gpio_1)
|
||||
self.gpio_2 = gpio.Gpio(gpio_2)
|
||||
|
||||
self.gpio_1.export()
|
||||
self.gpio_1.direction(gpio.Gpio.DIRECTION_OUT)
|
||||
self.gpio_2.export()
|
||||
self.gpio_2.direction(gpio.Gpio.DIRECTION_OUT)
|
||||
self.stop()
|
||||
|
||||
def stop(self):
|
||||
self.gpio_1.set(0)
|
||||
self.gpio_2.set(0)
|
||||
|
||||
def up(self):
|
||||
self.gpio_1.set(1)
|
||||
self.gpio_2.set(0)
|
||||
|
||||
def down(self):
|
||||
self.gpio_1.set(0)
|
||||
self.gpio_2.set(1)
|
179
gate_guard/gate.py
Normal file
179
gate_guard/gate.py
Normal file
@@ -0,0 +1,179 @@
|
||||
'''
|
||||
Created on Dec 19, 2016
|
||||
|
||||
@author: klaehn
|
||||
'''
|
||||
import time
|
||||
import mqtt
|
||||
import data_buffer
|
||||
import light_sensor
|
||||
import engine
|
||||
import power_sensor
|
||||
from numpy.core.umath import power
|
||||
|
||||
STATE_INIT = "init"
|
||||
STATE_OPENED = "open"
|
||||
STATE_CLOSED = "close"
|
||||
STATE_OPENING = "opening"
|
||||
STATE_CLOSING = "closing"
|
||||
STATE_ERROR = "error"
|
||||
|
||||
LIGHT_READ_DELAY_S = 30
|
||||
LIGHT_CONSECUTIVE_READS = 10
|
||||
LIGHT_LX_THRESHOLD = {"open":10, "close":5}
|
||||
|
||||
MQTT_HOST = "proxy"
|
||||
MQTT_TOPIC = "outdoor/chickenhouse/newgate"
|
||||
|
||||
LIGHT_SENSOR_I2C_BUS = 1
|
||||
LIGHT_SENSOR_I2C_ADDRESS = 0x23
|
||||
|
||||
POWER_SENSOR_I2C_BUS = 1
|
||||
POWER_SENSOR_I2C_ADDRESS = 0x40
|
||||
POWER_CONSECUTIVE_READS = 1000
|
||||
|
||||
MAX_ENGINE_POWER = {"up":330, "down":280}
|
||||
MAX_GATE_RUNTIME = {"open":300, "close":300}
|
||||
MIN_GATE_RUNTIME = {"open":10, "close":10}
|
||||
|
||||
|
||||
class Gate(object):
|
||||
def __init__(self):
|
||||
self.__state_handler = {STATE_INIT:self.__init_handler, \
|
||||
STATE_OPENED:self.__opened_handler, \
|
||||
STATE_CLOSED:self.__closed_handler, \
|
||||
STATE_OPENING:self.__opening_handler, \
|
||||
STATE_CLOSING:self.__closing_handler, \
|
||||
STATE_ERROR:self.__error_handler}
|
||||
self.__next_state = STATE_INIT
|
||||
self.__last_state = STATE_ERROR
|
||||
|
||||
self.__light_sensor = light_sensor.LightSensor(LIGHT_SENSOR_I2C_BUS, \
|
||||
LIGHT_SENSOR_I2C_ADDRESS)
|
||||
self.__light_data = data_buffer.DataBuffer(LIGHT_CONSECUTIVE_READS)
|
||||
self.__comserver = mqtt.Mqtt(MQTT_HOST)
|
||||
self.__engine = engine.Engine(gpio_1=13, gpio_2=19)
|
||||
self.__power_sensor = power_sensor.PowerSensor(POWER_SENSOR_I2C_BUS, \
|
||||
POWER_SENSOR_I2C_ADDRESS)
|
||||
self.__power_data = data_buffer.DataBuffer(POWER_CONSECUTIVE_READS)
|
||||
self.__gate_move_timeout = 0
|
||||
self.__light_read_timeout = 0
|
||||
self.__error_count = 0
|
||||
self.__runtime_open = 0
|
||||
self.__runtime_close = 0
|
||||
|
||||
def poll(self):
|
||||
current_time = time.time()
|
||||
if current_time >= self.__light_read_timeout:
|
||||
self.__light_read_timeout = current_time + LIGHT_READ_DELAY_S
|
||||
self.__light_data.push(self.__light_sensor.read())
|
||||
self.__state_handler[self.__next_state](self.__light_data.average())
|
||||
|
||||
def __update_state(self, new_state):
|
||||
self.__last_state = self.__next_state
|
||||
self.__next_state = new_state
|
||||
|
||||
|
||||
def __is_transition(self):
|
||||
if self.__last_state != self.__next_state:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def __init_handler(self, light_avg):
|
||||
'''
|
||||
In init we don't know anything neither about gate state nor about
|
||||
light. So first we try to reach STATE_OPENED.
|
||||
'''
|
||||
#pylint: disable=unused-argument
|
||||
self.__comserver.connect()
|
||||
self.__comserver.transmit(MQTT_TOPIC, str(time.time()) + \
|
||||
" gate gard initiated")
|
||||
self.__comserver.disconnect()
|
||||
self.__update_state(STATE_OPENING)
|
||||
|
||||
|
||||
def __opened_handler(self, light_avg):
|
||||
next_state = self.__next_state
|
||||
if self.__is_transition():
|
||||
self.__engine.down()
|
||||
time.sleep(5)
|
||||
self.__engine.stop()
|
||||
self.__comserver.transmit(MQTT_TOPIC, str(time.time()) + \
|
||||
" Opened " + \
|
||||
str(self.__power_data.average()) + " mW")
|
||||
self.__power_data.clear()
|
||||
|
||||
if (light_avg != None) and (light_avg <= LIGHT_LX_THRESHOLD["close"]):
|
||||
next_state = STATE_CLOSING
|
||||
|
||||
self.__update_state(next_state)
|
||||
|
||||
|
||||
def __closed_handler(self, light_avg):
|
||||
next_state = self.__next_state
|
||||
if self.__is_transition():
|
||||
self.__engine.up()
|
||||
time.sleep(5)
|
||||
self.__engine.stop()
|
||||
self.__comserver.transmit(MQTT_TOPIC, str(time.time()) + \
|
||||
" Closed " + \
|
||||
str(self.__power_data.average()) + " mW")
|
||||
self.__power_data.clear()
|
||||
|
||||
if (light_avg != None) and (light_avg > LIGHT_LX_THRESHOLD["open"]):
|
||||
next_state = STATE_OPENING
|
||||
|
||||
self.__update_state(next_state)
|
||||
|
||||
|
||||
def __opening_handler(self, light_avg):
|
||||
next_state = self.__next_state
|
||||
if self.__is_transition():
|
||||
self.__runtime_open = time.time() + MIN_GATE_RUNTIME["open"]
|
||||
self.__engine.up()
|
||||
self.__gate_move_timeout = time.time() + MAX_GATE_RUNTIME["open"]
|
||||
self.__comserver.transmit(MQTT_TOPIC, str(time.time()) + \
|
||||
" Opening " + str(light_avg) + " lx")
|
||||
|
||||
if time.time() > self.__gate_move_timeout:
|
||||
next_state = STATE_ERROR
|
||||
else:
|
||||
self.__power_data.push(self.__power_sensor.power_mw())
|
||||
current_avg = self.__power_data.average()
|
||||
if current_avg != None:
|
||||
if current_avg > MAX_ENGINE_POWER["up"]:
|
||||
if time.time() > self.__runtime_open:
|
||||
next_state = STATE_OPENED
|
||||
|
||||
self.__update_state(next_state)
|
||||
|
||||
def __closing_handler(self, light_avg):
|
||||
next_state = self.__next_state
|
||||
if self.__is_transition():
|
||||
self.__runtime_close = time.time() + MIN_GATE_RUNTIME["close"]
|
||||
self.__engine.down()
|
||||
self.__gate_move_timeout = time.time() + MAX_GATE_RUNTIME["close"]
|
||||
self.__comserver.transmit(MQTT_TOPIC, str(time.time()) + \
|
||||
" Closing " + str(light_avg) + " lx")
|
||||
|
||||
if time.time() > self.__gate_move_timeout:
|
||||
next_state = STATE_ERROR
|
||||
else:
|
||||
self.__power_data.push(self.__power_sensor.power_mw())
|
||||
current_avg = self.__power_data.average()
|
||||
if current_avg != None:
|
||||
if current_avg > MAX_ENGINE_POWER["down"]:
|
||||
if time.time() > self.__runtime_close:
|
||||
next_state = STATE_CLOSED
|
||||
|
||||
self.__update_state(next_state)
|
||||
|
||||
|
||||
def __error_handler(self, light_avg):
|
||||
#pylint: disable=unused-argument
|
||||
if self.__is_transition():
|
||||
self.__engine.stop()
|
||||
self.__comserver.transmit(MQTT_TOPIC, str(time.time()) + \
|
||||
" Error handler!!!")
|
||||
self.__update_state(STATE_INIT)
|
16
gate_guard/light_sensor.py
Normal file
16
gate_guard/light_sensor.py
Normal file
@@ -0,0 +1,16 @@
|
||||
'''
|
||||
Created on Mar 30, 2017
|
||||
|
||||
@author: tkla
|
||||
'''
|
||||
import smbus
|
||||
|
||||
class LightSensor(object):
|
||||
def __init__(self, bus = 1, addr = 0x23):
|
||||
self.__i2c_device = bus
|
||||
self.__i2c_addr = addr
|
||||
self.__i2c_device = smbus.SMBus(self.__i2c_device)
|
||||
|
||||
def read(self):
|
||||
data = self.__i2c_device.read_i2c_block_data(self.__i2c_addr, 0x10)
|
||||
return int(round((data[0] * 256 + data[1]) / 1.2, 0))
|
48
gate_guard/power_sensor.py
Normal file
48
gate_guard/power_sensor.py
Normal file
@@ -0,0 +1,48 @@
|
||||
'''
|
||||
Created on Dec 19, 2016
|
||||
|
||||
@author: klaehn
|
||||
'''
|
||||
import smbus
|
||||
|
||||
class PowerSensor(object):
|
||||
'''
|
||||
Power sensor wrapper
|
||||
'''
|
||||
def __init__(self, bus = 1, addr = 0x40):
|
||||
self.__bus = smbus.SMBus(bus)
|
||||
self.__addr = addr
|
||||
|
||||
value = [(0x1000 >> 8) & 0xFF, 0x1000 & 0xFF]
|
||||
self.__bus.write_i2c_block_data(self.__addr, 0x05, value)
|
||||
config = 0x2000 | 0x1800 | 0x0400 | 0x0018 | 0x0007
|
||||
value = [(config >> 8) & 0xFF, config & 0xFF]
|
||||
self.__bus.write_i2c_block_data(self.__addr, 0x00, value)
|
||||
|
||||
def shunt_voltage_mv(self):
|
||||
''' Read the voltage at the shunt resistor [mV] '''
|
||||
data = self.__bus.read_i2c_block_data(self.__addr, 0x01)
|
||||
voltage = data[0] * 256 + data[1]
|
||||
return voltage * 0.01
|
||||
|
||||
def current_ma(self):
|
||||
''' Read the current [mA] '''
|
||||
data = self.__bus.read_i2c_block_data(self.__addr, 0x04)
|
||||
if data[0] >> 7 == 1:
|
||||
current = data[0] * 256 + data[1]
|
||||
if current & (1 << 15):
|
||||
current = current - (1 << 16)
|
||||
else:
|
||||
current = (data[0] << 8) | (data[1])
|
||||
return current / 10
|
||||
|
||||
def power_mw(self):
|
||||
''' Read the power [mW] '''
|
||||
data = self.__bus.read_i2c_block_data(self.__addr, 0x03)
|
||||
if data[0] >> 7 == 1:
|
||||
power = data[0] * 256 + data[1]
|
||||
if power & (1 << 15):
|
||||
power = power - (1 << 16)
|
||||
else:
|
||||
power = (data[0] << 8) | (data[1])
|
||||
return power / 2
|
Reference in New Issue
Block a user