The ESP32 C code that takes the picture and sends it to the
server
The Server Code
Hardware
Front side of the ESP32 Cam showing the ESP32 chip, WiFi antenna
and reset switch(hidden behind the red wire). The yellow and
white wires connect IO0 to gnd when connected. Thus allowing
programs to be uploaded to the chip.
The back side of the ESP32 Cam showing the camera, ribbon cable
and the camera connector. The "flash" LED can also be seen
(the yellow square in the lower left).
The silver metal bit under the camera is a micro SD Card
socket. Not being used at the moment.
An image of me taking the the above picture of the ESP32 Cam
captured by the ESP32 Cam.
This device goes from USB to RS232. Many other ESP development
boards have a built-in USB interface that provides both power and
the USB to RS232 conversion for uploads.
The ESP32 Cam does not have this so we need to supply it with this
FTDI device.
Where, from - Ali Express.
And I doubt that it is an appropriately licenced FTDI device.
The ESP32 and the Raspberry Pi do not seem to care.
The ESP32 Cam also came from Ali Express. $5.98 for the ESP32
Cam and the OV2640 Camera.
The camera is fixed focus and supports multiple resolutions from 320
x 240 to 1600 x 1200. I am using 800 x 600 to create a
reasonable picture without overwhelming my server. Each .jpg
image is between 20 and 60K.
// The following 3 values are updated by a request to giveTime.php
int capture_interval = 60000; // Milliseconds between
photo captures.
int delayNum =
60000;
// Default update delay
String takeNow =
"N";
// Take a picture now when "T" which comes from
// Set up the URLs so that the mac address can be sent so we can
differentiate between cameras.
String macAddr;
// Script that receives images and mac address
char post_url[80] =
"http://deid:xxx@drsol.com/~deid/espcam/getPicture.php?11:11:11:11:11:11";
// Get capture time, delay time and take now and flash flags from
server.
// Function to connect to wifi
bool init_wifi() {
int connAttempts = 0;
Serial.println("\r\nConnecting to: " + String(ssid));
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED ) {
delay(500);
Serial.print(".");
if (connAttempts > 10) return false;
connAttempts++;
}
return true;
}
// Function to handle http events
esp_err_t _http_event_handler(esp_http_client_event_t *evt) {
switch (evt->event_id) {
case HTTP_EVENT_ERROR:
Serial.println("HTTP_EVENT_ERROR");
break;
case HTTP_EVENT_ON_CONNECTED:
Serial.println("HTTP_EVENT_ON_CONNECTED");
break;
case HTTP_EVENT_HEADER_SENT:
Serial.println("HTTP_EVENT_HEADER_SENT");
break;
case HTTP_EVENT_ON_HEADER:
Serial.println();
Serial.printf("HTTP_EVENT_ON_HEADER, key=%s, value=%s",
evt->header_key, evt->header_value);
break;
case HTTP_EVENT_ON_DATA:
Serial.println();
Serial.printf("HTTP_EVENT_ON_DATA,
len=%d", evt->data_len);
if
(!esp_http_client_is_chunked_response(evt->client)) {
// Write out data
// printf("%.*s",
evt->data_len, (char*)evt->data);
}
break;
case HTTP_EVENT_ON_FINISH:
Serial.println("");
Serial.println("HTTP_EVENT_ON_FINISH");
break;
case HTTP_EVENT_DISCONNECTED:
Serial.println("HTTP_EVENT_DISCONNECTED");
break;
}
return ESP_OK;
}
// Function to take and send a photo
static esp_err_t take_send_photo() {
Serial.println("Taking picture...");
// Set up frame buffer
camera_fb_t * fb = NULL;
esp_err_t res = ESP_OK;
// get a picture and punt if it fails
if (doFlash) {
digitalWrite(4, HIGH);
}
delay(10);
fb = esp_camera_fb_get();
digitalWrite(4, LOW);
if (!fb) {
Serial.println("Camera capture failed");
return ESP_FAIL;
}
// Set up to send picture to the server
// Get a handle
//Set up an array/structure for the http config
esp_http_client_config_t config_client = {0};
// Set the URL
config_client.url =
post_url;
// Set the event handler function
config_client.event_handler = _http_event_handler;
// Set the method e.g. POST, not GET
config_client.method = HTTP_METHOD_POST;
// Initialize the http client
http_client = esp_http_client_init(&config_client);
// POST data comes from fb (the picture)
esp_http_client_set_post_field(http_client, (const char
*)fb->buf, fb->len);
// and it is an image.
esp_http_client_set_header(http_client, "Content-Type",
"image/jpg");
// and finally do the request.
esp_err_t err = esp_http_client_perform(http_client);
// If all is well print the result.
if (err == ESP_OK) {
Serial.print("esp_http_client_get_status_code:
");
Serial.println(esp_http_client_get_status_code(http_client));
}
// Clean up
esp_http_client_cleanup(http_client);
esp_camera_fb_return(fb);
}
//Function to get capture time, delay time, takeNow and doFlash.
void getRepeat() {
// I'm using a different http library as I know it and this
// is my code now.
HTTPClient http;
// Request data
http.begin(time_url);
int stat = http.GET();
// If the return status is OK get the returned data string.
if (stat > 0) {
String text = http.getString();
Serial.println(text);
// Parse the string into capture time, delay
time and take immediate picture and flash flags
int comma1Pos = text.indexOf(',');
String capture = text.substring(0, comma1Pos);
Serial.println(capture);
int comma2Pos = text.indexOf(',', comma1Pos +
1);
String delayString = text.substring(comma1Pos +
1, comma2Pos);
takeNow = text.substring(comma2Pos + 1,
comma2Pos + 2);
// Set the flash flag
if (text.substring(comma2Pos + 3, comma2Pos +
4) == "F") {
doFlash = true;
} else {
doFlash = false;
}
delayNum = delayString.toInt() * 1000;
capture_interval = capture.toInt() * 1000;
// If the takeNow value is T then take a
picture now.
if (takeNow.compareTo("T") == 0) {
take_send_photo();
}
// Attempt to connect to WiFi and display result
if (init_wifi()) {
internet_connected = true;
Serial.println("Internet connected");
}
// Pick up mac address as it is unique so we know which
camera this is.
macAddr = WiFi.macAddress();
// Append the mac address to the urls
macAddr.toCharArray(post_url+57, 18);
macAddr.toCharArray(time_url+55, 18);
Serial.println(post_url);
Serial.println(time_url);
// If pseudo ram is available use larger frame buffer and
better jpeg quality
// The following are various choices.
// jpeg quality is from 0 to 63 with 0 being best and
probably too much for the esp.
/*
FRAMESIZE_UXGA (1600 x 1200)
FRAMESIZE_QVGA (320 x 240)
FRAMESIZE_CIF (352 x 288)
FRAMESIZE_VGA (640 x 480)
FRAMESIZE_SVGA (800 x 600)
FRAMESIZE_XGA (1024 x 768)
FRAMESIZE_SXGA (1280 x 1024)
*/
/* This if has been commented out to force svga
so as to not have a stupid amount of
space used for no good reason
A future enhancement might be to have
this switched by data from giveTime.php
// Initilize the camera with the configuration defined
above.
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error
0x%x", err);
return;
}
// Take a photo when we first start.
take_send_photo();
}
// ---- Loop ----
void loop() {
// Check if it is time to take another picture - and if so
do it.
current_millis = millis();
Serial.println(current_millis);
if (current_millis - last_capture_millis >
capture_interval) {
last_capture_millis = millis();
take_send_photo();
}
// Get the delay time etc/ from the web. This should
be a loop, not just a delay.
if (WiFi.status() != WL_CONNECTED) {
ESP.restart();
}
getRepeat();
delay(delayNum);
}
Server Code
Control and Display
Server PHP Scripts
setCaptureInterval.php -
Set the capture interval and delay time
Set take picture immediately after delay time
Turns the flash on and off
Display the existing pictures.
setCaptureIntervalFile.php
Put the updated data from setCaptureInterval into a file.
giveTime.php
Give the above times and "immediate" and flash request to
the ESP32 Cam when requested.
getPicture.php
Grab and save a picture that the ESP32 Cam has sent.
setCaptureInterval.php - Get The Data - Block
Diagram/flowchart
setCaptureInterval.php - get the data - Code
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01
Transitional//EN">
<?php
// http://drsol.com/~deid/esp/setCaptureInterval.php
// Display images and change the parameters of image capture
// Has a camera been choosen? If not display camera choice,
// otherwise display the camera images and form
if (! isset ($_GET['camera'])) {
?>
<!-- Display the camera choice form -->
<html>
<head>
<meta name=viewport content="width=device-width,
initial-scale=1.0">
</head>
<body>
<h1>ESP32 Cam</h1>
<h2>Choose Camera</h2>
<form method="get" action="setCaptureInterval.php">
Camera 1: <input type="radio" name="camera"
value=1><br>
Camera 2: <input type="radio" name="camera"
value=2><br>
<input type="submit">
</form>
</body></html>
<?php
exit();
// Display the Camera images and form
} else {
$camera = $_GET['camera'] ;
}
// Define file locations and get a reverse sorted list of all the
picture files
$files = glob ("picts".$camera . "/*.jpg");
rsort($files);
$fileName = "/home/deid/esp32camCaptureInfo.txt";
// Read the file that contains the time and flags so we can
populate the html with existing values.
$captureInfo = file($fileName);
list($time, $delay, $take, $flash) = explode(",",
$captureInfo[$camera-1]);
// Should the flash box be pre-checked?
if (trim($flash) == 'F') {
$checked = 'checked';
} else {
$checked = '';
}
?>
<!---
Display a form for gathering the data. Prefilled with the current
status read from the file above.
--->
<html>
<head>
<meta name=viewport
content="width=device-width, initial-scale=1.0">
<meta http-equiv="content-type"
content="text/html; charset=UTF-8">
<meta http-equiv="refresh" content="60;
URL=http://drsol.com/~deid/esp/setCaptureInterval.php?camera=<?php
echo $camera ?>" />
<title>Set Capture Intervals
etc.</title>
</head>
<body>
<h1>ESP32 Cam <?php echo
$camera ?></h1>
<form method="post"
action="setCaptureIntervalFile.php?camera=<?php echo $camera;
?>">
Capture Interval in seconds:
<input type="text" size="5" name="time" value=<?php echo
$time; ?>> <br>
Check for request delay: <input
type="text" size="5" name="delay" value=<?php echo $delay;
?>><br>
Take a picture now: <input
type="checkbox" name="take" value="T"><br>
Flash on: <input type="checkbox"
name="flash" value="F" <?php echo $checked; ?>><br>
<input type="submit"
value="Update">
</form><br>
<!-- Change Camera Link -->
<a
href="setCaptureInterval.php"><b>Change
Camera</b></a>
<h3>Recent pictures:</h3><p>
<table width="80%"><tr>
<?php
// Display the recent pictures and links to older pictures.
// Put the images and captions in a table. Both linked to the
larger image.
for ($j=0; $j<4; $j=$j+3) {
// Add the pictures
for($i=0; $i<3; $i++) {
echo '<td><a href="' . $files[$i+$j] .
'">';
echo '<img width="100%" src="' .
$files[$i+$j] . '"></a></td> ';
}
echo "</tr><tr>";
// Add the captions
for($i=0; $i<3; $i++) {
echo '<td style="text-align:
center";><a href="' . $files[$i+$j] . '">';
echo date('Y-m-d H:i',
filemtime($files[$i+$j])) . "</td>";
}
echo "</tr><tr>";
}
echo "</tr></table>";
// Display all the rest of the links to the pictures
echo "<p><b>The rest of the
pictures</b><br>";
for ($i=6; $i<count($files); $i++){
echo '<a href="' . $files[$i] . '">'. date('Y-m-d
H:i', filemtime($files[$i])) . '</a><br>';
echo "\n";
}
?>
<br>
Done:
</body>
</html>
setCaptureIntervalFile.php - put the data into a file - Block
diagram/flowchart
setCaptureIntervalFile.php - put the data into a file - Code
<?php
// Put the form data into the data file.
// http://drsol.com/~deid/esp/setCaptureIntervalFile.php
// Set data file name
$fileName = "/home/deid/esp32camCaptureInfo.txt";
// Get the camera number
$camera = $_GET['camera'];
echo "<html><head>";
// Send us back to the input form page after a few seconds
echo "<meta http-equiv=\"refresh\" content=\"4;
URL=http://drsol.com/~deid/esp/setCaptureInterval.php?camera=$camera\"
/>";
echo "</head><body>";
// Read the the data file into an array
$Ptr = fopen($fileName, 'r+');
if (!$Ptr) {
exit("Could not open data file");
}
// Lock and read the file into an array;
flock($Ptr, LOCK_EX);
while (!feof($Ptr)) {
$line = fgets($Ptr);
if ($line) {
$captureInfo[] = $line;
}
}
// Get the data from the input form
// Number of seconds between pictures
$time = $_POST['time'];
// Get take picture now and flash on. They will be
respectively T or F if checked and nothing if not.
// So set the "nothing" to N for both take and flash
if (isset($_POST['take'])) {
$take = $_POST['take'];
} else {
$take = 'N';
}
if (isset($_POST['flash'])) {
$flash = $_POST['flash'];
} else {
$flash = 'N';
}
$delay = $_POST['delay'];
// Update the correct array entry;
$captureInfo[$camera-1] = "$time,$delay,$take,$flash";
// Empty the file
ftruncate($Ptr, 0);
rewind($Ptr);
// Write the data to the file.
echo "<h2>Thanks</h2>Wrote the data:<br>";
foreach ($captureInfo as $i) {
if (fwrite($Ptr, trim($i) . "\n")) {
echo "$i<br>";
} else {
echo "Oops. Did not write.
<br>$i<br>";
}
}
// Unlock and close the file and we are done
fflush($Ptr);
flock($Ptr, LOCK_UN);
fclose($Ptr);
?>
</body></html>
giveTime.php called by the ESP32 Cam to get times etc.
Block diagram/flowchart
giveTime.php called by the ESP32 Cam to get times etc. - Code
<?php
// giveTime.php
// This script is requested by the ESP32 Cameras
// It give the capture and delay times and "immediate" and flash
request to the ESP 32 Cam when requested.
require("whichcamera.php");
// Set minimum times. Seconds
$min = 60;
// Read the data file into an array.
$fileName = "/home/deid/esp32camCaptureInfo.txt";
$captureInfo = file($fileName);
// Get the camera number
$mac = $_SERVER['QUERY_STRING'];
$camera = trim(camera($mac));
// If we have a new camera;
if (count($captureInfo) < $camera) {
$time = 3600;
$delay = 300;
$take = 'N';
$flash = 'N';
$captureInfo[$camera] = "$time,$delay,$take,$flash";
}
// Split the entry for this camera into the four variables.
$line = $captureInfo[$camera-1];
list($time, $delay, $take, $flash) = explode(",", $line);
// Check the numbers for validity.
if ($time < $min) {
$time = $min;
}
if ($delay < $min) {
$delay = $min;
}
// write the data out thus sending a reply to the ESP32 Cam
// and close the file for read.
echo "$time,$delay,$take,$flash";
// Open the file for write and lock.
// and clear the take "immediate" picture now flag.
$Ptr = fopen($fileName, 'w') or die("no open");
flock($Ptr, LOCK_EX);
// Create a name using the epoch to make it a unique name.
$fileToWrite =
"/home/deid/public_html/espcam/picts$camera/ddpload-".time().".jpg";
// Write the file.
// Note one has to mess with permissions to allow the web server
to do this.
file_put_contents($fileToWrite, $received);
Function camera();
<?php
function camera ($mac) {
// Open the data file and lock it
$FH = fopen("/home/deid/cameras.dat", 'a+');
flock ($FH, LOCK_EX);
// Read the file into an asociative array
while (!feof($FH)) {
$line = fgets($FH);
if ($line == "") {
break;
}
list($k, $v) = explode (',', $line);
$acam[$k] = $v;
}
// Is our mac in there? Return the value if yes.
if (isset($acam[$mac])) {
flock ($FH, LOCK_UN);
fclose($FH);
return trim($acam[$mac]);
} else {
// Not there - add a new camera
$v = count($acam) + 1;
fwrite($FH, "$mac,$v\n");
flock ($FH, LOCK_UN);
fclose($FH);
return trim($v);
}
}
?>
Data File camera.dat - used to determine which camera this is
1,1
2,2
AC:67:B2:2E:14:D0,3
Data File esp32camCaptureInfo.txt - contains time etc. settings
for the cameras