But before getting to the link.
When you have a question about the code. Post it here or in the discussion tab on github, don't make an issue on github for it.
The issues tab is meant for when there is a real issue. Otherwise management is difficult.
When you want to build with us on the code. Do this on the "development" environment
Pulls to the master will be ignored.
Now getting to the beautifull part, the link.
https://github.com/Hoeby/ESP32-Doorbell
Original post
I wanted a doorbell with camera.
But it also needed to trigger a dummy in domoticz, so it could send a telegram message with image (or other things).
Of course there are wonderfull camera doorbells. But the cost a fortune. And they are not open-source.
A few years back a made something with a wemos-D1 and ESPeasy with a lot of extra pcb's .
It was big (12,5cm x 5cm). Needed something smaller and easier.
Then i found the ESP32-cam.
Bought one to experiment on it and liked it.
There are wonderfull programs online, but non was out of the box usefull for domoticz. The most common needed to start the program everytime. Easy to do settings in de left bar. But a lot of overhead which is not needed for a doorbell
Therefor i made my own program. The basic was the common program, and that i changed and added things.
What can my program do
- Stream live camera * http://IP --> for live stream
- Capture a image, needed for integration in domoticz * http://IP/capture --> for image capture, needed for domoticz
- Accepts http command, to activacted outputs on the ESP32-CAM * http://IP/ON or http://IP/OFF --> output LED ON and OFF
- push button input, which can activate output (button with LED) and send http to control IDX number
That are some functions.
But i also tried to putt all variables on top of the script.
So that people wo are not that familair with IDE, don't have to go tru the program, but can do all at the top of the program
Just to say, i am not a programmer. Just like it to do this stuff, but it could be that i made things that are not made by programmer rules.
For now i am making a drawing, to 3D print a housing for it.
Code: Select all
#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"
#include <HTTPClient.h>
//Invoeren van je network credentials
const char* ssid = "WiFi-SSID"; //Wifi SSID waarop ESP32-cam zich moet aanmelden.
const char* password = "WiFi-Password"; //Bijbehorend wachtwoord welke bij het gekozen SSID behoort.
//#define FixedIP //Haal de // weg om fixed IP te gebruiken, anders staat deze op DHCP.
#define IPaddr 192,168,178,111 //Voer IP adres is, digits tussen komma's invoeren.
#define SubnetAddr 255,255,255,0 //Voer subnet mask in, digits tussen de komma's invoeren.
#define GatewayAddr 192,168,178,254 //Voer Gateway adres in, digits tussen de komma's invoeren. Er moet een gateway ingevoerd worden, of deze ook correct is, wordt niet gecontroleerd.
//Invoeren van je domoticz credentials
String DomoticzIP = "192.168.178.110"; //Domoticz Server IP adres.
String DomoticzPort = "8080"; //Domoticz Server poort adres.
String DomoticzIDX = "1"; //Domoticz IDX nummer welke geschakeld moet worden.
String DomoticzSwitch = "On"; //Domoticz IDX state. Bij activeren ingang, moet IDX dan On of Off gestuurd worden?.
#define DomoticzResetSwitch //Domoticz, haal de // weg, dan wordt er een tegengesteld commando van DomoticzSwitch gestuurd nadat de ButtonTimer is verstreken.
//Voorbeeld als DomoticzResetSwitch defined is; Bij drukken gaat domoticz device op ON, na verstrijken ButtonTimer weer op OFF
//Invoeren van je push-button + LED credentials
const int buttonPin1 = 12; //GPIO nummer voor de ingang van drukknop.
const int ledPin1 = 13; //De tijd dat LED aangestuurd is. In deze tijd kan de ingang nog wel geactiveerd worden, maar programma doet er niets mee, totdat tijd verstreken is.
const int ledPin2 = 14; //De tijd dat LED aangestuurd is. In deze tijd kan de ingang nog wel geactiveerd worden, maar programma doet er niets mee, totdat tijd verstreken is.
const int ledPin3 = 15; //De tijd dat LED aangestuurd is. In deze tijd kan de ingang nog wel geactiveerd worden, maar programma doet er niets mee, totdat tijd verstreken is.
#define ButtonTimer 10000 //De tijd dat LED aangestuurd is. In deze tijd kan de ingang nog wel geactiveerd worden, maar programma doet er niets mee, totdat tijd verstreken is.
int buttonState1 = 0; //Zet de ingang op 0, zodat deze niet direct actief is bij opstarten.
#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
typedef struct {
httpd_req_t *req;
size_t len;
} jpg_chunking_t;
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;
}
#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;
}
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;
}
static esp_err_t ON_handler(httpd_req_t *req){
#define OKon "HTTP/1.1 200 OK, Output On"
httpd_resp_send(req, OKon, strlen(OKon)); // Response body can be empty
// Serial.println("TEST ON");
GpioVarOn();
}
static esp_err_t OFF_handler(httpd_req_t *req){
#define OKoff "HTTP/1.1 200 OK, Output OFF"
httpd_resp_send(req, OKoff, strlen(OKoff)); // Response body can be empty
// Serial.println("TEST OFF");
GpioVarOff();
}
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) {
Serial.println("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");
size_t out_len, out_width, out_height;
uint8_t * out_buf;
bool s;
{
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();
Serial.printf("JPG: %uB %ums\n", (uint32_t)(fb_len), (uint32_t)((fr_end - fr_start)/1000));
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
};
httpd_uri_t capture_uri = {
.uri = "/capture",
.method = HTTP_GET,
.handler = capture_handler,
.user_ctx = NULL
};
httpd_uri_t ON_uri = {
.uri = "/ON",
.method = HTTP_GET,
.handler = ON_handler,
.user_ctx = NULL
};
httpd_uri_t OFF_uri = {
.uri = "/OFF",
.method = HTTP_GET,
.handler = OFF_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);
httpd_register_uri_handler(stream_httpd, &ON_uri); //voor LED ON
httpd_register_uri_handler(stream_httpd, &OFF_uri); //voor LED OFF
httpd_register_uri_handler(stream_httpd, &capture_uri); //Still image capture
}
}
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
// initialize the LED pin as an output:
pinMode(ledPin1, OUTPUT);
pinMode(ledPin2, OUTPUT);
pinMode(ledPin3, OUTPUT);
// initialize the pushbutton pin as an input:
pinMode(buttonPin1, INPUT_PULLUP);
Serial.begin(115200);
Serial.setDebugOutput(false);
Serial.println();
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_SVGA;
config.jpeg_quality = 10;
config.fb_count = 2;
} else {
config.frame_size = FRAMESIZE_VGA;
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;
}
// Wi-Fi connection
WiFi.begin(ssid, password);
#if defined(FixedIP)
IPAddress ip(IPaddr);
IPAddress gateway(GatewayAddr);
IPAddress subnet(SubnetAddr);
WiFi.config(ip, gateway, subnet);
#endif
delay(5000);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.print("Camera Stream Ready! Go to: http://");
Serial.print(WiFi.localIP());
// Start streaming web server
startCameraServer();
}
void loop() {
buttonState1 = digitalRead(buttonPin1);
if (buttonState1 == LOW) {
digitalWrite(ledPin1, HIGH);
digitalWrite(ledPin2, HIGH);
digitalWrite(ledPin3, HIGH);
Serial.println("Start Button delay");
HTTPClient http;
String url="http://" + DomoticzIP + ":" + DomoticzPort + "/json.htm?type=command¶m=switchlight&idx=" + DomoticzIDX + "&switchcmd=" + DomoticzSwitch + "";
http.begin(url); //Specify destination for HTTP request
http.addHeader("Content-Type", "text/plain"); //Specify content-type header
int httpResponseCode = http.POST("POSTING from ESP32"); //Send the actual POST request
delay(ButtonTimer);
digitalWrite(ledPin2, LOW);
digitalWrite(ledPin3, LOW);
Serial.println("Start Button end");
#if defined(DomoticzResetSwitch)
if (DomoticzSwitch == "On") {
String url="http://" + DomoticzIP + ":" + DomoticzPort + "/json.htm?type=command¶m=switchlight&idx=" + DomoticzIDX + "&switchcmd=Off";
http.begin(url); //Specify destination for HTTP request
http.addHeader("Content-Type", "text/plain"); //Specify content-type header
int httpResponseCode = http.POST("POSTING from ESP32"); //Send the actual POST request
} else {
String url="http://" + DomoticzIP + ":" + DomoticzPort + "/json.htm?type=command¶m=switchlight&idx=" + DomoticzIDX + "&switchcmd=On";
http.begin(url); //Specify destination for HTTP request
http.addHeader("Content-Type", "text/plain"); //Specify content-type header
int httpResponseCode = http.POST("POSTING from ESP32"); //Send the actual POST request
}
#endif
}
}
// LED uitgang aan commando: http://ESP32-CAM-IP/ON
void GpioVarOn() {
Serial.println("Output On");
digitalWrite(ledPin1, HIGH);
}
// LED uitgang aan commando: http://ESP32-CAM-IP/OFF
void GpioVarOff() {
Serial.println("Output Off");
digitalWrite(ledPin1, LOW);
}