Text Editor
Adafruit recommends using the Mu editor for editing your CircuitPython code. You can get more info in this guide.
Alternatively, you can use any text editor that saves simple text files.
Download the Project Bundle
Your project will use a specific set of CircuitPython libraries and the code.py file. To get everything you need, click on the Download Project Bundle link below, and uncompress the .zip file.
Hook your QT Py/board to your computer via a known good USB data+power cable. It should show up as a thumb drive named CIRCUITPY.
Using File Explorer/Finder (depending on your Operating System), drag the contents of the uncompressed bundle directory onto your board's CIRCUITPY drive, replacing any existing files or directories with the same names, and adding any new ones that are necessary.
Continue below the program listing for a breakdown of how the program functions, section by section.
# SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries # SPDX-License-Identifier: MIT import board import digitalio import rotaryio from adafruit_hid.mouse import Mouse from usb_hid import devices SCALE = 4 class RelativeEncoder: def __init__(self, pin_a, pin_b, divisor=1): self._encoder = rotaryio.IncrementalEncoder(pin_a, pin_b, divisor) self._old = self._encoder.position @property def delta(self): old = self._old new = self._old = self._encoder.position return new - old xpos = RelativeEncoder(board.A0, board.A1) ypos = RelativeEncoder(board.A2, board.A3) lmb = digitalio.DigitalInOut(board.SCL) lmb.pull = digitalio.Pull.UP rmb = digitalio.DigitalInOut(board.SDA) rmb.pull = digitalio.Pull.UP mouse = Mouse(devices) while True: dx = xpos.delta * SCALE dy = ypos.delta * SCALE l = not lmb.value r = not rmb.value mouse.report[0] = ( mouse.MIDDLE_BUTTON if (l and r) else mouse.LEFT_BUTTON if l else mouse.RIGHT_BUTTON if r else 0) if dx or dy: mouse.move(dx, dy) else: mouse._send_no_move() # pylint: disable=protected-access
How it works
Preliminaries
After the required import lines, there's a line where you can customize the amount of movement per tick. You can also check your operating system settings for mouse speed & acceleration settings.
# SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries # SPDX-License-Identifier: MIT import board import digitalio import rotaryio from adafruit_hid.mouse import Mouse from usb_hid import devices SCALE = 4
Next, the code defines a class that keeps track of how far the mouse has been moved since the last report to the host computer:
class RelativeEncoder: def __init__(self, pin_a, pin_b, divisor=1): self._encoder = rotaryio.IncrementalEncoder(pin_a, pin_b, divisor) self._old = self._encoder.position @property def delta(self): old = self._old new = self._old = self._encoder.position return new - old
The code then creates objects to track the X and Y movements and the state of the two buttons:
xpos = RelativeEncoder(board.A0, board.A1) ypos = RelativeEncoder(board.A2, board.A3) lmb = digitalio.DigitalInOut(board.SCL) lmb.pull = digitalio.Pull.UP rmb = digitalio.DigitalInOut(board.SDA) rmb.pull = digitalio.Pull.UP
Finally, it's time to actually emulate a mouse. After creating the Mouse object, the forever-loop repeatedly sends events to the computer, either a "move" if any X or Y motion is detected, or a "no move" to just update the state of the buttons.
Because the buttons are "pulled up" and switch to a LOW logic value when pressed, the button values are inverted with not.
When both buttons are pressed together, a middle button event is sent instead.
In the case that there was no mouse motion, the code sends a "no move" message so that any button changes are sent to the host computer. This isn't the normal way of sending button events (but it works better with the structure of this code), hence the need to use a pylint comment to indicate this was intended and correct.
mouse = Mouse(devices) while True: dx = xpos.delta * SCALE dy = ypos.delta * SCALE l = not lmb.value r = not rmb.value mouse.report[0] = ( mouse.MIDDLE_BUTTON if (l and r) else mouse.LEFT_BUTTON if l else mouse.RIGHT_BUTTON if r else 0) if dx or dy: mouse.move(dx, dy) else: mouse._send_no_move() # pylint: disable=protected-access
And that's it!
Text editor powered by tinymce.