From 71f35fe8223272d682f2aeb05786614bbca94124 Mon Sep 17 00:00:00 2001 From: Joshua King Date: Fri, 6 Mar 2026 08:55:37 -0500 Subject: [PATCH] Add image publishing functionality to Litter Box Camera: implement base64 encoding and MQTT publishing on image capture --- esphome/litter-box-cam.yaml | 58 +++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/esphome/litter-box-cam.yaml b/esphome/litter-box-cam.yaml index 3c8605b..83f0dbc 100644 --- a/esphome/litter-box-cam.yaml +++ b/esphome/litter-box-cam.yaml @@ -113,11 +113,15 @@ globals: type: int restore_value: yes initial_value: '0' - + - id: motion_cooldown_active type: bool initial_value: 'false' + - id: publish_next_image + type: bool + initial_value: 'false' + # ----------------------------------------------------------------------------- # Camera Configuration (OV3660 - DFR1154 Specific) # ----------------------------------------------------------------------------- @@ -150,6 +154,36 @@ esp32_camera: brightness: 0 contrast: 1 saturation: 0 + on_image: + then: + - lambda: |- + if (!id(publish_next_image)) return; + id(publish_next_image) = false; + + const uint8_t *data = x->get_data(); + size_t len = x->get_data_length(); + size_t out_len = 4 * ((len + 2) / 3); + char *buf = (char *)malloc(out_len + 1); + if (!buf) return; + + static const char b64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + size_t j = 0; + for (size_t i = 0; i < len; i += 3) { + uint32_t b = ((uint32_t)data[i] << 16); + if (i + 1 < len) b |= ((uint32_t)data[i + 1] << 8); + if (i + 2 < len) b |= data[i + 2]; + buf[j++] = b64[(b >> 18) & 0x3F]; + buf[j++] = b64[(b >> 12) & 0x3F]; + buf[j++] = (i + 1 < len) ? b64[(b >> 6) & 0x3F] : '='; + buf[j++] = (i + 2 < len) ? b64[b & 0x3F] : '='; + } + buf[j] = '\0'; + + id(mqtt_client).publish("litter_box/camera/image", buf); + ESP_LOGI("capture", "Published image (%d bytes raw, %d base64)", len, j); + id(status_message).publish_state("Image sent for analysis"); + free(buf); # Web server for manual viewing esp32_camera_web_server: @@ -293,26 +327,8 @@ script: - light.turn_on: status_led - delay: 200ms - lambda: |- - auto cam = id(litter_cam); - camera::Camera::FrameBuffer *fb = cam->get_image(); - if (fb) { - size_t out_len = 4 * ((fb->length + 2) / 3); - char *base64_buffer = (char *)malloc(out_len + 1); - if (base64_buffer) { - size_t encoded_len = 0; - int ret = mbedtls_base64_encode( - (unsigned char *)base64_buffer, out_len + 1, &encoded_len, - fb->buf, fb->length); - if (ret == 0) { - base64_buffer[encoded_len] = '\0'; - id(mqtt_client).publish("litter_box/camera/image", base64_buffer); - ESP_LOGI("capture", "Published image"); - id(status_message).publish_state("Image sent for analysis"); - } - free(base64_buffer); - } - cam->return_image(fb); - } + id(publish_next_image) = true; + id(litter_cam).request_image(esphome::esp32_camera::SINGLE_SHOT); - light.turn_off: status_led - lambda: 'id(motion_cooldown_active) = true;' - delay: 30s