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_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
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 busio
23import digitalio
24import displayio
25from adafruit_ticks import ticks_ms, ticks_less
26import adafruit_ov5640
27
28# Set to True to enable the various effects & exposure modes to be tested
29test_effects = False
30
31# Release any resources currently in use for the displays
32displayio.release_displays()
33
34state = digitalio.DigitalInOut(board.IO4)
35state.switch_to_output(True)
36
37spi = busio.SPI(MOSI=board.LCD_MOSI, clock=board.LCD_CLK)
38display_bus = displayio.FourWire(
39 spi,
40 command=board.LCD_D_C,
41 chip_select=board.LCD_CS,
42 reset=board.LCD_RST,
43 baudrate=80_000_000,
44)
45_INIT_SEQUENCE = (
46 b"\x01\x80\x80" # Software reset then delay 0x80 (128ms)
47 b"\xEF\x03\x03\x80\x02"
48 b"\xCF\x03\x00\xC1\x30"
49 b"\xED\x04\x64\x03\x12\x81"
50 b"\xE8\x03\x85\x00\x78"
51 b"\xCB\x05\x39\x2C\x00\x34\x02"
52 b"\xF7\x01\x20"
53 b"\xEA\x02\x00\x00"
54 b"\xc0\x01\x23" # Power control VRH[5:0]
55 b"\xc1\x01\x10" # Power control SAP[2:0];BT[3:0]
56 b"\xc5\x02\x3e\x28" # VCM control
57 b"\xc7\x01\x86" # VCM control2
58 b"\x36\x01\x40" # Memory Access Control
59 b"\x37\x01\x00" # Vertical scroll zero
60 b"\x3a\x01\x55" # COLMOD: Pixel Format Set
61 b"\xb1\x02\x00\x18" # Frame Rate Control (In Normal Mode/Full Colors)
62 b"\xb6\x03\x08\x82\x27" # Display Function Control
63 b"\xF2\x01\x00" # 3Gamma Function Disable
64 b"\x26\x01\x01" # Gamma curve selected
65 b"\xe0\x0f\x0F\x31\x2B\x0C\x0E\x08\x4E\xF1\x37\x07\x10\x03\x0E\x09\x00" # Set Gamma
66 b"\xe1\x0f\x00\x0E\x14\x03\x11\x07\x31\xC1\x48\x08\x0F\x0C\x31\x36\x0F" # Set Gamma
67 b"\x11\x80\x78" # Exit Sleep then delay 0x78 (120ms)
68 b"\x29\x80\x78" # Display on then delay 0x78 (120ms)
69)
70
71display = displayio.Display(display_bus, _INIT_SEQUENCE, width=320, height=240)
72
73bus = busio.I2C(scl=board.CAMERA_SIOC, sda=board.CAMERA_SIOD)
74cam = adafruit_ov5640.OV5640(
75 bus,
76 data_pins=board.CAMERA_DATA,
77 clock=board.CAMERA_PCLK,
78 vsync=board.CAMERA_VSYNC,
79 href=board.CAMERA_HREF,
80 mclk=board.CAMERA_XCLK,
81 size=adafruit_ov5640.OV5640_SIZE_QVGA,
82)
83
84cam.flip_x = False
85cam.flip_y = False
86chip_id = cam.chip_id
87print(f"Detected 0x{chip_id:x}")
88cam.test_pattern = False
89cam.effect = adafruit_ov5640.OV5640_SPECIAL_EFFECT_NONE
90cam.saturation = 3
91bitmap = displayio.Bitmap(cam.width, cam.height, 65536)
92print(len(memoryview(bitmap)))
93display.auto_refresh = False
94
95
96def special_modes(cam_obj):
97 def effect_modes(cam_obj):
98 for i in [
99 "NONE",
100 "NEGATIVE",
101 "GRAYSCALE",
102 "RED_TINT",
103 "GREEN_TINT",
104 "BLUE_TINT",
105 "SEPIA",
106 ]:
107 print(f"Effect {i}")
108 cam_obj.effect = getattr(adafruit_ov5640, f"OV5640_SPECIAL_EFFECT_{i}")
109 yield
110 cam_obj.effect = adafruit_ov5640.OV5640_SPECIAL_EFFECT_NONE
111
112 def saturation_modes(cam_obj):
113 for i in range(-4, 5):
114 print(f"Saturation {i}")
115 cam_obj.saturation = i
116 yield
117 cam_obj.saturation = 0
118
119 def brightness_modes(cam_obj):
120 for i in range(-4, 5):
121 print(f"Brightness {i}")
122 cam_obj.brightness = i
123 yield
124 cam_obj.brightness = 0
125
126 def contrast_modes(cam_obj):
127 for i in range(-3, 4):
128 print(f"Contrast {i}")
129 cam_obj.contrast = i
130 yield
131 cam_obj.contrast = 0
132
133 def white_balance_modes(cam_obj): # pylint: disable=unused-variable
134 for i in ["AUTO", "SUNNY", "FLUORESCENT", "CLOUDY", "INCANDESCENT"]:
135 print(f"White Balance {i}")
136 cam_obj.white_balance = getattr(
137 adafruit_ov5640, f"OV5640_WHITE_BALANCE_{i}"
138 )
139 yield
140 cam_obj.white_balance = adafruit_ov5640.OV5640_WHITE_BALANCE_AUTO
141
142 def exposure_value_modes(cam_obj): # pylint: disable=unused-variable
143 for i in range(-3, 4):
144 print(f"EV {i}")
145 cam_obj.exposure_value = i
146 yield
147 cam_obj.exposure_value = 0
148
149 def nite_modes(cam_obj): # pylint: disable=unused-variable
150 print("Night Mode On")
151 cam_obj.night_mode = True
152 print(cam_obj.night_mode)
153 yield
154 print("Night Mode Off")
155 cam_obj.night_mode = False
156 print(cam_obj.night_mode)
157 yield
158
159 def test_modes(cam_obj):
160 print("Test pattern On")
161 cam_obj.test_pattern = True
162 yield
163 print("Test pattern Off")
164 cam_obj.test_pattern = False
165 yield
166
167 while True:
168 yield from test_modes(cam_obj)
169 yield from contrast_modes(cam_obj)
170 yield from effect_modes(cam_obj)
171 yield from saturation_modes(cam_obj)
172 yield from brightness_modes(cam_obj)
173 # These don't work right (yet)
174 # yield from exposure_value_modes(cam_obj) # Issue #8
175 # yield from nite_modes(cam_obj) # Issue #6
176
177
178def main():
179 deadline = 0
180 effects = iter((None,))
181
182 display.auto_refresh = False
183 display_bus.send(42, struct.pack(">hh", 0, bitmap.width - 1))
184 display_bus.send(43, struct.pack(">hh", 0, bitmap.height - 1))
185
186 if test_effects:
187 time_per_effect = 1500
188 deadline = ticks_ms() + time_per_effect
189 effects = special_modes(cam)
190
191 while True:
192 if test_effects:
193 now = ticks_ms()
194 if ticks_less(deadline, now):
195 deadline += time_per_effect
196 next(effects)
197 state.value = True
198 cam.capture(bitmap)
199 state.value = False
200 display_bus.send(44, bitmap)
201
202
203main()
JPEG (internal storage)¶
Record JPEG images onto internal storage. Requires use of ov5640_jpeg_kaluga1_3_boot.py (below) 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 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 board
21import busio
22import displayio
23import microcontroller
24
25import adafruit_ili9341
26import adafruit_ov5640
27
28# Release any resources currently in use for the displays
29displayio.release_displays()
30spi = busio.SPI(MOSI=board.LCD_MOSI, clock=board.LCD_CLK)
31display_bus = displayio.FourWire(
32 spi,
33 command=board.LCD_D_C,
34 chip_select=board.LCD_CS,
35 reset=board.LCD_RST,
36 baudrate=80_000_000,
37)
38display = adafruit_ili9341.ILI9341(display_bus, width=320, height=240, rotation=90)
39
40try:
41 with open("/boot_out.txt", "ab") as f:
42 pass
43except OSError as e:
44 print(e)
45 print(
46 "A 'read-only filesystem' error occurs if you did not correctly install"
47 "\nov5640_jpeg_kaluga1_3_boot.py as CIRCUITPY/boot.py and reset the"
48 '\nboard while holding the "mode" button'
49 "\n\nThis message is also shown after the board takes a picture and auto-restarts"
50 )
51 raise SystemExit from e
52
53bus = busio.I2C(scl=board.CAMERA_SIOC, sda=board.CAMERA_SIOD)
54cam = adafruit_ov5640.OV5640(
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 size=adafruit_ov5640.OV5640_SIZE_QSXGA,
62)
63
64cam.colorspace = adafruit_ov5640.OV5640_COLOR_JPEG
65cam.quality = 5
66b = bytearray(cam.capture_buffer_size)
67print(f"Capturing jpeg image of up to {len(b)} bytes")
68jpeg = cam.capture(b)
69
70print(f"Captured {len(jpeg)} bytes of jpeg data")
71try:
72 print(end="Writing to internal storage (this is SLOW)")
73 with open("/cam.jpg", "wb") as f:
74 for i in range(0, len(jpeg), 4096):
75 print(end=".")
76 f.write(jpeg[i : i + 4096])
77 print()
78 print("Wrote to CIRCUITPY/cam.jpg")
79 print("Resetting so computer sees new content of CIRCUITPY")
80 time.sleep(0.5)
81 microcontroller.reset() # pylint: disable=no-member
82
83except OSError as e:
84 print(e)
85 print(
86 "A 'read-only filesystem' error occurs if you did not correctly install"
87 "\nov5640_jpeg_kaluga1_3_boot.py as CIRCUITPY/boot.py and reset the board"
88 )
Use with above example 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"""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 # pylint: disable=no-member
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.
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 analogio
27import board
28import busio
29import displayio
30import neopixel
31import sdcardio
32import storage
33
34import adafruit_ili9341
35import adafruit_ov5640
36
37# Release any resources currently in use for the displays
38displayio.release_displays()
39spi = busio.SPI(MOSI=board.LCD_MOSI, clock=board.LCD_CLK)
40display_bus = displayio.FourWire(
41 spi,
42 command=board.LCD_D_C,
43 chip_select=board.LCD_CS,
44 reset=board.LCD_RST,
45 baudrate=80_000_000,
46)
47display = adafruit_ili9341.ILI9341(display_bus, width=320, height=240, rotation=90)
48
49V_MODE = 1.98
50V_RECORD = 2.41
51
52a = analogio.AnalogIn(board.IO6)
53
54pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.3, auto_write=False)
55
56bus = busio.I2C(scl=board.CAMERA_SIOC, sda=board.CAMERA_SIOD)
57cam = adafruit_ov5640.OV5640(
58 bus,
59 data_pins=board.CAMERA_DATA,
60 clock=board.CAMERA_PCLK,
61 vsync=board.CAMERA_VSYNC,
62 href=board.CAMERA_HREF,
63 mclk=board.CAMERA_XCLK,
64 size=adafruit_ov5640.OV5640_SIZE_QSXGA,
65)
66
67sd_spi = busio.SPI(clock=board.IO18, MOSI=board.IO14, MISO=board.IO17)
68sd_cs = board.IO12
69sdcard = sdcardio.SDCard(sd_spi, sd_cs)
70vfs = storage.VfsFat(sdcard)
71storage.mount(vfs, "/sd")
72
73
74def exists(filename):
75 try:
76 os.stat(filename)
77 return True
78 except OSError as _:
79 return False
80
81
82_image_counter = 0
83
84
85def open_next_image():
86 global _image_counter # pylint: disable=global-statement
87 while True:
88 filename = f"/sd/img{_image_counter:04d}.jpg"
89 _image_counter += 1
90 if exists(filename):
91 continue
92 print("# writing to", filename)
93 return open(filename, "wb")
94
95
96cam.colorspace = adafruit_ov5640.OV5640_COLOR_JPEG
97cam.quality = 7
98b = bytearray(cam.capture_buffer_size)
99
100print("Press 'record' button to take a JPEG image")
101while True:
102 pixel[0] = 0x0000FF
103 pixel.write()
104 a_voltage = a.value * a.reference_voltage / 65535 # pylint: disable=no-member
105 record_pressed = abs(a_voltage - V_RECORD) < 0.05
106 if record_pressed:
107 pixel[0] = 0xFF0000
108 pixel.write()
109 time.sleep(0.01)
110 jpeg = cam.capture(b)
111 print(
112 f"Captured {len(jpeg)} bytes of jpeg data"
113 f" (had allocated {cam.capture_buffer_size} bytes"
114 )
115 print(f"Resolution {cam.width}x{cam.height}")
116 try:
117 pixel[0] = 0x00FF00
118 pixel.write()
119 with open_next_image() as f:
120 f.write(jpeg)
121 print("# Wrote image")
122 pixel[0] = 0x000000
123 pixel.write()
124 except OSError as e:
125 print(e)
126 while record_pressed:
127 a_voltage = (
128 a.value * a.reference_voltage / 65535
129 ) # pylint: disable=no-member
130 record_pressed = abs(a_voltage - V_RECORD) < 0.05
GIF (SD card)¶
Record stop-motion GIF images to an SD card.
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 busio
55import displayio
56import gifio
57import sdcardio
58import storage
59
60import adafruit_ov5640
61
62V_RECORD = int(2.41 * 65536 / 3.3)
63V_FUZZ = 2000
64
65a = analogio.AnalogIn(board.IO6)
66
67
68def record_pressed():
69 value = a.value
70 return abs(value - V_RECORD) < V_FUZZ
71
72
73displayio.release_displays()
74spi = busio.SPI(MOSI=board.LCD_MOSI, clock=board.LCD_CLK)
75display_bus = displayio.FourWire(
76 spi,
77 command=board.LCD_D_C,
78 chip_select=board.LCD_CS,
79 reset=board.LCD_RST,
80 baudrate=80_000_000,
81)
82_INIT_SEQUENCE = (
83 b"\x01\x80\x80" # Software reset then delay 0x80 (128ms)
84 b"\xEF\x03\x03\x80\x02"
85 b"\xCF\x03\x00\xC1\x30"
86 b"\xED\x04\x64\x03\x12\x81"
87 b"\xE8\x03\x85\x00\x78"
88 b"\xCB\x05\x39\x2C\x00\x34\x02"
89 b"\xF7\x01\x20"
90 b"\xEA\x02\x00\x00"
91 b"\xc0\x01\x23" # Power control VRH[5:0]
92 b"\xc1\x01\x10" # Power control SAP[2:0];BT[3:0]
93 b"\xc5\x02\x3e\x28" # VCM control
94 b"\xc7\x01\x86" # VCM control2
95 b"\x36\x01\x40" # Memory Access Control
96 b"\x37\x01\x00" # Vertical scroll zero
97 b"\x3a\x01\x55" # COLMOD: Pixel Format Set
98 b"\xb1\x02\x00\x18" # Frame Rate Control (In Normal Mode/Full Colors)
99 b"\xb6\x03\x08\x82\x27" # Display Function Control
100 b"\xF2\x01\x00" # 3Gamma Function Disable
101 b"\x26\x01\x01" # Gamma curve selected
102 b"\xe0\x0f\x0F\x31\x2B\x0C\x0E\x08\x4E\xF1\x37\x07\x10\x03\x0E\x09\x00" # Set Gamma
103 b"\xe1\x0f\x00\x0E\x14\x03\x11\x07\x31\xC1\x48\x08\x0F\x0C\x31\x36\x0F" # Set Gamma
104 b"\x11\x80\x78" # Exit Sleep then delay 0x78 (120ms)
105 b"\x29\x80\x78" # Display on then delay 0x78 (120ms)
106)
107
108display = displayio.Display(display_bus, _INIT_SEQUENCE, width=320, height=240)
109
110sd_spi = busio.SPI(clock=board.IO18, MOSI=board.IO14, MISO=board.IO17)
111sd_cs = board.IO12
112sdcard = sdcardio.SDCard(sd_spi, sd_cs, baudrate=24_000_000)
113vfs = storage.VfsFat(sdcard)
114storage.mount(vfs, "/sd")
115
116bus = busio.I2C(scl=board.CAMERA_SIOC, sda=board.CAMERA_SIOD)
117cam = adafruit_ov5640.OV5640(
118 bus,
119 data_pins=board.CAMERA_DATA,
120 clock=board.CAMERA_PCLK,
121 vsync=board.CAMERA_VSYNC,
122 href=board.CAMERA_HREF,
123 mclk=board.CAMERA_XCLK,
124 size=adafruit_ov5640.OV5640_SIZE_240X240,
125)
126
127
128def exists(filename):
129 try:
130 os.stat(filename)
131 return True
132 except OSError as _:
133 return False
134
135
136_image_counter = 0
137
138
139def next_filename(extension="jpg"):
140 global _image_counter # pylint: disable=global-statement
141 while True:
142 filename = f"/sd/img{_image_counter:04d}.{extension}"
143 if exists(filename):
144 print(f"File exists: {filename}", end="\r")
145 _image_counter += 1
146 continue
147 print()
148 return filename
149
150
151# Pre-cache the next image number
152next_filename("gif")
153
154# Blank the whole display, we'll draw what we want with directio
155empty_group = displayio.Group()
156display.root_group = empty_group
157display.auto_refresh = False
158display.refresh()
159
160
161def open_next_image(extension="jpg"):
162 while True:
163 filename = next_filename(extension)
164 print("# writing to", filename)
165 return open(filename, "wb")
166
167
168cam.flip_x = False
169cam.flip_y = False
170chip_id = cam.chip_id
171print(f"Detected 0x{chip_id:x}")
172cam.test_pattern = False
173cam.effect = adafruit_ov5640.OV5640_SPECIAL_EFFECT_NONE
174cam.saturation = 3
175
176# Alternately recording to these two bitmaps
177rec1 = displayio.Bitmap(cam.width, cam.height, 65536)
178rec2 = displayio.Bitmap(cam.width, cam.height, 65536)
179# Prior frame kept here
180old_frame = displayio.Bitmap(cam.width, cam.height, 65536)
181# Displayed (onion skinned) frame here
182onionskin = displayio.Bitmap(cam.width, cam.height, 65536)
183
184ow = (display.width - onionskin.width) // 2
185oh = (display.height - onionskin.height) // 2
186display_bus.send(42, struct.pack(">hh", ow, onionskin.width + ow - 1))
187display_bus.send(43, struct.pack(">hh", oh, onionskin.height + ow - 1))
188
189
190class ContinuousCapture:
191 def __init__(self, camera, buffer1, buffer2):
192 camera = getattr(camera, "_imagecapture", camera)
193 self._camera = camera
194 print("buffer1", buffer1)
195 print("buffer2", buffer2)
196 camera.continuous_capture_start(buffer1, buffer2)
197
198 def __exit__(self, exc_type, exc_val, exc_tb):
199 self._camera.continuous_capture_stop()
200
201 def __enter__(self):
202 return self
203
204 def get_frame(self):
205 return self._camera.continuous_capture_get_frame()
206
207 __next__ = get_frame
208
209
210def wait_record_pressed_update_display(first_frame, cap):
211 while record_pressed():
212 pass
213 while True:
214 frame = cap.get_frame()
215 if record_pressed():
216 return frame
217
218 if first_frame:
219 # First frame -- display as-is
220 display_bus.send(44, frame)
221 else:
222 bitmaptools.alphablend(
223 onionskin, old_frame, frame, displayio.Colorspace.RGB565_SWAPPED
224 )
225 display_bus.send(44, onionskin)
226
227
228def take_stop_motion_gif(n_frames=10, replay_frame_time=0.3):
229 print(f"0/{n_frames}")
230 with ContinuousCapture(cam, rec1, rec2) as cap:
231 frame = wait_record_pressed_update_display(True, cap)
232 with open_next_image("gif") as f, gifio.GifWriter(
233 f, cam.width, cam.height, displayio.Colorspace.RGB565_SWAPPED, dither=True
234 ) as g:
235 g.add_frame(frame, replay_frame_time)
236 for i in range(1, n_frames):
237 print(f"{i}/{n_frames}")
238
239 # CircuitPython Versions <= 8.2.0
240 if hasattr(old_frame, "blit"):
241 old_frame.blit(
242 0, 0, frame, x1=0, y1=0, x2=frame.width, y2=frame.height
243 )
244
245 # CircuitPython Versions >= 9.0.0
246 else:
247 bitmaptools.blit(
248 old_frame,
249 frame,
250 0,
251 0,
252 x1=0,
253 y1=0,
254 x2=frame.width,
255 y2=frame.height,
256 )
257
258 frame = wait_record_pressed_update_display(False, cap)
259 g.add_frame(frame, replay_frame_time)
260 print("done")
261
262
263est_frame_size = cam.width * cam.height * 128 // 126 + 1
264est_hdr_size = 1000
265
266dither = True
267while True:
268 take_stop_motion_gif()