when i upload the code(claude) to esp32 cam.This seems repeatedly in serial monitor:
ho 8 tail 4 room 4
load:0x40080404,len:3524
entry 0x400805b8
ets Jul 29 2019 12:21:46
rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0030,len:4916
load:0x40078000,len:16436
load:0x40080400,len:4
ho 8 tail 4 room 4
load:0x40080404,len:3524
entry 0x400805b8
ets Jul 29 2019 12:21:46
rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0030,len:4916
load:0x40078000,len:16436
load:0x40080400,len:4
ho 8 tail 4 room 4
load:0x40080404,len:3524
entry 0x400805b8
ets Jul 29 2019 12:21:46
rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0030,len:4916
load:0x40078000,len:16436
load:0x40080400,len:4
ho 8 tail 4 room 4
load:0x40080404,len:3524
entry 0x400805b8
ets Jul 29 2019 12:21:46
rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0030,len:4916
load:0x40078000,len:16436
load:0x40080400,len:4
ho 8 tail 4 room 4
load:0x40080404,len:3524
entry 0x400805b8
the code:
#include "esp_camera.h"
#include <Arduino.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <iostream>
#include <sstream>
#include <ESP32Servo.h>
// Define hardware pins
#define VACUUM_PIN 1 // Pin for vacuum motor relay
#define SERVO_PIN 16 // Pin for servo motor
#define ULTRASONIC_TRIG 2 // Pin for ultrasonic sensor trigger
#define ULTRASONIC_ECHO 4 // Pin for ultrasonic sensor echo
// Motor pins structure
struct MOTOR_PINS
{
int pinIN1;
int pinIN2;
};
// L298N motor driver connections
// Note: We're not using ENA/ENB pins due to ESP32-CAM pin limitations
std::vector<MOTOR_PINS> motorPins =
{
{12, 13}, // RIGHT_MOTOR Pins (IN1, IN2)
{14, 15}, // LEFT_MOTOR Pins (IN3, IN4)
};
// Direction definitions
#define UP 1
#define DOWN 2
#define LEFT 3
#define RIGHT 4
#define STOP 0
#define RIGHT_MOTOR 0
#define LEFT_MOTOR 1
#define FORWARD 1
#define BACKWARD -1
#define STOP_MOTOR 0
// Operation mode
bool automaticMode = false;
bool vacuumOn = false;
// Camera pins
#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
// WiFi credentials
const char* ssid = "VacuumBot";
const char* password = "vacuum1234";
AsyncWebServer server(80);
AsyncWebSocket wsCamera("/Camera");
AsyncWebSocket wsRobotInput("/RobotInput");
uint32_t cameraClientId = 0;
// Create servo and ultrasonic sensor objects
Servo servoMotor;
unsigned long lastDistanceCheck = 0;
int servoAngle = 90; // Center position
int minDistance = 30; // Minimum distance in cm before turning
// HTML for the web interface
const char* htmlHomePage PROGMEM = R"HTMLHOMEPAGE(
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<style>
body {
font-family: Arial, sans-serif;
background-color: #f0f0f0;
margin: 0;
padding: 20px;
text-align: center;
}
.container {
max-width: 800px;
margin: 0 auto;
background-color: white;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
padding: 20px;
}
h1 {
color: #2c3e50;
}
`
.camera-container {
width: 100%;
margin: 20px 0;
position: relative;
overflow: hidden;
border-radius: 10px;
}
#cameraImage {
width: 100%;
max-width: 640px;
height: auto;
border-radius: 10px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.controls {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 20px;
}
.mode-controls {
display: flex;
justify-content: center;
margin-bottom: 20px;
gap: 20px;
}
.mode-button {
padding: 12px 20px;
border: none;
border-radius: 30px;
background-color: #3498db;
color: white;
font-weight: bold;
cursor: pointer;
transition: background-color 0.3s;
}
`
.mode-button.active {
background-color: #2ecc71;
}
.mode-button:hover {
background-color: #2980b9;
}
.manual-controls {
display: grid;
grid-template-columns: repeat(3, 70px);
grid-template-rows: repeat(3, 70px);
gap: 5px;
}
.control-button {
background-color: #34495e;
color: white;
border: none;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
cursor: pointer;
transition: transform 0.1s, background-color 0.3s;
}
.control-button:active {
transform: scale(0.95);
background-color: #2c3e50;
}
.vacuum-control {
margin-top: 20px;
}
.vacuum-button {
padding: 12px 20px;
border: none;
border-radius: 30px;
background-color: #e74c3c;
color: white;
font-weight: bold;
cursor: pointer;
transition: background-color 0.3s;
width: 200px;
}
.vacuum-button.active {
background-color: #c0392b;
}
.status {
margin-top: 20px;
padding: 10px;
background-color: #f8f9fa;
border-radius: 5px;
font-size: 14px;
}
/* Arrow symbols */
.arrow-up::after { content: "↑"; }
.arrow-down::after { content: "↓"; }
.arrow-left::after { content: "←"; }
.arrow-right::after { content: "→"; }
.arrow-stop::after { content: "■"; }
</style>
</head>
<body>
<div class="container">
<h1>Smart Vacuum Cleaner</h1>
<div class="camera-container">
<img id="cameraImage" src="" alt="Camera Feed">
</div>
<div class="controls">
<div class="mode-controls">
<button id="manualModeBtn" class="mode-button active" onclick="setMode('manual')">Manual Mode</button>
<button id="autoModeBtn" class="mode-button" onclick="setMode('auto')">Auto Mode</button>
</div>
<div id="manualControls" class="manual-controls">
<div></div>
<button class="control-button arrow-up" ontouchstart='sendCommand("move","1")' ontouchend='sendCommand("move","0")' onmousedown='sendCommand("move","1")' onmouseup='sendCommand("move","0")'></button>
<div></div>
<button class="control-button arrow-left" ontouchstart='sendCommand("move","3")' ontouchend='sendCommand("move","0")' onmousedown='sendCommand("move","3")' onmouseup='sendCommand("move","0")'></button>
<button class="control-button arrow-stop" ontouchstart='sendCommand("move","0")' onmousedown='sendCommand("move","0")'></button>
<button class="control-button arrow-right" ontouchstart='sendCommand("move","4")' ontouchend='sendCommand("move","0")' onmousedown='sendCommand("move","4")' onmouseup='sendCommand("move","0")'></button>
<div></div>
<button class="control-button arrow-down" ontouchstart='sendCommand("move","2")' ontouchend='sendCommand("move","0")' onmousedown='sendCommand("move","2")' onmouseup='sendCommand("move","0")'></button>
<div></div>
</div>
<div class="vacuum-control">
<button id="vacuumBtn" class="vacuum-button" onclick="toggleVacuum()">Turn Vacuum ON</button>
</div>
<div class="status" id="statusDisplay">
Status: Manual Mode | Vacuum: OFF
</div>
</div>
</div>
<script>
// WebSocket connections
var cameraWebSocket;
var inputWebSocket;
var isManualMode = true;
var isVacuumOn = false;
function initWebSockets() {
// Camera WebSocket
cameraWebSocket = new WebSocket("ws://" + window.location.hostname + "/Camera");
cameraWebSocket.binaryType = 'blob';
cameraWebSocket.onopen = function(event) {
console.log("Camera WebSocket connected");
};
cameraWebSocket.onclose = function(event) {
console.log("Camera WebSocket disconnected, reconnecting...");
setTimeout(function() { initWebSockets(); }, 2000);
};
cameraWebSocket.onmessage = function(event) {
document.getElementById("cameraImage").src = URL.createObjectURL(event.data);
};
// Control WebSocket
inputWebSocket = new WebSocket("ws://" + window.location.hostname + "/RobotInput");
inputWebSocket.onopen = function(event) {
console.log("Control WebSocket connected");
// Send initial mode
sendCommand("mode", isManualMode ? "manual" : "auto");
sendCommand("vacuum", isVacuumOn ? "1" : "0");
};
inputWebSocket.onclose = function(event) {
console.log("Control WebSocket disconnected, reconnecting...");
setTimeout(function() { initWebSockets(); }, 2000);
};
}
function sendCommand(command, value) {
if (inputWebSocket && inputWebSocket.readyState === WebSocket.OPEN) {
inputWebSocket.send(command + "," + value);
}
}
function setMode(mode) {
isManualMode = (mode === 'manual');
document.getElementById('manualModeBtn').classList.toggle('active', isManualMode);
document.getElementById('autoModeBtn').classList.toggle('active', !isManualMode);
document.getElementById('manualControls').style.opacity = isManualMode ? "1" : "0.5";
document.getElementById('manualControls').style.pointerEvents = isManualMode ? "auto" : "none";
sendCommand("mode", mode);
updateStatus();
}
function toggleVacuum() {
isVacuumOn = !isVacuumOn;
document.getElementById('vacuumBtn').textContent = isVacuumOn ? "Turn Vacuum OFF" : "Turn Vacuum ON";
document.getElementById('vacuumBtn').classList.toggle('active', isVacuumOn);
sendCommand("vacuum", isVacuumOn ? "1" : "0");
updateStatus();
}
function updateStatus() {
const modeText = isManualMode ? "Manual Mode" : "Automatic Mode";
const vacuumText = isVacuumOn ? "ON" : "OFF";
document.getElementById('statusDisplay').textContent = `Status: ${modeText} | Vacuum: ${vacuumText}`;
}
window.onload = function() {
initWebSockets();
updateStatus();
};
</script>
</body>
</html>
)HTMLHOMEPAGE";
// Function to measure distance with ultrasonic sensor
int getDistance() {
// Clear the trigger pin
digitalWrite(ULTRASONIC_TRIG, LOW);
delayMicroseconds(2);
// Set the trigger pin HIGH for 10 microseconds
digitalWrite(ULTRASONIC_TRIG, HIGH);
delayMicroseconds(10);
digitalWrite(ULTRASONIC_TRIG, LOW);
// Read the echo pin, return the sound wave travel time in microseconds
long duration = pulseIn(ULTRASONIC_ECHO, HIGH);
// Calculate the distance
int distance = duration * 0.034 / 2; // Speed of sound wave divided by 2 (go and back)
return distance;
}
// Move the servo to look around
void scanSurroundings() {
// Scan from left to right
for (int angle = 0; angle <= 180; angle += 45) {
servoMotor.write(angle);
delay(300); // Give servo time to move
int distance = getDistance();
// If the distance is safe, return this angle
if (distance > minDistance) {
servoAngle = angle;
return;
}
}
// If no safe direction found, turn around
servoAngle = 180;
}
// Robot movement control
void rotateMotor(int motorNumber, int motorDirection) {
if (motorDirection == FORWARD) {
digitalWrite(motorPins[motorNumber].pinIN1, HIGH);
digitalWrite(motorPins[motorNumber].pinIN2, LOW);
}
else if (motorDirection == BACKWARD) {
digitalWrite(motorPins[motorNumber].pinIN1, LOW);
digitalWrite(motorPins[motorNumber].pinIN2, HIGH);
}
else {
digitalWrite(motorPins[motorNumber].pinIN1, LOW);
digitalWrite(motorPins[motorNumber].pinIN2, LOW);
}
}
void moveRobot(int direction) {
Serial.printf("Moving robot: %d\n", direction);
switch(direction) {
case UP:
rotateMotor(RIGHT_MOTOR, FORWARD);
rotateMotor(LEFT_MOTOR, FORWARD);
break;
case DOWN:
rotateMotor(RIGHT_MOTOR, BACKWARD);
rotateMotor(LEFT_MOTOR, BACKWARD);
break;
case LEFT:
rotateMotor(RIGHT_MOTOR, FORWARD);
rotateMotor(LEFT_MOTOR, BACKWARD);
break;
case RIGHT:
rotateMotor(RIGHT_MOTOR, BACKWARD);
rotateMotor(LEFT_MOTOR, FORWARD);
break;
case STOP:
default:
rotateMotor(RIGHT_MOTOR, STOP_MOTOR);
rotateMotor(LEFT_MOTOR, STOP_MOTOR);
break;
}
}
// Control vacuum motor
void setVacuum(bool state) {
vacuumOn = state;
digitalWrite(VACUUM_PIN, state ? HIGH : LOW);
Serial.printf("Vacuum: %s\n", state ? "ON" : "OFF");
}
// Automatic navigation mode
void runAutomaticMode() {
// Check distance every 500ms
if (millis() - lastDistanceCheck > 500) {
lastDistanceCheck = millis();
// Get distance from ultrasonic sensor
int distance = getDistance();
Serial.printf("Distance: %d cm\n", distance);
// If obstacle detected, scan surroundings and change direction
if (distance < minDistance) {
moveRobot(STOP);
delay(100);
scanSurroundings();
// Based on servo angle, decide where to go
if (servoAngle < 45) {
// Obstacle on the left, turn right
moveRobot(RIGHT);
delay(700);
}
else if (servoAngle > 135) {
// Obstacle on the right, turn left
moveRobot(LEFT);
delay(700);
}
else {
// Obstacle ahead, turn around
moveRobot(LEFT);
delay(1400); // Longer turn to do a 180
}
// Return servo to center
servoMotor.write(90);
// Move forward
moveRobot(UP);
}
else {
// No obstacle, keep moving forward
moveRobot(UP);
}
}
}
// Web server handlers
void handleRoot(AsyncWebServerRequest *request) {
request->send_P(200, "text/html", htmlHomePage);
}
void handleNotFound(AsyncWebServerRequest *request) {
request->send(404, "text/plain", "File Not Found");
}
// WebSocket event handlers
void onRobotInputWebSocketEvent(AsyncWebSocket *server,
AsyncWebSocketClient *client,
AwsEventType type,
void *arg,
uint8_t *data,
size_t len) {
switch (type) {
case WS_EVT_CONNECT:
Serial.printf("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());
break;
case WS_EVT_DISCONNECT:
Serial.printf("WebSocket client #%u disconnected\n", client->id());
moveRobot(STOP);
break;
case WS_EVT_DATA:
AwsFrameInfo *info;
info = (AwsFrameInfo*)arg;
if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
std::string myData = "";
myData.assign((char *)data, len);
std::istringstream ss(myData);
std::string key, value;
std::getline(ss, key, ',');
std::getline(ss, value, ',');
Serial.printf("Key [%s] Value[%s]\n", key.c_str(), value.c_str());
if (key == "move") {
int directionValue = atoi(value.c_str());
moveRobot(directionValue);
}
else if (key == "mode") {
automaticMode = (value == "auto");
Serial.printf("Mode changed to: %s\n", automaticMode ? "Automatic" : "Manual");
// If switching to manual mode, stop the robot
if (!automaticMode) {
moveRobot(STOP);
}
}
else if (key == "vacuum") {
bool vacuumState = (value == "1");
setVacuum(vacuumState);
}
}
break;
case WS_EVT_PONG:
case WS_EVT_ERROR:
break;
default:
break;
}
}
void onCameraWebSocketEvent(AsyncWebSocket *server,
AsyncWebSocketClient *client,
AwsEventType type,
void *arg,
uint8_t *data,
size_t len) {
switch (type) {
case WS_EVT_CONNECT:
Serial.printf("Camera WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());
cameraClientId = client->id();
break;
case WS_EVT_DISCONNECT:
Serial.printf("Camera WebSocket client #%u disconnected\n", client->id());
cameraClientId = 0;
break;
case WS_EVT_DATA:
case WS_EVT_PONG:
case WS_EVT_ERROR:
break;
default:
break;
}
}
// Camera setup
void setupCamera() {
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;
// Set lower resolution to improve performance
config.frame_size = FRAMESIZE_VGA;
config.jpeg_quality = 12;
config.fb_count = 1;
// Camera initialization
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
}
// Enable PSRAM if available
if (psramFound()) {
heap_caps_malloc_extmem_enable(20000);
Serial.println("PSRAM initialized");
}
}
// Send camera frame to WebSocket clients
void sendCameraPicture() {
if (cameraClientId == 0) {
return;
}
// Capture a frame
camera_fb_t * fb = esp_camera_fb_get();
if (!fb) {
Serial.println("Failed to get camera frame buffer");
return;
}
// Send the image via WebSocket
wsCamera.binary(cameraClientId, fb->buf, fb->len);
esp_camera_fb_return(fb);
// Wait for message to be delivered
while (true) {
AsyncWebSocketClient * clientPointer = wsCamera.client(cameraClientId);
if (!clientPointer || !(clientPointer->queueIsFull())) {
break;
}
delay(1);
}
}
`
`
// Set up the hardware pin modes
void setupPins() {
// Setup Motor pins
for (int i = 0; i < motorPins.size(); i++) {
pinMode(motorPins[i].pinIN1, OUTPUT);
pinMode(motorPins[i].pinIN2, OUTPUT);
}
// Setup vacuum control pin
pinMode(VACUUM_PIN, OUTPUT);
digitalWrite(VACUUM_PIN, LOW); // Start with vacuum off
// Setup ultrasonic sensor pins
pinMode(ULTRASONIC_TRIG, OUTPUT);
pinMode(ULTRASONIC_ECHO, INPUT);
// Initialize servo
servoMotor.attach(SERVO_PIN);
servoMotor.write(90); // Center position
// Initialize all motors to stopped position
moveRobot(STOP);
}
void setup() {
Serial.begin(115200);
// Setup hardware pins
setupPins();
// Set up WiFi access point
WiFi.softAP(ssid, password);
IPAddress IP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(IP);
// Configure web server routes
server.on("/", HTTP_GET, handleRoot);
server.onNotFound(handleNotFound);
// Add WebSocket handlers
wsCamera.onEvent(onCameraWebSocketEvent);
server.addHandler(&wsCamera);
wsRobotInput.onEvent(onRobotInputWebSocketEvent);
server.addHandler(&wsRobotInput);
// Start the server
server.begin();
Serial.println("HTTP server started");
// Initialize camera
setupCamera();
Serial.println("Smart Vacuum Cleaner Robot ready!");
}
void loop() {
// Clean up disconnected WebSocket clients
wsCamera.cleanupClients();
wsRobotInput.cleanupClients();
// Send camera picture
sendCameraPicture();
// Run automatic mode if enabled
if (automaticMode) {
runAutomaticMode();
}
// Small delay to prevent CPU overload
delay(10);
}