Skip to content

Instantly share code, notes, and snippets.

@adeguet1
Created May 25, 2023 13:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save adeguet1/76b9b3b75d07b67c5f1a2ebb46a56035 to your computer and use it in GitHub Desktop.
Save adeguet1/76b9b3b75d07b67c5f1a2ebb46a56035 to your computer and use it in GitHub Desktop.
Python script to detect any new SD card and copy some files on it. Used to flash firmware for dVRK controllers.
#
# Script used to monitor new block device (SD card) and copy new dVRK
# firmware files automatically
#
# Anton Deguet
#
import pyudev
import subprocess
import time
import datetime
import os
import shutil
context = pyudev.Context()
monitor = pyudev.Monitor.from_netlink(context)
monitor.filter_by(subsystem='block')
now = datetime.datetime.now()
date_time = now.strftime("%Y-%m-%d-%H.%M.%S")
for device in iter(monitor.poll, None):
if device.action == 'add':
# find the device and path
dir(device)
print('udev device: {}'.format(device))
slash_dev = device.device_node
print('/dev device: {}'.format(slash_dev))
time.sleep(1.0) # 1 second to let the OS mount the device
mount_path = subprocess.run(['findmnt', '-n', '-o', 'TARGET', slash_dev],
stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
print('mount point: {}'.format(mount_path))
# create backup dirctory if needed
existing_files = os.listdir(mount_path)
if existing_files:
# create back dir
backup_path = mount_path + '/backups'
if not os.path.exists(backup_path):
os.mkdir(backup_path)
backup_path = backup_path + '/' + date_time
if not os.path.exists(backup_path):
os.mkdir(backup_path)
# move files
for f in existing_files:
if f != 'backups':
full_file = mount_path + '/' + f
print('move {} to backup directory {}'.format(full_file, backup_path))
shutil.move(full_file, backup_path)
# copy files
files = ['BOOT.bin', 'firmware.xsvf']
for f in files:
print('copy {} to {}'.format(f, mount_path))
shutil.copy(f, mount_path)
# leave a timestamp on card
subprocess.run(['touch', mount_path + '/created-' + date_time],
stdout=subprocess.PIPE)
# and finally, umount
umount_result = subprocess.run(['umount', mount_path],
stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
print('umount {}'.format(umount_result))
@adeguet1
Copy link
Author

Instead of a sleep to wait for the drive to be mounted, we could use a loop to call findmnt with a shorter sleep in the loop. This might be faster and safer (in case 1 second is not enough).

@pkazanzides
Copy link

If files are found, maybe there can be a prompt whether to delete them or archive them in a backup dir? I'll also request for Keshuai to come up with a more descriptive name than firmware.xsvf. Maybe espm-firmware.xsvf or just espm.xsvf.

@keshuaixu
Copy link

#
# Script used to monitor new block device (SD card) and copy new dVRK
# firmware files automatically
#
# Anton Deguet
#

import urllib.request
import os.path
import zipfile
import shutil
import pyudev
import subprocess
import time
import datetime

zip_url = 'https://github.com/jhu-cisst/mechatronics-embedded/releases/latest/download/fpgav3-micro-sd.zip'
zip_filename = 'fpgav3-micro-sd.zip'

if os.path.exists(zip_filename):
    print('Removing', zip_filename)
    os.remove(zip_filename)

if os.path.exists('sd_unzipped'):
    print('Removing unzipped files')
    shutil.rmtree('sd_unzipped')

print('Downloading', zip_url)
urllib.request.urlretrieve(zip_url, zip_filename)

print('Unzipping', zip_filename)
with zipfile.ZipFile(zip_filename, 'r') as zip_ref:
    zip_ref.extractall('sd_unzipped')
print('Ready to write to SD card. Please insert card now.')

context = pyudev.Context()
monitor = pyudev.Monitor.from_netlink(context)
monitor.filter_by(subsystem='block')

now = datetime.datetime.now()
date_time = now.strftime("%Y-%m-%d-%H.%M.%S")

count = 0

for device in iter(monitor.poll, None):
    if device.action == 'add':
        # find the device and path
        dir(device)
        print('udev device: {}'.format(device))
        slash_dev = device.device_node
        print('/dev device: {}'.format(slash_dev))
        time.sleep(1.0) # 1 second to let the OS mount the device
        mount_path = subprocess.run(['findmnt', '-n', '-o', 'TARGET', slash_dev],
                                    stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
        print('mount point: {}'.format(mount_path))
        if not mount_path:
            print('Could not find mount point. Ignored.')
            continue

        existing_files = os.listdir(mount_path)
        if not (os.path.exists(mount_path + '/BOOT.bin') or len(existing_files) == 0):
            print('The drive is not empty and does not appear to be a dVRK card. Aborting. If you want to write to this drive, please empty it first.')
        else:
            # create backup dirctory if needed
            if existing_files:
                # create back dir
                backup_path = mount_path + '/backups'
                if not os.path.exists(backup_path):
                    os.mkdir(backup_path)
                backup_path = backup_path + '/' + date_time
                if not os.path.exists(backup_path):
                    os.mkdir(backup_path)
                # move files
                for f in existing_files:
                    if f != 'backups':
                        full_file = mount_path + '/' + f
                        print('move {} to backup directory {}'.format(full_file, backup_path))
                        shutil.move(full_file, backup_path)

            # copy files
            files = os.listdir('sd_unzipped')
            for f in files:
                print('copy {} to {}'.format(f, mount_path))
                # does not work with directories
                shutil.copy('sd_unzipped/' + f, mount_path)

            # leave a timestamp on card
            subprocess.run(['touch', mount_path + '/created-' + date_time],
                        stdout=subprocess.PIPE)

            # and finally, umount
            umount_result = subprocess.run(['umount', mount_path],
                                        stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
            print('umount {}'.format(umount_result))

            count += 1
            print(f'Done. Please insert the next card. Count: {count}.')

@pkazanzides
Copy link

Looks o.k., but why not unzip the file directly to the SD card (with -o option to overwrite)? That would preserve the original file dates, which may be useful as another check that it is the released version.

@keshuaixu
Copy link

I was trying to save CPU time by only unzipping once, but preserving the original file dates is more important.

@adeguet1
Copy link
Author

For the dVRK, the script has been moved to the main repository: jhu-dvrk/sawIntuitiveResearchKit@1313fba

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment