71
71
_NVM_RESOLUTION = const (1 )
72
72
_NVM_EFFECT = const (2 )
73
73
_NVM_MODE = const (3 )
74
+ _NVM_TIMELAPSE_RATE = const (4 )
75
+ _NVM_TIMELAPSE_SUBMODE = const (5 )
74
76
75
77
76
78
class PyCameraBase : # pylint: disable=too-many-instance-attributes,too-many-public-methods
@@ -168,7 +170,27 @@ class PyCameraBase: # pylint: disable=too-many-instance-attributes,too-many-pub
168
170
"Sepia" ,
169
171
"Solarize" ,
170
172
)
171
- modes = ("JPEG" , "GIF" , "GBOY" , "STOP" )
173
+
174
+ timelapse_rates = (
175
+ 5 ,
176
+ 10 ,
177
+ 20 ,
178
+ 30 ,
179
+ 60 ,
180
+ 90 ,
181
+ 60 * 2 ,
182
+ 60 * 3 ,
183
+ 60 * 4 ,
184
+ 60 * 5 ,
185
+ 60 * 10 ,
186
+ 60 * 15 ,
187
+ 60 * 30 ,
188
+ 60 * 60 ,
189
+ )
190
+
191
+ timelapse_submodes = ("HiPwr" , "MedPwr" , "LowPwr" )
192
+
193
+ modes = ("JPEG" , "GIF" , "GBOY" , "STOP" , "LAPS" )
172
194
173
195
_INIT_SEQUENCE = (
174
196
b"\x01 \x80 \x78 " # _SWRESET and Delay 120ms
@@ -187,6 +209,11 @@ def __init__(self) -> None: # pylint: disable=too-many-statements
187
209
self ._timestamp = time .monotonic ()
188
210
self ._bigbuf = None
189
211
self ._botbar = None
212
+ self ._timelapsebar = None
213
+ self .timelapse_rate_label = None
214
+ self ._timelapsestatus = None
215
+ self .timelapsestatus_label = None
216
+ self .timelapse_submode_label = None
190
217
self ._camera_device = None
191
218
self ._display_bus = None
192
219
self ._effect_label = None
@@ -249,13 +276,13 @@ def make_debounced_expander_pin(pin_no):
249
276
def make_camera_ui (self ):
250
277
"""Create displayio widgets for the standard camera UI"""
251
278
self ._sd_label = label .Label (
252
- terminalio .FONT , text = "SD ??" , color = 0x0 , x = 150 , y = 10 , scale = 2
279
+ terminalio .FONT , text = "SD ??" , color = 0x0 , x = 170 , y = 10 , scale = 2
253
280
)
254
281
self ._effect_label = label .Label (
255
282
terminalio .FONT , text = "EFFECT" , color = 0xFFFFFF , x = 4 , y = 10 , scale = 2
256
283
)
257
284
self ._mode_label = label .Label (
258
- terminalio .FONT , text = "MODE" , color = 0xFFFFFF , x = 150 , y = 10 , scale = 2
285
+ terminalio .FONT , text = "MODE" , color = 0xFFFFFF , x = 170 , y = 10 , scale = 2
259
286
)
260
287
self ._topbar = displayio .Group ()
261
288
self ._res_label = label .Label (
@@ -268,8 +295,23 @@ def make_camera_ui(self):
268
295
self ._botbar .append (self ._effect_label )
269
296
self ._botbar .append (self ._mode_label )
270
297
298
+ self ._timelapsebar = displayio .Group (x = 0 , y = 180 )
299
+ self .timelapse_submode_label = label .Label (
300
+ terminalio .FONT , text = "SubM" , color = 0xFFFFFF , x = 160 , y = 10 , scale = 2
301
+ )
302
+ self .timelapse_rate_label = label .Label (
303
+ terminalio .FONT , text = "Time" , color = 0xFFFFFF , x = 90 , y = 10 , scale = 2
304
+ )
305
+ self .timelapsestatus_label = label .Label (
306
+ terminalio .FONT , text = "Status" , color = 0xFFFFFF , x = 0 , y = 10 , scale = 2
307
+ )
308
+ self ._timelapsebar .append (self .timelapse_rate_label )
309
+ self ._timelapsebar .append (self .timelapsestatus_label )
310
+ self ._timelapsebar .append (self .timelapse_submode_label )
311
+
271
312
self .splash .append (self ._topbar )
272
313
self .splash .append (self ._botbar )
314
+ self .splash .append (self ._timelapsebar )
273
315
274
316
def init_accelerometer (self ):
275
317
"""Initialize the accelerometer"""
@@ -338,6 +380,8 @@ def init_camera(self, init_autofocus=True) -> None:
338
380
self .camera .saturation = 3
339
381
self .resolution = microcontroller .nvm [_NVM_RESOLUTION ]
340
382
self .mode = microcontroller .nvm [_NVM_MODE ]
383
+ self .timelapse_rate = microcontroller .nvm [_NVM_TIMELAPSE_RATE ]
384
+ self .timelapse_submode = microcontroller .nvm [_NVM_TIMELAPSE_SUBMODE ]
341
385
342
386
if init_autofocus :
343
387
self .autofocus_init ()
@@ -461,6 +505,9 @@ def select_setting(self, setting_name):
461
505
self ._res_label .text = self .resolutions [self ._resolution ]
462
506
self ._mode_label .color = 0xFFFFFF
463
507
self ._mode_label .background_color = 0x0
508
+ self .timelapse_rate_label .color = 0xFFFFFF
509
+ self .timelapse_rate_label .background_color = None
510
+
464
511
if setting_name == "effect" :
465
512
self ._effect_label .color = 0x0
466
513
self ._effect_label .background_color = 0xFFFFFF
@@ -478,6 +525,13 @@ def select_setting(self, setting_name):
478
525
self ._res_label .text = "LED CLR"
479
526
self ._res_label .color = 0x0
480
527
self ._res_label .background_color = 0xFFFFFF
528
+ elif setting_name == "led_color" :
529
+ self ._res_label .text = "LED CLR"
530
+ self ._res_label .color = 0x0
531
+ self ._res_label .background_color = 0xFFFFFF
532
+ elif setting_name == "timelapse_rate" :
533
+ self .timelapse_rate_label .color = 0x0
534
+ self .timelapse_rate_label .background_color = 0xFFFFFF
481
535
self .display .refresh ()
482
536
483
537
@property
@@ -538,6 +592,40 @@ def resolution(self, res):
538
592
self ._res_label .text = self .resolutions [res ]
539
593
self .display .refresh ()
540
594
595
+ @property
596
+ def timelapse_rate (self ):
597
+ """Get or set the amount of time between timelapse shots"""
598
+ return self ._timelapse_rate
599
+
600
+ @timelapse_rate .setter
601
+ def timelapse_rate (self , setting ):
602
+ setting = (setting + len (self .timelapse_rates )) % len (self .timelapse_rates )
603
+ self ._timelapse_rate = setting
604
+ if self .timelapse_rates [setting ] < 60 :
605
+ self .timelapse_rate_label .text = "%d S" % self .timelapse_rates [setting ]
606
+ else :
607
+ self .timelapse_rate_label .text = "%d M" % (
608
+ self .timelapse_rates [setting ] / 60
609
+ )
610
+ microcontroller .nvm [_NVM_TIMELAPSE_RATE ] = setting
611
+ self .display .refresh ()
612
+
613
+ @property
614
+ def timelapse_submode (self ):
615
+ """Get or set the power mode for timelapsing"""
616
+ return self ._timelapse_submode
617
+
618
+ @timelapse_submode .setter
619
+ def timelapse_submode (self , setting ):
620
+ setting = (setting + len (self .timelapse_submodes )) % len (
621
+ self .timelapse_submodes
622
+ )
623
+ self ._timelapse_submode = setting
624
+ self .timelapse_submode_label .text = self .timelapse_submodes [
625
+ self ._timelapse_submode
626
+ ]
627
+ microcontroller .nvm [_NVM_TIMELAPSE_SUBMODE ] = setting
628
+
541
629
def init_display (self ):
542
630
"""Initialize the TFT display"""
543
631
# construct displayio by hand
@@ -799,6 +887,80 @@ def led_color(self, new_color):
799
887
else :
800
888
self .pixels [:] = colors
801
889
890
+ def get_camera_autosettings (self ):
891
+ """Collect all the settings related to exposure and white balance"""
892
+ exposure = (
893
+ (self .read_camera_register (0x3500 ) << 12 )
894
+ + (self .read_camera_register (0x3501 ) << 4 )
895
+ + (self .read_camera_register (0x3502 ) >> 4 )
896
+ )
897
+ white_balance = [
898
+ self .read_camera_register (x )
899
+ for x in (0x3400 , 0x3401 , 0x3402 , 0x3403 , 0x3404 , 0x3405 )
900
+ ]
901
+
902
+ settings = {
903
+ "gain" : self .read_camera_register (0x350B ),
904
+ "exposure" : exposure ,
905
+ "wb" : white_balance ,
906
+ }
907
+ return settings
908
+
909
+ def set_camera_wb (self , wb_register_values = None ):
910
+ """Set the camera white balance.
911
+
912
+ The argument of `None` selects auto white balance, while
913
+ a list of 6 numbers sets a specific white balance.
914
+
915
+ The numbers can come from the datasheet or from
916
+ ``get_camera_autosettings()["wb"]``."""
917
+ if wb_register_values is None :
918
+ # just set to auto balance
919
+ self .camera .whitebal = True
920
+ return
921
+
922
+ if len (wb_register_values ) != 6 :
923
+ raise RuntimeError ("Please pass in 0x3400~0x3405 inclusive!" )
924
+
925
+ self .write_camera_register (0x3212 , 0x03 )
926
+ self .write_camera_register (0x3406 , 0x01 )
927
+ for i , reg_val in enumerate (wb_register_values ):
928
+ self .write_camera_register (0x3400 + i , reg_val )
929
+ self .write_camera_register (0x3212 , 0x13 )
930
+ self .write_camera_register (0x3212 , 0xA3 )
931
+
932
+ def set_camera_exposure (self , new_exposure = None ):
933
+ """Set the camera's exposure values
934
+
935
+ The argument of `None` selects auto exposure.
936
+
937
+ Otherwise, the new exposure data should come from
938
+ ``get_camera_autosettings()["exposure"]``."""
939
+ if new_exposure is None :
940
+ # just set auto expose
941
+ self .camera .exposure_ctrl = True
942
+ return
943
+ self .camera .exposure_ctrl = False
944
+
945
+ self .write_camera_register (0x3500 , (new_exposure >> 12 ) & 0xFF )
946
+ self .write_camera_register (0x3501 , (new_exposure >> 4 ) & 0xFF )
947
+ self .write_camera_register (0x3502 , (new_exposure << 4 ) & 0xFF )
948
+
949
+ def set_camera_gain (self , new_gain = None ):
950
+ """Set the camera's exposure values
951
+
952
+ The argument of `None` selects auto gain control.
953
+
954
+ Otherwise, the new exposure data should come from
955
+ ``get_camera_autosettings()["gain"]``."""
956
+ if new_gain is None :
957
+ # just set auto expose
958
+ self .camera .gain_ctrl = True
959
+ return
960
+
961
+ self .camera .gain_ctrl = False
962
+ self .write_camera_register (0x350B , new_gain )
963
+
802
964
803
965
class PyCamera (PyCameraBase ):
804
966
"""Wrapper class for the PyCamera hardware"""
0 commit comments