Skip to content

Commit

Permalink
First release candidate (#4)
Browse files Browse the repository at this point in the history
* Improvements in ESP-HAL
* Minor improvements and update readme. Release candidate.
  • Loading branch information
cnadler86 authored Oct 7, 2024
1 parent 1230d79 commit f0d05ed
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 59 deletions.
6 changes: 4 additions & 2 deletions .github/workflows/ESP32.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
name: Build MicroPython Camera for ESP32 Boards
name: ESP32

on:
workflow_dispatch:
push:
paths:
- 'src/**'
pull_request:
branches:
- master
Expand Down Expand Up @@ -138,4 +140,4 @@ jobs:
with:
name: firmware-${{ matrix.board }}
path: ~/${{ matrix.board }}.bin
retention-days: 1
retention-days: 90
100 changes: 53 additions & 47 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,55 @@
# Camera API for micropython.
# Camera API for micropython
[![ESP32 Port](https://github.com/cnadler86/micropython-camera-API/actions/workflows/ESP32.yml/badge.svg)](https://github.com/cnadler86/micropython-camera-API/actions/workflows/ESP32.yml)

This project aims to support cameras in different ports in micropython, starting with the ESP32-Port and omnivision (OV2640 & OV5640) cameras. The project implements a general API for cameras in micropython (such as circuitpython have done).
At the moment, this is a micropython user module, but it might get in the micropython repo in the future.
The API is stable, but it might change without previous anounce.

## Precomiled FW (the easy way) (work in progress)
If you are not familiar with building a custom firmware, you can go to the actions tab and start the build workflow by yourself. Then, you can download one of the generic FWs that suits your board.
## Precomiled FW (the easy way)
If you are not familiar with building a custom firmware, you can go to the [releases](https://github.com/cnadler86/micropython-camera-API/releases) page and download one of the generic FWs that suits your board.

## Using the API
```python
from camera import Camera, GrabMode, PixelFormat, FrameSize, GainCeiling

# Camera construction and initialization
camera = Camera(
data_pins=[1,2,3,4,5,6,7,8],
vsync_pin=9,
href_pin=10,
sda_pin=11,
scl_pin=12,
pclk_pin=13,
xclk_pin=14,
xclk_freq=20000000,
powerdown_pin=-1,
reset_pin=-1,
pixel_format=PixelFormat.RGB565,
frame_size=FrameSize.QVGA,
jpeg_quality=15,
fb_count=1,
grab_mode=GrabMode.WHEN_EMPTY
)

#Camera construction using defaults (if you specified them in mpconfigboard.h)
camera = Camera()

# Capture image
img = camera.capture()

# Camera reconfiguration
camera.reconfigure(pixel_format=PixelFormat.JPEG,frame_size=FrameSize.QVGA,grab_mode=GrabMode.LATEST, fb_count=2)
camera.set_quality(10)
```

You can get and set sensor properties by the respective methods (e.g. camera.get_brightness() or camera.set_vflip(True). See autocompletitions in Thonny in order to see the list of methods.
If you want more insides 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).
Notice that for the methods in here you need to prefix a get/set, depending that you want to do.

## Setup build environment (the DIY way)
## Build your custom FW
### Setup build environment (the DIY way)
To build the project, follow the following instructions:
- [ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/v5.2.2/esp32/get-started/index.html): I used version 5.2.2, but it might work with other versions.
- [ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/v5.2.3/esp32/get-started/index.html): I used version 5.2.2, but it might work with other versions (see notes).
- Clone the micropython repo and this repo in a folder, e.g. "MyESPCam". I used the actual micropython master branch (between v1.23 and before 1.24).
- You will have to add the ESP32-Camera driver (I used v2.0.12). To do this, add the following to the respective idf_component.yml file (e.g. in micropython/ports/esp32/main_esp32s3/idf_component.yml):
```
Expand All @@ -17,7 +58,7 @@ To build the project, follow the following instructions:
```
You can also clone the https://github.com/espressif/esp32-camera repository inside the esp-idf/components folder instead of altering the idf_component.yml file.

## Add camera configurations to your board (Optional, but recomended)
### Add camera configurations to your board (Optional, but recomended)
To make things easier, add the following lines to your board config-file "mpconfigboard.h" with the respective pins and camera parameters. Otherwise you will need to pass all parameters during construction.
Don't forget the empty line at the buttom.
Example for xiao sense:
Expand Down Expand Up @@ -46,52 +87,17 @@ Example for xiao sense:
```

## Build the API
### Build the API
To build the project, you could do it the following way:

```bash
$ . <path2esp-idf>/esp-idf/export.sh
$ cd MyESPCam/micropython/ports/esp32
$ make USER_C_MODULES=../../../../mp_camera/src/micropython.cmake BOARD=<Your-Board> clean
$ make USER_C_MODULES=../../../../mp_camera/src/micropython.cmake BOARD=<Your-Board> submodules
$ make USER_C_MODULES=../../../../mp_camera/src/micropython.cmake BOARD=<Your-Board> all
$ make USER_C_MODULES=../../../../micropython-camera-API/src/micropython.cmake BOARD=<Your-Board> clean
$ make USER_C_MODULES=../../../../micropython-camera-API/src/micropython.cmake BOARD=<Your-Board> submodules
$ make USER_C_MODULES=../../../../micropython-camera-API/src/micropython.cmake BOARD=<Your-Board> all
```
if you experience problems, visit [MicroPython external C modules](https://docs.micropython.org/en/latest/develop/cmodules.html).

## Using the API
```python
from camera import Camera, GrabMode, PixelFormat, FrameSize, GainCeiling

# Camera construction and initialization
camera = Camera(
data_pins=[1,2,3,4,5,6,7,8],
vsync_pin=9,
href_pin=10,
sda_pin=11,
scl_pin=12,
pclk_pin=13,
xclk_pin=14,
xclk_freq=20000000,
powerdown_pin=-1,
reset_pin=-1,
pixel_format=PixelFormat.RGB565,
frame_size=FrameSize.QVGA,
jpeg_quality=15,
fb_count=1,
grab_mode=GrabMode.WHEN_EMPTY
)

#Camera construction using defaults (if you specified them in mpconfigboard.h)
camera = Camera()

# Capture image
img = camera.capture()

# Camera reconfiguration
camera.reconfigure(pixel_format=PixelFormat.JPEG,frame_size=FrameSize.QVGA,grab_mode=GrabMode.LATEST, fb_count=2)
camera.set_quality(10)
```

You can get and set sensor properties by the respective methods (e.g. camera.get_brightness() or camera.set_vflip(True). See autocompletitions in Thonny in order to see the list of methods.
If you want more insides 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).
Notice that for the methods in here you need to prefix a get/set, depending that you want to do.
## Notes
If your target board is a ESP32, I recomend using IDF >= 5.2, since older versions may lead to IRAM overflow during build. Alternatively you can modify your sdkconfig-file (see [issue #1](https://github.com/cnadler86/micropython-camera-API/issues/1)).
20 changes: 10 additions & 10 deletions src/modcamera.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,43 +31,42 @@

#define TAG "ESP32_MPY_CAMERA"

#if !CONFIG_SPIRAM //TODO: Better test if enought RAM is available on runtime?
#if !CONFIG_SPIRAM
#error Camera only works on boards configured with spiram
#endif

//TODO: improve error message
void raise_micropython_error_from_esp_err(esp_err_t err) {
switch (err) {
case ESP_OK:
return;

case ESP_ERR_NO_MEM:
mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("ESP_ERR_NO_MEM: Out of memory"));
mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Out of memory"));
break;

case ESP_ERR_INVALID_ARG:
mp_raise_ValueError(MP_ERROR_TEXT("ESP_ERR_INVALID_ARG: Invalid argument"));
mp_raise_ValueError(MP_ERROR_TEXT("Invalid argument"));
break;

case ESP_ERR_INVALID_STATE:
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("ESP_ERR_INVALID_STATE: Invalid state"));
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Invalid state"));
break;

case ESP_ERR_NOT_FOUND:
mp_raise_OSError(MP_ENOENT);
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Camera not found"));
break;

case ESP_ERR_NOT_SUPPORTED:
mp_raise_NotImplementedError(MP_ERROR_TEXT("ESP_ERR_NOT_SUPPORTED: Operation not supported"));
mp_raise_NotImplementedError(MP_ERROR_TEXT("Operation/Function not supported/implemented"));
break;

case ESP_ERR_TIMEOUT:
mp_raise_OSError(MP_ETIMEDOUT);
break;

default:
// mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("ESP_ERR: Unknown error 0x%04x"), err);
mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("ESP_ERR: Unknown error"));
mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("Unknown error 0x%04x"), err);
// mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("Unknown error"));
break;
}
}
Expand Down Expand Up @@ -151,6 +150,7 @@ void mp_camera_hal_deinit(mp_camera_obj_t *self) {
esp_err_t err = esp_camera_deinit();
raise_micropython_error_from_esp_err(err);
self->initialized = false;
ESP_LOGI(TAG, "Camera deinitialized");
}
}

Expand Down Expand Up @@ -290,7 +290,7 @@ const mp_rom_map_elem_t mp_camera_hal_gainceiling_table[] = {
{ MP_ROM_QSTR(MP_QSTR_128X), MP_ROM_INT(GAINCEILING_128X) },
};

//OPEN: Makros with convertion function, since the API will use standarized values.
//TODO: Makros with convertion function, since the API will use standarized values.
// Helper functions to get and set camera and sensor information
#define SENSOR_STATUS_GETSET_IN_RANGE(type, name, status_field_name, setter_function_name, min_val, max_val) \
SENSOR_GETSET_IN_RANGE(type, name, status.status_field_name, setter_function_name, min_val, max_val)
Expand Down

0 comments on commit f0d05ed

Please sign in to comment.