Skip to content

Timelapse mode #22

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 88 additions & 5 deletions adafruit_pycamera/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,16 @@
_AW_CARDDET = const(8)
_AW_SDPWR = const(9)
_AW_DOWN = const(15)
_AW_LEFT = const(14)
_AW_RIGHT = const(14)
_AW_UP = const(13)
_AW_RIGHT = const(12)
_AW_LEFT = const(12)
_AW_OK = const(11)

_NVM_RESOLUTION = const(1)
_NVM_EFFECT = const(2)
_NVM_MODE = const(3)
_NVM_TIMELAPSE_RATE = const(4)
_NVM_TIMELAPSE_SUBMODE = const(5)


class PyCameraBase: # pylint: disable=too-many-instance-attributes,too-many-public-methods
Expand Down Expand Up @@ -168,7 +170,31 @@ class PyCameraBase: # pylint: disable=too-many-instance-attributes,too-many-pub
"Sepia",
"Solarize",
)
modes = ("JPEG", "GIF", "GBOY", "STOP")

timelapse_rates = (
5,
10,
20,
30,
60,
90,
60 * 2,
60 * 3,
60 * 4,
60 * 5,
60 * 10,
60 * 15,
60 * 30,
60 * 60
)

timelapse_submodes = (
"HiPwr",
"MedPwr",
"LowPwr"
)

modes = ("JPEG", "GIF", "GBOY", "STOP", "LAPS")

_INIT_SEQUENCE = (
b"\x01\x80\x78" # _SWRESET and Delay 120ms
Expand Down Expand Up @@ -249,13 +275,13 @@ def make_debounced_expander_pin(pin_no):
def make_camera_ui(self):
"""Create displayio widgets for the standard camera UI"""
self._sd_label = label.Label(
terminalio.FONT, text="SD ??", color=0x0, x=150, y=10, scale=2
terminalio.FONT, text="SD ??", color=0x0, x=170, y=10, scale=2
)
self._effect_label = label.Label(
terminalio.FONT, text="EFFECT", color=0xFFFFFF, x=4, y=10, scale=2
)
self._mode_label = label.Label(
terminalio.FONT, text="MODE", color=0xFFFFFF, x=150, y=10, scale=2
terminalio.FONT, text="MODE", color=0xFFFFFF, x=170, y=10, scale=2
)
self._topbar = displayio.Group()
self._res_label = label.Label(
Expand All @@ -268,8 +294,23 @@ def make_camera_ui(self):
self._botbar.append(self._effect_label)
self._botbar.append(self._mode_label)

self._timelapsebar = displayio.Group(x=0, y=180)
self._timelapse_submode_label = label.Label(
terminalio.FONT, text="SubM", color=0xFFFFFF,x=160, y=10, scale=2
)
self._timelapse_rate_label = label.Label(
terminalio.FONT, text="Time", color=0xFFFFFF,x=90, y=10, scale=2
)
self._timelapsestatus_label = label.Label(
terminalio.FONT, text="Status", color=0xFFFFFF, x=0, y=10, scale=2
)
self._timelapsebar.append(self._timelapse_rate_label)
self._timelapsebar.append(self._timelapsestatus_label)
self._timelapsebar.append(self._timelapse_submode_label)

self.splash.append(self._topbar)
self.splash.append(self._botbar)
self.splash.append(self._timelapsebar)

def init_accelerometer(self):
"""Initialize the accelerometer"""
Expand Down Expand Up @@ -338,6 +379,8 @@ def init_camera(self, init_autofocus=True) -> None:
self.camera.saturation = 3
self.resolution = microcontroller.nvm[_NVM_RESOLUTION]
self.mode = microcontroller.nvm[_NVM_MODE]
self.timelapse_rate = microcontroller.nvm[_NVM_TIMELAPSE_RATE]
self.timelapse_submode = microcontroller.nvm[_NVM_TIMELAPSE_SUBMODE]

if init_autofocus:
self.autofocus_init()
Expand Down Expand Up @@ -461,6 +504,9 @@ def select_setting(self, setting_name):
self._res_label.text = self.resolutions[self._resolution]
self._mode_label.color = 0xFFFFFF
self._mode_label.background_color = 0x0
self._timelapse_rate_label.color = 0xFFFFFF
self._timelapse_rate_label.background_color = None

if setting_name == "effect":
self._effect_label.color = 0x0
self._effect_label.background_color = 0xFFFFFF
Expand All @@ -478,6 +524,13 @@ def select_setting(self, setting_name):
self._res_label.text = "LED CLR"
self._res_label.color = 0x0
self._res_label.background_color = 0xFFFFFF
elif setting_name == "led_color":
self._res_label.text = "LED CLR"
self._res_label.color = 0x0
self._res_label.background_color = 0xFFFFFF
elif setting_name == "timelapse_rate":
self._timelapse_rate_label.color = 0x0
self._timelapse_rate_label.background_color = 0xFFFFFF
self.display.refresh()

@property
Expand Down Expand Up @@ -538,6 +591,36 @@ def resolution(self, res):
self._res_label.text = self.resolutions[res]
self.display.refresh()


@property
def timelapse_rate(self):
"""Get or set the amount of time between timelapse shots"""
return self._timelapse_rate

@timelapse_rate.setter
def timelapse_rate(self, setting):
setting = (setting + len(self.timelapse_rates)) % len(self.timelapse_rates)
self._timelapse_rate = setting
if self.timelapse_rates[setting] < 60:
self._timelapse_rate_label.text = "%d S" % self.timelapse_rates[setting]
else:
self._timelapse_rate_label.text = "%d M" % (self.timelapse_rates[setting] / 60)
microcontroller.nvm[_NVM_TIMELAPSE_RATE] = setting
self.display.refresh()


@property
def timelapse_submode(self):
"""Get or set the power mode for timelapsing"""
return self._timelapse_submode

@timelapse_submode.setter
def timelapse_submode(self, setting):
setting = (setting + len(self.timelapse_submodes)) % len(self.timelapse_submodes)
self._timelapse_submode = setting
self._timelapse_submode_label.text = self.timelapse_submodes[self._timelapse_submode]
microcontroller.nvm[_NVM_TIMELAPSE_SUBMODE] = setting

def init_display(self):
"""Initialize the TFT display"""
# construct displayio by hand
Expand Down
65 changes: 59 additions & 6 deletions examples/camera/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# SPDX-License-Identifier: Unlicense

import time

import math
import bitmaptools
import displayio
import gifio
Expand All @@ -14,13 +14,16 @@
pycam = adafruit_pycamera.PyCamera()
# pycam.live_preview_mode()

settings = (None, "resolution", "effect", "mode", "led_level", "led_color")
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
Expand All @@ -34,6 +37,41 @@
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 / "
pycam._timelapse_rate_label.text = pycam._timelapse_rate_label.text
pycam._timelapse_submode_label.text = pycam._timelapse_submode_label.text
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Were these re-assignments of the label text really needed? I'll try testing without it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes because otherwise display refresh doesnt pick up the labels after the screen has been blitted manually

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, will add a comment to that effect


# 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)
Expand Down Expand Up @@ -127,6 +165,7 @@
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()
Expand All @@ -152,27 +191,41 @@
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.left.fell:
print("LF")
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.right.fell:
print("RT")
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
timelapse_remaining = pycam.timelapse_rates[pycam.timelapse_rate]
timelapse_timestamp = time.time() + timelapse_remaining + 1
else: # is running, turn off
timelapse_remaining = None