ESP32-CAM Capture

Thanks…

I was wondering if I could use this method to send the image via http command? Maybe using binary?

What’s your aim? If you use the native api you can just take snapshots (ha service) :camera_flash:

My aim is to send the data/image over the http request as the ESP will be on another network

(Wantig to set up a remote camera and send the images to Home Assistant via the Nabu Casa Rest API, similar to the link of my other post but rather than the button sending the “human readable uptime” state, it sends an image from the camera)

Somewhat advanced stuff. Did you give a look at the camera web server?

Yeah had a look but looks to only run locally via the API.

Would be a treat if I could set the host URL of the API.

Just no idea how to send the image remotely.

It might be a while, but I have a similar request. In my case, I made the esp32cam wake up every 10 minutes for 20 seconds. There’s an automation in HA that runs every 14 seconds and tries to download the picture.

Ideally, HA would know when to take the picture, I think it might be possible to do that. A better case would be if esp32cam can upload the file directly somewhere.

Battery consumption is somewhat decent, can be improved with longer sleep times - 150-240 mA while active, 3-5mA while sleeping. I’m keeping a backup of the old photo while getting the new one. Anyway, this is as far as I’ve gotten, for now.

ESPHome code:

esphome:
  name: esp32cam1
  platform: ESP32
  board: esp-wrover-kit

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  fast_connect: true
  reboot_timeout: 60s
  use_address: esp32cam1.local
  manual_ip:
    static_ip: <your ip here>
    gateway: !secret gateway
    subnet: !secret subnet 
  ap:
    ssid: esp32cam1
    password: "********"

captive_portal:
    
# Example configuration entry
esp32_camera:
  external_clock:
    pin: GPIO0
    frequency: 20MHz
  i2c_pins:
    sda: GPIO26
    scl: GPIO27
  data_pins: [GPIO5, GPIO18, GPIO19, GPIO21, GPIO36, GPIO39, GPIO34, GPIO35]
  vsync_pin: GPIO25
  href_pin: GPIO23
  pixel_clock_pin: GPIO22
  power_down_pin: GPIO32

  # Image settings
  name: ESP32 Cam 1
  # ...
  resolution: 640x480
  jpeg_quality: 10
  max_framerate: 1 fps
  idle_framerate: 0.01 fps

switch:
  - platform: restart
    name: Restart device

light:
  - platform: monochromatic
    output: led
    name: "LED"

output:
  - platform: ledc
    id: led
    pin: GPIO4
    frequency: "1000Hz"

sensor:
  - platform: wifi_signal
    name: WiFi signal

  - platform: uptime
    name: Uptime Sensor

time:
  - platform: homeassistant
    id: homeassistant_time

# Enable logging
logger:
  level: VERBOSE

# Enable Home Assistant API
api:

ota:

deep_sleep:
  run_duration: 15s
  sleep_duration: 600s

esp32_camera_web_server:
  - port: 8080
    mode: stream
  - port: 8081
    mode: snapshot

In configuration.yaml for HA:

shell_command:
  snapshot_esp32cam1: cp ./www/esp32cam1.jpg ./www/esp32cam1_old.jpg && curl --max-time 10 http://<your IP here>:8081/ -o ./www/esp32cam1.jpg -s

Automation:

alias: esp32cam1 snapshot
description: ""
trigger:
  - platform: time_pattern
    hours: "*"
    minutes: "*"
    seconds: /14
condition: []
action:
  - service: shell_command.snapshot_esp32cam1
    data: {}
mode: single
1 Like

Why not trigger the download from the esp device when it wakes up and takes the pic.

You can run any home assistant service from the esp.

1 Like

how can i start such a service (capture photo and save to HA disk) ?
yaml example ?

You ever wondered why there isnt an esphome action to trigger a snapshot? I never realized untill now that the only option seems to be a HA service

