Pan and Tilt Control for an ESP32-CAM


Mounting an ESP32-CAM into a pan and tilt platform with a mobile phone touch interface to control and view the video stream.

This servo part of this project was much harder to make work than with other ESP32 modules. The ESP32-CAM has limited resources available and at every turn it presented a new problem!

With a standard ESP32, controlling servo motors is easy using the ESP32Servo library. For example this pan and tilt sketch controls the 3D printed pan and tilt platform below. You can even power the servos from the 5v pin of the micro-controller.

Lolin ESP32 Pan and Tilt

Attempting to use this library with the ESP32-CAM however led to instant hangs. From my experiments it appears there is a clash on the channels. I found some different code that allows the channels to be chosen and after a few tests I found I could use channels 2 and 4 with the ESP32-CAM.

Mounting the ESP32-CAM in a pan and tilt platform was also a bit tricky. In the end I 3D printed some parts and glued and bolted it together. It doesn’t look great and could do with being designed from the ground up but this is just a proof of concept for later projects so it doesn’t matter

ESP32-CAM Pan and Tilt Platform

I also tried various web server libraries and the ESPAsyncServer library seemed to work the best. I also moved to WebSockets for the data transfer because it seemed to be more efficient and not queue up requests from the mobile phone.

ESP32-CAM Pan and Tilt Wiring Diagram

ESP32-CAM Pan Tilt Code

If you need help with the ArduinoWebSockets or ESPAsyncWebServer libraries please see this tutorial:

#include <ArduinoWebsockets.h>
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include "esp_camera.h"
#include "soc/soc.h"      
#include "soc/rtc_cntl_reg.h"

