def __init__(self, ID_fridge, ID_beer=None, ID_ambient=None, cooler=None, heater=None, door=None): # We must have at least a fridge sensor self.cs = ControlSettings() self.cv = ControlVariables() self.cc = ControlConstants() # State variables self.state = STATES['IDLE'] self.cs.mode = MODES['MODE_OFF'] self.doPosPeakDetect = None self.doNegPeakDetect = None self.door = door self.doorOpen = None self.cooler = cooler self.heater = heater self.light = None # Not implemented self.fan = None # Not implemented self.lastIdleTime = ticks.seconds() self.waitTime = None self.storedBeerSetting = None # cameraLight.setActive(false); # this is for cases where the device manager hasn't configured beer/fridge sensor. # if (self.beerSensor==None): self.beerSensor = tempSensor.sensor(ID_beer) # if (self.fridgeSensor==None): self.fridgeSensor = tempSensor.sensor(ID_fridge) self.ambientSensor = tempSensor.sensor(ID_ambient) self.beerSensor.init() self.fridgeSensor.init() self.ambientSensor.init() self.updateTemperatures() self.reset() # Do not allow heating/cooling directly after reset. # A failing script + CRON + Arduino uno (which resets on serial # connect) could damage the compressor # For test purposes, set these to -3600 to eliminate waiting # after reset self.lastHeatTime = ticks.seconds() self.lastCoolTime = ticks.seconds() self.integralUpdateCounter = 0
def pickTempSetting(self, readTemp, writeTemp, tempName, printAnnotation, row): # Handle temperatures as integers internally, with a resolution of 0.1C # i.e. divide by 10 oldSetting = readTemp() startVal = oldSetting minVal = int(self.tempControl.cc.tempSettingMin * 10) maxVal = int(self.tempControl.cc.tempSettingMax * 10) if oldSetting is None: # previous temperature was not defined, start at 20C startVal = 20.0 startVal *= 10 t = float(startVal) / 10 startPos = self.encoder.pos # The encoder is free-running, so establish zero point # rotaryEncoder.setRange(startVal, minVal, maxVal) blinkTimer = 0 lastChangeTime = ticks.seconds() while (ticks.timeSince(lastChangeTime) < MENU_TIMEOUT): # time out at 10 seconds if (self.encoder.changed): lastChangeTime = ticks.seconds() blinkTimer = 0 # startVal = tenthsToFixed(encoder.read()) t = startVal + (self.encoder.pos - startPos) t = max(minVal, min(t, maxVal)) t = float(t) / 10 display.printTemperatureAt(12, row, t) display.update() if (self.encoder.pushed): # rotaryEncoder.resetPushed() writeTemp(t) printAnnotation("%s temp set to %s in Menu." % (tempName, t)) while self.encoder.pushed: pass return True if (blinkTimer == 0): display.printTemperatureAt(12, row, t) display.update() elif (blinkTimer == 128): display.printAt(12, row, " " * 5) display.update() blinkTimer += 1 blinkTimer &= 0xff # Blink timer is an 8-bit value time.sleep(0.003) # delay for blinking # Time Out. Setting is not written return False # FIXME: Do something if the encoder is not pushed
def blinkLoop(self, changed, # function called to update the value show, # function called to show the current value hide, # function called to blank out the current value pushed): # function to handle selection # @return {@code true} if a value was selected. {@code false} on timeout. lastChangeTime = ticks.seconds() blinkTimer = 0 while (ticks.timeSince(lastChangeTime) < MENU_TIMEOUT): # time out at 10 seconds if (self.encoder.changed): lastChangeTime = ticks.seconds() blinkTimer = 0 changed() display.update() if (blinkTimer == 0): show() display.update() elif (blinkTimer == 128): hide() display.update() if (self.encoder.pushed): # rotaryEncoder.resetPushed() show() display.update() while self.encoder.pushed: pass pushed() return True blinkTimer += 1 blinkTimer &= 0xff # blink timer is an 8-bit value time.sleep(0.003) # wait.millis(3) # delay for blinking return False
def printAllTemperatures(): """Print all temperatures on the LCD.""" global flags # alternate between beer and room temp if (flags & LCD_FLAG_ALTERNATE_ROOM): # bool displayRoom = ((ticks.seconds()&0x08)==0) && !BREWPI_SIMULATE && tempControl.ambientSensor->isConnected() displayRoom = (((int(ticks.seconds())&0x04)==0) and (tempControl.ambientSensor.deviceID is not None or tempControl.ambientSensor.topic is not None)) if (displayRoom ^ ((flags & LCD_FLAG_DISPLAY_ROOM)!=0)): # transition flags = (flags | LCD_FLAG_DISPLAY_ROOM) if displayRoom else (flags & ~LCD_FLAG_DISPLAY_ROOM) printStationaryText() printBeerTemp() printBeerSet() printFridgeTemp() printFridgeSet()
def printAllTemperatures(): """Print all temperatures on the LCD.""" global flags # alternate between beer and room temp if (flags & LCD_FLAG_ALTERNATE_ROOM): # bool displayRoom = ((ticks.seconds()&0x08)==0) && !BREWPI_SIMULATE && tempControl.ambientSensor->isConnected() displayRoom = (((int(ticks.seconds())&0x04)==0) and tempControl.ambientSensor.deviceID is not None) if (displayRoom ^ ((flags & LCD_FLAG_DISPLAY_ROOM)!=0)): # transition flags = (flags | LCD_FLAG_DISPLAY_ROOM) if displayRoom else (flags & ~LCD_FLAG_DISPLAY_ROOM) printStationaryText() printBeerTemp() printBeerSet() printFridgeTemp() printFridgeSet()
def updateState(self): print("Update state. Mode %s, state %s" % ({v: k for k, v in MODES.items()}[self.cs.mode], {v: k for k, v in STATES.items()}[self.state])) stayIdle = False newDoorOpen = self.door.isOpen if (newDoorOpen != self.doorOpen): self.doorOpen = newDoorOpen print("Fridge door %s" % ('opened' if self.doorOpen else 'closed')) self.piLink.printFridgeAnnotation("Fridge door %s" % ("opened" if self.doorOpen else "closed")) if (self.cs.mode == MODES['MODE_OFF']): self.state = STATES['STATE_OFF'] stayIdle = True # stay idle when one of the required sensors is disconnected, # or the fridge setting is INVALID_TEMP # if(isDisabledOrInvalid(cs.fridgeSetting) || # !fridgeSensor->isConnected() || # (!beerSensor->isConnected() && tempControl.modeIsBeer())): # self.state = 'IDLE' # stayIdle = True # Stay idle if the fridge sensor isn't connected # TODO - Make sure this didn't break beer settings/profiles elif not self.cs.fridgeSetting or not self.fridgeSensor.temperature: self.state = STATES['IDLE'] stayIdle = True elif not self.beerSensor.temperature and (self.cs.mode == MODES['MODE_BEER_CONSTANT'] or self.cs.mode == MODES['MODE_BEER_PROFILE']): self.state = STATES['IDLE'] stayIdle = True sinceIdle = self.timeSinceIdle() sinceCooling = self.timeSinceCooling() sinceHeating = self.timeSinceHeating() fridgeFast = self.fridgeSensor.readFastFiltered() beerFast = self.beerSensor.readFastFiltered() secs = ticks.seconds() if self.state in (STATES['IDLE'], STATES['STATE_OFF'], STATES['WAITING_TO_COOL'], STATES['WAITING_TO_HEAT'], STATES['WAITING_FOR_PEAK_DETECT']): self.lastIdleTime = secs if not stayIdle: # set waitTime to zero. It will be set to the maximum # required waitTime below when wait is in effect. # if(stayIdle): # break self.resetWaitTime() if (fridgeFast > (self.cs.fridgeSetting + self.cc.idleRangeHigh)): # fridge temperature is too high self.updateWaitTime(MIN_SWITCH_TIME, sinceHeating) if (self.cs.mode == MODES['MODE_FRIDGE_CONSTANT']): self.updateWaitTime(MIN_COOL_OFF_TIME_FRIDGE_CONSTANT, sinceCooling) else: if (beerFast < ( self.cs.beerSetting + 0.03125)): # + 16) ): # If beer is already under target, stay/go to idle. 1/2 sensor bit idle zone self.state = STATES['IDLE'] # beer is already colder than setting, stay in or go to idle # break # FIXME: We need to skip the next if statement else: self.updateWaitTime(MIN_COOL_OFF_TIME, sinceCooling) if (self.cooler != None): # FIXME was &defaultActuator): if (self.getWaitTime() > 0): self.state = STATES['WAITING_TO_COOL'] else: self.state = STATES['COOLING'] elif (fridgeFast < (self.cs.fridgeSetting + self.cc.idleRangeLow)): # fridge temperature is too low print("Fridge temperature is too low") self.updateWaitTime(MIN_SWITCH_TIME, sinceCooling) self.updateWaitTime(MIN_HEAT_OFF_TIME, sinceHeating) if (self.cs.mode != MODES['MODE_FRIDGE_CONSTANT']): if (beerFast > ( self.cs.beerSetting - 0.03125)): # - 16)){ // If beer is already over target, stay/go to idle. 1/2 sensor bit idle zone self.state = STATES['IDLE'] # beer is already warmer than setting, stay in or go to idle # break # FIXME: We need to skip the next if statement # if(self.heater != &defaultActuator or (self.lightAsHeater and (self.light != &defaultActuator))): # FIXME what is &defaultActuator ? if ((self.heater != None or (self.cc.lightAsHeater and (self.light != None)))): if (self.getWaitTime() > 0): self.state = STATES['WAITING_TO_HEAT'] else: self.state = STATES['HEATING'] else: self.state = STATES['IDLE'] # within IDLE range, always go to IDLE # break if (self.state == STATES['HEATING'] or self.state == STATES['COOLING']): # If peak detect is not finished, but the fridge wants to switch to heat/cool # Wait for peak detection and show on display if self.doNegPeakDetect: self.updateWaitTime(COOL_PEAK_DETECT_TIME, sinceCooling) self.state = STATES['WAITING_FOR_PEAK_DETECT'] elif self.doPosPeakDetect: self.updateWaitTime(HEAT_PEAK_DETECT_TIME, sinceHeating) self.state = STATES['WAITING_FOR_PEAK_DETECT'] elif self.state in (STATES['COOLING'], STATES['COOLING_MIN_TIME']): self.doNegPeakDetect = True self.lastCoolTime = secs self.updateEstimatedPeak(self.cc.maxCoolTimeForEstimate, self.cs.coolEstimator, sinceIdle) self.state = STATES['COOLING'] # set to cooling here, so the display of COOLING/COOLING_MIN_TIME is correct # stop cooling when estimated fridge temp peak lands on target or if beer is already too cold (1/2 sensor bit idle zone) if (self.cv.estimatedPeak <= self.cs.fridgeSetting or (self.cs.mode != MODES['MODE_FRIDGE_CONSTANT'] and beerFast < (self.cs.beerSetting - 0.03125))): if (sinceIdle > MIN_COOL_ON_TIME): self.cv.negPeakEstimate = self.cv.estimatedPeak # remember estimated peak when I switch to IDLE, to adjust estimator later self.state = STATES['IDLE'] # break else: self.state = STATES['COOLING_MIN_TIME'] # break elif self.state in (STATES['HEATING'], STATES['HEATING_MIN_TIME']): self.doPosPeakDetect = True self.lastHeatTime = secs self.updateEstimatedPeak(self.cc.maxHeatTimeForEstimate, self.cs.heatEstimator, sinceIdle) self.state = STATES[ 'HEATING'] # reset to heating here, so the display of HEATING/HEATING_MIN_TIME is correct # stop heating when estimated fridge temp peak lands on target or if beer is already too warm (1/2 sensor bit idle zone) if (self.cv.estimatedPeak >= self.cs.fridgeSetting or (self.cs.mode != MODES['MODE_FRIDGE_CONSTANT'] and beerFast > (self.cs.beerSetting + 0.03125))): if (sinceIdle > MIN_HEAT_ON_TIME): self.cv.posPeakEstimate = self.cv.estimatedPeak # remember estimated peak when I switch to IDLE, to adjust estimator later self.state = STATES['IDLE'] # break else: self.state = STATES['HEATING_MIN_TIME'] # break elif self.state in (STATES['DOOR_OPEN']): pass # do nothing else: logging.debug("Unknown state in updatePID: %s" % self.state)
def updateState(self): print("Update state. Mode %s, state %s" % ({v: k for k, v in MODES.items() }[self.cs.mode], {v: k for k, v in STATES.items()}[self.state])) stayIdle = False newDoorOpen = self.door.isOpen if (newDoorOpen != self.doorOpen): self.doorOpen = newDoorOpen print("Fridge door %s" % ('opened' if self.doorOpen else 'closed')) self.piLink.printFridgeAnnotation( "Fridge door %s" % ("opened" if self.doorOpen else "closed")) if (self.cs.mode == MODES['MODE_OFF']): self.state = STATES['STATE_OFF'] stayIdle = True # stay idle when one of the required sensors is disconnected, # or the fridge setting is INVALID_TEMP # if(isDisabledOrInvalid(cs.fridgeSetting) || # !fridgeSensor->isConnected() || # (!beerSensor->isConnected() && tempControl.modeIsBeer())): # self.state = 'IDLE' # stayIdle = True # Stay idle if the fridge sensor isn't connected # TODO - Make sure this didn't break beer settings/profiles elif not self.cs.fridgeSetting or not self.fridgeSensor.temperature: self.state = STATES['IDLE'] stayIdle = True elif not self.beerSensor.temperature and ( self.cs.mode == MODES['MODE_BEER_CONSTANT'] or self.cs.mode == MODES['MODE_BEER_PROFILE']): self.state = STATES['IDLE'] stayIdle = True sinceIdle = self.timeSinceIdle() sinceCooling = self.timeSinceCooling() sinceHeating = self.timeSinceHeating() fridgeFast = self.fridgeSensor.readFastFiltered() beerFast = self.beerSensor.readFastFiltered() secs = ticks.seconds() if self.state in (STATES['IDLE'], STATES['STATE_OFF'], STATES['WAITING_TO_COOL'], STATES['WAITING_TO_HEAT'], STATES['WAITING_FOR_PEAK_DETECT']): self.lastIdleTime = secs if not stayIdle: # set waitTime to zero. It will be set to the maximum # required waitTime below when wait is in effect. # if(stayIdle): # break self.resetWaitTime() if (fridgeFast > (self.cs.fridgeSetting + self.cc.idleRangeHigh)): # fridge temperature is too high self.updateWaitTime(MIN_SWITCH_TIME, sinceHeating) if (self.cs.mode == MODES['MODE_FRIDGE_CONSTANT']): self.updateWaitTime(MIN_COOL_OFF_TIME_FRIDGE_CONSTANT, sinceCooling) else: if ( beerFast < (self.cs.beerSetting + 0.03125) ): # + 16) ): # If beer is already under target, stay/go to idle. 1/2 sensor bit idle zone self.state = STATES[ 'IDLE'] # beer is already colder than setting, stay in or go to idle # break # FIXME: We need to skip the next if statement else: self.updateWaitTime(MIN_COOL_OFF_TIME, sinceCooling) if (self.cooler != None): # FIXME was &defaultActuator): if (self.getWaitTime() > 0): self.state = STATES['WAITING_TO_COOL'] else: self.state = STATES['COOLING'] elif (fridgeFast < (self.cs.fridgeSetting + self.cc.idleRangeLow)): # fridge temperature is too low print("Fridge temperature is too low") self.updateWaitTime(MIN_SWITCH_TIME, sinceCooling) self.updateWaitTime(MIN_HEAT_OFF_TIME, sinceHeating) if (self.cs.mode != MODES['MODE_FRIDGE_CONSTANT']): if ( beerFast > (self.cs.beerSetting - 0.03125) ): # - 16)){ // If beer is already over target, stay/go to idle. 1/2 sensor bit idle zone self.state = STATES[ 'IDLE'] # beer is already warmer than setting, stay in or go to idle # break # FIXME: We need to skip the next if statement # if(self.heater != &defaultActuator or (self.lightAsHeater and (self.light != &defaultActuator))): # FIXME what is &defaultActuator ? if ((self.heater != None or (self.cc.lightAsHeater and (self.light != None)))): if (self.getWaitTime() > 0): self.state = STATES['WAITING_TO_HEAT'] else: self.state = STATES['HEATING'] else: self.state = STATES[ 'IDLE'] # within IDLE range, always go to IDLE # break if (self.state == STATES['HEATING'] or self.state == STATES['COOLING']): # If peak detect is not finished, but the fridge wants to switch to heat/cool # Wait for peak detection and show on display if self.doNegPeakDetect: self.updateWaitTime(COOL_PEAK_DETECT_TIME, sinceCooling) self.state = STATES['WAITING_FOR_PEAK_DETECT'] elif self.doPosPeakDetect: self.updateWaitTime(HEAT_PEAK_DETECT_TIME, sinceHeating) self.state = STATES['WAITING_FOR_PEAK_DETECT'] elif self.state in (STATES['COOLING'], STATES['COOLING_MIN_TIME']): self.doNegPeakDetect = True self.lastCoolTime = secs self.updateEstimatedPeak(self.cc.maxCoolTimeForEstimate, self.cs.coolEstimator, sinceIdle) self.state = STATES[ 'COOLING'] # set to cooling here, so the display of COOLING/COOLING_MIN_TIME is correct # stop cooling when estimated fridge temp peak lands on target or if beer is already too cold (1/2 sensor bit idle zone) if (self.cv.estimatedPeak <= self.cs.fridgeSetting or (self.cs.mode != MODES['MODE_FRIDGE_CONSTANT'] and beerFast < (self.cs.beerSetting - 0.03125))): if (sinceIdle > MIN_COOL_ON_TIME): self.cv.negPeakEstimate = self.cv.estimatedPeak # remember estimated peak when I switch to IDLE, to adjust estimator later self.state = STATES['IDLE'] # break else: self.state = STATES['COOLING_MIN_TIME'] # break elif self.state in (STATES['HEATING'], STATES['HEATING_MIN_TIME']): self.doPosPeakDetect = True self.lastHeatTime = secs self.updateEstimatedPeak(self.cc.maxHeatTimeForEstimate, self.cs.heatEstimator, sinceIdle) self.state = STATES[ 'HEATING'] # reset to heating here, so the display of HEATING/HEATING_MIN_TIME is correct # stop heating when estimated fridge temp peak lands on target or if beer is already too warm (1/2 sensor bit idle zone) if (self.cv.estimatedPeak >= self.cs.fridgeSetting or (self.cs.mode != MODES['MODE_FRIDGE_CONSTANT'] and beerFast > (self.cs.beerSetting + 0.03125))): if (sinceIdle > MIN_HEAT_ON_TIME): self.cv.posPeakEstimate = self.cv.estimatedPeak # remember estimated peak when I switch to IDLE, to adjust estimator later self.state = STATES['IDLE'] # break else: self.state = STATES['HEATING_MIN_TIME'] # break elif self.state in (STATES['DOOR_OPEN']): pass # do nothing else: logging.debug("Unknown state in updatePID: %s" % self.state)