Skip to content

Commit

Permalink
Merge branch '4-0-rc1' into framerate_limit
Browse files Browse the repository at this point in the history
  • Loading branch information
easytarget authored Mar 9, 2022
2 parents 20f4c24 + d064e88 commit cfe81b0
Show file tree
Hide file tree
Showing 15 changed files with 300 additions and 185 deletions.
12 changes: 6 additions & 6 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ branches:
before_script:
- "export DISPLAY=:99.0"
- sleep 3 # give xvfb some time to start
- wget https://downloads.arduino.cc/arduino-1.8.18-linux64.tar.xz
- tar xf arduino-1.8.18-linux64.tar.xz
- mv arduino-1.8.18 $HOME/arduino_ide
- wget https://downloads.arduino.cc/arduino-1.8.19-linux64.tar.xz
- tar xf arduino-1.8.19-linux64.tar.xz
- mv arduino-1.8.19 $HOME/arduino_ide
- cd $HOME/arduino_ide/hardware
- mkdir esp32
- cd esp32
- wget https://github.com/espressif/arduino-esp32/archive/refs/tags/2.0.1.tar.gz
- tar -xzf 2.0.1.tar.gz
- mv arduino-esp32-2.0.1/ esp32
- wget https://github.com/espressif/arduino-esp32/archive/refs/tags/2.0.2.tar.gz
- tar -xzf 2.0.2.tar.gz
- mv arduino-esp32-2.0.2/ esp32
- cd esp32/tools
- python --version
- python get.py
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Pull requests are the best way to propose changes to the codebase (I use [Github
1. Fork the repo and create your branch from `master`.
2. Give your branch a clear descriptive name and do your changes there.
3. If you've changed the HTTP APIs, update the documentation.
4. Issue a pull request against a branch *of the same name* in the main repo.
4. Issue a pull request against the master branch in the main repo.
5. Clearly describe your changes and the reason for them in the pull request.

## Any contributions you make will be under the GNU Lesser General Public License v2.1
Expand Down
26 changes: 12 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ But expanded with:
* Over The Air firmware updates
* Lots of minor fixes and tweaks, documentation etc.

And 'reduced' by removing the Face Recognition features
And 'reduced' by removing the Face Recognition features
* **If you want to try the Face Recognition features** please use the [`3.x` maintenance branch](https://github.com/easytarget/esp32-cam-webserver/tree/3.x), which still recieves bugfixes, but is not receiving any further development.
* They were a demo, only worked in low resolution modes, did not preserve the face database between power cycles, and were of little use in real-world applications.
* There are other (specialised) sketches for the ESP-CAM that do use face recognitioni more effectively, if this is your thing :-)
Expand All @@ -33,7 +33,7 @@ I have four [AI-THINKER ESP32-CAM](https://github.com/raphaelbs/esp32-cam-ai-thi
https://github.com/raphaelbs/esp32-cam-ai-thinker
* The AI thinker wiki can be quite informative, when run through an online translator and read sensibly:
https://wiki.ai-thinker.com/esp32-cam
* Default pinouts are also included for WRover Kit, ESP Eye and M5Stack esp32 camera modules.
* Default pinouts are also included for WRover Kit, ESP Eye and M5Stack esp32 camera modules.
I do not have any of these boards, so they are untested by me. Please [let me know](https://github.com/easytarget/esp32-cam-webserver/issues) if you find issues or have a board not [in the list](./camera_pins.h).

## Troubleshooting:
Expand All @@ -48,7 +48,11 @@ The ESP itself is susceptible to the usual list of WiFi problems, not helped by
A basic limitation of the sketch is that it can can only support one stream at a time. If you try to connect to a cam that is already streaming (or attempting to stream) you will get no response and, eventually, a timeout. The stream itself is a [MJPEG stream](https://en.wikipedia.org/wiki/Motion_JPEG), which relies on the client (the web browser) to hold the connection open and request each new frame in turn via javascript. This can cause errors when browsers run into Javascript or caching problem, fail to request new frames or refuse to close the connection.
* You can check the `/dump` page of the cam to see if it currently reports the camera as streaming or not.

The existing [issues list](https://github.com/easytarget/esp32-cam-webserver/issues?q=is%3Aissue) on Github is a good place to start if you have a specific issue not covered above.
A lot of common issues with this sketch are discussed and covered in the discussion forums:

https://github.com/easytarget/esp32-cam-webserver/discussions/categories/common-issues

The existing [issues list](https://github.com/easytarget/esp32-cam-webserver/issues?q=is%3Aissue) on Github is a good place to start if you have a specific issue not covered above or in the forums.

Note that I do not respond to any Private Messages (via github, hackaday, or wherever) for support.

Expand All @@ -74,7 +78,7 @@ Is pretty simple, You just need jumper wires, no soldering really required, see
Download the latest release of the sketch from https://github.com/easytarget/esp32-cam-webserver/releases/latest
- You can get the latest stable development release by cloning / downloading the `master` branch of the repo.

This will give you an archive file with the Version number in it, eg.`esp32-cam-webserver-3.0.zip`. Tou need to unpack this into your Arduino sketch folder, and then you need to rename the folder you just extracted to remove the version number, eg.`esp32-cam-webserver-3.0` becomes `esp32-cam-webserver`.
This will give you an archive file with the Version number in it, eg.`esp32-cam-webserver-4.0.zip`. You need to unpack this into your Arduino sketch folder, and then you need to **rename the folder you extracted to remove the version number**, eg.`esp32-cam-webserver-4.0` becomes `esp32-cam-webserver`.

Once you have done that you can open the sketch in the IDE by going to the `esp32-cam-webserver` sketch folder and selecting `esp32-cam-webserver.ino`.

Expand All @@ -84,7 +88,7 @@ By default the sketch assumes you have an AI-THINKER board, it creates an Access

To make a permanent config with your home wifi settings, different defaults or a different board; copy (or rename) the file `myconfig.sample.h` in the sketch folder to `myconfig.h` and edit that, all the usable defaults are in that file. Because this is your private copy of the config it will not get overwritten if you update the main sketch!

### Programming
### Programming

Assuming you are using the latest Espressif Arduino core the `ESP32 Dev Module` board will appear in the ESP32 Arduino section of the boards list. Select this (do not use the `AI-THINKER` entry listed in the boiards menu, it is not OTA compatible, and will caus the module to crash and reboot rather than updating if you use it.
![IDE board config](Docs/ota-board-selection.png)
Expand All @@ -107,7 +111,7 @@ Go to the URL given in the serial output, the web UI should appear with the sett

The WiFi details can be stored in an (optional) header file to allow easier code development, and a camera name for the UI title can be configured. The lamp and status LED's are optional, and the lamp uses a exponential scale for brightness so that the control has some finess.

All of the face recognition code has been removed as of V4.0; this reduces the code size enough to allow OTA programming while improving compile and programming times.
All of the face recognition code has been removed as of V4.0; this reduces the code size enough to allow OTA programming while improving compile and programming times.

The compressed and binary encoded HTML used in the example has been unpacked to raw text, this makes it much easier to access and modify the Javascript and UI elements. Given the relatively small size of the index page there is very little benefit from compressing it.

Expand Down Expand Up @@ -141,17 +145,11 @@ Contributions are welcome; please see the [Contribution guidelines](CONTRIBUTING

Time allowing; my Current plan is:

V4
* Remove face recognition entirely;
* **Done**, see the `NoFace` branch :sunglasses:
* Not optional, this is a code and maintenance nightmare. V3 can be maintained on a branch for those who need it.
V4
* Investigate using SD card to capture images
* Implement OTA and a better network stack for remembering multiple AP's, auto-config etc.
* **Basic OTA is Done**, see the `NoFace` branch.
* Implement a better network stack for remembering multiple AP's, auto-config etc.
* Advanced (web upload) OTA might be nice to have if possible
* For the Network setup I want to implement https://github.com/Hieromon/AutoConnect
* UI Skinning/Theming
* OSD
* Temperature/humidity/pressure sensor support (bme20,dht11)
You can check the [enhancement list](https://github.com/easytarget/esp32-cam-webserver/issues?q=is%3Aissue+label%3Aenhancement) (past and present), and add any thoughts you may have there.

53 changes: 43 additions & 10 deletions app_httpd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ extern int sketchSpace;
extern String sketchMD5;
extern bool otaEnabled;
extern char otaPassword[];
extern unsigned long xclk;

typedef struct {
httpd_req_t *req;
Expand All @@ -78,6 +79,9 @@ static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %
httpd_handle_t stream_httpd = NULL;
httpd_handle_t camera_httpd = NULL;

// Flag that can be set to kill all active streams
bool streamKill;

#ifdef __cplusplus
extern "C" {
#endif
Expand Down Expand Up @@ -143,7 +147,7 @@ void serialDump() {
int McuTf = temprature_sens_read(); // fahrenheit
Serial.printf("System up: %" PRId64 ":%02i:%02i:%02i (d:h:m:s)\r\n", upDays, upHours, upMin, upSec);
Serial.printf("Active streams: %i, Previous streams: %lu, Images captured: %lu\r\n", streamCount, streamsServed, imagesServed);
Serial.printf("Freq: %i MHz\r\n", ESP.getCpuFreqMHz());
Serial.printf("CPU Freq: %i MHz, Xclk Freq: %i MHz\r\n", ESP.getCpuFreqMHz(), xclk);
Serial.printf("MCU temperature : %i C, %i F (approximate)\r\n", McuTc, McuTf);
Serial.printf("Heap: %i, free: %i, min free: %i, max block: %i\r\n", ESP.getHeapSize(), ESP.getFreeHeap(), ESP.getMinFreeHeap(), ESP.getMaxAllocHeap());
if(psramFound()) {
Expand Down Expand Up @@ -173,7 +177,10 @@ static esp_err_t capture_handler(httpd_req_t *req){
esp_err_t res = ESP_OK;

Serial.println("Capture Requested");
if (autoLamp && (lampVal != -1)) setLamp(lampVal);
if (autoLamp && (lampVal != -1)) {
setLamp(lampVal);
delay(75); // coupled with the status led flash this gives ~150ms for lamp to settle.
}
flashLED(75); // little flash of status LED

int64_t fr_start = esp_timer_get_time();
Expand All @@ -199,12 +206,16 @@ static esp_err_t capture_handler(httpd_req_t *req){
Serial.println("Capture Error: Non-JPEG image returned by camera module");
}
esp_camera_fb_return(fb);
fb = NULL;

int64_t fr_end = esp_timer_get_time();
if (debugData) {
Serial.printf("JPG: %uB %ums\r\n", (uint32_t)(fb_len), (uint32_t)((fr_end - fr_start)/1000));
}
imagesServed++;
if (autoLamp && (lampVal != -1)) setLamp(0);
if (autoLamp && (lampVal != -1)) {
setLamp(0);
}
return res;
}

Expand All @@ -215,6 +226,8 @@ static esp_err_t stream_handler(httpd_req_t *req){
uint8_t * _jpg_buf = NULL;
char * part_buf[64];

streamKill = false;

Serial.println("Stream requested");
if (autoLamp && (lampVal != -1)) setLamp(lampVal);
streamCount = 1; // at present we only have one stream handler, so values are 0 or 1..
Expand Down Expand Up @@ -273,7 +286,7 @@ static esp_err_t stream_handler(httpd_req_t *req){
free(_jpg_buf);
_jpg_buf = NULL;
}
if(res != ESP_OK){
if((res != ESP_OK) || streamKill){
// This is the only exit point from the stream loop.
// We end the stream here only if a Hard failure has been encountered or the connection has been interrupted.
break;
Expand Down Expand Up @@ -340,6 +353,7 @@ static esp_err_t cmd_handler(httpd_req_t *req){
if(s->pixformat == PIXFORMAT_JPEG) res = s->set_framesize(s, (framesize_t)val);
}
else if(!strcmp(variable, "quality")) res = s->set_quality(s, val);
else if(!strcmp(variable, "xclk")) { xclk = val; res = s->set_xclk(s, LEDC_TIMER_0, val); }
else if(!strcmp(variable, "contrast")) res = s->set_contrast(s, val);
else if(!strcmp(variable, "brightness")) res = s->set_brightness(s, val);
else if(!strcmp(variable, "saturation")) res = s->set_saturation(s, val);
Expand Down Expand Up @@ -389,6 +403,7 @@ static esp_err_t cmd_handler(httpd_req_t *req){
if (filesystem) removePrefs(SPIFFS);
}
else if(!strcmp(variable, "reboot")) {
if (lampVal != -1) setLamp(0); // kill the lamp; otherwise it can remain on during the soft-reboot
esp_task_wdt_init(3,true); // schedule a a watchdog panic event for 3 seconds in the future
esp_task_wdt_add(NULL);
periph_module_disable(PERIPH_I2C0_MODULE); // try to shut I2C down properly
Expand Down Expand Up @@ -422,6 +437,7 @@ static esp_err_t status_handler(httpd_req_t *req){
p+=sprintf(p, "\"min_frame_time\":%d,", minFrameTime);
p+=sprintf(p, "\"framesize\":%u,", s->status.framesize);
p+=sprintf(p, "\"quality\":%u,", s->status.quality);
p+=sprintf(p, "\"xclk\":%u,", xclk);
p+=sprintf(p, "\"brightness\":%d,", s->status.brightness);
p+=sprintf(p, "\"contrast\":%d,", s->status.contrast);
p+=sprintf(p, "\"saturation\":%d,", s->status.saturation);
Expand Down Expand Up @@ -496,7 +512,7 @@ static esp_err_t logo_svg_handler(httpd_req_t *req){

static esp_err_t dump_handler(httpd_req_t *req){
flashLED(75);
Serial.println("\r\nDump Requested via Web");
Serial.println("\r\nDump requested via Web");
serialDump();
static char dumpOut[2000] = "";
char * d = dumpOut;
Expand All @@ -509,7 +525,7 @@ static esp_err_t dump_handler(httpd_req_t *req){
d+= sprintf(d,"<link rel=\"stylesheet\" type=\"text/css\" href=\"/style.css\">\n");
d+= sprintf(d,"</head>\n");
d+= sprintf(d,"<body>\n");
d+= sprintf(d,"<img src=\"/logo.svg\" style=\"position: relative; float: right;\">\n");
d+= sprintf(d,"<img src=\"/logo.svg\" style=\"position: relative; float: right;\">\n");
if (critERR.length() > 0) {
d+= sprintf(d,"<span style=\"color:red;\">%s<hr></span>\n", critERR.c_str());
d+= sprintf(d,"<h2 style=\"color:red;\">(the serial log may give more information)</h2><br>\n");
Expand Down Expand Up @@ -570,7 +586,7 @@ static esp_err_t dump_handler(httpd_req_t *req){

d+= sprintf(d,"Up: %" PRId64 ":%02i:%02i:%02i (d:h:m:s)<br>\n", upDays, upHours, upMin, upSec);
d+= sprintf(d,"Active streams: %i, Previous streams: %lu, Images captured: %lu<br>\n", streamCount, streamsServed, imagesServed);
d+= sprintf(d,"Freq: %i MHz<br>\n", ESP.getCpuFreqMHz());
d+= sprintf(d,"CPU Freq: %i MHz, Xclk Freq: %i MHz<br>\n", ESP.getCpuFreqMHz(), xclk);
d+= sprintf(d,"<span title=\"NOTE: Internal temperature sensor readings can be innacurate on the ESP32-c1 chipset, and may vary significantly between devices!\">");
d+= sprintf(d,"MCU temperature : %i &deg;C, %i &deg;F</span>\n<br>", McuTc, McuTf);
d+= sprintf(d,"Heap: %i, free: %i, min free: %i, max block: %i<br>\n", ESP.getHeapSize(), ESP.getFreeHeap(), ESP.getMinFreeHeap(), ESP.getMaxAllocHeap());
Expand All @@ -590,6 +606,7 @@ static esp_err_t dump_handler(httpd_req_t *req){
// Footer
d+= sprintf(d,"<br><div class=\"input-group\">\n");
d+= sprintf(d,"<button title=\"Instant Refresh; the page reloads every minute anyway\" onclick=\"location.replace(document.URL)\">Refresh</button>\n");
d+= sprintf(d,"<button title=\"Stop any active streams\" onclick=\"let throwaway = fetch('stop');setTimeout(function(){\nlocation.replace(document.URL);\n}, 200);\">Stop Stream</button>\n");
d+= sprintf(d,"<button title=\"Close this page\" onclick=\"javascript:window.close()\">Close</button>\n");
d+= sprintf(d,"</div>\n</body>\n");
// A javascript timer to refresh the page every minute.
Expand All @@ -601,6 +618,15 @@ static esp_err_t dump_handler(httpd_req_t *req){
return httpd_resp_send(req, dumpOut, strlen(dumpOut));
}

static esp_err_t stop_handler(httpd_req_t *req){
flashLED(75);
Serial.println("\r\nStream stop requested via Web");
streamKill = true;
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
return httpd_resp_send(req, NULL, 0);
}


static esp_err_t style_handler(httpd_req_t *req){
httpd_resp_set_type(req, "text/css");
httpd_resp_set_hdr(req, "Content-Encoding", "identity");
Expand All @@ -609,15 +635,15 @@ static esp_err_t style_handler(httpd_req_t *req){

static esp_err_t streamviewer_handler(httpd_req_t *req){
flashLED(75);
Serial.println("Stream Viewer requested");
Serial.println("Stream viewer requested");
httpd_resp_set_type(req, "text/html");
httpd_resp_set_hdr(req, "Content-Encoding", "identity");
return httpd_resp_send(req, (const char *)streamviewer_html, streamviewer_html_len);
}

static esp_err_t error_handler(httpd_req_t *req){
flashLED(75);
Serial.println("Sending Error page");
Serial.println("Sending error page");
std::string s(error_html);
size_t index;
while ((index = s.find("<APPURL>")) != std::string::npos)
Expand Down Expand Up @@ -705,7 +731,7 @@ static esp_err_t index_handler(httpd_req_t *req){

void startCameraServer(int hPort, int sPort){
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
config.max_uri_handlers = 12; // we use more than the default 8 (on port 80)
config.max_uri_handlers = 16; // we use more than the default 8 (on port 80)

httpd_uri_t index_uri = {
.uri = "/",
Expand Down Expand Up @@ -767,6 +793,12 @@ void startCameraServer(int hPort, int sPort){
.handler = dump_handler,
.user_ctx = NULL
};
httpd_uri_t stop_uri = {
.uri = "/stop",
.method = HTTP_GET,
.handler = stop_handler,
.user_ctx = NULL
};
httpd_uri_t stream_uri = {
.uri = "/",
.method = HTTP_GET,
Expand Down Expand Up @@ -817,6 +849,7 @@ void startCameraServer(int hPort, int sPort){
httpd_register_uri_handler(camera_httpd, &favicon_ico_uri);
httpd_register_uri_handler(camera_httpd, &logo_svg_uri);
httpd_register_uri_handler(camera_httpd, &dump_uri);
httpd_register_uri_handler(camera_httpd, &stop_uri);
}

config.server_port = sPort;
Expand Down
Loading

0 comments on commit cfe81b0

Please sign in to comment.