Compare commits

..

No commits in common. "main" and "enhance" have entirely different histories.

30 changed files with 354 additions and 418 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

@ -4,39 +4,37 @@
repos:
- repo: https://github.com/python/black
rev: 23.3.0
rev: 20.8b1
hooks:
- id: black
- repo: https://github.com/fsfe/reuse-tool
rev: v1.1.2
rev: v0.12.1
hooks:
- id: reuse
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
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
rev: pylint-2.7.1
hooks:
- 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
@ -69,16 +69,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
@ -1350,7 +1336,7 @@ class OV2640(_SCCBCameraBase): # pylint: disable=too-many-instance-attributes
self.test_pattern = self._test_pattern
@property
def exposure(self) -> int:
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)
@ -1359,7 +1345,7 @@ class OV2640(_SCCBCameraBase): # pylint: disable=too-many-instance-attributes
return aec_1_0 | (aec_9_2 << 2) | (aec_15_10 << 10)
@exposure.setter
def exposure(self, exposure: int) -> None:
def exposure(self, exposure):
aec_1_0 = exposure & 0x11
aec_9_2 = (exposure >> 2) & 0b11111111
aec_15_10 = exposure >> 10

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.
#
on_rtd = os.environ.get("READTHEDOCS", None) == "True"
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

@ -89,7 +89,7 @@ Preview images on LCD then save BMP images to SD on Kaluga 1.3 fitted with an il
Kaluga 1.3 with Adafruit IO
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Upload JPEG images to Adafruit IO. Requires that WIFI and Adafruit IO be configured in ``settings.toml``.
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

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

@ -11,7 +11,7 @@ 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
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
@ -21,38 +21,31 @@ base64 encoding. In practice, "SVGA" (800x600) images are typically around
"""
import binascii
import ssl
import time
from os import getenv
from secrets import secrets # pylint: disable=no-name-in-module
import board
import busio
import wifi
import adafruit_connection_manager
import socketpool
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)
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=aio_username,
password=aio_key,
username=secrets["aio_username"],
password=secrets["aio_key"],
socket_pool=pool,
ssl_context=ssl_context,
ssl_context=ssl.create_default_context(),
)
mqtt_client.connect()
io = IO_MQTT(mqtt_client)

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

@ -109,7 +109,7 @@ tg = displayio.TileGrid(
),
)
g.append(tg)
display.root_group = g
display.show(g)
sd_spi = busio.SPI(clock=board.IO18, MOSI=board.IO14, MISO=board.IO17)

View file

@ -62,7 +62,7 @@ tg = displayio.TileGrid(
),
)
g.append(tg)
display.root_group = g
display.show(g)
display.auto_refresh = False
while True:

View file

@ -67,7 +67,7 @@ tg = displayio.TileGrid(
),
)
g.append(tg)
display.root_group = g
display.show(g)
display.auto_refresh = False
while True:

View file

@ -65,7 +65,7 @@ tg = displayio.TileGrid(
),
)
g.append(tg)
display.root_group = g
display.show(g)
display.auto_refresh = False
while True:

View file

@ -76,7 +76,7 @@ tg = TileGrid(
bitmap, pixel_shader=ColorConverter(input_colorspace=Colorspace.BGR565_SWAPPED)
)
g.append(tg)
display.root_group = g
display.show(g)
display.auto_refresh = False
while True:

View file

@ -80,7 +80,7 @@ tg = displayio.TileGrid(
),
)
g.append(tg)
display.root_group = g
display.show(g)
display.auto_refresh = False

View file

@ -102,7 +102,7 @@ tg = TileGrid(
bitmap, pixel_shader=ColorConverter(input_colorspace=Colorspace.RGB565_SWAPPED)
)
g.append(tg)
display.root_group = g
display.show(g)
display.auto_refresh = False

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.
"""