This example from the Adafruit CircuitPython PyCamera library is similar to the Basic Camera, but adds autofocus and on-camera user interaction via the MEMENTO's buttons to give you more of a point-and-shoot camera experience.
You can use the buttons to adjust:
- resolutions
- effects
- modes
- optional NeoPixel LED colors
Format your SD card if necessary and insert it in the MEMENTO SD card reader as shown on the Basic Camera page.
CircuitPython
Be sure you've installed CircuitPython on your MEMENTO before proceeding.
Download the Project Bundle
Your project will use a specific set of CircuitPython libraries, and the code.py file. To get everything you need, click on the Download Project Bundle button below, and uncompress the .zip file.
Connect your computer to the board via a known good USB power+data cable. A new flash drive should show up as CIRCUITPY.
Drag the contents of the uncompressed bundle directory for the version of CircuitPython you're running (e.g., /examples/camera/CircuitPython 9.x/) onto your board's CIRCUITPY drive, replacing any existing files or directories with the same names, and adding any new ones that are necessary.
# SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries # SPDX-FileCopyrightText: 2023 Limor Fried for Adafruit Industries # # SPDX-License-Identifier: Unlicense import ssl import os import time import socketpool import adafruit_requests import rtc import adafruit_ntp import wifi import bitmaptools import displayio import gifio import ulab.numpy as np import adafruit_pycamera # Wifi details are in settings.toml file, also, # timezone info should be included to allow local time and DST adjustments # # UTC_OFFSET, if present, will override TZ and DST and no API query will be done # UTC_OFFSET=-25200 # # TZ="America/Phoenix" UTC_OFFSET = os.getenv("UTC_OFFSET") TZ = os.getenv("TZ") print(f"Connecting to {os.getenv('CIRCUITPY_WIFI_SSID')}") SSID = os.getenv("CIRCUITPY_WIFI_SSID") PASSWORD = os.getenv("CIRCUITPY_WIFI_PASSWORD") if SSID and PASSWORD: wifi.radio.connect( os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD") ) if wifi.radio.connected: print(f"Connected to {os.getenv('CIRCUITPY_WIFI_SSID')}!") print("My IP address is", wifi.radio.ipv4_address) pool = socketpool.SocketPool(wifi.radio) if UTC_OFFSET is None: requests = adafruit_requests.Session(pool, ssl.create_default_context()) response = requests.get("http://worldtimeapi.org/api/timezone/" + TZ) response_as_json = response.json() UTC_OFFSET = response_as_json["raw_offset"] + response_as_json["dst_offset"] print(f"UTC_OFFSET: {UTC_OFFSET}") ntp = adafruit_ntp.NTP( pool, server="pool.ntp.org", tz_offset=UTC_OFFSET // 3600 ) print(f"ntp time: {ntp.datetime}") rtc.RTC().datetime = ntp.datetime else: print("Wifi failed to connect. Time not set.") else: print("Wifi config not found in settintgs.toml. Time not set.") pycam = adafruit_pycamera.PyCamera() # pycam.live_preview_mode() settings = ( None, "resolution", "effect", "mode", "led_level", "led_color", "timelapse_rate", ) curr_setting = 0 print("Starting!") # pycam.tone(200, 0.1) last_frame = displayio.Bitmap(pycam.camera.width, pycam.camera.height, 65535) onionskin = displayio.Bitmap(pycam.camera.width, pycam.camera.height, 65535) timelapse_remaining = None timelapse_timestamp = None while True: if pycam.mode_text == "STOP" and pycam.stop_motion_frame != 0: # alpha blend new_frame = pycam.continuous_capture() bitmaptools.alphablend( onionskin, last_frame, new_frame, displayio.Colorspace.RGB565_SWAPPED ) pycam.blit(onionskin) elif pycam.mode_text == "GBOY": bitmaptools.dither( last_frame, pycam.continuous_capture(), displayio.Colorspace.RGB565_SWAPPED ) pycam.blit(last_frame) elif pycam.mode_text == "LAPS": if timelapse_remaining is None: pycam.timelapsestatus_label.text = "STOP" else: timelapse_remaining = timelapse_timestamp - time.time() pycam.timelapsestatus_label.text = f"{timelapse_remaining}s / " # Manually updating the label text a second time ensures that the label # is re-painted over the blitted preview. pycam.timelapse_rate_label.text = pycam.timelapse_rate_label.text pycam.timelapse_submode_label.text = pycam.timelapse_submode_label.text # only in high power mode do we continuously preview if (timelapse_remaining is None) or ( pycam.timelapse_submode_label.text == "HiPwr" ): pycam.blit(pycam.continuous_capture()) if pycam.timelapse_submode_label.text == "LowPwr" and ( timelapse_remaining is not None ): pycam.display.brightness = 0.05 else: pycam.display.brightness = 1 pycam.display.refresh() if timelapse_remaining is not None and timelapse_remaining <= 0: # no matter what, show what was just on the camera pycam.blit(pycam.continuous_capture()) # pycam.tone(200, 0.1) # uncomment to add a beep when a photo is taken try: pycam.display_message("Snap!", color=0x0000FF) pycam.capture_jpeg() except TypeError as e: pycam.display_message("Failed", color=0xFF0000) time.sleep(0.5) except RuntimeError as e: pycam.display_message("Error\nNo SD Card", color=0xFF0000) time.sleep(0.5) pycam.live_preview_mode() pycam.display.refresh() pycam.blit(pycam.continuous_capture()) timelapse_timestamp = ( time.time() + pycam.timelapse_rates[pycam.timelapse_rate] + 1 ) else: pycam.blit(pycam.continuous_capture()) # print("\t\t", capture_time, blit_time) pycam.keys_debounce() # test shutter button if pycam.shutter.long_press: print("FOCUS") print(pycam.autofocus_status) pycam.autofocus() print(pycam.autofocus_status) if pycam.shutter.short_count: print("Shutter released") if pycam.mode_text == "STOP": pycam.capture_into_bitmap(last_frame) pycam.stop_motion_frame += 1 try: pycam.display_message("Snap!", color=0x0000FF) pycam.capture_jpeg() except TypeError as e: pycam.display_message("Failed", color=0xFF0000) time.sleep(0.5) except RuntimeError as e: pycam.display_message("Error\nNo SD Card", color=0xFF0000) time.sleep(0.5) pycam.live_preview_mode() if pycam.mode_text == "GBOY": try: f = pycam.open_next_image("gif") except RuntimeError as e: pycam.display_message("Error\nNo SD Card", color=0xFF0000) time.sleep(0.5) continue with gifio.GifWriter( f, pycam.camera.width, pycam.camera.height, displayio.Colorspace.RGB565_SWAPPED, dither=True, ) as g: g.add_frame(last_frame, 1) if pycam.mode_text == "GIF": try: f = pycam.open_next_image("gif") except RuntimeError as e: pycam.display_message("Error\nNo SD Card", color=0xFF0000) time.sleep(0.5) continue i = 0 ft = [] pycam._mode_label.text = "RECORDING" # pylint: disable=protected-access pycam.display.refresh() with gifio.GifWriter( f, pycam.camera.width, pycam.camera.height, displayio.Colorspace.RGB565_SWAPPED, dither=True, ) as g: t00 = t0 = time.monotonic() while (i < 15) or not pycam.shutter_button.value: i += 1 _gifframe = pycam.continuous_capture() g.add_frame(_gifframe, 0.12) pycam.blit(_gifframe) t1 = time.monotonic() ft.append(1 / (t1 - t0)) print(end=".") t0 = t1 pycam._mode_label.text = "GIF" # pylint: disable=protected-access print(f"\nfinal size {f.tell()} for {i} frames") print(f"average framerate {i / (t1 - t00)}fps") print(f"best {max(ft)} worst {min(ft)} std. deviation {np.std(ft)}") f.close() pycam.display.refresh() if pycam.mode_text == "JPEG": pycam.tone(200, 0.1) try: pycam.display_message("Snap!", color=0x0000FF) pycam.capture_jpeg() pycam.live_preview_mode() except TypeError as e: pycam.display_message("Failed", color=0xFF0000) time.sleep(0.5) pycam.live_preview_mode() except RuntimeError as e: pycam.display_message("Error\nNo SD Card", color=0xFF0000) time.sleep(0.5) if pycam.card_detect.fell: print("SD card removed") pycam.unmount_sd_card() pycam.display.refresh() if pycam.card_detect.rose: print("SD card inserted") pycam.display_message("Mounting\nSD Card", color=0xFFFFFF) for _ in range(3): try: print("Mounting card") pycam.mount_sd_card() print("Success!") break except OSError as e: print("Retrying!", e) time.sleep(0.5) else: pycam.display_message("SD Card\nFailed!", color=0xFF0000) time.sleep(0.5) pycam.display.refresh() if pycam.up.fell: print("UP") key = settings[curr_setting] if key: print("getting", key, getattr(pycam, key)) setattr(pycam, key, getattr(pycam, key) + 1) if pycam.down.fell: print("DN") key = settings[curr_setting] if key: setattr(pycam, key, getattr(pycam, key) - 1) if pycam.right.fell: print("RT") curr_setting = (curr_setting + 1) % len(settings) if pycam.mode_text != "LAPS" and settings[curr_setting] == "timelapse_rate": curr_setting = (curr_setting + 1) % len(settings) print(settings[curr_setting]) # new_res = min(len(pycam.resolutions)-1, pycam.get_resolution()+1) # pycam.set_resolution(pycam.resolutions[new_res]) pycam.select_setting(settings[curr_setting]) if pycam.left.fell: print("LF") curr_setting = (curr_setting - 1 + len(settings)) % len(settings) if pycam.mode_text != "LAPS" and settings[curr_setting] == "timelaps_rate": curr_setting = (curr_setting + 1) % len(settings) print(settings[curr_setting]) pycam.select_setting(settings[curr_setting]) # new_res = max(1, pycam.get_resolution()-1) # pycam.set_resolution(pycam.resolutions[new_res]) if pycam.select.fell: print("SEL") if pycam.mode_text == "LAPS": pycam.timelapse_submode += 1 pycam.display.refresh() if pycam.ok.fell: print("OK") if pycam.mode_text == "LAPS": if timelapse_remaining is None: # stopped print("Starting timelapse") timelapse_remaining = pycam.timelapse_rates[pycam.timelapse_rate] timelapse_timestamp = time.time() + timelapse_remaining + 1 # dont let the camera take over auto-settings saved_settings = pycam.get_camera_autosettings() # print(f"Current exposure {saved_settings=}") pycam.set_camera_exposure(saved_settings["exposure"]) pycam.set_camera_gain(saved_settings["gain"]) pycam.set_camera_wb(saved_settings["wb"]) else: # is running, turn off print("Stopping timelapse") timelapse_remaining = None pycam.camera.exposure_ctrl = True pycam.set_camera_gain(None) # go back to autogain pycam.set_camera_wb(None) # go back to autobalance pycam.set_camera_exposure(None) # go back to auto shutter
Use the Camera
Taking pictures is just as simple as with the Basic Camera -- you could say it's a snap -- simply follow these two steps:
- point the MEMENTO at a subject while framing it in the display
- press-and-release the shutter button fairly quickly
Autofocus
However, now you can use the autofocus feature to help keep closer subjects looking sharp:
- point the MEMENTO at a subject while framing it in the display
- press-and-hold the shutter button until you hear the beep (you should also see the focus change if there is a close subject in frame)
- release the shutter button to finish enabling autofocus
- press-and-release the shutter button to snap your pic!
I ate the banana. So now we have a can of Liquid Wrench to take its place. Which is great, because it is still yellow, but has a lot more detail on which to focus!
In the first photo, the MEMENTO's default distant focus is shown. Note how the Droid Builders patch is in focus but the mid and foreground objects aren't.
For this second shot I pointed the camera at the can of Liquid Wrench, held the shutter button to set the MEMENTO's autofocus, released it, then aimed it back at the patch and snapped the pic. Such nice close-up focus!
Settings
While the Basic Camera example settings could only be changed directly in code, in the Fancy Camera example you can use the MEMENTO's four directional buttons to change settings on the fly.
- press Right button to pick a menu category Resolution, Effect, or Mode
- the category will be highlighted
- press Up or Down buttons to select the choices within the mode:
- Resolution will page through the different available resolutions
- Effect picks the different effects, such as Normal, Invert, B&W, Reddish, Sepia, Solarize, etc.
- Mode will flip between JPEG, GIF, GBOY, STOP, LAPS (we'll cover stop motion mode and timelapse modes elsewhere in this guide)
Text editor powered by tinymce.