/ examples / ov2640_jpeg_sd_kaluga1_3.py
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)