├── Diagrams └── PanTiltCamera.png ├── README.md ├── LICENSE └── Pan_Tilt_Camera └── Pan_Tilt_Camera.ino /Diagrams/PanTiltCamera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/un0038998/PanTiltCamera/HEAD/Diagrams/PanTiltCamera.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PanTiltCamera 2 | This repository contains code and diagram for pan tilt control using servo for esp32 camera 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Ujwal Nandanwar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Pan_Tilt_Camera/Pan_Tilt_Camera.ino: -------------------------------------------------------------------------------- 1 | #include "esp_camera.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define DUMMY_SERVO1_PIN 12 //We need to create 2 dummy servos. 11 | #define DUMMY_SERVO2_PIN 13 //So that ESP32Servo library does not interfere with pwm channel and timer used by esp32 camera. 12 | 13 | #define PAN_PIN 14 14 | #define TILT_PIN 15 15 | 16 | Servo dummyServo1; 17 | Servo dummyServo2; 18 | Servo panServo; 19 | Servo tiltServo; 20 | 21 | //Camera related constants 22 | #define PWDN_GPIO_NUM 32 23 | #define RESET_GPIO_NUM -1 24 | #define XCLK_GPIO_NUM 0 25 | #define SIOD_GPIO_NUM 26 26 | #define SIOC_GPIO_NUM 27 27 | #define Y9_GPIO_NUM 35 28 | #define Y8_GPIO_NUM 34 29 | #define Y7_GPIO_NUM 39 30 | #define Y6_GPIO_NUM 36 31 | #define Y5_GPIO_NUM 21 32 | #define Y4_GPIO_NUM 19 33 | #define Y3_GPIO_NUM 18 34 | #define Y2_GPIO_NUM 5 35 | #define VSYNC_GPIO_NUM 25 36 | #define HREF_GPIO_NUM 23 37 | #define PCLK_GPIO_NUM 22 38 | 39 | const char* ssid = "NowISeeYou"; 40 | const char* password = "12345678"; 41 | 42 | AsyncWebServer server(80); 43 | AsyncWebSocket wsCamera("/Camera"); 44 | AsyncWebSocket wsServoInput("/ServoInput"); 45 | uint32_t cameraClientId = 0; 46 | 47 | #define LIGHT_PIN 4 48 | const int PWMLightChannel = 4; 49 | 50 | const char* htmlHomePage PROGMEM = R"HTMLHOMEPAGE( 51 | 52 | 53 | 54 | 55 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 123 | 124 | 125 | 126 | 127 | 132 | 133 | 134 | 135 | 136 | 141 | 142 |
Pan: 119 |
120 | 121 |
122 |
Tilt: 128 |
129 | 130 |
131 |
Light: 137 |
138 | 139 |
140 |
143 | 144 | 196 | 197 | 198 | )HTMLHOMEPAGE"; 199 | 200 | void handleRoot(AsyncWebServerRequest *request) 201 | { 202 | request->send_P(200, "text/html", htmlHomePage); 203 | } 204 | 205 | void handleNotFound(AsyncWebServerRequest *request) 206 | { 207 | request->send(404, "text/plain", "File Not Found"); 208 | } 209 | 210 | void onServoInputWebSocketEvent(AsyncWebSocket *server, 211 | AsyncWebSocketClient *client, 212 | AwsEventType type, 213 | void *arg, 214 | uint8_t *data, 215 | size_t len) 216 | { 217 | switch (type) 218 | { 219 | case WS_EVT_CONNECT: 220 | Serial.printf("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str()); 221 | break; 222 | case WS_EVT_DISCONNECT: 223 | Serial.printf("WebSocket client #%u disconnected\n", client->id()); 224 | panServo.write(90); 225 | tiltServo.write(90); 226 | ledcWrite(PWMLightChannel, 0); 227 | break; 228 | case WS_EVT_DATA: 229 | AwsFrameInfo *info; 230 | info = (AwsFrameInfo*)arg; 231 | if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) 232 | { 233 | std::string myData = ""; 234 | myData.assign((char *)data, len); 235 | Serial.printf("Key,Value = [%s]\n", myData.c_str()); 236 | std::istringstream ss(myData); 237 | std::string key, value; 238 | std::getline(ss, key, ','); 239 | std::getline(ss, value, ','); 240 | if ( value != "" ) 241 | { 242 | int valueInt = atoi(value.c_str()); 243 | if (key == "Pan") 244 | { 245 | panServo.write(valueInt); 246 | } 247 | else if (key == "Tilt") 248 | { 249 | tiltServo.write(valueInt); 250 | } 251 | else if (key == "Light") 252 | { 253 | ledcWrite(PWMLightChannel, valueInt); 254 | } 255 | } 256 | } 257 | break; 258 | case WS_EVT_PONG: 259 | case WS_EVT_ERROR: 260 | break; 261 | default: 262 | break; 263 | } 264 | } 265 | 266 | void onCameraWebSocketEvent(AsyncWebSocket *server, 267 | AsyncWebSocketClient *client, 268 | AwsEventType type, 269 | void *arg, 270 | uint8_t *data, 271 | size_t len) 272 | { 273 | switch (type) 274 | { 275 | case WS_EVT_CONNECT: 276 | Serial.printf("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str()); 277 | cameraClientId = client->id(); 278 | break; 279 | case WS_EVT_DISCONNECT: 280 | Serial.printf("WebSocket client #%u disconnected\n", client->id()); 281 | cameraClientId = 0; 282 | break; 283 | case WS_EVT_DATA: 284 | break; 285 | case WS_EVT_PONG: 286 | case WS_EVT_ERROR: 287 | break; 288 | default: 289 | break; 290 | } 291 | } 292 | 293 | void setupCamera() 294 | { 295 | camera_config_t config; 296 | config.ledc_channel = LEDC_CHANNEL_0; 297 | config.ledc_timer = LEDC_TIMER_0; 298 | config.pin_d0 = Y2_GPIO_NUM; 299 | config.pin_d1 = Y3_GPIO_NUM; 300 | config.pin_d2 = Y4_GPIO_NUM; 301 | config.pin_d3 = Y5_GPIO_NUM; 302 | config.pin_d4 = Y6_GPIO_NUM; 303 | config.pin_d5 = Y7_GPIO_NUM; 304 | config.pin_d6 = Y8_GPIO_NUM; 305 | config.pin_d7 = Y9_GPIO_NUM; 306 | config.pin_xclk = XCLK_GPIO_NUM; 307 | config.pin_pclk = PCLK_GPIO_NUM; 308 | config.pin_vsync = VSYNC_GPIO_NUM; 309 | config.pin_href = HREF_GPIO_NUM; 310 | config.pin_sscb_sda = SIOD_GPIO_NUM; 311 | config.pin_sscb_scl = SIOC_GPIO_NUM; 312 | config.pin_pwdn = PWDN_GPIO_NUM; 313 | config.pin_reset = RESET_GPIO_NUM; 314 | config.xclk_freq_hz = 20000000; 315 | config.pixel_format = PIXFORMAT_JPEG; 316 | 317 | config.frame_size = FRAMESIZE_VGA; 318 | config.jpeg_quality = 10; 319 | config.fb_count = 1; 320 | 321 | // camera init 322 | esp_err_t err = esp_camera_init(&config); 323 | if (err != ESP_OK) 324 | { 325 | Serial.printf("Camera init failed with error 0x%x", err); 326 | return; 327 | } 328 | 329 | if (psramFound()) 330 | { 331 | heap_caps_malloc_extmem_enable(20000); 332 | Serial.printf("PSRAM initialized. malloc to take memory from psram above this size"); 333 | } 334 | } 335 | 336 | void sendCameraPicture() 337 | { 338 | if (cameraClientId == 0) 339 | { 340 | return; 341 | } 342 | unsigned long startTime1 = millis(); 343 | //capture a frame 344 | camera_fb_t * fb = esp_camera_fb_get(); 345 | if (!fb) 346 | { 347 | Serial.println("Frame buffer could not be acquired"); 348 | return; 349 | } 350 | 351 | unsigned long startTime2 = millis(); 352 | wsCamera.binary(cameraClientId, fb->buf, fb->len); 353 | esp_camera_fb_return(fb); 354 | 355 | //Wait for message to be delivered 356 | while (true) 357 | { 358 | AsyncWebSocketClient * clientPointer = wsCamera.client(cameraClientId); 359 | if (!clientPointer || !(clientPointer->queueIsFull())) 360 | { 361 | break; 362 | } 363 | delay(1); 364 | } 365 | 366 | unsigned long startTime3 = millis(); 367 | Serial.printf("Time taken Total: %d|%d|%d\n",startTime3 - startTime1, startTime2 - startTime1, startTime3-startTime2 ); 368 | } 369 | 370 | void setUpPinModes() 371 | { 372 | dummyServo1.attach(DUMMY_SERVO1_PIN); 373 | dummyServo2.attach(DUMMY_SERVO2_PIN); 374 | panServo.attach(PAN_PIN); 375 | tiltServo.attach(TILT_PIN); 376 | 377 | //Set up flash light 378 | ledcSetup(PWMLightChannel, 1000, 8); 379 | pinMode(LIGHT_PIN, OUTPUT); 380 | ledcAttachPin(LIGHT_PIN, PWMLightChannel); 381 | } 382 | 383 | 384 | void setup(void) 385 | { 386 | setUpPinModes(); 387 | Serial.begin(115200); 388 | 389 | WiFi.softAP(ssid, password); 390 | IPAddress IP = WiFi.softAPIP(); 391 | Serial.print("AP IP address: "); 392 | Serial.println(IP); 393 | 394 | server.on("/", HTTP_GET, handleRoot); 395 | server.onNotFound(handleNotFound); 396 | 397 | wsCamera.onEvent(onCameraWebSocketEvent); 398 | server.addHandler(&wsCamera); 399 | 400 | wsServoInput.onEvent(onServoInputWebSocketEvent); 401 | server.addHandler(&wsServoInput); 402 | 403 | server.begin(); 404 | Serial.println("HTTP server started"); 405 | 406 | setupCamera(); 407 | } 408 | 409 | 410 | void loop() 411 | { 412 | wsCamera.cleanupClients(); 413 | wsServoInput.cleanupClients(); 414 | sendCameraPicture(); 415 | //Serial.printf("SPIRam Total heap %d, SPIRam Free Heap %d\n", ESP.getPsramSize(), ESP.getFreePsram()); 416 | } 417 | --------------------------------------------------------------------------------