Duh-Vinci Surgical Robot
Below is the Multi-Million Dollar Ultra-High Technology Da Vinci Surgical Robot.
And this is the Cheap Low Technology Duh Vinci Surgical Robot.
Tablet Interface
Circuit
Parts List:
4 x Me Arm Open Source Robot
1 x Dissection Kit
1 x SparkFun Blynk Board
1 x 4 Line Voltage Level Shifter
1 x ESP32-Cam
1 x PCA9685 12-bit 16-PWM Servo Controller Board
4 x Cat6 RJ45 Surface Mount Box - 1 Port
2 x Cat6 RJ45 Surface Mount Box - 2 Port
1 x 5v/3.3V DC Power Supply
1 x 6v DC Power Supply (Servos)
Support Equipment:
1 x Android Tablet
ESP32-CAM Setup
From Geeksville Micro-RTSP GitHub Repository
Under Examples: ESP32-devcam.ino
#include "OV2640.h" #include <WiFi.h> #include <WebServer.h> #include <WiFiClient.h> #include "SimStreamer.h" #include "OV2640Streamer.h" #include "CRtspSession.h" #define ENABLE_OLED //if want use oled ,turn on thi macro // #define SOFTAP_MODE // If you want to run our own softap turn this on #define ENABLE_WEBSERVER #define ENABLE_RTSPSERVER #ifdef ENABLE_OLED #include "SSD1306.h" #define OLED_ADDRESS 0x3c #define I2C_SDA 14 #define I2C_SCL 13 SSD1306Wire display(OLED_ADDRESS, I2C_SDA, I2C_SCL, GEOMETRY_128_32); bool hasDisplay; // we probe for the device at runtime #endif OV2640 cam; #ifdef ENABLE_WEBSERVER WebServer server(80); #endif #ifdef ENABLE_RTSPSERVER WiFiServer rtspServer(8554); #endif #ifdef SOFTAP_MODE IPAddress apIP = IPAddress(192, 168, 1, 1); #else #include "wifikeys.h" #endif #ifdef ENABLE_WEBSERVER void handle_jpg_stream(void) { WiFiClient client = server.client(); String response = "HTTP/1.1 200 OK\r\n"; response += "Content-Type: multipart/x-mixed-replace; boundary=frame\r\n\r\n"; server.sendContent(response); while (1) { cam.run(); if (!client.connected()) break; response = "--frame\r\n"; response += "Content-Type: image/jpeg\r\n\r\n"; server.sendContent(response); client.write((char *)cam.getfb(), cam.getSize()); server.sendContent("\r\n"); if (!client.connected()) break; } } void handle_jpg(void) { WiFiClient client = server.client(); cam.run(); if (!client.connected()) { return; } String response = "HTTP/1.1 200 OK\r\n"; response += "Content-disposition: inline; filename=capture.jpg\r\n"; response += "Content-type: image/jpeg\r\n\r\n"; server.sendContent(response); client.write((char *)cam.getfb(), cam.getSize()); } void handleNotFound() { String message = "Server is running!\n\n"; message += "URI: "; message += server.uri(); message += "\nMethod: "; message += (server.method() == HTTP_GET) ? "GET" : "POST"; message += "\nArguments: "; message += server.args(); message += "\n"; server.send(200, "text/plain", message); } #endif void lcdMessage(String msg) { #ifdef ENABLE_OLED if(hasDisplay) { display.clear(); display.drawString(128 / 2, 32 / 2, msg); display.display(); } #endif } void setup() { #ifdef ENABLE_OLED hasDisplay = display.init(); if(hasDisplay) { display.flipScreenVertically(); display.setFont(ArialMT_Plain_16); display.setTextAlignment(TEXT_ALIGN_CENTER); } #endif lcdMessage("booting"); Serial.begin(115200); while (!Serial) { ; } cam.init(esp32cam_aithinker_config); IPAddress ip; #ifdef SOFTAP_MODE const char *hostname = "devcam"; // WiFi.hostname(hostname); // FIXME - find out why undefined lcdMessage("starting softAP"); WiFi.mode(WIFI_AP); WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); bool result = WiFi.softAP(hostname, "12345678", 1, 0); if (!result) { Serial.println("AP Config failed."); return; } else { Serial.println("AP Config Success."); Serial.print("AP MAC: "); Serial.println(WiFi.softAPmacAddress()); ip = WiFi.softAPIP(); } #else lcdMessage(String("join ") + ssid); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print(F(".")); } ip = WiFi.localIP(); Serial.println(F("WiFi connected")); Serial.println(""); Serial.println(ip); #endif lcdMessage(ip.toString()); #ifdef ENABLE_WEBSERVER server.on("/", HTTP_GET, handle_jpg_stream); server.on("/jpg", HTTP_GET, handle_jpg); server.onNotFound(handleNotFound); server.begin(); #endif #ifdef ENABLE_RTSPSERVER rtspServer.begin(); #endif } CStreamer *streamer; CRtspSession *session; WiFiClient client; // FIXME, support multiple clients void loop() { #ifdef ENABLE_WEBSERVER server.handleClient(); #endif #ifdef ENABLE_RTSPSERVER uint32_t msecPerFrame = 100; static uint32_t lastimage = millis(); // If we have an active client connection, just service that until gone // (FIXME - support multiple simultaneous clients) if(session) { session->handleRequests(0); // we don't use a timeout here, // instead we send only if we have new enough frames uint32_t now = millis(); if(now > lastimage + msecPerFrame || now < lastimage) { // handle clock rollover session->broadcastCurrentFrame(now); lastimage = now; // check if we are overrunning our max frame rate now = millis(); if(now > lastimage + msecPerFrame) printf("warning exceeding max frame rate of %d ms\n", now - lastimage); } if(session->m_stopped) { delete session; delete streamer; session = NULL; streamer = NULL; } } else { client = rtspServer.accept(); if(client) { //streamer = new SimStreamer(&client, true); // our streamer for UDP/TCP based RTP transport streamer = new OV2640Streamer(&client, cam); // our streamer for UDP/TCP based RTP transport session = new CRtspSession(&client, streamer); // our threads RTSP session and state } } #endif }
Modified OV2640.cpp
#include "OV2640.h" #define TAG "OV2640" // definitions appropriate for the ESP32-CAM devboard (and most clones) camera_config_t esp32cam_config{ .pin_pwdn = -1, // FIXME: on the TTGO T-Journal I think this is GPIO 0 .pin_reset = 15, .pin_xclk = 27, .pin_sscb_sda = 25, .pin_sscb_scl = 23, .pin_d7 = 19, .pin_d6 = 36, .pin_d5 = 18, .pin_d4 = 39, .pin_d3 = 5, .pin_d2 = 34, .pin_d1 = 35, .pin_d0 = 17, .pin_vsync = 22, .pin_href = 26, .pin_pclk = 21, .xclk_freq_hz = 20000000, .ledc_timer = LEDC_TIMER_0, .ledc_channel = LEDC_CHANNEL_0, .pixel_format = PIXFORMAT_JPEG, // .frame_size = FRAMESIZE_UXGA, // needs 234K of framebuffer space // .frame_size = FRAMESIZE_SXGA, // needs 160K for framebuffer // .frame_size = FRAMESIZE_XGA, // needs 96K or even smaller FRAMESIZE_SVGA - can work if using only 1 fb .frame_size = FRAMESIZE_SVGA, .jpeg_quality = 12, //0-63 lower numbers are higher quality .fb_count = 2 // if more than one i2s runs in continous mode. Use only with jpeg }; camera_config_t esp32cam_aithinker_config{ .pin_pwdn = 32, .pin_reset = -1, .pin_xclk = 0, .pin_sscb_sda = 26, .pin_sscb_scl = 27, // Note: LED GPIO is apparently 4 not sure where that goes // per https://github.com/donny681/ESP32_CAMERA_QR/blob/e4ef44549876457cd841f33a0892c82a71f35358/main/led.c .pin_d7 = 35, .pin_d6 = 34, .pin_d5 = 39, .pin_d4 = 36, .pin_d3 = 21, .pin_d2 = 19, .pin_d1 = 18, .pin_d0 = 5, .pin_vsync = 25, .pin_href = 23, .pin_pclk = 22, .xclk_freq_hz = 20000000, .ledc_timer = LEDC_TIMER_1, .ledc_channel = LEDC_CHANNEL_1, .pixel_format = PIXFORMAT_JPEG, // .frame_size = FRAMESIZE_UXGA, // needs 234K of framebuffer space // .frame_size = FRAMESIZE_SXGA, // needs 160K for framebuffer // .frame_size = FRAMESIZE_XGA, // needs 96K or even smaller FRAMESIZE_SVGA - can work if using only 1 fb
.frame_size = FRAMESIZE_QVGA,
.jpeg_quality = 12, //0-63 lower numbers are higher quality .fb_count = 2 // if more than one i2s runs in continous mode. Use only with jpeg }; camera_config_t esp32cam_ttgo_t_config{ .pin_pwdn = 26, .pin_reset = -1, .pin_xclk = 32, .pin_sscb_sda = 13, .pin_sscb_scl = 12, .pin_d7 = 39, .pin_d6 = 36, .pin_d5 = 23, .pin_d4 = 18, .pin_d3 = 15, .pin_d2 = 4, .pin_d1 = 14, .pin_d0 = 5, .pin_vsync = 27, .pin_href = 25, .pin_pclk = 19, .xclk_freq_hz = 20000000, .ledc_timer = LEDC_TIMER_0, .ledc_channel = LEDC_CHANNEL_0, .pixel_format = PIXFORMAT_JPEG, .frame_size = FRAMESIZE_SVGA, .jpeg_quality = 12, //0-63 lower numbers are higher quality .fb_count = 2 // if more than one i2s runs in continous mode. Use only with jpeg }; void OV2640::run(void) { if (fb) //return the frame buffer back to the driver for reuse esp_camera_fb_return(fb); fb = esp_camera_fb_get(); } void OV2640::runIfNeeded(void) { if (!fb) run(); } int OV2640::getWidth(void) { runIfNeeded(); return fb->width; } int OV2640::getHeight(void) { runIfNeeded(); return fb->height; } size_t OV2640::getSize(void) { runIfNeeded(); if (!fb) return 0; // FIXME - this shouldn't be possible but apparently the new cam board returns null sometimes? return fb->len; } uint8_t *OV2640::getfb(void) { runIfNeeded(); if (!fb) return NULL; // FIXME - this shouldn't be possible but apparently the new cam board returns null sometimes? return fb->buf; } framesize_t OV2640::getFrameSize(void) { return _cam_config.frame_size; } void OV2640::setFrameSize(framesize_t size) { _cam_config.frame_size = size; } pixformat_t OV2640::getPixelFormat(void) { return _cam_config.pixel_format; } void OV2640::setPixelFormat(pixformat_t format) { switch (format) { case PIXFORMAT_RGB565: case PIXFORMAT_YUV422: case PIXFORMAT_GRAYSCALE: case PIXFORMAT_JPEG: _cam_config.pixel_format = format; break; default: _cam_config.pixel_format = PIXFORMAT_GRAYSCALE; break; } } esp_err_t OV2640::init(camera_config_t config) { memset(&_cam_config, 0, sizeof(_cam_config)); memcpy(&_cam_config, &config, sizeof(config)); esp_err_t err = esp_camera_init(&_cam_config); if (err != ESP_OK) { printf("Camera probe failed with error 0x%x", err); return err; } // ESP_ERROR_CHECK(gpio_install_isr_service(0)); return ESP_OK; }