def wrapper(*args, **kwargs):
   p = Panda()
   for i in [0, 1, 2, 0xFFFF]:
     p.can_clear(i)
   p.reset()
   p.close()
   f(*args, **kwargs)
Beispiel #2
0
 def wrapper():
   p = Panda()
   for i in [0, 1, 2, 0xFFFF]:
     p.can_clear(i)
   p.reset()
   p.close()
   fn()
Beispiel #3
0
    def wrapper(panda_type=None, **kwargs):
        # Change panda_types to a list
        if panda_type is not None:
            if not isinstance(panda_type, list):
                panda_type = [panda_type]

        # If not done already, get panda serials and their type
        global _panda_serials
        if _panda_serials == None:
            _panda_serials = []
            for serial in Panda.list():
                p = Panda(serial=serial)
                _panda_serials.append((serial, p.get_type()))
                p.close()

        # Find a panda with the correct types and add the corresponding serial
        serials = []
        for p_type in panda_type:
            found = False
            for serial, pt in _panda_serials:
                # Never take the same panda twice
                if (pt == p_type) and (serial not in serials):
                    serials.append(serial)
                    found = True
                    break
            if not found:
                raise IOError(
                    "No unused panda found for type: {}".format(p_type))
        return fn(serials, **kwargs)
 def wrapper(*args, **kwargs):
     for serial in Panda.list():
         p = Panda(serial)
         for i in [0, 1, 2, 3, 0xFFFF]:
             p.can_clear(i)
         p.reset()
         p.close()
     f(*args, **kwargs)
Beispiel #5
0
 def test_usb_fw(self):
   subprocess.check_output(f"cd {BASEDIR} && PEDAL=1 PEDAL_USB=1 scons", shell=True)
   self._flash_over_can(PEDAL_BUS, f"{BASEDIR}board/obj/pedal_usb.bin.signed")
   time.sleep(2)
   p = Panda(PEDAL_SERIAL)
   self.assertTrue(p.is_pedal())
   p.close()
   self.assertTrue(self._listen_can_frames() > 40)
Beispiel #6
0
def init_panda_serials():
    global panda_jungle, _panda_serials
    _panda_serials = []
    panda_jungle.set_panda_power(True)
    time.sleep(5)
    for serial in Panda.list():
        p = Panda(serial=serial)
        _panda_serials.append((serial, p.get_type()))
        p.close()
    print('Found', str(len(_panda_serials)), 'pandas')
Beispiel #7
0
def init_panda_serials():
    global panda_jungle, _panda_serials
    _panda_serials = []
    panda_jungle.set_panda_power(True)
    time.sleep(5)
    for serial in Panda.list():
        if serial not in PANDAS_EXCLUDE:
            p = Panda(serial=serial)
            _panda_serials.append((serial, p.get_type()))
            p.close()
    print(f"Found {len(_panda_serials)} pandas")
def flash_release(path=None, st_serial=None):
    from panda import Panda, PandaDFU
    from zipfile import ZipFile

    def status(x):
        print("\033[1;32;40m" + x + "\033[00m")

    if st_serial is not None:
        # look for Panda
        panda_list = Panda.list()
        if len(panda_list) == 0:
            raise Exception(
                "panda not found, make sure it's connected and your user can access it"
            )
        elif len(panda_list) > 1:
            raise Exception("Please only connect one panda")
        st_serial = panda_list[0]
        print("Using panda with serial %s" % st_serial)

    if path is None:
        print(
            "Fetching latest firmware from github.com/commaai/panda-artifacts")
        r = requests.get(
            "https://raw.githubusercontent.com/commaai/panda-artifacts/master/latest.json"
        )
        url = json.loads(r.text)['url']
        r = requests.get(url)
        print("Fetching firmware from %s" % url)
        path = io.BytesIO(r.content)

    zf = ZipFile(path)
    zf.printdir()

    version = zf.read("version").decode()
    status("0. Preparing to flash " + str(version))

    code_bootstub = zf.read("bootstub.panda.bin")
    code_panda = zf.read("panda.bin")

    # enter DFU mode
    status("1. Entering DFU mode")
    panda = Panda(st_serial)
    panda.reset(enter_bootstub=True)
    panda.reset(enter_bootloader=True)
    time.sleep(1)

    # program bootstub
    status("2. Programming bootstub")
    dfu = PandaDFU(PandaDFU.st_serial_to_dfu_serial(st_serial))
    dfu.program_bootstub(code_bootstub)
    time.sleep(1)

    # flash main code
    status("3. Flashing main code")
    panda = Panda(st_serial)
    panda.flash(code=code_panda)
    panda.close()

    # check for connection
    status("4. Verifying version")
    panda = Panda(st_serial)
    my_version = panda.get_version()
    print("dongle id: %s" % panda.get_serial()[0])
    print(my_version, "should be", version)
    assert (str(version) == str(my_version))

    # done!
    status("6. Success!")
