changeset 1678:7831b5de3572

espNode checkpoint
author drewp@bigasterisk.com
date Mon, 27 Sep 2021 22:59:39 -0700
parents aa35ae7a1acc
children f88ff1021ee0
files espNode/air_quality_indoor.yaml espNode/desk/platformio.ini espNode/desk/src/fingerprint.cpp espNode/desk/src/mqtt.cpp espNode/desk/src/mqtt.h espNode/garage_hall_cam.yaml espNode/garage_hall_cam/src/camera.cpp espNode/garage_hall_cam/src/main.cpp espNode/office_back_cam.yaml espNode/readcam.py espNode/tasks.py
diffstat 10 files changed, 472 insertions(+), 152 deletions(-) [+]
line wrap: on
line diff
--- a/espNode/air_quality_indoor.yaml	Wed Sep 22 01:22:05 2021 -0700
+++ b/espNode/air_quality_indoor.yaml	Mon Sep 27 22:59:39 2021 -0700
@@ -8,7 +8,7 @@
   ssid: !secret wifi_ssid
   password: !secret wifi_password
   domain: ''
-  use_address: 10.2.0.32
+  use_address: 10.2.0.22
   
 logger:
   baud_rate: 115200
--- a/espNode/desk/platformio.ini	Wed Sep 22 01:22:05 2021 -0700
+++ b/espNode/desk/platformio.ini	Mon Sep 27 22:59:39 2021 -0700
@@ -10,7 +10,8 @@
 
 [env:ttgo-t1]
 platform = espressif32
-board = ttgo-t1
+; board = ttgo-t1
+board = esp32dev
 framework = arduino
 upload_port = /dev/ttyUSB0
 upload_protocol = esptool
--- a/espNode/desk/src/fingerprint.cpp	Wed Sep 22 01:22:05 2021 -0700
+++ b/espNode/desk/src/fingerprint.cpp	Mon Sep 27 22:59:39 2021 -0700
@@ -31,13 +31,12 @@
   finger.led_control(led_breathe, led_medium, led_purple, led_forever);
 }
 void BlinkClearSuccess() {
-  finger.led_control(led_breathe, led_medium, led_purple, 1);
+  finger.led_control(led_breathe, led_medium, led_purple, /*times=*/1);
 }
 void BlinkError() {
   finger.led_control(led_flash, led_medium, led_red, /*times=*/3);
   delay(500);
 }
-void BlinkDoorUnlocked() {}
 void BlinkStartEnroll() {
   finger.led_control(led_flash, led_slow, led_blue, led_forever);
 }
@@ -45,7 +44,7 @@
   finger.led_control(led_flash, led_medium, led_blue, led_forever);
 }
 void BlinkClearEnroll() {
-  finger.led_control(led_flash, led_slow, led_blue, 1);
+  finger.led_control(led_flash, led_slow, led_blue, /*times=*/1);
 }
 
 void (*queued)() = nullptr;
@@ -56,7 +55,7 @@
     queued = nullptr;
   }
 }
-
+namespace {
 bool NeedToGetBackToMainLoopSoon() {
   return mqtt::HasPendingMessage() || queued;
 }
@@ -128,6 +127,9 @@
     case FPM_DBREADFAIL:
       errStr = "Invalid model";
       break;
+    case FPM_DBCLEARFAIL:
+      errStr = "Could not clear database";
+      break;
     default:
       char buf[100];
       snprintf(buf, sizeof(buf), "Unknown error (%d)", p);
@@ -203,7 +205,7 @@
   snprintf(msg, sizeof(msg), "Found id %d confidence %d", fid, score);
   LogDetect(msg);
 }
-
+}  // namespace
 void ScanLoop() {
   const std::string& log_mode = "scan";
   if (!GetImage(log_mode)) {
@@ -221,7 +223,7 @@
 
   ReportFoundMatch(fid, score);
 }
-
+namespace {
 bool get_free_id(const std::string& log_mode, int16_t* fid) {
   int16_t p = -1;
   for (int page = 0; page < (params.capacity / FPM_TEMPLATES_PER_PAGE) + 1;
@@ -305,7 +307,7 @@
     return EnrollFailed(log_mode);
   }
 }
-
+}  // namespace
 void Enroll() {
   const std::string log_mode = "enroll";
   BlinkStartEnroll();
@@ -425,7 +427,14 @@
   }
 }
 
