In the Video
Japanese【日本語】
English【英語】
1. Overview
We will do electronic work to watch the video taken by the camera on the web browser of the smartphone.
The product to be used is the TimerCamera of M5Stack.
This product implements a web server and distribution server to realize video distribution.
2. The development environment
Arduino was developed in Italy under the philosophy of “making things easier and easier to understand”.
Currently, it is widely used for learning all over the world, and the library is also substantial.
So, if you want to start electronic work, I think this is the only development environment.
Therefore, I use Arduino at this time.
3. Software
All program files are put together in this zip file. Please download as needed.
Listed below are the 3 files of the Arduino program in this zip file.
//*************************************************************************
// CameraWebAccess Ver2023.02.03
// Arduino Board : M5Stack-Timer-CAM [M5Stack ver 2.0.6]
// Written by IT-Taro
//***********************************************************************
#include <WiFi.h>
#include "esp_http_server.h"
#include "esp_camera.h"
// ################ for Battery Use###############
//#include "battery.h"
//#include "soc/rtc_cntl_reg.h" // for BrouwnoutDetector Disable
//#define BATTERY_ENABLE
// ####################### Wi-Fi settings (Preferences) #######################
// Wi-Fi settings
const char *ssid = "##### SSID #####";
const char *password = "### PASSWORD ###";
IPAddress ip(192, 168, 1, 123); // IP address (IP used by this machine)
IPAddress gateway(192, 168, 1, 1); // default gateway
IPAddress subnet(255, 255, 255, 0); // sub-net mask
IPAddress dns(192, 168, 1, 1); // DNS server[Required setting: WiFiClientSecure cannot be used with a fixed IP]
// ###################################################################
// pin arrangement etc.
const byte LED_PIN = 2; // green LED
// CAMERA_MODEL_M5_UNIT_CAM
#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
httpd_handle_t webServer = NULL;
httpd_handle_t streamServer = NULL;
void setup() {
Serial.begin(115200);
#ifdef BATTERY_ENABLE
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable detector
bat_init();
bat_hold_output();
#endif
//Serial.setDebugOutput(true);
//Serial.println();
// ####### CAMERA initial settings #######
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;
// Image size setting:QVGA(320x240),CIF(400x296),HVGA(480x320),VGA(640x480),SVGA(800x600),XGA(1024x768)
config.frame_size = FRAMESIZE_SVGA; // HTML needs to be sized
config.jpeg_quality = 10;
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;
}
/*sensor_t *s = esp_camera_sensor_get();
// initial sensors are flipped vertically and colors are a bit saturated
s->set_vflip(s, 1); // flip it back
s->set_brightness(s, 1); // up the blightness just a bit
s->set_saturation(s, -2); // lower the saturation
// drop down frame size for higher initial frame rate
s->set_framesize(s, FRAMESIZE_QVGA);*/
// ####### PIN setting start #######
pinMode ( LED_PIN, OUTPUT );
// ####### Wireless Wi-Fi connection #######
bool ledFlag = true;
WiFi.config( ip, gateway, subnet, dns );
WiFi.begin ( ssid, password );
while ( WiFi.status() != WL_CONNECTED ) { // Infinite loop processing until connected
// LED flashes every second while connected
ledFlag = !ledFlag;
digitalWrite(LED_PIN, ledFlag);
delay ( 1000 );
Serial.print ( "." );
}
// Wi-Fi connection completed (IP address display)
Serial.print ( "Wi-Fi Connected! IP address: " );
Serial.println ( WiFi.localIP() );
// LED lights when Wi-Fi is connected (Wi-Fi connection status)
digitalWrite ( LED_PIN, true );
// ####### HTTP Server settings and Start #######
stratHttpServer();
Serial.println("Setup Finished!");
}
void loop() {
delay(1);
}
void stratHttpServer(){
// webServer
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
//config.max_uri_handlers = 16; // 8 or more must be set
httpd_uri_t index_uri = {
.uri = "/",
.method = HTTP_GET,
.handler = index_handler,
.user_ctx = NULL};
httpd_uri_t capture_uri = {
.uri = "/capture",
.method = HTTP_GET,
.handler = capture_handler,
.user_ctx = NULL};
httpd_uri_t reset_uri = {
.uri = "/reset",
.method = HTTP_GET,
.handler = reset_handler,
.user_ctx = NULL};
httpd_uri_t stream_uri = {
.uri = "/stream",
.method = HTTP_GET,
.handler = stream_handler,
.user_ctx = NULL};
// Start the web(httpd) server
if (httpd_start(&webServer, &config) == ESP_OK) {
httpd_register_uri_handler(webServer, &index_uri);
httpd_register_uri_handler(webServer, &capture_uri);
httpd_register_uri_handler(webServer, &reset_uri);
}
// StremServer
config.server_port += 1;
config.ctrl_port += 1;
// Start the stream server
if (httpd_start(&streamServer, &config) == ESP_OK) {
/* Register URI handlers */
httpd_register_uri_handler(streamServer, &stream_uri);
}
}
static esp_err_t stream_handler( httpd_req_t *req ) {
#define PART_BOUNDARY "123456789000000000000987654321"
char strbuf[128];
esp_err_t res = ESP_OK;
camera_fb_t *fb = NULL;
static const char *_STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
static const char *_STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
Serial.println( "Start Stream!" );
// Send first reply packet
res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
if (res != ESP_OK) {
return res;
}
// Initial response packet header setting when sending image data (loop)
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
httpd_resp_set_hdr(req, "X-Framerate", "60");
// Repeat image transmission
while (true) {
// Get camera JPEG
fb = esp_camera_fb_get();
if (!fb) {
Serial.println( "Camera capture failed" );
res = ESP_FAIL;
break;
}
// send image separator
if (res == ESP_OK) {
res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
}
// send image header
if (res == ESP_OK) {
size_t hlen = snprintf((char *)strbuf, 128, "Content-Type: image/jpeg\r\nContent-Length: %u\r\nX-Timestamp: %d.%06d\r\n\r\n",
fb->len, fb->timestamp.tv_sec, fb->timestamp.tv_usec);
res = httpd_resp_send_chunk(req, (const char *)strbuf, hlen);
}
// Image JPEG data transmission
if (res == ESP_OK) {
res = httpd_resp_send_chunk(req, (const char *)fb->buf, fb->len);
}
// Camera termination processing
if (fb) {
esp_camera_fb_return(fb);
fb = NULL;
}
// Exit loop if response is unsuccessful
if (res != ESP_OK) {
Serial.println( "Stop Stream!" );
break;
}
}
return res;
}
static esp_err_t capture_handler( httpd_req_t *req ) {
camera_fb_t *fb = NULL;
esp_err_t res = ESP_OK;
Serial.println( "Start Capture!" );
// Get camera JPEG
fb = esp_camera_fb_get();
if (!fb) {
Serial.println( "Camera capture failed" );
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", "*");
res = httpd_resp_send_chunk(req, (const char *)fb->buf, fb->len);
// camera end processing
if (fb) {
esp_camera_fb_return(fb);
fb = NULL;
}
Serial.println( "Finish Capture!" );
return res;
}
static esp_err_t reset_handler( httpd_req_t *req ) {
Serial.println ( "Reset" );
char *resMsg = "OK Reboot!";
httpd_resp_send(req, resMsg, strlen(resMsg) );
delay(1000);
ESP.restart();
}
// HTML top response processing
static esp_err_t index_handler( httpd_req_t *req ) {
// response HTML definition
char *htmltop =
"<!DOCTYPE html><html lang=\"jp\"><head><meta charset=\"UTF-8\"/>\r\n"
"<style type=\"text/css\"><!--\r\n"
"#contents { max-width: 800px; }\r\n"
"img { width:100%;height:600px; background-color:grey; }\r\n"
"h1 { margin: 0px; font-size: 36px; }\r\n"
"button { width:150px;height:50px; font-size: 24px; }\r\n"
"footer { text-align: right; }\r\n"
".underTheEarthKai { background-image: radial-gradient(50% 150%, #CCCCCC 5%, #777777 100%); }\r\n"
"background-image: linear-gradient(-173deg, rgba(255,255,255,0.20) 0%, #000000 100%),\r\n"
"linear-gradient(72deg, rgba(255,255,255,0.25) 25%, rgba(0,0,0,0.25) 100%),\r\n"
"radial-gradient(47% 102%, rgba(255,255,255,0.50) 0%, rgba(21,24,32,0.60) 120%);background-blend-mode: multiply; }\r\n"
"#msg { vertical-align:middle; }\r\n"
".floatleft { float:left; }\r\n"
".floatright { float:right; }\r\n"
"--></style>\r\n"
"<title>M5Stack TimerCamera</title><link rel=\"shortcut icon\" href=\"https://hobby-it.com/favicon.ico\"></head>\r\n"
"<body class=\"underTheEarthKai\"><center><div id=\"contents\"><header><h1>Web Camera [M5Stack TimerCamera]</h1></header>\r\n"
"<div class=\"floatright\"><button id=\"capbtn\" type=\"button\" onclick=\"window.open('/reset')\">REBOOT</button></div>\r\n"
"<form id=\"canform\" method=\"get\" action=\"javascript:void(0);\">\r\n"
"<div id=\"contentImg\"><img id=\"live\"></div>\r\n"
"<div><div class=\"floatleft\">\r\n"
"<button id=\"startbtn\" onclick=\"wsConnect()\">START</button>\r\n"
"<button id=\"endbtn\" onclick=\"wsDisconnect()\">STOP</button>\r\n"
"</div><div class=\"floatright\">\r\n"
"<button id=\"clearbtn\" onclick=\"clearimg()\">CLEAR</button>\r\n"
"<button id=\"capbtn\" onclick=\"capture()\">ONE-SHOT</button>\r\n"
"</div></div><br><br><br>\r\n"
"<div><font color=\"red\" size=+3><span id=\"msg\">Please press the button</span></font></div>\r\n"
"<footer>©Hobby-IT</footer></form></div></center>\r\n"
"\r\n"
"<script language=\"javascript\" type=\"text/javascript\">\r\n"
"function dispMessage(message){ document.getElementById('msg').innerHTML = message;}\r\n"
"function wsConnect(){\r\n"
" document.getElementById(\"startbtn\").disabled = true;\r\n"
" var baseHost = document.location.origin;\r\n"
" var streamUrl = baseHost + ':81/stream';\r\n"
" document.getElementById('live').src = streamUrl;\r\n"
" dispMessage('Delivery started');\r\n"
"}\r\n"
"function wsDisconnect(){\r\n"
" window.stop();\r\n"
" document.getElementById(\"startbtn\").disabled = false;\r\n"
" dispMessage('Delivery finished');\r\n"
"}\r\n"
"function clearimg(){\r\n"
" document.getElementById('live').src='';\r\n"
" document.getElementById('live').remove();\r\n"
" var imgTag = document.createElement('img');\r\n"
" imgTag.id = \"live\";\r\n"
" document.getElementById('contentImg').appendChild(imgTag);\r\n"
" dispMessage('Cleared');\r\n"
"}\r\n"
"function capture(){\r\n"
" var baseHost = document.location.origin;\r\n"
" var captureUrl = baseHost + '/capture';\r\n"
" document.getElementById('live').src = captureUrl;\r\n"
" dispMessage('I took a picture');\r\n"
"}\r\n"
"</script></body></html>\r\n";
// Send response HTML
Serial.println("send htmltop");
return httpd_resp_send(req, htmltop, strlen(htmltop));
}
4. PartsList
As a web camera for electronic work, it compares the prices of items used.
Comments