Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 15 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,24 @@ That is it! Modify `pico_usb.txt` to change the functionality. See below to know

- pico_usb.txt - here is where your executable pseudo-code is located.
- layout.txt - here is where you select your keyboard layout.
- code.py - interpreter that executes your pesudo code. Free to modify. (1)
- boot.py - this code executes before the USB is recognised. Free to modify. (1)
- code.py - interpreter that executes your pesudo code. Free to modify.
- boot.py - this code executes before the USB is recognised. Free to modify.

**pico_usb.txt API:**

- delay() - waits for the specified amount of time before resuming execution. Example: delay(0.8)
- press() - presses one or more buttons once. For example to press enter, use `press(enter)`. To "select all", use `press(control + a)`.
- write() - sequentially presses many buttons in a row. example: `write(Hello world!)`
- hold() - presses and holds down one or more buttons until `release()` is called
- release() - releases **all** held keys
- move(x, y) - moves the mouse on the main display to the given location, from the current location as a reference. negative x = left, possitive x = right, negative y = down, possitive y = up.
- click(btn)- clicks the mouse. `btn` is the mouse button, options are left, right, middle
- scroll(x) - scrolls the mouse. Negative number scrolls down, possitive scroll up
- volume(x) - Modifies the system volume. Negative numbers move the volume slider down by x, possitive move it up by x. min volume = 0. max = 100. `volume(mute)` mutes the speakers.
- loop() - loops everything before this command
- `#` - must be the first character in the line. That line will be ignored.
- `delay` - waits for the specified amount of time before resuming execution. Example: `delay 0.8`
- `press` - presses one or more buttons once. For example to press enter, use `press enter`. To "select all", use `press control + a`. Press also causes the Pico to release all keys after being executed.
- `write` - sequentially presses many buttons in a row. example: `write Hello world!`
- `writefile` - reads a file and uses the contents as keypresses. Make sure the file isn't too large (>100kB). example: `writefile commands.txt`
- `hold` - presses and holds down one or more buttons until `release` is called
- `release` - releases **all** held keys
- `move x, y` - moves the mouse on the main display to the given location, from the current location as a reference. negative x = left, possitive x = right, negative y = down, possitive y = up.
- `click btn` - clicks the mouse. `btn` is the mouse button, options are left, right, middle
- `scroll` - scrolls the mouse. Negative number scrolls down, possitive scroll up
- `volume x` - Modifies the system volume. Negative numbers move the volume slider down by x, possitive move it up by x. min volume = 0. max = 100. `volume mute` mutes the speakers.
- `loop` - loops everything after this command up until the end of the file
- `loop x` - same as loop but stops after x times

## Development

Expand Down
16 changes: 5 additions & 11 deletions src/boot.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,16 @@
storage.remount("/", readonly=False)
m = storage.getmount("/")
m.label = "PicoUSB"
storage.remount("/", readonly=True)
storage.enable_usb_drive()

time.sleep(0.1) #wait a bit so the button gets pulled up
time.sleep(0.05) # Wait a bit so the button gets pulled up

if mode.value:
storage.disable_usb_drive()
else:
time.sleep(0.1) #check again after 100ms to see if the button is still pressed
if mode.value:
storage.disable_usb_drive()
else:
storage.enable_usb_drive()
microcontroller.on_next_reset(microcontroller.RunMode.SAFE_MODE)
microcontroller.reset()

storage.remount("/", readonly=True)
storage.enable_usb_drive()
microcontroller.on_next_reset(microcontroller.RunMode.SAFE_MODE)
microcontroller.reset()

# in case you screw up and disable usb drive without the ability to enable it, to enter safe mode write in shell:
# import microcontroller
Expand Down
229 changes: 114 additions & 115 deletions src/code.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import io
import os
import re
import time
import board
import storage
Expand All @@ -13,10 +11,15 @@
from adafruit_hid.keycode import Keycode
from adafruit_hid.mouse import Mouse

from exceptions import PicoCommandException, PicoLayoutException, PicoKeyboardException

cc = ConsumerControl(usb_hid.devices)
kb = Keyboard(usb_hid.devices)
ms = Mouse(usb_hid.devices)

from adafruit_hid.keyboard_layout_us import KeyboardLayout
layout = KeyboardLayout(kb)

bt = digitalio.DigitalInOut(board.GP25)
bt.direction = digitalio.Direction.INPUT
bt.pull = digitalio.Pull.UP
Expand All @@ -25,137 +28,133 @@
led.direction = digitalio.Direction.OUTPUT
led.value = True

looping = False
loop_pos = 0

