Mercurial > code > home > repos > homeauto
changeset 1718:82213d91471c
new cam component with http server
author | drewp@bigasterisk.com |
---|---|
date | Sun, 07 Aug 2022 04:43:47 -0700 |
parents | e9540ee0cf73 |
children | fb082013fa24 |
files | espNode/cam.yaml espNode/component/cam.h espNode/tasks.py |
diffstat | 3 files changed, 206 insertions(+), 6 deletions(-) [+] |
line wrap: on
line diff
--- a/espNode/cam.yaml Sun Aug 07 02:26:11 2022 -0700 +++ b/espNode/cam.yaml Sun Aug 07 04:43:47 2022 -0700 @@ -9,6 +9,8 @@ platform: ESP32 board: esp32cam build_path: $build_path + includes: + - component/cam.h wifi: ssid: !secret wifi_ssid @@ -36,14 +38,18 @@ name: "flash" output: flash_out default_transition_length: 0s - # - platform: binary - # output: gpio_4 - # name: flash - - output: - platform: ledc id: flash_out pin: GPIO4 frequency: 19531Hz + channel: 4 + + +custom_component: + - lambda: |- + auto camc = new esphome::CamComponent(); + // App.register_component(camc); + return {camc}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/espNode/component/cam.h Sun Aug 07 04:43:47 2022 -0700 @@ -0,0 +1,194 @@ +#pragma once + +#include <Arduino.h> +#include <WiFi.h> + +#include "esp_camera.h" +#include "esp_http_server.h" +#include "esp_timer.h" +#include "esphome.h" +#include "esphome/core/component.h" +#include "esphome/core/log.h" +#include "img_converters.h" + +namespace esphome { +// #elif defined(CAMERA_MODEL_AI_THINKER) +#define PWDN_GPIO_NUM 32 +#define RESET_GPIO_NUM -1 +#define XCLK_GPIO_NUM 0 +#define SIOD_GPIO_NUM 26 +#define SIOC_GPIO_NUM 27 + +#define Y9_GPIO_NUM 35 +#define Y8_GPIO_NUM 34 +#define Y7_GPIO_NUM 39 +#define Y6_GPIO_NUM 36 +#define Y5_GPIO_NUM 21 +#define Y4_GPIO_NUM 19 +#define Y3_GPIO_NUM 18 +#define Y2_GPIO_NUM 5 +#define VSYNC_GPIO_NUM 25 +#define HREF_GPIO_NUM 23 +#define PCLK_GPIO_NUM 22 + +static const char *TAG = "cam"; + +typedef struct { + httpd_req_t *req; + size_t len; +} jpg_chunking_t; + +class CamComponent : public Component { + public: + float get_setup_priority() const override { + return esphome::setup_priority::AFTER_CONNECTION; + } + + void setup() override { + ESP_LOGD(TAG, "setup"); + camera_config_t config; + config.ledc_channel = LEDC_CHANNEL_0; + config.ledc_timer = LEDC_TIMER_0; + config.pin_d0 = Y2_GPIO_NUM; + config.pin_d1 = Y3_GPIO_NUM; + config.pin_d2 = Y4_GPIO_NUM; + config.pin_d3 = Y5_GPIO_NUM; + config.pin_d4 = Y6_GPIO_NUM; + config.pin_d5 = Y7_GPIO_NUM; + config.pin_d6 = Y8_GPIO_NUM; + config.pin_d7 = Y9_GPIO_NUM; + config.pin_xclk = XCLK_GPIO_NUM; + config.pin_pclk = PCLK_GPIO_NUM; + config.pin_vsync = VSYNC_GPIO_NUM; + config.pin_href = HREF_GPIO_NUM; + config.pin_sscb_sda = SIOD_GPIO_NUM; + config.pin_sscb_scl = SIOC_GPIO_NUM; + config.pin_pwdn = PWDN_GPIO_NUM; + config.pin_reset = RESET_GPIO_NUM; + config.xclk_freq_hz = 20000000; + config.frame_size = FRAMESIZE_QVGA; + config.pixel_format = PIXFORMAT_JPEG; // for streaming + // config.pixel_format = PIXFORMAT_RGB565; // for face detection/recognition + // config.grab_mode = CAMERA_GRAB_WHEN_EMPTY; + // config.fb_location = CAMERA_FB_IN_PSRAM; + config.jpeg_quality = 12; + config.fb_count = 1; + + if (psramFound()) { + config.jpeg_quality = 10; + config.fb_count = 2; + // config.grab_mode = CAMERA_GRAB_LATEST; + } + + ESP_LOGD(TAG, "camera init"); + esp_err_t err = esp_camera_init(&config); + if (err != ESP_OK) { + Serial.printf("Camera init failed with error 0x%x", err); + return; + } + + startCameraServer(); + } + + static size_t jpg_encode_stream(void *arg, size_t index, const void *data, + size_t len) { + jpg_chunking_t *j = (jpg_chunking_t *)arg; + if (!index) { + j->len = 0; + } + if (httpd_resp_send_chunk(j->req, (const char *)data, len) != ESP_OK) { + return 0; + } + j->len += len; + return len; + } + + static esp_err_t capture_handler(httpd_req_t *req) { + camera_fb_t *fb = NULL; + esp_err_t res = ESP_OK; + int64_t fr_start = esp_timer_get_time(); + + fb = esp_camera_fb_get(); + + if (!fb) { + ESP_LOGE(TAG, "Camera capture failed"); + httpd_resp_send_500(req); + return ESP_FAIL; + } + + httpd_resp_set_type(req, "image/jpeg"); + httpd_resp_set_hdr(req, "Content-Disposition", + "inline; filename=capture.jpg"); + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); + + char ts[32]; + snprintf(ts, 32, "%ld.%06ld", fb->timestamp.tv_sec, fb->timestamp.tv_usec); + httpd_resp_set_hdr(req, "X-Timestamp", (const char *)ts); + + size_t fb_len = 0; + if (fb->format == PIXFORMAT_JPEG) { + fb_len = fb->len; + res = httpd_resp_send(req, (const char *)fb->buf, fb->len); + } else { + jpg_chunking_t jchunk = {req, 0}; + res = + frame2jpg_cb(fb, 80, jpg_encode_stream, &jchunk) ? ESP_OK : ESP_FAIL; + httpd_resp_send_chunk(req, NULL, 0); + fb_len = jchunk.len; + } + esp_camera_fb_return(fb); + int64_t fr_end = esp_timer_get_time(); + ESP_LOGI(TAG, "JPG: %uB %ums", (uint32_t)(fb_len), + (uint32_t)((fr_end - fr_start) / 1000)); + return res; + } + + void startCameraServer() { + ESP_LOGD(TAG, "startCameraServer"); + httpd_config_t config = HTTPD_DEFAULT_CONFIG(); + config.server_port = 8000; + config.max_uri_handlers = 16; + + httpd_uri_t capture_uri = {.uri = "/capture", + .method = HTTP_GET, + .handler = capture_handler, + .user_ctx = NULL}; + + // httpd_uri_t stream_uri = { + // .uri = "/stream", + // .method = HTTP_GET, + // .handler = stream_handler, + // .user_ctx = NULL + // }; + + // ra_filter_init(&ra_filter, 20); + ESP_LOGCONFIG(TAG, "startCameraServer2"); + + ESP_LOGI(TAG, "Starting web server on port: '%d'", config.server_port); + httpd_handle_t camera_httpd = NULL; + if (httpd_start(&camera_httpd, &config) == ESP_OK) { + // httpd_register_uri_handler(camera_httpd, &index_uri); + // httpd_register_uri_handler(camera_httpd, &cmd_uri); + // httpd_register_uri_handler(camera_httpd, &status_uri); + httpd_register_uri_handler(camera_httpd, &capture_uri); + // httpd_register_uri_handler(camera_httpd, &bmp_uri); + + // httpd_register_uri_handler(camera_httpd, &xclk_uri); + // httpd_register_uri_handler(camera_httpd, ®_uri); + // httpd_register_uri_handler(camera_httpd, &greg_uri); + // httpd_register_uri_handler(camera_httpd, &pll_uri); + // httpd_register_uri_handler(camera_httpd, &win_uri); + } + + // config.server_port += 1; + // config.ctrl_port += 1; + // ESP_LOGI(TAG, "Starting stream server on port: '%d'", + // config.server_port); if (httpd_start(&stream_httpd, &config) == ESP_OK) + // { + // httpd_register_uri_handler(stream_httpd, &stream_uri); + // } + } + + void loop() override {} +}; +} // namespace esphome \ No newline at end of file
--- a/espNode/tasks.py Sun Aug 07 02:26:11 2022 -0700 +++ b/espNode/tasks.py Sun Aug 07 04:43:47 2022 -0700 @@ -84,7 +84,7 @@ def prep_tmp(ctx, project): tmp = Path(f'/tmp/esphome_build/{project}') ctx.run(f'mkdir -p {tmp}') - ctx.run(f'cp secrets.yaml {tmp}') + ctx.run(f'rsync -a component secrets.yaml {tmp}') return tmp