Skip to content

Commit 16b2bfb

Browse files
committed
Refactors camera API examples and documentation to use properties over get/set methods
Enhances typings for better autocompletion. Fixed missing acamera module Fixed missing sentinel in property setter
1 parent 3495aa7 commit 16b2bfb

File tree

7 files changed

+81
-28
lines changed

7 files changed

+81
-28
lines changed

README.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,14 @@ The following keyword arguments have default values:
130130

131131
### Initializing the camera
132132

133+
The camera initializes when constructing the camera object per default. If you set init=False during construction, you need to call the init method manually:
134+
133135
```python
134136
cam.init()
135137
```
136138

139+
Note that most of the camera seeting can only be set or aquired after initialization.
140+
137141
### Capture image
138142

139143
The general way of capturing an image is calling the `capture` method:
@@ -192,14 +196,14 @@ This gives you the possibility of creating an asynchronous application without u
192196
Here are just a few examples:
193197

194198
```python
195-
cam.set_quality(90) # The quality goes from 0% to 100%, meaning 100% is the highest but has probably no compression
196-
camera.get_brightness()
197-
camera.set_vflip(True) #Enable vertical flip
199+
cam.quality = 90 # The quality goes from 0% to 100%, meaning 100% is the highest but has probably no compression
200+
print("cam.brightness =", cam.brightness)
201+
camera.vflip = True #Enable vertical flip
198202
```
199203

