Skip to content
3 changes: 3 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@
],
install_requires=[
"Click>=7.0",
"packaging",
"prompt-toolkit>=2.0.10,<3.0",
"pyserial>=3.4",
"pyusb",
"requests",
"setuptools",
"setuptools_scm",
"setuptools_scm_git_archive",
Expand Down
85 changes: 85 additions & 0 deletions src/druid/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@

import click

import requests
import os
from packaging import version

from druid import __version__
from druid.crow import Crow
from druid import repl as druid_repl
from druid import pydfu

@click.group(invoke_without_command=True)
@click.pass_context
Expand Down Expand Up @@ -44,6 +49,86 @@ def upload(filename):
time.sleep(0.3)
click.echo(crow.read(1000000))

@cli.command(short_help="Update crow firmware")
def firmware():
""" Update crow firmware
"""
print("Checking for updates...")
git_query = requests.get('https://raw.githubusercontent.com/monome/crow/main/version.txt')
git_data = git_query.text.split()
print(">> git version", git_data[0])

with Crow() as crow:
local_version = "none"
try:
crow.connect()
except:
print("No crow found, or might be in bootloader mode already...")
local_version = "0"

# crow found: clear script and read version
if local_version != "0":
crow.write("crow.reset()")
time.sleep(0.1)
c = crow.read(1000000)
crow.write("^^v")
tmp = (crow.read(100)).split("'")
local_version = tmp[1][1:]

print(">> local version: ", local_version)

if version.parse(local_version) >= version.parse(git_data[0]):
print("Up to date.")
exit()

# delete old crow.dfu if exists
if os.path.exists("crow.dfu"):
os.remove("crow.dfu")

print("Downloading new version:", git_data[1])
res = requests.get(git_data[1])
with open('crow.dfu', 'wb') as fwfile:
fwfile.write(res.content)

if local_version != "0":
crow.write('^^b')
time.sleep(1.0)
print("Crow bootloader enabled.")

try:
pydfu.init()
except ValueError:
print("Error: pydfu didn't find crow!")
exit()

elements = pydfu.read_dfu_file("crow.dfu")
if not elements:
return
print("Writing memory...")
pydfu.write_elements(elements, False, progress=pydfu.cli_progress)

print("Exiting DFU...")
pydfu.exit_dfu()

os.remove("crow.dfu")
print("Update complete.")


@cli.command(short_help="Clear userscript")
def clearscript():
""" Clear userscript from crow' flash memory """
try:
pydfu.init()
except ValueError:
print("Error: pydfu didn't find crow! Check you've forced the bootloader.")
exit()
print("Clearing userscript...")
# note we must write a single byte of 0x00 but are primarily just triggering erase of the flash page
pydfu.write_elements([{"addr":0x08010000, "size": 1, "data": [0]}], False, progress=pydfu.cli_progress)
print("Complete. Exiting DFU...")
pydfu.exit_dfu()


@cli.command()
@click.argument("filename", type=click.Path(exists=True), required=False)
def repl(filename):
Expand Down
10 changes: 5 additions & 5 deletions src/druid/crow.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@

logger = logging.getLogger(__name__)

def find_serial_port(hwid):
def find_serial_port():
for portinfo in serial.tools.list_ports.comports():
if hwid in portinfo.hwid:
if "crow" in portinfo.product:
return portinfo
raise DeviceNotFoundError(f"can't find device {hwid}")
raise DeviceNotFoundError(f"can't find crow device")

class Crow:
def __init__(self, serial=None):
Expand All @@ -23,7 +23,7 @@ def __init__(self, serial=None):
self.event_handlers = {}

def find_device(self):
portinfo = find_serial_port('USB VID:PID=0483:5740')
portinfo = find_serial_port()
try:
return serial.Serial(
portinfo.device,
Expand Down Expand Up @@ -62,7 +62,7 @@ def raise_event(self, event, *args, **kwargs):

def replace_handlers(self, handlers):
self.event_handlers = handlers

def reconnect(self, err_event=False):
try:
self.connect()
Expand Down
Loading