def execute_command(function, command):
if function == "DELAY":
if command.isdigit():
time.sleep(float(command))
def change_layout(layout_id: str):
global layout
global KeyboardLayout
del KeyboardLayout
if layout_id == "US":
from adafruit_hid.keyboard_layout_us import KeyboardLayout
elif layout_id in ("SI", "HR", "BA"):
from keyboard_layouts.keyboard_layout_win_cr import KeyboardLayout
elif layout_id == "GB":
from keyboard_layouts.keyboard_layout_win_uk import KeyboardLayout
elif layout_id == "FR":
from keyboard_layouts.keyboard_layout_win_fr import KeyboardLayout
elif layout_id == "CZ":
from keyboard_layouts.keyboard_layout_win_cz import KeyboardLayout
elif layout_id == "BR":
from keyboard_layouts.keyboard_layout_win_br import KeyboardLayout
elif layout_id == "DE":
from keyboard_layouts.keyboard_layout_win_de import KeyboardLayout
elif layout_id == "ES":
from keyboard_layouts.keyboard_layout_win_es import KeyboardLayout
elif layout_id == "HU":
from keyboard_layouts.keyboard_layout_win_hu import KeyboardLayout
elif layout_id == "IT":
from keyboard_layouts.keyboard_layout_win_it import KeyboardLayout
elif layout_id == "PO":
from keyboard_layouts.keyboard_layout_win_po import KeyboardLayout
elif layout_id == "SE":
from keyboard_layouts.keyboard_layout_win_sw import KeyboardLayout
elif layout_id == "TR":
from keyboard_layouts.keyboard_layout_win_tr import KeyboardLayout
elif layout_id == "BE":
from keyboard_layouts.keyboard_layout_win_bene import KeyboardLayout
else:
raise PicoLayoutException("Unknown keyboard layout")
layout = KeyboardLayout(kb)


def execute_command(function: str, command: str):
if function[0] == '#':
return
if function in ("DELAY", "SLEEP"):
time.sleep(float(command))
elif function == "LAYOUT":
change_layout(command)
elif function == "PRESS":
command = command.split(" + ")
for c in range(0, len(command), 1):
command[c] = command[c].upper()
if len(command) <= 6:
keys = [0] * len(command)
for idx in range(0, len(command), 1):
keys[idx] = getattr(Keycode, command[idx])
kb.send(*keys)
elif function == "WRITE":
layout.write(command)
command: list[str] = [x.strip().upper() for x in command.split("+")]
if len(command) > 6:
raise PicoKeyboardException("Too many keys pressed at once!")
kb.send(*(Keycode.__dict__[k] for k in command))
elif function in ("WRITE", "WRITELN"):
layout.write(command.replace("\\n", "\n"))
if function == "WRITELN":
kb.send(layout._char_to_keycode('\n'))
elif function == "WRITEFILE":
layout.write(open(command, "r").read().replace("\r", ""))
elif function == "HOLD":
command = command.split(" + ")
for c in range(0, len(command), 1):
command[c] = command[c].upper()
if len(command) <= 6:
keys = [0] * len(command)
for idx in range(0, len(command), 1):
keys[idx] = getattr(Keycode, command[idx])
kb.press(*keys)
command = [x.strip().upper() for x in command.split("+")]
if len(command) > 6:
raise PicoKeyboardException("Too many keys held at once!")
kb.press(*(Keycode.__dict__[k] for k in command))
elif function == "RELEASE":
kb.release_all()
elif function == "MOVE":
command = command.split(", ")
pos = [0] * 2
for i in range(0, len(command), 1):
pos[i] = int(command[i])
ms.move(x=pos[0], y=-1*pos[1], wheel=0)
x, y = [int(a) for a in command.split(',')]
ms.move(x=x, y=-1*y, wheel=0)
elif function == "SCROLL":
ms.move(x=0, y=0, wheel=int(command))
elif function == "CLICK":
command = command.lower() # We love consistency!!
if command == "left":
ms.click(Mouse.LEFT_BUTTON)
elif command == "middle":
ms.click(Mouse.MIDDLE_BUTTON)
elif command == "right":
ms.click(Mouse.RIGHT_BUTTON)
elif function == "VOLUME":
if command.isdigit():
for vc in range(0, abs(int(command)), 1):
if int(command) > 0:
cc.send(ConsumerControlCode.VOLUME_INCREMENT)
elif int(command) < 0:
cc.send(ConsumerControlCode.VOLUME_DECREMENT)
elif command == "mute":
if command.lower() == "mute":
cc.send(ConsumerControlCode.MUTE)

else:
amount = int(command)
to_send = ConsumerControlCode.VOLUME_INCREMENT
if amount < 0:
amount = -amount
to_send = ConsumerControlCode.VOLUME_DECREMENT
for _ in range(amount):
cc.send(to_send)
else:
raise PicoCommandException("Unknown command")

def get_substr(string, start, end):
command = ""
for idx in range(start+1, end):
command += string[idx]
return command

