Skip to content

Commit 355d9a8

Browse files
committed
Adding picowbell timelapse code
Adding CircuitPython code for the picowbell tripler timelapse camera
1 parent a5d6e23 commit 355d9a8

File tree

2 files changed

+234
-0
lines changed

2 files changed

+234
-0
lines changed
Binary file not shown.

RP2040_PiCowbell_Timelapse/code.py

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
# SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
import os
6+
import time
7+
import gc
8+
import microcontroller
9+
import digitalio
10+
import sdcardio
11+
import storage
12+
import board
13+
import busio
14+
from adafruit_ticks import ticks_ms, ticks_add, ticks_diff
15+
import displayio
16+
from adafruit_neokey.neokey1x4 import NeoKey1x4
17+
import adafruit_st7789
18+
from adafruit_display_text import label
19+
from adafruit_bitmap_font import bitmap_font
20+
import adafruit_ov5640
21+
22+
gc.collect()
23+
# Release any resources currently in use for the displays
24+
displayio.release_displays()
25+
gc.collect()
26+
print("Initializing SD card")
27+
spi = busio.SPI(clock=board.GP18, MOSI=board.GP19, MISO=board.GP16)
28+
sd_cs = board.GP17
29+
sdcard = sdcardio.SDCard(spi, sd_cs)
30+
vfs = storage.VfsFat(sdcard)
31+
storage.mount(vfs, "/sd")
32+
gc.collect()
33+
i2c = busio.I2C(board.GP5, board.GP4)
34+
tft_cs = board.GP21
35+
tft_dc = board.GP20
36+
display_bus = displayio.FourWire(spi, command=tft_dc, chip_select=tft_cs, reset=None)
37+
display = adafruit_st7789.ST7789(display_bus, width=240, height=240, rowstart=80, rotation=0)
38+
39+
gc.collect()
40+
splash = displayio.Group()
41+
display.root_group = splash
42+
43+
gc.collect()
44+
font = bitmap_font.load_font("Helvetica-Bold-16.pcf")
45+
t2 = label.Label(font, text="Start?", color=(255, 255, 255))
46+
t2.anchor_point = (0.5, 0.0)
47+
t2.anchored_position = (display.width / 2, 0)
48+
splash.append(t2)
49+
t1 = label.Label(font, text="Interval: 3s", color=(255, 255, 255))
50+
t1.anchor_point = (0.5, 1.0)
51+
t1.anchored_position = (display.width / 2, display.height)
52+
splash.append(t1)
53+
54+
print("construct camera")
55+
gc.collect()
56+
reset = digitalio.DigitalInOut(board.GP14)
57+
cam = adafruit_ov5640.OV5640(
58+
i2c,
59+
data_pins=(
60+
board.GP6,
61+
board.GP7,
62+
board.GP8,
63+
board.GP9,
64+
board.GP10,
65+
board.GP11,
66+
board.GP12,
67+
board.GP13,
68+
),
69+
clock=board.GP3,
70+
vsync=board.GP0,
71+
href=board.GP2,
72+
mclk=None,
73+
shutdown=None,
74+
reset=reset,
75+
size=adafruit_ov5640.OV5640_SIZE_QCIF,
76+
)
77+
print("print chip id")
78+
print(cam.chip_id)
79+
gc.collect()
80+
cam.quality = 3
81+
gc.collect()
82+
83+
def exists(filename):
84+
try:
85+
os.stat(filename)
86+
return True
87+
except OSError as _:
88+
return False
89+
90+
_image_counter = 0
91+
92+
def open_next_image():
93+
global _image_counter # pylint: disable=global-statement
94+
while True:
95+
filename = f"/sd/img{_image_counter:04d}.jpg"
96+
_image_counter += 1
97+
if exists(filename):
98+
continue
99+
print("# writing to", filename)
100+
return open(filename, "wb")
101+
102+
cam.colorspace = adafruit_ov5640.OV5640_COLOR_RGB
103+
gc.collect()
104+
105+
bitmap = displayio.Bitmap(cam.width, cam.height, 65535)
106+
if bitmap is None:
107+
raise SystemExit("Could not allocate a bitmap")
108+
109+
tg = displayio.TileGrid(bitmap,
110+
pixel_shader=displayio.ColorConverter(input_colorspace=displayio.Colorspace.RGB565_SWAPPED),
111+
x=int((display.width - bitmap.width) / 2), y=int((display.height - bitmap.height) / 2)
112+
)
113+
splash.append(tg)
114+
gc.collect()
115+
116+
# Create a NeoKey object
117+
neokey = NeoKey1x4(i2c, addr=0x30)
118+
# states for key presses
119+
key_0_state = False
120+
key_1_state = False
121+
key_2_state = False
122+
key_3_state = False
123+
124+
tm0 = time.monotonic_ns()
125+
display.auto_refresh = False
126+
mode = 0
127+
intervals = [3, 5, 10, 30, 60]
128+
index = 0
129+
clock = ticks_ms()
130+
count = 0
131+
b = bytearray()
132+
while True:
133+
if mode == 0: # preview
134+
cam.capture(bitmap)
135+
bitmap.dirty()
136+
display.refresh(minimum_frames_per_second=0)
137+
tm1 = time.monotonic_ns()
138+
# print("fps", 1e9 / (t1 - t0))
139+
tm0 = tm1
140+
if mode == 1: # timelapse
141+
if ticks_diff(ticks_ms(), clock) >= intervals[index]*1000:
142+
gc.collect()
143+
time.sleep(0.01)
144+
jpeg = cam.capture(b)
145+
# print(f"Captured {len(jpeg)} bytes of jpeg data")
146+
print(f" (had allocated {cam.capture_buffer_size} bytes")
147+
print(f"Resolution {cam.width}x{cam.height}")
148+
try:
149+
with open_next_image() as f:
150+
f.write(jpeg)
151+
print("# Wrote image")
152+
except OSError as e:
153+
print(e)
154+
count += 1
155+
t2.text = f"Timelapsing! {count} photos taken"
156+
display.refresh(minimum_frames_per_second=0)
157+
clock = ticks_add(clock, intervals[index]*1000)
158+
if not neokey[0] and key_0_state:
159+
key_0_state = False
160+
neokey.pixels[0] = 0x0
161+
if not neokey[1] and key_1_state:
162+
key_1_state = False
163+
neokey.pixels[1] = 0x0
164+
if not neokey[2] and key_2_state:
165+
key_2_state = False
166+
neokey.pixels[2] = 0x0
167+
if not neokey[3] and key_3_state:
168+
key_3_state = False
169+
neokey.pixels[3] = 0x0
170+
# if 1st neokey is pressed...
171+
if neokey[0] and not key_0_state:
172+
mode = 1
173+
print("Button A")
174+
# turn on NeoPixel
175+
neokey.pixels[0] = 0xFF0000
176+
time.sleep(.2)
177+
try:
178+
del bitmap
179+
del tg
180+
except KeyError:
181+
continue
182+
gc.collect()
183+
#cam.size=adafruit_ov5640.OV5640_SIZE_VGA
184+
cam.colorspace = adafruit_ov5640.OV5640_COLOR_JPEG
185+
b = bytearray(cam.capture_buffer_size)
186+
t1.text=f"Taking photos every {intervals[index]}s"
187+
clock = ticks_ms()
188+
key_0_state = True
189+
190+
# if 2nd neokey is pressed...change interval
191+
if neokey[1] and not key_1_state:
192+
print("Button B")
193+
# turn on NeoPixel
194+
neokey.pixels[1] = 0xFFFF00
195+
time.sleep(.2)
196+
index = (index + 1) % len(intervals)
197+
t1.text=f"Interval: {intervals[index]}s"
198+
key_1_state = True
199+
200+
# if 3rd neokey is pressed...autofocus
201+
if neokey[2] and not key_2_state:
202+
print("Button C")
203+
# turn on NeoPixel
204+
neokey.pixels[2] = 0x00FF00
205+
time.sleep(.2)
206+
t2.text = "Autofocusing.."
207+
try:
208+
display.refresh(minimum_frames_per_second=0)
209+
cam.autofocus()
210+
except AttributeError:
211+
print("This camera module does not have autofocus..")
212+
continue
213+
key_2_state = True
214+
215+
# if 4th neokey is pressed...stop timelapse & reboot
216+
if neokey[3] and not key_3_state:
217+
print("Button D")
218+
# turn on NeoPixel
219+
neokey.pixels[3] = 0x00FFFF
220+
time.sleep(.2)
221+
gc.collect()
222+
del b
223+
gc.collect()
224+
t2.text = "Ending timelapse.."
225+
t1.text = f"Took {count} photos"
226+
display.refresh(minimum_frames_per_second=0)
227+
print("resetting..")
228+
time.sleep(5)
229+
del t1
230+
del t2
231+
gc.collect()
232+
neokey.pixels[3] = 0x000000
233+
microcontroller.reset()
234+
key_3_state = True

0 commit comments

Comments
 (0)