class CO2_stat(object): """Code associated with the thermostat controller""" def __init__(self): self.logger = Logger("CO2_stat") self.logger.debug("initialize CO2 controller object") self._co2 = SCD30(self.logger) self._fan = Fan(self.logger) def check(self, co2=None, test=False): """Adjust the fan depending upon the CO2 Args: temp: optional test CO2 Returns: None Raises: None """ target_co2 = TARGET_CO2 if co2 == None: co2, temp, rh = self._co2.get_data() msg = "{} {} {} {}".format("CO2:", co2, " Target CO2:", target_co2) # Get target temperature from file self.logger.info(msg) if co2 > target_co2: self._fan.set(Fan.ON) else: self._fan.set(Fan.OFF)
class EC(object): GAIN = 1 def __init__(self, logger=None): self._pin = 0 # ADS1115 Channel to use self._logger = logger if logger == None: self._logger = Logger("EC", Logger.INFO) # Create an ADS1115 ADC (16-bit) instance. self._adc = ADS1115(logger) def getEC(self): """Read the sensor value Args: None Returns: EC value as number between 0 and 32,000 Raises: None """ ec = self._adc.read_adc(self._pin, gain=self.GAIN) self._logger.info("{} {} {}: {}".format("EC pin", self._pin, "value", ec)) return ec
class Humidistat(object): """Code associated with the thermostat controller""" def __init__(self): self._logger = Logger("Humidistat") self._logger.setLevel(Logger.INFO) self._logger.debug("initialize Humidistat object") self._co2 = SCD30() self._humidifier = Humidifier() self._target_rh = 80 def check(self, rh=None, test=False): """Adjust the fhumidifier depending upon the rh Args: temp: optional test rh Returns: None Raises: None """ if rh == None: co2, temp, rh = self._co2.get_data() msg = "{} {} {} {}".format("Humidity:", rh, " Target Humidity:", self._target_rh) self._logger.info(msg) if rh > self._target_rh: self._humidifier.set(Humidifier.OFF) else: self._humidifier.set(Humidifier.ON)
class CouchHeartbeat(object): """Heartbeat object """ logger = None def __init__(self): """Standard constructor Get and hold a Python logger """ self._logger = Logger('CouchHeartbeat', Logger.INFO, "/home/pi/MVP/logs/heartbeat.log") def check(self, port): """Ping the database Should return the welcome message Throws an exception if cannot make a connection to the database Args: port: port the database is communicating on Returns: None Raises: None """ cmd = 'curl -X GET http://localhost:5984' ret = os.system(cmd) if ret == 0: self._logger.info('localhost Couch is Up') else: self._logger.error('Couch is Down') self.restart() def restart(self): """System restart (reboot) Args: None Returns: None Raises: None """ cmd = 'sudo reboot' self.logger.warning('System restart: %s' % (cmd)) os.system(cmd)
class V_CO2(object): def __init__(self, logger=None): self._logger = logger if logger == None: self._logger = Logger("V_CO2", Logger.INFO) self._gdx = gdx() self._gdx.open_usb() #self._headers = self._gdx.enabled_sensor_info(self._gdx) self._headers = self._gdx.enabled_sensor_info() self._info = self._gdx.sensor_info() self._logger.info("Initialized V_CO2") sleep(20) # wait 20 seconds to warm up def _exit_(self): self.close() def close(self): self._gdx.stop() self._gdx.close() def info(self): # 0 = sensor number, 1 = description, 2 = units, 3 = incompatible sensors for info in self._info: sensor_number = info[0] sensor_description = info[1] sensor_units = info[2] incompatible_sensors = info[3] print("sensor number = ", sensor_number) print("sensor description = ", sensor_description) print("sensor units = ", sensor_units) print("incompatible sensors = ", incompatible_sensors) print() print('End Sensor Info') def read(self): self._gdx.select_sensors([1,2, 3]) self._gdx.start(period=1000) for x in range(0, 6): data = self._gdx.read() print(data) def getData(self): self._gdx.select_sensors([1,2, 3]) self._gdx.start(period=1000) data = self._gdx.read() if data is None: return None temp = data[0] humidity = data[1] co2 = data[2] return temp, humidity, co2 def log(self): # isolate Sheet to this function # Note: this is a push data function from G_AppendUtil import AppendUtil util = AppendUtil() # Pull data from all three channels co2, temp, humidity self._gdx.select_sensors([1,2,3]) self._gdx.start(period=1000) while True: data = self._gdx.read() co2 = data[0] temp = data[1] hum = data[2] c_rec = ['Environment_Observation', '', 'Canopy', 'Air', 'CO2', "{:3.1f}".format(co2), 'ppm', 'Vernier', 'Success',''] t_rec = ['Environment_Observation', '', 'Canopy', 'Air', 'Temperature', "{:3.1f}".format(temp), 'C', 'Vernier', 'Success',''] h_rec = ['Environment_Observation', '', 'Canopy', 'Air', 'Humidity', "{:3.1f}".format(hum), '%', 'Vernier', 'Success',''] util.save(c_rec) util.save(t_rec) util.save(h_rec) sleep(3) print(data)
class LogShroom(object): def __init__(self, logger=None): """Create sensor object Args: None Returns: None Raises: None """ self._logger = logger if logger == None: self._logger = Logger("LogShroom", Logger.INFO, file="/home/pi/MVP/logs/obsv.log") self._logger.debug("Initialize LogShroom") self._couch = CouchUtil(self._logger) # flag for testing self._test = False def log(self): """Routine to log all sensors Args: None Returns: None Raises: None """ self.getSCD() def getSCD(self): """Record CO2 sensor (scd30) Generates co2, temperature and relative humidity Args: None Returns: None Raises: None """ from scd30 import SCD30 self._scd = SCD30(self._logger) self._logger.debug("In SCD30") try: co2, temp, rh = self._scd.get_data() status = 'Success' if self._test: status = 'Test' self._couch.saveList([ 'Environment_Observation', '', 'Top', 'Air', 'CO2', "{:10.1f}".format(co2), 'ppm', 'scd30', status, '' ]) self._couch.saveList([ 'Environment_Observation', '', 'Top', 'Air', 'Temperature', "{:10.1f}".format(temp), 'Centigrade', 'scd30', status, '' ]) self._couch.saveList([ 'Environment_Observation', '', 'Top', 'Air', 'Humidity', "{:10.1f}".format(rh), 'Percent', 'scd30', status, '' ]) self._logger.info("{} {:6.1f}, {} {:3.1f}, {} {:3.1f}".format( "EnvObsv-CO2:", co2, "Temp", temp, "Humidity:", rh)) except Exception as e: status = 'Failure' if self._test: status = 'Test' self._couch.saveList([ 'Environment_Observation', '', 'Top', 'Air', 'CO2', '', 'ppm', 'scd30', status, str(e) ]) self._couch.saveList([ 'Environment_Observation', '', 'Top', 'Air', 'Temperature', '', 'Centigrde', 'scd30', status, '' ]) self._couch.saveList([ 'Environment_Observation', '', 'Top', 'Air', 'Humidity', '', 'Percent', 'scd30', status, '' ]) self._logger.debug("{} {}".format("EnvObsv-SCD30 Error:", e))
class SI7021(object): def __init__(self, logger=None): self._addr = addr self._path = path self._logger = logger if logger == None: self._logger = Logger("SI7021", Logger.INFO, "/home/pi/MVP/logs/obsv.log") self._i2c = I2C(path, addr, self._logger) def calc_humidity(self, read): """Calculate relative humidity from sensor reading Args: read: the sensor value Returns: rh: calculated relative humidity Raises: None """ rh = ((125.0 * read) / 65536.0) - 6.0 return rh def calc_temp(self, read): """Calculate relative humidity from sensor reading Args: read: the sensor value Returns: tempC: calculated temperature in Centigrade Raises: None """ tempC = ((175.72 * read) / 65536.0) - 46.85 return tempC def get_tempC_prior(self): """Get the temperature from the prior humidity reading Args: None Returns: tempC: calculated temperature in Centigrade Raises: None """ msgs = self._i2c.get_data([previous_temp], 0.03, 3) if msgs == None: return None else: value = self._i2c.bytesToWord(msgs[0].data[0], msgs[0].data[1]) tempC = self.calc_temp(value) return tempC def get_humidity(self): """Get the humidity Args: None Returns: rh: calculated relative humidity Raises: None """ msgs = self._i2c.get_data([rh_no_hold], 0.03, 2) value = self._i2c.bytesToWord(msgs[0].data[0], msgs[0].data[1]) if value == None: return None else: rh = self.calc_humidity(value) return rh def get_tempC(self): """Get the temperature (new reading) Args: None Returns: tempC: calculated temperature in Centigrade Raises: None """ msgs = self._i2c.get_data([temp_no_hold], 0.03, 3) value = self._i2c.bytesToWord(msgs[0].data[0], msgs[0].data[1]) if value == None: return None else: return self.calc_temp(value) def get_rev(self): """Get the firmware revision number Args: None Returns: rev: coded revision number Raises: None """ self._logger.info("\nGet Revision") # msgs = self._i2c.get_msg([firm_rev_1_1, firm_rev_1_2], 3) msgs = self._i2c.get_data([firm_rev_1_1, firm_rev_1_2], 0.03, 3) # Need to test, may error out on some conditions if not ((msgs is None) or (msgs[0].data is None)): rev = msgs[0].data[0] if rev == 0xFF: self._logger.info("version 1.0") elif rev == 0x20: self._logger.info("version 2.0") else: self._logger.error("Unknown") else: self._logger.error("No Revision Data Available") return rev def get_id1(self): """Print the first part of the chips unique id Args: None Returns: None Raises: None """ self._logger.info("\nGet ID 1") try: msgs = self._i2c.get_data([read_id_1_1, read_id_1_2], 0.05, 4) ret = msgs[0].data for data in ret: self._logger.info("ID: " + str(hex(data))) except Exception as e: self._logger.error("Error getting msgs " + str(e)) def get_id2(self): """Print the second part of the chips unique id The device version is in SNA_3 Args: None Returns: None Raises: None """ self._logger.info("\nGet ID 2") msgs = self._i2c.get_data([read_id_2_1, read_id_2_2], 0.05, 4) ret = msgs[0].data for data in ret: self._logger.info("ID" + str(hex(data))) sna3 = msgs[0].data[0] if sna3 == 0x00: self._logger.info("Device: Engineering Sample") elif sna3 == 0xFF: self._logger.info("Device: Engineering Sample") elif sna3 == 0x14: self._logger.info("Device: SI7020") elif sna3 == 0x15: self._logger.info("Device: SI7021") else: self._logger.error("Unknown") def reset(self): """Reset the device Args: None Returns: None Raises: None """ self._logger.info("\nReset") rev_1 = self._i2c.msg_write([reset_cmd]) self._logger.info("Reset: " + str(rev_1))
class Reservoir: FULL = 0 EMPTY = 1 OK = 2 vol_per_mm = 0.3785 # estimate 1 gal per 10 mm, this is reservoir specific vol_per_sec = 0.3785 # estimate 100 ml per sec, this is reservoir specific def __init__(self): '''Get distances for determining reservoir levels''' self.res = {'full': full_ec, 'empty': empty_ec, 'timeout': timeout} self._activity_type = 'Agronomic_Activity' self._logger = Logger('LogReservoir', Logger.INFO) self._persist = Persistence(self._logger) self._logger.detail("Reservoir Initialized") # flag for testing self._test = False def getStatus(self): "Logic for calling the reservoir full" self._logger.detail("In getStatus") ec = self.getEC() if ec <= full_ec: self._logger.debug("{}, {}, {:10.1f}".format( "Reservoir Full", EC, ec)) return Reservoir.FULL, ec elif ec >= empty_ec: self._logger.debug("{}, {}, {:10.1f}".format( "Reservoir Empty", EC, ec)) return Reservoir.EMPTY, ec else: self._logger.debug("{}, {}, {:10.1f}".format( "Reservoir not Empty", EC, ec)) return Reservoir.OK, ec def isFull(self): self._logger.detail("In isFull") status, ec = self.getStatus() if status == Reservoir.FULL: return True else: return False def getEC(self): '''Get EC reading''' self._logger.detail("In getEC") snsr = EC(self._logger) return snsr.getEC() def fill(self, test=False): ''' Routine to control re-filling of the reservoir''' self._logger.detail("{}".format("In Fill")) start_ec = self.getEC() start_t = time.time() pump = Pump() pump.on() # Loop till filled or times out while (not self.isFull()) and self.isFilling(start_t): self._logger.detail("{}".format("In Fill Loop")) self._logger.detail("{}".format("Exit Fill Loop, close solenoid")) # Close valve pump.off() # Calculate amount filled stop_t = time.time() stop_ec = self.getEC() dif_t = stop_t - start_t volume = dif_t * self.vol_per_sec self._logger.detail("{}".format("Exit Fill")) return volume def isFilling(self, start_time, test=False): '''Check that actually filling: the distance is actually changing''' start_ec = self.getEC() self._logger.detail("{} {}".format("Filling, Start EC:", start_ec)) time.sleep(fill_time) # Check for level change first, else will never get to this logic till timeout end_ec = self.getEC() change = start_ec - end_ec if end_ec < start_ec: # need to see at least a 5mm change self._logger.detail("{} {} {} {} {} {}".format( "Still Filling, change:", change, "Start", start_ec, "End", end_ec)) return True else: self._logger.detail("{} {} {} {} {} {}".format( "Not Filling, no change:", change, "Start", start_ec, "End", end_ec)) return False # Check for timeout stop_time = time.time() if stop_time - start_time > self.res['timeout']: self._logger.detail("{}".format("Timeout")) return False else: return True def checkReservoir(self): '''Check condition of reservoir and fill if necessary''' self._logger.detail("{}".format("Check Reservoir")) status, ec = self.getStatus() self._logger.debug("{} {} {} {}".format("Status:", status, "EC", ec)) # Is full, log state if status == Reservoir.FULL: self._logger.detail("{}".format("Status: Full")) self._logger.info("{} {} {} {}".format("EC:", ec, "Full level:", self.res['full'], "Empty:", self.res['empty'])) return True else: # Needs filling self._logger.debug("{}".format("Status: Filling")) volume = self.fill() if volume > 0: # Filled, log volume self.logState(volume, 'Success') return True else: # Failure self._logger.debug("{}".format("Status: Failure")) level = 'Empty' if status == '2': level = 'Ok' self._logger.error("{}".format("Failure to fill Reservoir")) self._logger.error("{} {} {} {}".format( "EC:", ec, "Full level:", self.res['full'], "Empty:", self.res['empty'])) self.logState(volume, 'Failure') return False def logState(self, value, status_qualifier): if self._test: status_qualifier = 'Test' txt = { 'Volume': value, 'full_level': self.res['full'], 'empty_level': self.res['empty'], 'status': 'Full' } self._persist.save([ 'State_Change', '', 'Nutrient', 'Reservoir', 'Volume', value, 'Liter', 'Solenoid', status_qualifier, '' ]) self._logger.info(txt)
class LogSensorsExtra(object): def __init__(self, lvl=Logger.INFO): """Record optional sensor data Args: lvl: Logging level Returns: None Raises: None """ self._logger = Logger("LogSensor-Extra", lvl, file="/home/pi/MVP/logs/obsv.log") self._activity_type = "Environment_Observation" self._test = False self._persist = Persistence(self._logger) def getOneWire(self, test=False): """Loop OneWire temperature sensors Assumes there are four Args: test: flag for testing Returns: None Raises: None """ self._logger.debug("In getOneWire") from OneWireTemp import OneWireTemp for sensor in OneWireTemp.one_temp: self.logOneWire(sensor, OneWireTemp.one_temp[sensor]) def logOneWire(self, sensor, name, test=False): """Record OneWire temperature sensor Args: sensor: number of the sensor name: name of the sensor test: flag for testing Returns: None Raises: None """ self._logger.debug("In logOneWire") from OneWireTemp import OneWireTemp try: ow = OneWireTemp() temp = ow.getTempC(sensor) status_qualifier = 'Success' if self._test: status_qualifier = 'Test' rec = [ self._activity_type, '', name, 'Air', 'Temperature', "{:10.1f}".format(temp), 'Centigrade', 'DS18B20-' + str(sensor), status_qualifier, '' ] self._persist.save(rec) self._logger.info("{}, {}, {:10.1f}".format( name, status_qualifier, temp)) except Exception as e: status_qualifier = 'Failure' if test: status_qualifier = 'Test' rec = [ self._activity_type, '', name, 'Air', 'Temperature', '', 'Centigrade', 'DS18B20-' + str(sensor), status_qualifier, str(e) ] self._persist.save(rec) self._logger.error("{}, {}, {}".format(name, status_qualifier, e)) def getLux(self, test=False): """Record LUX sensor (TSL2561) Args: test: flag for testing Returns: None Raises: None """ from TSL2561 import TSL2561 lx = TSL2561() self._logger.info("TSL2561 - LUX") try: lux = lx.getLux() status_qualifier = 'Success' if test: status_qualifier = 'Test' rec = [ self._activity_type, '', 'Canopy', 'Light', 'LUX', "{:3.1f}".format(lux), 'lux', 'TSL2561', status_qualifier, '' ] self._persist.save(rec) self._logger.info("{}, {}, {:10.1f}".format( "LUX", status_qualifier, lux)) except Exception as e: status_qualifier = 'Failure' if test: status_qualifier = 'Test' rec = [ self._activity_type, '', 'Canopy', 'Light', 'LUX', '', 'lux', 'TSL2561', status_qualifier, str(e) ] self._persist.save(rec) self._logger.error("{}, {}, {}".format("LUX", status_qualifier, e)) def getEC(self, test=False): """Record EC sensor (EC - ADC reading) Args: test: flag for testing Returns: None Raises: None """ from EC import EC self._logger.info("EC") try: s = EC() ec = s.getEC() status_qualifier = 'Success' if test: status_qualifier = 'Test' print("{}, {}, {:10.1f}".format("EC", status_qualifier, ec)) rec = [ self._activity_type, '', 'Reservoir', 'Nutrient', 'EC', "{:3.1f}".format(ec), 'EC', 'EC', status_qualifier, '' ] self._persist.save(rec) self._logger.info("{}, {}, {:10.1f}".format( "EC", status_qualifier, ec)) except Exception as e: status_qualifier = 'Failure' if test: status_qualifier = 'Test' print("{}, {}, {:10.1f}".format("EC", status_qualifier, ec)) rec = [ self._activity_type, '', 'Reservoir', 'Nutrient', 'EC', '', 'EC', 'EC', status_qualifier, str(e) ] self._persist.save(rec) self._logger.error("{}, {}, {}".format("EC CCS811", status_qualifier, e)) def getCO2_NDIR(self, test=False): """Record CO2 sensor (NDIR) Args: test: flag for testing Returns: None Raises: None """ from NDIR import Sensor self._logger.info("CO2 - NDIR") try: sensor = Sensor() sensor.begin() co2 = sensor.getCO2() status_qualifier = 'Success' if test: status_qualifier = 'Test' print("{}, {}, {:10.1f}".format("CO2 Canopy", status_qualifier, co2)) rec = [ self._activity_type, '', 'Canopy', 'Air', 'CO2', "{:3.1f}".format(co2), 'ppm', 'MH-Z16-NDIR', status_qualifier, '' ] self._persist.save(rec) self._logger.debug("{}, {}, {:10.1f}".format( "CO2", status_qualifier, co2)) except Exception as e: status_qualifier = 'Failure' if test: status_qualifier = 'Test' print("{}, {}, {:10.1f}".format("CO2 Canopy", status_qualifier, co2)) rec = [ self._activity_type, '', 'Canopy', 'Air', 'CO2', '', 'ppm', 'MH-Z16-NDIR', status_qualifier, str(e) ] self._persist.save(rec) self._logger.error("{}, {}, {}".format("CO2 NDIR", status_qualifier, e)) def getCO2_CCS811(self, test=False): """Record CO2 sensor (CCS811) Args: test: flag for testing Returns: None Raises: None """ from CCS811 import CCS811 self._logger.info("CO2 CCS811") try: sensor = CCS811(SLAVE) co2 = sensor.get_co2() status_qualifier = 'Success' if test: status_qualifier = 'Test' print("{}, {}, {:10.1f}".format("CO2 Canopy", status_qualifier, co2)) rec = [ self._activity_type, '', 'Canopy', 'Air', 'CO2', "{:3.1f}".format(co2), 'ppm', 'CCS811', str(e) ] self._persist.save(rec) self._logger.debug("{}, {}, {:10.1f}".format( "CCS811 - CO2", status_qualifier, co2)) except Exception as e: status_qualifier = 'Failure' if test: status_qualifier = 'Test' print("{}, {}, {:10.1f}".format("CO2 Canopy", status_qualifier, co2)) rec = [ self._activity_type, '', 'Canopy', 'Air', 'CO2', '', 'ppm', 'CCS811', status_qualifier, str(e) ] self._persist.save(rec) self._logger.error("{}, {}, {}".format("CO2 CCS811", status_qualifier, e)) def getSCD(self): """Record CO2 sensor (scd30) Generates co2, temperature and relative humidity Args: None Returns: None Raises: None """ from scd30 import SCD30 self._scd = SCD30(self._logger) self._logger.debug("In SCD30") try: co2, temp, rh = self._scd.get_data() status = 'Success' if self._test: status = 'Test' c_rec = [ 'Environment_Observation', '', 'Top', 'Air', 'CO2', "{:10.1f}".format(co2), 'ppm', 'scd30', status, '' ] t_rec = [ 'Environment_Observation', '', 'Top', 'Air', 'Temperature', "{:10.1f}".format(temp), 'Centigrade', 'scd30', status, '' ] h_rec = [ 'Environment_Observation', '', 'Top', 'Air', 'Humidity', "{:10.1f}".format(rh), 'Percent', 'scd30', status, '' ] self._persist.save(c_rec) self._persist.save(t_rec) self._persist.save(h_rec) self._logger.info("{} {:6.1f}, {} {:3.1f}, {} {:3.1f}".format( "EnvObsv-CO2:", co2, "Temp", temp, "Humidity:", rh)) except Exception as e: status = 'Failure' if self._test: status = 'Test' c_rec = [ 'Environment_Observation', '', 'Top', 'Air', 'CO2', '', 'ppm', 'scd30', status, str(e) ] t_rec = [ 'Environment_Observation', '', 'Top', 'Air', 'Temperature', '', 'Centigrde', 'scd30', status, '' ] h_rec = [ 'Environment_Observation', '', 'Top', 'Air', 'Humidity', '', 'Percent', 'scd30', status, '' ] self._persist.save(c_rec) self._persist.save(t_rec) self._persist.save(h_rec) self._logger.debug("{} {}".format("EnvObsv-SCD30 Error:", e)) def log(self): '''Log extra sensors Uncomment desired sensors Imports are in the function to avoid loading unnecessary code ''' #self.getOneWire() self.getLux() self.getEC() self.getCO2_NDIR() #self.getCO2_CCS811() self.getSCD()