Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 35 additions & 38 deletions .github/workflows/ESP32.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,46 +52,37 @@ jobs:
run: |
sudo apt-get update
sudo apt-get install -y git wget flex bison gperf python3 python3-pip python3-venv cmake ninja-build ccache libffi-dev libssl-dev dfu-util libusb-1.0-0


# Clone MicroPython (if not cached)
- name: Clone MicroPython
id: clone-micropython
if: steps.cache_esp_idf.outputs.cache-hit != 'true'
run: |
cd ~
if [ "${{ github.ref }}" == "refs/heads/master" ]; then
echo "Master branch detected - cloning MicroPython release: $MPY_RELEASE"
git clone --depth 1 --branch ${{ env.MPY_RELEASE }} https://github.com/micropython/micropython.git
else
echo "Development branch detected - cloning latest MicroPython master"
git clone --depth 1 https://github.com/micropython/micropython.git
fi
cd micropython
cd mpy-cross
make

echo "Micropython setup successfully"

# Download and set up ESP-IDF (if not cached)
- name: Set up ESP-IDF
id: export-idf
if: steps.cache_esp_idf.outputs.cache-hit != 'true'
run: |
cd ~
git clone --depth 1 --branch v5.4.2 https://github.com/espressif/esp-idf.git
# git clone --depth 1 --branch ${{ env.IDF_VER }} https://github.com/espressif/esp-idf.git
git clone --depth 1 --branch v5.5.1 https://github.com/espressif/esp-idf.git
git -C esp-idf submodule update --init --recursive --filter=tree:0
cd esp-idf
./install.sh all
cd components
# latest_cam_driver=$(curl -s https://api.github.com/repos/espressif/esp32-camera/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
# git clone --depth 1 --branch $latest_cam_driver https://github.com/espressif/esp32-camera.git
git clone https://github.com/cnadler86/esp32-camera.git
cd ~/esp-idf/
source ./export.sh
cd ~
git clone https://github.com/espressif/esp-adf-libs.git
cp -r ~/esp-adf-libs/esp_new_jpeg ~/esp-idf/components/

# Clone the latest MicroPython release (if not cached)
- name: Clone MicroPython latest release
id: clone-micropython
if: steps.cache_esp_idf.outputs.cache-hit != 'true'
run: |
echo "Cloning MicroPython release: $MPY_RELEASE"
cd ~/esp-idf/
source ./export.sh
cd ~
git clone --depth 1 --branch ${{ env.MPY_RELEASE }} https://github.com/micropython/micropython.git
cd micropython
# git submodule update --init --depth 1
cd mpy-cross
make
cd ~/micropython/ports/esp32
make submodules
echo "Micropython setup successfully"
source ~/micropython/tools/ci.sh && echo "IDF_VER=$IDF_VER" >> $GITHUB_ENV

# Dynamically create jobs for each board
build:
Expand Down Expand Up @@ -147,6 +138,8 @@ jobs:

- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive

- name: Install ESP-IDF dependencies
run: |
Expand All @@ -159,28 +152,32 @@ jobs:
cd ${{ github.workspace }}
cd ..
git clone https://github.com/cnadler86/mp_jpeg.git
cd ~/esp-idf/components/esp32-camera
CAM_DRIVER=$(git describe --tags --always --dirty)
# cd ~/esp32-camera
# CAM_DRIVER=$(git describe --tags --always --dirty)
cd ~/micropython/ports/esp32
source ~/esp-idf/export.sh

# Check if a variant is defined and adjust the idf.py command
IFS='@' read -r BUILD_TARGET CAMERA_MODEL <<< "${{ matrix.board }}"
IFS='-' read -r BOARD_NAME BOARD_VARIANT <<< "${BUILD_TARGET}"

# Build command step by step
IDF_CMD="idf.py -D MICROPY_BOARD=$BOARD_NAME -D USER_C_MODULES=${{ github.workspace }}/micropython.cmake -B build-$BUILD_TARGET -D EXTRA_COMPONENT_DIRS=${{ github.workspace }}"

