# Face Tracking Robot with MEMENTO

## Overview

https://youtu.be/gz6YTwYlKcc

![](https://cdn-learn.adafruit.com/assets/assets/000/128/561/medium800thumb/adafruit_products_hero-follow-loop.jpg?1709600536)

In [a previous guide, we created a computer vision system to detect and recognize faces using the MEMENTO Camera](https://learn.adafruit.com/facial-detection-and-recognition-with-memento).

This guide builds upon the computer vision system from the previous guide to build a playful robot that uses the Adafruit MEMENTO to capture an image, detect a face, and track it until the face is no longer in the frame.

### About the code used in this guide
This project uses an example written by Me-No-Dev for Espressif Systems, [CameraWebServer.ino](https://github.com/espressif/arduino-esp32/tree/master/libraries/ESP32/examples/Camera/CameraWebServer). This example was modified by the author of this guide to reduce the flash size overhead.&nbsp;

![adafruit_products_hero-ship-look.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/533/medium640/adafruit_products_hero-ship-look.jpg?1709585755)

To do so, this project removed the web functionality, isolated the face detection / recognition calls, brought the overhead into **main.cpp** and **ra\_filter.h.** It adds compatibility for the Adafruit MEMENTO development board (added camera compatibility and added "blitting" the camera's raw image to the MEMENTO's TFT instead of to a webpage), sets up a PlatformIO build environment to decrease compile time, and creates an interactive robotics demo around the code.

## Parts
### MEMENTO - Python Programmable DIY Camera - Bare Board

[MEMENTO - Python Programmable DIY Camera - Bare Board](https://www.adafruit.com/product/5420)
Make memories, or just a cool camera-based project,&nbsp;with **Adafruit's MEMENTO Camera Board**. It's a development board with everything you need to create programmable camera and vision projects: with a camera module, TFT preview screen, buttons, SD card slot and...

Out of Stock
[Buy Now](https://www.adafruit.com/product/5420)
[Related Guides to the Product](https://learn.adafruit.com/products/5420/guides)
![Video of a DIY camera on a lazy susan.](https://cdn-shop.adafruit.com/product-videos/640x480/5420-05.jpg)

### Adafruit MEMENTO Camera Enclosure & Hardware Kit

[Adafruit MEMENTO Camera Enclosure & Hardware Kit](https://www.adafruit.com/product/5843)
Once you've picked up your **MEMENTO Camera** and you're ready to take it out into the world, here is a chic and minimalist enclosure that will look great on the runways of Paris or the street photography of New York City! These front and back plates have been...

In Stock
[Buy Now](https://www.adafruit.com/product/5843)
[Related Guides to the Product](https://learn.adafruit.com/products/5843/guides)
![Overhead shot of two square-shaped PCB boards for a DIY camera above eight black plastic screws and four black plastic hex nuts with 3pin to 3pin JST PH cable and adhesive sticker.](https://cdn-shop.adafruit.com/640x480/5843-05.jpg)

### Micro Servo with 3-pin JST PH 2mm Cable - TowerPro SG92R

[Micro Servo with 3-pin JST PH 2mm Cable - TowerPro SG92R](https://www.adafruit.com/product/4326)
This tiny little servo can rotate approximately 180 degrees (90 in each direction), and works just like the standard kinds you're used to but&nbsp;_smaller_. You can use any servo code, hardware or library to control these servos. Good for beginners who want to make stuff move...

Out of Stock
[Buy Now](https://www.adafruit.com/product/4326)
[Related Guides to the Product](https://learn.adafruit.com/products/4326/guides)
![Angled shot of a micro servo with a JST cable.](https://cdn-shop.adafruit.com/640x480/4326-00.jpg)

### Part: USB A to USB C Cable
quantity: 1
Pink and Purple Woven USB A to USB C Cable - 1 meter long
[USB A to USB C Cable](https://www.adafruit.com/product/5153)

### Part: 256MB Micro SD Card
quantity: 1
256MB Micro SD Memory Card
[256MB Micro SD Card](https://www.adafruit.com/product/5251)

### Part: 3.7V 420mAh Lithium Ion Polymer Battery
quantity: 1
Lithium Ion Polymer Battery with Short Cable - 3.7V 420mAh
[3.7V 420mAh Lithium Ion Polymer Battery](https://www.adafruit.com/product/4236)

If you do not have the MEMENTO Camera Enclosure Kit (or if it is out of stock), you can build your own ring light for the MEMENTO using the following parts:

### Part: NeoPixel Ring with 12x RGBW LEDs
quantity: 1
NeoPixel Ring - 12 x 5050 RGBW LEDs w/ Integrated Drivers - Natural White - ~4500K
[NeoPixel Ring with 12x RGBW LEDs](https://www.adafruit.com/product/2852)

### Part: JST PH 3-pin Plug-Plug Cable
quantity: 1
JST PH 2mm 3-pin Plug-Plug Cable - 100mm long
[JST PH 3-pin Plug-Plug Cable](https://www.adafruit.com/product/4336)

### Part: Magnetic Pin Back
quantity: 1
Magnetic Pin Back
[Magnetic Pin Back](https://www.adafruit.com/product/1170)

### Part: Jumper Wires Socket and Plug-Plug
quantity: 1
Jumper Wires Socket and Plug-Plug
[Jumper Wires Socket and Plug-Plug](https://www.adafruit.com/product/4635)

### Part: Camera and Tripod 3/8" to 1/4" Adapter Screw
quantity: 1
Camera and Tripod 3/8" to 1/4" Adapter Screw
[Camera and Tripod 3/8" to 1/4" Adapter Screw](https://www.adafruit.com/product/2392)

### Part: M3x6mm Screws
quantity: 4
M3x6mm Screws

### Part: M2.5x6mm Screws
quantity: 4
M2.5x6mm Screws

### Part: M3x20mm Screws
quantity: 3
M3x20mm Screws

![](https://cdn-learn.adafruit.com/assets/assets/000/128/564/medium800/adafruit_products_hero-bots.jpg?1709602366)

# Face Tracking Robot with MEMENTO

## Wiring

![](https://cdn-learn.adafruit.com/assets/assets/000/127/949/medium800/adafruit_products_memento-robot_bb.png?1708708955)

### Wiring the Servo

For wiring the [Micro Servo with 3-pin JST PH](https://www.adafruit.com/product/4326), connect the servo's 3-PIN JST connector to the receptacle labeled A1 on the MEMENTO.

![adafruit_products_servo-jst.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/546/medium640/adafruit_products_servo-jst.jpg?1709588286)

### Wiring the NeoPixel Ring using the MEMENTO Camera Enclosure Kit

If you are using the&nbsp;[Adafruit MEMENTO Camera Enclosure Kit](https://www.adafruit.com/product/5843)&nbsp;with this guide, use the included JST cable to connect the&nbsp;_Adafruit MEMENTO Camera LED Ring Front Plate_ 3-pin JST Connector and the 3-pin JST connector labeled **A0** on the MEMENTO.

### Wiring the NeoPixel Ring without the MEMENTO Camera Enclosure Kit

If you do not have the MEMENTO Camera Enclosure Kit (or if it is out of stock), you can build your ring light for the MEMENTO using the following parts:

### NeoPixel Ring - 12 x 5050 RGBW LEDs w/ Integrated Drivers

[NeoPixel Ring - 12 x 5050 RGBW LEDs w/ Integrated Drivers](https://www.adafruit.com/product/2852)
What is better than smart RGB LEDs? Smart RGB+White LEDs! These NeoPixel rings now have 4 LEDs in them (red, green, blue _and_ white) for excellent lighting effects. Round and round and round they go! &nbsp;

**This is the 12 LED RGBW NeoPixel Ring in Natural White**....

In Stock
[Buy Now](https://www.adafruit.com/product/2852)
[Related Guides to the Product](https://learn.adafruit.com/products/2852/guides)
![NeoPixel Ring with 12 x 5050 RGBW LEDs lighting up rainbow and white](https://cdn-shop.adafruit.com/product-videos/640x480/2852-01.jpg)

### JST PH 2mm 3-pin Plug-Plug Cable - 100mm long

[JST PH 2mm 3-pin Plug-Plug Cable - 100mm long](https://www.adafruit.com/product/4336)
This cable is a little over 100mm / 4" long&nbsp;and fitted with JST-PH 3-pin connectors on either end.&nbsp;

We dig the solid and compact nature of these connectors and the latch that keeps the cable from coming apart easily. We're carrying these to <a...></a...>

In Stock
[Buy Now](https://www.adafruit.com/product/4336)
[Related Guides to the Product](https://learn.adafruit.com/products/4336/guides)
![Angled shot of JST PH 3-pin Plug-Plug Cable - 100mm long.](https://cdn-shop.adafruit.com/640x480/4336-01.jpg)

### Premium Silicone Covered Extension Jumper Wires - 200mm x 40

[Premium Silicone Covered Extension Jumper Wires - 200mm x 40](https://www.adafruit.com/product/4635)
These premium extension jumper wires are handy for making wire harnesses or jumpering between headers on PCBs. They're&nbsp;200mm (~7.8") long and come loose as a pack of 40&nbsp;(10&nbsp;pieces of red, blue, yellow, and Adafruit black). They have 0.1" socket 'female'...

In Stock
[Buy Now](https://www.adafruit.com/product/4635)
[Related Guides to the Product](https://learn.adafruit.com/products/4635/guides)
![Angled shot of bundle of Premium Silicone Covered Extension Jumper Wires - 200mm x 40](https://cdn-shop.adafruit.com/640x480/4635-02.jpg)

Use socket and plug Jumper wires to easily connect the Neopixel ring when mounting to the robot enclosure.

![adafruit_products_led-jumper-ends.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/534/medium640/adafruit_products_led-jumper-ends.jpg?1709587025)

![adafruit_products_jumper-ends.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/535/medium640/adafruit_products_jumper-ends.jpg?1709587204)

Cut the JST PH 3-pin cable in half using a wire cutter. Using a soldering iron, make the following connections between the cable and the NeoPixel ring:

Make the following connections between the MEMENTO and the NeoPixel ring:
* MEMENTO **A0 VCC** to NeoPixel ring **PWR**
* MEMENTO **A0 GND** to NeoPixel ring **GND**
* MEMENTO **A0 Data** to NeoPixel ring data **IN**

# Face Tracking Robot with MEMENTO

## 3D Printing

## 3D Printed Parts

STL files for 3D printing are oriented to print "as-is" on FDM style machines.

Parts are designed to 3D print without any support material using PLA filament.

Original design source files may be downloaded using the links below.

![adafruit_products_parts-3d.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/558/medium640/adafruit_products_parts-3d.jpg?1709599672)

[Download STLs](https://www.thingiverse.com/thing:6518243)
[Edit Design](https://a360.co/49Zak2J)
## Slice with Settings for PLA Material&nbsp;

The parts were sliced using CURA with the slice settings below.

- PLA filament 200c extruder
- 0.25 layer height
- 10% gyroid infill
- 60mm/s print speed
- 60c heated bed
- Brim line count 2

![adafruit_products_slice.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/559/medium640/adafruit_products_slice.jpg?1709600137)

## Supports

- Support Overhang Angle: 50
- Support Destiny: 6%
- Enable Support Interface
- Enable Support Roof
- Support Z Distance: .21

![adafruit_products_supports.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/560/medium640/adafruit_products_supports.jpg?1709600502)

![](https://cdn-learn.adafruit.com/assets/assets/000/128/597/medium800thumb/adafruit_products_hero-two.jpg?1709691682)

# Face Tracking Robot with MEMENTO

## Assembly

## Mount LED ring

Thread the jumper wires through the cut outs in the circular mount. The LED ring PCB press fits inside the body part.

![adafruit_products_led-thread.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/536/medium640/adafruit_products_led-thread.jpg?1709587467)

![adafruit_products_led-threaded.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/537/medium640/adafruit_products_led-threaded.jpg?1709587501)

## Frame assembly

Use four M3x6mm to connect the servo holder part to the frame part.

The frame mounts to the MOMENTO board with three M3x20mm long screws.

![adafruit_products_servo-parts.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/538/medium640/adafruit_products_servo-parts.jpg?1709587565)

![adafruit_products_servo-frame.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/539/medium640/adafruit_products_servo-frame.jpg?1709587616)

## Mount Servo

Use two screws to mount the servo to the holder.

## Attach frame assembly

The frame aligns and mounts to the three mounts on the MEMENTO PCB.&nbsp;

![adafruit_products_servo-screws.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/540/medium640/adafruit_products_servo-screws.jpg?1709587678)

![adafruit_products_frame-attached.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/541/medium640/adafruit_products_frame-attached.jpg?1709587756)

## Connect JST cables

Connect the servo and LED ring JST cables to the ports on the MEMENTO.

## Mount frame assembly

The frame assembly mounts to the standoffs on the body print.

![adafruit_products_frame-cables.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/542/medium640/adafruit_products_frame-cables.jpg?1709587862)

![adafruit_products_frame-cable-tucked.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/543/medium640/adafruit_products_frame-cable-tucked.jpg?1709587976)

## Connect jumper wires

The LED jumper socket and pin wires connect and then are tucked inside the body.

## Attach back body

Align the back body print with the tab holes on the sides.

![adafruit_products_frame-cable-connect.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/544/medium640/adafruit_products_frame-cable-connect.jpg?1709588044)

![adafruit_products_body-align.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/545/medium640/adafruit_products_body-align.jpg?1709588095)

## Base assemble

The base plate houses the servo horn and magnet clips.

Press fit the included servo horn into the cutout on the base plate.&nbsp;

Align the magnet bar to the cutouts on the rectangular base plate. The magnet bar part press fits into place so the magnets are flush with the cutouts.

![adafruit_products_base-parts.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/550/medium640/adafruit_products_base-parts.jpg?1709592230)

![adafruit_products_base-parts-align.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/551/medium640/adafruit_products_base-parts-align.jpg?1709592274)

## Combine base plates

Use four M2.5x6mm long screws to attach both base plates.

![adafruit_products_base-parts-assembled.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/552/medium640/adafruit_products_base-parts-assembled.jpg?1709592602)

![adafruit_products_base-align.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/553/medium640/adafruit_products_base-align.jpg?1709592648)

## Mount with magnets

The magnets are strong enough to hold the robot on metal surfaces. A ferromagnetic bar is used to help mount to a shoulder.

![adafruit_products_hero-shoulder-close.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/566/medium640/adafruit_products_hero-shoulder-close.jpg?1709603905)

![adafruit_products_shoulder-bar.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/567/medium640/adafruit_products_shoulder-bar.jpg?1709603927)

## Tripod attachment

The tripod attachment uses the included servo horn and 3/8 to 1/4-20 tripod thread.

![adafruit_products_tripod-assemble.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/554/medium640/adafruit_products_tripod-assemble.jpg?1709592779)

![adafruit_products_tripod-assembled.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/555/medium640/adafruit_products_tripod-assembled.jpg?1709592888)

![](https://cdn-learn.adafruit.com/assets/assets/000/128/556/medium800thumb/adafruit_products_hero-follow-loop.jpg?1709596916)

# Face Tracking Robot with MEMENTO

## Upload Code

## Upload Code using&nbsp;Web Serial ESPTool
![](https://cdn-learn.adafruit.com/assets/assets/000/128/576/medium800/adafruit_products_hero-espTool.jpg?1709651327)

The WebSerial ESPTool was designed to be a web-capable option for programming Espressif ESP family microcontroller boards that have a serial-based ROM bootloader. It allows you to erase the contents of the microcontroller and program up to 4 files at different offsets.

For boards that lack native USB, like the ESP32 or ESP32-C3 microcontroller, this is how firmware like CircuitPython&nbsp; **.bin** &nbsp;files can be loaded.&nbsp; **There is no drag-and-drop to a folder option for these boards.**

For boards with native USB, like ESP32-S2, -S3, etc. this is how the UF2 bootloader&nbsp; **.bin** &nbsp;file can be loaded. Once the UF2 bootloader is on, firmware like CircuitPython&nbsp; **.uf2** &nbsp;files can be drag-and-dropped to a&nbsp; **BOOT** &nbsp;folder.

This tool is a good alternative for folks who cannot run Python&nbsp; **esptool.py** &nbsp;on their computer or are having difficulty installing or using&nbsp; **esptool.py**.

### Enable Web Serial
You will have to use the Chrome or Chromium-based browser for this to work.&nbsp;[For example, Edge and Opera are Chromium](https://en.wikipedia.org/wiki/Chromium_(web_browser)). Safari and Firefox, etc are&nbsp;_not_&nbsp;supported -&nbsp;[they have not implemented Web Serial](https://developer.mozilla.org/en-US/docs/Web/API/Serial#browser_compatibility)!

![adafruit_products_image.png](https://cdn-learn.adafruit.com/assets/assets/000/127/458/medium640/adafruit_products_image.png?1706906866)

If you have an ancient version of Chrome, you'll need to enable the Serial API, which is easy.

Visit&nbsp;&nbsp; **chrome://flags** &nbsp;from within Chrome. Find and enable the&nbsp; **Experimental Web Platform features**

**Restart Chrome**

![adafruit_products_Enable_Features.jpg](https://cdn-learn.adafruit.com/assets/assets/000/127/459/medium640/adafruit_products_Enable_Features.jpg?1706906912)

### Enter Bootloader Mode

Before you can use the tool, you will need to put your board in bootloader mode. **Before you start, make sure your MEMENTO is plugged into a USB port to your computer using a data/sync cable.** &nbsp;Charge-only cables will not work!

**Turn on the On/Off switch** &nbsp;- check that you see the green power light on so you know the board is powered, a prerequisite!

To enter the bootloader:

1. **Press and hold the BOOT/DFU button down (green box).**&nbsp;Don't let go of it yet!
2. **Press and release the Reset button (red box).**&nbsp;You should still have the BOOT/DFU button pressed while you do this.
3. **Now you can release the BOOT/DFU button.**

![](https://cdn-learn.adafruit.com/assets/assets/000/127/460/medium800/adafruit_products_bootReset.jpg?1706907056)

No USB drive will appear when you've entered the ROM bootloader. This is normal!

## Download the application for your board

Click the button below to download the application binary file for your board.

[Download firmware for MEMENTO Face Tracking Robot](https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/MEMENTO/Memento_Shoulder_Robot/memento_shoulder_robot_firmware.bin)
## Connect and Upload

You should have plugged in&nbsp; **only the MEMENTO board that you intend to flash**. That way there's no confusion in picking the proper port when it's time!

In the Chrome browser visit [https://adafruit.github.io/Adafruit\_WebSerial\_ESPTool/](https://adafruit.github.io/Adafruit_WebSerial_ESPTool/). You should see something like the image shown.

![factory_reset___esp32_s2_s3_adafruit_products_ESPTool.png](https://cdn-learn.adafruit.com/assets/assets/000/127/461/medium640/factory_reset___esp32_s2_s3_adafruit_products_ESPTool.png?1706907329)

Press the&nbsp; **Connect** button in the top right of the web browser. You will get a pop-up asking you to select the COM or Serial port.

**Remember, you should remove all other USB devices so&nbsp;**** _only_&nbsp;the MEMENTO board is attached, that way there's no confusion over multiple ports!**

On some systems, such as MacOS, there may be additional system ports that appear in the list.

![adafruit_products_factory_reset___esp32_s2_s3_Select_Device.png](https://cdn-learn.adafruit.com/assets/assets/000/127/462/medium640/adafruit_products_factory_reset___esp32_s2_s3_Select_Device.png?1706907389)

The JavaScript code will now try to connect to the ROM bootloader. It may time out for a bit until it succeeds. On success, you will see that it is **Connected** and will print out a unique **MAC address** identifying the board along with other information that was detected.

![adafruit_products_factory_reset___esp32_s2_Screen_Shot_2022-04-04_at_3.16.00_PM_(1).png](https://cdn-learn.adafruit.com/assets/assets/000/127/463/medium640/adafruit_products_factory_reset___esp32_s2_Screen_Shot_2022-04-04_at_3.16.00_PM_%281%29.png?1706907433)

On the top of the tool, ensure the offset is set to `0x0` and click _"Choose a File..."_

From the file browser, select the .bin file you downloaded earlier.

![adafruit_products_choose_file.png](https://cdn-learn.adafruit.com/assets/assets/000/127/481/medium640/adafruit_products_choose_file.png?1707158390)

Click _Program_ and the binary will be uploaded to your MEMENTO board.

![adafruit_products_program.png](https://cdn-learn.adafruit.com/assets/assets/000/127/483/medium640/adafruit_products_program.png?1707158607)

After the binary is uploaded to the MEMENTO, the console will instruct you to reset your device.

**Press the RESET button on the MEMENTO**. You should see the MEMENTO's screen briefly glow green, then show a preview of what the camera is seeing.

![adafruit_products_fw_done_upload.png](https://cdn-learn.adafruit.com/assets/assets/000/127/485/medium640/adafruit_products_fw_done_upload.png?1707158646)

# Face Tracking Robot with MEMENTO

## Usage

After uploading the code and pressing RESET, the MEMENTO's screen displays what the camera module sees.

The display shows the overlay when a face is detected. Note robots do not work!

![adafruit_products_hero-face-track-screen.jpg](https://cdn-learn.adafruit.com/assets/assets/000/128/562/medium640/adafruit_products_hero-face-track-screen.jpg?1709600619)

Move in front of the camera and make sure your face is fully visible.

When the robot detects your face, the NeoPixel ring will light up.

![adafruit_products_hero-follow-wide-1.gif](https://cdn-learn.adafruit.com/assets/assets/000/128/548/medium640thumb/adafruit_products_hero-follow-wide-1.jpg?1709589399)

The robot will track your face, following it left and right.&nbsp;

![adafruit_products_hero-fololw-left-right.gif](https://cdn-learn.adafruit.com/assets/assets/000/128/547/medium640thumb/adafruit_products_hero-fololw-left-right.jpg?1709589003)

When a face is no longer detected, the robot will move its head back to a resting position and turn off its ring light.

![adafruit_products_hero-follow-loop.gif](https://cdn-learn.adafruit.com/assets/assets/000/128/557/medium640thumb/adafruit_products_hero-follow-loop.jpg?1709596975)

## What am I seeing? How does Face Detection Work?
For more details about how the MEMENTO's TFT display illustrates how the computer vision for this project works, [read through this section of a previous guide](https://learn.adafruit.com/facial-detection-and-recognition-with-memento/usage#face-detection-explanation-3164642).

![](https://cdn-learn.adafruit.com/assets/assets/000/128/598/medium800/adafruit_products_ui-track.jpg?1709691822)

## Troubleshooting

### Uneven Lighting

One area we noticed the camera had issues with is uneven lighting. The author of this guide has a window parallel to his desk which caused uneven lighting conditions. The camera would track halfway, but stop when the face became overly saturated by light from the window.

We attempted to correct this by gently illuminating the face with a NeoPixel ring. However, you may find this is not enough. Putting a light source behind the camera will even out the lighting in your room/office/workshop.

### Increasing Detection Accuracy

One of the ways to increase this project's face detection accuracy is to find a "sweet spot" in the bounding boxes width. When the robot detects a face, it prints the dimensions of the bounding box, in pixels, to the Serial port (you can read this using the Arduino Serial Monitor or a similar tool). Since the width of the frame is 240 pixels, you may find moving further (or closer) to the camera increases detection accuracy.

Once you have the robot accurately tracking your face, write down the dimensions of the bounding box. You can also put a piece of painter's tape on the floor to mark the ideal distance between you and the robot.

# Face Tracking Robot with MEMENTO

## Code Walkthrough

![](https://cdn-learn.adafruit.com/assets/assets/000/128/577/medium800/adafruit_products_hero-guie-wide.jpg?1709651795)

## Example/Demo Code History and Explanation

This project uses an example written by Me-No-Dev for Espressif Systems named&nbsp;[CameraWebServer.ino](https://github.com/espressif/arduino-esp32/tree/master/libraries/ESP32/examples/Camera/CameraWebServer). The example was modified by the author of this guide for Adafruit Industries to reduce the flash size overhead (we removed the web server functionality, isolated the face detection/recognition calls, brought this overhead into&nbsp; **main.cpp** &nbsp;and&nbsp; **ra\_filter.h** ), added compatibility for the Adafruit MEMENTO development board (added camera compatibility and added "blitting" the camera's raw image to the MEMENTO's TFT instead of to a webpage), and build an interactive robotics demo around it.

So, since this is a larger codebase than a typical learn project and we only modified the code, this page won't explain&nbsp;_everything_&nbsp;that CameraWebServer does. It will explain the important and modifiable code segments within&nbsp; **main.cpp&nbsp;** as they pertain to this project.

### Capturing and Detecting a Photo

The `loop` function's first call is to `performFaceDetection()`, a function that captures a frame from the camera and performs face detection on it.

Within `performFaceDetection()`, a photo (aka, a "frame") is captured from the camera and stored into a buffer (`fb = esp_camera_fb_get()`). Then, two-stages of inference are run on this frame to attempt detecting a face. 

Within the first stage, inference on a model (`s1`) to detect objects is performed. The s1.infer() function call performs inference on the image, stored in the framebuffer (fb). If any objects are detected, they're stored in a list, `candidates`.

```auto
std::list&lt;dl::detect::result_t&gt; &amp;candidates = s1.infer((uint16_t *)fb-&gt;buf, {(int)fb-&gt;height, (int)fb-&gt;width, 3});
```

The second line runs inference on a different model, `s2`. This inference attempts to differentiate unique faces from the generic objects detected in `s1`. Then, the faces are stored in a list, `results`, which is returned back to the `loop()` function.

```auto
std::list&lt;dl::detect::result_t&gt; performFaceDetection() {
  // Capture a frame from the camera into the frame buffer
  fb = esp_camera_fb_get();
  if (!fb) {
    Serial.printf("ERROR: Camera capture failed\n");
    return std::list&lt;dl::detect::result_t&gt;();
  }

  // Perform face detection
  std::list&lt;dl::detect::result_t&gt; candidates =
      s1.infer((uint16_t *)fb-&gt;buf, {(int)fb-&gt;height, (int)fb-&gt;width, 3});
  std::list&lt;dl::detect::result_t&gt; results = s2.infer(
      (uint16_t *)fb-&gt;buf, {(int)fb-&gt;height, (int)fb-&gt;width, 3}, candidates);
  return results;
}
```

### Did it Detect a Face?
If `performFaceDetection()` detected a face, the `detectionResults` list will have a non-zero size. In the `loop()`, upon successful detection of a face, the NeoPixel ring will fill with a blue color to indicate to the user that the robot is tracking a face.

```auto
if (detectionResults.size() &gt; 0) {
    // Fill NeoPixel ring with a blue color while tracking
    pixels.fill(pixels.Color(0, 0, 255), 0, pixels.numPixels());
    pixels.show();
    ....
```

If a face was not detected in the past 3 seconds (the value of `DELAY_SERVO_CENTER`, in seconds), the robot "resets" itself by returning the head servo to its center position, adjusting the bounding box location to point to the center of the frame, and turning of fthe blue NeoPixel ring. 

```auto
} else {
    // We aren't tracking a face anymore
    isTrackingFace = false;

    // No face has been detected for DELAY_SERVO_CENTER seconds, re-center the
    // servo
    if ((millis() - prvDetectTime) &gt; DELAY_SERVO_CENTER) {
      Serial.println("Lost track of face, moving servo to center position!");
      curServoPos = SERVO_CENTER;
      headServo.write(curServoPos);

      // Reset the previous detection time
      prvDetectTime = millis();

      // Re-center the bounding box at the middle of the TFT
      prv_face_box_x_midpoint = 120;

      // Clear the NeoPixels while we're not tracking a face
      pixels.clear();
      pixels.show();
    }
```

### "Debouncing" the Face Detection
The `performFaceDetection()` function executes very quickly and we want to detect and track a new face. To do this, the code segment below allows the code to avoid re-triggering on every face by implementing a time delay of `DELAY_DETECTION` seconds.

```auto
if ((!isTrackingFace) &amp;&amp; ((millis() - prvDetectTime) &gt; DELAY_DETECTION)) {
  Serial.println("Face Detected!\nTracking new face...");
  isTrackingFace = true;
}
```

### Drawing to the TFT
The code prints text to the TFT indiciating that a face is tracked by the robot. Then, the `draw_face_boxes()` function is called with the frame buffer, its meta-data, and the `detectionResults` list.

```auto
/// Write to TFT
tft.setCursor(0, 230);
tft.setTextColor(ST77XX_GREEN);
tft.print("TRACKING FACE");

// Draw face detection boxes and landmarks on the framebuffer
fb_data_t rfb;
rfb.width = fb-&gt;width;
rfb.height = fb-&gt;height;
rfb.data = fb-&gt;buf;
rfb.bytes_per_pixel = 2;
rfb.format = FB_RGB565;
draw_face_boxes(&amp;rfb, &amp;detectionResults);
...
```

Within `draw_face_boxes()`, a bounding box is drawn around the face to illustrate to the user what the code "sees".

```auto
// draw a bounding box around the face
tft.drawFastHLine(x, y, w, color);
tft.drawFastHLine(x, y + h - 1, w, color);
tft.drawFastVLine(x, y, h, color);
tft.drawFastVLine(x + w - 1, y, h, color);
Serial.printf("Bounding box width: %d px\n", w);
Serial.printf("Bounding box height: %d px\n", h);
```

The center/midpoint of the bounding box is then calculated. The code uses this new center point to compare against the _previous&nbsp;_center point, as a way to measure how much the face moved (and in what direction on the X-axis).

```auto
// Calculate the current bounding box's x-midpoint so we can compare it
// against the previous midpoint
cur_face_box_x_midpoint = x + (w / 2);

// Draw a circle at the midpoint of the bounding box
tft.fillCircle(cur_face_box_x_midpoint, y + (h / 2), 5, ST77XX_BLUE);
```

At the end of the `loop()`, the 240x240px frame buffer is drawn to the TFT display. Since the next iteration of loop() requires the use of the frame buffer to take a photo, we release it using `esp_camera_fb_return()`.

```auto
// Blit out the framebuffer to the TFT
uint8_t temp;
for (uint32_t i = 0; i &lt; fb-&gt;len; i += 2) {
  temp = fb-&gt;buf[i + 0];
  fb-&gt;buf[i + 0] = fb-&gt;buf[i + 1];
  fb-&gt;buf[i + 1] = temp;
}
pyCameraFb-&gt;setFB((uint16_t *)fb-&gt;buf);
tft.drawRGBBitmap(0, 0, (uint16_t *)pyCameraFb-&gt;getBuffer(), 240, 240);

// Release the framebuffer
esp_camera_fb_return(fb);
```

### Tracking the Face's Movement and Moving the Robot's Head

The `trackFace()` function handles moving the robot's head servo. Specifically, it handles calculations to answer the following questions:

1) Should we move the servo at all?

2) How much to move the servo by

3) In what direction should we move the servo?

The `trackFace()` function first checks if the bounding box has moved by comparing the current bounding box's midpoint position on the x-axis against the previous bounding box's x-axis midpoint value.

```auto
void trackFace() {
  // Check if the bounding box has moved and if this is the first frame with a
  // face detected, just save the coordinates
  if ((cur_face_box_x_midpoint != prv_face_box_x_midpoint) &amp;&amp;
      (prv_face_box_x_midpoint != 0)) {
      ...
```

If the face has moved, the code calculates the difference between both bounding box midpoints, in pixels.

```auto
Serial.printf("x_midpoint (curr. face):      %d px\n",
cur_face_box_x_midpoint);
Serial.printf("x_midpoint (prv. face):       %d px\n",
prv_face_box_x_midpoint);

// Calculate the difference between the new bounding box midpoint and the
// previous bounding box midpoint, in pixels
int mp_diff_pixels = abs(cur_face_box_x_midpoint - prv_face_box_x_midpoint);
Serial.printf("Difference between midpoints: %d px\n", mp_diff_pixels);
```

If the servo moved whenever a difference between the midpoints was detected, it'd be very "jittery". To smooth its motion, the code only moves the servo if it's past an adjustable `SERVO_HYSTERESIS` value of 2 pixels. 

Then, the proportional amount of degrees the servo should move is calculated by multiplying the difference between the midpoints, in pixels, with a `SERVO_MOVEMENT_FACTOR` value. The resulting `servoStepAmount` degrees is more accurate than moving the servo a fixed amount of steps and results in smoother motion.

We've also implemented the concept of "dead zones" to smooth the servo's motion even further. These `dead_zones` are calculated as +/-10px from the center of the bounding box. If the bounding box's x-midpoint value is past the deadzone on either the right or the left, the servo's postion is incremented (moving left) or decremented (moving left).

```auto
// Only move the servo if the magnitude of the difference between the
// midpoints is greater than SERVO_HYSTERESIS
// NOTE: This smooths the servo's motion to avoid the servo from
// "jittering".
if (mp_diff_pixels &gt; SERVO_HYSTERESIS) {
  // Calculate how many steps, in degrees, the servo should move
  int servoStepAmount = mp_diff_pixels * SERVO_MOVEMENT_FACTOR;

  // Move the servo to the left or right, depending where x_midpoint is
  // located relative to the dead zones
  if (cur_face_box_x_midpoint &lt; deadzoneStart)
  	curServoPos += servoStepAmount;
  else if (cur_face_box_x_midpoint &gt; deadzoneEnd)
    curServoPos -= servoStepAmount;
}
```

The absolute `curServoPos` value is calculated (so we don't step in a negative direction, past the physical limitation of the servo) and is written to the servo.

```auto
// Move the servo to the new position
if (curServoPos != prvServoPos) {
curServoPos = abs(curServoPos);
Serial.printf("Moving servo to new position: %d degrees\n", curServoPos);
headServo.write(curServoPos);
}
```

Finally, the values of `prv_face_box_x_midpoint` and `prvServoPos` are updated to reflect the current position of the servo and the frame.

```auto
}
  // Update the previous midpoint coordinates with the new coordinates
  prv_face_box_x_midpoint = cur_face_box_x_midpoint;
  // Save the current servo position for the next comparison
  prvServoPos = curServoPos;
}
```

## Going Further - Tuning and Tweaking

This guide's code was tested in the guide author's office. The ML model was not trained on the guide author and it was not created by us. So, the accuracy of your robot's face detection may vary due to a large amount of factors such as room lighting, distance from the camera, camera's field of view in your environment, etc. You may find that modifying and fine-tuning the `SERVO_HYSTERESIS` and `SERVO_MOVEMENT_FACTOR` values results in smoother overall motion. 

# Face Tracking Robot with MEMENTO

## Restoring MEMENTO Demo and UF2 Bootloader

You're probably used to seeing the&nbsp; **CAMERABOOT** &nbsp;drive when loading CircuitPython or Arduino. The&nbsp; **CAMERABOOT** &nbsp;drive is part of the UF2 bootloader and allows you to drag and drop files, such as CircuitPython.&nbsp; However, the application in this guide uses a large partition size, which overwrites the UF2 bootloader present on the MEMENTO.

This means that after installing the facial detection application, double-tapping the RESET button will not put your MEMENTO into bootloader mode.&nbsp;

However, you can get your MEMENTO back to "normal" by writing a new binary application to the board that repairs your board's UF2 bootloader and performs a factory reset:

[Instructions for MEMENTO Factory Reset and Bootloader Repair](https://learn.adafruit.com/adafruit-memento-camera-board/factory-reset#factory-reset-and-bootloader-repair-3107941)
# Face Tracking Robot with MEMENTO

## Modify Code using PlatformIO

![](https://cdn-learn.adafruit.com/assets/assets/000/128/568/medium800thumb/adafruit_products_hero-two.jpg?1709610925)

Warning: 

This project library brings in a&nbsp;_considerable_&nbsp;_number_&nbsp;of dependencies and takes a&nbsp;_looong&nbsp;_time to compile using the Arduino IDE. If you want to modify the code in this guide, you'll want to compile the project using PlatformIO rather than Arduino IDE.

## Install PlatformIO

Follow this page's instructions to install PlatformIO and Visual Studio Code (the IDE of choice for using PlatformIO).

[Download and Install PlatformIO](https://platformio.org/install/ide?install=vscode)
## Configure Your Workspace

The ZIP file below includes a pre-configured workspace for using PlatformIO.&nbsp; **Download and unzip this file**. Then, save it somewhere safe, like your desktop.

[Download Project Code and PlatformIO Environment](https://github.com/adafruit/Adafruit_Learning_System_Guides/tree/main/MEMENTO/Memento_Shoulder_Robot/platformio_memento_shoulder_camera)
Open Visual Studio Code (VSCode). To ensure you have installed the PlatformIO extension properly,&nbsp; **look for the alien symbol in your VSCode sidebar.**

![](https://cdn-learn.adafruit.com/assets/assets/000/127/466/medium800/adafruit_products_vscode.png?1707146655)

Underneath Start,&nbsp; **click&nbsp;_Open..._**

![](https://cdn-learn.adafruit.com/assets/assets/000/127/467/medium800/adafruit_products_open.png?1707146708)

 **Navigate to the folder created when you unzipped the zip file. Then, Click Open** &nbsp;to open the workspace.

![](https://cdn-learn.adafruit.com/assets/assets/000/127/468/medium800/adafruit_products_memento_vscode_fd_cam.png?1707146927)

A large amount of configuration files and directories will appear in your VSCode instance.

To compile this code, focus on the following files and directories:

- **platformio.ini** - This is the project configuration file used to build the demo code. More [documentation about this file is located here](https://docs.platformio.org/en/latest/projectconf/index.html).
- **lib** directory - This directory is intended for project-specific (private) libraries. PlatformIO will compile them to static libraries and link them into executable files.
  - For this project, the specific library within this directory is the [Adafruit\_PyCamera](https://github.com/adafruit/Adafruit_PyCamera/) library.

- **src** directory - The directory where the project's source code, **main.cpp** , is located as well as the included headers (such as **ra\_filter.h** which stores the facial recognition/detection overhead).

## Build and Upload with PlatformIO

Before the code is built, you'll need to make two changes to the **platformio.ini** file:

- Change&nbsp;`upload_port` to reflect the MEMENTO upload port.&nbsp;
  - Don't know the desired port? There are steps to find them for [Windows](https://learn.adafruit.com/adafruit-memento-camera-board/advanced-serial-console-on-windows#whats-the-com-2977217),&nbsp;[MacOS](https://learn.adafruit.com/adafruit-memento-camera-board/advanced-serial-console-on-mac-and-linux#whats-the-port-2977243), and&nbsp;[Linux](https://learn.adafruit.com/adafruit-memento-camera-board/advanced-serial-console-on-linux).

- The&nbsp;`monitor_port`&nbsp;is different from the&nbsp;`upload_port`, and will only appear on your computer when you've uploaded the test code. For now, leave this alone.
  - After uploading the test code, change&nbsp;`monitor_port`&nbsp;to reflect the MEMENTO's monitor/serial port.

![](https://cdn-learn.adafruit.com/assets/assets/000/127/469/medium800/adafruit_products_pio_ini.png?1707147298)

Navigate to&nbsp; **src/main.cpp** &nbsp;to open the example code.

![](https://cdn-learn.adafruit.com/assets/assets/000/127/470/medium800/adafruit_products_main.png?1707147359)

With this file open,&nbsp;click the Alien symbol on the VSCode sidebar&nbsp;to open the PlatformIO Project Explorer.

![](https://cdn-learn.adafruit.com/assets/assets/000/127/472/medium800/adafruit_products_pio_alien.png?1707147801)

Underneath PlatformIO's Project Tasks, click&nbsp; **Build**.&nbsp;

![](https://cdn-learn.adafruit.com/assets/assets/000/127/473/medium800/adafruit_products_build_task.png?1707147811)

Once the build task is completed, the terminal will show&nbsp;`SUCCESS`&nbsp;along with the time it took to compile the project.

![](https://cdn-learn.adafruit.com/assets/assets/000/127/474/medium800/adafruit_products_build_suc.png?1707147851)

Before uploading this project to the board,&nbsp;[put the board into ROM Bootloader Mode](https://learn.adafruit.com/adafruit-memento-camera-board/factory-reset?preview_token=4voYBMZdO8AtkFcVdydTUA#step-2-enter-rom-bootloader-mode-3106832).&nbsp;

From the PlatformIO Project Tasks menu, click&nbsp; **Upload**.

![](https://cdn-learn.adafruit.com/assets/assets/000/127/475/medium800/adafruit_products_upload_task.png?1707150597)

Once the upload completes, the terminal should look like the following screenshot and show&nbsp;`SUCCESS`.

![](https://cdn-learn.adafruit.com/assets/assets/000/127/476/medium800/Cursor_and_adafruit_products_upload_complete_png__2276%C3%971300_.png?1707150631)

Press the RST (Reset) button on the&nbsp;MEMENTO to run the uploaded code.

![](https://cdn-learn.adafruit.com/assets/assets/000/127/477/medium800/adafruit_products_rst.png?1707150653)

After the board resets, you'll see a preview of what the camera module is seeing on the MEMENTO display. Follow the "Usage" page in this guide for detailed usage instructions.

Congrats - you have successfully compiled and uploaded the example code. You may make any modifications or extensions to this code that you'd like!


## Featured Products

### MEMENTO - Python Programmable DIY Camera - Bare Board

[MEMENTO - Python Programmable DIY Camera - Bare Board](https://www.adafruit.com/product/5420)
Make memories, or just a cool camera-based project,&nbsp;with **Adafruit's MEMENTO Camera Board**. It's a development board with everything you need to create programmable camera and vision projects: with a camera module, TFT preview screen, buttons, SD card slot and...

Out of Stock
[Buy Now](https://www.adafruit.com/product/5420)
[Related Guides to the Product](https://learn.adafruit.com/products/5420/guides)
### Adafruit MEMENTO Camera Enclosure & Hardware Kit

[Adafruit MEMENTO Camera Enclosure & Hardware Kit](https://www.adafruit.com/product/5843)
Once you've picked up your **MEMENTO Camera** and you're ready to take it out into the world, here is a chic and minimalist enclosure that will look great on the runways of Paris or the street photography of New York City! These front and back plates have been...

In Stock
[Buy Now](https://www.adafruit.com/product/5843)
[Related Guides to the Product](https://learn.adafruit.com/products/5843/guides)
### ADABOX 021 - MEMENTO Python Programmable DIY Camera

[ADABOX 021 - MEMENTO Python Programmable DIY Camera](https://www.adafruit.com/product/5419)
Out of Stock
[Buy Now](https://www.adafruit.com/product/5419)
[Related Guides to the Product](https://learn.adafruit.com/products/5419/guides)
### Micro Servo with 3-pin JST PH 2mm Cable - TowerPro SG92R

[Micro Servo with 3-pin JST PH 2mm Cable - TowerPro SG92R](https://www.adafruit.com/product/4326)
This tiny little servo can rotate approximately 180 degrees (90 in each direction), and works just like the standard kinds you're used to but&nbsp;_smaller_. You can use any servo code, hardware or library to control these servos. Good for beginners who want to make stuff move...

Out of Stock
[Buy Now](https://www.adafruit.com/product/4326)
[Related Guides to the Product](https://learn.adafruit.com/products/4326/guides)
### Pink and Purple Woven USB A to USB C Cable - 1 meter long

[Pink and Purple Woven USB A to USB C Cable - 1 meter long](https://www.adafruit.com/product/5153)
This cable is not only super-fashionable, with a woven pink and purple Blinka-like pattern, it's also made for USB C for our modernized breakout boards, Feathers, and more.&nbsp;&nbsp;[If you want something just like it but for Micro B, we...](https://www.adafruit.com/product/4111)

Out of Stock
[Buy Now](https://www.adafruit.com/product/5153)
[Related Guides to the Product](https://learn.adafruit.com/products/5153/guides)
### 256MB Micro SD Memory Card

[256MB Micro SD Memory Card](https://www.adafruit.com/product/5251)
Add storage in a jiffy using this **256MB microSD card**. Preformatted to FAT32, so it works out of the packaging with our projects. Works great with any device in the Adafruit shop that uses microSD cards. Ideal for use with Feathers, data loggers, or small Linux SBCs (not good...

In Stock
[Buy Now](https://www.adafruit.com/product/5251)
[Related Guides to the Product](https://learn.adafruit.com/products/5251/guides)
### Lithium Ion Polymer Battery with Short Cable - 3.7V 420mAh

[Lithium Ion Polymer Battery with Short Cable - 3.7V 420mAh](https://www.adafruit.com/product/4236)
Lithium-ion polymer (also known as 'lipo' or 'lipoly') batteries are thin, light, and powerful. The output ranges from 4.2V when completely charged to 3.7V. This battery has a capacity of 420mAh for a total of about 1.55 Wh. If you need a larger (or smaller!) battery, <a...></a...>

Out of Stock
[Buy Now](https://www.adafruit.com/product/4236)
[Related Guides to the Product](https://learn.adafruit.com/products/4236/guides)
### Camera and Tripod 3/8" to 1/4" Adapter Screw

[Camera and Tripod 3/8" to 1/4" Adapter Screw](https://www.adafruit.com/product/2392)
_Whaddya got a screw loose or something?_

This **3/8" to 1/4"**  **Adapter Screw** is super handy if you're building projects that connect to a tripod or a camera. It's a simple fix for getting a tripod with a 1/4" machine screw...

In Stock
[Buy Now](https://www.adafruit.com/product/2392)
[Related Guides to the Product](https://learn.adafruit.com/products/2392/guides)

## Related Guides

- [Adafruit MEMENTO Camera Board](https://learn.adafruit.com/adafruit-memento-camera-board.md)
- [DIY IoT Doorbell Camera with MEMENTO](https://learn.adafruit.com/diy-iot-doorbell-camera-with-memento.md)
- [OpenAI Image Descriptors with MEMENTO](https://learn.adafruit.com/openai-image-descriptors-with-memento.md)
- [MEMENTO Wireless Remote with TouchOSC](https://learn.adafruit.com/memento-wireless-remote.md)
- [Remote Shutter Button for MEMENTO](https://learn.adafruit.com/memento-shutter.md)
- [MEMENTO Camera Quick Start Guide](https://learn.adafruit.com/memento-camera-quick-start-guide.md)
- [IoT Bird Feeder with Camera](https://learn.adafruit.com/iot-window-bird-feeder-with-camera.md)
- [Set up Web Workflow on the Adafruit MEMENTO](https://learn.adafruit.com/set-up-web-workflow-on-the-adafruit-memento.md)
- [BLE Cat Thermal Printer with MEMENTO](https://learn.adafruit.com/ble-cat-thermal-printer-with-memento.md)
- [Facial Detection and Recognition with MEMENTO](https://learn.adafruit.com/facial-detection-and-recognition-with-memento.md)
- [3D Printed Camera Case for MEMENTO](https://learn.adafruit.com/memento-3d-case.md)
- [AdaBox 021](https://learn.adafruit.com/adabox021.md)
- [Wave Shield](https://learn.adafruit.com/adafruit-wave-shield-audio-shield-for-arduino.md)
- [Adafruit ESP32 Feather V2](https://learn.adafruit.com/adafruit-esp32-feather-v2.md)
- [Raspberry Pi Thermal Camera](https://learn.adafruit.com/raspberry-pi-thermal-camera.md)