-void DeleteAll() {}
+void DeleteAll() {
+  int16_t p = finger.emptyDatabase();
+  if (p == FPM_OK) {
+    LogStore("Database cleared");
+  } else {
+    LogFpmError("deleteAll", "emptyDatabase", p);
+  }
+}
 
 void Setup() {
   fserial.begin(57600, SERIAL_8N1, 26 /*rx*/, 27 /*tx*/);
--- a/espNode/desk/src/mqtt.cpp	Wed Sep 22 01:22:05 2021 -0700
+++ b/espNode/desk/src/mqtt.cpp	Mon Sep 27 22:59:39 2021 -0700
@@ -37,12 +37,15 @@
 
 Published from MCU:
 
+mode in {scan, enroll, download, setModel, deleteModel}
+
 fingerprint/<mode>/status = junk
 fingerprint/<mode>/error/<caller> = FPM error message
 fingerprint/store = some change to fingerprint storage
 fingerprint/detect = input finger
 fingerprint/model/<fid> = binary model data
 fingerprint/image/<fid> = binary image data
+fingerprint/temperature = "%.3fC"
 */
 void Publish(const std::string& subtopic, const std::string& msg) {
   std::string topic = "fingerprint/" + subtopic;
@@ -61,6 +64,7 @@
   snprintf(buf, sizeof(buf), "%.3fC", temp_c);
   mqttClient.publish("fingerprint/temperature", 1, /*retain=*/true, buf);
 }
+
 void onMqttConnect(bool sessionPresent) {
   Serial.println("Connected to MQTT.");
   Serial.print("Session present: ");
--- a/espNode/desk/src/mqtt.h	Wed Sep 22 01:22:05 2021 -0700
+++ b/espNode/desk/src/mqtt.h	Mon Sep 27 22:59:39 2021 -0700
@@ -14,7 +14,7 @@
 namespace mqtt {
 
 void Setup();
-void Publish(std::string subtopic, std::string msg);
+void Publish(const std::string& subtopic, const std::string& msg);
 void StopTimer();
 void ConnectToMqtt();
 bool HasPendingMessage();
--- a/espNode/garage_hall_cam.yaml	Wed Sep 22 01:22:05 2021 -0700
+++ b/espNode/garage_hall_cam.yaml	Mon Sep 27 22:59:39 2021 -0700
@@ -1,25 +1,23 @@
 # jump IO0 to GND on the board for programming
 
-# bang(pts/13):/my/proj/homeauto/espNode% forever { catchsegv ./readcam.py --cam garage_hall_cam  --port 10020 -v }
-
 
 esphome:
   name: garage_hall_cam
   platform: ESP32
   board: nodemcu-32s
-  build_path: build
+  build_path: garage_hall_cam
 
 wifi:
   ssid: !secret wifi_ssid
   password: !secret wifi_password
   domain: ''
-  use_address: 10.2.0.28
+  use_address: 10.2.0.74
      
-#mqtt:
-#  broker: '10.2.0.1'
-#  port: 1883
-#  username: ''
-#  password: ''
+mqtt:
+ broker: '10.2.0.1'
+ port: 1883
+ username: ''
+ password: ''
   
 logger:
   baud_rate: 115200
@@ -27,54 +25,6 @@
   
 ota:
 
-api:
-  port: 6053
-  password: 'MyPassword'
-  
-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
-
-  name: camera
-
-  # https://github.com/esphome/esphome/blob/dev/esphome/components/esp32_camera/esp32_camera.cpp#L265 says a 'stream' is 5 sec long
+# Serves multipart mime stream of jpegs at GET /
 
-  # setting to 5 causes 'Setup Failed: ERROR'
-  max_framerate: 4 fps
-  # https://github.com/raphaelbs/esp32-cam-ai-thinker#capabilities says camera
-  # is likely ov2640 with these native resolutions:
-  # uxga=1600x1200 svga=800x600 cif=400x296
-  # My camera has 'Setup Failed: ERROR' if this is not 640x480. Not sure why.
-  # Also the camera had 'Setup Failed: ERROR' when its cable needed reseating.
-
-  #   160x120 (QQVGA)    'Got invalid frame', then no more
-  #   128x160 (QQVGA2)
-  #   176x144 (QCIF)     fps: 25 jpg: 20 img: 2KB burst of frames then stopped. 
-  #                      fps: 20 jpg: 20 no frames
-  #   240x176 (HQVGA)
-  #   320x240 (QVGA)     fps: 10 jpg: 20 some frames, 4.5KB
-  #   400x296 (CIF)
-  #   640x480 (VGA)      fps: 4 jpg: 10 works,  20KB
-  #   800x600 (SVGA)
-  #   1024x768 (XGA)
-  #   1280x1024 (SXGA)   fps: 1  works
-  #   1600x1200 (UXGA)
-
-  resolution: 640x480
-  
-  # 10 to 63. default=10. higher is
-  # worse. https://github.com/esphome/esphome/blob/6682c43dfaeb1c006943ae546145e5f22262cadb/esphome/components/esp32_camera/__init__.py#L84
-  # sets the lower limit to 10, but
-  # https://github.com/raphaelbs/esp32-cam-ai-thinker/blob/master/components/ov2640/sensors/ov2640.c#L345
-  # suggests that it might be 0 (for an ov2640, anyway).
-  jpeg_quality: 10
-  
+# gst-launch-1.0  souphttpsrc location=http://10.2.0.74/ do-timestamp=true ! multipartdemux ! jpegdec ! autovideosink
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/espNode/garage_hall_cam/src/camera.cpp	Mon Sep 27 22:59:39 2021 -0700
@@ -0,0 +1,259 @@
+// from https://raw.githubusercontent.com/RuiSantosdotme/ESP32-CAM-Arduino-IDE/master/ESP32-CAM-Video-Streaming/ESP32-CAM-Video-Streaming.ino
+
+
+
+/*********
+  Rui Santos
+  Complete project details at https://RandomNerdTutorials.com/esp32-cam-video-streaming-web-server-camera-home-assistant/
+  
+  IMPORTANT!!! 
+   - Select Board "AI Thinker ESP32-CAM"
+   - GPIO 0 must be connected to GND to upload a sketch
+   - After connecting GPIO 0 to GND, press the ESP32-CAM on-board RESET button to put your board in flashing mode
+  
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files.
+
+  The above copyright notice and this permission notice shall be included in all
+  copies or substantial portions of the Software.
+*********/
+
+#include "esp_camera.h"
+#include <WiFi.h>
+#include "esp_timer.h"
+#include "img_converters.h"
+#include "Arduino.h"
+#include "fb_gfx.h"
+#include "soc/soc.h" //disable brownout problems
+#include "soc/rtc_cntl_reg.h"  //disable brownout problems
+#include "esp_http_server.h"
+
+//Replace with your network credentials
+const char* ssid = "REPLACE_WITH_YOUR_SSID";
+const char* password = "REPLACE_WITH_YOUR_PASSWORD";
+
+#define PART_BOUNDARY "123456789000000000000987654321"
+
+// This project was tested with the AI Thinker Model, M5STACK PSRAM Model and M5STACK WITHOUT PSRAM
+#define CAMERA_MODEL_AI_THINKER
+//#define CAMERA_MODEL_M5STACK_PSRAM
+//#define CAMERA_MODEL_M5STACK_WITHOUT_PSRAM
+
+// Not tested with this model
+//#define CAMERA_MODEL_WROVER_KIT
+
+#if defined(CAMERA_MODEL_WROVER_KIT)
+  #define PWDN_GPIO_NUM    -1
+  #define RESET_GPIO_NUM   -1
+  #define XCLK_GPIO_NUM    21
+  #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      19
+  #define Y4_GPIO_NUM      18
+  #define Y3_GPIO_NUM       5
+  #define Y2_GPIO_NUM       4
+  #define VSYNC_GPIO_NUM   25
+  #define HREF_GPIO_NUM    23
+  #define PCLK_GPIO_NUM    22
+
+#elif defined(CAMERA_MODEL_M5STACK_PSRAM)
+  #define PWDN_GPIO_NUM     -1
+  #define RESET_GPIO_NUM    15
+  #define XCLK_GPIO_NUM     27
+  #define SIOD_GPIO_NUM     25
+  #define SIOC_GPIO_NUM     23
+  
+  #define Y9_GPIO_NUM       19
+  #define Y8_GPIO_NUM       36
+  #define Y7_GPIO_NUM       18
+  #define Y6_GPIO_NUM       39
+  #define Y5_GPIO_NUM        5
+  #define Y4_GPIO_NUM       34
+  #define Y3_GPIO_NUM       35
+  #define Y2_GPIO_NUM       32
+  #define VSYNC_GPIO_NUM    22
+  #define HREF_GPIO_NUM     26
+  #define PCLK_GPIO_NUM     21
+
+#elif defined(CAMERA_MODEL_M5STACK_WITHOUT_PSRAM)
+  #define PWDN_GPIO_NUM     -1
+  #define RESET_GPIO_NUM    15
+  #define XCLK_GPIO_NUM     27
+  #define SIOD_GPIO_NUM     25
+  #define SIOC_GPIO_NUM     23
+  
+  #define Y9_GPIO_NUM       19
+  #define Y8_GPIO_NUM       36
+  #define Y7_GPIO_NUM       18
+  #define Y6_GPIO_NUM       39
+  #define Y5_GPIO_NUM        5
+  #define Y4_GPIO_NUM       34
+  #define Y3_GPIO_NUM       35
+  #define Y2_GPIO_NUM       17
+  #define VSYNC_GPIO_NUM    22
+  #define HREF_GPIO_NUM     26
+  #define PCLK_GPIO_NUM     21
+
+#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
+#else
+  #error "Camera model not selected"
+#endif
+
+static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
+static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
+static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
+
+httpd_handle_t stream_httpd = NULL;
+
+static esp_err_t stream_handler(httpd_req_t *req){
+  camera_fb_t * fb = NULL;
+  esp_err_t res = ESP_OK;
+  size_t _jpg_buf_len = 0;
+  uint8_t * _jpg_buf = NULL;
+  char * part_buf[64];
+
+  res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
+  if(res != ESP_OK){
+    return res;
+  }
+
+  // https://github.com/RuiSantosdotme/ESP32-CAM-Arduino-IDE/pull/4
+  res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
+  if(res != ESP_OK){
+    return res;
+  }
+
+  while(true){
+    fb = esp_camera_fb_get();
+    if (!fb) {
+      Serial.println("Camera capture failed");
+      res = ESP_FAIL;
+    } else {
+      if(fb->width > 400){
+        if(fb->format != PIXFORMAT_JPEG){
+          bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len);
+          esp_camera_fb_return(fb);
+          fb = NULL;
+          if(!jpeg_converted){
+            Serial.println("JPEG compression failed");
+            res = ESP_FAIL;
+          }
+        } else {
+          _jpg_buf_len = fb->len;
+          _jpg_buf = fb->buf;
+        }
+      }
+    }
+    if(res == ESP_OK){
+      size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);
+      res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
+    }
+    if(res == ESP_OK){
+      res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
+    }
+    if(res == ESP_OK){
+      res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
+    }
+    if(fb){
+      esp_camera_fb_return(fb);
+      fb = NULL;
+      _jpg_buf = NULL;
+    } else if(_jpg_buf){
+      free(_jpg_buf);
+      _jpg_buf = NULL;
+    }
+    if(res != ESP_OK){
+      break;
+    }
+    //Serial.printf("MJPG: %uB\n",(uint32_t)(_jpg_buf_len));
+  }
+  return res;
+}
+
+void startCameraServer(){
+  httpd_config_t config = HTTPD_DEFAULT_CONFIG();
+  config.server_port = 80;
+
+  httpd_uri_t index_uri = {
+    .uri       = "/",
+    .method    = HTTP_GET,
+    .handler   = stream_handler,
+    .user_ctx  = NULL
+  };
+  
+  //Serial.printf("Starting web server on port: '%d'\n", config.server_port);
+  if (httpd_start(&stream_httpd, &config) == ESP_OK) {
+    httpd_register_uri_handler(stream_httpd, &index_uri);
+  }
+}
+
+void cam_setup() {
+  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
+ 
+  Serial.begin(115200);
+  Serial.setDebugOutput(false);
+  
+  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.pixel_format = PIXFORMAT_JPEG; 
+  
+  if(psramFound()){
+    config.frame_size = FRAMESIZE_UXGA;
+    config.jpeg_quality = 10;
+    config.fb_count = 2;
+  } else {
+    config.frame_size = FRAMESIZE_SVGA;
+    config.jpeg_quality = 12;
+    config.fb_count = 1;
+  }
+  
+  // 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;
+  }
+  
+  // Start streaming web server
+  startCameraServer();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/espNode/garage_hall_cam/src/main.cpp	Mon Sep 27 22:59:39 2021 -0700
@@ -0,0 +1,147 @@
+// Auto generated code by esphome
+// ========== AUTO GENERATED INCLUDE BLOCK BEGIN ===========
+#include "esphome.h"
+using namespace esphome;
+logger::Logger *logger_logger;
+wifi::WiFiComponent *wifi_wificomponent;
+ota::OTAComponent *ota_otacomponent;
+mqtt::MQTTClientComponent *mqtt_mqttclientcomponent;
+using namespace mqtt;
+using namespace json;
+// ========== AUTO GENERATED INCLUDE BLOCK END ==========="
+
+// camera.cpp
+extern void cam_setup();
+
+void setup() {
+  // ===== DO NOT EDIT ANYTHING BELOW THIS LINE =====
+  // ========== AUTO GENERATED CODE BEGIN ===========
+  // async_tcp:
+  // esphome:
+  //   name: garage_hall_cam
+  //   platform: ESP32
+  //   board: nodemcu-32s
+  //   build_path: garage_hall_cam
+  //   arduino_version: espressif32@1.12.4
+  //   platformio_options: {}
+  //   includes: []
+  //   libraries: []
+  App.pre_setup("garage_hall_cam", __DATE__ ", " __TIME__);
+  // logger:
+  //   baud_rate: 115200
+  //   level: DEBUG
+  //   id: logger_logger
+  //   tx_buffer_size: 512
+  //   hardware_uart: UART0
+  //   logs: {}
+  logger_logger = new logger::Logger(115200, 512, logger::UART_SELECTION_UART0);
+  logger_logger->pre_setup();
+  App.register_component(logger_logger);
+  // wifi:
+  //   domain: ''
+  //   use_address: 10.2.0.74
+  //   id: wifi_wificomponent
+  //   reboot_timeout: 15min
+  //   power_save_mode: LIGHT
+  //   fast_connect: false
+  //   networks:
+  //   - ssid: !secret 'wifi_ssid'
+  //     password: !secret 'wifi_password'
+  //     id: wifi_wifiap
+  //     priority: 0.0
+  wifi_wificomponent = new wifi::WiFiComponent();
+  wifi_wificomponent->set_use_address("10.2.0.74");
+  wifi::WiFiAP wifi_wifiap = wifi::WiFiAP();
+  wifi_wifiap.set_ssid("...");
+  wifi_wifiap.set_password("...");
+  wifi_wifiap.set_priority(0.0f);
+  wifi_wificomponent->add_sta(wifi_wifiap);
+  wifi_wificomponent->set_reboot_timeout(900000);
+  wifi_wificomponent->set_power_save_mode(wifi::WIFI_POWER_SAVE_LIGHT);
+  wifi_wificomponent->set_fast_connect(false);
+  App.register_component(wifi_wificomponent);
+  // ota:
+  //   id: ota_otacomponent
+  //   safe_mode: true
+  //   port: 3232
+  //   password: ''
+  ota_otacomponent = new ota::OTAComponent();
+  ota_otacomponent->set_port(3232);
+  ota_otacomponent->set_auth_password("");
+  App.register_component(ota_otacomponent);
+  ota_otacomponent->start_safe_mode();
+  // mqtt:
+  //   broker: 10.2.0.1
+  //   port: 1883
+  //   username: ''
+  //   password: ''
+  //   id: mqtt_mqttclientcomponent
+  //   discovery: true
+  //   discovery_retain: true
+  //   discovery_prefix: homeassistant
+  //   topic_prefix: garage_hall_cam
+  //   keepalive: 15s
+  //   reboot_timeout: 15min
+  //   birth_message:
+  //     topic: garage_hall_cam/status
+  //     payload: online
+  //     qos: 0
+  //     retain: true
+  //   will_message:
+  //     topic: garage_hall_cam/status
+  //     payload: offline
+  //     qos: 0
+  //     retain: true
+  //   shutdown_message:
+  //     topic: garage_hall_cam/status
+  //     payload: offline
+  //     qos: 0
+  //     retain: true
+  //   log_topic:
+  //     topic: garage_hall_cam/debug
+  //     qos: 0
+  //     retain: true
+  mqtt_mqttclientcomponent = new mqtt::MQTTClientComponent();
+  App.register_component(mqtt_mqttclientcomponent);
+  mqtt_mqttclientcomponent->set_broker_address("10.2.0.1");
+  mqtt_mqttclientcomponent->set_broker_port(1883);
+  mqtt_mqttclientcomponent->set_username("");
+  mqtt_mqttclientcomponent->set_password("");
+  mqtt_mqttclientcomponent->set_discovery_info("homeassistant", true);
+  mqtt_mqttclientcomponent->set_topic_prefix("garage_hall_cam");
+  mqtt_mqttclientcomponent->set_birth_message(mqtt::MQTTMessage{
+      .topic = "garage_hall_cam/status",
+      .payload = "online",
+      .qos = 0,
+      .retain = true,
+  });
+  mqtt_mqttclientcomponent->set_last_will(mqtt::MQTTMessage{
+      .topic = "garage_hall_cam/status",
+      .payload = "offline",
+      .qos = 0,
+      .retain = true,
+  });
+  mqtt_mqttclientcomponent->set_shutdown_message(mqtt::MQTTMessage{
+      .topic = "garage_hall_cam/status",
+      .payload = "offline",
+      .qos = 0,
+      .retain = true,
+  });
+  mqtt_mqttclientcomponent->set_log_message_template(mqtt::MQTTMessage{
+      .topic = "garage_hall_cam/debug",
+      .payload = "",
+      .qos = 0,
+      .retain = true,
+  });
+  mqtt_mqttclientcomponent->set_keep_alive(15);
+  mqtt_mqttclientcomponent->set_reboot_timeout(900000);
+  // json:
+  // =========== AUTO GENERATED CODE END ============
+  // ========= YOU CAN EDIT AFTER THIS LINE =========
+  App.setup();
+  cam_setup();
+}
+
+void loop() {
+  App.loop();
+}
--- a/espNode/office_back_cam.yaml	Wed Sep 22 01:22:05 2021 -0700
+++ b/espNode/office_back_cam.yaml	Mon Sep 27 22:59:39 2021 -0700
@@ -10,19 +10,24 @@
 
 # bang(pts/15):/my/proj/homeauto/espNode% catchsegv ./readcam.py --cam office_back_cam  --port 10021
 
-
+#red esp-tx
+#ora esp-rx
+#nc
+#blu gpio0
+#yel 3v3
+#nc
 
 esphome:
-  name: office_back_cam
+  name: workshop_cam
   platform: ESP32
   board: esp32cam
-  build_path: build
+  build_path: office_back_cam
 
 wifi:
   ssid: !secret wifi_ssid
   password: !secret wifi_password
   domain: ''
-  use_address: '10.2.0.35'
+  use_address: 10.2.0.39
 
 mqtt:
   broker: '10.2.0.1'
@@ -36,84 +41,29 @@
   
 ota:
 
-api:
-  port: 6053
-  password: 'MyPassword'
-  
-# https://randomnerdtutorials.com/esp32-cam-ai-thinker-pinout/
-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
-
-  name: camera
-
-  # https://github.com/esphome/esphome/blob/dev/esphome/components/esp32_camera/esp32_camera.cpp#L265 says a 'stream' is 5 sec long
-
-  # setting to 5 causes 'Setup Failed: ERROR'
-  max_framerate: 4 fps
-  # https://github.com/raphaelbs/esp32-cam-ai-thinker#capabilities says camera
-  # is likely ov2640 with these native resolutions:
-  # uxga=1600x1200 svga=800x600 cif=400x296
-  
-  #   160x120 (QQVGA)    'Got invalid frame', then no more
-  #   128x160 (QQVGA2)
-  #   176x144 (QCIF)     fps: 25 jpg: 20 img: 2KB burst of frames then stopped. 
-  #                      fps: 20 jpg: 20 no frames
-  #   240x176 (HQVGA)
-  #   320x240 (QVGA)     fps: 10 jpg: 20 some frames, 4.5KB
-  #   400x296 (CIF)
-  #   640x480 (VGA)      fps: 4 jpg: 10 works,  20KB
-  #   800x600 (SVGA)
-  #   1024x768 (XGA)
-  #   1280x1024 (SXGA)   fps: 1  works
-  #   1600x1200 (UXGA)
-
-  resolution: 640x480
-  
-  # 10 to 63. default=10. higher is
-  # worse. https://github.com/esphome/esphome/blob/6682c43dfaeb1c006943ae546145e5f22262cadb/esphome/components/esp32_camera/__init__.py#L84
-  # sets the lower limit to 10, but
-  # https://github.com/raphaelbs/esp32-cam-ai-thinker/blob/master/components/ov2640/sensors/ov2640.c#L345
-  # suggests that it might be 0 (for an ov2640, anyway).
-  jpeg_quality: 10
- 
-output:
-  - platform: gpio
-    pin: GPIO4
-    id: gpio_4
-
-light:
-  - platform: fastled_clockless
-    chipset: WS2812B
-    pin: GPIO13
-    num_leds: 1
-    rgb_order: GRB
-    name: "strip"
-  # - platform: monochromatic
-  #   name: "flash"
-  #   output: flash_out
-  - platform: binary
-    output: gpio_4
-    name: flash
+# light:
+#   - platform: fastled_clockless
+#     chipset: WS2812B
+#     pin: GPIO13
+#     num_leds: 1
+#     rgb_order: GRB
+#     name: "strip"
+#   # - platform: monochromatic
+#   #   name: "flash"
+#   #   output: flash_out
+#   - platform: binary
+#     output: gpio_4
+#     name: flash
 
   
-dallas:
-  - pin: GPIO15
-    update_interval: 10s
+# dallas:
+#   - pin: GPIO15
+#     update_interval: 10s
 
-sensor:
-  - platform: dallas
-    index: 0
-    name: temperature
+# sensor:
+#   - platform: dallas
+#     index: 0
+#     name: temperature
 
 # output:
 #   - platform: ledc
--- a/espNode/tasks.py	Wed Sep 22 01:22:05 2021 -0700
+++ b/espNode/tasks.py	Mon Sep 27 22:59:39 2021 -0700
@@ -2,7 +2,7 @@
 
 tag = 'esphome/esphome:dev'
 esphome = f'docker run --rm -v `pwd`:/config -v /usr/share/fonts:/usr/share/fonts -it {tag}'
-
+esphomeUsb = esphome.replace('--rm', '--rm --device=/dev/ttyUSB0')
 # on dash for lcd code for theater display:
 #tag = 'esphome_dev'
 #esphome = '/home/drewp/Downloads/esphome/env/bin/esphome'
@@ -20,7 +20,7 @@
     board = board.replace('.yaml', '')
     print('connect gnd, 3v3, rx/tx per https://randomnerdtutorials.com/esp32-cam-video-streaming-web-server-camera-home-assistant/, ')
     print('rts to reset (if possible), dtr to gpio0 per https://github.com/espressif/esptool/wiki/ESP32-Boot-Mode-Selection#automatic-bootloader')
-    ctx.run(f"{esphome} --device=/dev/ttyUSB0 {board}.yaml run", pty=True)
+    ctx.run(f"{esphomeUsb} run {board}.yaml --device=/dev/ttyUSB0", pty=True)
 
 @task
 def program_board_over_wifi(ctx, board):
@@ -30,7 +30,7 @@
 @task
 def monitor_usb(ctx, board):
     board = board.replace('.yaml', '')
-    ctx.run(f"{esphome} --device=/dev/ttyUSB0 {board}.yaml logs", pty=True)
+    ctx.run(f"{esphomeUsb} logs {board}.yaml --device=/dev/ttyUSB0", pty=True)
 
 # device up?
 #  nmap -Pn -p 3232,6053 10.2.0.21