Beispiel #9
0
class ELMCarSimulator():
    def __init__(self,
                 sn,
                 silent=False,
                 can_kbaud=500,
                 can=True,
                 can11b=True,
                 can29b=True,
                 lin=True):
        self.__p = Panda(sn if sn else Panda.list()[0])
        self.__on = True
        self.__stop = False
        self.__silent = silent

        self.__lin_timer = None
        self.__lin_active = False
        self.__lin_enable = lin
        self.__lin_monitor_thread = threading.Thread(target=self.__lin_monitor)

        self.__can_multipart_data = None
        self.__can_kbaud = can_kbaud
        self.__can_extra_noise_msgs = deque()
        self.__can_enable = can
        self.__can11b = can11b
        self.__can29b = can29b
        self.__can_monitor_thread = threading.Thread(target=self.__can_monitor)

    @property
    def panda(self):
        return self.__p

    def stop(self):
        if self.__lin_timer:
            self.__lin_timer.cancel()
            self.__lin_timeout_handler()

        self.__stop = True

    def join(self):
        if self.__lin_monitor_thread.is_alive():
            self.__lin_monitor_thread.join()
        if self.__can_monitor_thread.is_alive():
            self.__can_monitor_thread.join()
        if self.__p:
            print("closing handle")
            self.__p.close()

    def set_enable(self, on):
        self.__on = on

    def start(self):
        self.panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
        if self.__lin_enable:
            self.__lin_monitor_thread.start()
        if self.__can_enable:
            self.__can_monitor_thread.start()

    #########################
    # LIN related functions #
    #########################

    def __lin_monitor(self):
        print("STARTING LIN THREAD")
        self.panda.set_uart_baud(2, 10400)
        self.panda.kline_drain()  # Toss whatever was already there

        lin_buff = bytearray()

        while not self.__stop:
            lin_msg = self.panda.serial_read(2)
            if not lin_msg:
                continue

            lin_buff += lin_msg
            #print("    ** Buff", lin_buff)
            if lin_buff.endswith(
                    b'\x00\xc1\x33\xf1\x81\x66'):  # Leading 0 is wakeup
                lin_buff = bytearray()
                self.__lin_active = True
                print("GOT LIN (KWP FAST) WAKEUP SIGNAL")
                self._lin_send(0x10, b'\xC1\x8F\xE9')
                self.__reset_lin_timeout()
                continue
            if self.__lin_active:
                msglen = lin_buff[0] & 0x7
                if lin_buff[0] & 0xF8 not in (0x80, 0xC0):
                    print("Invalid bytes at start of message")
                    print("    BUFF", lin_buff)
                    continue
                if len(lin_buff) < msglen + 4: continue
                if lin_checksum(lin_buff[:-1]) != lin_buff[-1]: continue
                self.__lin_process_msg(
                    lin_buff[0] & 0xF8,  # Priority
                    lin_buff[1],
                    lin_buff[2],
                    lin_buff[3:-1])
                lin_buff = bytearray()

    def _lin_send(self, to_addr, msg):
        if not self.__silent:
            print("    LIN Reply (%x)" % to_addr, binascii.hexlify(msg))

        PHYS_ADDR = 0x80
        #FUNC_ADDR = 0xC0
        RECV = 0xF1
        #SEND = 0x33 # Car OBD Functional Address
        headers = struct.pack("BBB", PHYS_ADDR | len(msg), RECV, to_addr)
        if not self.__silent:
            print("    Sending LIN", binascii.hexlify(headers + msg),
                  hex(sum(bytearray(headers + msg)) % 0x100))
        self.panda.kline_send(headers + msg)

    def __reset_lin_timeout(self):
        if self.__lin_timer:
            self.__lin_timer.cancel()
        self.__lin_timer = threading.Timer(5, self.__lin_timeout_handler)
        self.__lin_timer.start()

    def __lin_timeout_handler(self):
        print("LIN TIMEOUT")
        self.__lin_timer = None
        self.__lin_active = False

    @property
    def lin_active(self):
        return self.__lin_active

    def __lin_process_msg(self, priority, toaddr, fromaddr, data):
        self.__reset_lin_timeout()

        if not self.__silent and data != b'\x3E':
            print("LIN MSG", "Addr:", hex(toaddr), "obdLen:", len(data),
                  binascii.hexlify(data))

        outmsg = None
        #if data == b'\x3E':
        #    print("KEEP ALIVE")
        #el
        if len(data) > 1:
            outmsg = self._process_obd(data[0], data[1])

        if outmsg:
            obd_header = struct.pack("BB", 0x40 | data[0], data[1])
            if len(outmsg) <= 5:
                self._lin_send(0x10, obd_header + outmsg)
            else:
                first_msg_len = min(4, len(outmsg) % 4) or 4
                self._lin_send(
                    0x10, obd_header + b'\x01' + b'\x00' *
                    (4 - first_msg_len) + outmsg[:first_msg_len])

                for num, i in enumerate(range(first_msg_len, len(outmsg), 4)):
                    self._lin_send(
                        0x10, obd_header +
                        struct.pack('B', (num + 2) % 0x100) + outmsg[i:i + 4])

    #########################
    # CAN related functions #
    #########################

    def __can_monitor(self):
        print("STARTING CAN THREAD")
        self.panda.set_can_speed_kbps(0, self.__can_kbaud)
        self.panda.can_recv()  # Toss whatever was already there

        while not self.__stop:
            for address, ts, data, src in self.panda.can_recv():
                if self.__on and src == 0 and len(data) == 8 and data[0] >= 2:
                    if not self.__silent:
                        print("Processing CAN message", src, hex(address),
                              binascii.hexlify(data))
                    self.__can_process_msg(data[1], data[2], address, ts, data,
                                           src)
                elif not self.__silent:
                    print("Rejecting CAN message", src, hex(address),
                          binascii.hexlify(data))

    def can_mode_11b(self):
        self.__can11b = True
        self.__can29b = False

    def can_mode_29b(self):
        self.__can11b = False
        self.__can29b = True

    def can_mode_11b_29b(self):
        self.__can11b = True
        self.__can29b = True

    def change_can_baud(self, kbaud):
        self.__can_kbaud = kbaud
        self.panda.set_can_speed_kbps(0, self.__can_kbaud)

    def can_add_extra_noise(self, noise_msg, addr=None):
        self.__can_extra_noise_msgs.append((addr, noise_msg))

    def _can_send(self, addr, msg):
        if not self.__silent:
            print("    CAN Reply (%x)" % addr, binascii.hexlify(msg))
        self.panda.can_send(addr, msg + b'\x00' * (8 - len(msg)), 0)
        if self.__can_extra_noise_msgs:
            noise = self.__can_extra_noise_msgs.popleft()
            self.panda.can_send(noise[0] if noise[0] is not None else addr,
                                noise[1] + b'\x00' * (8 - len(noise[1])), 0)

    def _can_addr_matches(self, addr):
        if self.__can11b and (addr == 0x7DF or (addr & 0x7F8) == 0x7E0):
            return True
        if self.__can29b and (addr == 0x18db33f1 or
                              (addr & 0x1FFF00FF) == 0x18da00f1):
            return True
        return False

    def __can_process_msg(self, mode, pid, address, ts, data, src):
        if not self.__silent:
            print("CAN MSG", binascii.hexlify(data[1:1 + data[0]]), "Addr:",
                  hex(address), "Mode:",
                  hex(mode)[2:].zfill(2), "PID:",
                  hex(pid)[2:].zfill(2), "canLen:", len(data),
                  binascii.hexlify(data))

        if self._can_addr_matches(address) and len(data) == 8:
            outmsg = None
            if data[:3] == b'\x30\x00\x00' and len(self.__can_multipart_data):
                if not self.__silent:
                    print("Request for more data")
                outaddr = 0x7E8 if address == 0x7DF or address == 0x7E0 else 0x18DAF110
                msgnum = 1
                while (self.__can_multipart_data):
                    datalen = min(7, len(self.__can_multipart_data))
                    msgpiece = struct.pack(
                        "B",
                        0x20 | msgnum) + self.__can_multipart_data[:datalen]
                    self._can_send(outaddr, msgpiece)
                    self.__can_multipart_data = self.__can_multipart_data[7:]
                    msgnum = (msgnum + 1) % 0x10
                    time.sleep(0.01)

            else:
                outmsg = self._process_obd(mode, pid)

            if outmsg:
                outaddr = 0x7E8 if address == 0x7DF or address == 0x7E0 else 0x18DAF110

                if len(outmsg) <= 5:
                    self._can_send(
                        outaddr,
                        struct.pack("BBB",
                                    len(outmsg) + 2, 0x40 | data[1], pid) +
                        outmsg)
                else:
                    first_msg_len = min(3, len(outmsg) % 7)
                    payload_len = len(outmsg) + 3
                    msgpiece = struct.pack("BBBBB", 0x10 |
                                           ((payload_len >> 8) & 0xF),
                                           payload_len & 0xFF, 0x40 | data[1],
                                           pid, 1) + outmsg[:first_msg_len]
                    self._can_send(outaddr, msgpiece)
                    self.__can_multipart_data = outmsg[first_msg_len:]

    #########################
    # General OBD functions #
    #########################

    def _process_obd(self, mode, pid):
        if mode == 0x01:  # Mode: Show current data
            if pid == 0x00:  # List supported things
                return b"\xff\xff\xff\xfe"  # b"\xBE\x1F\xB8\x10" #Bitfield, random features
            elif pid == 0x01:  # Monitor Status since DTC cleared
                return b"\x00\x00\x00\x00"  # Bitfield, random features
            elif pid == 0x04:  # Calculated engine load
                return b"\x2f"
            elif pid == 0x05:  # Engine coolant temperature
                return b"\x3c"
            elif pid == 0x0B:  # Intake manifold absolute pressure
                return b"\x90"
            elif pid == 0x0C:  # Engine RPM
                return b"\x1A\xF8"
            elif pid == 0x0D:  # Vehicle Speed
                return b"\x53"
            elif pid == 0x10:  # MAF air flow rate
                return b"\x01\xA0"
            elif pid == 0x11:  # Throttle Position
                return b"\x90"
            elif pid == 0x33:  # Absolute Barometric Pressure
                return b"\x90"
        elif mode == 0x09:  # Mode: Request vehicle information
            if pid == 0x02:  # Show VIN
                return b"1D4GP00R55B123456"
            if pid == 0xFC:  # test long multi message. Ligned up for LIN responses
                return b''.join((struct.pack(">BBH", 0xAA, 0xAA, num + 1)
                                 for num in range(80)))
            if pid == 0xFD:  # test long multi message
                parts = (b'\xAA\xAA\xAA' + struct.pack(">I", num)
                         for num in range(80))
                return b'\xAA\xAA\xAA' + b''.join(parts)
            if pid == 0xFE:  # test very long multi message
                parts = (b'\xAA\xAA\xAA' + struct.pack(">I", num)
                         for num in range(584))
                return b'\xAA\xAA\xAA' + b''.join(parts) + b'\xAA'
            if pid == 0xFF:
                return b'\xAA\x00\x00' + \
                       b"".join(((b'\xAA' * 5) + struct.pack(">H", num + 1) for num in range(584)))