try:
file = io.open("/layout.txt", "r")
line = file.readline()
command = get_substr(line, line.find("("), line.rfind(")"))
if command == "US":
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
layout = KeyboardLayoutUS(kb)
elif command == "CRO":
from keyboard_layouts.keyboard_layout_win_cr import KeyboardLayout
layout = KeyboardLayout(kb)
elif command == "UK":
from keyboard_layouts.keyboard_layout_win_uk import KeyboardLayout
layout = KeyboardLayout(kb)
elif command == "FR":
from keyboard_layouts.keyboard_layout_win_fr import KeyboardLayout
layout = KeyboardLayout(kb)
elif command == "CZ":
from keyboard_layouts.keyboard_layout_win_cz import KeyboardLayout
layout = KeyboardLayout(kb)
elif command == "BR":
from keyboard_layouts.keyboard_layout_win_br import KeyboardLayout
layout = KeyboardLayout(kb)
elif command == "DE":
from keyboard_layouts.keyboard_layout_win_de import KeyboardLayout
layout = KeyboardLayout(kb)
elif command == "ES":
from keyboard_layouts.keyboard_layout_win_es import KeyboardLayout
layout = KeyboardLayout(kb)
elif command == "HU":
from keyboard_layouts.keyboard_layout_win_hu import KeyboardLayout
layout = KeyboardLayout(kb)
elif command == "IT":
from keyboard_layouts.keyboard_layout_win_it import KeyboardLayout
layout = KeyboardLayout(kb)
elif command == "PO":
from keyboard_layouts.keyboard_layout_win_po import KeyboardLayout
layout = KeyboardLayout(kb)
elif command == "SW":
from keyboard_layouts.keyboard_layout_win_sw import KeyboardLayout
layout = KeyboardLayout(kb)
elif command == "TR":
from keyboard_layouts.keyboard_layout_win_tr import KeyboardLayout
layout = KeyboardLayout(kb)
elif command == "BE":
from keyboard_layouts.keyboard_layout_win_bene import KeyboardLayout
layout = KeyboardLayout(kb)
file = io.open("/pico_usb.txt", "r")
line = file.readline()
while line != "":
function = line.split("(",1)[0].upper()
command = get_substr(line, line.find("("), line.rfind(")"))
if looping == False:
loop_pos += len(line)
if function == "LOOP":
looping = True
loop_pos = 0
loop_times = -1
# It is a good idea to seek and constantly read instead
# of storing the whole file in RAM, as the file could
# potentially be bigger than our tiny RAM
file: io.TextIOWrapper = io.open("/pico_usb.txt", "r")
file.seek(0, 2) # Move to the end of the file
file_end = file.tell()
file.seek(0)
while line := file.readline():
if not line.strip():
continue
line = line.rstrip('\r\n').split(" ", 1)
if len(line) == 2:
function, command = line
else:
function = line[0]
command = None
function = function.strip().upper()
if function.upper() == "LOOP":
loop_pos = file.tell()
if command:
loop_times = int(command)
continue
execute_command(function, command)
line = file.readline()
file.close()
file = io.open("/pico_usb.txt", "r")
while looping == True:
file.seek(loop_pos)
line = file.readline()
while line != "":
function = line.split("(",1)[0].upper()
command = get_substr(line, line.find("("), line.rfind(")"))
execute_command(function, command)
line = file.readline()

file.close()

except OSError as e:
print(e)
kb.release_all()
if loop_pos and file.tell() == file_end:
if not loop_times:
break
file.seek(loop_pos)
# Explicitly check if we set loop_times so we
# don't end up decrementing it and creating
# a huge number (could result in a crash)
if loop_times != -1:
loop_times -= 1
finally:
kb.release_all()
if file:
file.close()
32 changes: 19 additions & 13 deletions src/example.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
# dont forget to change your keyboard layout in layout.txt

delay(3) #on slower computers it is better to have bigger delays. I usually use delay(0.8) on my faster PC.
press(windows + d) # minimizes all windows
delay(2)
press(windows + e) # opens windows explorer on windows
delay(2)
press(control + l) # edit path/link.
delay(2)
write(https://youtu.be/dQw4w9WgXcQ)
delay(2)
press(enter) # Will open the default browser.
delay(2)
press(windows + d) # minimizes all windows
volume(100)
# on slower computers it is better to have bigger delays. I usually use delay(0.8) on my faster PC.
delay 3
# minimizes all windows
press windows + d
delay 2
# opens windows explorer on windows
press windows + e
delay 2
# edit path/link.
press control + l
delay 2
write https://youtu.be/dQw4w9WgXcQ
delay 2
# Will open the default browser.
press enter
delay 2
# minimizes all windows
press windows + d
volume 100
3 changes: 3 additions & 0 deletions src/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class PicoLayoutException(Exception): pass
class PicoCommandException(Exception): pass
class PicoKeyboardException(Exception): pass
21 changes: 0 additions & 21 deletions src/layout.txt

This file was deleted.

Loading