diff --git a/.gitignore b/.gitignore index 70fdffe40..59aa40e4c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ cores/arduino/api/ cores/arduino/api* **/.vscode/ **/.development +.DS_Store diff --git a/libraries/Camera/README.md b/libraries/Camera/README.md new file mode 100644 index 000000000..8316af355 --- /dev/null +++ b/libraries/Camera/README.md @@ -0,0 +1,5 @@ +# 📹 Library for Arduino Supported Cameras + +The Arduino camera library captures pixels from supported cameras on Arduino boards and stores them in a buffer for continuous retrieval and processing. + +📖 For more information about this library please read the documentation [here](./docs/). diff --git a/libraries/Camera/docs/README.md b/libraries/Camera/docs/README.md new file mode 100644 index 000000000..55d95e398 --- /dev/null +++ b/libraries/Camera/docs/README.md @@ -0,0 +1,61 @@ +# Arduino Camera Library + +[![License](https://img.shields.io/badge/License-LGPLv3-blue.svg)](https://github.com/arduino/ArduinoCore-mbed/blob/master/libraries/Camera/LICENSE) + +The Arduino camera library is a C++ library designed to capture pixels from cameras on supported Arduino products. It is currently compatible with three camera models: + +- OmniVision OV7670 (On the [Arduino Giga R1](https://docs.arduino.cc/hardware/giga-r1-wifi)) +- Himax HM01B0 & HM0360 (On the [Arduino Portenta Vision Shield](https://docs.arduino.cc/hardware/portenta-vision-shield)) +- Galaxy Core GC2145 (On the [Arduino Nicla Vision](https://docs.arduino.cc/hardware/nicla-vision)) + +This library captures pixels and stores them in a frame buffer. The frames can then be retrieved continuously for processing. + +The library provides methods to initialize the camera, capture frames, and access the pixels in the frame buffer. It supports various configuration options, such as setting the resolution and format of the captured frames. Please note that not all configurations are available for all cameras. The Himax camera for example only supports grayscale. + +## Features + +- Captures pixels and stores them in a frame buffer +- Store frame buffer on external RAM +- Frames can be retrieved continuously for processing +- Supports configuration options such as resolution and format +- Motion detection callback on supported camera (Himax HM0360) +- Simulated optical zoom on supported camera (GC2145) + + +## Usage + +To use this library, you must have a supported Arduino board and camera module. Once you have connected the camera to the board, you can include the camera library in your Arduino sketch and use its functions to capture frames and access the pixels. Here is a minimal example for the Arduino Nicla Vision: + +```cpp +#include "camera.h" +#include "gc2145.h" + +GC2145 galaxyCore; +Camera cam(galaxyCore); +FrameBuffer fb; + +void setup() { + cam.begin(CAMERA_R320x240, CAMERA_RGB565, 30); +} + +void loop() { + if (cam.grabFrame(fb, 3000) == 0) { + // Do something with the frame, e.g. send it over serial + Serial.write(fb.getBuffer(), cam.frameSize()); + } +} +``` +## Examples + +- **CameraRawBytes:** This example demonstrates how to capture raw bytes from the camera and display them on the computer by using a [Processing](https://processing.org/download) sketch. It uses UART to communicate with Processing and the `Camera` library to capture and retrieve the raw bytes. +The Processing sketch can be found [here](../extras/CameraRawBytesVisualizer/CameraRawBytesVisualizer.pde). +- **MotionDetection:** This example shows how to use the camera to detect motion in the captured frames on the camera. If motion is detected, a callback function is executed in an interrupt context. +- **GigaCamera:** This example demonstrates how to use the camera on the Arduino Giga R1 to capture images and display them on an attached LCD display that is driven by a ST7701 controller. + +## API + +The API documentation can be found [here](./api.md). + +## License + +This library is released under the [LGPLv3 license](https://github.com/arduino/ArduinoCore-mbed/blob/master/libraries/Camera/LICENSE). diff --git a/libraries/Camera/docs/api.md b/libraries/Camera/docs/api.md new file mode 100644 index 000000000..65b0aa08f --- /dev/null +++ b/libraries/Camera/docs/api.md @@ -0,0 +1,547 @@ +# Summary + + Members | Descriptions +--------------------------------|--------------------------------------------- +`class` [`Camera`](#class-camera) | The main class for controlling a camera using the provided [ImageSensor](#class-imagesensor). +`class` [`FrameBuffer`](#class-framebuffer) | Frame buffer class for storing captured frames. +`class` [`ImageSensor`](#class-imagesensor) | Abstract base class for image sensor drivers. +`class` [`ScanResults`](#class-scanresults) | A template class used to store the results from an I2C scan. + +# class `Camera` + +The main class for controlling a camera using the provided [ImageSensor](#). + +## Summary + + Members | Descriptions +--------------------------------|--------------------------------------------- +`public ` [`Camera`](#)`(`[`ImageSensor`](#)` & sensor)` | Pointer to the frame buffer. +`public bool` [`begin`](#)`(int32_t resolution,int32_t pixformat,int32_t framerate)` | Initialize the camera. +`public int` [`getID`](#)`()` | Get the I2C address of the image sensor. +`public int` [`setFrameRate`](#)`(int32_t framerate)` | Set the frame rate of the image sensor. +`public int` [`setResolution`](#)`(int32_t resolution)` | Set the resolution of the image sensor. +`public int` [`setPixelFormat`](#)`(int32_t pixelformat)` | Set the pixel (color) format of the image sensor. +`public int` [`setStandby`](#)`(bool enable)` | Set the sensor in standby mode. +`public int` [`setTestPattern`](#)`(bool enable,bool walking)` | Set the test pattern mode for the sensor. +`public int` [`frameSize`](#)`()` | Get the frame size. This is the number of bytes in a frame as determined by the resolution and pixel format. +`public int` [`grabFrame`](#)`(`[`FrameBuffer`](#)` & fb,uint32_t timeout)` | Capture a frame. +`public int` [`enableMotionDetection`](#)`(`[`md_callback_t`](#)` callback)` | Enable motion detection with the specified callback. +`public int` [`disableMotionDetection`](#)`()` | Disable motion detection. +`public int` [`setMotionDetectionWindow`](#)`(uint32_t x,uint32_t y,uint32_t w,uint32_t h)` | Set the motion detection window. +`public int` [`setMotionDetectionThreshold`](#)`(uint32_t threshold)` | Set the motion detection threshold. On the Himax HM01B0, the recommended threshold range is 3 - 240 (0x03 to 0xF0). +`public int` [`motionDetected`](#)`()` | Check if motion was detected and clear the motion detection flag. +`public void` [`debug`](#)`(Stream & stream)` | Output debug information to a stream. You can use this function to output debug information to the serial port by passing Serial as the stream. + +## Members + +### `public ` [`Camera`](#)`(`[`ImageSensor`](#)` & sensor)` + +Pointer to the frame buffer. + +Construct a new [Camera](#) object. + +#### Parameters +* `sensor` Reference to the [ImageSensor](#) object that is the driver for the camera sensor. + +### `public bool` [`begin`](#)`(int32_t resolution,int32_t pixformat,int32_t framerate)` + +Initialize the camera. + +#### Parameters +* `resolution` Initial resolution (default: CAMERA_R320x240). Note that not all resolutions are supported by all cameras. + +* `pixformat` Initial pixel format (default: CAMERA_GRAYSCALE). Note that not all pixel formats are supported by all cameras. + +* `framerate` Initial frame rate (default: 30) + +#### Returns +true If the camera is successfully initialized + +#### Returns +false Otherwise + +### `public int` [`getID`](#)`()` + +Get the I2C address of the image sensor. + +#### Returns +int The sensor ID + +### `public int` [`setFrameRate`](#)`(int32_t framerate)` + +Set the frame rate of the image sensor. + +This has no effect on cameras that do not support variable frame rates. + +#### Parameters +* `framerate` The desired frame rate in frames per second + +#### Returns +int 0 on success, non-zero on failure + +### `public int` [`setResolution`](#)`(int32_t resolution)` + +Set the resolution of the image sensor. + +This has no effect on cameras that do not support variable resolutions. + +#### Parameters +* `resolution` The desired resolution, as defined in the resolution enum + +#### Returns +int 0 on success, non-zero on failure + +### `public int` [`setPixelFormat`](#)`(int32_t pixelformat)` + +Set the pixel (color) format of the image sensor. + +This has no effect on cameras that do not support variable pixel formats. e.g. the Himax HM01B0 only supports grayscale. + +#### Parameters +* `pixelformat` The desired pixel format, as defined in the pixel format enum + +#### Returns +int 0 on success, non-zero on failure + +### `public int` [`setStandby`](#)`(bool enable)` + +Set the sensor in standby mode. + +This has no effect on cameras that do not support standby mode. + +#### Parameters +* `enable` true to enable standby mode, false to disable + +#### Returns +int 0 on success, non-zero on failure (or not implemented) + +### `public int` [`setTestPattern`](#)`(bool enable,bool walking)` + +Set the test pattern mode for the sensor. + +#### Parameters +* `enable` true to enable test pattern, false to disable + +* `walking` true for walking test pattern, false for other test pattern (if supported) The walking test pattern alternates between black and white pixels which is useful for detecting stuck-at faults + +#### Returns +int 0 on success, non-zero on failure (or not implemented) + +### `public int` [`frameSize`](#)`()` + +Get the frame size. This is the number of bytes in a frame as determined by the resolution and pixel format. + +#### Returns +int The frame size in bytes + +### `public int` [`grabFrame`](#)`(`[`FrameBuffer`](#)` & fb,uint32_t timeout)` + +Capture a frame. + +#### Parameters +* `fb` Reference to a [FrameBuffer](#) object to store the frame data + +* `timeout` Time in milliseconds to wait for a frame (default: 5000) + +#### Returns +int 0 if successful, non-zero otherwise + +### `public int` [`enableMotionDetection`](#)`(`[`md_callback_t`](#)` callback)` + +Enable motion detection with the specified callback. + +This has no effect on cameras that do not support motion detection. + +#### Parameters +* `callback` Function to be called when motion is detected + +#### Returns +int 0 on success, non-zero on failure + +### `public int` [`disableMotionDetection`](#)`()` + +Disable motion detection. + +#### Returns +int 0 on success, non-zero on failure + +### `public int` [`setMotionDetectionWindow`](#)`(uint32_t x,uint32_t y,uint32_t w,uint32_t h)` + +Set the motion detection window. + +#### Parameters +* `x` The x-coordinate of the window origin + +* `y` The y-coordinate of the window origin + +* `w` The width of the window + +* `h` The height of the window + +#### Returns +int 0 on success, non-zero on failure + +### `public int` [`setMotionDetectionThreshold`](#)`(uint32_t threshold)` + +Set the motion detection threshold. On the Himax HM01B0, the recommended threshold range is 3 - 240 (0x03 to 0xF0). + +#### Parameters +* `threshold` The motion detection threshold + +#### Returns +int 0 on success, non-zero on failure + +### `public int` [`motionDetected`](#)`()` + +Check if motion was detected and clear the motion detection flag. + +This function must be called after the motion detection callback was executed to clear the motion detection flag. + +#### Returns +int 0 if no motion is detected, non-zero if motion is detected + +### `public void` [`debug`](#)`(Stream & stream)` + +Output debug information to a stream. You can use this function to output debug information to the serial port by passing Serial as the stream. + +#### Parameters +* `stream` Stream to output the debug information + +# class `FrameBuffer` + +Frame buffer class for storing captured frames. + +## Summary + + Members | Descriptions +--------------------------------|--------------------------------------------- +`public ` [`FrameBuffer`](#)`(int32_t x,int32_t y,int32_t bpp)` | Flag indicating if the buffer is allocated on the heap. +`public ` [`FrameBuffer`](#)`(int32_t address)` | Construct a new [FrameBuffer](#) object with a given address. This is useful if a resolution higher than 320x240 is required and external RAM shall be used. In thast case, the following code can be used: +`public ` [`FrameBuffer`](#)`()` | Construct a new [FrameBuffer](#) object with no custom size or address. +`public uint32_t` [`getBufferSize`](#)`()` | Get the buffer size in bytes. +`public uint8_t *` [`getBuffer`](#)`()` | Get a pointer to the frame buffer. This can be used to read the pixels from the frame buffer. E.g. to print all pixels to the serial monitor as hex values: +`public void` [`setBuffer`](#)`(uint8_t * buffer)` | Set the frame buffer pointer. +`public bool` [`hasFixedSize`](#)`()` | Check if the frame buffer has a fixed size. This is the case if the frame buffer is constructed with a width, height, and bits per pixel. +`public bool` [`isAllocated`](#)`()` | Check if the frame buffer is allocated on the heap. + +## Members + +### `public ` [`FrameBuffer`](#)`(int32_t x,int32_t y,int32_t bpp)` + +Flag indicating if the buffer is allocated on the heap. + +Construct a new [FrameBuffer](#) object with a fixed size. + +#### Parameters +* `x` Width of the frame buffer + +* `y` Height of the frame buffer + +* `bpp` Bits per pixel + +### `public ` [`FrameBuffer`](#)`(int32_t address)` + +Construct a new [FrameBuffer](#) object with a given address. This is useful if a resolution higher than 320x240 is required and external RAM shall be used. In thast case, the following code can be used: + +```cpp +#include "SDRAM.h" +[FrameBuffer](#) fb(SDRAM_START_ADDRESS); +... +// In setup() add: +SDRAM.begin(); +``` + +#### Parameters +* `address` The memory address of the frame buffer + +### `public ` [`FrameBuffer`](#)`()` + +Construct a new [FrameBuffer](#) object with no custom size or address. + +### `public uint32_t` [`getBufferSize`](#)`()` + +Get the buffer size in bytes. + +#### Returns +uint32_t The buffer size in bytes + +### `public uint8_t *` [`getBuffer`](#)`()` + +Get a pointer to the frame buffer. This can be used to read the pixels from the frame buffer. E.g. to print all pixels to the serial monitor as hex values: + +```cpp +uint8_t *fb = fb.getBuffer(); +for (int i = 0; i < fb.getBufferSize(); i++) { + Serial.print(fb[i], HEX); + Serial.print(" "); +} +``` + +#### Returns +uint8_t* Pointer to the frame buffer + +### `public void` [`setBuffer`](#)`(uint8_t * buffer)` + +Set the frame buffer pointer. + +#### Parameters +* `buffer` Pointer to the frame buffer + +### `public bool` [`hasFixedSize`](#)`()` + +Check if the frame buffer has a fixed size. This is the case if the frame buffer is constructed with a width, height, and bits per pixel. + +#### Returns +true If the frame buffer has a fixed size + +#### Returns +false Otherwise + +### `public bool` [`isAllocated`](#)`()` + +Check if the frame buffer is allocated on the heap. + +#### Returns +true If the frame buffer is allocated + +#### Returns +false Otherwise + +# class `ImageSensor` + +Abstract base class for image sensor drivers. + +## Summary + + Members | Descriptions +--------------------------------|--------------------------------------------- +`public inline virtual ` [`~ImageSensor`](#)`()` | +`public int` [`init`](#)`()` | Initialize the image sensor. This prepares the sensor for capturing frames by setting the registers to their default values. +`public int` [`reset`](#)`()` | Reset the image sensor. This is useful if the sensor is stuck. +`public int` [`getID`](#)`()` | Get the I2C address of the image sensor. +`public bool` [`getMono`](#)`()` | Check if the image sensor is monochrome aka grayscale. +`public uint32_t` [`getClockFrequency`](#)`()` | Get the clock frequency of the image sensor. +`public int` [`setFrameRate`](#)`(int32_t framerate)` | Set the frame rate of the image sensor. +`public int` [`setResolution`](#)`(int32_t resolution)` | Set the resolution of the image sensor. +`public int` [`setPixelFormat`](#)`(int32_t pixelformat)` | Set the pixel (color) format of the image sensor. +`public int` [`enableMotionDetection`](#)`(`[`md_callback_t`](#)` callback)` | Enable motion detection with the specified callback. +`public int` [`disableMotionDetection`](#)`()` | Disable motion detection. +`public int` [`setMotionDetectionWindow`](#)`(uint32_t x,uint32_t y,uint32_t w,uint32_t h)` | Set the motion detection window. +`public int` [`setMotionDetectionThreshold`](#)`(uint32_t threshold)` | Set the motion detection threshold. On the Himax HM01B0, the recommended threshold range is 3 - 240 (0x03 to 0xF0). +`public int` [`motionDetected`](#)`()` | Check if motion was detected and clear the motion detection flag. +`public void` [`debug`](#)`(Stream & stream)` | Output debug information to a stream. You can use this function to output debug information to the serial port by passing Serial as the stream. +`public inline virtual int` [`setStandby`](#)`(bool enable)` | Set the sensor in standby mode. +`public inline virtual int` [`setTestPattern`](#)`(bool enable,bool walking)` | Set the test pattern mode for the sensor. + +## Members + +### `public inline virtual ` [`~ImageSensor`](#)`()` + +### `public int` [`init`](#)`()` + +Initialize the image sensor. This prepares the sensor for capturing frames by setting the registers to their default values. + +#### Returns +int 0 on success, non-zero on failure + +### `public int` [`reset`](#)`()` + +Reset the image sensor. This is useful if the sensor is stuck. + +#### Returns +int 0 on success, non-zero on failure + +### `public int` [`getID`](#)`()` + +Get the I2C address of the image sensor. + +#### Returns +int The sensor ID + +### `public bool` [`getMono`](#)`()` + +Check if the image sensor is monochrome aka grayscale. + +#### Returns +true If the sensor is monochrome + +#### Returns +false Otherwise + +### `public uint32_t` [`getClockFrequency`](#)`()` + +Get the clock frequency of the image sensor. + +#### Returns +uint32_t The clock frequency in Hz + +### `public int` [`setFrameRate`](#)`(int32_t framerate)` + +Set the frame rate of the image sensor. + +This has no effect on cameras that do not support variable frame rates. + +#### Parameters +* `framerate` The desired frame rate in frames per second + +#### Returns +int 0 on success, non-zero on failure + +### `public int` [`setResolution`](#)`(int32_t resolution)` + +Set the resolution of the image sensor. + +This has no effect on cameras that do not support variable resolutions. + +#### Parameters +* `resolution` The desired resolution, as defined in the resolution enum + +#### Returns +int 0 on success, non-zero on failure + +### `public int` [`setPixelFormat`](#)`(int32_t pixelformat)` + +Set the pixel (color) format of the image sensor. + +This has no effect on cameras that do not support variable pixel formats. e.g. the Himax HM01B0 only supports grayscale. + +#### Parameters +* `pixelformat` The desired pixel format, as defined in the pixel format enum + +#### Returns +int 0 on success, non-zero on failure + +### `public int` [`enableMotionDetection`](#)`(`[`md_callback_t`](#)` callback)` + +Enable motion detection with the specified callback. + +This has no effect on cameras that do not support motion detection. + +#### Parameters +* `callback` Function to be called when motion is detected + +#### Returns +int 0 on success, non-zero on failure + +### `public int` [`disableMotionDetection`](#)`()` + +Disable motion detection. + +#### Returns +int 0 on success, non-zero on failure + +### `public int` [`setMotionDetectionWindow`](#)`(uint32_t x,uint32_t y,uint32_t w,uint32_t h)` + +Set the motion detection window. + +#### Parameters +* `x` The x-coordinate of the window origin + +* `y` The y-coordinate of the window origin + +* `w` The width of the window + +* `h` The height of the window + +#### Returns +int 0 on success, non-zero on failure + +### `public int` [`setMotionDetectionThreshold`](#)`(uint32_t threshold)` + +Set the motion detection threshold. On the Himax HM01B0, the recommended threshold range is 3 - 240 (0x03 to 0xF0). + +#### Parameters +* `threshold` The motion detection threshold + +#### Returns +int 0 on success, non-zero on failure + +### `public int` [`motionDetected`](#)`()` + +Check if motion was detected and clear the motion detection flag. + +This function must be called after the motion detection callback was executed to clear the motion detection flag. + +#### Returns +int 0 if no motion is detected, non-zero if motion is detected + +### `public void` [`debug`](#)`(Stream & stream)` + +Output debug information to a stream. You can use this function to output debug information to the serial port by passing Serial as the stream. + +#### Parameters +* `stream` Stream to output the debug information + +### `public inline virtual int` [`setStandby`](#)`(bool enable)` + +Set the sensor in standby mode. + +This has no effect on cameras that do not support standby mode. + +#### Parameters +* `enable` true to enable standby mode, false to disable + +#### Returns +int 0 on success, non-zero on failure (or not implemented) + +### `public inline virtual int` [`setTestPattern`](#)`(bool enable,bool walking)` + +Set the test pattern mode for the sensor. + +#### Parameters +* `enable` true to enable test pattern, false to disable + +* `walking` true for walking test pattern, false for other test pattern (if supported) The walking test pattern alternates between black and white pixels which is useful for detecting stuck-at faults + +#### Returns +int 0 on success, non-zero on failure (or not implemented) + +# class `ScanResults` + +A template class used to store the results from an I2C scan. + +#### Parameters +* `T` Data type for the device address (e.g. uint8_t) + +## Summary + + Members | Descriptions +--------------------------------|--------------------------------------------- +`public inline bool` [`operator==`](#)`(T toBeFound)` | Equality operator to check if a given device address is found in the [ScanResults](#). +`public inline bool` [`operator!=`](#)`(T toBeFound)` | Inequality operator to check if a given device address is not found in the [ScanResults](#). +`public inline void` [`push`](#)`(T obj)` | Add a device address to the [ScanResults](#). + +## Members + +### `public inline bool` [`operator==`](#)`(T toBeFound)` + +Equality operator to check if a given device address is found in the [ScanResults](#). + +#### Parameters +* `toBeFound` The device address to be checked + +#### Returns +true If the device address is found in the [ScanResults](#) + +#### Returns +false Otherwise + +### `public inline bool` [`operator!=`](#)`(T toBeFound)` + +Inequality operator to check if a given device address is not found in the [ScanResults](#). + +#### Parameters +* `toBeFound` The device address to be checked + +#### Returns +true If the device address is not found in the [ScanResults](#) + +#### Returns +false Otherwise + +### `public inline void` [`push`](#)`(T obj)` + +Add a device address to the [ScanResults](#). + +#### Parameters +* `obj` The device address to be added diff --git a/libraries/Camera/library.properties b/libraries/Camera/library.properties index 3d53c641b..6f348878b 100644 --- a/libraries/Camera/library.properties +++ b/libraries/Camera/library.properties @@ -2,8 +2,8 @@ name=Camera version=1.0 author=Arduino maintainer=Arduino -sentence=Camera library for Portenta H7 Vision Shield -paragraph= -category=Other +sentence=Library to capture pixels from supported cameras on Arduino boards. +paragraph=The Arduino camera library is a C++ library designed to capture frames from cameras on supported Arduino products. It is currently compatible with three camera models, namely OV7670, Himax HM0360, Himax HM01B0, and GC2145. This library captures pixels and stores them in a frame buffer. The frames can then be retrieved continuously for processing. +category=Device Control url=https://github.com/arduino/ArduinoCore-mbed/tree/master/libraries/Camera -architectures=mbed,mbed_portenta,mbed_nicla +architectures=mbed,mbed_portenta,mbed_nicla,mbed_giga diff --git a/libraries/Camera/src/camera.cpp b/libraries/Camera/src/camera.cpp index bada5a802..82a679755 100644 --- a/libraries/Camera/src/camera.cpp +++ b/libraries/Camera/src/camera.cpp @@ -124,19 +124,21 @@ static TIM_HandleTypeDef htim = {0}; static DMA_HandleTypeDef hdma = {0}; static DCMI_HandleTypeDef hdcmi = {0}; +/// Table to store the amount of bytes per pixel for each pixel format const uint32_t pixtab[CAMERA_PMAX] = { - 1, - 1, - 2, + 1, // CAMERA_GRAYSCALE + 1, // CAMERA_BAYER + 2, // CAMERA_RGB565 }; +/// Table to store the resolution width and height for each resolution const uint32_t restab[CAMERA_RMAX][2] = { - {160, 120 }, - {320, 240 }, + {160, 120 }, // QQVGA + {320, 240 }, // QVGA {320, 320 }, - {640, 480 }, - {800, 600 }, - {1600, 1200}, + {640, 480 }, // VGA + {800, 600 }, // SVGA + {1600, 1200}, // UXGA }; extern "C" { diff --git a/libraries/Camera/src/camera.h b/libraries/Camera/src/camera.h index 182d9041d..1e5872daa 100644 --- a/libraries/Camera/src/camera.h +++ b/libraries/Camera/src/camera.h @@ -14,23 +14,39 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * - * Camera driver. */ + +/** + * @file Camera.h + * @brief Header file for the Arduino Camera library. + * + * This library allows capturing pixels from supported cameras on Arduino products. + * Supported cameras: OV7670, HM0360, HM01B0, and GC2145. + * The pixels are returned in a frame buffer from which frames can be retrieved continuously. + */ + #ifndef __CAMERA_H #define __CAMERA_H #include "Wire.h" +/// Camera I2C addresses #define HM01B0_I2C_ADDR (0x24) #define HM0360_I2C_ADDR (0x24) #define GC2145_I2C_ADDR (0x3C) +/** + * Camera pixel format enumeration + * The different formats use different number of bits per pixel: + * Grayscale (8-bit), Bayer (8-bit), RGB565 (16-bit) + **/ enum { CAMERA_GRAYSCALE = 0, CAMERA_BAYER = 1, CAMERA_RGB565 = 2, - CAMERA_PMAX + CAMERA_PMAX /* Sentinel value */ }; +/// Camera resolution enumeration enum { CAMERA_R160x120 = 0, /* QQVGA Resolution */ CAMERA_R320x240 = 1, /* QVGA Resolution */ @@ -38,59 +54,279 @@ enum { CAMERA_R640x480 = 3, /* VGA */ CAMERA_R800x600 = 5, /* SVGA */ CAMERA_R1600x1200 = 6, /* UXGA */ - CAMERA_RMAX + CAMERA_RMAX /* Sentinel value */ }; // Resolution table extern const uint32_t restab[CAMERA_RMAX][2]; + +/** + * @class FrameBuffer + * @brief Frame buffer class for storing captured frames. + */ class FrameBuffer { private: - int32_t _fb_size; - uint8_t *_fb; - bool _isAllocated; + int32_t _fb_size; /// Frame buffer size in bytes + uint8_t *_fb; /// Pointer to the frame buffer + bool _isAllocated; /// Flag indicating if the buffer is allocated on the heap + public: + /** + * @brief Construct a new FrameBuffer object with a fixed size. + * + * @param x Width of the frame buffer + * @param y Height of the frame buffer + * @param bpp Bits per pixel + */ FrameBuffer(int32_t x, int32_t y, int32_t bpp); + + /** + * @brief Construct a new FrameBuffer object with a given address. + * This is useful if a resolution higher than 320x240 is required and external RAM shall be used. + * In that case, the following code can be used: + * + * @code {.cpp} + * #include "SDRAM.h" + * FrameBuffer fb(SDRAM_START_ADDRESS); + * ... + * // In setup() add: + * SDRAM.begin(); + * @endcode + * It can also be used to store the frame buffer in a different location in the RAM while avoiding the use of malloc(). + * @param address The memory address of the frame buffer + */ FrameBuffer(int32_t address); + + /** + * @brief Construct a new FrameBuffer object with no custom size or address. + */ FrameBuffer(); + + /** + * @brief Get the buffer size in bytes. + * + * @return uint32_t The buffer size in bytes + */ uint32_t getBufferSize(); + + /** + * @brief Get a pointer to the frame buffer. + * This can be used to read the pixels from the frame buffer. + * E.g. to print all pixels to the serial monitor as hex values: + * @code {.cpp} + * uint8_t *fb = fb.getBuffer(); + * for (int i = 0; i < fb.getBufferSize(); i++) { + * Serial.print(fb[i], HEX); + * Serial.print(" "); + * } + * @endcode + * @return uint8_t* Pointer to the frame buffer + */ uint8_t* getBuffer(); + + /** + * @brief Set the frame buffer pointer. + * + * @param buffer Pointer to the frame buffer + */ void setBuffer(uint8_t *buffer); + + /** + * @brief Check if the frame buffer has a fixed size. + * This is the case if the frame buffer is constructed with a width, height, and bits per pixel. + * @return true If the frame buffer has a fixed size + * @return false Otherwise + */ bool hasFixedSize(); + + /** + * @brief Check if the frame buffer is allocated on the heap. + * + * @return true If the frame buffer is allocated + * @return false Otherwise + */ bool isAllocated(); }; +/// Function type definition for motion detection callbacks typedef void (*md_callback_t)(); + +/** + * @class ImageSensor + * @brief Abstract base class for image sensor drivers. + */ class ImageSensor { public: virtual ~ImageSensor() { } + + /** + * @brief Initialize the image sensor. + * This prepares the sensor for capturing frames by setting the registers to their default values. + * @return int 0 on success, non-zero on failure + */ virtual int init() = 0; + + /** + * @brief Reset the image sensor. + * The effect differs between the camera models. + * It has no effect on the GC2145. On the OV7670, it resets all configuration registers to the default values. + * On HM0360 and HM01B0 the registers are reset to the defaults. The camera also enters Standby Mode, where there is no video output and the power consumption is lowered. + * @return int 0 on success, non-zero on failure + */ virtual int reset() = 0; + + /** + * @brief Get the I2C address of the image sensor. + * @return int The sensor ID + */ virtual int getID() = 0; + + /** + * @brief Check if the image sensor is monochrome aka grayscale. + * + * @return true If the sensor is monochrome + * @return false Otherwise + */ virtual bool getMono() = 0; + + /** + * @brief Get the clock frequency of the image sensor. + * + * @return uint32_t The clock frequency in Hz + */ virtual uint32_t getClockFrequency() = 0; + + /** + * @brief Set the frame rate of the image sensor. + * @note This has no effect on cameras that do not support variable frame rates. + * @param framerate The desired frame rate in frames per second + * @return int 0 on success, non-zero on failure + */ virtual int setFrameRate(int32_t framerate) = 0; + + /** + * @brief Set the resolution of the image sensor. + * @note This has no effect on cameras that do not support variable resolutions. + * @param resolution The desired resolution, as defined in the resolution enum + * @return int 0 on success, non-zero on failure + */ virtual int setResolution(int32_t resolution) = 0; + + /** + * @brief Set the pixel (color) format of the image sensor. + * @note This has no effect on cameras that do not support variable pixel formats. + * e.g. the Himax HM01B0 only supports grayscale. + * @param pixelformat The desired pixel format, as defined in the pixel format enum + * @return int 0 on success, non-zero on failure + */ virtual int setPixelFormat(int32_t pixelformat) = 0; + + /** + * @brief Enable motion detection with the specified callback. + * + * @note This has no effect on cameras that do not support motion detection. + * Currently only the Himax HM01B0 and HM0360 support motion detection. + * @note This has no effect on cameras that do not support motion detection. + * @param callback Function to be called when motion is detected + * @return int 0 on success, non-zero on failure + */ virtual int enableMotionDetection(md_callback_t callback) = 0; + + /** + * @brief Disable motion detection. + * + * @note This has no effect on cameras that do not support motion detection. + * Currently only the Himax HM01B0 and HM0360 support motion detection. + * @return int 0 on success, non-zero on failure + */ virtual int disableMotionDetection() = 0; + + /** + * @brief Set the motion detection window. + * + * @note This has no effect on cameras that do not support motion detection. + * Currently only the Himax HM01B0 and HM0360 support motion detection. + * @param x The x-coordinate of the window origin + * @param y The y-coordinate of the window origin + * @param w The width of the window + * @param h The height of the window + * @return int 0 on success, non-zero on failure + */ virtual int setMotionDetectionWindow(uint32_t x, uint32_t y, uint32_t w, uint32_t h) = 0; + + /** + * @brief Set the motion detection threshold. + * + * @note This has no effect on cameras that do not support motion detection. + * Currently only the Himax HM01B0 and HM0360 support motion detection. + * On the Himax HM01B0, the recommended threshold range is 3 - 240 (0x03 to 0xF0). + * @param threshold The motion detection threshold + * @return int 0 on success, non-zero on failure + */ virtual int setMotionDetectionThreshold(uint32_t threshold) = 0; + + /** + * @brief Check if motion was detected and clear the motion detection flag. + * + * @note This has no effect on cameras that do not support motion detection. + * Currently only the Himax HM01B0 and HM0360 support motion detection. + * @note This function must be called after the motion detection callback was executed to clear the motion detection flag. + * @return int 0 if no motion is detected, non-zero if motion is detected + */ virtual int motionDetected() = 0; + + /** + * @brief Output debug information to a stream. + * You can use this function to output debug information to the serial port by passing Serial as the stream. + * @param stream Stream to output the debug information + */ virtual void debug(Stream &stream) = 0; + /** + * @brief Set the sensor in standby mode. + * @note This has no effect on cameras that do not support standby mode. + * @note None of the currently supported camera drivers implement this function. + * The HM01B0 and HM0360 cameras can be set in standby mode by calling reset() instead. + * @param enable true to enable standby mode, false to disable + * @return int 0 on success, non-zero on failure (or not implemented) + */ virtual int setStandby(bool enable) { return -1; } + /** + * @brief Set the test pattern mode for the sensor. + * + * @note This has no effect on cameras that do not support test pattern mode. + * @param enable true to enable test pattern, false to disable + * @param walking true for walking test pattern, false for other test pattern (if supported) + * The walking test pattern alternates between black and white pixels which is useful for detecting stuck-at faults + * @return int 0 on success, non-zero on failure (or not implemented) + */ virtual int setTestPattern(bool enable, bool walking) { return -1; } }; -template class ScanResults { + +/** + * @class ScanResults + * @brief A template class used to store the results from an I2C scan. + * + * @param T Data type for the device address (e.g. uint8_t) + */ +template +class ScanResults { public: + /** + * @brief Equality operator to check if a given device address is found in the ScanResults. + * + * @param toBeFound The device address to be checked + * @return true If the device address is found in the ScanResults + * @return false Otherwise + */ bool operator==(T toBeFound) { for (int i = 0; i < howMany; i++) { if (toBeFound == data[i]) { @@ -99,47 +335,203 @@ template class ScanResults { } return false; } + + /** + * @brief Inequality operator to check if a given device address is not found in the ScanResults. + * + * @param toBeFound The device address to be checked + * @return true If the device address is not found in the ScanResults + * @return false Otherwise + */ bool operator!=(T toBeFound) { return !(*this == toBeFound); } + + /** + * @brief Add a device address to the ScanResults. + * + * @param obj The device address to be added + */ void push(T obj) { data[howMany++] = obj; } + private: - T data[20]; - int howMany = 0; + T data[20]; /// Array to store the device addresses (max 20). + int howMany = 0; /// Number of device addresses in the ScanResults }; + +/** + * @class Camera + * @brief The main class for controlling a camera using the provided ImageSensor. + */ class Camera { private: - int32_t pixformat; - int32_t resolution; - int32_t framerate; - ImageSensor *sensor; - int reset(); - ScanResults i2cScan(); - Stream *_debug; - arduino::MbedI2C *_i2c; - FrameBuffer *_framebuffer; + int32_t pixformat; /// Pixel format + int32_t resolution; /// Camera resolution + int32_t framerate; /// Frame rate + ImageSensor *sensor; /// Pointer to the camera sensor + int reset(); /// Reset the camera + ScanResults i2cScan(); /// Perform an I2C scan + Stream *_debug; /// Pointer to the debug stream + arduino::MbedI2C *_i2c; /// Pointer to the I2C interface + FrameBuffer *_framebuffer; /// Pointer to the frame buffer + public: + /** + * @brief Construct a new Camera object. + * + * @param sensor Reference to the ImageSensor object that is the driver for the camera sensor. + */ Camera(ImageSensor &sensor); + + /** + * @brief Initialize the camera. + * + * @param resolution Initial resolution (default: CAMERA_R320x240). Note that not all resolutions are supported by all cameras. + * @param pixformat Initial pixel format (default: CAMERA_GRAYSCALE). Note that not all pixel formats are supported by all cameras. + * @param framerate Initial frame rate (default: 30) + * @return true If the camera is successfully initialized + * @return false Otherwise + */ bool begin(int32_t resolution=CAMERA_R320x240, int32_t pixformat=CAMERA_GRAYSCALE, int32_t framerate=30); + + /** + * @brief Get the I2C address of the image sensor. + * @return int The sensor ID + */ int getID(); + + /** + * @brief Set the frame rate of the image sensor. + * + * @note This has no effect on cameras that do not support variable frame rates. + * @param framerate The desired frame rate in frames per second + * @return int 0 on success, non-zero on failure + */ int setFrameRate(int32_t framerate); + + /** + * @brief Set the resolution of the image sensor. + * + * @note This has no effect on cameras that do not support variable resolutions. + * @param resolution The desired resolution, as defined in the resolution enum + * @return int 0 on success, non-zero on failure + */ int setResolution(int32_t resolution); + + /** + * @brief Set the pixel (color) format of the image sensor. + * + * @note This has no effect on cameras that do not support variable pixel formats. + * e.g. the Himax HM01B0 only supports grayscale. + * @param pixelformat The desired pixel format, as defined in the pixel format enum + * @return int 0 on success, non-zero on failure + */ int setPixelFormat(int32_t pixelformat); + + /** + * @brief Set the sensor in standby mode. + * + * @note This has no effect on cameras that do not support standby mode. + * @note None of the currently supported camera drivers implement this function. + * The HM01B0 and HM0360 cameras can be set in standby mode by calling reset() instead. + * @param enable true to enable standby mode, false to disable + * @return int 0 on success, non-zero on failure (or not implemented) + */ int setStandby(bool enable); + + /** + * @brief Set the test pattern mode for the sensor. + * + * @note This has no effect on cameras that do not support test pattern mode. + * @param enable true to enable test pattern, false to disable + * @param walking true for walking test pattern, false for other test pattern (if supported) + * The walking test pattern alternates between black and white pixels which is useful for detecting stuck-at faults + * @return int 0 on success, non-zero on failure (or not implemented) + */ int setTestPattern(bool enable, bool walking); + + /** + * @brief Get the frame size. This is the number of bytes in a frame as determined by the resolution and pixel format. + * + * @return int The frame size in bytes + */ int frameSize(); + + /** + * @brief Capture a frame. + * + * @param fb Reference to a FrameBuffer object to store the frame data + * @param timeout Time in milliseconds to wait for a frame (default: 5000) + * @return int 0 if successful, non-zero otherwise + */ int grabFrame(FrameBuffer &fb, uint32_t timeout=5000); + + /** + * @brief Enable motion detection with the specified callback. + * + * @note This has no effect on cameras that do not support motion detection. + * Currently only the Himax HM01B0 and HM0360 support motion detection. + * @param callback Function to be called when motion is detected + * @return int 0 on success, non-zero on failure + */ int enableMotionDetection(md_callback_t callback=NULL); + + /** + * @brief Disable motion detection. + * + * @note This has no effect on cameras that do not support motion detection. + * Currently only the Himax HM01B0 and HM0360 support motion detection. + * @return int 0 on success, non-zero on failure + */ int disableMotionDetection(); + + /** + * @brief Set the motion detection window. + * + * @note This has no effect on cameras that do not support motion detection. + * Currently only the Himax HM01B0 and HM0360 support motion detection. + * @param x The x-coordinate of the window origin + * @param y The y-coordinate of the window origin + * @param w The width of the window + * @param h The height of the window + * @return int 0 on success, non-zero on failure + */ int setMotionDetectionWindow(uint32_t x, uint32_t y, uint32_t w, uint32_t h); + + /** + * @brief Set the motion detection threshold. + * + * @note This has no effect on cameras that do not support motion detection. + * Currently only the Himax HM01B0 and HM0360 support motion detection. + * On the Himax HM01B0, the recommended threshold range is 3 - 240 (0x03 to 0xF0). + * @param threshold The motion detection threshold + * @return int 0 on success, non-zero on failure + */ int setMotionDetectionThreshold(uint32_t threshold); + + /** + * @brief Check if motion was detected and clear the motion detection flag. + * + * @note This has no effect on cameras that do not support motion detection. + * Currently only the Himax HM01B0 and HM0360 support motion detection. + * @note This function must be called after the motion detection callback was executed to clear the motion detection flag. + * @return int 0 if no motion is detected, non-zero if motion is detected + */ int motionDetected(); + + /** + * @brief Output debug information to a stream. + * You can use this function to output debug information to the serial port by passing Serial as the stream. + * @param stream Stream to output the debug information + */ void debug(Stream &stream); + }; #endif // __CAMERA_H +/// The I2C bus used to communicate with the camera extern arduino::MbedI2C CameraWire;