Skip to content

Commit b28f7a1

Browse files
authored
Merge pull request #41 from iabdalkader/camera_library
Add camera library.
2 parents ab684ac + 0ddba96 commit b28f7a1

File tree

10 files changed

+549
-17
lines changed

10 files changed

+549
-17
lines changed

Diff for: libraries/Camera/README.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# 📹 Library for Arduino Supported Cameras
2+
3+
The Arduino camera library captures pixels from supported cameras on Arduino boards and stores them in a buffer for continuous retrieval and processing.
4+
5+
📖 For more information about this library please read the documentation [here](./docs/).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#include "camera.h"
2+
3+
Camera cam;
4+
5+
void fatal_error(const char *msg) {
6+
Serial.println(msg);
7+
pinMode(LED_BUILTIN, OUTPUT);
8+
while (1) {
9+
digitalWrite(LED_BUILTIN, HIGH);
10+
delay(100);
11+
digitalWrite(LED_BUILTIN, LOW);
12+
delay(100);
13+
}
14+
}
15+
16+
void setup(void) {
17+
Serial.begin(115200);
18+
if (!cam.begin(320, 240, CAMERA_RGB565)) {
19+
fatal_error("Camera begin failed");
20+
}
21+
cam.setVerticalFlip(false);
22+
cam.setHorizontalMirror(false);
23+
}
24+
25+
void loop() {
26+
FrameBuffer fb;
27+
if (cam.grabFrame(fb)) {
28+
if (Serial.read() == 1) {
29+
Serial.write(fb.getBuffer(), fb.getBufferSize());
30+
}
31+
cam.releaseFrame(fb);
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
This sketch reads a raw Stream of RGB565 pixels
3+
from the Serial port and displays the frame on
4+
the window.
5+
Use with the Examples -> CameraCaptureRawBytes Arduino sketch.
6+
This example code is in the public domain.
7+
*/
8+
9+
import processing.serial.*;
10+
import java.nio.ByteBuffer;
11+
import java.nio.ByteOrder;
12+
13+
Serial myPort;
14+
15+
// must match resolution used in the Arduino sketch
16+
final int cameraWidth = 320;
17+
final int cameraHeight = 240;
18+
19+
// Must match the image mode in the Arduino sketch
20+
boolean useGrayScale = false;
21+
22+
// Must match the baud rate in the Arduino sketch
23+
final int baudRate = 115200;
24+
25+
final int cameraBytesPerPixel = useGrayScale ? 1 : 2;
26+
final int cameraPixelCount = cameraWidth * cameraHeight;
27+
final int bytesPerFrame = cameraPixelCount * cameraBytesPerPixel;
28+
final int timeout = int((bytesPerFrame / float(baudRate / 10)) * 1000 * 2); // Twice the transfer rate
29+
30+
PImage myImage;
31+
byte[] frameBuffer = new byte[bytesPerFrame];
32+
int lastUpdate = 0;
33+
boolean shouldRedraw = false;
34+
35+
void setup() {
36+
size(640, 480);
37+
38+
// If you have only ONE serial port active you may use this:
39+
//myPort = new Serial(this, Serial.list()[0], baudRate); // if you have only ONE serial port active
40+
41+
// If you know the serial port name
42+
//myPort = new Serial(this, "COM5", baudRate); // Windows
43+
myPort = new Serial(this, "/dev/ttyACM0", baudRate); // Linux
44+
//myPort = new Serial(this, "/dev/cu.usbmodem14301", baudRate); // Mac
45+
46+
// wait for a full frame of bytes
47+
myPort.buffer(bytesPerFrame);
48+
49+
myImage = createImage(cameraWidth, cameraHeight, ALPHA);
50+
51+
// Let the Arduino sketch know we're ready to receive data
52+
myPort.write(1);
53+
}
54+
55+
void draw() {
56+
// Time out after a few seconds and ask for new data
57+
if(millis() - lastUpdate > timeout) {
58+
println("Connection timed out.");
59+
myPort.clear();
60+
myPort.write(1);
61+
}
62+
63+
if(shouldRedraw){
64+
PImage img = myImage.copy();
65+
img.resize(640, 480);
66+
image(img, 0, 0);
67+
shouldRedraw = false;
68+
}
69+
}
70+
71+
int[] convertRGB565ToRGB888(short pixelValue){
72+
//RGB565
73+
int r = (pixelValue >> (6+5)) & 0x01F;
74+
int g = (pixelValue >> 5) & 0x03F;
75+
int b = (pixelValue) & 0x01F;
76+
//RGB888 - amplify
77+
r <<= 3;
78+
g <<= 2;
79+
b <<= 3;
80+
return new int[]{r,g,b};
81+
}
82+
83+
void serialEvent(Serial myPort) {
84+
lastUpdate = millis();
85+
86+
// read the received bytes
87+
myPort.readBytes(frameBuffer);
88+
89+
// Access raw bytes via byte buffer
90+
ByteBuffer bb = ByteBuffer.wrap(frameBuffer);
91+
92+
// Ensure proper endianness of the data for > 8 bit values.
93+
// The 1 byte bb.get() function will always return the bytes in the correct order.
94+
bb.order(ByteOrder.BIG_ENDIAN);
95+
96+
int i = 0;
97+
98+
while (bb.hasRemaining()) {
99+
if(useGrayScale){
100+
// read 8-bit pixel data
101+
byte pixelValue = bb.get();
102+
103+
// set pixel color
104+
myImage.pixels[i++] = color(Byte.toUnsignedInt(pixelValue));
105+
} else {
106+
// read 16-bit pixel data
107+
int[] rgbValues = convertRGB565ToRGB888(bb.getShort());
108+
109+
// set pixel RGB color
110+
myImage.pixels[i++] = color(rgbValues[0], rgbValues[1], rgbValues[2]);
111+
}
112+
}
113+
114+
myImage.updatePixels();
115+
116+
// Ensures that the new image data is drawn in the next draw loop
117+
shouldRedraw = true;
118+
119+
// Let the Arduino sketch know we received all pixels
120+
// and are ready for the next frame
121+
myPort.write(1);
122+
}
123+
124+
void keyPressed() {
125+
if (key == ' ') {
126+
useGrayScale = !useGrayScale; // change boolean value of greyscale when space is pressed
127+
}
128+
}

Diff for: libraries/Camera/library.properties

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
name=Camera
2+
version=1.0
3+
author=Arduino
4+
maintainer=Arduino <[email protected]>
5+
sentence=Library to capture pixels from supported cameras on Arduino boards.
6+
paragraph=The Arduino camera library is a C++ library designed to capture frames from cameras on supported Arduino products.
7+
category=Device Control
8+
url=https://github.com/arduino/ArduinoCore-zephyr/tree/master/libraries/Camera
9+
architectures=zephyr,zephyr_portenta,zephyr_nicla,zephyr_giga

Diff for: libraries/Camera/src/camera.cpp

+170
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/*
2+
* Copyright 2025 Arduino SA
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU Lesser General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public License
15+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
*
17+
* Camera driver.
18+
*/
19+
#include "Arduino.h"
20+
#include "camera.h"
21+
22+
#include <zephyr/kernel.h>
23+
#include <zephyr/device.h>
24+
#include <zephyr/drivers/video.h>
25+
#include <zephyr/drivers/video-controls.h>
26+
27+
FrameBuffer::FrameBuffer() : vbuf(NULL) {
28+
29+
}
30+
31+
uint32_t FrameBuffer::getBufferSize() {
32+
if (this->vbuf) {
33+
return this->vbuf->bytesused;
34+
}
35+
}
36+
37+
uint8_t* FrameBuffer::getBuffer() {
38+
if (this->vbuf) {
39+
return this->vbuf->buffer;
40+
}
41+
}
42+
43+
Camera::Camera() : vdev(NULL), byte_swap(false), yuv_to_gray(false) {
44+
for (size_t i = 0; i < ARRAY_SIZE(this->vbuf); i++) {
45+
this->vbuf[i] = NULL;
46+
}
47+
}
48+
49+
bool Camera::begin(uint32_t width, uint32_t height, uint32_t pixformat, bool byte_swap) {
50+
#if DT_HAS_CHOSEN(zephyr_camera)
51+
this->vdev = DEVICE_DT_GET(DT_CHOSEN(zephyr_camera));
52+
#endif
53+
54+
if (!this->vdev || !device_is_ready(this->vdev)) {
55+
return false;
56+
}
57+
58+
switch (pixformat) {
59+
case CAMERA_RGB565:
60+
this->byte_swap = byte_swap;
61+
pixformat = VIDEO_PIX_FMT_RGB565;
62+
break;
63+
case CAMERA_GRAYSCALE:
64+
// There's no support for mono sensors.
65+
this->yuv_to_gray = true;
66+
pixformat = VIDEO_PIX_FMT_YUYV;
67+
break;
68+
default:
69+
break;
70+
}
71+
72+
// Get capabilities
73+
struct video_caps caps = {0};
74+
if (video_get_caps(this->vdev, VIDEO_EP_OUT, &caps)) {
75+
return false;
76+
}
77+
78+
for (size_t i=0; caps.format_caps[i].pixelformat != NULL; i++) {
79+
const struct video_format_cap *fcap = &caps.format_caps[i];
80+
if (fcap->width_min == width &&
81+
fcap->height_min == height &&
82+
fcap->pixelformat == pixformat) {
83+
break;
84+
}
85+
if (caps.format_caps[i+1].pixelformat == NULL) {
86+
Serial.println("The specified format is not supported");
87+
return false;
88+
}
89+
}
90+
91+
// Set format.
92+
static struct video_format fmt = {
93+
.pixelformat = pixformat,
94+
.width = width,
95+
.height = height,
96+
.pitch = width * 2,
97+
};
98+
99+
if (video_set_format(this->vdev, VIDEO_EP_OUT, &fmt)) {
100+
Serial.println("Failed to set video format");
101+
return false;
102+
}
103+
104+
// Allocate video buffers.
105+
for (size_t i = 0; i < ARRAY_SIZE(this->vbuf); i++) {
106+
this->vbuf[i] = video_buffer_aligned_alloc(fmt.pitch * fmt.height,
107+
CONFIG_VIDEO_BUFFER_POOL_ALIGN,
108+
K_FOREVER);
109+
if (this->vbuf[i] == NULL) {
110+
Serial.println("Failed to allocate video buffers");
111+
return false;
112+
}
113+
video_enqueue(this->vdev, VIDEO_EP_OUT, this->vbuf[i]);
114+
}
115+
116+
// Start video capture
117+
if (video_stream_start(this->vdev)) {
118+
Serial.println("Failed to start capture");
119+
return false;
120+
}
121+
122+
return true;
123+
}
124+
125+
bool Camera::grabFrame(FrameBuffer &fb, uint32_t timeout) {
126+
if (this->vdev == NULL) {
127+
return false;
128+
}
129+
130+
if (video_dequeue(this->vdev, VIDEO_EP_OUT, &fb.vbuf, K_MSEC(timeout))) {
131+
return false;
132+
}
133+
134+
if (this->byte_swap) {
135+
uint16_t *pixels = (uint16_t *) fb.vbuf->buffer;
136+
for (size_t i=0; i<fb.vbuf->bytesused / 2; i++) {
137+
pixels[i] = __REVSH(pixels[i]);
138+
}
139+
}
140+
141+
if (this->yuv_to_gray) {
142+
uint8_t *pixels = (uint8_t *) fb.vbuf->buffer;
143+
for (size_t i=0; i<fb.vbuf->bytesused / 2; i++) {
144+
pixels[i] = pixels[i*2];
145+
}
146+
fb.vbuf->bytesused /= 2;
147+
}
148+
149+
return true;
150+
}
151+
152+
bool Camera::releaseFrame(FrameBuffer &fb) {
153+
if (this->vdev == NULL) {
154+
return false;
155+
}
156+
157+
if (video_enqueue(this->vdev, VIDEO_EP_OUT, fb.vbuf)) {
158+
return false;
159+
}
160+
161+
return true;
162+
}
163+
164+
bool Camera::setVerticalFlip(bool flip_enable) {
165+
return video_set_ctrl(this->vdev, VIDEO_CID_VFLIP, (void *) flip_enable) == 0;
166+
}
167+
168+
bool Camera::setHorizontalMirror(bool mirror_enable) {
169+
return video_set_ctrl(this->vdev, VIDEO_CID_HFLIP, (void *) mirror_enable) == 0;
170+
}

0 commit comments

Comments
 (0)