Simple test
Ensure your device works with this simple test.
examples/camera/code.py
1# SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries
2# SPDX-FileCopyrightText: 2023 Limor Fried for Adafruit Industries
3#
4# SPDX-License-Identifier: Unlicense
5import os
6import ssl
7import time
8
9import adafruit_ntp
10import adafruit_requests
11import bitmaptools
12import displayio
13import gifio
14import rtc
15import socketpool
16import ulab.numpy as np
17import wifi
18
19import adafruit_pycamera
20
21# Wifi details are in settings.toml file, also,
22# timezone info should be included to allow local time and DST adjustments
23# # UTC_OFFSET, if present, will override TZ and DST and no API query will be done
24# UTC_OFFSET=-25200
25# # TZ="America/Phoenix"
26
27UTC_OFFSET = os.getenv("UTC_OFFSET")
28TZ = os.getenv("TZ")
29
30print(f"Connecting to {os.getenv('CIRCUITPY_WIFI_SSID')}")
31SSID = os.getenv("CIRCUITPY_WIFI_SSID")
32PASSWORD = os.getenv("CIRCUITPY_WIFI_PASSWORD")
33
34if SSID and PASSWORD:
35 try:
36 wifi.radio.connect(SSID, PASSWORD)
37 if wifi.radio.connected:
38 print(f"Connected to {SSID}!")
39 print("My IP address is", wifi.radio.ipv4_address)
40 pool = socketpool.SocketPool(wifi.radio)
41
42 if UTC_OFFSET is None:
43 requests = adafruit_requests.Session(pool, ssl.create_default_context())
44 response = requests.get("http://worldtimeapi.org/api/timezone/" + TZ)
45 response_as_json = response.json()
46 UTC_OFFSET = response_as_json["raw_offset"] + response_as_json["dst_offset"]
47 print(f"UTC_OFFSET: {UTC_OFFSET}")
48
49 ntp = adafruit_ntp.NTP(pool, server="pool.ntp.org", tz_offset=UTC_OFFSET // 3600)
50 print(f"ntp time: {ntp.datetime}")
51 rtc.RTC().datetime = ntp.datetime
52 else:
53 print("Wifi failed to connect. Time not set.")
54 except Exception as e:
55 print(f"Wifi error: {e}. Continuing without network.")
56else:
57 print("Wifi config not found in settings.toml. Time not set.")
58
59pycam = adafruit_pycamera.PyCamera()
60# pycam.live_preview_mode()
61
62settings = (
63 None,
64 "resolution",
65 "effect",
66 "mode",
67 "led_level",
68 "led_color",
69 "timelapse_rate",
70)
71curr_setting = 0
72
73print("Starting!")
74# pycam.tone(200, 0.1)
75last_frame = displayio.Bitmap(pycam.camera.width, pycam.camera.height, 65535)
76onionskin = displayio.Bitmap(pycam.camera.width, pycam.camera.height, 65535)
77timelapse_remaining = None
78timelapse_timestamp = None
79
80while True:
81 if pycam.mode_text == "STOP" and pycam.stop_motion_frame != 0:
82 # alpha blend
83 new_frame = pycam.continuous_capture()
84 bitmaptools.alphablend(
85 onionskin, last_frame, new_frame, displayio.Colorspace.RGB565_SWAPPED
86 )
87 pycam.blit(onionskin)
88 elif pycam.mode_text == "GBOY":
89 bitmaptools.dither(
90 last_frame, pycam.continuous_capture(), displayio.Colorspace.RGB565_SWAPPED
91 )
92 pycam.blit(last_frame)
93 elif pycam.mode_text == "LAPS":
94 if timelapse_remaining is None:
95 pycam.timelapsestatus_label.text = "STOP"
96 else:
97 timelapse_remaining = timelapse_timestamp - time.time()
98 pycam.timelapsestatus_label.text = f"{timelapse_remaining}s / "
99 # Manually updating the label text a second time ensures that the label
100 # is re-painted over the blitted preview.
101 pycam.timelapse_rate_label.text = pycam.timelapse_rate_label.text
102 pycam.timelapse_submode_label.text = pycam.timelapse_submode_label.text
103
104 # only in high power mode do we continuously preview
105 if (timelapse_remaining is None) or (pycam.timelapse_submode_label.text == "HiPwr"):
106 pycam.blit(pycam.continuous_capture())
107 if pycam.timelapse_submode_label.text == "LowPwr" and (timelapse_remaining is not None):
108 pycam.display.brightness = 0.05
109 else:
110 pycam.display.brightness = 1
111 pycam.display.refresh()
112
113 if timelapse_remaining is not None and timelapse_remaining <= 0:
114 # no matter what, show what was just on the camera
115 pycam.blit(pycam.continuous_capture())
116 # pycam.tone(200, 0.1) # uncomment to add a beep when a photo is taken
117 try:
118 pycam.display_message("Snap!", color=0x0000FF)
119 pycam.capture_jpeg()
120 except TypeError:
121 pycam.display_message("Failed", color=0xFF0000)
122 time.sleep(0.5)
123 except RuntimeError:
124 pycam.display_message("Error\nNo SD Card", color=0xFF0000)
125 time.sleep(0.5)
126 pycam.live_preview_mode()
127 pycam.display.refresh()
128 pycam.blit(pycam.continuous_capture())
129 timelapse_timestamp = time.time() + pycam.timelapse_rates[pycam.timelapse_rate] + 1
130 else:
131 pycam.blit(pycam.continuous_capture())
132 # print("\t\t", capture_time, blit_time)
133
134 pycam.keys_debounce()
135 # test shutter button
136 if pycam.shutter.long_press:
137 print("FOCUS")
138 print(pycam.autofocus_status)
139 pycam.autofocus()
140 print(pycam.autofocus_status)
141 if pycam.shutter.short_count:
142 print("Shutter released")
143 if pycam.mode_text == "STOP":
144 pycam.capture_into_bitmap(last_frame)
145 pycam.stop_motion_frame += 1
146 try:
147 pycam.display_message("Snap!", color=0x0000FF)
148 pycam.capture_jpeg()
149 except TypeError:
150 pycam.display_message("Failed", color=0xFF0000)
151 time.sleep(0.5)
152 except RuntimeError:
153 pycam.display_message("Error\nNo SD Card", color=0xFF0000)
154 time.sleep(0.5)
155 pycam.live_preview_mode()
156
157 if pycam.mode_text == "GBOY":
158 try:
159 f = pycam.open_next_image("gif")
160 except RuntimeError:
161 pycam.display_message("Error\nNo SD Card", color=0xFF0000)
162 time.sleep(0.5)
163 continue
164
165 with gifio.GifWriter(
166 f,
167 pycam.camera.width,
168 pycam.camera.height,
169 displayio.Colorspace.RGB565_SWAPPED,
170 dither=True,
171 ) as g:
172 g.add_frame(last_frame, 1)
173
174 if pycam.mode_text == "GIF":
175 try:
176 f = pycam.open_next_image("gif")
177 except RuntimeError:
178 pycam.display_message("Error\nNo SD Card", color=0xFF0000)
179 time.sleep(0.5)
180 continue
181 i = 0
182 ft = []
183 pycam._mode_label.text = "RECORDING"
184
185 pycam.display.refresh()
186 with gifio.GifWriter(
187 f,
188 pycam.camera.width,
189 pycam.camera.height,
190 displayio.Colorspace.RGB565_SWAPPED,
191 dither=True,
192 ) as g:
193 t00 = t0 = time.monotonic()
194 while (i < 15) or not pycam.shutter_button.value:
195 i += 1
196 _gifframe = pycam.continuous_capture()
197 g.add_frame(_gifframe, 0.12)
198 pycam.blit(_gifframe)
199 t1 = time.monotonic()
200 ft.append(1 / (t1 - t0))
201 print(end=".")
202 t0 = t1
203 pycam._mode_label.text = "GIF"
204 print(f"\nfinal size {f.tell()} for {i} frames")
205 print(f"average framerate {i / (t1 - t00)}fps")
206 print(f"best {max(ft)} worst {min(ft)} std. deviation {np.std(ft)}")
207 f.close()
208 pycam.display.refresh()
209
210 if pycam.mode_text == "JPEG":
211 pycam.tone(200, 0.1)
212 try:
213 pycam.display_message("Snap!", color=0x0000FF)
214 pycam.capture_jpeg()
215 pycam.live_preview_mode()
216 except TypeError:
217 pycam.display_message("Failed", color=0xFF0000)
218 time.sleep(0.5)
219 pycam.live_preview_mode()
220 except RuntimeError:
221 pycam.display_message("Error\nNo SD Card", color=0xFF0000)
222 time.sleep(0.5)
223
224 if pycam.card_detect.fell:
225 print("SD card removed")
226 pycam.unmount_sd_card()
227 pycam.display.refresh()
228 if pycam.card_detect.rose:
229 print("SD card inserted")
230 pycam.display_message("Mounting\nSD Card", color=0xFFFFFF)
231 for _ in range(3):
232 try:
233 print("Mounting card")
234 pycam.mount_sd_card()
235 print("Success!")
236 break
237 except OSError as e:
238 print("Retrying!", e)
239 time.sleep(0.5)
240 else:
241 pycam.display_message("SD Card\nFailed!", color=0xFF0000)
242 time.sleep(0.5)
243 pycam.display.refresh()
244
245 if pycam.up.fell:
246 print("UP")
247 key = settings[curr_setting]
248 if key:
249 print("getting", key, getattr(pycam, key))
250 setattr(pycam, key, getattr(pycam, key) + 1)
251 if pycam.down.fell:
252 print("DN")
253 key = settings[curr_setting]
254 if key:
255 setattr(pycam, key, getattr(pycam, key) - 1)
256 if pycam.right.fell:
257 print("RT")
258 curr_setting = (curr_setting + 1) % len(settings)
259 if pycam.mode_text != "LAPS" and settings[curr_setting] == "timelapse_rate":
260 curr_setting = (curr_setting + 1) % len(settings)
261 print(settings[curr_setting])
262 # new_res = min(len(pycam.resolutions)-1, pycam.get_resolution()+1)
263 # pycam.set_resolution(pycam.resolutions[new_res])
264 pycam.select_setting(settings[curr_setting])
265 if pycam.left.fell:
266 print("LF")
267 curr_setting = (curr_setting - 1 + len(settings)) % len(settings)
268 if pycam.mode_text != "LAPS" and settings[curr_setting] == "timelapse_rate":
269 curr_setting = (curr_setting + 1) % len(settings)
270 print(settings[curr_setting])
271 pycam.select_setting(settings[curr_setting])
272 # new_res = max(1, pycam.get_resolution()-1)
273 # pycam.set_resolution(pycam.resolutions[new_res])
274 if pycam.select.fell:
275 print("SEL")
276 if pycam.mode_text == "LAPS":
277 pycam.timelapse_submode += 1
278 pycam.display.refresh()
279 if pycam.ok.fell:
280 print("OK")
281 if pycam.mode_text == "LAPS":
282 if timelapse_remaining is None: # stopped
283 print("Starting timelapse")
284 timelapse_remaining = pycam.timelapse_rates[pycam.timelapse_rate]
285 timelapse_timestamp = time.time() + timelapse_remaining + 1
286 # dont let the camera take over auto-settings
287 saved_settings = pycam.get_camera_autosettings()
288 # print(f"Current exposure {saved_settings=}")
289 pycam.set_camera_exposure(saved_settings["exposure"])
290 pycam.set_camera_gain(saved_settings["gain"])
291 pycam.set_camera_wb(saved_settings["wb"])
292 else: # is running, turn off
293 print("Stopping timelapse")
294
295 timelapse_remaining = None
296 pycam.camera.exposure_ctrl = True
297 pycam.set_camera_gain(None) # go back to autogain
298 pycam.set_camera_wb(None) # go back to autobalance
299 pycam.set_camera_exposure(None) # go back to auto shutter