Ankur MajumdarMaitreya Agarwal
Published © MIT

Mentor Mitra AI: Edge-Powered Robotic Mentor for Kids

Privacy first robotic companion running edge AI locally. Combines vision, voice & emotion detection, no cloud dependency, 101% privacy.

IntermediateWork in progress5 days20
Mentor Mitra AI: Edge-Powered Robotic Mentor for Kids

Things used in this project

Hardware components

Raspberry Pi 5
Raspberry Pi 5
×1
Raspberry PI AI Camera
Raspberry PI AI Camera
×1
Espressif ESP32 Development Board - Developer Edition
Espressif ESP32 Development Board - Developer Edition
×2
GC9A01 - Round Displays 1.28
×2
LDR, 5 Mohm
LDR, 5 Mohm
×1
EMax 12g ES08MD high Sensitive servo
Seeed Studio EMax 12g ES08MD high Sensitive servo
×4
Gravity: Analog Sound Sensor For Arduino
DFRobot Gravity: Analog Sound Sensor For Arduino
×1
Speaker: 3W, 4 ohms
Speaker: 3W, 4 ohms
×1
Desktop Microphone, Unidirectional
Desktop Microphone, Unidirectional
×1
Grove - I2C Touch Sensor(MPR121)
Seeed Studio Grove - I2C Touch Sensor(MPR121)
×2
RFduino Battery Shield
RFduino Battery Shield
×1
Li-Ion Battery 1000mAh
Li-Ion Battery 1000mAh
×1
DC/DC Converter, Buck Regulator
DC/DC Converter, Buck Regulator
×1
Jumper wires (generic)
Jumper wires (generic)
×75

Software apps and online services

Arduino IDE
Arduino IDE
Ollama
Raspbian
Raspberry Pi Raspbian
Home Assistant
Home Assistant
Ubuntu
Ubuntu
Llama 3.2
Whisper
Home Assistant Wyoming Protocol
Piper
OpenCV
OpenCV
Face Recognition
Emotion Recognition

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Soldering iron (generic)
Soldering iron (generic)
PCBWay 3D Printing
PCBWay 3D Printing

Story

Read more

Custom parts and enclosures

3-D print Files

The 3-D printed files created for Mentor Mitra V2 , Originally Created by my dearest friend and my team member Maitreya Agarwal on Fusion 3D.

Sketchfab still processing.

Body Back

Sketchfab still processing.

Base

Sketchfab still processing.

Body Main

Sketchfab still processing.

Face Front

Sketchfab still processing.

Hand Left

Sketchfab still processing.

Hand Right

Sketchfab still processing.

Schematics

Problems of Today, Limits of Today’s Solutions

This schematic highlights key challenges in modern society and the limitations of existing solutions

Addressing the Challenges

This schematic illustrates how the identified problems are addressed through appropriate solutions.

Mentor Mitra v1 — Internal System Architecture

This diagram shows the complete process flow and data movement across internal system components.

System Architecture Continued

Code

ESP32-Based Head & Hand Control Architecture

C/C++
The head turret and hand movements are controlled by a single ESP32. Hand motion is driven by a sound sensor, while head turret tracking uses a master–slave setup: once the Raspberry Pi 5 detects a face, it sends tracking data via serial communication to the ESP32, which controls the Mentor Mitra head in a two-degree-of-freedom (2-DOF) system.
#include <ESP32Servo.h>

// ----- Sound trigger config -----
#define SOUND_SENSOR_PIN 18        // digital OUT from sound module (active LOW)
unsigned long lastEventMs = 0;
const unsigned long refractoryMs = 300; // ignore retriggers within this window

// ----- Servo objects -----
Servo panServo;    // Pan (GPIO12)
Servo tiltServo;   // Tilt (GPIO13)
Servo servoA;      // Extra servo A (GPIO32)
Servo servoB;      // Extra servo B (GPIO33)

// ----- GPIO assignments -----
const int panPin   = 12;  // Pan
const int tiltPin  = 13;  // Tilt
const int servoAPin = 32; // extra 1
const int servoBPin = 33; // extra 2

// ----- Center positions -----
const int panCenter  = 130;
const int tiltCenter = 80;

// ----- Position tracking -----
int currentPanAngle  = 90;
int currentTiltAngle = 90;

// ----- Function declarations -----
void runDemo();
void centerServos();
void processCommand(String command);
void showCommands();
void smoothMove(Servo &servo, int startAngle, int endAngle, int &currentPos, int delayMs = 15);