Beispiel #10
0
    # 'COMPUTER_BRAKE_REQUEST': 1,
    # 'COMPUTER_BRAKE_REQUEST_2': 1,
    # 'COUNTER': 0,
    # 'CRUISE_BOH2': 0,
    # 'CRUISE_BOH3': 0,
    # 'CRUISE_CANCEL_CMD': 1,
    # 'CRUISE_FAULT_CMD': 0,
    # 'CRUISE_OVERRIDE': 1,
    # 'CRUISE_STATES': 0,
    # 'FCW': 'no_fcw',
    # 'SET_ME_0X80': 128,
    # 'ZEROS_BOH': 0,
    # 'ZEROS_BOH4': 0,
    # 'ZEROS_BOH6': 0}

    print("Press Enter to send braking commands")
    idx_counter = 1
    total_cmds_sent = 0
    try:
        while raw_input() == '':
            for i in range(100):
                cmd = create_brake_command(True, True, True, 0, idx_counter)
                print("Sending: " + str(cmd) + " (#" + str(total_cmds_sent) +
                      ")")
                panda.can_send(cmd[0], cmd[2], 0)
                idx_counter += 1
                idx_counter %= 4
                total_cmds_sent += 1
    except KeyboardInterrupt:
        panda.close()
Beispiel #11
0
if JUNGLE_SERIAL:
    panda_jungle = PandaJungle(JUNGLE_SERIAL)
    panda_jungle.set_panda_power(False)
    time.sleep(2)
    panda_jungle.set_panda_power(True)
    time.sleep(4)
    #panda_jungle.set_can_enable(0, False)
    #panda_jungle.set_can_enable(1, False)
    #panda_jungle.set_can_enable(2, False)