I’m very new to ESP home and generally YAML, following, as I’m currently struggling to make a mailbox “sensor” that uses esp32-cam to take a picture every 5 minutes, then sleeps, I’d like to keep it all in the ESP home / HA ecosystem.

I’ll be trying the above example tonight, but am also following for any improvements yall come up with. Thanks!

Whats the purpose of taking a picture every 5 min? Is this going inside a mail box? If there is a door or if its inside the mail box then snap a picture on door open, motion, vibration, light/lux level etc. Surely you just want to know when theres mail and not 300 pictures of the inside of your mailbox every day i assume?

That “example” above that you plan to try out. That isnt an example, thats someone elses configuration for how they need and want things to work and just copying it and trying it isnt a good idea. Just go to the website, they give you esp32-Cam base configurations you can use to start with. Theres also more than 1 type of esp32-Cam so make sure you use the config thats for YOUR board and not someone elses.

Do a search… click on the magnifying glass icon up top.
Type in questions there and it gives you answers.

lol. I’m not saving 300 pictures.

I guess I’m kinda lucky that copy and pasting the above code worked out for me.

Here’s where I am, I’ve got it to where esp32-cam wakes up every 5 minutes for 90s. When it’s awake it sends a mqtt message, and I setup an automation to run the shell script on receipt of the message. I used curl to download the jpg from the snapshot, always overwriting the same file name.

The mailbox is 300ft from the house, it would be nice to glimpse a screen to see if the mail is in, so I don’t have to walk out and check multiple times a day.

It’s not installed yet, I’m waiting for battery stuff to come in.

Right now I’m struggling with the wake up pin configuration. But I agree only walking up when the door opened would be a lot better.

There are interrupt pins specifically for this kind of thing. If the board is sleeping and that interrupt pin changes state(Door opens) it will wake the board and do whatever automation you create and then go back to sleep. I’m not a big fan of battery powered and all that but, this is a frequent conversation in the forums and you shouldn’t have a hard time finding details on doing this.

Lol @bkbilly your use-case and approach is litterally identical to mine. What brought me here. (Gas Meter reading, photo, upload to server, sleep)
I’m working with GPT but its having issues with ESP Home YAML. A lot of guessing.

Ideally I’d like to get this working as a stand alone device that doesn’t need ESP Home or Home Assistant, uploads directly to my server for processing.
The following is where I’m at now… but the syntax to take the photo is currently where I’m stuck.



substitutions:
  name: esphome-web-hash
  friendly_name: Gas Meter Reader

esphome:
  name: ${name}
  friendly_name: ${friendly_name}
  min_version: 2024.6.0
  name_add_mac_suffix: false
  project:
    name: esphome.web
    version: dev

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:

# Allow Over-The-Air updates
ota:
- platform: esphome

# Allow provisioning Wi-Fi via serial
improv_serial:

wifi:
  ssid: "SSID_HERE"
  password: "PASSWORD_HERE"
  # Set up a Wi-Fi access point as a fallback
  ap: {}

# Enable captive portal for Wi-Fi provisioning
captive_portal:

dashboard_import:
  package_import_url: github://esphome/example-configs/esphome-web/esp32.yaml@main
  import_full_config: true

# Sets up Bluetooth LE (Only on ESP32) to allow the user to provision wifi credentials to the device.
esp32_improv:
  authorizer: none

# Enable web server for local control and access
web_server:
  port: 80

# Template switch to trigger the photo capture and upload script
switch:
  - platform: template
    name: "Take Photo and Upload"
    id: photo_upload_trigger
    turn_on_action:
      - logger.log: "Digital trigger activated, taking a photo..."
      - script.execute: take_photo_and_upload

