71
71
_NVM_MODE = const (3 )
72
72
73
73
74
- class PyCamera : # pylint: disable=too-many-instance-attributes,too-many-public-methods
75
- """Wrapper class for the PyCamera hardware"""
74
+ class PyCameraBase : # pylint: disable=too-many-instance-attributes,too-many-public-methods
75
+ """Base class for PyCamera hardware"""
76
+
77
+ """Wrapper class for the PyCamera hardware with lots of smarts"""
76
78
77
79
_finalize_firmware_load = (
78
80
0x3022 ,
@@ -176,59 +178,41 @@ class PyCamera: # pylint: disable=too-many-instance-attributes,too-many-public-
176
178
b"\x29 \x80 \x05 " # _DISPON and Delay 5ms
177
179
)
178
180
179
- def i2c_scan (self ):
180
- """Print an I2C bus scan"""
181
- while not self ._i2c .try_lock ():
182
- pass
183
-
184
- try :
185
- print (
186
- "I2C addresses found:" ,
187
- [hex (device_address ) for device_address in self ._i2c .scan ()],
188
- )
189
- finally : # unlock the i2c bus when ctrl-c'ing out of the loop
190
- self ._i2c .unlock ()
191
-
192
181
def __init__ (self ) -> None : # pylint: disable=too-many-statements
193
- self . _timestamp = time . monotonic ()
182
+ displayio . release_displays ()
194
183
self ._i2c = board .I2C ()
195
184
self ._spi = board .SPI ()
196
- self .deinit_display ()
197
-
198
- self .splash = displayio .Group ()
199
- self ._sd_label = label .Label (
200
- terminalio .FONT , text = "SD ??" , color = 0x0 , x = 150 , y = 10 , scale = 2
201
- )
202
- self ._effect_label = label .Label (
203
- terminalio .FONT , text = "EFFECT" , color = 0xFFFFFF , x = 4 , y = 10 , scale = 2
204
- )
205
- self ._mode_label = label .Label (
206
- terminalio .FONT , text = "MODE" , color = 0xFFFFFF , x = 150 , y = 10 , scale = 2
207
- )
185
+ self ._timestamp = time .monotonic ()
186
+ self ._bigbuf = None
187
+ self ._botbar = None
188
+ self ._camera_device = None
189
+ self ._display_bus = None
190
+ self ._effect_label = None
191
+ self ._image_counter = 0
192
+ self ._mode_label = None
193
+ self ._res_label = None
194
+ self ._sd_label = None
195
+ self ._topbar = None
196
+ self .accel = None
197
+ self .camera = None
198
+ self .display = None
199
+ self .pixels = None
200
+ self .sdcard = None
201
+ self .splash = None
208
202
209
- # turn on the display first, its reset line may be shared with the IO expander(?)
210
- if not self .display :
211
- self .init_display ()
203
+ # Reset display and I/O expander
204
+ self ._tft_aw_reset = DigitalInOut (board .TFT_RESET )
205
+ self ._tft_aw_reset .switch_to_output (False )
206
+ time .sleep (0.05 )
207
+ self ._tft_aw_reset .switch_to_output (True )
212
208
213
209
self .shutter_button = DigitalInOut (board .BUTTON )
214
210
self .shutter_button .switch_to_input (Pull .UP )
215
211
self .shutter = Button (self .shutter_button )
216
212
217
- print ("reset camera" )
218
213
self ._cam_reset = DigitalInOut (board .CAMERA_RESET )
219
214
self ._cam_pwdn = DigitalInOut (board .CAMERA_PWDN )
220
215
221
- self ._cam_reset .switch_to_output (False )
222
- self ._cam_pwdn .switch_to_output (True )
223
- time .sleep (0.01 )
224
- self ._cam_pwdn .switch_to_output (False )
225
- time .sleep (0.01 )
226
- self ._cam_reset .switch_to_output (True )
227
- time .sleep (0.01 )
228
-
229
- print ("pre cam @" , time .monotonic () - self ._timestamp )
230
- self .i2c_scan ()
231
-
232
216
# AW9523 GPIO expander
233
217
self ._aw = adafruit_aw9523 .AW9523 (self ._i2c , address = 0x58 )
234
218
print ("Found AW9523" )
@@ -260,17 +244,40 @@ def make_debounced_expander_pin(pin_no):
260
244
261
245
self .mute = make_expander_output (_AW_MUTE , False )
262
246
263
- self .sdcard = None
264
- try :
265
- self .mount_sd_card ()
266
- except RuntimeError :
267
- pass # no card found, its ok!
268
- print ("sdcard done @" , time .monotonic () - self ._timestamp )
247
+ def make_camera_ui (self ):
248
+ """Create displayio widgets for the standard camera UI"""
249
+ self .splash = displayio .Group ()
250
+ self ._sd_label = label .Label (
251
+ terminalio .FONT , text = "SD ??" , color = 0x0 , x = 150 , y = 10 , scale = 2
252
+ )
253
+ self ._effect_label = label .Label (
254
+ terminalio .FONT , text = "EFFECT" , color = 0xFFFFFF , x = 4 , y = 10 , scale = 2
255
+ )
256
+ self ._mode_label = label .Label (
257
+ terminalio .FONT , text = "MODE" , color = 0xFFFFFF , x = 150 , y = 10 , scale = 2
258
+ )
259
+ self ._topbar = displayio .Group ()
260
+ self ._res_label = label .Label (
261
+ terminalio .FONT , text = "" , color = 0xFFFFFF , x = 0 , y = 10 , scale = 2
262
+ )
263
+ self ._topbar .append (self ._res_label )
264
+ self ._topbar .append (self ._sd_label )
265
+
266
+ self ._botbar = displayio .Group (x = 0 , y = 210 )
267
+ self ._botbar .append (self ._effect_label )
268
+ self ._botbar .append (self ._mode_label )
269
+
270
+ self .splash .append (self ._topbar )
271
+ self .splash .append (self ._botbar )
269
272
273
+ def init_accelerometer (self ):
274
+ """Initialize the accelerometer"""
270
275
# lis3dh accelerometer
271
276
self .accel = adafruit_lis3dh .LIS3DH_I2C (self ._i2c , address = 0x19 )
272
277
self .accel .range = adafruit_lis3dh .RANGE_2_G
273
278
279
+ def init_neopixel (self ):
280
+ """Initialize the neopixels (onboard & ring)"""
274
281
# main board neopixel
275
282
neopix = neopixel .NeoPixel (board .NEOPIXEL , 1 , brightness = 0.1 )
276
283
neopix .fill (0 )
@@ -282,6 +289,17 @@ def make_debounced_expander_pin(pin_no):
282
289
)
283
290
self .pixels .fill (0 )
284
291
292
+ def init_camera (self , init_autofocus = True ) -> None :
293
+ """Initialize the camera, by default including autofocus"""
294
+ print ("reset camera" )
295
+ self ._cam_reset .switch_to_output (False )
296
+ self ._cam_pwdn .switch_to_output (True )
297
+ time .sleep (0.01 )
298
+ self ._cam_pwdn .switch_to_output (False )
299
+ time .sleep (0.01 )
300
+ self ._cam_reset .switch_to_output (True )
301
+ time .sleep (0.01 )
302
+
285
303
print ("Initializing camera" )
286
304
self .camera = espcamera .Camera (
287
305
data_pins = board .CAMERA_DATA ,
@@ -305,33 +323,12 @@ def make_debounced_expander_pin(pin_no):
305
323
self .camera .address ,
306
324
)
307
325
)
308
- print ("camera done @" , time .monotonic () - self ._timestamp )
309
- print (dir (self .camera ))
310
326
311
327
self ._camera_device = I2CDevice (self ._i2c , self .camera .address )
312
- # display.auto_refresh = False
313
328
314
329
self .camera .hmirror = False
315
330
self .camera .vflip = True
316
331
317
- self ._bigbuf = None
318
-
319
- self ._topbar = displayio .Group ()
320
- self ._res_label = label .Label (
321
- terminalio .FONT , text = "" , color = 0xFFFFFF , x = 0 , y = 10 , scale = 2
322
- )
323
- self ._topbar .append (self ._res_label )
324
- self ._topbar .append (self ._sd_label )
325
-
326
- self ._botbar = displayio .Group (x = 0 , y = 210 )
327
- self ._botbar .append (self ._effect_label )
328
- self ._botbar .append (self ._mode_label )
329
-
330
- self .splash .append (self ._topbar )
331
- self .splash .append (self ._botbar )
332
- self .display .root_group = self .splash
333
- self .display .refresh ()
334
-
335
332
self .led_color = 0
336
333
self .led_level = 0
337
334
@@ -340,6 +337,10 @@ def make_debounced_expander_pin(pin_no):
340
337
self .camera .saturation = 3
341
338
self .resolution = microcontroller .nvm [_NVM_RESOLUTION ]
342
339
self .mode = microcontroller .nvm [_NVM_MODE ]
340
+
341
+ if init_autofocus :
342
+ self .autofocus_init ()
343
+
343
344
print ("init done @" , time .monotonic () - self ._timestamp )
344
345
345
346
def autofocus_init_from_file (self , filename ):
@@ -526,15 +527,15 @@ def resolution(self, res):
526
527
self ._res_label .text = self .resolutions [res ]
527
528
self .display .refresh ()
528
529
529
- def init_display (self , reset = True ):
530
+ def init_display (self ):
530
531
"""Initialize the TFT display"""
531
532
# construct displayio by hand
532
533
displayio .release_displays ()
533
534
self ._display_bus = displayio .FourWire (
534
535
self ._spi ,
535
536
command = board .TFT_DC ,
536
537
chip_select = board .TFT_CS ,
537
- reset = board . TFT_RESET if reset else None ,
538
+ reset = None ,
538
539
baudrate = 60_000_000 ,
539
540
)
540
541
self .display = board .DISPLAY
@@ -546,6 +547,7 @@ def init_display(self, reset=True):
546
547
height = 240 ,
547
548
colstart = 80 ,
548
549
auto_refresh = False ,
550
+ backlight_pin = board .TFT_BACKLIGHT ,
549
551
)
550
552
self .display .root_group = self .splash
551
553
self .display .refresh ()
@@ -562,7 +564,7 @@ def display_message(self, message, color=0xFF0000, scale=3):
562
564
text_area = label .Label (terminalio .FONT , text = message , color = color , scale = scale )
563
565
text_area .anchor_point = (0.5 , 0.5 )
564
566
if not self .display :
565
- self .init_display (None )
567
+ self .init_display ()
566
568
text_area .anchored_position = (self .display .width / 2 , self .display .height / 2 )
567
569
568
570
# Show it
@@ -572,10 +574,11 @@ def display_message(self, message, color=0xFF0000, scale=3):
572
574
573
575
def mount_sd_card (self ):
574
576
"""Attempt to mount the SD card"""
575
- self ._sd_label .text = "NO SD"
576
- self ._sd_label .color = 0xFF0000
577
+ if self ._sd_label is not None :
578
+ self ._sd_label .text = "NO SD"
579
+ self ._sd_label .color = 0xFF0000
577
580
if not self .card_detect .value :
578
- raise RuntimeError ("SD card detection failed " )
581
+ raise RuntimeError ("No SD card inserted " )
579
582
if self .sdcard :
580
583
self .sdcard .deinit ()
581
584
# depower SD card
@@ -585,6 +588,7 @@ def mount_sd_card(self):
585
588
# deinit display and SPI bus because we need to drive all SD pins LOW
586
589
# to ensure nothing, not even an I/O pin, could possibly power the SD
587
590
# card
591
+ had_display = self .display is not None
588
592
self .deinit_display ()
589
593
self ._spi .deinit ()
590
594
sckpin = DigitalInOut (board .SCK )
@@ -604,23 +608,28 @@ def mount_sd_card(self):
604
608
self ._card_power .value = False
605
609
card_cs .deinit ()
606
610
print ("sdcard init @" , time .monotonic () - self ._timestamp )
607
- self .sdcard = sdcardio .SDCard (self ._spi , board .CARD_CS , baudrate = 20_000_000 )
608
- vfs = storage .VfsFat (self .sdcard )
609
- print ("mount vfs @" , time .monotonic () - self ._timestamp )
610
- storage .mount (vfs , "/sd" )
611
- self .init_display (None )
612
- self ._image_counter = 0
613
- self ._sd_label .text = "SD OK"
614
- self ._sd_label .color = 0x00FF00
611
+ try :
612
+ self .sdcard = sdcardio .SDCard (self ._spi , board .CARD_CS , baudrate = 20_000_000 )
613
+ vfs = storage .VfsFat (self .sdcard )
614
+ print ("mount vfs @" , time .monotonic () - self ._timestamp )
615
+ storage .mount (vfs , "/sd" )
616
+ self ._image_counter = 0
617
+ if self ._sd_label is not None :
618
+ self ._sd_label .text = "SD OK"
619
+ self ._sd_label .color = 0x00FF00
620
+ finally :
621
+ if had_display :
622
+ self .init_display ()
615
623
616
624
def unmount_sd_card (self ):
617
625
"""Unmount the SD card, if mounted"""
618
626
try :
619
627
storage .umount ("/sd" )
620
628
except OSError :
621
629
pass
622
- self ._sd_label .text = "NO SD"
623
- self ._sd_label .color = 0xFF0000
630
+ if self ._sd_label is not None :
631
+ self ._sd_label .text = "NO SD"
632
+ self ._sd_label .color = 0xFF0000
624
633
625
634
def keys_debounce (self ):
626
635
"""Debounce all keys.
@@ -775,3 +784,21 @@ def led_color(self, new_color):
775
784
self .pixels .fill (colors )
776
785
else :
777
786
self .pixels [:] = colors
787
+
788
+
789
+ class PyCamera (PyCameraBase ):
790
+ """Wrapper class for the PyCamera hardware"""
791
+
792
+ def __init__ (self , init_autofocus = True ):
793
+ super ().__init__ ()
794
+
795
+ self .make_camera_ui ()
796
+ self .init_accelerometer ()
797
+ self .init_neopixel ()
798
+ self .init_display ()
799
+ self .init_camera (init_autofocus )
800
+ try :
801
+ self .mount_sd_card ()
802
+ except Exception as exc : # pylint: disable=broad-exception-caught
803
+ # No SD card inserted, it's OK
804
+ print (exc )
0 commit comments