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