Compare commits

..

No commits in common. "main" and "jpeg-mode" have entirely different histories.

34 changed files with 372 additions and 1211 deletions

View file

@ -1,13 +0,0 @@
# SPDX-FileCopyrightText: 2021 Adafruit Industries
#
# SPDX-License-Identifier: MIT
Thank you for contributing! Before you submit a pull request, please read the following.
Make sure any changes you're submitting are in line with the CircuitPython Design Guide, available here: https://docs.circuitpython.org/en/latest/docs/design_guide.html
If your changes are to documentation, please verify that the documentation builds locally by following the steps found here: https://adafru.it/build-docs
Before submitting the pull request, make sure you've run Pylint and Black locally on your code. You can do this manually or using pre-commit. Instructions are available here: https://adafru.it/check-your-code
Please remove all of this text before submitting. Include an explanation or list of changes included in your PR, as well as, if applicable, a link to any related issues.

View file

@ -10,5 +10,71 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Run Build CI workflow
uses: adafruit/workflows-circuitpython-libs/build@main
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- name: Translate Repo Name For Build Tools filename_prefix
id: repo-name
run: |
echo ::set-output name=repo-name::$(
echo ${{ github.repository }} |
awk -F '\/' '{ print tolower($2) }' |
tr '_' '-'
)
- name: Set up Python 3.6
uses: actions/setup-python@v1
with:
python-version: 3.6
- name: Versions
run: |
python3 --version
- name: Checkout Current Repo
uses: actions/checkout@v1
with:
submodules: true
- name: Checkout tools repo
uses: actions/checkout@v2
with:
repository: adafruit/actions-ci-circuitpython-libs
path: actions-ci
- name: Install dependencies
# (e.g. - apt-get: gettext, etc; pip: circuitpython-build-tools, requirements.txt; etc.)
run: |
source actions-ci/install.sh
- name: Pip install pylint, Sphinx, pre-commit
run: |
pip install --force-reinstall pylint Sphinx sphinx-rtd-theme pre-commit
- name: Library version
run: git describe --dirty --always --tags
- name: Setup problem matchers
uses: adafruit/circuitpython-action-library-ci-problem-matchers@v1
- name: Pre-commit hooks
run: |
pre-commit run --all-files
- name: Build assets
run: circuitpython-build-bundles --filename_prefix ${{ steps.repo-name.outputs.repo-name }} --library_location .
- name: Archive bundles
uses: actions/upload-artifact@v2
with:
name: bundles
path: ${{ github.workspace }}/bundles/
- name: Check For docs folder
id: need-docs
run: |
echo ::set-output name=docs::$( find . -wholename './docs' )
- name: Build docs
if: contains(steps.need-docs.outputs.docs, 'docs')
working-directory: docs
run: sphinx-build -E -W -b html . _build/html
- name: Check For setup.py
id: need-pypi
run: |
echo ::set-output name=setup-py::$( find . -wholename './setup.py' )
- name: Build Python package
if: contains(steps.need-pypi.outputs.setup-py, 'setup.py')
run: |
pip install --upgrade setuptools wheel twine readme_renderer testresources
python setup.py sdist
python setup.py bdist_wheel --universal
twine check dist/*

85
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,85 @@
# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
#
# SPDX-License-Identifier: MIT
name: Release Actions
on:
release:
types: [published]
jobs:
upload-release-assets:
runs-on: ubuntu-latest
steps:
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- name: Translate Repo Name For Build Tools filename_prefix
id: repo-name
run: |
echo ::set-output name=repo-name::$(
echo ${{ github.repository }} |
awk -F '\/' '{ print tolower($2) }' |
tr '_' '-'
)
- name: Set up Python 3.6
uses: actions/setup-python@v1
with:
python-version: 3.6
- name: Versions
run: |
python3 --version
- name: Checkout Current Repo
uses: actions/checkout@v1
with:
submodules: true
- name: Checkout tools repo
uses: actions/checkout@v2
with:
repository: adafruit/actions-ci-circuitpython-libs
path: actions-ci
- name: Install deps
run: |
source actions-ci/install.sh
- name: Build assets
run: circuitpython-build-bundles --filename_prefix ${{ steps.repo-name.outputs.repo-name }} --library_location .
- name: Upload Release Assets
# the 'official' actions version does not yet support dynamically
# supplying asset names to upload. @csexton's version chosen based on
# discussion in the issue below, as its the simplest to implement and
# allows for selecting files with a pattern.
# https://github.com/actions/upload-release-asset/issues/4
#uses: actions/upload-release-asset@v1.0.1
uses: csexton/release-asset-action@master
with:
pattern: "bundles/*"
github-token: ${{ secrets.GITHUB_TOKEN }}
upload-pypi:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Check For setup.py
id: need-pypi
run: |
echo ::set-output name=setup-py::$( find . -wholename './setup.py' )
- name: Set up Python
if: contains(steps.need-pypi.outputs.setup-py, 'setup.py')
uses: actions/setup-python@v1
with:
python-version: '3.x'
- name: Install dependencies
if: contains(steps.need-pypi.outputs.setup-py, 'setup.py')
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build and publish
if: contains(steps.need-pypi.outputs.setup-py, 'setup.py')
env:
TWINE_USERNAME: ${{ secrets.pypi_username }}
TWINE_PASSWORD: ${{ secrets.pypi_password }}
run: |
python setup.py sdist
twine upload dist/*

View file

@ -1,19 +0,0 @@
# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
#
# SPDX-License-Identifier: MIT
name: GitHub Release Actions
on:
release:
types: [published]
jobs:
upload-release-assets:
runs-on: ubuntu-latest
steps:
- name: Run GitHub Release CI workflow
uses: adafruit/workflows-circuitpython-libs/release-gh@main
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
upload-url: ${{ github.event.release.upload_url }}

View file

@ -1,19 +0,0 @@
# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
#
# SPDX-License-Identifier: MIT
name: PyPI Release Actions
on:
release:
types: [published]
jobs:
upload-release-assets:
runs-on: ubuntu-latest
steps:
- name: Run PyPI Release CI workflow
uses: adafruit/workflows-circuitpython-libs/release-pypi@main
with:
pypi-username: ${{ secrets.pypi_username }}
pypi-password: ${{ secrets.pypi_password }}

56
.gitignore vendored
View file

@ -1,48 +1,18 @@
# SPDX-FileCopyrightText: 2022 Kattni Rembor, written for Adafruit Industries
# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# SPDX-License-Identifier: Unlicense
# Do not include files and directories created by your personal work environment, such as the IDE
# you use, except for those already listed here. Pull requests including changes to this file will
# not be accepted.
# This .gitignore file contains rules for files generated by working with CircuitPython libraries,
# including building Sphinx, testing with pip, and creating a virual environment, as well as the
# MacOS and IDE-specific files generated by using MacOS in general, or the PyCharm or VSCode IDEs.
# If you find that there are files being generated on your machine that should not be included in
# your git commit, you should create a .gitignore_global file on your computer to include the
# files created by your personal setup. To do so, follow the two steps below.
# First, create a file called .gitignore_global somewhere convenient for you, and add rules for
# the files you want to exclude from git commits.
# Second, configure Git to use the exclude file for all Git repositories by running the
# following via commandline, replacing "path/to/your/" with the actual path to your newly created
# .gitignore_global file:
# git config --global core.excludesfile path/to/your/.gitignore_global
# CircuitPython-specific files
*.mpy
# Python-specific files
__pycache__
*.pyc
# Sphinx build-specific files
_build
# This file results from running `pip -e install .` in a local repository
*.egg-info
# Virtual environment-specific files
.env
.venv
# MacOS-specific files
*.DS_Store
# IDE-specific files
.idea
__pycache__
_build
*.pyc
.env
.python-version
build*/
bundles
*.DS_Store
.eggs
dist
**/*.egg-info
.vscode
*~

View file

@ -3,40 +3,38 @@
# SPDX-License-Identifier: Unlicense
repos:
- repo: https://github.com/python/black
rev: 23.3.0
- repo: https://github.com/python/black
rev: 20.8b1
hooks:
- id: black
- repo: https://github.com/fsfe/reuse-tool
rev: v1.1.2
- id: black
- repo: https://github.com/fsfe/reuse-tool
rev: v0.12.1
hooks:
- id: reuse
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
- id: reuse
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.3.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/pycqa/pylint
rev: v2.17.4
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/pycqa/pylint
rev: pylint-2.7.1
hooks:
- id: pylint
- id: pylint
name: pylint (library code)
types: [python]
args:
- --disable=consider-using-f-string
exclude: "^(docs/|examples/|tests/|setup.py$)"
- id: pylint
name: pylint (example code)
- repo: local
hooks:
- id: pylint_examples
name: pylint (examples code)
description: Run pylint rules on "examples/*.py" files
types: [python]
files: "^examples/"
args:
- --disable=missing-docstring,invalid-name,consider-using-f-string,duplicate-code
- id: pylint
name: pylint (test code)
entry: /usr/bin/env bash -c
args: ['([[ ! -d "examples" ]] || for example in $(find . -path "./examples/*.py"); do pylint --disable=missing-docstring,invalid-name $example; done)']
language: system
- id: pylint_tests
name: pylint (tests code)
description: Run pylint rules on "tests/*.py" files
types: [python]
files: "^tests/"
args:
- --disable=missing-docstring,consider-using-f-string,duplicate-code
entry: /usr/bin/env bash -c
args: ['([[ ! -d "tests" ]] || for test in $(find . -path "./tests/*.py"); do pylint --disable=missing-docstring $test; done)']
language: system

View file

@ -26,7 +26,7 @@ jobs=1
# List of plugins (as comma separated values of python modules names) to load,
# usually to register additional checkers.
load-plugins=pylint.extensions.no_self_use
load-plugins=
# Pickle collected data for later comparisons.
persistent=yes
@ -54,8 +54,8 @@ confidence=
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W"
# disable=import-error,raw-checker-failed,bad-inline-option,locally-disabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,deprecated-str-translate-call
disable=raw-checker-failed,bad-inline-option,locally-disabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,import-error,pointless-string-statement,unspecified-encoding
# disable=import-error,print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call
disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,import-error,bad-continuation,pointless-string-statement
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
@ -225,6 +225,12 @@ max-line-length=100
# Maximum number of lines in a module
max-module-lines=1000
# List of optional constructs for which whitespace checking is disabled. `dict-
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
# `empty-line` allows space-only lines.
no-space-check=trailing-comma,dict-separator
# Allow the body of a class to be on the same line as the declaration if body
# contains single statement.
single-line-class-stmt=no
@ -251,22 +257,38 @@ min-similarity-lines=12
[BASIC]
# Naming hint for argument names
argument-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct argument names
argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Naming hint for attribute names
attr-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct attribute names
attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Bad variable names which should always be refused, separated by a comma
bad-names=foo,bar,baz,toto,tutu,tata
# Naming hint for class attribute names
class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
# Regular expression matching correct class attribute names
class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
# Naming hint for class names
# class-name-hint=[A-Z_][a-zA-Z0-9]+$
class-name-hint=[A-Z_][a-zA-Z0-9_]+$
# Regular expression matching correct class names
# class-rgx=[A-Z_][a-zA-Z0-9]+$
class-rgx=[A-Z_][a-zA-Z0-9_]+$
# Naming hint for constant names
const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$
# Regular expression matching correct constant names
const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
@ -274,6 +296,9 @@ const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
# ones are exempt.
docstring-min-length=-1
# Naming hint for function names
function-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct function names
function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
@ -284,12 +309,21 @@ good-names=r,g,b,w,i,j,k,n,x,y,z,ex,ok,Run,_
# Include a hint for the correct naming format with invalid-name
include-naming-hint=no
# Naming hint for inline iteration names
inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$
# Regular expression matching correct inline iteration names
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
# Naming hint for method names
method-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct method names
method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Naming hint for module names
module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
# Regular expression matching correct module names
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
@ -305,6 +339,9 @@ no-docstring-rgx=^_
# to this list to register other decorators that produce valid properties.
property-classes=abc.abstractproperty
# Naming hint for variable names
variable-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct variable names
variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
@ -396,4 +433,4 @@ min-public-methods=1
# Exceptions that will emit a warning when being caught. Defaults to
# "Exception"
overgeneral-exceptions=builtins.Exception
overgeneral-exceptions=Exception

View file

@ -1,22 +0,0 @@
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
sphinx:
configuration: docs/conf.py
build:
os: ubuntu-20.04
tools:
python: "3"
python:
install:
- requirements: docs/requirements.txt
- requirements: requirements.txt

7
.readthedocs.yml Normal file
View file

@ -0,0 +1,7 @@
# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense
python:
version: 3
requirements_file: requirements.txt

View file

@ -3,11 +3,11 @@ Introduction
.. image:: https://readthedocs.org/projects/adafruit-circuitpython-ov2640/badge/?version=latest
:target: https://docs.circuitpython.org/projects/ov2640/en/latest/
:target: https://circuitpython.readthedocs.io/projects/ov2640/en/latest/
:alt: Documentation Status
.. image:: https://raw.githubusercontent.com/adafruit/Adafruit_CircuitPython_Bundle/main/badges/adafruit_discord.svg
.. image:: https://img.shields.io/discord/327254708534116352.svg
:target: https://adafru.it/discord
:alt: Discord
@ -23,8 +23,6 @@ Introduction
CircuitPython driver for OV2640 Camera.
This driver is designed to work directly with the OV2640 camera module through an 18-pin header.
It does not work with products such as ArduCam which process the camera data themselves.
Dependencies
=============
@ -69,16 +67,15 @@ Using the ESP32-S2 Kaluga Dev Kit and its included camera, capture a 160x120 ima
cam.capture(buf)
Documentation
=============
API documentation for this library can be found on `Read the Docs <https://docs.circuitpython.org/projects/ov2640/en/latest/>`_.
For information on building library documentation, please check out `this guide <https://learn.adafruit.com/creating-and-sharing-a-circuitpython-library/sharing-our-docs-on-readthedocs#sphinx-5-1>`_.
Contributing
============
Contributions are welcome! Please read our `Code of Conduct
<https://github.com/adafruit/Adafruit_CircuitPython_OV2640/blob/main/CODE_OF_CONDUCT.md>`_
before contributing to help this project stay welcoming.
Documentation
=============
For information on building library documentation, please check out
`this guide <https://learn.adafruit.com/creating-and-sharing-a-circuitpython-library/sharing-our-docs-on-readthedocs#sphinx-5-1>`_.

View file

@ -1,4 +1,3 @@
SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
SPDX-FileCopyrightText: Copyright (c) 2021 Jeff Epler for Adafruit Industries
SPDX-License-Identifier: MIT

View file

@ -26,10 +26,10 @@ Implementation Notes
* Adafruit's Bus Device library: https:# github.com/adafruit/Adafruit_CircuitPython_BusDevice
"""
# pylint: disable=too-many-lines,unnecessary-lambda-assignment
# pylint: disable=too-many-lines
# imports
__version__ = "0.0.0+auto.0"
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_OV7670.git"
import time
@ -41,14 +41,6 @@ from adafruit_bus_device.i2c_device import I2CDevice
from micropython import const
try:
from typing import Optional, Union, Type, List
from busio import I2C
from microcontroller import Pin
from circuitpython_typing import WriteableBuffer
except ImportError:
pass
CTRLI = const(0x50)
_R_BYPASS = const(0x05)
_QS = const(0x44)
@ -962,17 +954,17 @@ _ov2640_color_settings = {
class _RegBits:
def __init__(self, bank: int, reg: int, shift: int, mask: int) -> None:
def __init__(self, bank, reg, shift, mask):
self.bank = bank
self.reg = reg
self.shift = shift
self.mask = mask
def __get__(self, obj: "_SCCBCameraBase", objtype: Optional[Type] = None) -> int:
def __get__(self, obj, objtype=None):
reg_value = obj._read_bank_register(self.bank, self.reg)
return (reg_value >> self.shift) & self.mask
def __set__(self, obj: "_SCCBCameraBase", value: Union[bool, int]) -> None:
def __set__(self, obj, value):
if value & ~self.mask:
raise ValueError(
f"Value 0x{value:02x} does not fit in mask 0x{self.mask:02x}"
@ -984,38 +976,38 @@ class _RegBits:
class _SCCBCameraBase: # pylint: disable=too-few-public-methods
def __init__(self, i2c_bus: I2C, i2c_address: int) -> None:
def __init__(self, i2c_bus, i2c_address):
self._i2c_device = I2CDevice(i2c_bus, i2c_address)
self._bank = None
def _get_reg_bits(self, bank: int, reg: int, shift: int, mask: int) -> int:
def _get_reg_bits(self, bank, reg, shift, mask):
return (self._read_bank_register(bank, reg) >> shift) & mask
def _set_reg_bits( # pylint: disable=too-many-arguments
self, bank: int, reg: int, shift: int, mask: int, value: int
) -> None:
def _set_reg_bits(
self, bank, reg, shift, mask, value
): # pylint: disable=too-many-arguments
reg_value = self._read_bank_register(bank, reg)
reg_value &= ~(mask << shift)
reg_value |= value << shift
self._write_register(reg, reg_value)
def _write_list(self, reg_list: List[int]) -> None:
def _write_list(self, reg_list):
for i in range(0, len(reg_list), 2):
self._write_register(reg_list[i], reg_list[i + 1])
time.sleep(0.001)
def _write_bank_register(self, bank: int, reg: int, value: int) -> None:
def _write_bank_register(self, bank, reg, value):
if self._bank != bank:
self._write_register(_BANK_SEL, bank)
self._write_register(reg, value)
def _read_bank_register(self, bank: int, reg: int) -> int:
def _read_bank_register(self, bank, reg):
if self._bank != bank:
self._write_register(_BANK_SEL, bank)
result = self._read_register(reg)
return result
def _write_register(self, reg: int, value: int) -> None:
def _write_register(self, reg, value):
if reg == _BANK_SEL:
if self._bank == value:
return
@ -1027,7 +1019,7 @@ class _SCCBCameraBase: # pylint: disable=too-few-public-methods
with self._i2c_device as i2c:
i2c.write(b)
def _read_register(self, reg: int) -> int:
def _read_register(self, reg):
b = bytearray(1)
b[0] = reg
with self._i2c_device as i2c:
@ -1047,17 +1039,17 @@ class OV2640(_SCCBCameraBase): # pylint: disable=too-many-instance-attributes
def __init__(
self,
i2c_bus: I2C,
data_pins: Pin,
clock: Pin,
vsync: Pin,
href: Pin,
shutdown: Optional[Pin] = None,
reset: Optional[Pin] = None,
mclk: Optional[Pin] = None,
mclk_frequency: int = 20_000_000,
i2c_address: int = 0x30,
size: int = OV2640_SIZE_QQVGA,
i2c_bus,
data_pins,
clock,
vsync,
href,
shutdown=None,
reset=None,
mclk=None,
mclk_frequency=20_000_000,
i2c_address=0x30,
size=OV2640_SIZE_QQVGA,
): # pylint: disable=too-many-arguments
"""
Args:
@ -1130,11 +1122,11 @@ class OV2640(_SCCBCameraBase): # pylint: disable=too-many-instance-attributes
data_pins=data_pins, clock=clock, vsync=vsync, href=href
)
def capture(self, buf: WriteableBuffer) -> Optional[memoryview]:
def capture(self, buf):
"""Capture an image into the buffer.
Args:
buf (WriteableBuffer): A WritableBuffer to contain the \
buf (Union[bytearray, memoryview]): A WritableBuffer to contain the \
captured image. Note that this can be a ulab array or a displayio Bitmap.
"""
self._imagecapture.capture(buf)
@ -1146,40 +1138,40 @@ class OV2640(_SCCBCameraBase): # pylint: disable=too-many-instance-attributes
return None
@property
def capture_buffer_size(self) -> int:
def capture_buffer_size(self):
"""Return the size of capture buffer to use with current resolution & colorspace settings"""
if self.colorspace == OV2640_COLOR_JPEG:
return self.width * self.height // 5
return self.width * self.height * 2
@property
def mclk_frequency(self) -> Optional[int]:
def mclk_frequency(self):
"""Get the actual frequency the generated mclk, or None"""
return self._mclk_pwm.frequency if self._mclk_pwm else None
@property
def width(self) -> int:
def width(self):
"""Get the image width in pixels. A buffer of 2*width*height bytes \
stores a whole image."""
return self._w
@property
def height(self) -> int:
def height(self):
"""Get the image height in pixels. A buffer of 2*width*height bytes \
stores a whole image."""
return self._h
@property
def colorspace(self) -> bytes:
def colorspace(self):
"""Get or set the colorspace, one of the ``OV2640_COLOR_`` constants."""
return self._colorspace
@colorspace.setter
def colorspace(self, colorspace: bytes) -> None:
def colorspace(self, colorspace):
self._colorspace = colorspace
self._set_size_and_colorspace()
def _set_colorspace(self) -> None:
def _set_colorspace(self):
colorspace = self._colorspace
settings = _ov2640_color_settings[colorspace]
@ -1188,7 +1180,7 @@ class OV2640(_SCCBCameraBase): # pylint: disable=too-many-instance-attributes
self._write_list(settings)
time.sleep(0.01)
def deinit(self) -> None:
def deinit(self):
"""Deinitialize the camera"""
self._imagecapture.deinit()
if self._mclk_pwm:
@ -1199,11 +1191,11 @@ class OV2640(_SCCBCameraBase): # pylint: disable=too-many-instance-attributes
self._reset.deinit()
@property
def size(self) -> int:
def size(self):
"""Get or set the captured image size, one of the ``OV2640_SIZE_`` constants."""
return self._size
def _set_size_and_colorspace(self) -> None:
def _set_size_and_colorspace(self):
size = self._size
width, height, ratio = _resolution_info[size]
offset_x, offset_y, max_x, max_y = _ratio_table[ratio]
@ -1214,7 +1206,8 @@ class OV2640(_SCCBCameraBase): # pylint: disable=too-many-instance-attributes
max_y //= 4
offset_x //= 4
offset_y //= 4
max_y = min(max_y, 296)
if max_y > 296:
max_y = 296
elif size <= OV2640_SIZE_SVGA:
mode = _OV2640_MODE_SVGA
@ -1226,11 +1219,11 @@ class OV2640(_SCCBCameraBase): # pylint: disable=too-many-instance-attributes
self._set_window(mode, offset_x, offset_y, max_x, max_y, width, height)
@size.setter
def size(self, size: int) -> None:
def size(self, size):
self._size = size
self._set_size_and_colorspace()
def _set_flip(self) -> None:
def _set_flip(self):
bits = 0
if self._flip_x:
bits |= _REG04_HFLIP_IMG
@ -1239,45 +1232,38 @@ class OV2640(_SCCBCameraBase): # pylint: disable=too-many-instance-attributes
self._write_bank_register(_BANK_SENSOR, _REG04, _REG04_SET(bits))
@property
def flip_x(self) -> bool:
def flip_x(self):
"""Get or set the X-flip flag"""
return self._flip_x
@flip_x.setter
def flip_x(self, value: bool) -> None:
def flip_x(self, value):
self._flip_x = bool(value)
self._set_flip()
@property
def flip_y(self) -> bool:
def flip_y(self):
"""Get or set the Y-flip flag"""
return self._flip_y
@flip_y.setter
def flip_y(self, value: bool) -> None:
def flip_y(self, value):
self._flip_y = bool(value)
self._set_flip()
@property
def product_id(self) -> int:
def product_id(self):
"""Get the product id (PID) register. The expected value is 0x26."""
return self._read_bank_register(_BANK_SENSOR, _REG_PID)
@property
def product_version(self) -> int:
"""Get the version (VER) register. The expected value is 0x41."""
def product_version(self):
"""Get the version (VER) register. The expected value is 0x4x."""
return self._read_bank_register(_BANK_SENSOR, _REG_VER)
def _set_window( # pylint: disable=too-many-arguments, too-many-locals
self,
mode: int,
offset_x: int,
offset_y: int,
max_x: int,
max_y: int,
width: int,
height: int,
) -> None:
def _set_window(
self, mode, offset_x, offset_y, max_x, max_y, width, height
): # pylint: disable=too-many-arguments, too-many-locals
self._w = width
self._h = height
@ -1348,22 +1334,3 @@ class OV2640(_SCCBCameraBase): # pylint: disable=too-many-instance-attributes
# Reestablish test pattern
if self._test_pattern:
self.test_pattern = self._test_pattern
@property
def exposure(self) -> int:
"""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: int) -> None:
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)

View file

@ -6,7 +6,6 @@
import os
import sys
import datetime
sys.path.insert(0, os.path.abspath(".."))
@ -17,7 +16,6 @@ sys.path.insert(0, os.path.abspath(".."))
# ones.
extensions = [
"sphinx.ext.autodoc",
"sphinxcontrib.jquery",
"sphinx.ext.intersphinx",
"sphinx.ext.napoleon",
"sphinx.ext.todo",
@ -31,12 +29,12 @@ autodoc_mock_imports = ["adafruit_bus_device", "digitalio", "imagecapture", "pwm
intersphinx_mapping = {
"python": ("https://docs.python.org/3", None),
"python": ("https://docs.python.org/3.4", None),
"BusDevice": (
"https://docs.circuitpython.org/projects/busdevice/en/latest/",
"https://circuitpython.readthedocs.io/projects/busdevice/en/latest/",
None,
),
"CircuitPython": ("https://docs.circuitpython.org/en/latest/", None),
"CircuitPython": ("https://circuitpython.readthedocs.io/en/latest/", None),
}
# Show the docstring from both the class and its __init__() method.
@ -52,14 +50,7 @@ master_doc = "index"
# General information about the project.
project = "Adafruit CircuitPython OV2640 Library"
creation_year = "2021"
current_year = str(datetime.datetime.now().year)
year_duration = (
current_year
if current_year == creation_year
else creation_year + " - " + current_year
)
copyright = year_duration + " Jeff Epler"
copyright = "2021 Jeff Epler"
author = "Jeff Epler"
# The version info for the project you're documenting, acts as replacement for
@ -76,7 +67,7 @@ release = "1.0"
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = "en"
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
@ -114,9 +105,19 @@ napoleon_numpy_docstring = False
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
import sphinx_rtd_theme
on_rtd = os.environ.get("READTHEDOCS", None) == "True"
html_theme = "sphinx_rtd_theme"
if not on_rtd: # only import and set the theme if we're building docs locally
try:
import sphinx_rtd_theme
html_theme = "sphinx_rtd_theme"
html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), "."]
except:
html_theme = "default"
html_theme_path = ["."]
else:
html_theme_path = ["."]
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,

View file

@ -4,93 +4,41 @@ Simple test
Ensure your device works with this simple test.
.. literalinclude:: ../examples/ov2640_simpletest.py
:caption: ov2640_simpletest.py
:caption: examples/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: ov2640_displayio_kaluga1_3_ili9341.py
:caption: ../examples/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: ov2640_displayio_kaluga1_3_st7789.py
:caption: ../examples/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: ov2640_displayio_pico_st7789_2in.py
:caption: ../examples/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:
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``.
Save an image to internal flash on Kaluga 1.3
.. literalinclude:: ../examples/ov2640_jpeg_kaluga1_3.py
:caption: ov2640_jpeg_kaluga1_3.py
:caption: ../examples/ov2640_jpeg_kaluga1_3.py
:linenos:
``boot.py`` for the above program
.. literalinclude:: ../examples/ov2640_jpeg_kaluga1_3_boot.py
:caption: ov2640_jpeg_kaluga1_3_boot.py
:caption: ../examples/ov2640_jpeg_kaluga1_3_boot.py
:linenos:
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.
Preview images on LCD then save to SD on Kaluga 1.3 fitted with an ili9341 display
.. literalinclude:: ../examples/ov2640_jpeg_sd_kaluga1_3.py
: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
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Upload JPEG images to Adafruit IO. Requires that WIFI and Adafruit IO be configured in ``settings.toml``.
.. literalinclude:: ../examples/ov2640_aio_kaluga1_3.py
:caption: ov2640_aio_kaluga1_3.py
:caption: ../examples/ov2640_jpeg_sd_kaluga1_3.py
:linenos:

View file

@ -27,9 +27,8 @@ Table of Contents
.. toctree::
:caption: Other Links
Download from GitHub <https://github.com/adafruit/Adafruit_CircuitPython_OV2640/releases/latest>
Download Library Bundle <https://circuitpython.org/libraries>
CircuitPython Reference Documentation <https://docs.circuitpython.org>
Download <https://github.com/adafruit/Adafruit_CircuitPython_OV2640/releases/latest>
CircuitPython Reference Documentation <https://circuitpython.readthedocs.io>
CircuitPython Support Forum <https://forums.adafruit.com/viewforum.php?f=60>
Discord Chat <https://adafru.it/discord>
Adafruit Learning System <https://learn.adafruit.com>

View file

@ -1,7 +0,0 @@
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense
sphinx
sphinxcontrib-jquery
sphinx-rtd-theme

View file

@ -1,90 +0,0 @@
# 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/settings.toml, 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 binascii
import time
from os import getenv
import board
import busio
import wifi
import adafruit_connection_manager
import adafruit_minimqtt.adafruit_minimqtt as MQTT
from adafruit_io.adafruit_io import IO_MQTT
import adafruit_ov2640
feed_name = "image"
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
aio_key = getenv("ADAFRUIT_AIO_KEY")
print("Connecting to WIFI")
wifi.radio.connect(ssid, password)
pool = adafruit_connection_manager.get_radio_socketpool(wifi.radio)
ssl_context = adafruit_connection_manager.get_radio_ssl_context(wifi.radio)
print("Connecting to Adafruit IO")
mqtt_client = MQTT.MQTT(
broker="io.adafruit.com",
username=aio_username,
password=aio_key,
socket_pool=pool,
ssl_context=ssl_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)

View file

@ -1,92 +0,0 @@
# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
# SPDX-FileCopyrightText: Copyright (c) 2021 Jeff Epler for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense
"""
This exampl us for the Espressif Soala Wrover with an OV2640 Camera
This example requires that your WIFI and Adafruit IO credentials be configured
in CIRCUITPY/settings.toml, 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 binascii
import time
from os import getenv
import board
import busio
import wifi
import adafruit_connection_manager
import adafruit_minimqtt.adafruit_minimqtt as MQTT
from adafruit_io.adafruit_io import IO_MQTT
import adafruit_ov2640
feed_name = "image-saola-ov2640"
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
aio_key = getenv("ADAFRUIT_AIO_KEY")
print("Connecting to WIFI")
wifi.radio.connect(ssid, password)
pool = adafruit_connection_manager.get_radio_socketpool(wifi.radio)
ssl_context = adafruit_connection_manager.get_radio_ssl_context(wifi.radio)
print("Connecting to Adafruit IO")
mqtt_client = MQTT.MQTT(
broker="io.adafruit.com",
username=aio_username,
password=aio_key,
socket_pool=pool,
ssl_context=ssl_context,
)
mqtt_client.connect()
io = IO_MQTT(mqtt_client)
bus = busio.I2C(scl=board.IO7, sda=board.IO8)
cam = adafruit_ov2640.OV2640(
bus,
data_pins=(
board.IO36,
board.IO37,
board.IO41,
board.IO42,
board.IO39,
board.IO40,
board.IO21,
board.IO38,
),
clock=board.IO33,
vsync=board.IO2,
href=board.IO3,
mclk=board.IO1,
mclk_frequency=20_000_000,
size=adafruit_ov2640.OV2640_SIZE_QVGA,
)
cam.flip_x = False
cam.flip_y = 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(feed_name, encoded_data)
print("Waiting 10s")
time.sleep(10)

View file

@ -1,216 +0,0 @@
# 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 ulab.numpy as np
import analogio
import board
import busio
import displayio
import sdcardio
import storage
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.root_group = 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:
return False
_image_counter = 0
def open_next_image(extension="jpg"):
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") # pylint: disable=consider-using-with
### 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("<H", value))
def put_dword(value):
output_file.write(struct.pack("<I", value))
def put_long(value):
output_file.write(struct.pack("<i", value))
def put_padding(length):
output_file.write(b"\0" * length)
filesize = 14 + 108 + height * width * 2
# BMP header
output_file.write(b"BM")
put_dword(filesize)
put_word(0) # Creator 1
put_word(0) # Creator 2
put_dword(14 + 108) # Offset of bitmap data
# DIB header (BITMAPV4HEADER)
put_dword(108) # sizeof(BITMAPV4HEADER)
put_long(width)
put_long(-height)
put_word(1) # number of color planes (must be 1)
put_word(16) # number of bits per pixel
put_dword(_BI_BITFIELDS) # "compression"
put_dword(2 * width * height) # size of raw bitmap data
put_long(11811) # 72dpi -> 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(the_bitmap):
with open_next_image("bmp") as f:
swapped = np.frombuffer(the_bitmap, dtype=np.uint16)
swapped.byteswap(inplace=True)
write_header(f, the_bitmap.width, the_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 # pylint: disable=no-member
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

View file

@ -1,168 +0,0 @@
# 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 analogio
import board
import busio
import displayio
import sdcardio
import storage
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:
return False
_image_counter = 0
def open_next_image():
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") # pylint: disable=consider-using-with
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))
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)
display_bus.send(44, bitmap)
main()

View file

@ -58,11 +58,11 @@ bitmap = displayio.Bitmap(320, 240, 65536)
tg = displayio.TileGrid(
bitmap,
pixel_shader=displayio.ColorConverter(
input_colorspace=displayio.Colorspace.BGR565_SWAPPED
input_colorspace=displayio.Colorspace.RGB565_SWAPPED
),
)
g.append(tg)
display.root_group = g
display.show(g)
display.auto_refresh = False
while True:

View file

@ -1,79 +0,0 @@
# 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. It probably won't work on v1.2 without modification.
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.
The camera included with the Kaluga development kit is the incompatible OV2640,
it won't work.
The audio board must be mounted between the Kaluga and the LCD, it provides the
I2C pull-ups(!)
"""
import board
import busio
import displayio
from adafruit_ili9341 import ILI9341
import ulab.numpy as np
import adafruit_ov2640
# Pylint is unable to see that the "size" property of OV2640_GrandCentral exists
# pylint: disable=attribute-defined-outside-init
# 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
)
display = ILI9341(display_bus, width=320, height=240, rotation=90)
bus = busio.I2C(scl=board.CAMERA_SIOC, sda=board.CAMERA_SIOD)
cam = adafruit_ov2640.OV2640(
bus,
data_pins=board.CAMERA_DATA,
clock=board.CAMERA_PCLK,
vsync=board.CAMERA_VSYNC,
href=board.CAMERA_HREF,
mclk=board.CAMERA_XCLK,
mclk_frequency=20_000_000,
size=adafruit_ov2640.OV2640_SIZE_QVGA,
)
cam.flip_x = False
cam.flip_y = True
pid = cam.product_id
ver = cam.product_version
print(f"Detected pid={pid:x} ver={ver:x}")
cam.test_pattern = True
g = displayio.Group(scale=1)
bitmap = displayio.Bitmap(320, 240, 65536)
arr = np.frombuffer(bitmap, dtype=np.uint16)
tg = displayio.TileGrid(
bitmap,
pixel_shader=displayio.ColorConverter(
input_colorspace=displayio.Colorspace.RGB565_SWAPPED
),
)
g.append(tg)
display.root_group = g
display.auto_refresh = False
while True:
cam.capture(bitmap)
arr[:] = ~arr # Invert every pixel in the bitmap, via the array
bitmap.dirty()
display.refresh(minimum_frames_per_second=0)
cam.deinit()

View file

@ -61,11 +61,11 @@ bitmap = displayio.Bitmap(320, 240, 65536)
tg = displayio.TileGrid(
bitmap,
pixel_shader=displayio.ColorConverter(
input_colorspace=displayio.Colorspace.BGR565_SWAPPED
input_colorspace=displayio.Colorspace.RGB565_SWAPPED
),
)
g.append(tg)
display.root_group = g
display.show(g)
display.auto_refresh = False
while True:

View file

@ -64,7 +64,7 @@ width = display.width
height = display.height
cam.size = adafruit_ov2640.OV2640_SIZE_QQVGA
# cam.test_pattern = True
cam.test_pattern = True
bitmap = Bitmap(cam.width, cam.height, 65536)
print(width, height, cam.width, cam.height)
@ -73,10 +73,10 @@ if bitmap is None:
g = Group(scale=1, x=(width - cam.width) // 2, y=(height - cam.height) // 2)
tg = TileGrid(
bitmap, pixel_shader=ColorConverter(input_colorspace=Colorspace.BGR565_SWAPPED)
bitmap, pixel_shader=ColorConverter(input_colorspace=Colorspace.RGB565_SWAPPED)
)
g.append(tg)
display.root_group = g
display.show(g)
display.auto_refresh = False
while True:

View file

@ -50,7 +50,6 @@ try:
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")

View file

@ -17,7 +17,7 @@ V_MODE = 1.98
V_RECORD = 2.41
a = analogio.AnalogIn(board.IO6)
a_voltage = a.value * a.reference_voltage / 65535 # pylint: disable=no-member
a_voltage = a.value * a.reference_voltage / 65535
if abs(a_voltage - V_MODE) > 0.05: # If mode is NOT pressed...
print("storage writable by CircuitPython")
storage.remount("/", readonly=False)

View file

@ -76,11 +76,11 @@ bitmap = displayio.Bitmap(320, 240, 65536)
tg = displayio.TileGrid(
bitmap,
pixel_shader=displayio.ColorConverter(
input_colorspace=displayio.Colorspace.BGR565_SWAPPED
input_colorspace=displayio.Colorspace.RGB565_SWAPPED
),
)
g.append(tg)
display.root_group = g
display.show(g)
display.auto_refresh = False
@ -95,7 +95,7 @@ def exists(filename):
try:
os.stat(filename)
return True
except OSError:
except OSError as e:
return False
@ -103,14 +103,14 @@ _image_counter = 0
def open_next_image():
global _image_counter # pylint: disable=global-statement
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") # pylint: disable=consider-using-with
return open(filename, "wb")
def capture_image():
@ -131,12 +131,16 @@ def capture_image():
cam.colorspace = old_colorspace
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)
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()

View file

@ -1,160 +0,0 @@
# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
# SPDX-FileCopyrightText: Copyright (c) 2021 Jeff Epler for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense
"""
Display an image on the LCD, then record an image when a button is pressed/held.
Uses the SD Card present on the 2inch st7789 breakout board
This example also requires an SD card breakout wired as follows:
SPI BUS - same as the ST7789 -- with MISO connected
* GP2: SD Clock Input
* GP3: SD Serial Input (MOSI)
* GP4: SD Serial Output (MISO)
* GP5: SD Chip Select
A button is needed to trigger the capture
Attach a button to GROUND and GP22
Insert a CircuitPython-compatible SD card before powering on the Kaluga.
Press the "Record" button on the audio daughterboard to take a photo.
"""
import time
import os
from displayio import (
Bitmap,
Group,
TileGrid,
FourWire,
release_displays,
ColorConverter,
Colorspace,
)
from adafruit_st7789 import ST7789
import board
import busio
import digitalio
import sdcardio
import storage
import adafruit_ov2640
release_displays()
# Set up the display (You must customize this block for your display!)
spi = busio.SPI(clock=board.GP2, MOSI=board.GP3, MISO=board.GP4)
# setup the SD Card
sd_cs = board.GP5
sdcard = sdcardio.SDCard(spi, sd_cs)
vfs = storage.VfsFat(sdcard)
storage.mount(vfs, "/sd")
# setup the button
button = digitalio.DigitalInOut(board.GP22)
button.pull = digitalio.Pull.UP
display_bus = FourWire(spi, command=board.GP0, chip_select=board.GP1, reset=None)
display = ST7789(display_bus, width=320, height=240, rotation=270)
display.auto_refresh = False
# Ensure the camera is shut down, so that it releases the SDA/SCL lines,
# then create the configuration I2C bus
with digitalio.DigitalInOut(board.GP10) as reset:
reset.switch_to_output(False)
time.sleep(0.001)
bus = busio.I2C(board.GP9, board.GP8)
# Set up the camera (you must customize this for your board!)
cam = adafruit_ov2640.OV2640(
bus,
data_pins=[
board.GP12,
board.GP13,
board.GP14,
board.GP15,
board.GP16,
board.GP17,
board.GP18,
board.GP19,
], # [16] [org] etc
clock=board.GP11, # [15] [blk]
vsync=board.GP7, # [10] [brn]
href=board.GP21, # [27/o14] [red]
mclk=board.GP20, # [16/o15]
shutdown=None,
reset=board.GP10,
) # [14]
width = display.width
height = display.height
cam.size = adafruit_ov2640.OV2640_SIZE_QQVGA
# cam.test_pattern = True
bitmap = Bitmap(cam.width, cam.height, 65536)
print(width, height, cam.width, cam.height)
if bitmap is None:
raise SystemExit("Could not allocate a bitmap")
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)
)
g.append(tg)
display.root_group = g
display.auto_refresh = False
def exists(filename):
try:
os.stat(filename)
return True
except OSError:
return False
_image_counter = 0
def open_next_image():
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") # pylint: disable=consider-using-with
def capture_image():
old_size = cam.size
old_colorspace = cam.colorspace
try:
cam.size = adafruit_ov2640.OV2640_SIZE_QVGA
cam.colorspace = adafruit_ov2640.OV2640_COLOR_JPEG
b = bytearray(cam.capture_buffer_size)
jpeg = cam.capture(b)
print(f"Captured {len(jpeg)} bytes of jpeg data")
with open_next_image() as f:
f.write(jpeg)
finally:
cam.size = old_size
cam.colorspace = old_colorspace
def main():
display.auto_refresh = False
while True:
if not button.value: # button pressed
capture_image()
cam.capture(bitmap)
bitmap.dirty()
display.refresh(minimum_frames_per_second=0)
main()

View file

@ -23,6 +23,7 @@ the test pattern!
import sys
import time
import digitalio
import busio
import board

View file

@ -1,3 +0,0 @@
# SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense

View file

@ -1,45 +1,6 @@
# SPDX-FileCopyrightText: 2022 Alec Delaney for Adafruit Industries
# SPDX-FileCopyrightText: 2020 Diego Elio Pettenò
#
# SPDX-License-Identifier: MIT
# SPDX-License-Identifier: Unlicense
[build-system]
requires = [
"setuptools",
"wheel",
"setuptools-scm",
]
[project]
name = "adafruit-circuitpython-ov2640"
description = "CircuitPython driver for OV2640 cameras"
version = "0.0.0+auto.0"
readme = "README.rst"
authors = [
{name = "Adafruit Industries", email = "circuitpython@adafruit.com"}
]
urls = {Homepage = "https://github.com/adafruit/Adafruit_CircuitPython_OV2640"}
keywords = [
"adafruit",
"ov2640",
"camera",
"breakout",
"hardware",
"micropythoncircuitpython",
]
license = {text = "MIT"}
classifiers = [
"Intended Audience :: Developers",
"Topic :: Software Development :: Libraries",
"Topic :: Software Development :: Embedded Systems",
"Topic :: System :: Hardware",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
]
dynamic = ["dependencies", "optional-dependencies"]
[tool.setuptools]
py-modules = ["adafruit_ov2640"]
[tool.setuptools.dynamic]
dependencies = {file = ["requirements.txt"]}
optional-dependencies = {optional = {file = ["optional_requirements.txt"]}}
[tool.black]
target-version = ['py35']

View file

@ -1,6 +1,7 @@
# SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries
# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
# SPDX-FileCopyrightText: Copyright (c) 2021 Jeff Epler for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense
# SPDX-License-Identifier: MIT
Adafruit-Blinka
adafruit-circuitpython-busdevice

9
setup.py.disabled Normal file
View file

@ -0,0 +1,9 @@
# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
# SPDX-FileCopyrightText: Copyright (c) 2021 Jeff Epler for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
This library is not deployed to PyPI. It is either a board-specific helper library, or
does not make sense for use on or is incompatible with single board computers and Linux.
"""