if [ -n "${BOARD_VARIANT}" ]; then
IDF_CMD="idf.py -D MICROPY_BOARD=$BOARD_NAME -D USER_C_MODULES=${{ github.workspace }}/src/micropython.cmake -D MICROPY_BOARD_VARIANT=$BOARD_VARIANT -B build-$BUILD_TARGET -D MP_CAMERA_DRIVER_VERSION=$CAM_DRIVER"
else
IDF_CMD="idf.py -D MICROPY_BOARD=$BOARD_NAME -D USER_C_MODULES=${{ github.workspace }}/src/micropython.cmake -B build-$BUILD_TARGET -D MP_CAMERA_DRIVER_VERSION=$CAM_DRIVER"
IDF_CMD="${IDF_CMD} -D MICROPY_BOARD_VARIANT=$BOARD_VARIANT"
fi

if [ -n "${CAMERA_MODEL}" ]; then
echo "FW_NAME=${CAMERA_MODEL}" >> $GITHUB_ENV
FINAL_CMD="${IDF_CMD} -D MICROPY_CAMERA_MODEL=${CAMERA_MODEL} build"
IDF_CMD="${IDF_CMD} -D MICROPY_CAMERA_MODEL=${CAMERA_MODEL}"
else
echo "FW_NAME=${BUILD_TARGET}" >> $GITHUB_ENV
FINAL_CMD="${IDF_CMD} build"
fi
make USER_C_MODULES=${{ github.workspace }}/src/micropython.cmake BOARD=$BOARD_NAME submodules

FINAL_CMD="${IDF_CMD} build"

make BOARD=$BOARD_NAME submodules
echo "Running command: $FINAL_CMD"
eval $FINAL_CMD
cd ~/micropython/ports/esp32/build-${BUILD_TARGET}
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
#.vscode folder
.vscode/

# Build
/build/

# User-specific files
*.rsuser
*.suo
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "esp32-camera"]
path = esp32-camera
url = https://github.com/cnadler86/esp32-camera.git
7 changes: 7 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# IDF component wrapper for micropython-camera-API user module
# This allows the IDF component manager to process idf_component.yml
# The actual MicroPython module is built via micropython.cmake

