Exemple #1
0
def launch_dfu(args, logger, cancellation_token):
    """
    Waits for a device that matches args.path and args.serial_number
    and then upgrades the device's firmware.
    """

    serial_number = args.serial_number
    find_odrive_cancellation_token = Event(cancellation_token)

    logger.info("Waiting for ODrive...")

    devices = [None, None]

    # Start background thread to scan for ODrives in DFU mode
    def find_device_in_dfu_mode_thread():
        devices[0] = find_device_in_dfu_mode(serial_number, find_odrive_cancellation_token)
        find_odrive_cancellation_token.set()
    t = threading.Thread(target=find_device_in_dfu_mode_thread)
    t.daemon = True
    t.start()
    

    # Scan for ODrives not in DFU mode
    # We only scan on USB because DFU is only implemented over USB
    devices[1] = odrive.find_any("usb", serial_number,
        find_odrive_cancellation_token, cancellation_token)
    find_odrive_cancellation_token.set()
    
    device = devices[0] or devices[1]
    firmware = FirmwareFromFile(args.file) if args.file else None

    update_device(device, firmware, logger, cancellation_token)
Exemple #2
0
def update_device(device, firmware, logger, cancellation_token):
    """
    Updates the specified device with the specified firmware.
    The device passed to this function can either be in
    normal mode or in DFU mode.
    The firmware should be an instance of Firmware or None.
    If firmware is None, the newest firmware for the device is
    downloaded from GitHub releases.
    """

    if isinstance(device, usb.core.Device):
        serial_number = device.serial_number
        dfudev = DfuDevice(device)
        if (logger._verbose):
            logger.debug("OTP:")
            dump_otp(dfudev)

        # Read hardware version from one-time-programmable memory
        otp_sector = [s for s in dfudev.sectors if s['name'] == 'OTP Memory' and s['addr'] == 0x1fff7800][0]
        otp_data = dfudev.read_sector(otp_sector)
        if otp_data[0] == 0:
            otp_data = otp_data[16:]
        if otp_data[0] == 0xfe:
            hw_version = (otp_data[3], otp_data[4], otp_data[5])
        else:
            hw_version = (0, 0, 0)
    else:
        serial_number = device.__channel__.usb_device.serial_number
        dfudev = None

        # Read hardware version as reported from firmware
        hw_version_major = device.hw_version_major if hasattr(device, 'hw_version_major') else 0
        hw_version_minor = device.hw_version_minor if hasattr(device, 'hw_version_minor') else 0
        hw_version_variant = device.hw_version_variant if hasattr(device, 'hw_version_variant') else 0
        hw_version = (hw_version_major, hw_version_minor, hw_version_variant)

    if hw_version < (3, 5, 0):
        print("  DFU mode is not supported on board version 3.4 or earlier.")
        print("  This is because entering DFU mode on such a device would")
        print("  break the brake resistor FETs under some circumstances.")
        print("Warning: DFU mode is not supported on ODrives earlier than v3.5 unless you perform a hardware mod.")
        if not odrive.utils.yes_no_prompt("Do you still want to continue?", False):
            raise OperationAbortedException()

    fw_version_major = device.fw_version_major if hasattr(device, 'fw_version_major') else 0
    fw_version_minor = device.fw_version_minor if hasattr(device, 'fw_version_minor') else 0
    fw_version_revision = device.fw_version_revision if hasattr(device, 'fw_version_revision') else 0
    fw_version_prerelease = device.fw_version_prerelease if hasattr(device, 'fw_version_prerelease') else True
    fw_version = (fw_version_major, fw_version_minor, fw_version_revision, fw_version_prerelease)

    print("Found ODrive {} ({}) with firmware {}{}".format(
                serial_number,
                get_hw_version_string(hw_version),
                get_fw_version_string(fw_version),
                " in DFU mode" if dfudev is not None else ""))

    if firmware is None:
        if hw_version == (0, 0, 0):
            if dfudev is None:
                suggestion = 'You have to manually flash an up-to-date firmware to make automatic checks work. Run `odrivetool dfu --help` for more info.'
            else:
                suggestion = 'Run "make write_otp" to program the board version.'
            raise Exception('Cannot check online for new firmware because the board version is unknown. ' + suggestion)
        print("Checking online for newest firmware...", end='')
        firmware = get_newest_firmware(hw_version)
        if firmware is None:
            raise Exception("could not find any firmware release for this board version")
        print(" found {}".format(get_fw_version_string(firmware.fw_version)))

    if firmware.fw_version <= fw_version:
        print()
        if firmware.fw_version < fw_version:
            print("Warning: you are about to flash firmware {} which is older than the firmware on the device ({}).".format(
                    get_fw_version_string(firmware.fw_version),
                    get_fw_version_string(fw_version)))
        else:
            print("You are about to flash firmware {} which is the same version as the firmware on the device ({}).".format(
                    get_fw_version_string(firmware.fw_version),
                    get_fw_version_string(fw_version)))
        if not odrive.utils.yes_no_prompt("Do you want to flash this firmware anyway?", False):
            raise OperationAbortedException()

    # load hex file
    # TODO: Either use the elf format or pack a custom format with a manifest.
    # This way we can for instance verify the target board version and only
    # have to publish one file for every board (instead of elf AND hex files).
    hexfile = IntelHex(firmware.get_as_hex())

    logger.debug("Contiguous segments in hex file:")
    for start, end in hexfile.segments():
        logger.debug(" {:08X} to {:08X}".format(start, end - 1))

    # Back up configuration
    if dfudev is None:
        do_backup_config = device.user_config_loaded if hasattr(device, 'user_config_loaded') else False
        if do_backup_config:
            odrive.configuration.backup_config(device, None, logger)
    elif not odrive.utils.yes_no_prompt("The configuration cannot be backed up because the device is already in DFU mode. The configuration may be lost after updating. Do you want to continue anyway?", True):
        raise OperationAbortedException()

    # Put the device into DFU mode if it's not already in DFU mode
    if dfudev is None:
        find_odrive_cancellation_token = Event(cancellation_token)
        put_into_dfu_mode(device, find_odrive_cancellation_token)
        stm_device = find_device_in_dfu_mode(serial_number, cancellation_token)
        find_odrive_cancellation_token.set()
        dfudev = DfuDevice(stm_device)

    logger.debug("Sectors on device: ")
    for sector in dfudev.sectors:
        logger.debug(" {:08X} to {:08X} ({})".format(
            sector['addr'],
            sector['addr'] + sector['len'] - 1,
            sector['name']))

    # fill sectors with data
    touched_sectors = list(populate_sectors(dfudev.sectors, hexfile))

    logger.debug("The following sectors will be flashed: ")
    for sector,_ in touched_sectors:
        logger.debug(" {:08X} to {:08X}".format(sector['addr'], sector['addr'] + sector['len'] - 1))

    # Erase
    try:
        for i, (sector, data) in enumerate(touched_sectors):
            print("Erasing... (sector {}/{})  \r".format(i, len(touched_sectors)), end='', flush=True)
            dfudev.erase_sector(sector)
        print('Erasing... done            \r', end='', flush=True)
    finally:
        print('', flush=True)

    # Flash
    try:
        for i, (sector, data) in enumerate(touched_sectors):
            print("Flashing... (sector {}/{})  \r".format(i, len(touched_sectors)), end='', flush=True)
            dfudev.write_sector(sector, data)
        print('Flashing... done            \r', end='', flush=True)
    finally:
        print('', flush=True)

    # Verify
    try:
        for i, (sector, expected_data) in enumerate(touched_sectors):
            print("Verifying... (sector {}/{})  \r".format(i, len(touched_sectors)), end='', flush=True)
            observed_data = dfudev.read_sector(sector)
            mismatch_pos = get_first_mismatch_index(observed_data, expected_data)
            if not mismatch_pos is None:
                mismatch_pos -= mismatch_pos % 16
                observed_snippet = ' '.join('{:02X}'.format(x) for x in observed_data[mismatch_pos:mismatch_pos+16])
                expected_snippet = ' '.join('{:02X}'.format(x) for x in expected_data[mismatch_pos:mismatch_pos+16])
                raise RuntimeError("Verification failed around address 0x{:08X}:\n".format(sector['addr'] + mismatch_pos) +
                                   "  expected: " + expected_snippet + "\n"
                                   "  observed: " + observed_snippet)
        print('Verifying... done            \r', end='', flush=True)
    finally:
        print('', flush=True)


    # If the flash operation failed for some reason, your device is bricked now.
    # You can unbrick it as long as the device remains powered on.
    # (or always with an STLink)
    # So for debugging you should comment this last part out.

    # Jump to application
    dfudev.jump_to_application(0x08000000)

    logger.info("Waiting for the device to reappear...")
    device = odrive.find_any("usb", serial_number,
                    cancellation_token, cancellation_token, timeout=30)

    if do_backup_config:
        odrive.configuration.restore_config(device, None, logger)
        os.remove(odrive.configuration.get_temp_config_filename(device))

    logger.success("Device firmware update successful.")
Exemple #3
0
            axes_by_name[axis_ctx.name] = axis_ctx

# Ensure mechanical couplings are valid
couplings = []
if test_rig_yaml['couplings'] is None:
    test_rig_yaml['couplings'] = {}
else:
    for coupling in test_rig_yaml['couplings']:
        c = [
            axes_by_name[axis_name] for axis_name in coupling
            if (axis_name in axes_by_name)
        ]
        if len(c) > 1:
            couplings.append(c)

app_shutdown_token = Event()

try:
    for test in all_tests:
        if isinstance(test, ODriveTest):

            def odrv_test_thread(odrv_name):
                odrv_ctx = odrives_by_name[odrv_name]
                logger.notify('* running {} on {}...'.format(
                    type(test).__name__, odrv_name))
                try:
                    test.check_preconditions(
                        odrv_ctx, logger.indent('  {}: '.format(odrv_name)))
                except:
                    raise PreconditionsNotMet()
                test.run_test(odrv_ctx,