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