diff --git a/api/opentrons/drivers/smoothie_drivers/driver_3_0.py b/api/opentrons/drivers/smoothie_drivers/driver_3_0.py index 399314d9f3f..c81c6a80ad2 100755 --- a/api/opentrons/drivers/smoothie_drivers/driver_3_0.py +++ b/api/opentrons/drivers/smoothie_drivers/driver_3_0.py @@ -269,8 +269,21 @@ def connect(self): self._setup() def disconnect(self): + if self._connection: + self._connection.close() self.simulating = True + def is_connected(self): + if not self._connection: + return False + return self._connection.is_open + + @property + def port(self): + if not self._connection: + return None + return self._connection.port + def get_fw_version(self): version = 'Virtual Smoothie' if not self.simulating: diff --git a/api/opentrons/robot/robot.py b/api/opentrons/robot/robot.py index dba59946b25..ec4c1e899b0 100755 --- a/api/opentrons/robot/robot.py +++ b/api/opentrons/robot/robot.py @@ -953,7 +953,7 @@ def get_serial_ports_list(self): def is_connected(self): if not self._driver: return False - return not self._driver.simulating + return self._driver.is_connected() def is_simulating(self): if not self._driver: diff --git a/api/opentrons/server/endpoints/update.py b/api/opentrons/server/endpoints/update.py index 5fcb33b1881..d44b8f4aa09 100644 --- a/api/opentrons/server/endpoints/update.py +++ b/api/opentrons/server/endpoints/update.py @@ -3,6 +3,8 @@ import asyncio from aiohttp import web +from opentrons import robot + log = logging.getLogger(__name__) @@ -20,6 +22,39 @@ async def _install(filename, loop): return res +async def _update_firmware(filename, loop): + # ensure there is a reference to the port + if not robot.is_connected(): + robot.connect() + + # get port name + port = str(robot._driver.port) + # set smoothieware into programming mode + robot._driver._smoothie_programming_mode() + # close the port so other application can access it + robot._driver._connection.close() + + # run lpc21isp, THIS WILL TAKE AROUND 1 MINUTE TO COMPLETE + update_cmd = 'lpc21isp -wipe -donotstart {0} {1} {2} 12000'.format( + filename, port, robot.config.serial_speed) + proc = await asyncio.create_subprocess_shell( + update_cmd, + stdout=asyncio.subprocess.PIPE, + loop=loop) + rd = await proc.stdout.read() + res = rd.decode().strip() + await proc.wait() + + # re-open the port + robot._driver._connection.open() + # reset smoothieware + robot._driver._smoothie_reset() + # run setup gcodes + robot._driver._setup() + + return res + + async def install_api(request): """ This handler accepts a POST request with Content-Type: multipart/form-data @@ -53,3 +88,39 @@ async def install_api(request): type(e), data, e.__traceback__)}, status=500) return res + + +async def update_firmware(request): + """ + This handler accepts a POST request with Content-Type: multipart/form-data + and a file field in the body named "hex". The file should be a valid HEX + image to be flashed to the LPC1769. The received file is flashed using + lpc21isp, and then deleted and a success code is returned. + """ + log.debug('Update Firmware request received') + data = await request.post() + try: + filename = data['hex'].filename + log.info('Flashing image "{}", this will take about 1 minute'.format( + filename)) + content = data['hex'].file.read() + + with open(filename, 'wb') as wf: + wf.write(content) + + msg = await _update_firmware(filename, request.loop) + log.debug('Firmware Update complete') + try: + os.remove(filename) + except OSError: + pass + log.debug("Result: {}".format(msg)) + res = web.json_response({ + 'message': msg, + 'filename': filename}) + except Exception as e: + res = web.json_response( + {'error': 'Exception {} raised by update of {}. Trace: {}'.format( + type(e), data, e.__traceback__)}, + status=500) + return res diff --git a/api/opentrons/server/main.py b/api/opentrons/server/main.py index 2f3320de8db..0db8dffecbb 100755 --- a/api/opentrons/server/main.py +++ b/api/opentrons/server/main.py @@ -82,6 +82,8 @@ def init(loop=None): server.app.router.add_post('/lights/on', control.turn_on_rail_lights) server.app.router.add_post('/lights/off', control.turn_off_rail_lights) server.app.router.add_post('/server/update', update.install_api) + server.app.router.add_post( + '/server/update_firmware', update.update_firmware) server.app.router.add_post('/server/restart', control.restart) server.app.router.add_get('/calibration/deck', dc_endp.start) server.app.router.add_post('/calibration/deck', dc_endp.dispatch) diff --git a/compute/scripts/lpc21isp b/compute/scripts/lpc21isp new file mode 100755 index 00000000000..935e9513d41 Binary files /dev/null and b/compute/scripts/lpc21isp differ