def save_settings_to_file(self): # This is format" "2019-01-08-16-50-15" oldSettingsFileName = 'settings-{0}.json'.format(time.strftime("%Y-%m-%dT%H-%M-%S")) settingsBackupDir = '{0}settings/controller-backup/'.format(util.addSlash(util.scriptPath())) if not os.path.exists(settingsBackupDir): os.makedirs(settingsBackupDir) # Set owner and permissions for directory fileMode = stat.S_IRWXU | stat.S_IRWXG | stat.S_IROTH | stat.S_IXOTH # 775 owner = 'brewpi' group = 'brewpi' uid = pwd.getpwnam(owner).pw_uid gid = grp.getgrnam(group).gr_gid os.chown(settingsBackupDir, uid, gid) # chown dir os.chmod(settingsBackupDir, fileMode) # chmod dir oldSettingsFilePath = os.path.join( settingsBackupDir, oldSettingsFileName) oldSettingsFile = open(oldSettingsFilePath, 'w') oldSettingsFile.write(json.dumps(self.oldSettings)) oldSettingsFile.truncate() oldSettingsFile.close() # Set owner and permissions for file fileMode = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP # 660 owner = 'brewpi' group = 'brewpi' uid = pwd.getpwnam(owner).pw_uid gid = grp.getgrnam(group).gr_gid os.chown(oldSettingsFilePath, uid, gid) # chown file os.chmod(oldSettingsFilePath, fileMode) # chmod file printStdErr("\nSaved old settings to file {0}.".format(oldSettingsFileName))
def parseProcess(self, process): """ Converts a psutil process into a BrewPiProcess object by parsing the config file it has been called with. Params: a psutil.Process object Returns: BrewPiProcess object """ bp = BrewPiProcess() db_config = None cfg = None try: bp.pid = process._pid try: # If this is a database configured installation, try loading via the process ID db_config = models.BrewPiDevice.objects.get(process_id=bp.pid) except: cfg = [s for s in process.cmdline() if '.cfg' in s] # get config file argument except psutil.NoSuchProcess: # process no longer exists return None if db_config is not None: # If this is a database-configured installation, use the database configuration bp.cfg = util.read_config_from_database_without_defaults(db_config) else: if cfg: cfg = cfg[0] # add full path to config file else: # use default config file location cfg = util.scriptPath() + "/settings/config.cfg" bp.cfg = util.read_config_file_with_defaults(cfg) bp.port = bp.cfg['port'] bp.sock = BrewPiSocket.BrewPiSocket(bp.cfg) return bp
def stopLogging(): global config logMessage("Stopped data logging, as requested in web interface. " + "BrewPi will continue to control temperatures, but will not log any data.") config = util.configSet(configFile, 'beerName', None) config = util.configSet(configFile, 'dataLogging', 'stopped') changeWwwSetting('beerName', None) return {'status': 0, 'statusMessage': "Successfully stopped logging"}
def startNewBrew(newName): global config if len(newName) > 1: # shorter names are probably invalid config = util.configSet(configFile, 'beerName', newName) config = util.configSet(configFile, 'dataLogging', 'active') startBeer(newName) logMessage("Notification: Restarted logging for beer '%s'." % newName) return {'status': 0, 'statusMessage': "Successfully switched to new brew '%s'. " % urllib.unquote(newName) + "Please reload the page."} else: return {'status': 1, 'statusMessage': "Invalid new brew name '%s', " "please enter a name with at least 2 characters" % urllib.unquote(newName)}
def __init__(self, host=None, port=None): self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # find BrewPi's via mdns lookup self.host=host self.port=port self.retries=10 # max reconnect attempts to try when doing a read or write operation self.retryCount=0 # count of reconnect attempts performed BrewPiUtil.logMessage("Connecting to BrewPi " + host + " on port " + str(port)) self.open() self.setTimeout(0.5) # self.timeout=self.sock.gettimeout() self.name=host + ':' + str(port) return
def setFiles(): global config global localJsonFileName global localCsvFileName global wwwJsonFileName global wwwCsvFileName global lastDay global day # create directory for the data if it does not exist beerFileName = config['beerName'] dataPath = util.addSlash( util.addSlash(util.scriptPath()) + 'data/' + beerFileName) wwwDataPath = util.addSlash( util.addSlash(config['wwwPath']) + 'data/' + beerFileName) if not os.path.exists(dataPath): os.makedirs(dataPath) os.chmod(dataPath, 0775) # give group all permissions if not os.path.exists(wwwDataPath): os.makedirs(wwwDataPath) os.chmod(wwwDataPath, 0775) # give group all permissions # Keep track of day and make new data file for each day day = time.strftime("%Y-%m-%d") lastDay = day # define a JSON file to store the data jsonFileName = beerFileName + '-' + day #if a file for today already existed, add suffix if os.path.isfile(dataPath + jsonFileName + '.json'): i = 1 while os.path.isfile(dataPath + jsonFileName + '-' + str(i) + '.json'): i += 1 jsonFileName = jsonFileName + '-' + str(i) localJsonFileName = dataPath + jsonFileName + '.json' brewpiJson.newEmptyFile(localJsonFileName) # Define a location on the web server to copy the file to after it is written wwwJsonFileName = wwwDataPath + jsonFileName + '.json' # Define a CSV file to store the data as CSV (might be useful one day) localCsvFileName = (dataPath + beerFileName + '.csv') wwwCsvFileName = (wwwDataPath + beerFileName + '.csv') # create new empty json file brewpiJson.newEmptyFile(localJsonFileName)
def getNewTemp(scriptPath): with open(util.addSlash(scriptPath) + 'settings/tempProfile.csv', 'rU') as csvfile: dialect = csv.Sniffer().sniff(csvfile.readline()) csvfile.seek(0) temperatureReader = csv.reader(csvfile, dialect) next(temperatureReader ) # Discard the first row, which is the table header prevTemp = None nextTemp = None interpolatedTemp = -99 prevDate = None nextDate = None now = time.mktime( time.localtime()) # Get current time in seconds since epoch for row in temperatureReader: dateString = row[0] try: date = time.mktime( time.strptime(dateString, "%Y-%m-%dT%H:%M:%S")) except ValueError: continue # Skip dates that cannot be parsed try: temperature = float(row[1]) except ValueError: if row[1].strip() == '': # Cell is left empty, this is allowed to disable temperature control in part of the profile temperature = None else: # Invalid number string, skip this row continue prevTemp = nextTemp nextTemp = temperature prevDate = nextDate nextDate = date timeDiff = now - nextDate if timeDiff < 0: if prevDate is None: interpolatedTemp = nextTemp # First set point is in the future break else: if prevTemp is None or nextTemp is None: # When the previous or next temperature is an empty cell, disable temperature control. # This is useful to stop temperature control after a while or to not start right away. interpolatedTemp = None else: interpolatedTemp = ((now - prevDate) / (nextDate - prevDate) * (nextTemp - prevTemp) + prevTemp) interpolatedTemp = round(interpolatedTemp, 2) break if interpolatedTemp == -99: # All set points in the past interpolatedTemp = nextTemp return interpolatedTemp
def open_serial(self, config, baud, timeout): self.close_bg_serial() if self.ser is None: self.ser = util.setupSerial(config, baud, timeout) if self.ser is None: return False return True
def retrieve_settings_from_serial(self): ser = self.ser self.oldSettings.clear() printStdErr("Requesting old settings from %(a)s..." % msg_map) expected_responses = 2 if not self.versionOld.isNewer("0.2.0"): # versions older than 2.0.0 did not have a device manager expected_responses += 1 ser.write("d{}") # installed devices time.sleep(1) ser.write("c") # control constants ser.write("s") # control settings time.sleep(2) while expected_responses: line = ser.readline() if line: line = util.asciiToUnicode(line) if line[0] == 'C': expected_responses -= 1 self.oldSettings['controlConstants'] = json_decode_response(line) elif line[0] == 'S': expected_responses -= 1 self.oldSettings['controlSettings'] = json_decode_response(line) elif line[0] == 'd': expected_responses -= 1 self.oldSettings['installedDevices'] = json_decode_response(line)
def parseProcess(self, process): """ Converts a psutil process into a BrewPiProcess object by parsing the config file it has been called with. :Params: A psutil.Process object :Returns: A BrewPiProcess object """ bp = BrewPiProcess() try: bp.pid = process._pid cfg = [s for s in process.cmdline() if '.cfg' in s] # get config file argument # Get brewpi.py file argument so we can grab path bps = [s for s in process.cmdline() if 'brewpi.py' in s] except psutil.NoSuchProcess: # process no longer exists return None if cfg: cfg = cfg[0] # add full path to config file else: # Get path from arguments and use that to build default path to config cfg = os.path.dirname(str(bps)).translate( str.maketrans('', '', r"[]'")) + '/settings/config.cfg' bp.cfg = util.readCfgWithDefaults(cfg) if bp.cfg['port'] is not None: bp.port = bp.cfg['port'] bp.sock = BrewPiSocket.BrewPiSocket(bp.cfg) return bp
def retrieve_settings_from_serial(self): ser = self.ser self.oldSettings.clear() printStdErr("\nRequesting old settings from %(a)s." % msg_map) expected_responses = 2 # versions older than 2.0.0 did not have a device manager if not self.versionOld.isNewer("0.2.0"): expected_responses += 1 ser.write("d{}".encode()) # installed devices time.sleep(1) ser.write("c".encode()) # control constants ser.write("s".encode()) # control settings time.sleep(2) while expected_responses: line = ser.readline().decode() if line: line = util.asciiToUnicode(str(line)) if line[0] == 'C': expected_responses -= 1 self.oldSettings['controlConstants'] = json_decode_response( line) elif line[0] == 'S': expected_responses -= 1 self.oldSettings['controlSettings'] = json_decode_response( line) elif line[0] == 'd': expected_responses -= 1 self.oldSettings['installedDevices'] = json_decode_response( line)
def open_serial(self, config, baud, timeout): if self.ser: self.ser.close() self.ser = None self.ser = util.setupSerial(config, baud, timeout, 1.0, True) if self.ser is None: return False return True
def resumeLogging(): global config logMessage("Continued logging data, as requested in web interface.") if config['dataLogging'] == 'paused': config = util.configSet(configFile, 'dataLogging', 'active') return {'status': 0, 'statusMessage': "Successfully continued logging."} else: return {'status': 1, 'statusMessage': "Logging was not paused."}
def open_serial(self, config, baud, timeout): if self.ser: self.ser.close() self.ser = None self.ser = util.setupSerial(config, baud, timeout) if self.ser is None: return False return True
def setFiles(): global config global localJsonFileName global localCsvFileName global wwwJsonFileName global wwwCsvFileName global lastDay global day # create directory for the data if it does not exist beerFileName = config['beerName'] dataPath = util.addSlash(util.addSlash(util.scriptPath()) + 'data/' + beerFileName) wwwDataPath = util.addSlash(util.addSlash(config['wwwPath']) + 'data/' + beerFileName) if not os.path.exists(dataPath): os.makedirs(dataPath) os.chmod(dataPath, 0775) # give group all permissions if not os.path.exists(wwwDataPath): os.makedirs(wwwDataPath) os.chmod(wwwDataPath, 0775) # give group all permissions # Keep track of day and make new data file for each day day = time.strftime("%Y-%m-%d") lastDay = day # define a JSON file to store the data jsonFileName = beerFileName + '-' + day #if a file for today already existed, add suffix if os.path.isfile(dataPath + jsonFileName + '.json'): i = 1 while os.path.isfile(dataPath + jsonFileName + '-' + str(i) + '.json'): i += 1 jsonFileName = jsonFileName + '-' + str(i) localJsonFileName = dataPath + jsonFileName + '.json' brewpiJson.newEmptyFile(localJsonFileName) # Define a location on the web server to copy the file to after it is written wwwJsonFileName = wwwDataPath + jsonFileName + '.json' # Define a CSV file to store the data as CSV (might be useful one day) localCsvFileName = (dataPath + beerFileName + '.csv') wwwCsvFileName = (wwwDataPath + beerFileName + '.csv') # create new empty json file brewpiJson.newEmptyFile(localJsonFileName)
def pauseLogging(): global config logMessage("Paused logging data, as requested in web interface. " + "BrewPi will continue to control temperatures, but will not log any data until resumed.") if config['dataLogging'] == 'active': config = util.configSet(configFile, 'dataLogging', 'paused') return {'status': 0, 'statusMessage': "Successfully paused logging."} else: return {'status': 1, 'statusMessage': "Logging already paused or stopped."}
def create(self): """ Creates a socket socket based on the settings in the member variables and assigns it to self.sock This function deletes old sockets for file sockets, so do not use it to connect to a socket that is in use. """ if self.type == 'i': # Internet socket self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.sock.bind((self.host, self.port)) util.logMessage('Bound to TCP socket on port %d ' % self.port) else: if os.path.exists(self.file): # if socket already exists, remove it. This prevents errors when the socket is corrupt after a crash. os.remove(self.file) self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.sock.bind(self.file) # Bind BEERSOCKET # set all permissions for socket os.chmod(self.file, 0777)
def connect(self): """ Connect to the socket represented by BrewPiSocket. Returns a new connected socket object. This function should be called when the socket is created by a different instance of brewpi. """ sock = socket.socket try: if self.type == 'i': # Internet socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) util.logMessage('Bound to existing TCP socket on port %d ' % self.port) sock.connect((self.host, self.port)) else: sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.connect(self.file) except socket.error: sock = False finally: return sock
def parseProcess(self, process): """ Converts a psutil process into a BrewPiProcess object by parsing the config file it has been called with. Params: a psutil.Process object Returns: BrewPiProcess object """ bp = BrewPiProcess() bp.pid = process._pid cfg = [s for s in process.cmdline() if '.cfg' in s] # get config file argument if cfg: cfg = cfg[0] # add full path to config file else: # use default config file location cfg = util.scriptPath() + "/settings/config.cfg" bp.cfg = util.readCfgWithDefaults(cfg) bp.port = bp.cfg['port'] bp.sock = BrewPiSocket.BrewPiSocket(bp.cfg) return bp
def startBeer(beerName): global config global localJsonFileName global localCsvFileName global wwwJsonFileName global wwwCsvFileName global lastDay global day # create directory for the data if it does not exist dataPath = util.addSlash(config["scriptPath"]) + "data/" + beerName + "/" wwwDataPath = util.addSlash(config["wwwPath"]) + "data/" + beerName + "/" if not os.path.exists(dataPath): os.makedirs(dataPath) os.chmod(dataPath, 0775) # give group all permissions if not os.path.exists(wwwDataPath): os.makedirs(wwwDataPath) os.chmod(wwwDataPath, 0775) # sudgive group all permissions # Keep track of day and make new data tabe for each day # This limits data table size, which can grow very big otherwise day = time.strftime("%Y-%m-%d") lastDay = day # define a JSON file to store the data table jsonFileName = config["beerName"] + "-" + day # if a file for today already existed, add suffix if os.path.isfile(dataPath + jsonFileName + ".json"): i = 1 while os.path.isfile(dataPath + jsonFileName + "-" + str(i) + ".json"): i += 1 jsonFileName = jsonFileName + "-" + str(i) localJsonFileName = dataPath + jsonFileName + ".json" brewpiJson.newEmptyFile(localJsonFileName) # Define a location on the web server to copy the file to after it is written wwwJsonFileName = wwwDataPath + jsonFileName + ".json" # Define a CSV file to store the data as CSV (might be useful one day) localCsvFileName = dataPath + config["beerName"] + ".csv" wwwCsvFileName = wwwDataPath + config["beerName"] + ".csv" changeWwwSetting("beerName", beerName)
def getNewTemp(scriptPath): temperatureReader = csv.reader( open(util.addSlash(scriptPath) + 'settings/tempProfile.csv', 'rb'), delimiter=',', quoting=csv.QUOTE_ALL) temperatureReader.next() # discard the first row, which is the table header prevTemp = None nextTemp = None interpolatedTemp = -99 prevDate = None nextDate = None now = time.mktime(time.localtime()) # get current time in seconds since epoch for row in temperatureReader: dateString = row[0] try: date = time.mktime(time.strptime(dateString, "%Y-%m-%dT%H:%M:%S")) except ValueError: continue # skip dates that cannot be parsed try: temperature = float(row[1]) except ValueError: if row[1].strip() == '': # cell is left empty, this is allowed to disable temperature control in part of the profile temperature = None else: # invalid number string, skip this row continue prevTemp = nextTemp nextTemp = temperature prevDate = nextDate nextDate = date timeDiff = now - nextDate if timeDiff < 0: if prevDate is None: interpolatedTemp = nextTemp # first set point is in the future break else: if prevTemp is None or nextTemp is None: # When the previous or next temperature is an empty cell, disable temperature control. # This is useful to stop temperature control after a while or to not start right away. interpolatedTemp = None else: interpolatedTemp = ((now - prevDate) / (nextDate - prevDate) * (nextTemp - prevTemp) + prevTemp) interpolatedTemp = round(interpolatedTemp, 2) break if interpolatedTemp == -99: # all set points in the past interpolatedTemp = nextTemp return interpolatedTemp
def read(self, size=1): #Returns: Bytes read from the port. #Read size bytes from the serial port. If a timeout is set it may return less characters as requested. With no timeout it will block until the requested number of bytes is read. bytes=None try: bytes=self.sock.recv(size) except socket.timeout: # timeout on receive just means there is nothing in the buffer. This is not an error return None except socket.error: # other socket errors probably mean we lost our connection. try to recover it. if self.retryCount < self.retries: self.retryCount=self.retryCount+1 BrewPiUtil.logMessage("Lost connection to controller on read. Attempting to reconnect.") self.sock.close() self.open() bytes=self.read(size) else: self.sock.close() BrewPiUtil.logMessage("Lost connection to controller on read. Exiting.") sys.exit(1) # return None if bytes is not None: if self.retryCount > 0: BrewPiUtil.logMessage("Successfully reconnected to controller on read.") self.retryCount = 0 return bytes
def detect_port(bootLoader=False, my_port=None): """ :return: first detected serial port as tuple: (port, name) :rtype: """ if my_port == "auto": my_port = None port = (None, None) ports = find_compatible_serial_ports(bootLoader=bootLoader, my_port=my_port) try: port = next(ports) except StopIteration: return port try: another_port = next(ports) BrewPiUtil.logMessage("Warning: Multiple compatible ports.") except StopIteration: pass return port
def save_settings_to_file(self): oldSettingsFileName = 'settings-' + time.strftime("%b-%d-%Y-%H-%M-%S") + '.json' settingsBackupDir = util.scriptPath() + '/settings/controller-backup/' if not os.path.exists(settingsBackupDir): os.makedirs(settingsBackupDir, 0777) oldSettingsFilePath = os.path.join(settingsBackupDir, oldSettingsFileName) oldSettingsFile = open(oldSettingsFilePath, 'wb') oldSettingsFile.write(json.dumps(self.oldSettings)) oldSettingsFile.truncate() oldSettingsFile.close() os.chmod(oldSettingsFilePath, 0777) # make sure file can be accessed by all in case the script ran as root printStdErr("Saved old settings to file " + oldSettingsFileName)
def parseProcess(self, process): """ Converts a psutil process into a BrewPiProcess object by parsing the config file it has been called with. Params: a psutil.Process object Returns: BrewPiProcess object """ bp = BrewPiProcess() try: bp.pid = process._pid cfg = [s for s in process.cmdline() if '.cfg' in s] # get config file argument except psutil.NoSuchProcess: # process no longer exists return None if cfg: cfg = cfg[0] # add full path to config file else: # use default config file location cfg = util.scriptPath() + "/settings/config.cfg" bp.cfg = util.readCfgWithDefaults(cfg) bp.port = bp.cfg['port'] bp.sock = BrewPiSocket.BrewPiSocket(bp.cfg) return bp
def write(self, data): #Returns: Number of bytes written. #Raises SerialTimeoutException: # In case a write timeout is configured for the port and the time is exceeded. #Write the string data to the port. try: bytes=self.sock.sendall(data) except socket.timeout: # A write timeout is probably a connection issue if self.retryCount < self.retries: self.retryCount=self.retryCount+1 BrewPiUtil.logMessage("Lost connection to controller on write. Attempting to reconnect.") self.sock.close() self.open() bytes=self.write(data) else: self.sock.close() BrewPiUtil.logMessage("Lost connection to controller on write. Exiting.") sys.exit(1) # return -1 except socket.error: # general errors are most likely to be a timeout disconnect from BrewPi, so try to recover. if self.retryCount < self.retries: BrewPiUtil.logMessage("Lost connection to controller on write. Attempting to reconnect.") self.retryCount=self.retryCount+1 self.sock.close() self.open() bytes=self.write(data) else: self.sock.close() BrewPiUtil.logMessage("Lost connection to controller on write, with socket.error. Exiting.") sys.exit(1) # return -1 if bytes>=0: if self.retryCount > 0: BrewPiUtil.logMessage("Successfully reconnected to controller on write.") self.retryCount = 0 return bytes
def parseProcess(self, process): """ Converts a psutil process into a BrewPiProcess object by parsing the config file it has been called with. Params: a psutil.Process object Returns: BrewPiProcess object """ bp = BrewPiProcess() bp.pid = process._pid cfg = [s for s in process.cmdline if '.cfg' in s] # get config file argument if cfg: cfg = cfg[0] # add full path to config file bp.cfg = util.readCfgWithDefaults(cfg) bp.port = bp.cfg['port'] bp.sock = BrewPiSocket.BrewPiSocket(bp.cfg) return bp
def changeWwwSetting(settingName, value): wwwSettingsFileName = util.addSlash(config['wwwPath']) + 'userSettings.json' if os.path.exists(wwwSettingsFileName): wwwSettingsFile = open(wwwSettingsFileName, 'r+b') try: wwwSettings = json.load(wwwSettingsFile) # read existing settings except json.JSONDecodeError: logMessage("Error in decoding userSettings.json, creating new empty json file") wwwSettings = {} # start with a fresh file when the json is corrupt. else: wwwSettingsFile = open(wwwSettingsFileName, 'w+b') # create new file wwwSettings = {} wwwSettings[settingName] = str(value) wwwSettingsFile.seek(0) wwwSettingsFile.write(json.dumps(wwwSettings)) wwwSettingsFile.truncate() wwwSettingsFile.close()
def open(self): mdnsLocator.locate_brewpi_services() # This causes all the BrewPi devices to resend their mDNS info try: self.sock.connect((self.host, self.port)) BrewPiUtil.logMessage("Successfully connected to controller.") except socket.gaierror as e: BrewPiUtil.logMessage("Unable to connect to BrewPi " + self.host + " on port " + str(self.port) + ". Exiting.") sys.exit(1) except socket.error as e: # Catches "bad file descriptor" error BrewPiUtil.logMessage("Unable to connect to BrewPi " + self.host + " on port " + str(self.port) + ". Exiting.") sys.exit(1)
def __init__(self, cfg): """ Creates a BrewPi socket object and reads the settings from a BrewPi ConfigObj. Does not create a socket, just prepares the settings. Args: cfg: a ConfigObj object form a BrewPi config file """ self.type = 'f' # default to file socket self.file = None self.host = 'localhost' self.port = None self.sock = 0 isWindows = sys.platform.startswith('win') useInternetSocket = bool(cfg.get('useInternetSocket', isWindows)) if useInternetSocket: self.port = cfg.get('socketPort', 6332) self.type = 'i' else: self.file = util.addSlash(cfg['scriptPath']) + 'BEERSOCKET'
def lineFromSerial(ser): global serialBuffer inWaiting = None newData = None try: inWaiting = ser.inWaiting() if inWaiting > 0: newData = ser.read(inWaiting) except (IOError, OSError, SerialException) as e: logMessage('Serial Error: {0})'.format(str(e))) return if newData: serialBuffer = serialBuffer + newData if '\n' in serialBuffer: lines = serialBuffer.partition('\n') # returns 3-tuple with line, separator, rest if(lines[1] == ''): # '\n' not found, first element is incomplete line serialBuffer = lines[0] return None else: # complete line received, [0] is complete line [1] is separator [2] is the rest serialBuffer = lines[2] return util.asciiToUnicode(lines[0])
def save_settings(self): ser, oldSettings = self.ser, self.oldSettings oldSettings.clear() printStdErr("Requesting old settings from %(a)s..." % msg_map) expected_responses = 2 if self.avrVersionOld.minor > 1: # older versions did not have a device manager expected_responses += 1 ser.write("d{}") # installed devices time.sleep(1) ser.write("c") # control constants ser.write("s") # control settings time.sleep(2) while expected_responses: line = ser.readline() if line: if line[0] == 'C': expected_responses -= 1 oldSettings['controlConstants'] = json_decode_response(line) elif line[0] == 'S': expected_responses -= 1 oldSettings['controlSettings'] = json_decode_response(line) elif line[0] == 'd': expected_responses -= 1 oldSettings['installedDevices'] = json_decode_response(line) oldSettingsFileName = 'oldAvrSettings-' + time.strftime("%b-%d-%Y-%H-%M-%S") + '.json' scriptDir = util.scriptPath() # <-- absolute dir the script is in if not os.path.exists(scriptDir + '/settings/avr-backup/'): os.makedirs(scriptDir + '/settings/avr-backup/') oldSettingsFile = open(scriptDir + '/settings/avr-backup/' + oldSettingsFileName, 'wb') oldSettingsFile.write(json.dumps(oldSettings)) oldSettingsFile.truncate() oldSettingsFile.close() printStdErr("Saved old settings to file " + oldSettingsFileName)
def updateFromGitHub(userInput = False, restoreSettings = True, restoreDevices = True): import BrewPiUtil as util from gitHubReleases import gitHubReleases import brewpiVersion import programController as programmer configFile = util.scriptPath() + '/settings/config.cfg' config = util.readCfgWithDefaults(configFile) printStdErr("Stopping any running instances of BrewPi to check/update controller...") quitBrewPi(config['wwwPath']) hwVersion = None shield = None board = None boardName = None family = None ser = None ### Get version number printStdErr("\nChecking current firmware version...") try: ser = util.setupSerial(config) hwVersion = brewpiVersion.getVersionFromSerial(ser) family = hwVersion.family shield = hwVersion.shield board = hwVersion.board boardName = hwVersion.boardName() except: printStdErr("Unable to connect to controller, perhaps it is disconnected or otherwise unavailable.") return -1 if ser: ser.close() # close serial port ser = None if not hwVersion: printStdErr("Unable to retrieve firmware version from controller") printStdErr("If your controller has not been programmed with an earlier version of BrewPi," " follow these instructions:") printStdErr("\n If you have an Arduino:") printStdErr("Please go to https://github.com/BrewPi/firmware/releases to download" "the firmware and upload via the BrewPi web interface") printStdErr("\n If you have a Spark Core:") printStdErr("Put it in DFU mode and run: sudo /home/brewpi/utils/flashDfu.py") else: printStdErr("Current firmware version on controller: " + hwVersion.toString()) printStdErr("\nChecking GitHub for latest release...") releases = gitHubReleases("https://api.github.com/repos/BrewPi/firmware") latest = releases.getLatestTag() printStdErr("Latest version on GitHub: " + latest) if hwVersion.isNewer(latest): printStdErr("\nVersion on GitHub is newer than your current version, downloading new version...") else: printStdErr("\nYour current firmware version is up-to-date. There is no need to update.") if userInput: printStdErr("If you are encountering problems, you can reprogram anyway." " Would you like to do this?" "\nType yes to reprogram or just press enter to keep your current firmware: ") choice = raw_input() if not any(choice == x for x in ["yes", "Yes", "YES", "yes", "y", "Y"]): return 0 printStdErr("Would you like me to try to restore you settings after programming? [Y/n]: ") choice = raw_input() if not any(choice == x for x in ["", "yes", "Yes", "YES", "yes", "y", "Y"]): restoreSettings = False printStdErr("Would you like me to try to restore your configured devices after programming? [Y/n]: ") choice = raw_input() if not any(choice == x for x in ["", "yes", "Yes", "YES", "yes", "y", "Y"]): restoreDevices = False else: return 0 printStdErr("Downloading latest firmware...") localFileName = None if family == "Arduino": localFileName = releases.getBin(latest, [boardName, shield, ".hex"]) elif family == "Spark": localFileName = releases.getBin(latest, [boardName, ".bin"]) if localFileName: printStdErr("Latest firmware downloaded to " + localFileName) else: printStdErr("Downloading firmware failed") return -1 result = programmer.programController(config, board, localFileName, {'settings': restoreSettings, 'devices': restoreDevices}) util.removeDontRunFile(config['wwwPath'] + "/do_not_run_brewpi") return result
# along with BrewPi. If not, see <http://www.gnu.org/licenses/>. import msvcrt import sys import os import simplejson as json # append parent directory to be able to import files sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/..") import expandLogMessage import BrewPiUtil as util # Read in command line arguments if len(sys.argv) < 2: print >> sys.stderr, 'Using default config path ./settings/config.cfg, to override use : %s <config file full path>' % sys.argv[0] configFile = util.addSlash(sys.path[0]) + '../settings/config.cfg' else: configFile = sys.argv[1] if not os.path.exists(configFile): sys.exit('ERROR: Config file "%s" was not found!' % configFile) config = util.readCfgWithDefaults(configFile) print "***** BrewPi Windows Test Terminal ****" print "This simple Python script lets you send commands to the Arduino." print "It also echoes everything the Arduino returns." print "On known debug ID's in JSON format, it expands the messages to the full message" print "press 's' to send a string to the Arduino, press 'q' to quit" # open serial port
choice = raw_input("\nThe update script can automatically check your Arduino version and " + "program it with the latest hex file from the BrewPi server, would you like to do this now? [Y/n]:") if (choice is "") or (choice is "Y") or (choice is "y") or (choice is "yes") or (choice is "YES"): print "Stopping any running instances of BrewPi to check/update hex file..." quitBrewPi(webPath) ### Check arduino hex file version against current brewpi version print "\nChecking Arduino hex file version..." try: import BrewPiUtil as util except: print "Error reading config util path" configFile = scriptPath + '/settings/config.cfg' config = util.readCfgWithDefaults(configFile) ### Get version number try: import brewpiVersion ser, conn = util.setupSerial(config) hwVersion = brewpiVersion.getVersionFromSerial(ser) shield = hwVersion.shield board = hwVersion.board if "standard" in board: board = "uno" with open(scriptPath + '/brewpi.py', 'r') as versionFile: for line in versionFile: if 'compatibleHwVersion =' in line: bpVersion = line.split("= ")[1].replace("\"", "") break
# GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with BrewPi. If not, see <http://www.gnu.org/licenses/>. import os import sys from configobj import ConfigObj import programArduino as programmer import BrewPiUtil as util # Read in command line arguments if len(sys.argv) < 2: sys.exit('Usage: %s <config file full path>' % sys.argv[0]) if not os.path.exists(sys.argv[1]): sys.exit('ERROR: Config file "%s" was not found!' % sys.argv[1]) configFile = sys.argv[1] config = ConfigObj(configFile) # global variables, will be initialized by startBeer() util.readCfgWithDefaults(configFile) hexFile = config['wwwPath'] + 'uploads/brewpi-uno-revC.hex' boardType = config['boardType'] result = programmer.programArduino(config, boardType, hexFile, {'settings': True, 'devices': True}) print result
def updateFromGitHub(userInput, beta, useDfu, restoreSettings=True, restoreDevices=True): import BrewPiUtil as util from gitHubReleases import gitHubReleases import brewpiVersion import programController as programmer configFile = util.scriptPath() + "/settings/config.cfg" config = util.readCfgWithDefaults(configFile) printStdErr("Stopping any running instances of BrewPi to check/update controller...") quitBrewPi(config["wwwPath"]) hwVersion = None shield = None board = None family = None ser = None ### Get version number printStdErr("\nChecking current firmware version...") try: ser = util.setupSerial(config) hwVersion = brewpiVersion.getVersionFromSerial(ser) family = hwVersion.family shield = hwVersion.shield board = hwVersion.board printStdErr("Found " + hwVersion.toExtendedString() + " on port " + ser.name + "\n") except: if hwVersion is None: printStdErr( "Unable to receive version from controller.\n" "Is your controller unresponsive and do you wish to try restoring your firmware? [y/N]: " ) choice = raw_input() if not any(choice == x for x in ["yes", "Yes", "YES", "yes", "y", "Y"]): printStdErr("Please make sure your controller is connected properly and try again.") return 0 port, name = autoSerial.detect_port() if not port: printStdErr("Could not find compatible device in available serial ports.") return 0 if "Particle" in name: family = "Particle" if "Photon" in name: board = "photon" elif "Core" in name: board = "core" elif "Arduino" in name: family = "Arduino" if "Leonardo" in name: board = "leonardo" elif "Uno" in name: board = "uno" if board is None: printStdErr("Unable to connect to controller, perhaps it is disconnected or otherwise unavailable.") return -1 else: printStdErr("Will try to restore the firmware on your %s" % name) if family == "Arduino": printStdErr( "Assuming a Rev C shield. If this is not the case, please program your Arduino manually" ) shield = "RevC" else: printStdErr( "Please put your controller in DFU mode now by holding the setup button during reset, until the LED blinks yellow." ) printStdErr("Press Enter when ready.") choice = raw_input() useDfu = True # use dfu mode when board is not responding to serial if ser: ser.close() # close serial port ser = None if hwVersion: printStdErr("Current firmware version on controller: " + hwVersion.toString()) else: restoreDevices = False restoreSettings = False printStdErr("\nChecking GitHub for available release...") releases = gitHubReleases("https://api.github.com/repos/BrewPi/firmware") availableTags = releases.getTags(beta) stableTags = releases.getTags(False) compatibleTags = [] for tag in availableTags: url = None if family == "Arduino": url = releases.getBinUrl(tag, [board, shield, ".hex"]) elif family == "Spark" or family == "Particle": url = releases.getBinUrl(tag, [board, "brewpi", ".bin"]) if url is not None: compatibleTags.append(tag) if len(compatibleTags) == 0: printStdErr("No compatible releases found for %s %s" % (family, board)) return -1 # default tag is latest stable tag, or latest unstable tag if no stable tag is found default_choice = next((i for i, t in enumerate(compatibleTags) if t in stableTags), compatibleTags[0]) tag = compatibleTags[default_choice] if userInput: print("\nAvailable releases:\n") for i, menu_tag in enumerate(compatibleTags): print("[%d] %s" % (i, menu_tag)) print("[" + str(len(compatibleTags)) + "] Cancel firmware update") num_choices = len(compatibleTags) while 1: try: choice = raw_input( "Enter the number [0-%d] of the version you want to program [default = %d (%s)]: " % (num_choices, default_choice, tag) ) if choice == "": break else: selection = int(choice) except ValueError: print("Use the number! [0-%d]" % num_choices) continue if selection == num_choices: return False # choice = skip updating try: tag = compatibleTags[selection] except IndexError: print("Not a valid choice. Try again") continue break else: printStdErr("Latest version on GitHub: " + tag) if hwVersion is not None and not hwVersion.isNewer(tag): if hwVersion.isEqual(tag): printStdErr("You are already running version %s." % tag) else: printStdErr("Your current version is newer than %s." % tag) if userInput: printStdErr( "If you are encountering problems, you can reprogram anyway." " Would you like to do this?" "\nType yes to reprogram or just press enter to keep your current firmware: " ) choice = raw_input() if not any(choice == x for x in ["yes", "Yes", "YES", "yes", "y", "Y"]): return 0 else: printStdErr("No update needed. Exiting.") exit(0) if hwVersion is not None and userInput: printStdErr("Would you like me to try to restore you settings after programming? [Y/n]: ") choice = raw_input() if not any(choice == x for x in ["", "yes", "Yes", "YES", "yes", "y", "Y"]): restoreSettings = False printStdErr("Would you like me to try to restore your configured devices after programming? [Y/n]: ") choice = raw_input() if not any(choice == x for x in ["", "yes", "Yes", "YES", "yes", "y", "Y"]): restoreDevices = False printStdErr("Downloading firmware...") localFileName = None system1 = None system2 = None if family == "Arduino": localFileName = releases.getBin(tag, [board, shield, ".hex"]) elif family == "Spark" or family == "Particle": localFileName = releases.getBin(tag, [board, "brewpi", ".bin"]) else: printStdErr("Error: Device family {0} not recognized".format(family)) return -1 if board == "photon": if hwVersion: oldVersion = hwVersion.version.vstring else: oldVersion = "0.0.0" latestSystemTag = releases.getLatestTagForSystem(prerelease=beta, since=oldVersion) if latestSystemTag is not None: printStdErr("Updated system firmware for the photon found in release {0}".format(latestSystemTag)) system1 = releases.getBin(latestSystemTag, ["photon", "system-part1", ".bin"]) system2 = releases.getBin(latestSystemTag, ["photon", "system-part2", ".bin"]) if system1: printStdErr("Downloaded new system firmware to:\n") printStdErr("{0}\nand\n".format(system1)) if system2: printStdErr("{0}\n".format(system2)) else: printStdErr("Error: system firmware part2 not found in release") return -1 else: printStdErr("Photon system firmware is up to date.\n") if localFileName: printStdErr("Latest firmware downloaded to:\n" + localFileName) else: printStdErr("Downloading firmware failed") return -1 printStdErr("\nUpdating firmware...\n") result = programmer.programController( config, board, localFileName, system1, system2, useDfu, {"settings": restoreSettings, "devices": restoreDevices} ) util.removeDontRunFile(config["wwwPath"] + "/do_not_run_brewpi") return result
print "Updating system firmware for the {0}, part 2: {1}".format(device_type, system2) p = subprocess.Popen(dfuPath + " -d {0} -a 0 -s 0x8060000 -D {1}".format(pid_vid[device_type], system2), shell=True) p.wait() time.sleep(1) print "Now writing BrewPi firmware {0} to {1}".format(binFile, device_type) p = subprocess.Popen(dfuPath + " -d {0} -a 0 -s 0x80A0000:leave -D {1}".format(pid_vid[device_type], binFile), shell=True) p.wait() time.sleep(1) print "Programming done" if not noReset: print "Now resetting EEPROM to defaults" # reset EEPROM to defaults configFile = util.scriptPath() + '/settings/config.cfg' config = util.readCfgWithDefaults(configFile) config_copy = config if 'socket:' in config['port']: print "Socket configured as serial port, using auto detect to find USB serial" config_copy['port'] = 'auto' raw_input("Press Enter to continue...") programmer = SerialProgrammer.create(config_copy, device_type) # open serial port print "Opening serial port to reset factory defaults" if not programmer.open_serial_with_retry(config_copy, 57600, 1): print "Could not open serial port after programming" else: programmer.fetch_version("Success! ")
if len(allProcesses.update()) > 1: # if I am not the only one running allProcesses.quitAll() time.sleep(2) if len(allProcesses.update()) > 1: print "Asking the other processes to quit nicely did not work. Killing them with force!" # redirect output of stderr and stdout to files in log directory if o in ('-l', '--log'): logToFiles = True # only start brewpi when the dontrunfile is not found if o in ('-d', '--dontrunfile'): checkDontRunFile = True if o in ('--checkstartuponly'): checkStartupOnly = True if not configFile: configFile = util.addSlash(sys.path[0]) + 'settings/config.cfg' config = util.readCfgWithDefaults(configFile) dontRunFilePath = config['wwwPath'] + 'do_not_run_brewpi' # check dont run file when it exists and exit it it does if checkDontRunFile: if os.path.exists(dontRunFilePath): # do not print anything, this will flood the logs exit(0) # check for other running instances of BrewPi that will cause conflicts with this instance allProcesses = BrewPiProcess.BrewPiProcesses() allProcesses.update() myProcess = allProcesses.me() if allProcesses.findConflicts(myProcess):
if len(allProcesses.update()) > 1: # if I am not the only one running allProcesses.quitAll() time.sleep(2) if len(allProcesses.update()) > 1: printStdErr("Asking the other processes to quit nicely did not work. Killing them with force!") # redirect output of stderr and stdout to files in log directory if o in ('-l', '--log'): logToFiles = True # only start brewpi when the dontrunfile is not found if o in ('-d', '--dontrunfile'): checkDontRunFile = True if o in ('--checkstartuponly'): checkStartupOnly = True if not configFile: configFile = util.addSlash(sys.path[0]) + 'settings/config.cfg' config = util.readCfgWithDefaults(configFile) dontRunFilePath = os.path.join(config['wwwPath'], 'do_not_run_brewpi') # check dont run file when it exists and exit it it does if checkDontRunFile: if os.path.exists(dontRunFilePath): # do not print anything, this will flood the logs exit(0) # check for other running instances of BrewPi that will cause conflicts with this instance allProcesses = BrewPiProcess.BrewPiProcesses() allProcesses.update() myProcess = allProcesses.me() if allProcesses.findConflicts(myProcess):
import msvcrt import sys import os # import simplejson as json import json # append parent directory to be able to import files sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/..") import expandLogMessage import BrewPiUtil as util # Read in command line arguments if len(sys.argv) < 2: print >> sys.stderr, 'Using default config path ./settings/config.cfg, to override use : %s <config file full path>' % sys.argv[ 0] configFile = util.addSlash(sys.path[0]) + '../settings/config.cfg' else: configFile = sys.argv[1] if not os.path.exists(configFile): sys.exit('ERROR: Config file "%s" was not found!' % configFile) config = util.readCfgWithDefaults(configFile) print "***** BrewPi Windows Test Terminal ****" print "This simple Python script lets you send commands to the Arduino." print "It also echoes everything the Arduino returns." print "On known debug ID's in JSON format, it expands the messages to the full message" print "press 's' to send a string to the Arduino, press 'q' to quit" # open serial port
time.sleep(2) if len(allProcesses.update()) > 1: printStdErr( "Asking the other processes to quit nicely did not work. Killing them with force!" ) # redirect output of stderr and stdout to files in log directory if o in ('-l', '--log'): logToFiles = True # only start brewpi when the dontrunfile is not found if o in ('-d', '--dontrunfile'): checkDontRunFile = True if o in ('--checkstartuponly'): checkStartupOnly = True if not configFile: configFile = util.addSlash(sys.path[0]) + 'settings/config.cfg' config = util.readCfgWithDefaults(configFile) dontRunFilePath = os.path.join(config['wwwPath'], 'do_not_run_brewpi') # check dont run file when it exists and exit it it does if checkDontRunFile: if os.path.exists(dontRunFilePath): # do not print anything, this will flood the logs exit(0) # check for other running instances of BrewPi that will cause conflicts with this instance allProcesses = BrewPiProcess.BrewPiProcesses() allProcesses.update() myProcess = allProcesses.me() if allProcesses.findConflicts(myProcess):
# complete line received, [0] is complete line [1] is separator [2] is the rest self.buffer = lines[2] return self.__asciiToUnicode(lines[0]) # remove extended ascii characters from string, because they can raise UnicodeDecodeError later def __asciiToUnicode(self, s): s = s.replace(chr(0xB0), '°') return unicode(s, 'ascii', 'ignore') if __name__ == '__main__': # some test code that requests data from serial and processes the response json import simplejson import time import BrewPiUtil as util config_file = util.addSlash(sys.path[0]) + 'settings/config.cfg' config = util.readCfgWithDefaults(config_file) ser = util.setupSerial(config, time_out=0) if not ser: printStdErr("Could not open Serial Port") exit() bg_ser = BackGroundSerial(ser) bg_ser.start() success = 0 fail = 0 for i in range(1, 5): # request control variables 4 times. This would overrun buffer if it was not read in a background thread # the json decode will then fail, because the message is clipped bg_ser.write('v')
# You should have received a copy of the GNU General Public License # along with BrewPi. If not, see <http://www.gnu.org/licenses/>. import serial import msvcrt import sys import os import simplejson as json import expandLogMessage import BrewPiUtil as util # Read in command line arguments if len(sys.argv) < 2: print >> sys.stderr, 'Using default config path ./settings/config.cfg, to override use : %s <config file full path>' % sys.argv[0] configFile = util.addSlash(sys.path[0]) + 'settings/config.cfg' else: configFile = sys.argv[1] if not os.path.exists(configFile): sys.exit('ERROR: Config file "%s" was not found!' % configFile) config = util.readCfgWithDefaults(configFile) print "***** BrewPi Windows Test Terminal ****" print "This simple Python script lets you send commands to the Arduino." print "It also echoes everything the Arduino returns." print "On known debug ID's in JSON format, it expands the messages to the full message" print "press 's' to send a string to the Arduino, press 'q' to quit" ser = 0
def flash_file(self, hexFile): config, boardType = self.config, self.boardType printStdErr("\nLoading programming settings from board.txt.") # location of Arduino sdk arduinohome = config.get('arduinoHome', '/usr/share/arduino/') # location of avr tools avrdudehome = config.get( 'avrdudeHome', arduinohome + 'hardware/tools/') # default to empty string because avrsize is on path avrsizehome = config.get('avrsizeHome', '') # location of global avr conf avrconf = config.get('avrConf', avrdudehome + 'avrdude.conf') boardsFile = loadBoardsFile(arduinohome) if not boardsFile: return False boardSettings = fetchBoardSettings(boardsFile, boardType) # parse the Arduino board file to get the right program settings for line in boardsFile: line = line.decode() if line.startswith(boardType): # strip board name, period and \n _boardType = (boardType + '.').encode() setting = line.encode().replace(_boardType, ''.encode(), 1).strip() [key, sign, val] = setting.rpartition('='.encode()) boardSettings[key] = val printStdErr("\nChecking hex file size with avr-size.") # start programming the Arduino avrsizeCommand = avrsizehome + 'avr-size ' + "\"" + hexFile + "\"" # check program size against maximum size p = sub.Popen(avrsizeCommand, stdout=sub.PIPE, stderr=sub.PIPE, shell=True) output, errors = p.communicate() #if errors != "": # printStdErr('\navr-size error: {0}'.format(errors)) # return False programSize = output.split()[7] printStdErr('\nProgram size: {0} bytes out of max {1}.'.format(programSize.decode(), boardSettings['upload.maximum_size'])) # Another check just to be sure! if int(programSize.decode()) > int(boardSettings['upload.maximum_size']): printStdErr( "\nERROR: Program size is bigger than maximum size for your Arduino {0}.".format(boardType)) return False hexFileDir = os.path.dirname(hexFile) hexFileLocal = os.path.basename(hexFile) time.sleep(1) # Get serial port while in bootloader bootLoaderPort = util.findSerialPort(bootLoader=True, my_port=config['port']) # bootLoaderPort = util.findSerialPort(bootLoader=True) if not bootLoaderPort: printStdErr("\nERROR: Could not find port in bootloader.") programCommand = (avrdudehome + 'avrdude' + ' -F' + # override device signature check ' -e' + # erase flash and eeprom before programming. This prevents issues with corrupted EEPROM ' -p ' + boardSettings['build.mcu'] + ' -c ' + boardSettings['upload.protocol'] + ' -b ' + boardSettings['upload.speed'] + ' -P ' + bootLoaderPort + ' -U ' + 'flash:w:' + "\"" + hexFileLocal + "\"" + ' -C ' + avrconf) printStdErr("\nProgramming Arduino with avrdude.") p = sub.Popen(programCommand, stdout=sub.PIPE, stderr=sub.PIPE, shell=True, cwd=hexFileDir) output, errors = p.communicate() output = output.decode() errors = errors.decode() # avrdude only uses stderr, append its output to the returnString printStdErr("\nResult of invoking avrdude:{0}".format(errors)) if("bytes of flash verified" in errors): printStdErr("Avrdude done, programming successful.") else: printStdErr("There was an error while programming.") return False printStdErr("\nGiving the Arduino 10 seconds to reset.") self.delay(10) return True
oldSettings["controlConstants"] = json.loads(line[2:]) elif line[0] == "S": oldSettings["controlSettings"] = json.loads(line[2:]) elif line[0] == "d": oldSettings["installedDevices"] = json.loads(line[2:]) except json.decoder.JSONDecodeError, e: printStdErr("JSON decode error: " + str(e)) printStdErr("Line received was: " + line) ser.close() del ser # Arduino won't reset when serial port is not completely removed oldSettingsFileName = "oldAvrSettings-" + time.strftime("%b-%d-%Y-%H-%M-%S") + ".json" printStdErr("Saving old settings to file " + oldSettingsFileName) scriptDir = util.scriptPath() # <-- absolute dir the script is in if not os.path.exists(scriptDir + "/settings/avr-backup/"): os.makedirs(scriptDir + "/settings/avr-backup/") oldSettingsFile = open(scriptDir + "/settings/avr-backup/" + oldSettingsFileName, "wb") oldSettingsFile.write(json.dumps(oldSettings)) oldSettingsFile.truncate() oldSettingsFile.close() printStdErr("Loading programming settings from board.txt") # parse the Arduino board file to get the right program settings for line in boardsFile: if line.startswith(boardType): # strip board name, period and \n
def programArduino(config, boardType, hexFile, restoreWhat): printStdErr("**** Arduino Program script started ****") arduinohome = config.get('arduinoHome', '/usr/share/arduino/') # location of Arduino sdk avrdudehome = config.get('avrdudeHome', arduinohome + 'hardware/tools/') # location of avr tools avrsizehome = config.get('avrsizeHome', '') # default to empty string because avrsize is on path avrconf = config.get('avrConf', avrdudehome + 'avrdude.conf') # location of global avr conf boardsFile = loadBoardsFile(arduinohome) boardSettings = fetchBoardSettings(boardsFile, boardType) restoreSettings = False restoreDevices = False if 'settings' in restoreWhat: if restoreWhat['settings']: restoreSettings = True if 'devices' in restoreWhat: if restoreWhat['devices']: restoreDevices = True # Even when restoreSettings and restoreDevices are set to True here, # they might be set to false due to version incompatibility later printStdErr("Settings will " + ("" if restoreSettings else "not ") + "be restored" + (" if possible" if restoreSettings else "")) printStdErr("Devices will " + ("" if restoreDevices else "not ") + "be restored" + (" if possible" if restoreSettings else "")) ser, port = openSerial(config['port'], config['altport'], 57600, 0.2) if ser is None: printStdErr("Could not open serial port. Programming aborted.") return 0 time.sleep(5) # give the arduino some time to reboot in case of an Arduino UNO printStdErr("Checking old version before programming.") avrVersionOld = brewpiVersion.getVersionFromSerial(ser) if avrVersionOld is None: printStdErr(("Warning: Cannot receive version number from Arduino. " + "Your Arduino is either not programmed yet or running a very old version of BrewPi. " "Arduino will be reset to defaults.")) else: printStdErr("Found " + avrVersionOld.toExtendedString() + \ " on port " + port + "\n") oldSettings = {} # request all settings from board before programming if avrVersionOld is not None: printStdErr("Requesting old settings from Arduino...") if avrVersionOld.minor > 1: # older versions did not have a device manager ser.write("d{}") # installed devices time.sleep(1) ser.write("c") # control constants ser.write("s") # control settings time.sleep(2) for line in ser: try: if line[0] == 'C': oldSettings['controlConstants'] = json.loads(line[2:]) elif line[0] == 'S': oldSettings['controlSettings'] = json.loads(line[2:]) elif line[0] == 'd': oldSettings['installedDevices'] = json.loads(line[2:]) except json.decoder.JSONDecodeError, e: printStdErr("JSON decode error: " + str(e)) printStdErr("Line received was: " + line) oldSettingsFileName = 'oldAvrSettings-' + time.strftime("%b-%d-%Y-%H-%M-%S") + '.json' printStdErr("Saving old settings to file " + oldSettingsFileName) scriptDir = util.scriptPath() # <-- absolute dir the script is in if not os.path.exists(scriptDir + '/settings/avr-backup/'): os.makedirs(scriptDir + '/settings/avr-backup/') oldSettingsFile = open(scriptDir + '/settings/avr-backup/' + oldSettingsFileName, 'wb') oldSettingsFile.write(json.dumps(oldSettings)) oldSettingsFile.truncate() oldSettingsFile.close()
print "Updating system firmware for the {0}, part 2: {1}".format(device_type, system2) p = subprocess.Popen(dfuPath + " -d {0} -a 0 -s 0x8060000 -D {1}".format(pid_vid[device_type], system2), shell=True) p.wait() time.sleep(1) print "Now writing BrewPi firmware {0} to {1}".format(binFile, device_type) p = subprocess.Popen(dfuPath + " -d {0} -a 0 -s 0x80A0000:leave -D {1}".format(pid_vid[device_type], binFile), shell=True) p.wait() time.sleep(1) print "Programming done" if not noReset: print "Now resetting EEPROM to defaults" # reset EEPROM to defaults configFile = util.scriptPath() + '/settings/config.cfg' config = util.readCfgWithDefaults(configFile) config_copy = config if 'socket:' in config['port']: print "Socket configured as serial port, using auto detect to find USB serial" config_copy['port'] = 'auto' programmer = SerialProgrammer.create(config_copy, device_type) # open serial port print "Opening serial port to reset factory defaults" if not programmer.open_serial_with_retry(config_copy, 57600, 1): print "Could not open serial port after programming" else: programmer.fetch_version("Success! ") time.sleep(5) programmer.reset_settings(testMode)