Simple test
Ensure your device works with this simple test.
examples/ov5640_simpletest.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"""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 board
27import busio
28
29import adafruit_ov5640
30
31print("construct bus")
32bus = busio.I2C(scl=board.CAMERA_SIOC, sda=board.CAMERA_SIOD)
33print("construct camera")
34cam = adafruit_ov5640.OV5640(
35 bus,
36 data_pins=board.CAMERA_DATA,
37 clock=board.CAMERA_PCLK,
38 vsync=board.CAMERA_VSYNC,
39 href=board.CAMERA_HREF,
40 mclk=board.CAMERA_XCLK,
41 size=adafruit_ov5640.OV5640_SIZE_QQVGA,
42)
43print("print chip id")
44print(cam.chip_id)
45
46
47cam.colorspace = adafruit_ov5640.OV5640_COLOR_YUV
48cam.flip_y = True
49cam.flip_x = True
50cam.test_pattern = False
51
52buf = bytearray(cam.capture_buffer_size)
53chars = b" .':-+=*%$#"
54remap = [chars[i * (len(chars) - 1) // 255] for i in range(256)]
55
56width = cam.width
57row = bytearray(width)
58
59print("capturing")
60cam.capture(buf)
61print("capture complete")
62
63sys.stdout.write("\033[2J")
64while True:
65 cam.capture(buf)
66 for j in range(0, cam.height, 2):
67 sys.stdout.write(f"\033[{j // 2}H")
68 for i in range(cam.width):
69 row[i] = remap[buf[2 * (width * j + i)]]
70 sys.stdout.write(row)
71 sys.stdout.write("\033[K")
72 sys.stdout.write("\033[J")
73 time.sleep(0.05)
Directio
Use an LCD as a viewfinder, bypassing displayio
examples/ov5640_directio_kaluga1_3_ili9341.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
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"""
18
19import struct
20
21import board
22import busdisplay
23import busio
24import digitalio
25import displayio
26import fourwire
27from adafruit_ticks import ticks_less, ticks_ms
28
29import adafruit_ov5640
30
31# Set to True to enable the various effects & exposure modes to be tested
32test_effects = False
33
34# Release any resources currently in use for the displays
35displayio.release_displays()
36
37state = digitalio.DigitalInOut(board.IO4)
38state.switch_to_output(True)
39
40spi = busio.SPI(MOSI=board.LCD_MOSI, clock=board.LCD_CLK)
41display_bus = fourwire.FourWire(
42 spi,
43 command=board.LCD_D_C,
44 chip_select=board.LCD_CS,
45 reset=board.LCD_RST,
46 baudrate=80_000_000,
47)
48_INIT_SEQUENCE = (
49 b"\x01\x80\x80" # Software reset then delay 0x80 (128ms)
50 b"\xef\x03\x03\x80\x02"
51 b"\xcf\x03\x00\xc1\x30"
52 b"\xed\x04\x64\x03\x12\x81"
53 b"\xe8\x03\x85\x00\x78"
54 b"\xcb\x05\x39\x2c\x00\x34\x02"
55 b"\xf7\x01\x20"
56 b"\xea\x02\x00\x00"
57 b"\xc0\x01\x23" # Power control VRH[5:0]
58 b"\xc1\x01\x10" # Power control SAP[2:0];BT[3:0]
59 b"\xc5\x02\x3e\x28" # VCM control
60 b"\xc7\x01\x86" # VCM control2
61 b"\x36\x01\x40" # Memory Access Control
62 b"\x37\x01\x00" # Vertical scroll zero
63 b"\x3a\x01\x55" # COLMOD: Pixel Format Set
64 b"\xb1\x02\x00\x18" # Frame Rate Control (In Normal Mode/Full Colors)
65 b"\xb6\x03\x08\x82\x27" # Display Function Control
66 b"\xf2\x01\x00" # 3Gamma Function Disable
67 b"\x26\x01\x01" # Gamma curve selected
68 b"\xe0\x0f\x0f\x31\x2b\x0c\x0e\x08\x4e\xf1\x37\x07\x10\x03\x0e\x09\x00" # Set Gamma
69 b"\xe1\x0f\x00\x0e\x14\x03\x11\x07\x31\xc1\x48\x08\x0f\x0c\x31\x36\x0f" # Set Gamma
70 b"\x11\x80\x78" # Exit Sleep then delay 0x78 (120ms)
71 b"\x29\x80\x78" # Display on then delay 0x78 (120ms)
72)
73
74display = busdisplay.BusDisplay(display_bus, _INIT_SEQUENCE, width=320, height=240)
75
76bus = busio.I2C(scl=board.CAMERA_SIOC, sda=board.CAMERA_SIOD)
77cam = adafruit_ov5640.OV5640(
78 bus,
79 data_pins=board.CAMERA_DATA,
80 clock=board.CAMERA_PCLK,
81 vsync=board.CAMERA_VSYNC,
82 href=board.CAMERA_HREF,
83 mclk=board.CAMERA_XCLK,
84 size=adafruit_ov5640.OV5640_SIZE_QVGA,
85)
86
87cam.flip_x = False
88cam.flip_y = False
89chip_id = cam.chip_id
90print(f"Detected 0x{chip_id:x}")
91cam.test_pattern = False
92cam.effect = adafruit_ov5640.OV5640_SPECIAL_EFFECT_NONE
93cam.saturation = 3
94bitmap = displayio.Bitmap(cam.width, cam.height, 65536)
95print(len(memoryview(bitmap)))
96display.auto_refresh = False
97
98
99def special_modes(cam_obj):
100 def effect_modes(cam_obj):
101 for i in [
102 "NONE",
103 "NEGATIVE",
104 "GRAYSCALE",
105 "RED_TINT",
106 "GREEN_TINT",
107 "BLUE_TINT",
108 "SEPIA",
109 ]:
110 print(f"Effect {i}")
111 cam_obj.effect = getattr(adafruit_ov5640, f"OV5640_SPECIAL_EFFECT_{i}")
112 yield
113 cam_obj.effect = adafruit_ov5640.OV5640_SPECIAL_EFFECT_NONE
114
115 def saturation_modes(cam_obj):
116 for i in range(-4, 5):
117 print(f"Saturation {i}")
118 cam_obj.saturation = i
119 yield
120 cam_obj.saturation = 0
121
122 def brightness_modes(cam_obj):
123 for i in range(-4, 5):
124 print(f"Brightness {i}")
125 cam_obj.brightness = i
126 yield
127 cam_obj.brightness = 0
128
129 def contrast_modes(cam_obj):
130 for i in range(-3, 4):
131 print(f"Contrast {i}")
132 cam_obj.contrast = i
133 yield
134 cam_obj.contrast = 0
135
136 def white_balance_modes(cam_obj):
137 for i in ["AUTO", "SUNNY", "FLUORESCENT", "CLOUDY", "INCANDESCENT"]:
138 print(f"White Balance {i}")
139 cam_obj.white_balance = getattr(adafruit_ov5640, f"OV5640_WHITE_BALANCE_{i}")
140 yield
141 cam_obj.white_balance = adafruit_ov5640.OV5640_WHITE_BALANCE_AUTO
142
143 def exposure_value_modes(cam_obj):
144 for i in range(-3, 4):
145 print(f"EV {i}")
146 cam_obj.exposure_value = i
147 yield
148 cam_obj.exposure_value = 0
149
150 def nite_modes(cam_obj):
151 print("Night Mode On")
152 cam_obj.night_mode = True
153 print(cam_obj.night_mode)
154 yield
155 print("Night Mode Off")
156 cam_obj.night_mode = False
157 print(cam_obj.night_mode)
158 yield
159
160 def test_modes(cam_obj):
161 print("Test pattern On")
162 cam_obj.test_pattern = True
163 yield
164 print("Test pattern Off")
165 cam_obj.test_pattern = False
166 yield
167
168 while True:
169 yield from test_modes(cam_obj)
170 yield from contrast_modes(cam_obj)
171 yield from effect_modes(cam_obj)
172 yield from saturation_modes(cam_obj)
173 yield from brightness_modes(cam_obj)
174 # These don't work right (yet)
175 # yield from exposure_value_modes(cam_obj) # Issue #8
176 # yield from nite_modes(cam_obj) # Issue #6
177
178
179def main():
180 deadline = 0
181 effects = iter((None,))
182
183 display.auto_refresh = False
184 display_bus.send(42, struct.pack(">hh", 0, bitmap.width - 1))
185 display_bus.send(43, struct.pack(">hh", 0, bitmap.height - 1))
186
187 if test_effects:
188 time_per_effect = 1500
189 deadline = ticks_ms() + time_per_effect
190 effects = special_modes(cam)
191
192 while True:
193 if test_effects:
194 now = ticks_ms()
195 if ticks_less(deadline, now):
196 deadline += time_per_effect
197 next(effects)
198 state.value = True
199 cam.capture(bitmap)
200 state.value = False
201 display_bus.send(44, bitmap)
202
203
204main()
JPEG (internal storage)
Record JPEG images onto internal storage. Requires use of ov5640_jpeg_kaluga1_3_boot.py (below) as boot.py.
examples/ov5640_jpeg_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"""
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 ov5640_jpeg_kaluga1_3_boot.py at CIRCUITPY/boot.py.
14Then, hold the Mode button (button K2 on the audio board) while resetting the
15board to make the internal flash readable by CircuitPython.
16"""
17
18import time
19
20import adafruit_ili9341
21import board
22import busio
23import displayio
24import fourwire
25import microcontroller
26
27import adafruit_ov5640
28
29# Release any resources currently in use for the displays
30displayio.release_displays()
31spi = busio.SPI(MOSI=board.LCD_MOSI, clock=board.LCD_CLK)
32display_bus = fourwire.FourWire(
33 spi,
34 command=board.LCD_D_C,
35 chip_select=board.LCD_CS,
36 reset=board.LCD_RST,
37 baudrate=80_000_000,
38)
39display = adafruit_ili9341.ILI9341(display_bus, width=320, height=240, rotation=90)
40
41try:
42 with open("/boot_out.txt", "ab") as f:
43 pass
44except OSError as e:
45 print(e)
46 print(
47 "A 'read-only filesystem' error occurs if you did not correctly install"
48 "\nov5640_jpeg_kaluga1_3_boot.py as CIRCUITPY/boot.py and reset the"
49 '\nboard while holding the "mode" button'
50 "\n\nThis message is also shown after the board takes a picture and auto-restarts"
51 )
52 raise SystemExit from e
53
54bus = busio.I2C(scl=board.CAMERA_SIOC, sda=board.CAMERA_SIOD)
55cam = adafruit_ov5640.OV5640(
56 bus,
57 data_pins=board.CAMERA_DATA,
58 clock=board.CAMERA_PCLK,
59 vsync=board.CAMERA_VSYNC,
60 href=board.CAMERA_HREF,
61 mclk=board.CAMERA_XCLK,
62 size=adafruit_ov5640.OV5640_SIZE_QSXGA,
63)
64
65cam.colorspace = adafruit_ov5640.OV5640_COLOR_JPEG
66cam.quality = 5
67b = bytearray(cam.capture_buffer_size)
68print(f"Capturing jpeg image of up to {len(b)} bytes")
69jpeg = cam.capture(b)
70
71print(f"Captured {len(jpeg)} bytes of jpeg data")
72try:
73 print(end="Writing to internal storage (this is SLOW)")
74 with open("/cam.jpg", "wb") as f:
75 for i in range(0, len(jpeg), 4096):
76 print(end=".")
77 f.write(jpeg[i : i + 4096])
78 print()
79 print("Wrote to CIRCUITPY/cam.jpg")
80 print("Resetting so computer sees new content of CIRCUITPY")
81 time.sleep(0.5)
82 microcontroller.reset()
83
84except OSError as e:
85 print(e)
86 print(
87 "A 'read-only filesystem' error occurs if you did not correctly install"
88 "\nov5640_jpeg_kaluga1_3_boot.py as CIRCUITPY/boot.py and reset the board"
89 )
Use with above example as boot.py
examples/ov5640_jpeg_kaluga1_3_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"""Use this file as CIRCUITPY/boot.py in conjunction with ov5640_jpeg_kaluga1_3.py
6
7It makes the CIRCUITPY filesystem writable to CircuitPython
8(and read-only to the PC) if 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
21print("measured voltage", a_voltage)
22if abs(a_voltage - V_MODE) < 0.05: # If mode IS pressed...
23 print("storage writable by CircuitPython")
24 storage.remount("/", readonly=False)
JPEG (SD card)
Record JPEG images to an SD card.
examples/ov5640_sdcard_kaluga_1_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"""
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 also requires an SD card breakout wired as follows:
14 * IO18: SD Clock Input
15 * IO17: SD Serial Output (MISO)
16 * IO14: SD Serial Input (MOSI)
17 * IO12: SD Chip Select
18
19Insert a CircuitPython-compatible SD card before powering on the Kaluga.
20Press the "Record" button on the audio daughterboard to take a photo.
21"""
22
23import os
24import time
25
26import adafruit_ili9341
27import analogio
28import board
29import busio
30import displayio
31import fourwire
32import neopixel
33import sdcardio
34import storage
35
36import adafruit_ov5640
37
38# Release any resources currently in use for the displays
39displayio.release_displays()
40spi = busio.SPI(MOSI=board.LCD_MOSI, clock=board.LCD_CLK)
41display_bus = fourwire.FourWire(
42 spi,
43 command=board.LCD_D_C,
44 chip_select=board.LCD_CS,
45 reset=board.LCD_RST,
46 baudrate=80_000_000,
47)
48display = adafruit_ili9341.ILI9341(display_bus, width=320, height=240, rotation=90)
49
50V_MODE = 1.98
51V_RECORD = 2.41
52
53a = analogio.AnalogIn(board.IO6)
54
55pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.3, auto_write=False)
56
57bus = busio.I2C(scl=board.CAMERA_SIOC, sda=board.CAMERA_SIOD)
58cam = adafruit_ov5640.OV5640(
59 bus,
60 data_pins=board.CAMERA_DATA,
61 clock=board.CAMERA_PCLK,
62 vsync=board.CAMERA_VSYNC,
63 href=board.CAMERA_HREF,
64 mclk=board.CAMERA_XCLK,
65 size=adafruit_ov5640.OV5640_SIZE_QSXGA,
66)
67
68sd_spi = busio.SPI(clock=board.IO18, MOSI=board.IO14, MISO=board.IO17)
69sd_cs = board.IO12
70sdcard = sdcardio.SDCard(sd_spi, sd_cs)
71vfs = storage.VfsFat(sdcard)
72storage.mount(vfs, "/sd")
73
74
75def exists(filename):
76 try:
77 os.stat(filename)
78 return True
79 except OSError as _:
80 return False
81
82
83class ImageCounter:
84 def __init__(self):
85 self.count = 0
86
87 def get_next(self):
88 while True:
89 filename = f"/sd/img{self.count:04d}.jpg"
90 self.count += 1
91 if exists(filename):
92 continue
93 print("# writing to", filename)
94 return open(filename, "wb")
95
96
97_image_counter = ImageCounter()
98
99
100def open_next_image():
101 return _image_counter.get_next()
102
103
104cam.colorspace = adafruit_ov5640.OV5640_COLOR_JPEG
105cam.quality = 7
106b = bytearray(cam.capture_buffer_size)
107
108print("Press 'record' button to take a JPEG image")
109while True:
110 pixel[0] = 0x0000FF
111 pixel.write()
112 a_voltage = a.value * a.reference_voltage / 65535
113 record_pressed = abs(a_voltage - V_RECORD) < 0.05
114 if record_pressed:
115 pixel[0] = 0xFF0000
116 pixel.write()
117 time.sleep(0.01)
118 jpeg = cam.capture(b)
119 print(
120 f"Captured {len(jpeg)} bytes of jpeg data"
121 f" (had allocated {cam.capture_buffer_size} bytes"
122 )
123 print(f"Resolution {cam.width}x{cam.height}")
124 try:
125 pixel[0] = 0x00FF00
126 pixel.write()
127 with open_next_image() as f:
128 f.write(jpeg)
129 print("# Wrote image")
130 pixel[0] = 0x000000
131 pixel.write()
132 except OSError as e:
133 print(e)
134 while record_pressed:
135 a_voltage = a.value * a.reference_voltage / 65535
136 record_pressed = abs(a_voltage - V_RECORD) < 0.05
GIF (SD card)
Record stop-motion GIF images to an SD card.
examples/ov5640_stopmotion_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"""
7Take a 10-frame stop motion GIF image.
8
9This example requires:
10 * `Espressif Kaluga v1.3 <https://www.adafruit.com/product/4729>`_ with compatible LCD display
11 * `MicroSD card breakout board + <https://www.adafruit.com/product/254>`_ connected as follows:
12 * CLK to board.IO18
13 * DI to board.IO14
14 * DO to board.IO17
15 * CS to IO12
16 * GND to GND
17 * 5V to 5V
18 * A compatible SD card inserted in the SD card slot
19 * A compatible OV5640 camera module connected to the camera header
20
21To use:
22
23Insert an SD card and power on.
24
25Set up the first frame using the viewfinder. Click the REC button to take a frame.
26
27Set up the next frame using the viewfinder. The previous and current frames are
28blended together on the display, which is called an "onionskin". Click the REC
29button to take the next frame.
30
31After 10 frames are recorded, the GIF is complete and you can begin recording another.
32
33
34About the Kaluga development kit:
35
36The Kaluga development kit comes in two versions (v1.2 and v1.3); this demo is
37tested on v1.3.
38
39The audio board must be mounted between the Kaluga and the LCD, it provides the
40I2C pull-ups(!)
41
42The v1.3 development kit's LCD can have one of two chips, the ili9341 or
43st7789. Furthermore, there are at least 2 ILI9341 variants, which differ
44by rotation. This example is written for one if the ILI9341 variants,
45the one which usually uses rotation=90 to get a landscape display.
46"""
47
48import os
49import struct
50
51import analogio
52import bitmaptools
53import board
54import busdisplay
55import busio
56import displayio
57import fourwire
58import gifio
59import sdcardio
60import storage
61
62import adafruit_ov5640
63
64V_RECORD = int(2.41 * 65536 / 3.3)
65V_FUZZ = 2000
66
67a = analogio.AnalogIn(board.IO6)
68
69
70def record_pressed():
71 value = a.value
72 return abs(value - V_RECORD) < V_FUZZ
73
74
75displayio.release_displays()
76spi = busio.SPI(MOSI=board.LCD_MOSI, clock=board.LCD_CLK)
77display_bus = fourwire.FourWire(
78 spi,
79 command=board.LCD_D_C,
80 chip_select=board.LCD_CS,
81 reset=board.LCD_RST,
82 baudrate=80_000_000,
83)
84_INIT_SEQUENCE = (
85 b"\x01\x80\x80" # Software reset then delay 0x80 (128ms)
86 b"\xef\x03\x03\x80\x02"
87 b"\xcf\x03\x00\xc1\x30"
88 b"\xed\x04\x64\x03\x12\x81"
89 b"\xe8\x03\x85\x00\x78"
90 b"\xcb\x05\x39\x2c\x00\x34\x02"
91 b"\xf7\x01\x20"
92 b"\xea\x02\x00\x00"
93 b"\xc0\x01\x23" # Power control VRH[5:0]
94 b"\xc1\x01\x10" # Power control SAP[2:0];BT[3:0]
95 b"\xc5\x02\x3e\x28" # VCM control
96 b"\xc7\x01\x86" # VCM control2
97 b"\x36\x01\x40" # Memory Access Control
98 b"\x37\x01\x00" # Vertical scroll zero
99 b"\x3a\x01\x55" # COLMOD: Pixel Format Set
100 b"\xb1\x02\x00\x18" # Frame Rate Control (In Normal Mode/Full Colors)
101 b"\xb6\x03\x08\x82\x27" # Display Function Control
102 b"\xf2\x01\x00" # 3Gamma Function Disable
103 b"\x26\x01\x01" # Gamma curve selected
104 b"\xe0\x0f\x0f\x31\x2b\x0c\x0e\x08\x4e\xf1\x37\x07\x10\x03\x0e\x09\x00" # Set Gamma
105 b"\xe1\x0f\x00\x0e\x14\x03\x11\x07\x31\xc1\x48\x08\x0f\x0c\x31\x36\x0f" # Set Gamma
106 b"\x11\x80\x78" # Exit Sleep then delay 0x78 (120ms)
107 b"\x29\x80\x78" # Display on then delay 0x78 (120ms)
108)
109
110display = busdisplay.BusDisplay(display_bus, _INIT_SEQUENCE, width=320, height=240)
111
112sd_spi = busio.SPI(clock=board.IO18, MOSI=board.IO14, MISO=board.IO17)
113sd_cs = board.IO12
114sdcard = sdcardio.SDCard(sd_spi, sd_cs, baudrate=24_000_000)
115vfs = storage.VfsFat(sdcard)
116storage.mount(vfs, "/sd")
117
118bus = busio.I2C(scl=board.CAMERA_SIOC, sda=board.CAMERA_SIOD)
119cam = adafruit_ov5640.OV5640(
120 bus,
121 data_pins=board.CAMERA_DATA,
122 clock=board.CAMERA_PCLK,
123 vsync=board.CAMERA_VSYNC,
124 href=board.CAMERA_HREF,
125 mclk=board.CAMERA_XCLK,
126 size=adafruit_ov5640.OV5640_SIZE_240X240,
127)
128
129
130def exists(filename):
131 try:
132 os.stat(filename)
133 return True
134 except OSError as _:
135 return False
136
137
138class ImageCounter:
139 def __init__(self):
140 self.count = 0
141
142 def next_filename(self, extension="jpg"):
143 while True:
144 filename = f"/sd/img{self.count:04d}.{extension}"
145 if exists(filename):
146 print(f"File exists: {filename}", end="\r")
147 self.count += 1
148 continue
149 print()
150 return filename
151
152
153_image_counter = ImageCounter()
154
155
156def next_filename(extension="jpg"):
157 return _image_counter.next_filename(extension)
158
159
160# Pre-cache the next image number
161next_filename("gif")
162
163# Blank the whole display, we'll draw what we want with directio
164empty_group = displayio.Group()
165display.root_group = empty_group
166display.auto_refresh = False
167display.refresh()
168
169
170def open_next_image(extension="jpg"):
171 while True:
172 filename = next_filename(extension)
173 print("# writing to", filename)
174 return open(filename, "wb")
175
176
177cam.flip_x = False
178cam.flip_y = False
179chip_id = cam.chip_id
180print(f"Detected 0x{chip_id:x}")
181cam.test_pattern = False
182cam.effect = adafruit_ov5640.OV5640_SPECIAL_EFFECT_NONE
183cam.saturation = 3
184
185# Alternately recording to these two bitmaps
186rec1 = displayio.Bitmap(cam.width, cam.height, 65536)
187rec2 = displayio.Bitmap(cam.width, cam.height, 65536)
188# Prior frame kept here
189old_frame = displayio.Bitmap(cam.width, cam.height, 65536)
190# Displayed (onion skinned) frame here
191onionskin = displayio.Bitmap(cam.width, cam.height, 65536)
192
193ow = (display.width - onionskin.width) // 2
194oh = (display.height - onionskin.height) // 2
195display_bus.send(42, struct.pack(">hh", ow, onionskin.width + ow - 1))
196display_bus.send(43, struct.pack(">hh", oh, onionskin.height + ow - 1))
197
198
199class ContinuousCapture:
200 def __init__(self, camera, buffer1, buffer2):
201 camera = getattr(camera, "_imagecapture", camera)
202 self._camera = camera
203 print("buffer1", buffer1)
204 print("buffer2", buffer2)
205 camera.continuous_capture_start(buffer1, buffer2)
206
207 def __exit__(self, exc_type, exc_val, exc_tb):
208 self._camera.continuous_capture_stop()
209
210 def __enter__(self):
211 return self
212
213 def get_frame(self):
214 return self._camera.continuous_capture_get_frame()
215
216 __next__ = get_frame
217
218
219def wait_record_pressed_update_display(first_frame, cap):
220 while record_pressed():
221 pass
222 while True:
223 frame = cap.get_frame()
224 if record_pressed():
225 return frame
226
227 if first_frame:
228 # First frame -- display as-is
229 display_bus.send(44, frame)
230 else:
231 bitmaptools.alphablend(onionskin, old_frame, frame, displayio.Colorspace.RGB565_SWAPPED)
232 display_bus.send(44, onionskin)
233
234
235def take_stop_motion_gif(n_frames=10, replay_frame_time=0.3):
236 print(f"0/{n_frames}")
237 with ContinuousCapture(cam, rec1, rec2) as cap:
238 frame = wait_record_pressed_update_display(True, cap)
239 with open_next_image("gif") as f, gifio.GifWriter(
240 f, cam.width, cam.height, displayio.Colorspace.RGB565_SWAPPED, dither=True
241 ) as g:
242 g.add_frame(frame, replay_frame_time)
243 for i in range(1, n_frames):
244 print(f"{i}/{n_frames}")
245
246 # CircuitPython Versions <= 8.2.0
247 if hasattr(old_frame, "blit"):
248 old_frame.blit(0, 0, frame, x1=0, y1=0, x2=frame.width, y2=frame.height)
249
250 # CircuitPython Versions >= 9.0.0
251 else:
252 bitmaptools.blit(
253 old_frame,
254 frame,
255 0,
256 0,
257 x1=0,
258 y1=0,
259 x2=frame.width,
260 y2=frame.height,
261 )
262
263 frame = wait_record_pressed_update_display(False, cap)
264 g.add_frame(frame, replay_frame_time)
265 print("done")
266
267
268est_frame_size = cam.width * cam.height * 128 // 126 + 1
269est_hdr_size = 1000
270
271dither = True
272while True:
273 take_stop_motion_gif()