ov2640_jpeg_sd_kaluga1_3.py
1 # SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries 2 # SPDX-FileCopyrightText: Copyright (c) 2021 Jeff Epler for Adafruit Industries 3 # 4 # SPDX-License-Identifier: Unlicense 5 6 """ 7 Display an image on the LCD, then record an image when the REC button is pressed/held. 8 9 The Kaluga development kit comes in two versions (v1.2 and v1.3); this demo is 10 tested on v1.3. 11 12 The audio board must be mounted between the Kaluga and the LCD, it provides the 13 I2C pull-ups(!) 14 15 The v1.3 development kit's LCD can have one of two chips, the ili9341 or 16 st7789. Furthermore, there are at least 2 ILI9341 variants, one of which needs 17 rotation=90! This demo is for the ili9341. If the display is garbled, try adding 18 rotation=90, or try modifying it to use ST7799. 19 20 This example also requires an SD card breakout wired as follows: 21 * IO18: SD Clock Input 22 * IO17: SD Serial Output (MISO) 23 * IO14: SD Serial Input (MOSI) 24 * IO12: SD Chip Select 25 26 Insert a CircuitPython-compatible SD card before powering on the Kaluga. 27 Press the "Record" button on the audio daughterboard to take a photo. 28 """ 29 30 import os 31 32 import analogio 33 import board 34 import busio 35 import displayio 36 import sdcardio 37 import storage 38 from adafruit_ili9341 import ILI9341 39 import adafruit_ov2640 40 41 V_MODE = 1.98 42 V_RECORD = 2.41 43 44 a = analogio.AnalogIn(board.IO6) 45 46 # Release any resources currently in use for the displays 47 displayio.release_displays() 48 49 spi = busio.SPI(MOSI=board.LCD_MOSI, clock=board.LCD_CLK) 50 display_bus = displayio.FourWire( 51 spi, command=board.LCD_D_C, chip_select=board.LCD_CS, reset=board.LCD_RST 52 ) 53 display = ILI9341(display_bus, width=320, height=240, rotation=90) 54 55 bus = busio.I2C(scl=board.CAMERA_SIOC, sda=board.CAMERA_SIOD) 56 cam = adafruit_ov2640.OV2640( 57 bus, 58 data_pins=board.CAMERA_DATA, 59 clock=board.CAMERA_PCLK, 60 vsync=board.CAMERA_VSYNC, 61 href=board.CAMERA_HREF, 62 mclk=board.CAMERA_XCLK, 63 mclk_frequency=20_000_000, 64 size=adafruit_ov2640.OV2640_SIZE_QVGA, 65 ) 66 67 cam.flip_x = False 68 cam.flip_y = True 69 pid = cam.product_id 70 ver = cam.product_version 71 print(f"Detected pid={pid:x} ver={ver:x}") 72 # cam.test_pattern = True 73 74 g = displayio.Group(scale=1) 75 bitmap = displayio.Bitmap(320, 240, 65536) 76 tg = displayio.TileGrid( 77 bitmap, 78 pixel_shader=displayio.ColorConverter( 79 input_colorspace=displayio.Colorspace.BGR565_SWAPPED 80 ), 81 ) 82 g.append(tg) 83 display.root_group = g 84 85 display.auto_refresh = False 86 87 sd_spi = busio.SPI(clock=board.IO18, MOSI=board.IO14, MISO=board.IO17) 88 sd_cs = board.IO12 89 sdcard = sdcardio.SDCard(sd_spi, sd_cs) 90 vfs = storage.VfsFat(sdcard) 91 storage.mount(vfs, "/sd") 92 93 94 def exists(filename): 95 try: 96 os.stat(filename) 97 return True 98 except OSError: 99 return False 100 101 102 _image_counter = 0 103 104 105 def open_next_image(): 106 global _image_counter # pylint: disable=global-statement 107 while True: 108 filename = f"/sd/img{_image_counter:04d}.jpg" 109 _image_counter += 1 110 if exists(filename): 111 continue 112 print("#", filename) 113 return open(filename, "wb") # pylint: disable=consider-using-with 114 115 116 def capture_image(): 117 old_size = cam.size 118 old_colorspace = cam.colorspace 119 120 try: 121 cam.size = adafruit_ov2640.OV2640_SIZE_UXGA 122 cam.colorspace = adafruit_ov2640.OV2640_COLOR_JPEG 123 b = bytearray(cam.capture_buffer_size) 124 jpeg = cam.capture(b) 125 126 print(f"Captured {len(jpeg)} bytes of jpeg data") 127 with open_next_image() as f: 128 f.write(jpeg) 129 finally: 130 cam.size = old_size 131 cam.colorspace = old_colorspace 132 133 134 display.auto_refresh = False 135 while True: 136 a_voltage = a.value * a.reference_voltage / 65535 # pylint: disable=no-member 137 record_pressed = abs(a_voltage - V_RECORD) < 0.05 138 if record_pressed: 139 capture_image() 140 cam.capture(bitmap) 141 bitmap.dirty() 142 display.refresh(minimum_frames_per_second=0)