for serial in Panda.list():
    if serial not in H7_PANDAS_EXCLUDE:
        p = Panda(serial=serial)
        if p.get_type() in H7_HW_TYPES:
            _panda_serials.append(serial)
        p.close()

if len(_panda_serials) < 2:
    print("Minimum two H7 type pandas should be connected.")
    assert False


def canfd_test(p_send, p_recv):
    for _ in range(500):
        sent_msgs = defaultdict(set)
        to_send = []
        for _ in range(200):
            bus = random.randrange(3)
            for dlc in range(len(DLC_TO_LEN)):
                address = random.randrange(1, 1 << 29)
                data = bytes(
Beispiel #12
0
def flash_release(path=None, st_serial=None):
    from panda import Panda, PandaDFU, ESPROM, CesantaFlasher
    from zipfile import ZipFile

    def status(x):
        print("\033[1;32;40m" + x + "\033[00m")

    if st_serial == None:
        # look for Panda
        panda_list = Panda.list()
        if len(panda_list) == 0:
            raise Exception(
                "panda not found, make sure it's connected and your user can access it"
            )
        elif len(panda_list) > 1:
            raise Exception("Please only connect one panda")
        st_serial = panda_list[0]
        print("Using panda with serial %s" % st_serial)

    if path == None:
        print(
            "Fetching latest firmware from github.com/commaai/panda-artifacts")
        r = requests.get(
            "https://raw.githubusercontent.com/commaai/panda-artifacts/master/latest.json"
        )
        url = json.loads(r.text)['url']
        r = requests.get(url)
        print("Fetching firmware from %s" % url)
        path = io.StringIO(r.content)

    zf = ZipFile(path)
    zf.printdir()

    version = zf.read("version")
    status("0. Preparing to flash " + version)

    code_bootstub = zf.read("bootstub.panda.bin")
    code_panda = zf.read("panda.bin")

    code_boot_15 = zf.read("boot_v1.5.bin")
    code_boot_15 = code_boot_15[0:2] + "\x00\x30" + code_boot_15[4:]

    code_user1 = zf.read("user1.bin")
    code_user2 = zf.read("user2.bin")

    # enter DFU mode
    status("1. Entering DFU mode")
    panda = Panda(st_serial)
    panda.enter_bootloader()
    time.sleep(1)

    # program bootstub
    status("2. Programming bootstub")
    dfu = PandaDFU(PandaDFU.st_serial_to_dfu_serial(st_serial))
    dfu.program_bootstub(code_bootstub)
    time.sleep(1)

    # flash main code
    status("3. Flashing main code")
    panda = Panda(st_serial)
    panda.flash(code=code_panda)
    panda.close()

    # flashing ESP
    if panda.is_white():
        status("4. Flashing ESP (slow!)")
        align = lambda x, sz=0x1000: x + "\xFF" * ((sz - len(x)) % sz)
        esp = ESPROM(st_serial)
        esp.connect()
        flasher = CesantaFlasher(esp, 230400)
        flasher.flash_write(0x0, align(code_boot_15), True)
        flasher.flash_write(0x1000, align(code_user1), True)
        flasher.flash_write(0x81000, align(code_user2), True)
        flasher.flash_write(0x3FE000, "\xFF" * 0x1000)
        flasher.boot_fw()
        del flasher
        del esp
        time.sleep(1)
    else:
        status("4. No ESP in non-white panda")

    # check for connection
    status("5. Verifying version")
    panda = Panda(st_serial)
    my_version = panda.get_version()
    print("dongle id: %s" % panda.get_serial()[0])
    print(my_version, "should be", version)
    assert (str(version) == str(my_version))

    # done!
    status("6. Success!")
Beispiel #13
0
class HondaCivicCan(object):
    def __init__(self):
        print("Loading CAN DBC file...")
        self.can_msg_parser = load_dbc_file(
            'honda_civic_touring_2016_can_for_cantools.dbc')
        print("Connecting to Panda board...")
        self.panda = Panda()
        print("Ready.")
        # ROS subscribers to send the commands?

    def decode_msg(self, message):
        """Return a dictionary with the decoded message

        Looks like:
        {'STEER_ANGLE_OFFSET': -0.5, 'CHECKSUM': 4, 'COUNTER': 0, 
        'STEER_ANGLE_RATE': -338, 'STEER_WHEEL_ANGLE': 86.5, 'STEER_ANGLE': 89.4}
        """
        # A message looks like:
        # (1024, 59709, bytearray(b'\x00\x00\x02\x00\x02'), 0)
        return self.can_msg_parser.decode_message(message[0], message[2])

    def encode_msg(self, frame_id, message):
        """Return the message in a dictionary encoded.
        """
        return self.can_msg_parser.encode_message(frame_id, message)

    def run(self):
        self.panda.set_safety_mode(self.panda.SAFETY_HONDA)

        try:
            while True:
                data = self.panda.can_recv()
                for msg in data:
                    decoded_msg = None
                    try:
                        decoded_msg = self.decode_msg(msg)
                    except KeyError:
                        pass
                    except ValueError:
                        pass
                    if decoded_msg:
                        # print decoded_msg
                        # continue
                        for k in decoded_msg.keys():
                            if 'STEER_TORQUE' in k:
                                print decoded_msg
                                print msg
                                print "---"
                                steer_msg = create_steering_control(1000, 0)[0]
                                print steer_msg
                                print self.decode_msg(steer_msg)
                                print
                                self.panda.can_send(steer_msg[0], steer_msg[2],
                                                    0)
                                # msg_to_send = self.encode_msg(891, {'CHECKSUM': 15, 'COUNTER': 1, 'WIPERS': 1})
                                # self.panda.can_send(msg[0], '\x00\x00\x01 \x00\x00\x00\x00', 0)

                # for pending_msg in self.pending_msg:
                #     panda.can_send(0xXXXX, pending_msg, 0)
        except KeyboardInterrupt:
            self.panda.close()
Beispiel #14
0
def flash_release(path=None, st_serial=None):
  from panda import Panda, PandaDFU, ESPROM, CesantaFlasher
  from zipfile import ZipFile

  def status(x):
    print("\033[1;32;40m"+x+"\033[00m")

  if st_serial == None:
    # look for Panda
    panda_list = Panda.list()
    if len(panda_list) == 0:
      raise Exception("panda not found, make sure it's connected and your user can access it")
    elif len(panda_list) > 1:
      raise Exception("Please only connect one panda")
    st_serial = panda_list[0]
    print("Using panda with serial %s" % st_serial)

  if path == None:
    print("Fetching latest firmware from github.com/commaai/panda-artifacts")
    r = requests.get("https://raw.githubusercontent.com/commaai/panda-artifacts/master/latest.json")
    url = json.loads(r.text)['url']
    r = requests.get(url)
    print("Fetching firmware from %s" % url)
    path = StringIO.StringIO(r.content)

  zf = ZipFile(path)
  zf.printdir()

  version = zf.read("version")
  status("0. Preparing to flash "+version)

  code_bootstub = zf.read("bootstub.panda.bin")
  code_panda = zf.read("panda.bin")

  code_boot_15 = zf.read("boot_v1.5.bin")
  code_boot_15 = code_boot_15[0:2] + "\x00\x30" + code_boot_15[4:]

  code_user1 = zf.read("user1.bin")
  code_user2 = zf.read("user2.bin")

  # enter DFU mode
  status("1. Entering DFU mode")
  panda = Panda(st_serial)
  panda.enter_bootloader()
  time.sleep(1)

  # program bootstub
  status("2. Programming bootstub")
  dfu = PandaDFU(PandaDFU.st_serial_to_dfu_serial(st_serial))
  dfu.program_bootstub(code_bootstub)
  time.sleep(1)

  # flash main code
  status("3. Flashing main code")
  panda = Panda(st_serial)
  panda.flash(code=code_panda)
  panda.close()

  # flashing ESP
  status("4. Flashing ESP (slow!)")
  align = lambda x, sz=0x1000: x+"\xFF"*((sz-len(x)) % sz)
  esp = ESPROM(st_serial)
  esp.connect()
  flasher = CesantaFlasher(esp, 230400)
  flasher.flash_write(0x0, align(code_boot_15), True)
  flasher.flash_write(0x1000, align(code_user1), True)
  flasher.flash_write(0x81000, align(code_user2), True)
  flasher.flash_write(0x3FE000, "\xFF"*0x1000)
  flasher.boot_fw()
  del flasher
  del esp
  time.sleep(1)

  # check for connection
  status("5. Verifying version")
  panda = Panda(st_serial)
  my_version = panda.get_version()
  print("dongle id: %s" % panda.get_serial()[0])
  print(my_version, "should be", version)
  assert(str(version) == str(my_version))

  # done!
  status("6. Success!")