diff --git a/.readthedocs.yaml b/.readthedocs.yaml index b79ec5b..fe4faae 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -8,6 +8,9 @@ # Required version: 2 +sphinx: + configuration: docs/conf.py + build: os: ubuntu-20.04 tools: diff --git a/README.rst b/README.rst index af6a199..48e9ca9 100644 --- a/README.rst +++ b/README.rst @@ -2,7 +2,7 @@ Introduction ============ -.. image:: https://readthedocs.org/projects/adafruit-circuitpython-pycamera/badge/?version=latest +.. image:: https://readthedocs.org/projects/circuitpython-pycamera/badge/?version=latest :target: https://docs.circuitpython.org/projects/pycamera/en/latest/ :alt: Documentation Status diff --git a/adafruit_pycamera/__init__.py b/adafruit_pycamera/__init__.py index 6a4807c..1ef4c94 100644 --- a/adafruit_pycamera/__init__.py +++ b/adafruit_pycamera/__init__.py @@ -817,14 +817,14 @@ def live_preview_mode(self): # self.effect = self._effect self.continuous_capture_start() - def open_next_image(self, extension="jpg"): + def open_next_image(self, extension="jpg", filename_prefix="img"): """Return an opened numbered file on the sdcard, such as "img01234.jpg".""" try: os.stat("/sd") except OSError as exc: # no SD card! raise RuntimeError("No SD card mounted") from exc while True: - filename = "/sd/img%04d.%s" % (self._image_counter, extension) + filename = f"/sd/{filename_prefix}{self._image_counter:04d}.{extension}" self._image_counter += 1 try: os.stat(filename) @@ -834,7 +834,7 @@ def open_next_image(self, extension="jpg"): print("Writing to", filename) return open(filename, "wb") - def capture_jpeg(self): + def capture_jpeg(self, filename_prefix="img"): """Capture a jpeg file and save it to the SD card""" try: os.stat("/sd") @@ -852,7 +852,7 @@ def capture_jpeg(self): print(f"Captured {len(jpeg)} bytes of jpeg data") print("Resolution %d x %d" % (self.camera.width, self.camera.height)) - with self.open_next_image() as dest: + with self.open_next_image(filename_prefix=filename_prefix) as dest: chunksize = 16384 for offset in range(0, len(jpeg), chunksize): dest.write(jpeg[offset : offset + chunksize]) @@ -946,6 +946,13 @@ def blit_overlay_into_last_capture(self): del cc565_swapped gc.collect() + @property + def last_saved_filename(self) -> str: + """ + The filename of the last image saved. + """ + return self._last_saved_image_filename + def continuous_capture_start(self): """Switch the camera to continuous-capture mode""" pass # pylint: disable=unnecessary-pass diff --git a/docs/conf.py b/docs/conf.py index cbf1452..9993665 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -137,7 +137,6 @@ import sphinx_rtd_theme html_theme = "sphinx_rtd_theme" -html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), "."] # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, diff --git a/examples/timestamp_filename/code.py b/examples/timestamp_filename/code.py new file mode 100644 index 0000000..05c2a2e --- /dev/null +++ b/examples/timestamp_filename/code.py @@ -0,0 +1,87 @@ +# SPDX-FileCopyrightText: Copyright (c) 2024 Tim Cocks for Adafruit Industries +# +# SPDX-License-Identifier: MIT +""" simple point-and-shoot camera example. With NTP and internal RTC to + add timestamp to photo filenames. Must install adafruit_ntp library! + Example code assumes WIFI credentials are properly setup and web workflow + enabled in settings.toml. If not, you'll need to add code to manually connect + to your network.""" + +import time +import wifi +import socketpool +import rtc +import adafruit_ntp +import adafruit_pycamera # pylint: disable=import-error + +pool = socketpool.SocketPool(wifi.radio) +ntp = adafruit_ntp.NTP(pool, tz_offset=0) +rtc.RTC().datetime = ntp.datetime + +pycam = adafruit_pycamera.PyCamera() +pycam.mode = 0 # only mode 0 (JPEG) will work in this example + +# User settings - try changing these: +pycam.resolution = 2 # 0-12 preset resolutions: +# 0: 240x240, 1: 320x240, 2: 640x480, 3: 800x600, 4: 1024x768, +# 5: 1280x720, 6: 1280x1024, 7: 1600x1200, 8: 1920x1080, 9: 2048x1536, +# 10: 2560x1440, 11: 2560x1600, 12: 2560x1920 +pycam.led_level = 1 # 0-4 preset brightness levels +pycam.led_color = 0 # 0-7 preset colors: 0: white, 1: green, 2: yellow, 3: red, +# 4: pink, 5: blue, 6: teal, 7: rainbow +pycam.effect = 0 # 0-7 preset FX: 0: normal, 1: invert, 2: b&w, 3: red, +# 4: green, 5: blue, 6: sepia, 7: solarize + +print("Simple camera ready.") +pycam.tone(800, 0.1) +pycam.tone(1200, 0.05) + +while True: + pycam.blit(pycam.continuous_capture()) + pycam.keys_debounce() + + if pycam.shutter.short_count: + print("Shutter released") + pycam.tone(1200, 0.05) + pycam.tone(1600, 0.05) + try: + pycam.display_message("snap", color=0x00DD00) + timestamp = "img_{}-{}-{}_{:02}-{:02}-{:02}_".format( + time.localtime().tm_year, + time.localtime().tm_mon, + time.localtime().tm_mday, + time.localtime().tm_hour, + time.localtime().tm_min, + time.localtime().tm_sec, + ) + pycam.capture_jpeg(filename_prefix=timestamp) + pycam.live_preview_mode() + except TypeError as exception: + pycam.display_message("Failed", color=0xFF0000) + time.sleep(0.5) + pycam.live_preview_mode() + except RuntimeError as exception: + 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 exception: + print("Retrying!", exception) + time.sleep(0.5) + else: + pycam.display_message("SD Card\nFailed!", color=0xFF0000) + time.sleep(0.5) + pycam.display.refresh()