def _update(self): updateTime = self.lastUpdateTime + datetime.timedelta( seconds=self.interval) # check update flag is enabled # check Brewfather URL is defined # check the updated interval has elapsed since last update # check relevant data for the system is valid # check the data has been updated since last update if self.bUpdate and self.sURL != "" and datetime.datetime.now() > updateTime and \ self.postdata["name"] != "" and \ self.bNewData: try: self.jsondump = json.dumps(self.postdata).encode('utf8') req = request.Request( self.sURL, data=self.jsondump, headers={'content-type': 'application/json'}) response = request.urlopen(req) self.bNewData = False self.lastUpdateTime = datetime.datetime.now() logger.info("BrewFather: " + self.postdata["temp"] + "C, " + self.postdata["gravity"] + "SG, " + self.postdata["aux_temp"] + "C, " + self.lastUpdateTime.strftime("%d.%m.%Y %H:%M:%S")) except: logger.error("Exception posting to Brewfather: " + self.getLastJSON()) else: logger.debug("Update parameters:\nbUpdate = "+str(self.bUpdate)+"\nsUrl = "+self.sURL+"\npostdata.Name = "+self.postdata["name"] + \ "\npostdata.temp = "+self.postdata["temp"]+"\npostdata.gravity = "+self.postdata["gravity"] + "\npostdata.aux_temp = "+self.postdata["aux_temp"] + \ "\nupdateTime = "+updateTime.strftime("%d.%m.%Y %H:%M:%S") + \ "\nlastUpdateTime = "+self.lastUpdateTime.strftime("%d.%m.%Y %H:%M:%S") + \ "\nCurrent Time = "+datetime.datetime.now().strftime("%d.%m.%Y %H:%M:%S"))
def run(self): logger.info("Starting Controller") self.bStopThread = False while self.bStopThread != True: curTime = datetime.datetime.now() sTime = curTime.strftime("%d.%m.%Y %H:%M:%S") self._readConf() # request status from Arduino self._updateState() time.sleep(UPDATE_INVERVAL) # reads and processes all data from serial queue while self.ser.inWaiting() > 0: inputValue = self.ser.readline() str = inputValue.decode() str = str.strip() logger.debug("RESPONSE Raw: "+str) self._processControllerResponse(str) if self.bStopRequest: if not self.bCoolOn and not self.bHeatOn: self.bStopThread = True else: self.stopHeatingCooling() logger.info("Controller Stopped")
def _sender_receiver(self): """Receive packet and send its own packet through rfm95.""" while True: # if there is something to send, send it. if self.packets_to_send: packet_to_send = self.packets_to_send.pop() self._send_packet(packet_to_send) # if there is no packet to send else: received_packet = self._receive() # if nothing is received, continue if not received_packet: continue # if the package has already been received, continue if self._packet_already_received(received_packet): id_to, id_from, id_packet, flags = received_packet[:4] logger.debug( 'message (id : {}) already received : ignoring'.format(id_packet)) continue # else, keep a trace of the packet self._remember_we_got_that_packet(received_packet) # if the packet is for me, drop it in the inbox if self._is_packet_for_me(received_packet): self.drop_in_inbox(received_packet) # if the packet is not for me, or if it is # for everyone, forward it. if not(self._is_packet_for_me(received_packet)) \ or self._is_packet_for_everyone(received_packet): self.packets_to_send.append(received_packet)
def read_data_from_futek_or_velostat(self, folder_name, pure_file_name): starting_exp_timestamp = 0.0 starting_exp_timestamp_file_path = "/home/app/tactile_sensor_velostat/experimental_data/starting_exp_time/starting_experiment_" + pure_file_name + ".txt" if isfile(starting_exp_timestamp_file_path): tex_file = open(starting_exp_timestamp_file_path, mode="r") starting_exp_timestamp = float(tex_file.readline()) logger.debug("I took %s folder with %s name", folder_name, pure_file_name) t = [] data = [] text = [] temp = "/home/app/tactile_sensor_velostat/experimental_data/" + folder_name file_name = temp + "/exp_" + pure_file_name + ".txt" print("name is " + file_name) text_file = open(file_name, "r") for line in text_file: full_line_data = line.split(" ") if float(full_line_data[1]) < starting_exp_timestamp: continue data.append(float(full_line_data[0])) t.append(float(full_line_data[1])) if len(full_line_data) > 2: text.append(full_line_data[2]) else: text.append("") return t, data, text
def _readConf(self): try: if os.path.isfile(CONFIGFILE) == False: logger.error("Controller configuration file is not valid: "+CONFIGFILE) ini = configparser.ConfigParser() ini.read(CONFIGFILE) if 'Controller' in ini: logger.debug("Reading Controller config") config = ini['Controller'] try: if config["MessageLevel"] == "DEBUG": logger.setLevel(logging.DEBUG) elif config["MessageLevel"] == "WARNING": logger.setLevel(logging.WARNING) elif config["MessageLevel"] == "ERROR": logger.setLevel(logging.ERROR) elif config["MessageLevel"] == "INFO": logger.setLevel(logging.INFO) else: logger.setLevel(logging.INFO) except KeyError: logger.setLevel(logging.INFO) try: if config["BeerTempAdjust"] != "": self.beerTAdjust = float(config.get("BeerTempAdjust")) else: raise Exception except: self.beerTAdjust = 0.0 logger.warning("Invalid BeerTempAdjust in configuration; using default: "+str(self.beerTAdjust)) try: if config["ChamberTempAdjust"] != "": self.chamberTAdjust = float(config.get("ChamberTempAdjust")) else: raise Exception except: self.chamberTAdjust = 0.0 logger.warning("Invalid BeerTempAdjust in configuration; using default: "+str(self.chamberTAdjust)) try: if config["OnDelay"] != "" and int(config["OnDelay"]) >= 0: self.onDelay = int(config.get("OnDelay"))*60 else: raise Exception except: self.onDelay = DEFAULT_ON_DELAY logger.warning("Invalid OnDelay in configuration; using default: "+str(self.onDelay)+" seconds") except: logger.warning("Problem read from configuration file: "+CONFIGFILE) logger.debug("Controller config updated")
def read_all_data_for_drawing_froce_comp(self, name, folder_subname=""): if folder_subname != "": folder_subname = "/" + folder_subname t_vel, data_vel, text_vel = self.read_data_from_futek_or_velostat( "velostat_data" + folder_subname, name) t_futek, data_futek, text_futek = self.read_data_from_futek_or_velostat( "futek_data" + folder_subname, name) logger.debug(len(t_vel)) return [[t_vel, t_futek], [data_vel, data_futek], [text_vel, text_futek]]
def _update(self): updateTime = self.lastUpdateTime + timedelta(seconds=self.interval) if datetime.now() > updateTime: logger.debug("Update data") self._readdata() self.lastUpdateTime = datetime.now() else: logger.debug("Update interval not reached:" + datetime.now().strftime(DATETIME_FORMAT) + " / " + updateTime.strftime(DATETIME_FORMAT))
def __init__(self, _sWelcome): threading.Thread.__init__(self) # variables to hold pir current and last states self.pir_state = 0 self.bStopRequest = False self.iDisplay = 1 self.sScreen1Row1 = _sWelcome self.sScreen1Row2 = " Target: " self.sScreen2Row1 = " SG: " self.sScreen2Row2 = " Beer: " self.sScreen3Row1 = "(W)Beer: " self.sScreen3Row2 = "Chamber: " self.lcdOffTime = datetime.datetime.now() self.displaySwitchTime = datetime.datetime.now() + datetime.timedelta( seconds=DISPLAY_ON_SEC) self.dataTime = datetime.datetime.now() # Initialize the GPIO Pins os.system('modprobe w1-gpio') # Turns on the GPIO module # Set the GPIO naming conventions GPIO.setmode(GPIO.BCM) GPIO.setwarnings(False) # Set the three GPIO pins for Output GPIO.setup(PIN_PIR, GPIO.IN) # Create LCD, passing in MCP GPIO adapter. self.lcd = I2C_LCD_driver.lcd() time.sleep(5) self.lcd.backlight(0) self.bLcdOn = False if _sWelcome != None: self.lcd.backlight(1) self.bLcdOn = True self.lcd.lcd_display_string(_sWelcome, 1) self.lcdOffTime = datetime.datetime.now() + datetime.timedelta( seconds=LCD_ON_SEC) logger.debug("1 - LCD is on to show welcome") else: self.lcd.backlight(0) self.bLcdOn = False # Loop until PIR output is 0 while GPIO.input(PIN_PIR) == 1: self.pir_state = 0
def get_reference_length_and_width(centroids): # Order coordinates in clockwise order tl, tr, br, bl = order_points(centroids) # The 'four_point_transform' function from imutils assumes that the width # and length of the new image will be the maximum respective width and # lengths of the given quadrilateral polygon. Hence, I do the same here. bottom_width = int(euclidean(br, bl)) top_width = int(euclidean(tr, tl)) left_height = int(euclidean(bl, tl)) right_height = int(euclidean(br, tr)) logger.debug(f'Bottom width [px]: {bottom_width:.1f} px') logger.debug(f'Top width [px]: {top_width:.1f} px') logger.debug(f'Left height [px]: {left_height:.1f} px') logger.debug(f'Right height [px]: {right_height:.1f} px') width_px = max(bottom_width, top_width) height_px = max(left_height, right_height) return height_px, width_px
def startHeating(self): curTime = datetime.datetime.now() sTime = curTime.strftime("%d.%m.%Y %H:%M:%S") logger.debug("("+sTime+"): Heating request") if curTime > self.heatEndTime + datetime.timedelta(seconds=self.onDelay): logger.debug("("+sTime+"): Heating Turned ON") self.ser.write(b'H\n') else: logger.debug("("+sTime+"): Heating delay has not expired") self.ser.write(b'O\n')
def __del__(self): logger.debug("Delete chamber class")
def _readConf(self): try: if os.path.isfile(CONFIGFILE) == False: logger.error("Chamber configuration file is not valid: " + CONFIGFILE) ini = configparser.ConfigParser() ini.read(CONFIGFILE) if 'Chamber' in ini: logger.debug("Reading Chamber config") config = ini['Chamber'] try: if config["MessageLevel"] == "DEBUG": logger.setLevel(logging.DEBUG) elif config["MessageLevel"] == "WARNING": logger.setLevel(logging.WARNING) elif config["MessageLevel"] == "ERROR": logger.setLevel(logging.ERROR) elif config["MessageLevel"] == "INFO": logger.setLevel(logging.INFO) else: logger.setLevel(logging.INFO) except KeyError: logger.setLevel(logging.INFO) # Read temperatures to target for each date try: if config["Temps"] != "": self.targetTemps = [] t = config["Temps"].split(",") for x in t: self.targetTemps.append(float(x)) else: raise Exception except: self.targetTemps = [DEFAULT_TEMP] logger.warning("Invalid temp values; using default: " + str(self.targetTemps[0])) # Read dates when temperature should change try: if config["Dates"] != "": self.tempDates = [] dts = config["Dates"].split(",") for x in dts: self.tempDates.append( datetime.datetime.strptime( x, '%d/%m/%Y %H:%M:%S')) else: raise Exception except: self.tempDates = [ datetime.datetime.now(), datetime.datetime.now() ] logger.warning( "Invalid date values; using default. Heating/cooling will NOT start" ) if len(self.tempDates) != len(self.targetTemps) + 1: self.tempDates = [ datetime.datetime.now(), datetime.datetime.now() ] self.targetTemps = [DEFAULT_TEMP] logger.warning( "Invalid date or time values; using default. Heating/cooling will NOT start" ) try: if config["BeerTemperatureBuffer"] != "" and float( config["BeerTemperatureBuffer"]) >= 0.0: self.bufferBeerTemp = float( config.get("BeerTemperatureBuffer")) else: raise Exception except: self.bufferBeerTemp = DEFAULT_BUFFER_BEER_TEMP logger.warning( "Invalid beer temperature buffer in configuration; using default: " + str(self.bufferBeerTemp)) try: if config["ChamberScaleBuffer"] != "" and float( config["ChamberScaleBuffer"]) >= 0.0: self.bufferChamberScale = float( config.get("ChamberScaleBuffer")) else: raise Exception except: self.bufferChamberScale = DEFAULT_BUFFER_CHAMBER_SCALE logger.warning( "Invalid chamber scale buffer in configuration; using default: " + str(self.bufferChamberScale)) except: logger.warning("Problem read from configuration file: " + CONFIGFILE) logger.debug("Chamber config updated")
def run(self): while not self.bStopRequest: curTime = datetime.datetime.now() # No data has been sent to interface in 15s so clear old data from being shown if curTime > self.dataTime + datetime.timedelta(seconds=15): self.clearScreen() logger.debug( "No data sent to interface within 15s, clearing data shown" ) # Set which of the displays should be shown if curTime > self.displaySwitchTime: self.iDisplay = self.iDisplay + 1 if self.iDisplay > NUM_DISPLAYS: self.iDisplay = 1 self.displaySwitchTime = curTime + datetime.timedelta( seconds=DISPLAY_ON_SEC) self.lcd.lcd_clear() self.pir_state = GPIO.input(PIN_PIR) # Motion detected if self.pir_state == 1: # Display should be turned on if not already on self.lcd.backlight(1) self.bLcdOn = True # Reset timeout of display when motion is detected self.lcdOffTime = curTime + datetime.timedelta( seconds=LCD_ON_SEC) logger.debug( "LCD ON due to detected motion, timeout reset -> PIR_STATE: " + str(self.pir_state) + " bLcdOn: " + str(self.bLcdOn) + " curTime: " + curTime.strftime("%d.%m.%Y %H:%M:%S") + " lcdOffTime: " + self.lcdOffTime.strftime("%d.%m.%Y %H:%M:%S")) # Motion not detected else: # display is on if self.bLcdOn: # Display should be turned off as timeout has expired if curTime > self.lcdOffTime: self.lcd.lcd_clear() self.lcd.backlight(0) self.bLcdOn = False logger.debug( "Turning LCD OFF -> PIR_STATE: " + str(self.pir_state) + " bLcdOn: " + str(self.bLcdOn) + " curTime: " + curTime.strftime("%d.%m.%Y %H:%M:%S") + " lcdOffTime: " + self.lcdOffTime.strftime("%d.%m.%Y %H:%M:%S")) # Display on until timeout expires else: logger.debug( "LCD ON without motion until timeout -> PIR_STATE: " + str(self.pir_state) + " bLcdOn: " + str(self.bLcdOn) + " curTime: " + curTime.strftime("%d.%m.%Y %H:%M:%S") + " lcdOffTime: " + self.lcdOffTime.strftime("%d.%m.%Y %H:%M:%S")) # Normal state for LCD to be off when no motion detected else: self.lcd.backlight(0) self.bLcdOn = False logger.debug("LCD remains OFF -> PIR_STATE: " + str(self.pir_state) + " bLcdOn: " + str(self.bLcdOn)) try: # If display is ON show latest values if self.bLcdOn: if self.iDisplay == 1: logger.debug("Displaying screen 1") self.lcd.lcd_display_string(self.sScreen1Row1, 1) self.lcd.lcd_display_string(self.sScreen1Row2, 2) elif self.iDisplay == 2: logger.debug("Displaying screen 2") self.lcd.lcd_display_string(self.sScreen2Row1, 1) self.lcd.lcd_display_string(self.sScreen2Row2, 2) elif self.iDisplay == 3: logger.debug("Displaying screen 3") self.lcd.lcd_display_string(self.sScreen3Row1, 1) self.lcd.lcd_display_string(self.sScreen3Row2, 2) else: self.iDisplay = 1 logger.debug( "Display increment messed up; resetting to 1") except: logger.error("*** PROBLEM WRITING TO LCD ***") # This line caused another exception in logging system # logger.error("Error:", sys.exc_info()[0]) # Recreate LCD self.lcd = I2C_LCD_driver.lcd() time.sleep(5) self.lcd.lcd_clear() self.lcdOffTime = curTime self.lcd.backlight(0) self.bLcdOn = False logger.debug(self.sScreen1Row1) logger.debug(self.sScreen1Row2) logger.debug(self.sScreen2Row1) logger.debug(self.sScreen2Row2) logger.debug(self.sScreen3Row1) logger.debug(self.sScreen3Row2) time.sleep(0.5)
def estimate_volume(equator_filename, poles_filename): """ Args: equator_filename: poles_filename: Returns: """ diameters = [] for fn in equator_filename, poles_filename: processer = \ Processer(f'{fn}{EXT}', TARGET_WIDTH, CAL_MTX, DIST, debug=DEBUG) ####################################################################### # Find squares centroids ####################################################################### im = ( processer .undistort() .resize() .blur(3) .get_processed_image() ) squares_centroids = find_squares(im) ####################################################################### # Find Orange ####################################################################### im = ( processer .show("Orange") .perspective_transform(squares_centroids) .show("Orange") .to_hsv() .show("Orange") .filter_by_hsv_color(lowHSV=HSV_LOW, highHSV=HSV_HIGH) .show("Orange") .negate() .show("Orange") .open(size=3, iterations=10, element='circle') .close(size=7, iterations=7, element='circle') .show("Orange") # TODO: show thresholded orange instead of the blob .get_processed_image() ) orange_kpts, orange_diam_px = find_orange(im) orange_centroids = np.array([kp.pt for kp in orange_kpts]) ####################################################################### # Transform pixels to mm ####################################################################### height_px, width_px = im.shape px_per_mm_height = height_px / REAL_H px_per_mm_width = width_px / REAL_W logger.debug(f'{px_per_mm_height:.1f} px/mm [height]') logger.debug(f'{px_per_mm_width:.1f} px/mm [width]') orange_diameter = calc_orange_real_diam(ref_mm=REAL_H, ref_px=height_px, orange_diam_px=orange_diam_px, focal_length_px=FOCAL_LENGTH_PX) logger.debug(f'Focal length: {FOCAL_LENGTH_PX:.1f} px') logger.debug(f'Image orange diameter: {orange_diam_px:.1f} px') logger.info(f'Estimated real orange diameter: {orange_diameter:.1f} mm') ####################################################################### # Draw ####################################################################### im = ( processer .undistort() .reset() .resize() .perspective_transform(squares_centroids) .get_processed_image() ) # Mark the orange im_with_keypoints = drawKeyPts(im, orange_kpts, (0,0,255), 10) # Draw lines between squares # tl, tr, br, bl = order_points(squares_centroids) # draw_line_with_long(im_with_keypoints, tl, tr, mm_per_px1) # draw_line_with_long(im_with_keypoints, br, tr, mm_per_px1) # draw_line_with_long(im_with_keypoints, bl, br, mm_per_px1) # draw_line_with_long(im_with_keypoints, bl, tl, mm_per_px1) # Draw orange diameter orange_diam_text_x = int(orange_centroids[0][0]) orange_diam_text_y = int(orange_centroids[0][1] - (orange_diam_px / 2) - 10) cv2.putText(im_with_keypoints, text = f"{orange_diameter:.1f} mm", org = (orange_diam_text_x, orange_diam_text_y), fontFace = cv2.FONT_HERSHEY_SIMPLEX, fontScale = 3.5, color = (0,0,255), thickness = 5) cv2.imwrite(f'{fn}-out{EXT}', im_with_keypoints) diameters.append(orange_diameter) logger.info(f'Finished with one orange.') ########################################################################### # FINAL CALC ########################################################################### orange_volume = calculate_orange_volume(diameters[0], diameters[1]) logger.info(f'Finished with orange {equator_filename}, {poles_filename}') logger.info(f'Estimated real orange volume: {orange_volume:.1f} ml\n') return orange_volume
def stopHeatingCooling(self): sTime = datetime.datetime.now().strftime("%d.%m.%Y %H:%M:%S") logger.debug("("+sTime+"): Turned Off") self.ser.write(b'O\n')
def _updateState(self): sTime = datetime.datetime.now().strftime("%d.%m.%Y %H:%M:%S") logger.debug("("+sTime+"): Status request") self.ser.write(b'?\n')
def _readdata(self): curTime = datetime.now() timeout = curTime + timedelta( minutes=1) # scan for 1 minute looking for data from tilt logger.debug("Connecting to Bluetooth...") try: sock = bluez.hci_open_dev(self.bluetoothDeviceId) except: logger.exception("Error accessing bluetooth device...") return False blescan.hci_le_set_scan_parameters(sock) blescan.hci_enable_le_scan(sock) gotData = 0 while (gotData == 0 and curTime < timeout): returnedList = blescan.parse_events(sock, 10) for beacon in returnedList: # returnedList is a list datatype of string datatypes seperated by commas (,) output = beacon.split( ',') # split the list into individual strings in an array uuid = output[1] if uuid in Tilt.color.keys(): logger.debug("Found Tilt hydrometer:" + uuid) gotData = 1 foundColor = Tilt.color.get(uuid) foundTemp = float(int(output[2], 16) - 32) * 5 / 9 foundSG = float(int(output[3], 16) / 1000) logger.debug("Found " + foundColor + " Tilt: T/" + str(foundTemp) + ", SG/" + str(foundSG)) if foundColor != self.color: logger.debug( "Skipping data; wrong color Tilt. Looking for: " + self.color) gotData = 0 # wrong tilt elif foundTemp > 200: # first reading always showed temperature of 537.2C and SG 1.0 logger.debug( "Skipping initial datasets as temp and SG are always wrong" ) timeout = curTime + timedelta( minutes=1 ) # extend timeout looking for valid data from tilt gotData = 0 # forces to continue reading from tilt looking for valid data else: self._storeData(foundTemp, foundSG, curTime) logger.info(self.color + " - " + curTime.strftime(DATETIME_FORMAT) + " - T:" + str(round(float(foundTemp), 1)) + " - SG:" + "{:5.3f}".format(round(float(foundSG), 3))) curTime = datetime.now() # Did not collect data before timeout; set data as invalid if gotData == 0: logger.debug("Timed out collecting data from Tilt") blescan.hci_disable_le_scan(sock) return True
def _evaluate(self): self.control self.targetTemps self.tempDates self.bufferBeerTemp self.bufferChamberScale # use wired data initially if self.control.isDataValid(): self.beerTemp = self.control.getBeerTemp() self.beerWireTemp = self.control.getBeerTemp() self.chamberTemp = self.control.getChamberTemp() self.timeData = self.control.timeOfData() else: self.beerTemp = DEFAULT_TEMP self.beerWireTemp = DEFAULT_TEMP self.chamberTemp = DEFAULT_TEMP self.control.stopHeatingCooling() logger.error( "Chamber paused (60s), heating and cooling stopped: controller data is invalid" ) self.paused = True time.sleep(PAUSE_DELAY) return # if Tilt is configured and available replace related values if (self.tilt is not None): tiltdatatime = self.tilt.timeOfData() if tiltdatatime is not None and tiltdatatime > datetime.datetime.now( ) - datetime.timedelta(minutes=5): self.beerTemp = self.tilt.getTemp() self.beerSG = self.tilt.getGravity() if self.timeData < tiltdatatime: self.timeData = tiltdatatime else: self.beerSG = DEFAULT_SG logger.error( "Data from tilt unavailable, checking again in 60s: using wired temperatures" ) time.sleep(PAUSE_DELAY) self.paused = False _curTime = datetime.datetime.now() # check which of the temperature change dates have passed datesPassed = 0 for dt in self.tempDates: if dt < _curTime: datesPassed = datesPassed + 1 # No configured dates have passed, leave chamer powered off if datesPassed == 0: # Turn off heating and cooling logger.debug( "Leaving chamber heating/cooling off until first date reached: " + self.tempDates[datesPassed].strftime("%d.%m.%Y %H:%M:%S")) self.control.stopHeatingCooling() return # check if last date has been reached. If so, heating/cooling should stop elif datesPassed == len(self.tempDates): logger.debug("Last date reached turning heating/cooling off: " + self.tempDates[datesPassed - 1].strftime("%d.%m.%Y %H:%M:%S")) self.control.stopHeatingCooling() return # date is within configured range else: self.targetTemp = self.targetTemps[datesPassed - 1] # beer is warmer than target + buffer, consider cooling if self.beerTemp > (self.targetTemp + self.bufferBeerTemp): # check how much cooler chamber is compared to target, do not want it too low or beer temperature will overshoot too far. if (self.targetTemp - self.chamberTemp) < self.bufferChamberScale * ( self.beerTemp - self.targetTemp): # Turn cooling ON logger.debug("Cooling to be turned ON - Target: " + str(self.targetTemp) + "; Beer: " + str(self.beerTemp) + "; Chamber: " + str(self.chamberTemp) + "; Beer Buffer: " + str(self.bufferBeerTemp) + "; Chamber Scale: " + str(self.bufferChamberScale)) self.control.startCooling() else: logger.debug("Chamber is cold enough to cool beer") self.control.stopHeatingCooling() # beer is cooler than target + buffer, consider heating elif self.beerTemp < (self.targetTemp - self.bufferBeerTemp): # check how much hotter chamber is compared to target, do not want it too high or beer temperature will overshoot too far. if (self.chamberTemp - self.targetTemp) < self.bufferChamberScale * ( self.targetTemp - self.beerTemp): # Turn heating ON logger.debug("Heating to be turned ON - Target: " + str(self.targetTemp) + "; Beer: " + str(self.beerTemp) + "; Chamber: " + str(self.chamberTemp) + "; Beer Buffer: " + str(self.bufferBeerTemp) + "; Chamber Scale: " + str(self.bufferChamberScale)) self.control.startHeating() else: logger.debug("Chamber is warm enough to heat beer") self.control.stopHeatingCooling() # beer is within range of target +/- buffer else: logger.debug("No heating/cooling needed - Target: " + str(self.targetTemp) + "; Beer: " + str(self.beerTemp) + "; Chamber: " + str(self.chamberTemp) + "; Beer Buffer: " + str(self.bufferBeerTemp) + "; Chamber Scale: " + str(self.bufferChamberScale)) self.control.stopHeatingCooling() return
def _readConf(self): try: if os.path.isfile(CONFIGFILE) == False: logger.error("BrewFather configuration file is not valid: " + CONFIGFILE) ini = configparser.ConfigParser() ini.read(CONFIGFILE) if 'BrewFather' in ini: logger.debug("Reading BrewFather config") config = ini['BrewFather'] try: if config["MessageLevel"] == "DEBUG": logger.setLevel(logging.DEBUG) elif config["MessageLevel"] == "WARNING": logger.setLevel(logging.WARNING) elif config["MessageLevel"] == "ERROR": logger.setLevel(logging.ERROR) elif config["MessageLevel"] == "INFO": logger.setLevel(logging.INFO) else: logger.setLevel(logging.INFO) except KeyError: logger.setLevel(logging.INFO) if config["Update"] != "": self.bUpdate = strtobool(config.get("Update")) else: raise Excpetion if config["UpdateURL"] != "": self.sURL = config.get("UpdateURL") else: raise Exception try: if config["UpdateIntervalSeconds"] != "": if int(config["UpdateIntervalSeconds"] ) >= MINIMUM_INTERVAL: self.interval = int( config.get("UpdateIntervalSeconds")) else: logger.warning( "Brewfather update interval cannot be less than 15min; using 900s" ) self.interval = MINIMUM_INTERVAL else: raise Exception except: logger.warning( "Error reading Brewfather update interval; using 900s") self.interval = MINIMUM_INTERVAL try: if config["Device"] != "": self.postdata["name"] = config["Device"] else: raise Exception except: self.postdata["name"] = "Fermonitor" except: self.bUpdate = False logger.warning( "Problem read from configuration file: " + CONFIGFILE + ". Updating BrewFather.app is disabled until configuration fixed. It could take a minute for updated values in config file to be used." ) print("[BrewFather]\nUpdate = " + str(self.bUpdate) + "\nUpdateURL = " + self.sURL + "\nUpdateIntervalSeconds = " + str(self.interval)) logger.debug("BrewFather config:\n[BrewFather]\nUpdate = " + str(self.bUpdate) + "\nUpdateURL = " + self.sURL + "\nUpdateIntervalSeconds = " + str(self.interval))
def read_settings(): global sTiltColor global chamberControlTemp global bUseTilt global logLevel chamberControlTemp = CONTROL_WIRE bUseTilt = False logLevel = logging.INFO logger.debug("Reading configfile: " + CONFIGFILE) try: if os.path.isfile(CONFIGFILE) == False: raise Exception ini = configparser.ConfigParser() ini.read(CONFIGFILE) except: raise IOError("Fermonitor configuration file is not valid: " + CONFIGFILE) try: config = ini['Fermonitor'] except: raise IOError("[Fermonitor] section not found in fermonitor.ini") try: if config["MessageLevel"] == "DEBUG": logLevel = logging.DEBUG elif config["MessageLevel"] == "WARNING": logLevel = logging.WARNING elif config["MessageLevel"] == "ERROR": logLevel = logging.ERROR elif config["MessageLevel"] == "INFO": logLevel = logging.INFO else: logLevel = logging.INFO except KeyError: logLevel = logging.INFO logger.setLevel(logLevel) try: if config["TiltColor"] != "": sTiltColor = config.get("TiltColor") else: raise Exception except: logger.warning("No color specified for Tilt. Tilt not used.") sTiltColor = "" logger.debug("Tilt color: " + sTiltColor) try: if config["ChamberControl"] != "": if config.get("ChamberControl") == "WIRE": chamberControlTemp = CONTROL_WIRE logger.debug("Chamber control temperature based on WIRE") elif config.get("ChamberControl") == "TILT": chamberControlTemp = CONTROL_TILT logger.debug("Chamber control temperature based on TILT") else: chamberControlTemp = CONTROL_WIRE logger.warning( "Invalid ChamberControl configuration; using default: WIRE" ) else: chamberControlTemp = CONTROL_WIRE logger.warning( "Invalid ChamberControl configuration; using default: WIRE") except: chamberControlTemp = CONTROL_WIRE logger.warning( "Invalid ChamberControl configuration; using default: WIRE") try: if config["MessageLevel"] == "DEBUG": logLevel = logging.DEBUG elif config["MessageLevel"] == "WARNING": logLevel = logging.WARNING elif config["MessageLevel"] == "ERROR": logLevel = logging.ERROR elif config["MessageLevel"] == "INFO": logLevel = logging.INFO else: logLevel = logging.INFO except KeyError: logLevel = logging.INFO logger.setLevel(logLevel) logger.debug("Completed reading settings") return
def main(): global timeBeer global target global tempBeer global tempChamber global tempBeerWire global gravity global cTilt global cChamber global cBrewfather logger.info("Starting Fermonitor...") read_settings() cInterface = interface.Interface("Fermonitor......") cInterface.setLogLevel(logLevel) cInterface.start() cChamber = chamber.Chamber(None) cChamber.start() cTilt = None # Start BrewFather thread to store temp and gravity logger.debug("Starting BrewFather Thread") cBrewfather = brewfather.BrewFather() cBrewfather.start() # Main loop for reading and logging data as well as controllng fermentor while True: read_settings() cInterface.setLogLevel(logLevel) # Handle Tilt if sTiltColor != "": if (cTilt is not None) and (cTilt.getColor() != sTiltColor): logger.debug("Tilt color changed: stopping Tilt: " + cTilt.getColor()) cTilt = None if cTilt is None: logger.debug("Starting Tilt monitoring: " + sTiltColor) cTilt = tilt.Tilt(sTiltColor) cTilt.start() time.sleep(10) else: cTilt = None if chamberControlTemp == CONTROL_TILT: cChamber.setTilt(cTilt) else: cChamber.setTilt(None) if not cChamber.paused: target = cChamber.getTargetTemp() tempBeer = cChamber.getBeerTemp() tempBeerWire = cChamber.getWireBeerTemp() tempChamber = cChamber.getChamberTemp() timeBeer = cChamber.timeOfData() if cTilt is not None: datatime = cTilt.timeOfData() if datatime is not None and datatime > datetime.datetime.now( ) - datetime.timedelta(minutes=5): gravity = cTilt.getGravity() else: gravity = None cBrewfather.setData(tempBeer, tempChamber, gravity) cInterface.setData(target, tempBeer, gravity, tempBeerWire, tempChamber) else: timeBeer = None target = None tempBeer = None tempChamber = None tempBeerWire = None time.sleep(1)
def __del__(self): logger.debug("Delete controller class")
def _processControllerResponse(self, str): try: curTime = datetime.datetime.now() sTime = self.lastUpdateTime.strftime("%d.%m.%Y %H:%M:%S") lhs, rhs = str.split(":", 1) if lhs == "C": if rhs == "-": logger.debug("("+sTime+"): Cooling is OFF") if self.bCoolOn == True: self.coolEndTime = curTime self.bCoolOn = False elif rhs == "+": logger.debug("("+sTime+"): Cooling is ON") self.bCoolOn = True elif lhs == "H": if rhs == "-": logger.debug("("+sTime+"): Heating is OFF") if self.bHeatOn == True: self.heatEndTime = curTime self.bHeatOn = False elif rhs == "+": logger.debug("("+sTime+"): Heating is ON") self.bHeatOn = True elif lhs == "F": self.chamberTemp = float(rhs) logger.debug("("+sTime+"): Fridge temperature: {:5.3f}C".format(round(float(self.getChamberTemp()),1))) self.lastUpdateTime = curTime elif lhs == "B": self.beerTemp = float(rhs) logger.debug("("+sTime+"): Beer temperature: {:5.3f}C".format(round(float(self.getBeerTemp()),1))) self.lastUpdateTime = curTime elif lhs == "S": self.internalTemp = float(rhs) logger.debug("("+sTime+"): Delta to safety temperature: "+rhs+"C") self.lastUpdateTime = curTime except BaseException as e: logger.error("ERROR: %s\n" % str(e))
def _readConf(self): bDefaultInterval = True bDefaultBtDeviceId = True if os.path.isfile(CONFIGFILE) == False: logger.error("Tilt configuration file is not valid: " + CONFIGFILE) else: ini = configparser.ConfigParser() try: logger.debug("Reading Tilt config: " + CONFIGFILE) ini.read(CONFIGFILE) if self.color in ini: config = ini[self.color] try: if config["UpdateIntervalSeconds"] != "" and int( config["UpdateIntervalSeconds"] ) >= MINIMUM_INTERVAL: self.interval = int( config.get("UpdateIntervalSeconds")) bDefaultInterval = False else: self.interval = MINIMUM_INTERVAL except KeyError: pass try: if config["BluetoothDeviceId"] != "" and int( config["BluetoothDeviceId"]) >= 0: self.bluetoothDeviceId = int( config.get("BluetoothDeviceId")) bDefaultBtDeviceId = False else: self.bluetoothDeviceId = 0 except KeyError: pass try: if config["MessageLevel"] == "DEBUG": logger.setLevel(logging.DEBUG) elif config["MessageLevel"] == "WARNING": logger.setLevel(logging.WARNING) elif config["MessageLevel"] == "ERROR": logger.setLevel(logging.ERROR) elif config["MessageLevel"] == "INFO": logger.setLevel(logging.INFO) else: logger.setLevel(logging.INFO) except KeyError: logger.setLevel(logging.INFO) else: logger.error("[" + self.color + "] section not found in ini file: " + CONFIGFILE) except: pass if bDefaultInterval or bDefaultBtDeviceId: logger.warning("Problem read from configuration file: \""+CONFIGFILE+ \ "\". Using some default values[**] for Tilt configuration. It could take a minute for updated values in config file to be used.") sConf = "Color = " + self.color sConf = sConf + "\nUpdateIntervalSeconds = " + str(self.interval) if bDefaultInterval: sConf = sConf + "**" sConf = sConf + "\nBluetoothDeviceId = " + str( self.bluetoothDeviceId) if bDefaultBtDeviceId: sConf = sConf + "**" print(sConf)
def __del__(self): self.lcd.lcd_clear() self.lcd.backlight(0) logger.debug("Delete interface class")