Simple test¶
Ensure your device works with this simple test.
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"""Capture an image from the camera and display it as ASCII art.
7
8This demo is designed to run on the Kaluga, but you can adapt it
9to other boards by changing the constructors for `bus` and `cam`
10appropriately.
11
12The camera is placed in YUV mode, so the top 8 bits of each color
13value can be treated as "greyscale".
14
15It's important that you use a terminal program that can interpret
16"ANSI" escape sequences. The demo uses them to "paint" each frame
17on top of the prevous one, rather than scrolling.
18
19Remember to take the lens cap off, or un-comment the line setting
20the test pattern!
21"""
22
23import sys
24import time
25
26import busio
27import board
28
29import adafruit_ov2640
30
31bus = busio.I2C(scl=board.CAMERA_SIOC, sda=board.CAMERA_SIOD)
32cam = adafruit_ov2640.OV2640(
33 bus,
34 data_pins=board.CAMERA_DATA,
35 clock=board.CAMERA_PCLK,
36 vsync=board.CAMERA_VSYNC,
37 href=board.CAMERA_HREF,
38 mclk=board.CAMERA_XCLK,
39 mclk_frequency=20_000_000,
40 size=adafruit_ov2640.OV2640_SIZE_QQVGA,
41)
42cam.colorspace = adafruit_ov2640.OV2640_COLOR_YUV
43cam.flip_y = True
44# cam.test_pattern = True
45
46buf = bytearray(2 * cam.width * cam.height)
47chars = b" .:-=+*#%@"
48remap = [chars[i * (len(chars) - 1) // 255] for i in range(256)]
49
50width = cam.width
51row = bytearray(2 * width)
52
53sys.stdout.write("\033[2J")
54while True:
55 cam.capture(buf)
56 for j in range(cam.height // 2):
57 sys.stdout.write(f"\033[{j}H")
58 for i in range(cam.width // 2):
59 row[i * 2] = row[i * 2 + 1] = remap[buf[4 * (width * j + i)]]
60 sys.stdout.write(row)
61 sys.stdout.write("\033[K")
62 sys.stdout.write("\033[J")
63 time.sleep(0.05)
LCD tests¶
Kaluga 1.3 with ili9341¶
Display an image from the camera on the Kaluga 1.3 board, if it is fitted with an ili9341 display.
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"""
7The Kaluga development kit comes in two versions (v1.2 and v1.3); this demo is
8tested on v1.3. It probably won't work on v1.2 without modification.
9
10The v1.3 development kit's LCD can have one of two chips, the ili9341 or
11st7789. Furthermore, there are at least 2 ILI9341 variants, one of which needs
12rotation=90! This demo is for the ili9341. If the display is garbled, try adding
13rotation=90, or try modifying it to use ST7799.
14
15The audio board must be mounted between the Kaluga and the LCD, it provides the
16I2C pull-ups(!)
17"""
18
19import board
20import busio
21import displayio
22from adafruit_ili9341 import ILI9341
23import adafruit_ov2640
24
25# Pylint is unable to see that the "size" property of OV2640_GrandCentral exists
26# pylint: disable=attribute-defined-outside-init
27
28# Release any resources currently in use for the displays
29displayio.release_displays()
30
31spi = busio.SPI(MOSI=board.LCD_MOSI, clock=board.LCD_CLK)
32display_bus = displayio.FourWire(
33 spi, command=board.LCD_D_C, chip_select=board.LCD_CS, reset=board.LCD_RST
34)
35display = ILI9341(display_bus, width=320, height=240, rotation=90)
36
37bus = busio.I2C(scl=board.CAMERA_SIOC, sda=board.CAMERA_SIOD)
38cam = adafruit_ov2640.OV2640(
39 bus,
40 data_pins=board.CAMERA_DATA,
41 clock=board.CAMERA_PCLK,
42 vsync=board.CAMERA_VSYNC,
43 href=board.CAMERA_HREF,
44 mclk=board.CAMERA_XCLK,
45 mclk_frequency=20_000_000,
46 size=adafruit_ov2640.OV2640_SIZE_QVGA,
47)
48
49cam.flip_x = False
50cam.flip_y = True
51pid = cam.product_id
52ver = cam.product_version
53print(f"Detected pid={pid:x} ver={ver:x}")
54# cam.test_pattern = True
55
56g = displayio.Group(scale=1)
57bitmap = displayio.Bitmap(320, 240, 65536)
58tg = displayio.TileGrid(
59 bitmap,
60 pixel_shader=displayio.ColorConverter(
61 input_colorspace=displayio.Colorspace.BGR565_SWAPPED
62 ),
63)
64g.append(tg)
65display.root_group = g
66
67display.auto_refresh = False
68while True:
69 cam.capture(bitmap)
70 bitmap.dirty()
71 display.refresh(minimum_frames_per_second=0)
72
73cam.deinit()
Kaluga 1.3 with st7789¶
Display an image from the camera on the Kaluga 1.3 board, if it is fitted with an st7789 display.
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"""
7The Kaluga development kit comes in two versions (v1.2 and v1.3); this demo is
8tested on v1.3.
9
10The v1.3 development kit's LCD can have one of two chips, the ili9341 or
11st7789. This demo is for the ili9341. There is no marking to distinguish the
12two chips. If the visible portion of the display's flexible cable has a bunch
13of straight lines, it may be an ili9341. If it has a bunch of wiggly traces,
14it may be an st7789. If in doubt, try both demos.
15
16The audio board must be mounted between the Kaluga and the LCD, it provides the
17I2C pull-ups(!)
18"""
19
20import board
21import busio
22import displayio
23from adafruit_st7789 import ST7789
24import adafruit_ov2640
25
26# Pylint is unable to see that the "size" property of OV2640_GrandCentral exists
27# pylint: disable=attribute-defined-outside-init
28
29# Release any resources currently in use for the displays
30displayio.release_displays()
31
32spi = busio.SPI(MOSI=board.LCD_MOSI, clock=board.LCD_CLK)
33display_bus = displayio.FourWire(
34 spi, command=board.LCD_D_C, chip_select=board.LCD_CS, reset=board.LCD_RST
35)
36display = ST7789(
37 display_bus, width=320, height=240, rotation=90, reverse_bytes_in_word=True
38)
39
40bus = busio.I2C(scl=board.CAMERA_SIOC, sda=board.CAMERA_SIOD)
41cam = adafruit_ov2640.OV2640(
42 bus,
43 data_pins=board.CAMERA_DATA,
44 clock=board.CAMERA_PCLK,
45 vsync=board.CAMERA_VSYNC,
46 href=board.CAMERA_HREF,
47 mclk=board.CAMERA_XCLK,
48 mclk_frequency=20_000_000,
49 size=adafruit_ov2640.OV2640_SIZE_QVGA,
50)
51
52# cam.flip_x = False
53# cam.flip_y = True
54pid = cam.product_id
55ver = cam.product_version
56print(f"Detected pid={pid:x} ver={ver:x}")
57# cam.test_pattern = True
58
59g = displayio.Group(scale=1)
60bitmap = displayio.Bitmap(320, 240, 65536)
61tg = displayio.TileGrid(
62 bitmap,
63 pixel_shader=displayio.ColorConverter(
64 input_colorspace=displayio.Colorspace.BGR565_SWAPPED
65 ),
66)
67g.append(tg)
68display.root_group = g
69
70display.auto_refresh = False
71while True:
72 cam.capture(bitmap)
73 bitmap.dirty()
74 display.refresh(minimum_frames_per_second=0)
75 print(".")
76
77cam.deinit()
Raspberry Pi Pico with st7789¶
Display an image from the camera connected to a Raspberry Pi Pico with an st7789 2” display
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"""
7Capture an image from the camera and display it on a supported LCD.
8"""
9
10import time
11from displayio import (
12 Bitmap,
13 Group,
14 TileGrid,
15 FourWire,
16 release_displays,
17 ColorConverter,
18 Colorspace,
19)
20
21from adafruit_st7789 import ST7789
22import board
23import busio
24import digitalio
25import adafruit_ov2640
26
27release_displays()
28# Set up the display (You must customize this block for your display!)
29spi = busio.SPI(clock=board.GP2, MOSI=board.GP3)
30display_bus = FourWire(spi, command=board.GP0, chip_select=board.GP1, reset=None)
31display = ST7789(display_bus, width=320, height=240, rotation=270)
32display.auto_refresh = False
33
34# Ensure the camera is shut down, so that it releases the SDA/SCL lines,
35# then create the configuration I2C bus
36
37with digitalio.DigitalInOut(board.GP10) as reset:
38 reset.switch_to_output(False)
39 time.sleep(0.001)
40 bus = busio.I2C(board.GP9, board.GP8)
41
42# Set up the camera (you must customize this for your board!)
43cam = adafruit_ov2640.OV2640(
44 bus,
45 data_pins=[
46 board.GP12,
47 board.GP13,
48 board.GP14,
49 board.GP15,
50 board.GP16,
51 board.GP17,
52 board.GP18,
53 board.GP19,
54 ], # [16] [org] etc
55 clock=board.GP11, # [15] [blk]
56 vsync=board.GP7, # [10] [brn]
57 href=board.GP21, # [27/o14] [red]
58 mclk=board.GP20, # [16/o15]
59 shutdown=None,
60 reset=board.GP10,
61) # [14]
62
63width = display.width
64height = display.height
65
66cam.size = adafruit_ov2640.OV2640_SIZE_QQVGA
67# cam.test_pattern = True
68bitmap = Bitmap(cam.width, cam.height, 65536)
69
70print(width, height, cam.width, cam.height)
71if bitmap is None:
72 raise SystemExit("Could not allocate a bitmap")
73
74g = Group(scale=1, x=(width - cam.width) // 2, y=(height - cam.height) // 2)
75tg = TileGrid(
76 bitmap, pixel_shader=ColorConverter(input_colorspace=Colorspace.BGR565_SWAPPED)
77)
78g.append(tg)
79display.root_group = g
80
81display.auto_refresh = False
82while True:
83 cam.capture(bitmap)
84 bitmap.dirty()
85 display.refresh(minimum_frames_per_second=0)
Kaluga 1.3 with ili9341, direct display¶
Preview images on LCD, bypassing displayio for slightly higher framerate
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"""
7The Kaluga development kit comes in two versions (v1.2 and v1.3); this demo is
8tested on v1.3.
9
10The audio board must be mounted between the Kaluga and the LCD, it provides the
11I2C pull-ups(!)
12
13The v1.3 development kit's LCD can have one of two chips, the ili9341 or
14st7789. Furthermore, there are at least 2 ILI9341 variants, which differ
15by rotation. This example is written for one if the ILI9341 variants,
16the one which usually uses rotation=90 to get a landscape display.
17
18This example also requires an SD card breakout wired as follows:
19 * IO18: SD Clock Input
20 * IO17: SD Serial Output (MISO)
21 * IO14: SD Serial Input (MOSI)
22 * IO12: SD Chip Select
23
24Insert a CircuitPython-compatible SD card before powering on the Kaluga.
25Press the "Record" button on the audio daughterboard to take a photo.
26"""
27
28import os
29import struct
30
31import analogio
32import board
33import busio
34import displayio
35import sdcardio
36import storage
37import adafruit_ov2640
38
39V_MODE = 1.98
40V_RECORD = 2.41
41
42a = analogio.AnalogIn(board.IO6)
43
44# Release any resources currently in use for the displays
45displayio.release_displays()
46
47spi = busio.SPI(MOSI=board.LCD_MOSI, clock=board.LCD_CLK)
48display_bus = displayio.FourWire(
49 spi,
50 command=board.LCD_D_C,
51 chip_select=board.LCD_CS,
52 reset=board.LCD_RST,
53 baudrate=80_000_000,
54)
55_INIT_SEQUENCE = (
56 b"\x01\x80\x80" # Software reset then delay 0x80 (128ms)
57 b"\xEF\x03\x03\x80\x02"
58 b"\xCF\x03\x00\xC1\x30"
59 b"\xED\x04\x64\x03\x12\x81"
60 b"\xE8\x03\x85\x00\x78"
61 b"\xCB\x05\x39\x2C\x00\x34\x02"
62 b"\xF7\x01\x20"
63 b"\xEA\x02\x00\x00"
64 b"\xc0\x01\x23" # Power control VRH[5:0]
65 b"\xc1\x01\x10" # Power control SAP[2:0];BT[3:0]
66 b"\xc5\x02\x3e\x28" # VCM control
67 b"\xc7\x01\x86" # VCM control2
68 b"\x36\x01\x40" # Memory Access Control
69 b"\x37\x01\x00" # Vertical scroll zero
70 b"\x3a\x01\x55" # COLMOD: Pixel Format Set
71 b"\xb1\x02\x00\x18" # Frame Rate Control (In Normal Mode/Full Colors)
72 b"\xb6\x03\x08\x82\x27" # Display Function Control
73 b"\xF2\x01\x00" # 3Gamma Function Disable
74 b"\x26\x01\x01" # Gamma curve selected
75 b"\xe0\x0f\x0F\x31\x2B\x0C\x0E\x08\x4E\xF1\x37\x07\x10\x03\x0E\x09\x00" # Set Gamma
76 b"\xe1\x0f\x00\x0E\x14\x03\x11\x07\x31\xC1\x48\x08\x0F\x0C\x31\x36\x0F" # Set Gamma
77 b"\x11\x80\x78" # Exit Sleep then delay 0x78 (120ms)
78 b"\x29\x80\x78" # Display on then delay 0x78 (120ms)
79)
80
81display = displayio.Display(display_bus, _INIT_SEQUENCE, width=320, height=240)
82
83bus = busio.I2C(scl=board.CAMERA_SIOC, sda=board.CAMERA_SIOD)
84cam = adafruit_ov2640.OV2640(
85 bus,
86 data_pins=board.CAMERA_DATA,
87 clock=board.CAMERA_PCLK,
88 vsync=board.CAMERA_VSYNC,
89 href=board.CAMERA_HREF,
90 mclk=board.CAMERA_XCLK,
91 mclk_frequency=20_000_000,
92 size=adafruit_ov2640.OV2640_SIZE_QVGA,
93)
94
95cam.flip_x = False
96cam.flip_y = True
97pid = cam.product_id
98ver = cam.product_version
99print(f"Detected pid={pid:x} ver={ver:x}")
100# cam.test_pattern = True
101
102bitmap = displayio.Bitmap(320, 240, 65536)
103
104display.auto_refresh = False
105
106sd_spi = busio.SPI(clock=board.IO18, MOSI=board.IO14, MISO=board.IO17)
107sd_cs = board.IO12
108sdcard = sdcardio.SDCard(sd_spi, sd_cs)
109vfs = storage.VfsFat(sdcard)
110storage.mount(vfs, "/sd")
111
112
113def exists(filename):
114 try:
115 os.stat(filename)
116 return True
117 except OSError:
118 return False
119
120
121_image_counter = 0
122
123
124def open_next_image():
125 global _image_counter # pylint: disable=global-statement
126 while True:
127 filename = f"/sd/img{_image_counter:04d}.jpg"
128 _image_counter += 1
129 if exists(filename):
130 continue
131 print("#", filename)
132 return open(filename, "wb") # pylint: disable=consider-using-with
133
134
135def capture_image():
136 old_size = cam.size
137 old_colorspace = cam.colorspace
138 exposure = cam.exposure
139 try:
140 cam.size = adafruit_ov2640.OV2640_SIZE_UXGA
141 cam.colorspace = adafruit_ov2640.OV2640_COLOR_JPEG
142 cam.exposure = exposure
143 b = bytearray(cam.capture_buffer_size)
144 jpeg = cam.capture(b)
145
146 print(f"Captured {len(jpeg)} bytes of jpeg data")
147 with open_next_image() as f:
148 f.write(jpeg)
149 finally:
150 cam.size = old_size
151 cam.colorspace = old_colorspace
152 cam.exposure = exposure
153
154
155def main():
156 display.auto_refresh = False
157 display_bus.send(42, struct.pack(">hh", 0, 319))
158 display_bus.send(43, struct.pack(">hh", 0, 239))
159 while True:
160 a_voltage = a.value * a.reference_voltage / 65535 # pylint: disable=no-member
161 record_pressed = abs(a_voltage - V_RECORD) < 0.05
162 if record_pressed:
163 capture_image()
164 cam.capture(bitmap)
165 display_bus.send(44, bitmap)
166
167
168main()
Image-saving tests¶
Kaluga 1.3 with ili9341, internal flash, JPEG¶
Preview images on LCD t hen save JPEG images to internal flash on Kaluga 1.3. Requires the second snippet of
code to be saved as boot.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"""
7The Kaluga development kit comes in two versions (v1.2 and v1.3); this demo is
8tested on v1.3.
9
10The audio board must be mounted between the Kaluga and the LCD, it provides the
11I2C pull-ups(!)
12
13You also need to place ov2640_jpeg_kaluga1_3_boot.py at CIRCUITPY/boot.py
14and reset the board to make the internal flash readable by CircuitPython.
15You can make CIRCUITPY readable from your PC by booting CircuitPython in
16safe mode or holding the "MODE" button on the audio daughterboard while
17powering on or resetting the board.
18"""
19
20import board
21import busio
22import adafruit_ov2640
23
24
25bus = busio.I2C(scl=board.CAMERA_SIOC, sda=board.CAMERA_SIOD)
26cam = adafruit_ov2640.OV2640(
27 bus,
28 data_pins=board.CAMERA_DATA,
29 clock=board.CAMERA_PCLK,
30 vsync=board.CAMERA_VSYNC,
31 href=board.CAMERA_HREF,
32 mclk=board.CAMERA_XCLK,
33 mclk_frequency=20_000_000,
34 size=adafruit_ov2640.OV2640_SIZE_QVGA,
35)
36
37pid = cam.product_id
38ver = cam.product_version
39print(f"Detected pid={pid:x} ver={ver:x}")
40# cam.test_pattern = True
41
42cam.colorspace = adafruit_ov2640.OV2640_COLOR_JPEG
43b = bytearray(cam.capture_buffer_size)
44jpeg = cam.capture(b)
45
46print(f"Captured {len(jpeg)} bytes of jpeg data")
47try:
48 with open("/jpeg.jpg", "wb") as f:
49 f.write(jpeg)
50except OSError as e:
51 print(e)
52 print(
53 "A 'read-only filesystem' error occurs if you did not correctly install"
54 "\nov2640_jpeg_kaluga1_3_boot.py as CIRCUITPY/boot.py and reset the board"
55 )
56print("Wrote to CIRCUITPY/jpeg.jpg")
boot.py
for the above program
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"""Use this file as CIRCUITPY/boot.py in conjunction with ov2640_jpeg_kaluga1_3.py
6
7It makes the CIRCUITPY filesystem writable to CircuitPython
8(and read-only to the PC) unless the "MODE" button on the audio
9daughterboard is held while the board is powered on or reset.
10"""
11
12import analogio
13import board
14import storage
15
16V_MODE = 1.98
17V_RECORD = 2.41
18
19a = analogio.AnalogIn(board.IO6)
20a_voltage = a.value * a.reference_voltage / 65535 # pylint: disable=no-member
21if abs(a_voltage - V_MODE) > 0.05: # If mode is NOT pressed...
22 print("storage writable by CircuitPython")
23 storage.remount("/", readonly=False)
Kaluga 1.3 with ili9341, external SD card, JPEG¶
Preview images on LCD then save JPEG images to SD on Kaluga 1.3 fitted with an ili9341 display.
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"""
7Display an image on the LCD, then record an image when the REC button is pressed/held.
8
9The Kaluga development kit comes in two versions (v1.2 and v1.3); this demo is
10tested on v1.3.
11
12The audio board must be mounted between the Kaluga and the LCD, it provides the
13I2C pull-ups(!)
14
15The v1.3 development kit's LCD can have one of two chips, the ili9341 or
16st7789. Furthermore, there are at least 2 ILI9341 variants, one of which needs
17rotation=90! This demo is for the ili9341. If the display is garbled, try adding
18rotation=90, or try modifying it to use ST7799.
19
20This 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
26Insert a CircuitPython-compatible SD card before powering on the Kaluga.
27Press the "Record" button on the audio daughterboard to take a photo.
28"""
29
30import os
31
32import analogio
33import board
34import busio
35import displayio
36import sdcardio
37import storage
38from adafruit_ili9341 import ILI9341
39import adafruit_ov2640
40
41V_MODE = 1.98
42V_RECORD = 2.41
43
44a = analogio.AnalogIn(board.IO6)
45
46# Release any resources currently in use for the displays
47displayio.release_displays()
48
49spi = busio.SPI(MOSI=board.LCD_MOSI, clock=board.LCD_CLK)
50display_bus = displayio.FourWire(
51 spi, command=board.LCD_D_C, chip_select=board.LCD_CS, reset=board.LCD_RST
52)
53display = ILI9341(display_bus, width=320, height=240, rotation=90)
54
55bus = busio.I2C(scl=board.CAMERA_SIOC, sda=board.CAMERA_SIOD)
56cam = 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
67cam.flip_x = False
68cam.flip_y = True
69pid = cam.product_id
70ver = cam.product_version
71print(f"Detected pid={pid:x} ver={ver:x}")
72# cam.test_pattern = True
73
74g = displayio.Group(scale=1)
75bitmap = displayio.Bitmap(320, 240, 65536)
76tg = displayio.TileGrid(
77 bitmap,
78 pixel_shader=displayio.ColorConverter(
79 input_colorspace=displayio.Colorspace.BGR565_SWAPPED
80 ),
81)
82g.append(tg)
83display.root_group = g
84
85display.auto_refresh = False
86
87sd_spi = busio.SPI(clock=board.IO18, MOSI=board.IO14, MISO=board.IO17)
88sd_cs = board.IO12
89sdcard = sdcardio.SDCard(sd_spi, sd_cs)
90vfs = storage.VfsFat(sdcard)
91storage.mount(vfs, "/sd")
92
93
94def 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
105def 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
116def 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
134display.auto_refresh = False
135while 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)
Kaluga 1.3 with ili9341, external SD card, BMP¶
Preview images on LCD then save BMP images to SD on Kaluga 1.3 fitted with an ili9341 display.
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"""
7The Kaluga development kit comes in two versions (v1.2 and v1.3); this demo is
8tested on v1.3.
9
10The audio board must be mounted between the Kaluga and the LCD, it provides the
11I2C pull-ups(!)
12
13The v1.3 development kit's LCD can have one of two chips, the ili9341 or
14st7789. Furthermore, there are at least 2 ILI9341 variants, one of which needs
15rotation=90! This demo is for the ili9341. If the display is garbled, try adding
16rotation=90, or try modifying it to use ST7799.
17
18This example also requires an SD card breakout wired as follows:
19 * IO18: SD Clock Input
20 * IO17: SD Serial Output (MISO)
21 * IO14: SD Serial Input (MOSI)
22 * IO12: SD Chip Select
23
24Insert a CircuitPython-compatible SD card before powering on the Kaluga.
25Press the "Record" button on the audio daughterboard to take a photo in BMP format.
26"""
27
28import os
29import struct
30import ulab.numpy as np
31
32import analogio
33import board
34import busio
35import displayio
36import sdcardio
37import storage
38import adafruit_ov2640
39
40# Nominal voltages of several of the buttons on the audio daughterboard
41V_MODE = 1.98
42V_RECORD = 2.41
43
44a = analogio.AnalogIn(board.IO6)
45
46# Release any resources currently in use for the displays
47displayio.release_displays()
48
49spi = busio.SPI(MOSI=board.LCD_MOSI, clock=board.LCD_CLK)
50display_bus = displayio.FourWire(
51 spi,
52 command=board.LCD_D_C,
53 chip_select=board.LCD_CS,
54 reset=board.LCD_RST,
55 baudrate=80_000_000,
56)
57_INIT_SEQUENCE = (
58 b"\x01\x80\x80" # Software reset then delay 0x80 (128ms)
59 b"\xEF\x03\x03\x80\x02"
60 b"\xCF\x03\x00\xC1\x30"
61 b"\xED\x04\x64\x03\x12\x81"
62 b"\xE8\x03\x85\x00\x78"
63 b"\xCB\x05\x39\x2C\x00\x34\x02"
64 b"\xF7\x01\x20"
65 b"\xEA\x02\x00\x00"
66 b"\xc0\x01\x23" # Power control VRH[5:0]
67 b"\xc1\x01\x10" # Power control SAP[2:0];BT[3:0]
68 b"\xc5\x02\x3e\x28" # VCM control
69 b"\xc7\x01\x86" # VCM control2
70 b"\x36\x01\x90" # Memory Access Control
71 b"\x37\x01\x00" # Vertical scroll zero
72 b"\x3a\x01\x55" # COLMOD: Pixel Format Set
73 b"\xb1\x02\x00\x18" # Frame Rate Control (In Normal Mode/Full Colors)
74 b"\xb6\x03\x08\x82\x27" # Display Function Control
75 b"\xF2\x01\x00" # 3Gamma Function Disable
76 b"\x26\x01\x01" # Gamma curve selected
77 b"\xe0\x0f\x0F\x31\x2B\x0C\x0E\x08\x4E\xF1\x37\x07\x10\x03\x0E\x09\x00" # Set Gamma
78 b"\xe1\x0f\x00\x0E\x14\x03\x11\x07\x31\xC1\x48\x08\x0F\x0C\x31\x36\x0F" # Set Gamma
79 b"\x11\x80\x78" # Exit Sleep then delay 0x78 (120ms)
80 b"\x29\x80\x78" # Display on then delay 0x78 (120ms)
81)
82
83display = displayio.Display(
84 display_bus, _INIT_SEQUENCE, width=320, height=240, auto_refresh=False
85)
86
87bus = busio.I2C(scl=board.CAMERA_SIOC, sda=board.CAMERA_SIOD)
88cam = adafruit_ov2640.OV2640(
89 bus,
90 data_pins=board.CAMERA_DATA,
91 clock=board.CAMERA_PCLK,
92 vsync=board.CAMERA_VSYNC,
93 href=board.CAMERA_HREF,
94 mclk=board.CAMERA_XCLK,
95 mclk_frequency=20_000_000,
96 size=adafruit_ov2640.OV2640_SIZE_QVGA,
97)
98
99cam.flip_x = False
100cam.flip_y = False
101cam.test_pattern = False
102
103g = displayio.Group(scale=1)
104bitmap = displayio.Bitmap(320, 240, 65536)
105tg = displayio.TileGrid(
106 bitmap,
107 pixel_shader=displayio.ColorConverter(
108 input_colorspace=displayio.Colorspace.RGB565_SWAPPED
109 ),
110)
111g.append(tg)
112display.root_group = g
113
114
115sd_spi = busio.SPI(clock=board.IO18, MOSI=board.IO14, MISO=board.IO17)
116sd_cs = board.IO12
117sdcard = sdcardio.SDCard(sd_spi, sd_cs)
118vfs = storage.VfsFat(sdcard)
119storage.mount(vfs, "/sd")
120
121
122def exists(filename):
123 try:
124 os.stat(filename)
125 return True
126 except OSError:
127 return False
128
129
130_image_counter = 0
131
132
133def open_next_image(extension="jpg"):
134 global _image_counter # pylint: disable=global-statement
135 while True:
136 filename = f"/sd/img{_image_counter:04d}.{extension}"
137 _image_counter += 1
138 if exists(filename):
139 continue
140 print("#", filename)
141 return open(filename, "wb") # pylint: disable=consider-using-with
142
143
144### These routines are for writing BMP files in the RGB565 or BGR565 formats.
145_BI_BITFIELDS = 3
146
147_bitmask_rgb565 = (0xF800, 0x7E0, 0x1F)
148_bitmask_bgr565 = (0x1F, 0x7E0, 0xF800)
149
150
151def write_header(output_file, width, height, masks):
152 def put_word(value):
153 output_file.write(struct.pack("<H", value))
154
155 def put_dword(value):
156 output_file.write(struct.pack("<I", value))
157
158 def put_long(value):
159 output_file.write(struct.pack("<i", value))
160
161 def put_padding(length):
162 output_file.write(b"\0" * length)
163
164 filesize = 14 + 108 + height * width * 2
165
166 # BMP header
167 output_file.write(b"BM")
168 put_dword(filesize)
169 put_word(0) # Creator 1
170 put_word(0) # Creator 2
171 put_dword(14 + 108) # Offset of bitmap data
172
173 # DIB header (BITMAPV4HEADER)
174 put_dword(108) # sizeof(BITMAPV4HEADER)
175 put_long(width)
176 put_long(-height)
177 put_word(1) # number of color planes (must be 1)
178 put_word(16) # number of bits per pixel
179 put_dword(_BI_BITFIELDS) # "compression"
180 put_dword(2 * width * height) # size of raw bitmap data
181 put_long(11811) # 72dpi -> pixels/meter
182 put_long(11811) # 72dpi -> pixels/meter
183 put_dword(0) # palette size
184 put_dword(0) # important color count
185 put_dword(masks[0]) # red mask
186 put_dword(masks[1]) # green mask
187 put_dword(masks[2]) # blue mask
188 put_dword(0) # alpha mask
189 put_dword(0) # CS Type
190 put_padding(3 * 3 * 4) # CIEXYZ infrmation
191 put_dword(144179) # 2.2 gamma red
192 put_dword(144179) # 2.2 gamma green
193 put_dword(144179) # 2.2 gamma blue
194
195
196def capture_image_bmp(the_bitmap):
197 with open_next_image("bmp") as f:
198 swapped = np.frombuffer(the_bitmap, dtype=np.uint16)
199 swapped.byteswap(inplace=True)
200 write_header(f, the_bitmap.width, the_bitmap.height, _bitmask_rgb565)
201 f.write(swapped)
202
203
204display.auto_refresh = False
205old_record_pressed = True
206
207while True:
208 a_voltage = a.value * a.reference_voltage / 65535 # pylint: disable=no-member
209 cam.capture(bitmap)
210 bitmap.dirty()
211
212 record_pressed = abs(a_voltage - V_RECORD) < 0.05
213 display.refresh(minimum_frames_per_second=0)
214 if record_pressed and not old_record_pressed:
215 capture_image_bmp(bitmap)
216 old_record_pressed = record_pressed
Kaluga 1.3 with Adafruit IO¶
Upload JPEG images to Adafruit IO. Requires that WIFI and Adafruit IO be configured in secrets.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"""
7The Kaluga development kit comes in two versions (v1.2 and v1.3); this demo is
8tested on v1.3.
9
10The audio board must be mounted between the Kaluga and the LCD, it provides the
11I2C pull-ups(!)
12
13This example requires that your WIFI and Adafruit IO credentials be configured
14in CIRCUITPY/secrets.py, and that you have created a feed called "image" with
15history disabled.
16
17The maximum image size is 100kB after base64 encoding, or about 65kB before
18base64 encoding. In practice, "SVGA" (800x600) images are typically around
1940kB even though the "capture_buffer_size" (theoretical maximum size) is
20(width*height/5) bytes or 96kB.
21"""
22
23import binascii
24import ssl
25import time
26from secrets import secrets # pylint: disable=no-name-in-module
27
28import board
29import busio
30import wifi
31import socketpool
32import adafruit_minimqtt.adafruit_minimqtt as MQTT
33from adafruit_io.adafruit_io import IO_MQTT
34import adafruit_ov2640
35
36feed_name = "image"
37
38print("Connecting to WIFI")
39wifi.radio.connect(secrets["ssid"], secrets["password"])
40pool = socketpool.SocketPool(wifi.radio)
41
42print("Connecting to Adafruit IO")
43mqtt_client = MQTT.MQTT(
44 broker="io.adafruit.com",
45 username=secrets["aio_username"],
46 password=secrets["aio_key"],
47 socket_pool=pool,
48 ssl_context=ssl.create_default_context(),
49)
50mqtt_client.connect()
51io = IO_MQTT(mqtt_client)
52
53bus = busio.I2C(scl=board.CAMERA_SIOC, sda=board.CAMERA_SIOD)
54cam = adafruit_ov2640.OV2640(
55 bus,
56 data_pins=board.CAMERA_DATA,
57 clock=board.CAMERA_PCLK,
58 vsync=board.CAMERA_VSYNC,
59 href=board.CAMERA_HREF,
60 mclk=board.CAMERA_XCLK,
61 mclk_frequency=20_000_000,
62 size=adafruit_ov2640.OV2640_SIZE_QVGA,
63)
64
65cam.flip_x = False
66cam.flip_y = False
67cam.test_pattern = False
68
69cam.size = adafruit_ov2640.OV2640_SIZE_SVGA
70cam.colorspace = adafruit_ov2640.OV2640_COLOR_JPEG
71jpeg_buffer = bytearray(cam.capture_buffer_size)
72while True:
73 jpeg = cam.capture(jpeg_buffer)
74 print(f"Captured {len(jpeg)} bytes of jpeg data")
75
76 # b2a_base64() appends a trailing newline, which IO does not like
77 encoded_data = binascii.b2a_base64(jpeg).strip()
78 print(f"Expanded to {len(encoded_data)} for IO upload")
79
80 io.publish("image", encoded_data)
81
82 print("Waiting 3s")
83 time.sleep(3)