200204
See autocompletions in Thonny in order to see the list of methods.
201205
If you want more insights in the methods and what they actually do, you can find a very good documentation [here](https://docs.circuitpython.org/en/latest/shared-bindings/espcamera/index.html).
202-
Note that each method requires a "get_" or "set_" prefix, depending on the desired action.
206+
Note: "get_" and "set_" prefixed methods are deprecated.
203207

204208
Take also a look in the examples folder.
205209

@@ -233,7 +237,7 @@ print(f"I2C devices found: {devices}")
233237
i2c.writeto(0x42, b'\x00\x01') # Write to another device
234238

235239
# Camera sensor communication works too
236-
cam.set_saturation(1) # Uses the shared I2C bus
240+
cam.saturation = 1 # Uses the shared I2C bus
237241
```
238242

239243
#### Alternative: Camera Creates Its Own I2C (Default)

examples/CameraSettings.html

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,52 +3,91 @@
33
<head>
44
<title>Micropython Camera Stream</title>
55
<style>
6+
* {
7+
box-sizing: border-box;
8+
}
69
body {
710
display: flex;
811
flex-direction: column;
912
justify-content: flex-start;
1013
align-items: center;
11-
height: 100vh;
14+
min-height: 100vh;
1215
margin: 0;
1316
background-color: #f0f0f0;
1417
}
1518
.container {
1619
display: flex;
1720
flex-direction: row;
18-
height: 100%;
1921
width: 100%;
22+
flex: 1;
2023
}
2124
.settings-container {
2225
display: flex;
2326
flex-direction: column;
24-
padding: 20px;
27+
padding: 15px;
2528
background-color: #ffffff;
2629
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
27-
margin-top: 20px;
2830
width: 300px;
31+
overflow-y: auto;
2932
}
3033
.setting {
3134
margin-bottom: 10px;
3235
}
36+
.setting label {
37+
display: block;
38+
font-size: 14px;
39+
margin-bottom: 3px;
40+
}
41+
.setting input[type="range"],
42+
.setting select {
43+
width: 100%;
44+
}
3345
.hidden {
3446
display: none;
3547
}
3648
.video-container {
3749
display: flex;
3850
justify-content: center;
3951
align-items: center;
40-
width: 100%;
52+
flex: 1;
4153
padding: 20px;
54+
overflow: hidden;
4255
}
4356
img {
57+
max-width: 100%;
58+
max-height: 100%;
4459
width: auto;
45-
height: 100%;
60+
height: auto;
4661
}
4762
.title-container {
4863
width: 100%;
4964
text-align: center;
5065
background-color: #ffffff;
5166
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
67+
padding: 10px;
68+
}
69+
.title-container h1 {
70+
margin: 0;
71+
font-size: 1.5em;
72+
}
73+
@media (max-width: 768px) {
74+
.container {
75+
flex-direction: column;
76+
}
77+
.settings-container {
78+
width: 100%;
79+
max-height: 40vh;
80+
order: 2;
81+
}
82+
.video-container {
83+
width: 100%;
84+
min-height: 50vh;
85+
padding: 10px;
86+
order: 1;
87+
}
88+
.title-container h1 {
89+
font-size: 1.2em;
90+
}
5291
}
5392
</style>
5493
<script>

examples/CameraSettings.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,35 +56,39 @@ async def handle_client(reader, writer):
5656
elif 'GET /set_' in request:
5757
method_name = request.split('GET /set_')[1].split('?')[0]
5858
value = int(request.split('value=')[1].split(' ')[0])
59-
set_method = getattr(cam, f'set_{method_name}', None)
60-
if callable(set_method):
59+
try:
60+
# Use property assignment instead of method call
61+
setattr(cam, method_name, value)
6162
print(f"setting {method_name} to {value}")
62-
set_method(value)
6363
response = 'HTTP/1.1 200 OK\r\n\r\n'
6464
writer.write(response.encode())
6565
await writer.drain()
66-
else:
66+
except (AttributeError, ValueError) as e:
67+
# Fallback to reconfigure if property doesn't exist
6768
try:
6869
cam.reconfigure(**{method_name: value})
6970
print(f"Camera reconfigured with {method_name}={value}")
7071
print("This action restores all previous configuration!")
7172
response = 'HTTP/1.1 200 OK\r\n\r\n'
72-
except Exception as e:
73-
print(f"Error with {method_name}: {e}")
73+
writer.write(response.encode())
74+
await writer.drain()
75+
except Exception as e2:
76+
print(f"Error setting {method_name}: {e2}")
7477
response = 'HTTP/1.1 404 Not Found\r\n\r\n'
75-
writer.write(response.encode())
76-
await writer.drain()
78+
writer.write(response.encode())
79+
await writer.drain()
7780

7881
elif 'GET /get_' in request:
7982
method_name = request.split('GET /get_')[1].split(' ')[0]
80-
get_method = getattr(cam, f'get_{method_name}', None)
81-
if callable(get_method):
82-
value = get_method()
83+
try:
84+
# Use property access instead of method call
85+
value = getattr(cam, method_name)
8386
print(f"{method_name} is {value}")
8487
response = f'HTTP/1.1 200 OK\r\n\r\n{value}'
8588
writer.write(response.encode())
8689
await writer.drain()
87-
else:
90+
except AttributeError as e:
91+
print(f"Error getting {method_name}: {e}")
8892
response = 'HTTP/1.1 404 Not Found\r\n\r\n'
8993
writer.write(response.encode())
9094
await writer.drain()

manifest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Include the board's default manifest.
22
include("$(PORT_DIR)/boards/manifest.py")
33
# Add custom driver
4-
module("src/acamera.py")
4+
module("acamera.py", base_path="src")

micropython.cmake

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ target_sources(usermod_mp_camera INTERFACE
1212
idf_component_get_property(camera_dir esp32-camera COMPONENT_DIR)
1313
target_include_directories(usermod_mp_camera INTERFACE
1414
${camera_dir}/driver/include
15-
${camera_dir}/driver/private_include
16-
${camera_dir}/sensors/private_include
1715
)
1816

1917
# # Set MP_CAMERA_DRIVER_VERSION if available

src/modcamera_api.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,10 @@ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_camera___exit___obj, 4, 4, mp_came
298298
// Property handler
299299
static void camera_obj_property(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
300300
mp_camera_obj_t *self = MP_OBJ_TO_PTR(self_in);
301-
301+
if (self->initialized == false) {
302+
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Camera not initialized"));
303+
}
304+
302305
if (dest[0] == MP_OBJ_NULL) {
303306
// Load (reading)
304307
switch (attr) {
@@ -501,6 +504,8 @@ static void camera_obj_property(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
501504
mp_camera_hal_set_lenc(self, mp_obj_is_true(dest[1]));
502505
break;
503506
default:
507+
// Indicate that the attribute was not found
508+
dest[1] = MP_OBJ_SENTINEL;
504509
return;
505510
}
506511
dest[0] = MP_OBJ_NULL;

typings/acamera.pyi

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
from camera import Camera as _Camera
2+
from camera import FrameSize, PixelFormat, GainCeiling, GrabMode
23

34
class Camera(_Camera):
45
async def acapture(self) -> memoryview:
56
"""Asynchronously capture a frame and return it as a memoryview.
67
78
Yields control back to the event loop while waiting for a frame to become available.
89
"""
9-
...
10+
...
11+
12+
__all__ = ['Camera', 'FrameSize', 'PixelFormat', 'GainCeiling', 'GrabMode']

0 commit comments

Comments
 (0)