├── imgs
├── qtpy-farty.jpg
├── qtpy-fire.gif
├── qtpy-oled.jpg
├── qtpy-capsense.gif
├── qtpy-encoder.gif
├── qtpy-neodisco.gif
├── qtpy-oledeyes.gif
├── qtpy-servoeye.gif
├── qtpy-flashsize-2MB.png
└── qtpy-capsense-ghost.gif
└── README.md
/imgs/qtpy-farty.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/todbot/qtpy-tricks/HEAD/imgs/qtpy-farty.jpg
--------------------------------------------------------------------------------
/imgs/qtpy-fire.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/todbot/qtpy-tricks/HEAD/imgs/qtpy-fire.gif
--------------------------------------------------------------------------------
/imgs/qtpy-oled.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/todbot/qtpy-tricks/HEAD/imgs/qtpy-oled.jpg
--------------------------------------------------------------------------------
/imgs/qtpy-capsense.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/todbot/qtpy-tricks/HEAD/imgs/qtpy-capsense.gif
--------------------------------------------------------------------------------
/imgs/qtpy-encoder.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/todbot/qtpy-tricks/HEAD/imgs/qtpy-encoder.gif
--------------------------------------------------------------------------------
/imgs/qtpy-neodisco.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/todbot/qtpy-tricks/HEAD/imgs/qtpy-neodisco.gif
--------------------------------------------------------------------------------
/imgs/qtpy-oledeyes.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/todbot/qtpy-tricks/HEAD/imgs/qtpy-oledeyes.gif
--------------------------------------------------------------------------------
/imgs/qtpy-servoeye.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/todbot/qtpy-tricks/HEAD/imgs/qtpy-servoeye.gif
--------------------------------------------------------------------------------
/imgs/qtpy-flashsize-2MB.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/todbot/qtpy-tricks/HEAD/imgs/qtpy-flashsize-2MB.png
--------------------------------------------------------------------------------
/imgs/qtpy-capsense-ghost.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/todbot/qtpy-tricks/HEAD/imgs/qtpy-capsense-ghost.gif
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # QT Py Tricks
2 |
3 | Some things to do on a stock [Adafruit QT Py](https://adafruit.com/qtpy)
4 | running [CircuitPython](https://circuitpython.org) 6.
5 | This list is mostly to help me remmeber how to do things in CircuitPython.
6 | Fore more general [CircuitPython tricks](https://github.com/todbot/circuitpython-tricks), check out my [CircuitPython Tricks page](https://github.com/todbot/circuitpython-tricks).
7 |
8 | These code snippets will also work on a Trinket M0 and really just about
9 | CircuitPython-compatible board, but you may need to adjust some of the `board` pins.
10 |
11 | Notes:
12 | - You will need to copy needed libraries from the [CircuitPython libraries bundle](https://circuitpython.org/libraries) to your CIRCUITPY drive
13 | - When copying files to QT Py or Trinket M0, and you're on a Mac,
14 | be sure to [use the `xattr` trick described here](https://todbot.com/blog/2020/10/03/prevent-annoying-mac-_-files-being-created-on-circuitpy/) to save flash space
15 | - Or, just use [`circup`](https://learn.adafruit.com/keep-your-circuitpython-libraries-on-devices-up-to-date-with-circup/install-circup) to install libraries. It's very nice! (on Mac, do `pip3 install circup`)
16 |
17 | ## Table of Contents
18 |
19 | * [Print "time" on OLED display](#print-time-on-oled-display)
20 | * [Disco Party on built-in Neopixel](#disco-party-on-built-in-neopixel)
21 | * [Output Farty Noises to DAC](#output-farty-noises-to-dac)
22 | * [Capsense Touch to Colors on Built-in LED](#capsense-touch-to-colors-on-built-in-led)
23 | * [Rotary Encoder to Built-in LED](#rotary-encoder-to-built-in-led)
24 | * [Fire Simulation on External Neopixel Strip](#fire-simulation-on-external-neopixel-strip)
25 | * [Two servos with Python Class for Easing / Sequencing](#two-servos-with-python-class-for-easing--sequencing)
26 | * [Spooky Eyes with Dual SSD1306 OLED displays](#spooky-eyes-with-dual-ssd1306-oled-displays)
27 | * [Use Capsense as Proximity Detector to Make Spooopy Ghost](#use-capsense-as-proximity-detector-to-make-spooopy-ghost)
28 | * [Get Size of Device's Flash Disk](#get-size-of-devices-flash-disk)
29 | * [Capsense Touch Sensor to USB keyboard](#capsense-touch-sensor-to-usb-keyboard)
30 |
31 |
32 | ## Print "time" on OLED display
33 |
34 | [Note: as of CircuitPython 7, this only works on QTPy RP2040, not QTPy M0 or QTPY M0 Haxpress]
35 |
36 | ```py
37 | import time
38 | import board
39 | import adafruit_ssd1306 # requires: adafruit_bus_device and adafruit_framebuf
40 | i2c = board.I2C()
41 | oled = adafruit_ssd1306.SSD1306_I2C(width=128, height=32, i2c=i2c)
42 | while True:
43 | oled.fill(0)
44 | oled.text( "hello world", 0,0,1) # requires 'font5x8.bin'
45 | oled.text("time:"+str(time.monotonic()), 0,8,1)
46 | oled.show()
47 | time.sleep(1.0)
48 | ```
49 |
50 |
51 | ## Disco Party on built-in Neopixel
52 | ```py
53 | import time
54 | import board
55 | import neopixel
56 | from random import randint
57 | pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
58 | while True:
59 | pixel.fill( (randint(0,255), randint(0,255), randint(0,255) ))
60 | time.sleep(0.1)
61 | ```
62 |
63 |
64 | ## Output Farty Noises to DAC
65 |
66 | ```py
67 | import time
68 | import board
69 | import analogio
70 | import random
71 | dac = analogio.AnalogOut(board.A0)
72 | i = 0
73 | di = 40000
74 | lasttime = 0
75 | while True:
76 | dac.value = i
77 | i = (i+di) & 0xffff
78 | if time.monotonic() - lasttime > 1:
79 | lasttime = time.monotonic()
80 | di = random.randrange(40000,80000)
81 | ```
82 |
83 |
84 | ([hear it in action](https://twitter.com/todbot/status/1313223090181042177))
85 |
86 |
87 |
88 | ## Capsense Touch to Colors on Built-in LED
89 |
90 | ```py
91 | import board
92 | from touchio import TouchIn
93 | import neopixel
94 | pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
95 | touchA = TouchIn(board.A1)
96 | touchB = TouchIn(board.A2)
97 | print("hello")
98 | while True:
99 | pixel[0] = (int(touchA.value*255), 0, int(touchB.value*255))
100 | ```
101 |
102 |
103 | ## Rotary Encoder to Built-in LED
104 |
105 | ```py
106 | import board
107 | import time
108 | import neopixel
109 | import rotaryio
110 | pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=1.0)
111 | encoder = rotaryio.IncrementalEncoder( board.MOSI, board.MISO ) # any two pins
112 | while True:
113 | b = (encoder.position % 32) * 8
114 | print(encoder.position,b)
115 | pixel.fill((0,0,b))
116 | time.sleep(0.1)
117 | ```
118 |
119 |
120 |
121 | ## Fire Simulation on External Neopixel Strip
122 |
123 |
124 |
125 | Uses Python array operations and list comprehensions for conciseness.
126 |
127 | ```py
128 | import board
129 | import time
130 | import neopixel
131 | from random import randint
132 | # External Neopixel strip, can be on any pin
133 | leds = neopixel.NeoPixel(board.RX,8,brightness=0.2,auto_write=False)
134 | while True:
135 | # reduce brightness of all pixels by (30,30,30)
136 | leds[:] = [[max(i-30,0) for i in l] for l in leds]
137 | # shift LED values up by one (0->1, 1->2, etc)
138 | leds[1:] = leds[0:-1] # '-1' means len-1 here
139 | # pick new random fire color for LED 0
140 | leds[0] = (randint(150,255),randint(50,100),0)
141 | leds.show()
142 | time.sleep(0.1)
143 | ```
144 |
145 | If you want it "flipped", so the fire goes from the top LED down to LED 0:
146 |
147 | ```py
148 | import time, board, neopixel
149 | from random import randint
150 | leds = neopixel.NeoPixel(board.RX,8,brightness=0.2,auto_write=False)
151 | while True:
152 | leds[:] = [[max(i-30,0) for i in l] for l in leds] # reduce brightness of all pixels by (30,30,30)
153 | leds[0:-1] = leds[1:] # shift LED values down by one
154 | leds[-1] = (randint(150,255),randint(50,100),0) # pick new random fire color for LED N
155 | leds.show()
156 | time.sleep(0.1)
157 | ```
158 |
159 |
160 | ## Two servos with Python Class for Easing / Sequencing
161 |
162 | ```py
163 | import board
164 | import time
165 | from pulseio import PWMOut
166 | from adafruit_motor import servo
167 | from random import random
168 |
169 | servoA = servo.Servo(PWMOut(board.RX, duty_cycle=2**15, frequency=50))
170 | servoB = servo.Servo(PWMOut(board.TX, duty_cycle=2**15, frequency=50))
171 |
172 | class AngleSequence:
173 | def __init__(self,angles,speed):
174 | self.angles = angles
175 | self.aindex = 0
176 | self.angle = angles[0]
177 | self.speed = speed
178 | def next_angle(self):
179 | self.aindex = (self.aindex + 1) % len(self.angles)
180 | def update(self):
181 | new_angle = self.angles[self.aindex]
182 | self.angle += (new_angle - self.angle) * self.speed # simple easing
183 | return self.angle
184 |
185 | seqA = AngleSequence( [90,70,90,90,90,90,60,80,100], 0.1) # set speed to taste
186 | seqB = AngleSequence( [90,90,90,80,70,90,120], 0.1) # set angles for desired animation
187 |
188 | lasttime = time.monotonic()
189 | while True:
190 | if time.monotonic() - lasttime > (0.2 + random()*4.0 ): # creepy random timing
191 | lasttime = time.monotonic()
192 | seqA.next_angle() # go to next angle in list
193 | seqB.next_angle()
194 | servoA.angle = seqA.update() # get updated (eased) servo angle
195 | servoB.angle = seqB.update()
196 | time.sleep(0.02) # wait a bit, note this affects 'speed'
197 | ```
198 |
199 |
200 |
201 | ## Spooky Eyes with Dual SSD1306 OLED displays
202 |
203 | Displays are at same address, so code can be much simpler
204 |
205 | [Note: as of CircuitPython 7, this only works on QTPy RP2040, not QTPy M0 or QTPY M0 Haxpress]
206 |
207 | ```py
208 | import time
209 | import board
210 | import random
211 | import adafruit_ssd1306 # requires: adafruit_bus_device, adafruit_framebuf
212 | i2c = board.I2C()
213 | oled = adafruit_ssd1306.SSD1306_I2C(width=128, height=64, i2c=i2c)
214 | i=0; inc=30
215 | while True:
216 | oled.fill(0)
217 | for d in (31,30,14,12,10,8): # various diameters
218 | oled.circle( 64+i,32, d,1)
219 | i = random.randint(-30,30)
220 | oled.show()
221 | time.sleep( 0.1 + random.random() )
222 | ```
223 |
224 |
225 |
226 | ## Use Capsense as Proximity Detector to Make Spooopy Ghost
227 | Computing the difference between current touch raw value anda baseline minimum
228 | provides a kind of proximity detector, if your antenna is big enough.
229 |
230 | ```py
231 | import time
232 | import board
233 | import digitalio
234 | import touchio
235 | from pulseio import PWMOut
236 | from adafruit_motor import servo
237 |
238 | touchA = touchio.TouchIn(board.A2)
239 | servoA = servo.Servo(PWMOut(board.RX, duty_cycle=2**15, frequency=50))
240 | touch_min = touchA.raw_value # baseline for proximity
241 | servo_pos_last = 160
242 |
243 | while True:
244 | # get proximity value, set within servo bounds (30-160)
245 | proximity_val = (touchA.raw_value - touch_min)
246 | servo_pos = 160 - min(160, max(30, proximity_val))
247 | servo_pos_last += (servo_pos - servo_pos_last) * 0.01 # easing/smoothing
248 | servoA.angle = servo_pos_last
249 | ```
250 |
251 |
252 |
253 |
254 | ## Get Size of Device's Flash Disk
255 | see [`os.statvfs()` docs](https://circuitpython.readthedocs.io/en/latest/shared-bindings/os/index.html#os.statvfs)
256 |
257 | ```py
258 | import os
259 | print("\nHello World!")
260 | fs_stat = os.statvfs('/')
261 | print("Disk size in MB", fs_stat[0] * fs_stat[2] / 1024 / 1024)
262 | print("Free space in MB", fs_stat[0] * fs_stat[3] / 1024 / 1024)
263 | while True: pass
264 | ```
265 |
266 |
267 |
268 | ## Capsense Touch Sensor to USB keyboard
269 | ```py
270 | import time
271 | import board
272 | import neopixel
273 | import touchio
274 | import usb_hid
275 | from adafruit_hid.keyboard import Keyboard
276 | from adafruit_hid.keycode import Keycode
277 | touchA= touchio.TouchIn(board.A0)
278 | pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2, auto_write=False)
279 | pixel[0] = (0,0,0)
280 | time.sleep(1) # Sleep for a bit to avoid a race condition on some systems
281 | kbd = Keyboard(usb_hid.devices)
282 | while True:
283 | if touchA.value:
284 | print("A press")
285 | pixel[0] = (255,0,0)
286 | kbd.send(Keycode.RIGHT_ARROW)
287 | pixel.show()
288 | pixel[0] = (0,0,0)
289 | time.sleep(0.2)
290 | ```
291 |
292 | ***
293 |
294 | # Really Helpful CircuitPython Links
295 |
296 | - https://circuitpython.readthedocs.io/projects/bundle/en/latest/drivers.html
297 | - https://circuitpython.readthedocs.io/en/latest/shared-bindings/support_matrix.html
298 | - https://learn.adafruit.com/circuitpython-essentials/circuitpython-essentials
299 | - https://learn.adafruit.com/welcome-to-circuitpython/overview
300 |
--------------------------------------------------------------------------------