Пример #1
0
    class OTA:
        def __init__(self):
            self.part = Partition(Partition.RUNNING).get_next_update()
            self.sha = hashlib.sha256()
            self.seq = 0
            self.block = 0
            self.buf = bytearray(BLOCKLEN)
            self.buflen = 0

        # handle processes one message with a chunk of data in msg. The sequence number seq needs
        # to increment sequentially and the last call needs to have last==True as well as the
        # sha set to the hashlib.sha256(entire_data).hexdigest().
        def handle(self, sha, msg, seq, last):
            if self.seq is None:
                raise ValueError("missing first message")
            elif self.seq < seq:
                # "duplicate message"
                log.warning("Duplicate OTA message seq=%d", seq)
                return None
            elif self.seq > seq:
                raise ValueError("message missing")
            else:
                self.seq += 1
            self.sha.update(msg)
            # avoid allocating memory: use buf as-is
            msglen = len(msg)
            if self.buflen + msglen >= BLOCKLEN:
                # got a full block, assemble it and write to flash
                cpylen = BLOCKLEN - self.buflen
                self.buf[self.buflen:BLOCKLEN] = msg[:cpylen]
                self.part.writeblocks(self.block, self.buf)
                self.block += 1
                msglen -= cpylen
                if msglen > 0:
                    self.buf[:msglen] = msg[cpylen:]
                self.buflen = msglen
            else:
                self.buf[self.buflen:self.buflen + msglen] = msg
                self.buflen += msglen
                if last and self.buflen > 0:
                    for i in range(BLOCKLEN - self.buflen):
                        self.buf[self.buflen + i] = 0xFF  # erased flash is ff
                    self.part.writeblocks(self.block, self.buf)
                    self.block += 1
            assert len(self.buf) == BLOCKLEN
            if last:
                return self.finish(sha)
            elif (seq & 7) == 0:
                # log.info("Sending ACK {}".format(seq))
                return "SEQ {}".format(seq).encode()

        def finish(self, check_sha):
            del self.buf
            self.seq = None
            calc_sha = binascii.hexlify(self.sha.digest())
            check_sha = check_sha.encode()
            if calc_sha != check_sha:
                raise ValueError("SHA mismatch calc:{} check={}".format(
                    calc_sha, check_sha))
            self.part.set_boot()
            return "OK"
Пример #2
0
Файл: ota.py Проект: iot49/iot49
class OTA:

    # constructor, follow by calling ota(...)
    def __init__(self, verbose=False):
        self.verbose = verbose
        # the partition we are writing to
        self.part = Partition(Partition.RUNNING).get_next_update()

        # sha of the new app, computed in _app_data
        self.sha = hashlib.sha256()

        # keeping track (_app_data)
        self.block = 0
        self.buf = bytearray(BLOCKLEN)
        self.buflen = 0  # length of current content of self.buf

    # load app into the next partition and set it as the next one to boot upon restart
    #     :param:  url of app
    #     :sha256: sha256 of app
    def ota(self, url, sha256):
        if sys.platform != 'esp32':
            raise ValueError("N/A")
        if self.verbose: print('OTA ', end='')
        buffer = bytearray(BLOCKLEN)
        mv = memoryview(buffer)
        sock = open_url(url)
        while True:
            sz = sock.readinto(buffer)
            if not sz: break
            self._app_data(mv[0:sz])
        self._finish(sha256)
        buffer = None
        gc.collect()

    # accept chunks of the app and write to self.part
    def _app_data(self, data, last=False):
        global BLOCKLEN, buf, buflen, block
        data_len = len(data)
        self.sha.update(data)
        if self.buflen + data_len >= BLOCKLEN:
            # got a full block, assemble it and write to flash
            cpylen = BLOCKLEN - self.buflen
            self.buf[self.buflen:BLOCKLEN] = data[:cpylen]
            assert len(self.buf) == BLOCKLEN
            if self.verbose: print('.', end='')
            self.part.writeblocks(self.block, self.buf)
            self.block += 1
            data_len -= cpylen
            if data_len > 0:
                self.buf[:data_len] = data[cpylen:]
            self.buflen = data_len
        else:
            self.buf[self.buflen:self.buflen + data_len] = data
            self.buflen += data_len
            if last and self.buflen > 0:
                for i in range(BLOCKLEN - self.buflen):
                    self.buf[self.buflen +
                             i] = 0xFF  # ord('-') # erased flash is ff
                if self.verbose: print('.', end='')
                self.part.writeblocks(self.block, self.buf)
                assert len(self.buf) == BLOCKLEN

    # finish writing the app to the partition and check the sha
    def _finish(self, check_sha):
        # flush the app buffer and complete the write
        self._app_data(b'', last=False)
        self._app_data(b'', last=True)
        del self.buf
        # check the sha
        calc_sha = binascii.hexlify(self.sha.digest())
        check_sha = check_sha.encode()
        if calc_sha != check_sha:
            raise ValueError(
                "SHA mismatch\n    calc:  {}\n    check: {}".format(
                    calc_sha, check_sha))
        self.part.set_boot()
        if self.verbose: print(' Done.')