The OV2640 camera module can also capture JPEG images up to 2 megapixels (1600x1200 pixels). This still requires a large buffer (of width×height÷5 bytes, or 384,000 bytes for a 2-megapixel image), so the demo below is coded for the Kaluga development board together with the MicroSD card breakout board+.
CircuitPython doesn't encode or decode the JPEG image itself, it just uses a JPEG-encoded file produced by the camera and stores it on the SD card.
Make the following connections for the SD card breakout:
- IO18 to CLK
- IO14 to DI
- IO17 to DO
- IO12 to CS
- GND to GND
- 5V to 5V
While the demo runs, it will show a live image on the LCD. When you hold the REC button, it will save the picture as a jpeg image to the inserted SD card. Note that because the REC button is only polled when the screen is not updating, you have to hold it, not just quickly press it.
# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
# SPDX-FileCopyrightText: Copyright (c) 2021 Jeff Epler for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense
"""
Display an image on the LCD, then record an image when the REC button is pressed/held.
The Kaluga development kit comes in two versions (v1.2 and v1.3); this demo is
tested on v1.3.
The audio board must be mounted between the Kaluga and the LCD, it provides the
I2C pull-ups(!)
The v1.3 development kit's LCD can have one of two chips, the ili9341 or
st7789. Furthermore, there are at least 2 ILI9341 variants, one of which needs
rotation=90! This demo is for the ili9341. If the display is garbled, try adding
rotation=90, or try modifying it to use ST7799.
This example also requires an SD card breakout wired as follows:
* IO18: SD Clock Input
* IO17: SD Serial Output (MISO)
* IO14: SD Serial Input (MOSI)
* IO12: SD Chip Select
Insert a CircuitPython-compatible SD card before powering on the Kaluga.
Press the "Record" button on the audio daughterboard to take a photo.
"""
import os
import analogio
import board
import busio
import displayio
import fourwire
import sdcardio
import storage
from adafruit_ili9341 import ILI9341
import adafruit_ov2640
V_MODE = 1.98
V_RECORD = 2.41
a = analogio.AnalogIn(board.IO6)
# Release any resources currently in use for the displays
displayio.release_displays()
spi = busio.SPI(MOSI=board.LCD_MOSI, clock=board.LCD_CLK)
display_bus = fourwire.FourWire(
spi, command=board.LCD_D_C, chip_select=board.LCD_CS, reset=board.LCD_RST
)
display = ILI9341(display_bus, width=320, height=240, rotation=90)
bus = busio.I2C(scl=board.CAMERA_SIOC, sda=board.CAMERA_SIOD)
cam = adafruit_ov2640.OV2640(
bus,
data_pins=board.CAMERA_DATA,
clock=board.CAMERA_PCLK,
vsync=board.CAMERA_VSYNC,
href=board.CAMERA_HREF,
mclk=board.CAMERA_XCLK,
mclk_frequency=20_000_000,
size=adafruit_ov2640.OV2640_SIZE_QVGA,
)
cam.flip_x = False
cam.flip_y = True
pid = cam.product_id
ver = cam.product_version
print(f"Detected pid={pid:x} ver={ver:x}")
# cam.test_pattern = True
g = displayio.Group(scale=1)
bitmap = displayio.Bitmap(320, 240, 65536)
tg = displayio.TileGrid(
bitmap,
pixel_shader=displayio.ColorConverter(input_colorspace=displayio.Colorspace.BGR565_SWAPPED),
)
g.append(tg)
display.root_group = g
display.auto_refresh = False
sd_spi = busio.SPI(clock=board.IO18, MOSI=board.IO14, MISO=board.IO17)
sd_cs = board.IO12
sdcard = sdcardio.SDCard(sd_spi, sd_cs)
vfs = storage.VfsFat(sdcard)
storage.mount(vfs, "/sd")
def exists(filename):
try:
os.stat(filename)
return True
except OSError:
return False
_image_counter = 0
def open_next_image():
global _image_counter # noqa: PLW0603
while True:
filename = f"/sd/img{_image_counter:04d}.jpg"
_image_counter += 1
if exists(filename):
continue
print("#", filename)
return open(filename, "wb")
def capture_image():
old_size = cam.size
old_colorspace = cam.colorspace
try:
cam.size = adafruit_ov2640.OV2640_SIZE_UXGA
cam.colorspace = adafruit_ov2640.OV2640_COLOR_JPEG
b = bytearray(cam.capture_buffer_size)
jpeg = cam.capture(b)
print(f"Captured {len(jpeg)} bytes of jpeg data")
with open_next_image() as f:
f.write(jpeg)
finally:
cam.size = old_size
cam.colorspace = old_colorspace
display.auto_refresh = False
while True:
a_voltage = a.value * a.reference_voltage / 65535
record_pressed = abs(a_voltage - V_RECORD) < 0.05
if record_pressed:
capture_image()
cam.capture(bitmap)
bitmap.dirty()
display.refresh(minimum_frames_per_second=0)
Page last edited January 22, 2025
Text editor powered by tinymce.