idf_component_register(
REQUIRES esp32-camera
)
53 changes: 42 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ If you want to play arround with AI, take a look at the [micropython binding for
- [Freeing the buffer](#freeing-the-buffer)
- [Is a frame available](#is-frame-available)
- [Additional methods](#additional-methods)
- [I2C Integration](#i2c-integration)
- [Additional information](#additional-information)
- [Build your custom firmware](#build-your-custom-firmware)
- [Setting up the build environment (DIY method)](#setting-up-the-build-environment-diy-method)
Expand Down Expand Up @@ -209,6 +210,42 @@ import camera
vers = camera.Version()
```

### I2C Integration

The camera uses I2C (SCCB protocol) to communicate with the camera sensor. You can share this I2C bus with other devices by passing an external I2C object (SoftI2C not supported) to the camera:

#### Sharing I2C with Camera

```python
import machine

# Create your own I2C object first
i2c = machine.I2C(0, scl=22, sda=21, freq=400000)

# Pass it to the camera (no need for sda_pin/scl_pin)
cam = camera.Camera(i2c=i2c, data_pins=..., pclk_pin=..., ...)

# The same I2C object can be used for other devices on the same bus!
devices = i2c.scan()
print(f"I2C devices found: {devices}")

# You can communicate with other I2C devices while camera is running
i2c.writeto(0x42, b'\x00\x01') # Write to another device

# Camera sensor communication works too
cam.set_saturation(1) # Uses the shared I2C bus
```

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

```python
# Camera creates and manages its own I2C internally
cam = camera.Camera(sda_pin=21, scl_pin=22, ...)

# In this mode, you cannot share I2C with other devices
# Use the first method if you need to share I2C
```

### Additional information

The firmware images support the following cameras out of the box, but is therefore big: OV7670, OV7725, OV2640, OV3660, OV5640, NT99141, GC2145, GC032A, GC0308, BF3005, BF20A6, SC030IOT
Expand All @@ -221,21 +258,19 @@ To build the project, follow these instructions:

- [ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/v5.2.3/esp32/get-started/index.html): I used version 5.2.3, but it might work with other versions (see notes).
- Clone the micropython repo and this repo in a folder, e.g. "MyESPCam". MicroPython version 1.24 or higher is required (at least commit 92484d8).
- You will have to add the ESP32-Camera driver from my fork. To do this, add the following to the respective idf_component.yml file (e.g. in micropython/ports/esp32/main_esp32s3/idf_component.yml):
- You will have to add the ESP32-Camera driver. To do this, add the following to the respective idf_component.yml file (e.g. in micropython/ports/esp32/main/idf_component.yml):

```yml
espressif/esp32-camera:
git: https://github.com/cnadler86/esp32-camera.git
```

Alternatively, you can clone the <https://github.com/cnadler86/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 recommended)

#### Supported camera models

This project supports various boards with camera interface out of the box. You typically only need to add a single line to your board config file ("mpconfigboard.h).
Example (don't forget to add the empty line at the bottom):
Example:

```c
#define MICROPY_CAMERA_MODEL_WROVER_KIT 1
Expand Down Expand Up @@ -303,17 +338,13 @@ If you also want to include the [mp_jpeg module](https://github.com/cnadler86/mp

### Build the API

To build the project, you could do it the following way:
To build the project, just use the buils script with the path to your micropython folder:

```bash
. <path2esp-idf>/esp-idf/export.sh
cd MyESPCam/micropython/ports/esp32
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
./build.sh -m path/to/micropython -b ESP32_GENERIC_S3
```

Micropython and camera-api folders are at the same level. Note that you need those extra "/../"s while been inside the esp32 port folder.
Use `./build.sh -h` to see all available options.
If you experience problems, visit [MicroPython external C modules](https://docs.micropython.org/en/latest/develop/cmodules.html).

## Notes
Expand Down
160 changes: 160 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
#!/bin/bash
set -e

# Default values
IDF_PATH_DEFAULT="$HOME/esp/esp-idf"
MICROPYTHON_PATH=""
IDF_PATH="$IDF_PATH_DEFAULT"
BOARD="ESP32_GENERIC_S3"
BOARD_VARIANT=""
CAMERA_MODEL=""
BUILD_DIR="build-mp_camera"

# Parse arguments
usage() {
echo "Usage: $0 <micropython_path> [-i <idf_path>] [-b <board>] [-v <board_variant>] [-c <camera_model>]"
echo ""
echo "Arguments:"
echo " <path> Path to MicroPython directory (required)"
echo ""
echo "Options:"
echo " -i <path> Path to ESP-IDF directory (optional, default: $IDF_PATH_DEFAULT)"
echo " -b <board> Board name (optional, e.g. ESP32_GENERIC_S3, default: $BOARD)"
echo " -v <variant> Board variant (optional, e.g. SPIRAM_OCT)"
echo " -c <model> Camera model (optional, e.g. FREENOVE_ESP32S3_CAM)"
echo " -h Show this help message"
echo ""
echo "Examples:"
echo " $0 ~/privat/micropython"
echo " $0 ~/privat/micropython -i ~/esp/esp-idf"
echo " $0 ~/privat/micropython -b ESP32_GENERIC_S3"
echo " $0 ~/privat/micropython -b ESP32_GENERIC_S3 -v SPIRAM_OCT"
echo " $0 ~/privat/micropython -b ESP32_GENERIC_S3 -c FREENOVE_ESP32S3_CAM"
exit 1
}

# Check required arguments first
if [ -z "$1" ]; then
echo "Error: MicroPython path is required as first argument"
echo ""
usage
fi

MICROPYTHON_PATH="$1"
shift

# Parse command line options
while getopts "i:b:v:c:h" opt; do
case $opt in
i) IDF_PATH="$OPTARG" ;;
b) BOARD="$OPTARG" ;;
v) BOARD_VARIANT="$OPTARG" ;;
c) CAMERA_MODEL="$OPTARG" ;;
h) usage ;;
*) usage ;;
esac
done

# Validate paths
if [ ! -d "$MICROPYTHON_PATH" ]; then
echo "Error: MicroPython directory not found: $MICROPYTHON_PATH"
exit 1
fi

if [ ! -d "$IDF_PATH" ]; then
echo "Error: ESP-IDF directory not found: $IDF_PATH"
exit 1
fi

if [ ! -f "$IDF_PATH/export.sh" ]; then
echo "Error: ESP-IDF export.sh not found in: $IDF_PATH"
exit 1
fi

# Get absolute paths
MICROPYTHON_PATH=$(realpath "$MICROPYTHON_PATH")
IDF_PATH=$(realpath "$IDF_PATH")
MODULE_PATH=$(dirname "$(realpath "$0")")

echo "=========================================="
echo "Building MicroPython with Camera Module"
echo "=========================================="
echo "MicroPython path: $MICROPYTHON_PATH"
echo "ESP-IDF path: $IDF_PATH"
echo "Module path: $MODULE_PATH"
if [ -n "$BOARD" ]; then
echo "Board: $BOARD"
BUILD_DIR="${BUILD_DIR}-${BOARD}"
if [ -n "$BOARD_VARIANT" ]; then
echo "Board variant: $BOARD_VARIANT"
BUILD_DIR="${BUILD_DIR}_${BOARD_VARIANT}"
fi
fi
if [ -n "$CAMERA_MODEL" ]; then
echo "Camera model: $CAMERA_MODEL"
fi
echo "Build directory: $BUILD_DIR"
echo "=========================================="
echo ""

# Source ESP-IDF environment
echo "Setting up ESP-IDF environment..."
source "$IDF_PATH/export.sh"

# Check and initialize camera API submodules if needed
cd "$MODULE_PATH"
if git submodule status | grep -q '^-'; then
echo "Initializing camera API submodules ..."
git submodule update --init --depth 1
fi

# Change to MicroPython ESP32 port directory
cd "$MICROPYTHON_PATH/ports/esp32"
make BOARD=$BOARD submodules

# Build idf.py command with optional parameters
IDF_CMD="idf.py -B $BUILD_DIR"

if [ -n "$BOARD" ]; then
IDF_CMD="$IDF_CMD -D MICROPY_BOARD=$BOARD"
fi

if [ -n "$BOARD_VARIANT" ]; then
IDF_CMD="$IDF_CMD -D MICROPY_BOARD_VARIANT=$BOARD_VARIANT"
fi

if [ -n "$CAMERA_MODEL" ]; then
IDF_CMD="$IDF_CMD -D MICROPY_CAMERA_MODEL=$CAMERA_MODEL"
fi

IDF_CMD="$IDF_CMD -D USER_C_MODULES=$MODULE_PATH/micropython.cmake"
IDF_CMD="$IDF_CMD -D EXTRA_COMPONENT_DIRS=$MODULE_PATH"
IDF_CMD="$IDF_CMD build"

# Build MicroPython with IR Learn module
echo ""
echo "Building MicroPython..."
echo "Command: $IDF_CMD"
eval $IDF_CMD

# Create firmware images
echo ""
echo "Creating firmware images..."
cd "$BUILD_DIR"

python "$MICROPYTHON_PATH/ports/esp32/makeimg.py" \
sdkconfig \
bootloader/bootloader.bin \
partition_table/partition-table.bin \
micropython.bin \
firmware.bin \
micropython.uf2

echo ""
echo "Build completed successfully!"
echo "Firmware files in: $MICROPYTHON_PATH/ports/esp32/$BUILD_DIR"
echo "=========================================="

# Clean up build directory
cd "$MODULE_PATH"
rm -rf build
1 change: 1 addition & 0 deletions esp32-camera
Submodule esp32-camera added at b9c5c5
Loading