void setup() {
  Serial.begin(115200);
  Serial.println("ESP32 4-Servo + Sound Controller Ready");

  // Sound input
  pinMode(SOUND_SENSOR_PIN, INPUT_PULLUP);

  // Allocate timers and set servo frequency
  ESP32PWM::allocateTimer(0);
  ESP32PWM::allocateTimer(1);
  ESP32PWM::allocateTimer(2);
  ESP32PWM::allocateTimer(3);

  panServo.setPeriodHertz(50);
  tiltServo.setPeriodHertz(50);
  servoA.setPeriodHertz(50);
  servoB.setPeriodHertz(50);

  // Attach with explicit pulse widths
  panServo.attach(panPin,   500, 2400);
  tiltServo.attach(tiltPin, 500, 2400);
  servoA.attach(servoAPin,  500, 2400);
  servoB.attach(servoBPin,  500, 2400);

  // Initialize at tracked positions
  panServo.write(currentPanAngle);
  tiltServo.write(currentTiltAngle);
  servoA.write(0);
  servoB.write(0);

  Serial.println("Pins: Pan=GPIO12, Tilt=GPIO13, A=GPIO32, B=GPIO33; Sound=GPIO18 (LOW=event)");
  Serial.println("Enter 'help' for commands; sound will randomize A/B servos 090");
}

void loop() {
  // ----- Sound-triggered random moves for servoA/servoB -----
  int soundState = digitalRead(SOUND_SENSOR_PIN); // LOW = sound detected
  unsigned long now = millis();
  if (soundState == LOW && (now - lastEventMs) > refractoryMs) {
    int a = random(0, 91); // 0..90 inclusive
    int b = random(0, 91);

    servoA.write(a);
    servoB.write(b);

    Serial.print(" Sound! A=");
    Serial.print(a);
    Serial.print(", B=");
    Serial.print(b);
    Serial.println("");

    lastEventMs = now;
  }

  // ----- Serial command handling for pan/tilt -----
  if (Serial.available()) {
    String command = Serial.readStringUntil('\n');
    command.trim();
    command.toLowerCase();
    if (command.length() > 0) {
      processCommand(command);
    }
  }

  delay(10);
}

// Smooth movement with position tracking
void smoothMove(Servo &servo, int startAngle, int endAngle, int &currentPos, int delayMs) {
  if (startAngle == endAngle) return;
  int step = (startAngle < endAngle) ? 1 : -1;
  Serial.print("Moving: " + String(startAngle) + "  " + String(endAngle) + " ");
  for (int angle = startAngle; angle != endAngle + step; angle += step) {
    servo.write(angle);
    currentPos = angle;
    delay(delayMs);
    if (abs(angle - startAngle) % 10 == 0) Serial.print(".");
  }
  Serial.println(" ");
}

void runDemo() {
  Serial.println(" [DEMO] Starting smooth 180x180 demonstration...");
  Serial.println(" Moving to start position (0, 0)");
  smoothMove(panServo,  currentPanAngle,  0, currentPanAngle, 10);
  smoothMove(tiltServo, currentTiltAngle, 0, currentTiltAngle, 10);
  delay(1000);
  Serial.println("  Pan sweep: 0  180");
  smoothMove(panServo, currentPanAngle, 180, currentPanAngle, 12);
  delay(500);
  Serial.println("  Tilt sweep: 0  180");
  smoothMove(tiltServo, currentTiltAngle, 180, currentTiltAngle, 12);
  delay(500);
  Serial.println("  Pan return: 180  0");
  smoothMove(panServo, currentPanAngle, 0, currentPanAngle, 12);
  delay(500);
  Serial.println("  Tilt return: 180  0");
  smoothMove(tiltServo, currentTiltAngle, 0, currentTiltAngle, 12);
  delay(500);
  Serial.println(" [DEMO] Complete! Current position: Pan=" + String(currentPanAngle) + ", Tilt=" + String(currentTiltAngle) + "");
  Serial.println();
}

void centerServos() {
  Serial.println(" [CENTER] Moving to center positions...");
  Serial.println("   Pan: " + String(currentPanAngle) + "  " + String(panCenter) + "");
  Serial.println("   Tilt: " + String(currentTiltAngle) + "  " + String(tiltCenter) + "");
  smoothMove(panServo,  currentPanAngle,  panCenter,  currentPanAngle, 15);
  smoothMove(tiltServo, currentTiltAngle, tiltCenter, currentTiltAngle, 15);
  Serial.println(" [CENTER] Complete! Servos centered at Pan=" + String(panCenter) + ", Tilt=" + String(tiltCenter) + "");
  Serial.println();
}