int index_html_gz_len = 3863;
const uint8_t index_html_gz[] = {
  0x1f, 0x8b, 0x08, 0x08, 0x66, 0x84, 0x3d, 0x5d, 0x00, 0xff, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x2e, 0x67, 0x7a, 0x00, 0x95, 0x58, 0x7b, 0x6f, 0x1a, 0x3b, 0x16, 0xff, 0x3b, 0x7c, 0x0a, 0x97, 0x68, 0x0b, 0x6c, 0x61, 0x86, 0x40, 0xdb, 0x6d, 0x28, 0xe4, 0x6a, 0xdb, 0x44, 0x6a, 0xa5, 0x56, 0xad, 0x36, 0xd9, 0x6d, 0xa2, 0xd5, 0x2a, 0x32, 0x33, 0x06, 0xdc, 0x7a, 0xec, 0xb9, 0xb6, 0x87, 0xc7, 0xdd, 0xe6, 0xbb, 0xf7, 0x1c, 0x8f, 0xe7, 0x45, 0x48, 0x94, 0xf2, 0x07, 0xcc, 0xd8, 0xe7, 0x77, 0x9e, 0x0f, 0x1f, 0x33, 0x5d, 0xd9, 0x44, 0x9c, 0xb5, 0xa6, 0x2b, 0x46, 0x63, 0xf8, 0x49, 0x98, 0xa5, 0x44, 0xd2, 0x84, 0xcd, 0x3a, 0x6b, 0xce, 0x36, 0xa9, 0xd2, 0xb6, 0x43, 0x8a, 0x4f, 0xa4, 0xa4, 0x65, 0xd2, 0xce, 0x3a, 0x1b, 0x1e, 0xdb, 0xd5, 0x2c, 0x66, 0x6b, 0x1e, 0xb1, 0x81, 0x7b, 0xe9, 0x97, 0x44, 0x5c, 0x72, 0xcb, 0xa9, 0x18, 0x98, 0x88, 0x0a, 0x36, 0x3b, 0x09, 0x86, 0xd5, 0x56, 0x66, 0x98, 0x76, 0xeb, 0x74, 0x0e, 0x5b, 0x52, 0x75, 0x48, 0x08, 0x22, 0x2d, 0xb7, 0x82, 0x9d, 0x7d, 0xa5, 0x92, 0x50, 0x19, 0x93, 0x2b, 0x2e, 0x2c, 0xb9, 0xb8, 0xfc, 0x3a, 0x1e, 0x0d, 0xde, 0xff, 0xf3, 0xf3, 0x34, 0xcc, 0x77, 0x5b, 0x53, 0x63, 0x77, 0xf0, 0x4b, 0x5a, 0xc7, 0xa8, 0x04, 0xe5, 0x92, 0x69, 0xf2, 0xff, 0x96, 0xe3, 0xea, 0xe4, 0x4f, 0xc8, 0xc9, 0x70, 0xf8, 0xb7, 0xb7, 0xf9, 0xca, 0x8a, 0xf1, 0xe5, 0xca, 0x4e, 0xc8, 0xcb, 0xd3, 0xf5, 0xca, 0x2f, 0xcd, 0x69, 0xf4, 0x63, 0xa9, 0x55, 0x26, 0xe3, 0x41, 0xa4, 0x84, 0xd2, 0x13, 0x72, 0x3c, 0x1e, 0x8f, 0xfd, 0x66, 0xcc, 0x4d, 0x2a, 0xe8, 0x6e, 0x42, 0x16, 0x82, 0x6d, 0xfd, 0x1a, 0x15, 0x7c, 0x29, 0x07, 0xdc, 0xb2, 0xc4, 0x4c, 0x48, 0x04, 0x56, 0x33, 0xed, 0x77, 0xbe, 0x67, 0xc6, 0xf2, 0xc5, 0x0e, 0xf8, 0x38, 0x6f, 0xec, 0xed, 0xaa, 0x35, 0xd3, 0x0b, 0xa1, 0x36, 0x13, 0xb2, 0xe2, 0x71, 0xcc, 0x64, 0x21, 0x5f, 0xe9, 0x18, 0x8c, 0xd7, 0x34, 0xe6, 0x19, 0x30, 0xfc, 0x47, 0x5a, 0xc8, 0xb1, 0x2a, 0x8b, 0x56, 0x03, 0x1a, 0x59, 0xae, 0xe4, 0x84, 0x48, 0x25, 0xd9, 0xdb, 0xd6, 0x5d, 0xeb, 0x18, 0x05, 0xdf, 0x37, 0xb0, 0x44, 0x15, 0x16, 0xd6, 0xd7, 0xee, 0x9b, 0xa8, 0x97, 0xf3, 0xee, 0xe8, 0xe5, 0xab, 0x3e, 0x19, 0x8d, 0x21, 0x0a, 0xa7, 0xa7, 0xbd, 0x86, 0x32, 0x88, 0x4e, 0xb7, 0xc4, 0x28, 0xc1, 0x63, 0x24, 0xa5, 0xdd, 0x93, 0xf1, 0xeb, 0x3e, 0xa9, 0xbe, 0x82, 0x57, 0x4d, 0x40, 0xa9, 0xfd, 0xab, 0xd2, 0xd3, 0x87, 0xb4, 0xaf, 0x85, 0x9a, 0x09, 0x16, 0x81, 0x96, 0x0d, 0xab, 0x26, 0x48, 0xbd, 0x66, 0x85, 0x71, 0x07, 0xb5, 0x06, 0x55, 0x5e, 0xbf, 0x01, 0xb5, 0x4f, 0xf0, 0x6b, 0x04, 0xba, 0x43, 0x1a, 0x0d, 0x41, 0x99, 0x82, 0xc5, 0x0a, 0xbd, 0x5c, 0x70, 0x88, 0x32, 0x6d, 0x10, 0x97, 0x2a, 0x5e, 0x8b, 0x83, 0x57, 0xd9, 0xfb, 0x6e, 0xe4, 0xdc, 0x04, 0x70, 0xaa, 0x19, 0x2d, 0x80, 0xa9, 0x32, 0x90, 0xaa, 0xa8, 0xf8, 0x82, 0x6f, 0x59, 0xec, 0x81, 0x3a, 0xf7, 0xec, 0xb0, 0xb4, 0x30, 0x75, 0x2f, 0x77, 0xad, 0xd6, 0xb1, 0xb1, 0x80, 0x4e, 0x5c, 0xe0, 0x5d, 0x06, 0x7a, 0x3e, 0x07, 0xd2, 0x2d, 0xa5, 0x71, 0xcc, 0xe5, 0x72, 0x52, 0x70, 0x49, 0xa8, 0x5e, 0x72, 0xb9, 0xf7, 0x3a, 0x98, 0x0b, 0x15, 0xfd, 0x18, 0x18, 0x4b, 0xb5, 0x3d, 0xbc, 0xc5, 0x64, 0xbc, 0xbf, 0xc1, 0xa5, 0x00, 0xd1, 0x87, 0x41, 0x7e, 0xcf, 0xa1, 0x08, 0x5a, 0xbb, 0xaf, 0x30, 0xe1, 0xc9, 0xd2, 0x2b, 0x5d, 0xe4, 0xbc, 0x13, 0x54, 0xb2, 0xd9, 0x7a, 0x87, 0xd5, 0x8a, 0x29, 0x01, 0xc6, 0xde, 0xc2, 0x7a, 0x3d, 0x35, 0x32, 0xe2, 0x65, 0x99, 0x84, 0x5e, 0x13, 0xf4, 0xda, 0x1b, 0x48, 0x2e, 0x74, 0xdb, 0x34, 0xcc, 0x6b, 0x17, 0x1e, 0x7c, 0x93, 0x99, 0xab, 0x78, 0x07, 0x3f, 0x5c, 0xa6, 0x99, 0x25, 0x76, 0x97, 0x42, 0xb3, 0xb1, 0x6c, 0x0b, 0x16, 0x31, 0xda, 0x21, 0x3c, 0x9e, 0x75, 0xf2, 0x27, 0x50, 0x11, 0x1b, 0x45, 0xec, 0xba, 0x44, 0xcc, 0xd7, 0x6e, 0x4b, 0x65, 0x10, 0xe3, 0xf7, 0x85, 0x41, 0x9d, 0x33, 0x10, 0x5b, 0xee, 0x95, 0x76, 0xba, 0xe5, 0xda, 0x06, 0x26, 0x4d, 0xe7, 0x8c, 0x4c, 0x43, 0x58, 0x70, 0x88, 0xf2, 0xc1, 0x53, 0xb4, 0xf7, 0x3d, 0xd5, 0x26, 0x91, 0xa0, 0xc6, 0xcc, 0xda, 0x3c, 0xa1, 0x4b, 0x56, 0x5b, 0x77, 0x9c, 0xa7, 0xe0, 0xc7, 0x1a, 0xac, 0x4d, 0x8c, 0x8e, 0x66, 0x6d, 0xb7, 0xe7, 0x59, 0x17, 0x3f, 0x26, 0xd2, 0x3c, 0xb5, 0x67, 0x2d, 0x60, 0x60, 0x2c, 0xc1, 0x96, 0x4a, 0x66, 0x24, 0x56, 0x51, 0x96, 0x40, 0xd3, 0x08, 0x96, 0xcc, 0x5e, 0x08, 0x86, 0x8f, 0xef, 0x76, 0x1f, 0xe3, 0x6e, 0x27, 0x67, 0xd7, 0x81, 0x4c, 0xcf, 0xe9, 0xbf, 0x5d, 0xde, 0xfe, 0xfb, 0x5f, 0x9f, 0x00, 0xd1, 0xde, 0x98, 0x49, 0x18, 0xb6, 0xc9, 0x0b, 0x68, 0x06, 0x32, 0x56, 0x9b, 0x00, 0xa2, 0x46, 0x31, 0x77, 0x83, 0x95, 0x02, 0xba, 0x17, 0xa4, 0x3d, 0x79, 0x33, 0x6a, 0x17, 0xb0, 0x8d, 0x01, 0x88, 0x04, 0x51, 0xdf, 0xd8, 0xfc, 0x12, 0xc2, 0xcb, 0x6c, 0x37, 0xe7, 0x04, 0x8c, 0x8f, 0x8e, 0x5a, 0x1b, 0x13, 0x28, 0x99, 0x30, 0x63, 0xc0, 0x32, 0x20, 0x2c, 0x9f, 0xce, 0xa0, 0x30, 0x8e, 0xf8, 0x82, 0x74, 0xfd, 0x4a, 0x10, 0x53, 0x38, 0x0c, 0x38, 0x70, 0xa4, 0x32, 0x62, 0x6a, 0x41, 0xde, 0x09, 0x35, 0xef, 0x21, 0x11, 0x21, 0x6b, 0xaa, 0x49, 0xa6, 0xc5, 0x97, 0xf9, 0x77, 0xa8, 0x70, 0x60, 0x02, 0xcc, 0x83, 0x08, 0x94, 0xb7, 0x2c, 0x5f, 0x82, 0xf7, 0x06, 0x1b, 0x94, 0x0c, 0x28, 0xb0, 0x3f, 0x00, 0x5f, 0x01, 0xa0, 0x04, 0xc3, 0xc6, 0x5d, 0xeb, 0xee, 0x6d, 0xab, 0x85, 0x2c, 0x63, 0x4d, 0x97, 0x1f, 0xb1, 0xf5, 0xd5, 0x9c, 0xf4, 0x67, 0xc6, 0xf4, 0xee, 0xd2, 0xb5, 0x12, 0xa5, 0xbb, 0x1d, 0xd7, 0x01, 0xd0, 0x45, 0x48, 0x5f, 0x65, 0xf6, 0x23, 0x80, 0x2a, 0x2d, 0xf6, 0x51, 0xdf, 0x30, 0xd9, 0x01, 0x5a, 0x2e, 0x04, 0x6a, 0xb1, 0x30, 0xcc, 0xba, 0xf5, 0x3d, 0xda, 0x0f, 0xae, 0x06, 0x0e, 0x10, 0xe7, 0x1b, 0x39, 0x35, 0x54, 0xd0, 0x39, 0x98, 0xf0, 0x41, 0x69, 0xfe, 0x17, 0x92, 0x89, 0x3a, 0x7d, 0x2e, 0x2d, 0x24, 0xa3, 0x06, 0xed, 0x7f, 0x98, 0xb6, 0x1c, 0xce, 0xc4, 0x3a, 0xa5, 0x97, 0xe5, 0x48, 0x1f, 0x4c, 0x16, 0x57, 0x24, 0xbd, 0x60, 0x4d, 0x45, 0x86, 0x61, 0xcc, 0x0f, 0xe5, 0x09, 0xe9, 0x40, 0x36, 0xdc, 0x57, 0xe3, 0x05, 0xac, 0x17, 0x7d, 0xaa, 0x46, 0x51, 0x08, 0xcf, 0x35, 0xf2, 0x9d, 0x79, 0x46, 0x16, 0x54, 0x18, 0xe8, 0xd8, 0xce, 0xfe, 0x4c, 0x6b, 0x10, 0x79, 0xdd, 0x78, 0xbb, 0xc9, 0xdf, 0xfc, 0x51, 0xef, 0xf7, 0xfc, 0x9b, 0xdf, 0xdb, 0x7e, 0x71, 0xce, 0x01, 0x66, 0xd0, 0xa4, 0x70, 0x61, 0xb7, 0xbf, 0x00, 0xf5, 0x65, 0xaf, 0xa0, 0xec, 0xfb, 0xee, 0xe9, 0x12, 0xba, 0x56, 0x9f, 0x18, 0xf8, 0xbe, 0xe2, 0x09, 0x83, 0x2a, 0x77, 0xc9, 0xec, 0x1d, 0x0d, 0xdd, 0xf4, 0x62, 0x0d, 0x72, 0x3f, 0x71, 0x03, 0x47, 0x2e, 0x83, 0xa0, 0xba, 0x83, 0xc7, 0x35, 0xc1, 0x4e, 0xdf, 0x65, 0xcd, 0x25, 0x3e, 0xf7, 0x73, 0xcd, 0xf3, 0xfa, 0x79, 0x1c, 0x0b, 0x82, 0x3c, 0xf2, 0x02, 0x05, 0x3f, 0x19, 0x97, 0xc0, 0xd1, 0xe3, 0x81, 0x4f, 0x43, 0x25, 0x0a, 0xce, 0x42, 0x28, 0x59, 0xf9, 0xfb, 0x8a, 0x3a, 0x68, 0x96, 0xfe, 0xae, 0x9e, 0x0e, 0x76, 0x50, 0xcf, 0xd6, 0x22, 0x93, 0xee, 0xac, 0xae, 0x34, 0xe9, 0x32, 0xac, 0x68, 0x37, 0xb8, 0x41, 0xe5, 0xb3, 0x00, 0xdb, 0x31, 0x99, 0xcd, 0x20, 0x9f, 0x6a, 0x2e, 0x2e, 0x48, 0x6a, 0xf3, 0xdd, 0x35, 0x04, 0x12, 0xa8, 0x9d, 0x2b, 0xcd, 0x7f, 0x87, 0xff, 0x0b, 0x22, 0xc1, 0x31, 0x4f, 0xc8, 0xa0, 0x08, 0xbd, 0x3f, 0x11, 0x2a, 0xc8, 0xcd, 0x61, 0xc8, 0x0d, 0x40, 0x7c, 0x72, 0xe4, 0x90, 0x3b, 0xc2, 0x40, 0xdd, 0x87, 0x44, 0x3e, 0x55, 0xce, 0x43, 0xcc, 0xeb, 0xb6, 0xc2, 0x61, 0x85, 0x19, 0x09, 0xd6, 0x16, 0x8d, 0xa7, 0x6e, 0x69, 0x59, 0x0e, 0x56, 0x67, 0x7e, 0xae, 0x81, 0x4e, 0xb5, 0xe7, 0x44, 0x88, 0x4a, 0xcd, 0x85, 0x95, 0xa2, 0x55, 0xdd, 0xec, 0x69, 0x56, 0x95, 0x50, 0x43, 0x86, 0x2f, 0xb9, 0x7d, 0xf6, 0xcd, 0xf0, 0xe4, 0xd4, 0x75, 0x1d, 0x59, 0x90, 0x6a, 0x86, 0xe1, 0x3f, 0x67, 0x0b, 0x9a, 0x09, 0xdb, 0x2d, 0x46, 0xb6, 0x87, 0x02, 0xea, 0xf2, 0xa2, 0xce, 0xc1, 0x8f, 0x50, 0x4e, 0xd9, 0x87, 0x63, 0x5a, 0x95, 0xfa, 0x01, 0xdc, 0x23, 0x81, 0xad, 0x9a, 0x42, 0x81, 0xb9, 0x17, 0xdd, 0x7b, 0x0a, 0xfc, 0x96, 0xd4, 0x47, 0x45, 0x95, 0x4f, 0x55, 0x37, 0x6a, 0xc6, 0x05, 0x3f, 0x55, 0x63, 0x6a, 0x86, 0xa6, 0x70, 0xe1, 0x67, 0x6a, 0x57, 0x01, 0x9d, 0x9b, 0x6e, 0xb1, 0xdd, 0x23, 0xd3, 0x7b, 0xcd, 0xfb, 0xf9, 0x73, 0xb2, 0x4f, 0x77, 0x5d, 0xa3, 0xab, 0x3a, 0xf1, 0xbe, 0xeb, 0x41, 0xf2, 0x95, 0xa6, 0xd2, 0x08, 0x38, 0x3a, 0x4b, 0x64, 0xbf, 0x54, 0x25, 0xaf, 0x60, 0x97, 0x99, 0x87, 0xec, 0x7a, 0xf2, 0xe1, 0x70, 0x9d, 0xb7, 0xfd, 0xd2, 0xcf, 0x78, 0x1e, 0xdc, 0x34, 0xd6, 0xbc, 0xd5, 0x2e, 0xc3, 0xc3, 0x90, 0x08, 0x9e, 0x70, 0xeb, 0xfa, 0x31, 0xcc, 0xb1, 0x30, 0x05, 0x13, 0x98, 0xe1, 0xcb, 0x51, 0x01, 0x32, 0x4e, 0xef, 0xc8, 0x78, 0x48, 0x12, 0x83, 0xb4, 0x2b, 0x6b, 0x53, 0x9c, 0x4f, 0x96, 0xdc, 0xae, 0xb2, 0x79, 0x10, 0xa9, 0x24, 0x94, 0x4c, 0x49, 0x0e, 0x7d, 0x28, 0x84, 0xab, 0xd0, 0x77, 0x73, 0xcb, 0x4c, 0x3a, 0x1e, 0xdd, 0xb2, 0x2d, 0x4d, 0x52, 0xc1, 0x4c, 0x08, 0x43, 0xe7, 0x3c, 0x4c, 0xa0, 0xeb, 0x33, 0x5d, 0x52, 0xde, 0x82, 0x80, 0x30, 0x62, 0x42, 0xa4, 0x2b, 0x78, 0xba, 0xc5, 0x06, 0xa7, 0x95, 0x80, 0xf1, 0xef, 0x56, 0x47, 0xb7, 0x11, 0xd5, 0xe1, 0x66, 0xb3, 0x09, 0x61, 0xf8, 0x61, 0xdb, 0x00, 0xaf, 0xac, 0x55, 0x99, 0xa0, 0x8e, 0x5d, 0xbb, 0xb5, 0x85, 0x67, 0xf1, 0x6c, 0x91, 0x0a, 0xe7, 0x2c, 0x1c, 0x81, 0xce, 0xd1, 0xaf, 0x3d, 0xf4, 0x0f, 0x9e, 0x2b, 0x45, 0x79, 0xf0, 0x45, 0xb7, 0x38, 0x74, 0x5c, 0x6d, 0xc0, 0xf5, 0x83, 0x2d, 0xa0, 0x9d, 0xc6, 0xe4, 0xe7, 0x4f, 0x07, 0x1e, 0x94, 0x87, 0x12, 0x39, 0x9b, 0x81, 0xa5, 0xf5, 0xb0, 0x59, 0xb0, 0xbd, 0x19, 0x44, 0x98, 0xa8, 0x4a, 0x2d, 0x9a, 0xb9, 0x5a, 0x49, 0x79, 0x44, 0x9b, 0xe2, 0xa3, 0x99, 0xcd, 0x74, 0x71, 0x63, 0x74, 0xa5, 0x02, 0x43, 0x5e, 0xb4, 0xaa, 0x7a, 0x40, 0xed, 0x22, 0x0e, 0xd7, 0x36, 0x06, 0x53, 0x20, 0x36, 0x88, 0xfd, 0xc4, 0xc8, 0xbf, 0x8b, 0xe3, 0x15, 0xfb, 0xd7, 0xd6, 0x37, 0x3f, 0x30, 0xfb, 0x59, 0xed, 0x88, 0xad, 0xb3, 0x75, 0x13, 0x09, 0xce, 0x8d, 0xa5, 0xc6, 0xcf, 0x1a, 0x7e, 0xf9, 0x03, 0xc3, 0x3d, 0x20, 0xdd, 0xa6, 0x73, 0x7a, 0x64, 0x02, 0xeb, 0xf5, 0x7a, 0xe9, 0x02, 0x93, 0x29, 0x19, 0xf6, 0x1a, 0xfa, 0x3a, 0xc6, 0x35, 0xb2, 0x9a, 0x0e, 0xb0, 0x81, 0x25, 0x90, 0xbf, 0x74, 0xbb, 0xbd, 0x7c, 0x0c, 0x6d, 0x56, 0x48, 0x9d, 0x58, 0x66, 0x02, 0xa6, 0x95, 0xfd, 0x7d, 0x17, 0x4d, 0xb4, 0xb6, 0xee, 0x8c, 0x3e, 0xc8, 0xf5, 0xef, 0xcd, 0xbe, 0xdd, 0xa8, 0xb9, 0xed, 0x57, 0x65, 0xfa, 0x64, 0xe7, 0xbe, 0x59, 0x59, 0x9e, 0x4c, 0x04, 0xee, 0xd6, 0x12, 0x58, 0x24, 0x5c, 0x28, 0x8d, 0x23, 0x69, 0xc7, 0xbd, 0x20, 0x6a, 0x0c, 0x25, 0x06, 0xa5, 0x83, 0x58, 0x2c, 0xa5, 0x74, 0xdb, 0x77, 0xa5, 0x84, 0x5c, 0x8a, 0xf7, 0x61, 0xaf, 0x93, 0xcb, 0x46, 0xcf, 0xa6, 0x54, 0x9e, 0xb3, 0xa5, 0x66, 0x0c, 0x1d, 0xe1, 0x50, 0x7f, 0x27, 0xa7, 0x43, 0x18, 0xf0, 0xee, 0xf5, 0x87, 0x0a, 0x63, 0xe1, 0x9f, 0x90, 0x0a, 0xe4, 0x58, 0x37, 0x41, 0xd5, 0xf0, 0x76, 0xe4, 0x7c, 0x50, 0x13, 0x02, 0x3a, 0xf4, 0x51, 0xa1, 0x1a, 0x0f, 0x77, 0x7d, 0x86, 0xbb, 0x98, 0xbf, 0x90, 0x4c, 0x43, 0x7f, 0x0b, 0x0b, 0xdd, 0x1f, 0x40, 0xbf, 0x00, 0x13, 0x65, 0x13, 0xd4, 0x07, 0x12, 0x00, 0x00

const char* ssid = "NSA";
const char* password = "orange";

#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

camera_fb_t * fb = NULL;
using namespace websockets;
WebsocketsServer WSserver;
AsyncWebServer webserver(80);

// Arduino like analogWrite
// value has to be between 0 and valueMax
void ledcAnalogWrite(uint8_t channel, uint32_t value, uint32_t valueMax = 180) {
  // calculate duty, 8191 from 2 ^ 13 - 1
  uint32_t duty = (8191 / valueMax) * min(value, valueMax);
  ledcWrite(channel, duty);

void setup() {
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
  // Ai-Thinker: pins 2 and 12
  ledcSetup(2, 50, 16); //channel, freq, resolution
  ledcAttachPin(2, 2); // pin, channel
  ledcSetup(4, 50, 16);
  ledcAttachPin(12, 4);
  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;
  //init with high specs to pre-allocate larger buffers
  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(&amp;config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);

  sensor_t * s = esp_camera_sensor_get();
  s->set_framesize(s, FRAMESIZE_QVGA);
  WiFi.begin(ssid, password);

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {

  Serial.print("Connected to ");
  Serial.print("IP address: ");

  webserver.on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
    AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", index_html_gz, sizeof(index_html_gz));
    response->addHeader("Content-Encoding", "gzip");


void handle_message(WebsocketsMessage msg) {
  int commaIndex =',');
  int panValue =, commaIndex).toInt();
  int tiltValue = + 1).toInt();

  panValue = map(panValue, -90, 90, 0, 180); // 0-180
  tiltValue = map(tiltValue, -90, 90, 180, 0); // 0-180 reversed

  ledcAnalogWrite(2, panValue); // channel, value
  ledcAnalogWrite(4, tiltValue);

void loop() {
  auto client = WSserver.accept();
  while (client.available()) {
    fb = esp_camera_fb_get();
    client.sendBinary((const char *)fb->buf, fb->len);
    fb = NULL;

Demonstration Video

I got variable results from the ESP32-CAM. Sometimes it was as fluid as the Lolin in the video below and other times it was quite jerky. I don’t know if this is an issue with (my) Wi-Fi or if the data transfer from the video overwhelms the web socket servo control data.


Lolin32 Pan and Tilt Platform:
ESP32-CAM Pan Tilt with Servos:
Touch Interface:

2 Replies to “Pan and Tilt Control for an ESP32-CAM”

  1. rob says:

    Do you think it would be possible to overlay the pan tilt control on top of the camera image, a bit like this

    1. WordBot says:

      I looked at the Github quickly and it looks like he’s using HTML Canvas to draw the interface on top of the video. This could be one approach. It might also work using CSS Z-Index to layer the control with a transparent background on top of the video.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

scroll to top