From f2d4416e097635734cd0f88c6224cf2574d46295 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 30 Jul 2021 10:05:31 -0500 Subject: [PATCH 1/9] Add 'exposure' property --- adafruit_ov2640.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/adafruit_ov2640.py b/adafruit_ov2640.py index 87d556a..13279fa 100644 --- a/adafruit_ov2640.py +++ b/adafruit_ov2640.py @@ -1334,3 +1334,22 @@ def _set_window( # Reestablish test pattern if self._test_pattern: self.test_pattern = self._test_pattern + + @property + def exposure(self): + """The exposure level of the sensor""" + aec_9_2 = self._get_reg_bits(_BANK_SENSOR, _AEC, 0, 0xFF) + aec_15_10 = self._get_reg_bits(_BANK_SENSOR, _REG45, 0, 0b111111) + aec_1_0 = self._get_reg_bits(_BANK_SENSOR, _REG04, 0, 0b11) + + return aec_1_0 | (aec_9_2 << 2) | (aec_15_10 << 10) + + @exposure.setter + def exposure(self, exposure): + aec_1_0 = exposure & 0x11 + aec_9_2 = (exposure >> 2) & 0b11111111 + aec_15_10 = exposure >> 10 + + self._set_reg_bits(_BANK_SENSOR, _AEC, 0, 0xFF, aec_9_2) + self._set_reg_bits(_BANK_SENSOR, _REG45, 0, 0b111111, aec_15_10) + self._set_reg_bits(_BANK_SENSOR, _REG04, 0, 0b11, aec_1_0) From 756ac6b4afeb38411eec42ff67dccdbd420a770b Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 30 Jul 2021 10:06:15 -0500 Subject: [PATCH 2/9] Use BGR565 colorspace This was not originally defined in CircuitPython. Doing this fixes a color cast that was incorrectly applied to the images. --- examples/ov2640_displayio_kaluga1_3_ili9341.py | 2 +- examples/ov2640_displayio_kaluga1_3_st7789.py | 2 +- examples/ov2640_displayio_pico_st7789_2in.py | 2 +- examples/ov2640_jpeg_sd_kaluga1_3.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/ov2640_displayio_kaluga1_3_ili9341.py b/examples/ov2640_displayio_kaluga1_3_ili9341.py index 41d4170..d39f8a5 100644 --- a/examples/ov2640_displayio_kaluga1_3_ili9341.py +++ b/examples/ov2640_displayio_kaluga1_3_ili9341.py @@ -58,7 +58,7 @@ tg = displayio.TileGrid( bitmap, pixel_shader=displayio.ColorConverter( - input_colorspace=displayio.Colorspace.RGB565_SWAPPED + input_colorspace=displayio.Colorspace.BGR565_SWAPPED ), ) g.append(tg) diff --git a/examples/ov2640_displayio_kaluga1_3_st7789.py b/examples/ov2640_displayio_kaluga1_3_st7789.py index 5c407e4..a894ffd 100644 --- a/examples/ov2640_displayio_kaluga1_3_st7789.py +++ b/examples/ov2640_displayio_kaluga1_3_st7789.py @@ -61,7 +61,7 @@ tg = displayio.TileGrid( bitmap, pixel_shader=displayio.ColorConverter( - input_colorspace=displayio.Colorspace.RGB565_SWAPPED + input_colorspace=displayio.Colorspace.BGR565_SWAPPED ), ) g.append(tg) diff --git a/examples/ov2640_displayio_pico_st7789_2in.py b/examples/ov2640_displayio_pico_st7789_2in.py index f0ca8d6..c4ed04f 100644 --- a/examples/ov2640_displayio_pico_st7789_2in.py +++ b/examples/ov2640_displayio_pico_st7789_2in.py @@ -73,7 +73,7 @@ g = Group(scale=1, x=(width - cam.width) // 2, y=(height - cam.height) // 2) tg = TileGrid( - bitmap, pixel_shader=ColorConverter(input_colorspace=Colorspace.RGB565_SWAPPED) + bitmap, pixel_shader=ColorConverter(input_colorspace=Colorspace.BGR565_SWAPPED) ) g.append(tg) display.show(g) diff --git a/examples/ov2640_jpeg_sd_kaluga1_3.py b/examples/ov2640_jpeg_sd_kaluga1_3.py index 5e449fc..93dcd53 100644 --- a/examples/ov2640_jpeg_sd_kaluga1_3.py +++ b/examples/ov2640_jpeg_sd_kaluga1_3.py @@ -76,7 +76,7 @@ tg = displayio.TileGrid( bitmap, pixel_shader=displayio.ColorConverter( - input_colorspace=displayio.Colorspace.RGB565_SWAPPED + input_colorspace=displayio.Colorspace.BGR565_SWAPPED ), ) g.append(tg) From 2c300b0c203ff77afba8ad113bab068994b748fc Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 30 Jul 2021 10:08:55 -0500 Subject: [PATCH 3/9] Add a "direct LCD access" example The "direct LCD access" example shows how to copy the image directly to an LCD of the right resolution. It achieves a somewhat higher framerate (4.1fps vs 2.5fps) but is not compatible with displayio. --- examples/ov2640_directio_kaluga1_3_ili9341.py | 176 ++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 examples/ov2640_directio_kaluga1_3_ili9341.py diff --git a/examples/ov2640_directio_kaluga1_3_ili9341.py b/examples/ov2640_directio_kaluga1_3_ili9341.py new file mode 100644 index 0000000..5b42fbe --- /dev/null +++ b/examples/ov2640_directio_kaluga1_3_ili9341.py @@ -0,0 +1,176 @@ +# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +# SPDX-FileCopyrightText: Copyright (c) 2021 Jeff Epler for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense + +""" +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, which differ +by rotation. This example is written for one if the ILI9341 variants, +the one which usually uses rotation=90 to get a landscape display. + +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 struct +import time + +import analogio +import board +import busio +import displayio +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 = displayio.FourWire( + spi, + command=board.LCD_D_C, + chip_select=board.LCD_CS, + reset=board.LCD_RST, + baudrate=80_000_000, +) +_INIT_SEQUENCE = ( + b"\x01\x80\x80" # Software reset then delay 0x80 (128ms) + b"\xEF\x03\x03\x80\x02" + b"\xCF\x03\x00\xC1\x30" + b"\xED\x04\x64\x03\x12\x81" + b"\xE8\x03\x85\x00\x78" + b"\xCB\x05\x39\x2C\x00\x34\x02" + b"\xF7\x01\x20" + b"\xEA\x02\x00\x00" + b"\xc0\x01\x23" # Power control VRH[5:0] + b"\xc1\x01\x10" # Power control SAP[2:0];BT[3:0] + b"\xc5\x02\x3e\x28" # VCM control + b"\xc7\x01\x86" # VCM control2 + b"\x36\x01\x40" # Memory Access Control + b"\x37\x01\x00" # Vertical scroll zero + b"\x3a\x01\x55" # COLMOD: Pixel Format Set + b"\xb1\x02\x00\x18" # Frame Rate Control (In Normal Mode/Full Colors) + b"\xb6\x03\x08\x82\x27" # Display Function Control + b"\xF2\x01\x00" # 3Gamma Function Disable + b"\x26\x01\x01" # Gamma curve selected + b"\xe0\x0f\x0F\x31\x2B\x0C\x0E\x08\x4E\xF1\x37\x07\x10\x03\x0E\x09\x00" # Set Gamma + b"\xe1\x0f\x00\x0E\x14\x03\x11\x07\x31\xC1\x48\x08\x0F\x0C\x31\x36\x0F" # Set Gamma + b"\x11\x80\x78" # Exit Sleep then delay 0x78 (120ms) + b"\x29\x80\x78" # Display on then delay 0x78 (120ms) +) + +display = displayio.Display(display_bus, _INIT_SEQUENCE, width=320, height=240) + +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 + +bitmap = displayio.Bitmap(320, 240, 65536) + +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 as e: + return False + + +_image_counter = 0 + + +def open_next_image(): + global _image_counter + 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 + exposure = cam.exposure + try: + cam.size = adafruit_ov2640.OV2640_SIZE_UXGA + cam.colorspace = adafruit_ov2640.OV2640_COLOR_JPEG + cam.exposure = exposure + 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 + cam.exposure = exposure + + +def main(): + display.auto_refresh = False + display_bus.send(42, struct.pack(">hh", 0, 319)) + display_bus.send(43, struct.pack(">hh", 0, 239)) + t0 = time.monotonic_ns() + 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) + display_bus.send(44, bitmap) + t1 = time.monotonic_ns() + print(1e9 / (t1 - t0), "fps") + t0 = t1 + # bitmap.dirty() + # display.refresh(minimum_frames_per_second=0) + + +main() From 2e2b8cb6d0c4fe0d052f7e3beb33600ebac71d8d Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 30 Jul 2021 10:09:56 -0500 Subject: [PATCH 4/9] Add Adafruit IO Example This pushes a JPEG image at 800x600 resolution to Adafruit IO every 3 seconds. --- examples/ov2640_aio_kaluga_1_3.py | 79 +++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 examples/ov2640_aio_kaluga_1_3.py diff --git a/examples/ov2640_aio_kaluga_1_3.py b/examples/ov2640_aio_kaluga_1_3.py new file mode 100644 index 0000000..853aac5 --- /dev/null +++ b/examples/ov2640_aio_kaluga_1_3.py @@ -0,0 +1,79 @@ +# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +# SPDX-FileCopyrightText: Copyright (c) 2021 Jeff Epler for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense + +""" +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(!) + +This example requires that your WIFI and Adafruit IO credentials be configured in CIRCUITPY/secrets.py, and that you have created a feed called "image" with history disabled. + +The maximum image size is 100kB after base64 encoding, or about 65kB before base64 encoding. In practice, "SVGA" (800x600) images are typically around 40kB even though the "capture_buffer_size" (theoretical maximum size) is (width*height/5) bytes or 96kB. +""" + +import os +import time +import binascii + +import board +import busio +import wifi +import socketpool +import ssl +import adafruit_ov2640 +from secrets import secrets +import adafruit_minimqtt.adafruit_minimqtt as MQTT +from adafruit_io.adafruit_io import IO_MQTT + +feed_name = "image" + +print("Connecting to WIFI") +wifi.radio.connect(secrets["ssid"], secrets["password"]) +pool = socketpool.SocketPool(wifi.radio) + +print("Connecting to Adafruit IO") +mqtt_client = MQTT.MQTT( + broker="io.adafruit.com", + username=secrets["aio_username"], + password=secrets["aio_key"], + socket_pool=pool, + ssl_context=ssl.create_default_context(), +) +mqtt_client.connect() +io = IO_MQTT(mqtt_client) + +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 = False +cam.test_pattern = False + +cam.size = adafruit_ov2640.OV2640_SIZE_SVGA +cam.colorspace = adafruit_ov2640.OV2640_COLOR_JPEG +jpeg_buffer = bytearray(cam.capture_buffer_size) +while True: + jpeg = cam.capture(jpeg_buffer) + print(f"Captured {len(jpeg)} bytes of jpeg data") + + # b2a_base64() appends a trailing newline, which IO does not like + encoded_data = binascii.b2a_base64(jpeg).strip() + print(f"Expanded to {len(encoded_data)} for IO upload") + + io.publish("image", encoded_data) + + print("Waiting 3s") + time.sleep(3) From 1bd0e118438c5039c12a157f04a9cee4a4285171 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 30 Jul 2021 10:19:30 -0500 Subject: [PATCH 5/9] FIx up examples page in the docs --- docs/examples.rst | 60 ++++++++++++++++--- ..._kaluga_1_3.py => ov2640_aio_kaluga1_3.py} | 0 2 files changed, 51 insertions(+), 9 deletions(-) rename examples/{ov2640_aio_kaluga_1_3.py => ov2640_aio_kaluga1_3.py} (100%) diff --git a/docs/examples.rst b/docs/examples.rst index 1369773..19f7c93 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -4,41 +4,83 @@ Simple test Ensure your device works with this simple test. .. literalinclude:: ../examples/ov2640_simpletest.py - :caption: examples/ov2640_simpletest.py + :caption: ov2640_simpletest.py :linenos: + +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. .. literalinclude:: ../examples/ov2640_displayio_kaluga1_3_ili9341.py - :caption: ../examples/ov2640_displayio_kaluga1_3_ili9341.py + :caption: ov2640_displayio_kaluga1_3_ili9341.py :linenos: +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. .. literalinclude:: ../examples/ov2640_displayio_kaluga1_3_st7789.py - :caption: ../examples/ov2640_displayio_kaluga1_3_st7789.py + :caption: ov2640_displayio_kaluga1_3_st7789.py :linenos: +Raspberry Pi Pico with st7789 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Display an image from the camera connected to a Raspberry Pi Pico with an st7789 2" display .. literalinclude:: ../examples/ov2640_displayio_pico_st7789_2in.py - :caption: ../examples/ov2640_displayio_pico_st7789_2in.py + :caption: ov2640_displayio_pico_st7789_2in.py + :linenos: + +Kaluga 1.3 with ili9341, direct display +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Preview images on LCD, bypassing displayio for slightly higher framerate + +.. literalinclude:: ../examples/ov2640_directio_kaluga1_3_ili9341.py + :caption: ../examples/ov2640_directio_kaluga1_3_ili9341.py :linenos: -Save an image to internal flash on Kaluga 1.3 + +Image-saving tests +------------------ + +Kaluga 1.3 with ili9341, internal flash +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +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``. .. literalinclude:: ../examples/ov2640_jpeg_kaluga1_3.py - :caption: ../examples/ov2640_jpeg_kaluga1_3.py + :caption: ov2640_jpeg_kaluga1_3.py :linenos: ``boot.py`` for the above program .. literalinclude:: ../examples/ov2640_jpeg_kaluga1_3_boot.py - :caption: ../examples/ov2640_jpeg_kaluga1_3_boot.py + :caption: ov2640_jpeg_kaluga1_3_boot.py :linenos: -Preview images on LCD then save to SD on Kaluga 1.3 fitted with an ili9341 display +Kaluga 1.3 with ili9341, external SD card +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Preview images on LCD then save JPEG images to SD on Kaluga 1.3 fitted with an ili9341 display. .. literalinclude:: ../examples/ov2640_jpeg_sd_kaluga1_3.py - :caption: ../examples/ov2640_jpeg_sd_kaluga1_3.py + :caption: ov2640_jpeg_sd_kaluga1_3.py + :linenos: + +Kaluga 1.3 with Adafruit IO +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Upload JPEG images to Adafruit IO. Requires that WIFI and Adafruit IO be configured in ``secrets.py``. + +.. literalinclude:: ../examples/ov2640_aio_kaluga1_3.py + :caption: ov2640_aio_kaluga1_3.py :linenos: diff --git a/examples/ov2640_aio_kaluga_1_3.py b/examples/ov2640_aio_kaluga1_3.py similarity index 100% rename from examples/ov2640_aio_kaluga_1_3.py rename to examples/ov2640_aio_kaluga1_3.py From 8957115512a5d80142e6eb044cfbed6b6bce30fc Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 30 Jul 2021 10:31:03 -0500 Subject: [PATCH 6/9] Add bitmap example --- docs/examples.rst | 18 ++- examples/ov2640_bmp_sd_kaluga1_3.py | 220 ++++++++++++++++++++++++++++ 2 files changed, 234 insertions(+), 4 deletions(-) create mode 100644 examples/ov2640_bmp_sd_kaluga1_3.py diff --git a/docs/examples.rst b/docs/examples.rst index 19f7c93..3205e91 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -51,8 +51,8 @@ Preview images on LCD, bypassing displayio for slightly higher framerate Image-saving tests ------------------ -Kaluga 1.3 with ili9341, internal flash -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +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``. @@ -67,8 +67,8 @@ code to be saved as ``boot.py``. :caption: ov2640_jpeg_kaluga1_3_boot.py :linenos: -Kaluga 1.3 with ili9341, external SD card -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +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. @@ -76,6 +76,16 @@ Preview images on LCD then save JPEG images to SD on Kaluga 1.3 fitted with an i :caption: ov2640_jpeg_sd_kaluga1_3.py :linenos: +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. + +.. literalinclude:: ../examples/ov2640_bmp_sd_kaluga1_3.py + :caption: ov2640_bmp_sd_kaluga1_3.py + :linenos: + + Kaluga 1.3 with Adafruit IO ~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/examples/ov2640_bmp_sd_kaluga1_3.py b/examples/ov2640_bmp_sd_kaluga1_3.py new file mode 100644 index 0000000..23d9999 --- /dev/null +++ b/examples/ov2640_bmp_sd_kaluga1_3.py @@ -0,0 +1,220 @@ +# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +# SPDX-FileCopyrightText: Copyright (c) 2021 Jeff Epler for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense + +""" +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 in BMP format. +""" + +import os +import struct +import time +import binascii +import ulab.numpy as np + +import analogio +import board +import busio +import displayio +import sdcardio +import storage +import ssl +from adafruit_ili9341 import ILI9341 +import adafruit_ov2640 + +# Nominal voltages of several of the buttons on the audio daughterboard +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 = displayio.FourWire( + spi, + command=board.LCD_D_C, + chip_select=board.LCD_CS, + reset=board.LCD_RST, + baudrate=80_000_000, +) +_INIT_SEQUENCE = ( + b"\x01\x80\x80" # Software reset then delay 0x80 (128ms) + b"\xEF\x03\x03\x80\x02" + b"\xCF\x03\x00\xC1\x30" + b"\xED\x04\x64\x03\x12\x81" + b"\xE8\x03\x85\x00\x78" + b"\xCB\x05\x39\x2C\x00\x34\x02" + b"\xF7\x01\x20" + b"\xEA\x02\x00\x00" + b"\xc0\x01\x23" # Power control VRH[5:0] + b"\xc1\x01\x10" # Power control SAP[2:0];BT[3:0] + b"\xc5\x02\x3e\x28" # VCM control + b"\xc7\x01\x86" # VCM control2 + b"\x36\x01\x90" # Memory Access Control + b"\x37\x01\x00" # Vertical scroll zero + b"\x3a\x01\x55" # COLMOD: Pixel Format Set + b"\xb1\x02\x00\x18" # Frame Rate Control (In Normal Mode/Full Colors) + b"\xb6\x03\x08\x82\x27" # Display Function Control + b"\xF2\x01\x00" # 3Gamma Function Disable + b"\x26\x01\x01" # Gamma curve selected + b"\xe0\x0f\x0F\x31\x2B\x0C\x0E\x08\x4E\xF1\x37\x07\x10\x03\x0E\x09\x00" # Set Gamma + b"\xe1\x0f\x00\x0E\x14\x03\x11\x07\x31\xC1\x48\x08\x0F\x0C\x31\x36\x0F" # Set Gamma + b"\x11\x80\x78" # Exit Sleep then delay 0x78 (120ms) + b"\x29\x80\x78" # Display on then delay 0x78 (120ms) +) + +display = displayio.Display( + display_bus, _INIT_SEQUENCE, width=320, height=240, auto_refresh=False +) + +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 = False +cam.test_pattern = False + +g = displayio.Group(scale=1) +bitmap = displayio.Bitmap(320, 240, 65536) +tg = displayio.TileGrid( + bitmap, + pixel_shader=displayio.ColorConverter( + input_colorspace=displayio.Colorspace.RGB565_SWAPPED + ), +) +g.append(tg) +display.show(g) + + +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 as e: + return False + + +_image_counter = 0 + + +def open_next_image(extension="jpg"): + global _image_counter + while True: + filename = f"/sd/img{_image_counter:04d}.{extension}" + _image_counter += 1 + if exists(filename): + continue + print("#", filename) + return open(filename, "wb") + + +### These routines are for writing BMP files in the RGB565 or BGR565 formats. +_BI_BITFIELDS = 3 + +_bitmask_rgb565 = (0xF800, 0x7E0, 0x1F) +_bitmask_bgr565 = (0x1F, 0x7E0, 0xF800) + + +def write_header(output_file, width, height, masks): + def put_word(value): + output_file.write(struct.pack(" pixels/meter + put_long(11811) # 72dpi -> pixels/meter + put_dword(0) # palette size + put_dword(0) # important color count + put_dword(masks[0]) # red mask + put_dword(masks[1]) # green mask + put_dword(masks[2]) # blue mask + put_dword(0) # alpha mask + put_dword(0) # CS Type + put_padding(3 * 3 * 4) # CIEXYZ infrmation + put_dword(144179) # 2.2 gamma red + put_dword(144179) # 2.2 gamma green + put_dword(144179) # 2.2 gamma blue + + +def capture_image_bmp(bitmap): + with open_next_image("bmp") as f: + swapped = np.frombuffer(bitmap, dtype=np.uint16) + swapped.byteswap(inplace=True) + write_header(f, bitmap.width, bitmap.height, _bitmask_rgb565) + f.write(swapped) + + +display.auto_refresh = False +old_record_pressed = True + +while True: + a_voltage = a.value * a.reference_voltage / 65535 + cam.capture(bitmap) + bitmap.dirty() + + record_pressed = abs(a_voltage - V_RECORD) < 0.05 + display.refresh(minimum_frames_per_second=0) + if record_pressed and not old_record_pressed: + capture_image_bmp(bitmap) + old_record_pressed = record_pressed From 6afe4618a2ff18664c55a65ada1f9fe6438c073f Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 30 Jul 2021 12:29:23 -0500 Subject: [PATCH 7/9] Fix pylint & black diagnostics --- examples/ov2640_aio_kaluga1_3.py | 18 +++++++----- examples/ov2640_bmp_sd_kaluga1_3.py | 14 +++++----- examples/ov2640_directio_kaluga1_3_ili9341.py | 8 +++--- examples/ov2640_jpeg_kaluga1_3.py | 3 +- examples/ov2640_jpeg_kaluga1_3_boot.py | 2 +- examples/ov2640_jpeg_sd_kaluga1_3.py | 28 ++++++++----------- examples/ov2640_simpletest.py | 1 - 7 files changed, 37 insertions(+), 37 deletions(-) diff --git a/examples/ov2640_aio_kaluga1_3.py b/examples/ov2640_aio_kaluga1_3.py index 853aac5..06a12c8 100644 --- a/examples/ov2640_aio_kaluga1_3.py +++ b/examples/ov2640_aio_kaluga1_3.py @@ -10,24 +10,28 @@ The audio board must be mounted between the Kaluga and the LCD, it provides the I2C pull-ups(!) -This example requires that your WIFI and Adafruit IO credentials be configured in CIRCUITPY/secrets.py, and that you have created a feed called "image" with history disabled. +This example requires that your WIFI and Adafruit IO credentials be configured +in CIRCUITPY/secrets.py, and that you have created a feed called "image" with +history disabled. -The maximum image size is 100kB after base64 encoding, or about 65kB before base64 encoding. In practice, "SVGA" (800x600) images are typically around 40kB even though the "capture_buffer_size" (theoretical maximum size) is (width*height/5) bytes or 96kB. +The maximum image size is 100kB after base64 encoding, or about 65kB before +base64 encoding. In practice, "SVGA" (800x600) images are typically around +40kB even though the "capture_buffer_size" (theoretical maximum size) is +(width*height/5) bytes or 96kB. """ -import os -import time import binascii +import ssl +import time +from secrets import secrets # pylint: disable=no-name-in-module import board import busio import wifi import socketpool -import ssl -import adafruit_ov2640 -from secrets import secrets import adafruit_minimqtt.adafruit_minimqtt as MQTT from adafruit_io.adafruit_io import IO_MQTT +import adafruit_ov2640 feed_name = "image" diff --git a/examples/ov2640_bmp_sd_kaluga1_3.py b/examples/ov2640_bmp_sd_kaluga1_3.py index 23d9999..28dd9dc 100644 --- a/examples/ov2640_bmp_sd_kaluga1_3.py +++ b/examples/ov2640_bmp_sd_kaluga1_3.py @@ -127,7 +127,7 @@ def exists(filename): try: os.stat(filename) return True - except OSError as e: + except OSError: return False @@ -135,14 +135,14 @@ def exists(filename): def open_next_image(extension="jpg"): - global _image_counter + global _image_counter # pylint: disable=global-statement while True: filename = f"/sd/img{_image_counter:04d}.{extension}" _image_counter += 1 if exists(filename): continue print("#", filename) - return open(filename, "wb") + return open(filename, "wb") # pylint: disable=consider-using-with ### These routines are for writing BMP files in the RGB565 or BGR565 formats. @@ -197,11 +197,11 @@ def put_padding(length): put_dword(144179) # 2.2 gamma blue -def capture_image_bmp(bitmap): +def capture_image_bmp(the_bitmap): with open_next_image("bmp") as f: - swapped = np.frombuffer(bitmap, dtype=np.uint16) + swapped = np.frombuffer(the_bitmap, dtype=np.uint16) swapped.byteswap(inplace=True) - write_header(f, bitmap.width, bitmap.height, _bitmask_rgb565) + write_header(f, the_bitmap.width, the_bitmap.height, _bitmask_rgb565) f.write(swapped) @@ -209,7 +209,7 @@ def capture_image_bmp(bitmap): old_record_pressed = True while True: - a_voltage = a.value * a.reference_voltage / 65535 + a_voltage = a.value * a.reference_voltage / 65535 # pylint: disable=no-member cam.capture(bitmap) bitmap.dirty() diff --git a/examples/ov2640_directio_kaluga1_3_ili9341.py b/examples/ov2640_directio_kaluga1_3_ili9341.py index 5b42fbe..6f71ee4 100644 --- a/examples/ov2640_directio_kaluga1_3_ili9341.py +++ b/examples/ov2640_directio_kaluga1_3_ili9341.py @@ -116,7 +116,7 @@ def exists(filename): try: os.stat(filename) return True - except OSError as e: + except OSError: return False @@ -124,14 +124,14 @@ def exists(filename): def open_next_image(): - global _image_counter + global _image_counter # pylint: disable=global-statement while True: filename = f"/sd/img{_image_counter:04d}.jpg" _image_counter += 1 if exists(filename): continue print("#", filename) - return open(filename, "wb") + return open(filename, "wb") # pylint: disable=consider-using-with def capture_image(): @@ -160,7 +160,7 @@ def main(): display_bus.send(43, struct.pack(">hh", 0, 239)) t0 = time.monotonic_ns() while True: - a_voltage = a.value * a.reference_voltage / 65535 + a_voltage = a.value * a.reference_voltage / 65535 # pylint: disable=no-member record_pressed = abs(a_voltage - V_RECORD) < 0.05 if record_pressed: capture_image() diff --git a/examples/ov2640_jpeg_kaluga1_3.py b/examples/ov2640_jpeg_kaluga1_3.py index 5d39cbe..c3d3ff6 100644 --- a/examples/ov2640_jpeg_kaluga1_3.py +++ b/examples/ov2640_jpeg_kaluga1_3.py @@ -50,6 +50,7 @@ except OSError as e: print(e) print( - "A 'read-only filesystem' error occurs if you did not correctly install\nov2640_jpeg_kaluga1_3_boot.py as CIRCUITPY/boot.py and reset the board" + "A 'read-only filesystem' error occurs if you did not correctly install" + "\nov2640_jpeg_kaluga1_3_boot.py as CIRCUITPY/boot.py and reset the board" ) print("Wrote to CIRCUITPY/jpeg.jpg") diff --git a/examples/ov2640_jpeg_kaluga1_3_boot.py b/examples/ov2640_jpeg_kaluga1_3_boot.py index 2a872fb..ff22faf 100644 --- a/examples/ov2640_jpeg_kaluga1_3_boot.py +++ b/examples/ov2640_jpeg_kaluga1_3_boot.py @@ -17,7 +17,7 @@ V_RECORD = 2.41 a = analogio.AnalogIn(board.IO6) -a_voltage = a.value * a.reference_voltage / 65535 +a_voltage = a.value * a.reference_voltage / 65535 # pylint: disable=no-member if abs(a_voltage - V_MODE) > 0.05: # If mode is NOT pressed... print("storage writable by CircuitPython") storage.remount("/", readonly=False) diff --git a/examples/ov2640_jpeg_sd_kaluga1_3.py b/examples/ov2640_jpeg_sd_kaluga1_3.py index 93dcd53..1d1807f 100644 --- a/examples/ov2640_jpeg_sd_kaluga1_3.py +++ b/examples/ov2640_jpeg_sd_kaluga1_3.py @@ -95,7 +95,7 @@ def exists(filename): try: os.stat(filename) return True - except OSError as e: + except OSError: return False @@ -103,14 +103,14 @@ def exists(filename): def open_next_image(): - global _image_counter + global _image_counter # pylint: disable=global-statement while True: filename = f"/sd/img{_image_counter:04d}.jpg" _image_counter += 1 if exists(filename): continue print("#", filename) - return open(filename, "wb") + return open(filename, "wb") # pylint: disable=consider-using-with def capture_image(): @@ -131,16 +131,12 @@ def capture_image(): cam.colorspace = old_colorspace -def main(): - 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) - - -main() +display.auto_refresh = False +while True: + a_voltage = a.value * a.reference_voltage / 65535 # pylint: disable=no-member + 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) diff --git a/examples/ov2640_simpletest.py b/examples/ov2640_simpletest.py index 0495db9..f6c4865 100644 --- a/examples/ov2640_simpletest.py +++ b/examples/ov2640_simpletest.py @@ -23,7 +23,6 @@ import sys import time -import digitalio import busio import board From e7141ef424a75ba28cbb94583681e67ad8a345ca Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 30 Jul 2021 12:41:08 -0500 Subject: [PATCH 8/9] Fix more pylint diagnostics --- examples/ov2640_bmp_sd_kaluga1_3.py | 4 ---- examples/ov2640_directio_kaluga1_3_ili9341.py | 7 ------- examples/ov2640_jpeg_sd_kaluga1_3.py | 2 +- 3 files changed, 1 insertion(+), 12 deletions(-) diff --git a/examples/ov2640_bmp_sd_kaluga1_3.py b/examples/ov2640_bmp_sd_kaluga1_3.py index 28dd9dc..7ca3f6b 100644 --- a/examples/ov2640_bmp_sd_kaluga1_3.py +++ b/examples/ov2640_bmp_sd_kaluga1_3.py @@ -27,8 +27,6 @@ import os import struct -import time -import binascii import ulab.numpy as np import analogio @@ -37,8 +35,6 @@ import displayio import sdcardio import storage -import ssl -from adafruit_ili9341 import ILI9341 import adafruit_ov2640 # Nominal voltages of several of the buttons on the audio daughterboard diff --git a/examples/ov2640_directio_kaluga1_3_ili9341.py b/examples/ov2640_directio_kaluga1_3_ili9341.py index 6f71ee4..51b4d4b 100644 --- a/examples/ov2640_directio_kaluga1_3_ili9341.py +++ b/examples/ov2640_directio_kaluga1_3_ili9341.py @@ -35,7 +35,6 @@ import displayio import sdcardio import storage -from adafruit_ili9341 import ILI9341 import adafruit_ov2640 V_MODE = 1.98 @@ -158,7 +157,6 @@ def main(): display.auto_refresh = False display_bus.send(42, struct.pack(">hh", 0, 319)) display_bus.send(43, struct.pack(">hh", 0, 239)) - t0 = time.monotonic_ns() while True: a_voltage = a.value * a.reference_voltage / 65535 # pylint: disable=no-member record_pressed = abs(a_voltage - V_RECORD) < 0.05 @@ -166,11 +164,6 @@ def main(): capture_image() cam.capture(bitmap) display_bus.send(44, bitmap) - t1 = time.monotonic_ns() - print(1e9 / (t1 - t0), "fps") - t0 = t1 - # bitmap.dirty() - # display.refresh(minimum_frames_per_second=0) main() diff --git a/examples/ov2640_jpeg_sd_kaluga1_3.py b/examples/ov2640_jpeg_sd_kaluga1_3.py index 1d1807f..f354c46 100644 --- a/examples/ov2640_jpeg_sd_kaluga1_3.py +++ b/examples/ov2640_jpeg_sd_kaluga1_3.py @@ -33,10 +33,10 @@ import board import busio import displayio +import adafruit_ov2640 import sdcardio import storage from adafruit_ili9341 import ILI9341 -import adafruit_ov2640 V_MODE = 1.98 V_RECORD = 2.41 From cc5eb830248fb79ad7a6b1772ee556e4ae1c7b7a Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 30 Jul 2021 12:52:39 -0500 Subject: [PATCH 9/9] more pylint --- examples/ov2640_directio_kaluga1_3_ili9341.py | 1 - examples/ov2640_jpeg_sd_kaluga1_3.py | 2 +- examples/ov2640_jpeg_sd_pico_st7789_2in.py | 11 +++++------ 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/examples/ov2640_directio_kaluga1_3_ili9341.py b/examples/ov2640_directio_kaluga1_3_ili9341.py index 51b4d4b..40d5643 100644 --- a/examples/ov2640_directio_kaluga1_3_ili9341.py +++ b/examples/ov2640_directio_kaluga1_3_ili9341.py @@ -27,7 +27,6 @@ import os import struct -import time import analogio import board diff --git a/examples/ov2640_jpeg_sd_kaluga1_3.py b/examples/ov2640_jpeg_sd_kaluga1_3.py index f354c46..1d1807f 100644 --- a/examples/ov2640_jpeg_sd_kaluga1_3.py +++ b/examples/ov2640_jpeg_sd_kaluga1_3.py @@ -33,10 +33,10 @@ import board import busio import displayio -import adafruit_ov2640 import sdcardio import storage from adafruit_ili9341 import ILI9341 +import adafruit_ov2640 V_MODE = 1.98 V_RECORD = 2.41 diff --git a/examples/ov2640_jpeg_sd_pico_st7789_2in.py b/examples/ov2640_jpeg_sd_pico_st7789_2in.py index 2a889d8..787123e 100644 --- a/examples/ov2640_jpeg_sd_pico_st7789_2in.py +++ b/examples/ov2640_jpeg_sd_pico_st7789_2in.py @@ -23,6 +23,7 @@ """ import time +import os from displayio import ( Bitmap, Group, @@ -32,15 +33,13 @@ ColorConverter, Colorspace, ) - from adafruit_st7789 import ST7789 import board import busio import digitalio -import adafruit_ov2640 import sdcardio import storage -import os +import adafruit_ov2640 release_displays() # Set up the display (You must customize this block for your display!) @@ -112,7 +111,7 @@ def exists(filename): try: os.stat(filename) return True - except OSError as e: + except OSError: return False @@ -120,14 +119,14 @@ def exists(filename): def open_next_image(): - global _image_counter + global _image_counter # pylint: disable=global-statement while True: filename = f"/sd/img{_image_counter:04d}.jpg" _image_counter += 1 if exists(filename): continue print("#", filename) - return open(filename, "wb") + return open(filename, "wb") # pylint: disable=consider-using-with def capture_image():