void processCommand(String command) {
  Serial.println(" Command received: '" + command + "'");
  if (command == "center") {
    centerServos();
  } else if (command == "demo") {
    runDemo();
  } else if (command.startsWith("pan ")) {
    int angle = command.substring(4).toInt();
    if (angle >= 0 && angle <= 180) {
      Serial.println("  [PAN] " + String(currentPanAngle) + "  " + String(angle) + "");
      smoothMove(panServo, currentPanAngle, angle, currentPanAngle, 15);
      Serial.println(" [PAN] Complete! Pan servo at " + String(angle) + "");
    } else {
      Serial.println(" ERROR: Pan angle must be 0-180");
    }
  } else if (command.startsWith("tilt ")) {
    int angle = command.substring(5).toInt();
    if (angle >= 0 && angle <= 180) {
      Serial.println("  [TILT] " + String(currentTiltAngle) + "  " + String(angle) + "");
      smoothMove(tiltServo, currentTiltAngle, angle, currentTiltAngle, 15);
      Serial.println(" [TILT] Complete! Tilt servo at " + String(angle) + "");
    } else {
      Serial.println(" ERROR: Tilt angle must be 0-180");
    }
  } else if (command.startsWith("both ")) {
    int spaceIndex = command.indexOf(' ', 5);
    if (spaceIndex > 0) {
      int panAngle  = command.substring(5, spaceIndex).toInt();
      int tiltAngle = command.substring(spaceIndex + 1).toInt();
      if (panAngle >= 0 && panAngle <= 180 && tiltAngle >= 0 && tiltAngle <= 180) {
        Serial.println(" [BOTH] Moving simultaneously:");
        Serial.println("   Pan: " + String(currentPanAngle) + "  " + String(panAngle) + "");
        Serial.println("   Tilt: " + String(currentTiltAngle) + "  " + String(tiltAngle) + "");
        int panSteps  = abs(panAngle  - currentPanAngle);
        int tiltSteps = abs(tiltAngle - currentTiltAngle);
        int maxSteps  = max(panSteps, tiltSteps);
        if (maxSteps > 0) {
          Serial.print("Moving both servos ");
          for (int step = 0; step <= maxSteps; step++) {
            int newPanAngle  = map(step, 0, maxSteps, currentPanAngle,  panAngle);
            int newTiltAngle = map(step, 0, maxSteps, currentTiltAngle, tiltAngle);
            panServo.write(newPanAngle);
            tiltServo.write(newTiltAngle);
            delay(15);
            if (step % 10 == 0) Serial.print(".");
          }
          Serial.println(" ");
          currentPanAngle  = panAngle;
          currentTiltAngle = tiltAngle;
        }
        Serial.println(" [BOTH] Complete! Pan=" + String(panAngle) + ", Tilt=" + String(tiltAngle) + "");
      } else {
        Serial.println(" ERROR: Both angles must be 0-180");
      }
    } else {
      Serial.println(" ERROR: Usage: both <pan_angle> <tilt_angle>");
    }
  } else if (command.startsWith("speed ")) {
    int speed = command.substring(6).toInt();
    if (speed >= 5 && speed <= 50) {
      Serial.println(" [SPEED TEST] Testing with " + String(speed) + "ms delay");
      Serial.println("   Pan: 0  90  180  center");
      smoothMove(panServo, currentPanAngle, 0,   currentPanAngle, speed);
      smoothMove(panServo, currentPanAngle, 90,  currentPanAngle, speed);
      smoothMove(panServo, currentPanAngle, 180, currentPanAngle, speed);
      smoothMove(panServo, currentPanAngle, panCenter, currentPanAngle, speed);
      Serial.println(" [SPEED TEST] Complete!");
    } else {
      Serial.println(" ERROR: Speed must be 5-50ms delay");
    }
  } else if (command == "status") {
    Serial.println(" [STATUS] Current servo positions:");
    Serial.println("   Pan (GPIO12):  " + String(currentPanAngle)  + "");
    Serial.println("   Tilt (GPIO13): " + String(currentTiltAngle) + "");
    Serial.println("   Pan center:    " + String(panCenter)  + "");
    Serial.println("   Tilt center:   " + String(tiltCenter) + "");
  } else if (command == "help" || command == "commands") {
    showCommands();
  } else {
    Serial.println(" Unknown command: '" + command + "'");
    Serial.println(" Type 'help' for available commands");
  }
  Serial.println();
}

void showCommands() {
  Serial.println("");
  Serial.println("           AVAILABLE COMMANDS         ");
  Serial.println("");
  Serial.println(" center            - Move to center   ");
  Serial.println(" demo              - Run 180x180 demo ");
  Serial.println(" pan <angle>       - Set pan (0-180)  ");
  Serial.println(" tilt <angle>      - Set tilt (0-180) ");
  Serial.println(" both <pan> <tilt> - Set both angles  ");
  Serial.println(" speed <5-50>      - Test speed       ");
  Serial.println(" status            - Show positions   ");
  Serial.println(" help              - Show this help   ");
  Serial.println("");
  Serial.println();
  Serial.println("Examples:");
  Serial.println("  pan 90        Move pan to 90");
  Serial.println("  tilt 45       Move tilt to 45");
  Serial.println("  both 130 80   Move to center positions");
  Serial.println("  speed 10      Test with 10ms delay");
  Serial.println();
}

Mentor Mitra V2 — Dual GC9A01 Eye Display

This repository hosts the Mentor Mitra V2 eyes displayed on two round GC9A01 displays, featuring default uncanny eyes and LDR integration

Child Emotion Recognition Module

Emotion recognition repositry used for predicting a child’s emotional state.

Child Face Recognition Module

Face recognition module used in Mentor Mitra to identify and track a child using Raspberry Pi and OpenCV.

Credits

Ankur Majumdar
1 project • 2 followers
Passionate about AI at the edge, embedded systems, and building meaningful hardware solutions that connect intelligence with emotion.
Maitreya Agarwal
0 projects • 1 follower

Comments