From bc51e3a9918ba94d57f1858a35e58ced65994d83 Mon Sep 17 00:00:00 2001 From: Sergio Costas Date: Tue, 8 Dec 2020 17:09:03 +0100 Subject: [PATCH] Added python flasher Added a python port of the flasher. It only works with already 'freed' devices, thus can be used to easily upload new firmware when doing tests, without having to open the devices. --- flash.py | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100755 flash.py diff --git a/flash.py b/flash.py new file mode 100755 index 00000000..44454d7e --- /dev/null +++ b/flash.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 + +import bluepy +import sys +import time +import struct + +class Flash(object): + def __init__(self): + super().__init__() + self._firmware = None + self._device = None + self._miEnabled = False + self._customEnabled = False + + def loadFirmware(self, firmware_name): + with open(firmware_name, "rb") as firmware_file: + firmware = firmware_file.read() + signature = struct.unpack(">L",firmware[8:12])[0] + if signature != 0x4b4e4c54: + print("The file is not a valid firmware file. Aborting.") + sys.exit(-1) + print(f"The firmware is {len(firmware)} bytes in length.") + while 0 != (len(firmware) % 16): + firmware += bytes([0xff]) + self._firmware = firmware + self._blockCount = int(len(firmware) / 16) + print(f"Count: {int(self._blockCount)}") + + def disconnect(self): + if not self._doTest: + self._device.disconnect() + + def connect(self, mac, doTest): + self._doTest = doTest + if doTest: + self._customAction() + else: + self._device = bluepy.btle.Peripheral(mac) + services = self._device.getServices() + self._services = {} + for service in services: + self._services[str(service.uuid)] = service + self._service = self._services['00010203-0405-0607-0809-0a0b0c0d1912'] + self._writeCharacteristic = self._service.getCharacteristics(forUUID='00010203-0405-0607-0809-0a0b0c0d2b12')[0] + self._detectMi() + + def _detectMi(self): + self._miEnabled = "ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6" in self._services + self._customEnabled = "00001f10-0000-1000-8000-00805f9b34fb" in self._services + + if self._miEnabled: + print("Detected Mi Thermometer. Can't flash it.") + self.disconnect() + sys.exit(-1) + elif self._customEnabled: + print("Detected device with valid custom Firmware") + self._customAction() + else: + print("Detected device with not valid Firmware. Can't flash it.") + self.disconnect() + sys.exit(-1) + + def _customAction(self): + self._otaCharSend(bytes([0x00, 0xff])) + self._otaCharSend(bytes([0x01, 0xff])) + if self._doTest: + endline = "\n" + else: + endline = "\r" + for c in range(self._blockCount): + print(f"Sending block {c} of {self._blockCount} ({int(100 * c / self._blockCount)}%)", end=endline) + offset = c * 16 + blockCount = struct.pack("> 1) & 0x7fff + if odd != 0: + crc ^= 0xa001 + return struct.pack("