# Camera configuration for ESP32-CAM
esp32_camera:
  name: "Gas Meter Camera"
  id: esp32cam_camera
  external_clock:
    pin: GPIO0
    frequency: 20MHz
  i2c_pins:
    sda: GPIO26
    scl: GPIO27
  data_pins: [GPIO5, GPIO18, GPIO19, GPIO21, GPIO36, GPIO39, GPIO34, GPIO35]
  vsync_pin: GPIO25
  href_pin: GPIO23
  pixel_clock_pin: GPIO22
  power_down_pin: GPIO32

  # Image Quality
  resolution: 800x600  # Set camera resolution, adjust as needed
  jpeg_quality: 10     # Set JPEG quality (lower number means higher quality)
  max_framerate: 1 fps
  idle_framerate: 0.01 fps

# Include the HTTP Request component in your ESPHome configuration
http_request:
  id: my_upload
  timeout: 10s  # Optional: Adjust the timeout as needed
  useragent: "ESPHome-GasMeterReader"  # Optional: Set a custom user agent

# Script to take a photo and upload it to a server using HTTP POST
script:
  - id: take_photo_and_upload
    then:
      - component.update: esp32cam_camera
      - delay: 5s
      - camera.take_photo:
          id: esp32cam_camera
          filename: "/photo.jpg"
      - delay: 10s
      - http_request.post:
          url: "http://your-server-endpoint/upload"  # Replace with your server URL
          headers:
            Content-Type: "multipart/form-data"
          file:
            path: "/photo.jpg"  # Path to the file on the ESP32
            name: "file"  # Field name in the form-data
      - logger.log: "Photo taken and uploaded successfully."

This seems very promising, but I gave up on this approach.
I got power near the gas meter and used the AI-on-the-edge-device to get the measurement.

If the take_photo_and_upload script works for you, you should also use the deep_sleep component to conserve power.

esp32camera on_image (Optional, Automation): An automation called when image taken. Image is available as image variable of type esp32_camera::CameraImageData.

 struct CameraImageData {
   uint8_t *data;
   size_t length;
 };

to use it:

esp32_camera:
  on_image:
    then:
      - lambda: |-
          ESP_LOGD("esp32_camera", "on_image length: %u data: %p", image.length, image.data);

for example writing a file to SD card:


                esp_err_t res;
                const uint8_t *data = image->get_data_buffer();
                size_t len = image->get_data_length();
                res = this->write_photo(to_string(this->image_file_name_).c_str(), data, len);
....



    ESP_LOGCONFIG(TAG, "fopen %s", filename );
    FILE *fd = fopen(filename, "wb");
    if (fd == NULL) {
        ESP_LOGE(TAG, "failed to open %s with %d", filename, len);
        fclose(fd);
        this->status_momentary_warning("write_photo Failed",this->timeout_);
        return ESP_FAIL;
    }
    size_t written = fwrite(data, 1, len, fd);
    if (written != len) {
        ESP_LOGE(TAG, "write_photo Failed to write all data to %s", filename);
        fclose(fd);
        return ESP_FAIL;
    }
1 Like

this code will send image to MQTT every minute:

esp32_camera:
  ... other settings like GPIO,resolution etc
  ...
  max_framerate: 1 fps
  idle_framerate: 1 fps
  on_image:
    then:
      - if:
          condition: 
            lambda: |-
              static int interval_counter = 0; 
              const int update_interval_max = 60;

              interval_counter--; 
          
              if (interval_counter <= 0) 
              {
                ESP_LOGI("mqttlog", "condition TRUE to send image via mqtt");
                interval_counter = update_interval_max; // Reset counter 
                return true;
              }
    
              ESP_LOGI("mqttlog", "condition FALSE image via mqtt, counter: %d", interval_counter);
              return false;

          then:
            - mqtt.publish:
                topic: "$device/cam"
                payload: !lambda return esphome::base64_encode(image.data, image.length);

Drawbacks:

  • base64 encoding means a 30% increase in size
  • camera captures image every second (1 FPS) - waste of cpu&power

Questions:

  1. MQTT can handle binary, can ESPHome send binary?

answer: currently no.

got info on discord (thank you @ssieb ) The api uses protobuf. The issue with mqtt is at one point in the transfer, the data is passed as a char * with no length. That needs to be changed.

1 Like