MicroPython Camera API

repository·master·Indexed 41 days ago

https://github.com/cnadler86/micropython-camera-api

A MicroPython API for controlling camera sensors such as OV2640 and OV5640 on platforms like ESP32. It provides synchronous and asynchronous interfaces (via the acamera package) for image capture and reconfiguration, including support for JPEG encoding/decoding through the mp_jpeg module. The API allows for detailed hardware pin configuration, I2C bus integration, and control over image geometry, quality, and exposure settings.

Tokens
3.2K
Snippets
13
Records
16
Agent score
1%

What's inside MicroPython Camera API

  1. Important hardware and performance notes

    master

    When using the camera API, keep the following constraints in mind:

    • ESP32 Performance: For standard ESP32 (non-S series), do not use frame sizes larger than QVGA when not using JPEG mode. JPEG mode generally provides better frame rates.
    • OV5640 Sensor: The OV5640 pinout is compatible with OV2640 boards, but the voltage supply is too high for internal 1.5V regulators. This causes overheating unless a heat sink is used. Only use OV5640 with ESP32S3 boards for recording. For still images, avoid frame sizes above FHD due to memory limits.
    • ESP32 Build Issues: If targeting ESP32, use ESP-IDF >= 5.2 to avoid IRAM overflow during the build process.
    • PSRAM: The driver requires PSRAM to be installed and activated on your board.
    • Frame Buffer (fb_count): Setting MICROPY_CAMERA_FB_COUNT to 2 can theoretically double the FPS (especially in JPEG mode) compared to a value of 1.
  2. Build custom firmware (DIY method)

    master

    To build your own MicroPython firmware with the camera API, you need to set up a build environment with the following requirements:

    1. ESP-IDF: Version 5.2.3 is recommended.
    2. MicroPython Repository: Clone the MicroPython repo (version 1.24 or higher, at least commit 92484d8).
    3. Camera Driver: You must add the esp32-camera driver to your build. Add the following to your idf_component.yml file (e.g., in micropython/ports/esp32/main/idf_component.yml):
      espressif/esp32-camera:
        git: https://github.com/cnadler86/esp32-camera.git
    1. Build Script: Use the provided ./build.sh script, passing the path to your MicroPython folder and the target board.
    ./build.sh -m path/to/micropython -b ESP32_GENERIC_S3
  3. Import the camera module

    master

    The standard API is imported from the camera module. For asynchronous support, use the acamera package.

    Standard import:

    from camera import Camera, GrabMode, PixelFormat, FrameSize, GainCeiling

    Asyncio import:

    from acamera import Camera, GrabMode, PixelFormat, FrameSize, GainCeiling
    from camera import Camera, GrabMode, PixelFormat, FrameSize, GainCeiling
  4. Integrate the camera with an external I2C bus

    master

    The camera uses the SCCB protocol (I2C) for sensor communication. By default, the camera creates and manages its own I2C bus. To share the I2C bus with other devices, pass an existing machine.I2C object to the Camera constructor.

    Note: SoftI2C is not supported; use hardware I2C.

    import machine
    from camera import Camera
    
    # 1. Create your own I2C object
    i2c = machine.I2C(0, scl=22, sda=21, freq=400000)
    
    # 2. Pass it to the camera (do not provide sda_pin/scl_pin)
    cam = Camera(i2c=i2c, data_pins=..., pclk_pin=..., ...)
    
    # 3. Use the same I2C object for other devices
    devices = i2c.scan()
    print(f"I2C devices found: {devices}")
    
    # You can still use camera methods that use I2C
    cam.saturation = 1
    i2c = machine.I2C(0, scl=22, sda=21, freq=400000)
    cam = Camera(i2c=i2c, data_pins=[...], pclk_pin=...)
  5. Configure unsupported camera models

    master

    If your board is not in the supported list, you must manually define the camera pins and parameters in your mpconfigboard.h file.

    Example configuration for a Xiao sense board:

    #define MICROPY_CAMERA_PIN_D0       (15)
    #define MICROPY_CAMERA_PIN_D1       (17)
    #define MICROPY_CAMERA_PIN_D2       (18)
    #define MICROPY_CAMERA_PIN_D3       (16)
    #define MICROPY_CAMERA_PIN_D4       (14)
    #define MICROPY_CAMERA_PIN_D5       (12)
    #define MICROPY_CAMERA_PIN_D6       (11)
    #define MICROPY_CAMERA_PIN_D7       (48)
    #define MICROPY_CAMERA_PIN_PCLK     (13)
    #define MICROPY_CAMERA_PIN_VSYNC    (38)
    #define MICROPY_CAMERA_PIN_HREF     (47)
    #define MICROPY_CAMERA_PIN_XCLK     (10)
    #define MICROPY_CAMERA_PIN_PWDN     (-1)
    #define MICROPY_CAMERA_PIN_RESET    (-1)
    #define MICROPY_CAMERA_PIN_SIOD     (40)        // SDA
    #define MICROPY_CAMERA_PIN_SIOC     (39)        // SCL
    #define MICROPY_CAMERA_XCLK_FREQ    (20000000)  // Frequencies are normally either 10 MHz or 20 MHz
    #define MICROPY_CAMERA_FB_COUNT     (2)         // Value between 1 (slow) and 2 (fast, more CPU/RAM usage)
    #define MICROPY_CAMERA_JPEG_QUALITY (85)        // Quality of JPEG output in percent (higher = better)
    #define MICROPY_CAMERA_GRAB_MODE    (1)         // 0=WHEN_EMPTY, 1=LATEST
  6. Configure supported camera models

    master

    For boards that are already supported, you can enable the camera by adding a single #define to your board's configuration file (mpconfigboard.h).

    Example for WROVER KIT:

    #define MICROPY_CAMERA_MODEL_WROVER_KIT       1

    Supported models include:

    • MICROPY_CAMERA_MODEL_WROVER_KIT (ESP32-WROVER-KIT)
    • MICROPY_CAMERA_MODEL_ESP_EYE (ESP-EYE)
    • MICROPY_CAMERA_MODEL_M5STACK_PSRAM (M5Stack PSRAM)
    • MICROPY_CAMERA_MODEL_M5STACK_UNITCAM (M5Stack UnitCam)
    • MICROPY_CAMERA_MODEL_M5STACK_V2_PSRAM (M5Stack V2 PSRAM)
    • MICROPY_CAMERA_MODEL_M5STACK_WIDE (M5Stack Wide)
    • MICROPY_CAMERA_MODEL_M5STACK_ESP32CAM (M5Stack ESP32CAM)
    • MICROPY_CAMERA_MODEL_M5STACK_CAMS3_UNIT (M5Stack CAMS3 Unit)
    • MICROPY_CAMERA_MODEL_M5STACK_ATOM_S3R (M5Stack Atom S3R)
    • MICROPY_CAMERA_MODEL_AI_THINKER (AI-Thinker ESP32-CAM)
    • MICROPY_CAMERA_MODEL_XIAO_ESP32S3 (XIAO ESP32S3)
    • MICROPY_CAMERA_MODEL_ESP32_MP_CAMERA_BOARD (ESP32 MP Camera Board)
    • MICROPY_CAMERA_MODEL_ESP32S3_CAM_LCD (ESP32-S3 CAM LCD)
    • MICROPY_CAMERA_MODEL_ESP32S3_EYE (ESP32-S3 EYE)
    • MICROPY_CAMERA_MODEL_FREENOVE_ESP32S3_CAM (Freenove ESP32-S3 CAM)
    • MICROPY_CAMERA_MODEL_DFRobot_ESP32S3 (DFRobot ESP32-S3)
    • MICROPY_CAMERA_MODEL_TTGO_T_JOURNAL (TTGO T-Journal)
    • MICROPY_CAMERA_MODEL_TTGO_T_CAMERA_PLUS (TTGO T-Camera Plus)
    • MICROPY_CAMERA_MODEL_NEW_ESPS3_RE1_0 (New ESP32-S3 RE:1.0)
    • MICROPY_CAMERA_MODEL_XENOIONEX (Xenoionex)
  7. Reconfigure the camera

    master

    Use reconfigure() to change camera settings after initialization. Supported optional arguments are:

    • frame_size: Frame size as FrameSize
    • pixel_format: Pixel format as PixelFormat
    • grab_mode: Grab mode as GrabMode
    • fb_count: Frame buffer count
    cam.reconfigure(pixel_format=PixelFormat.JPEG, frame_size=FrameSize.QVGA, grab_mode=GrabMode.LATEST, fb_count=2)
  8. Check if a frame is available

    master

    You can implement a non-asyncio polling loop to wait for a new frame using frame_available().

    Img = bytes(cam.capture())
    cam.free_buffer()
    while not cam.frame_available():
        # Do other tasks while waiting
        pass
    print('The frame is available now.')
    while not cam.frame_available():
        pass
  9. Free the camera buffer

    master

    To reduce latency (especially when fb_count=1), you can manually free the memoryview buffer. To do this safely, convert the captured memoryview to a bytes object first so the data is preserved before the buffer is deleted.

    Img = bytes(cam.capture())  # Copy data to a bytes object
    cam.free_buffer()          # Free the original memoryview
    Img = bytes(cam.capture())
    cam.free_buffer()
  10. Create a camera object

    master

    You can instantiate a Camera object using defaults (if using non-generic firmware or pre-configured boards) or by providing specific hardware pins and settings.

    Using defaults:

    cam = Camera()

    With specific settings:

    cam = Camera(pixel_format=PixelFormat.JPEG, frame_size=FrameSize.QVGA, jpeg_quality=90, fb_count=2, grab_mode=GrabMode.WHEN_EMPTY)

    Using generic firmware (requires manual pin definition):

    cam = 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,
    )
    cam = Camera(pixel_format=PixelFormat.JPEG, frame_size=FrameSize.QVGA, jpeg_quality=90, fb_count=2, grab_mode=GrabMode.WHEN_EMPTY)