I turned an $8 camera into a printer monitor that streams to my phone. No Octoprint, no Raspberry Pi, no cloud subscription. This page has everything — the camera, the code, the 3D printed stand, and two ways to set it up.

This is the written walkthrough for the video. Total build time: about 20 minutes once your print is done.

What You Need

Item Cost Link
ESP32-CAM-MB (I bought a 4-pack) ~$8 each ea. Amazon
Random Micro USB cable Included
3D Printed Camera Stand $3 one-time, or free for Patreon members Patreon
5v USB power supply Ask your old iPhone

That’s everything. The ESP32-CAM-MB is the camera with a built-in USB programmer — one unit, plug it straight into your computer, no extra wires.


Step 1: Get the Stand

I designed a simple stand for the ESP32-CAM-MB. Camera drops right in — no screws, no glue. Sits on any flat surface.

There are a few free ESP32-CAM stand models floating around online too — I made this one because none of the ones I tried worked for my setup. If you’d rather hunt around and find a free one that fits your situation, go for it. The rest of this guide — the code, the setup, the troubleshooting — is free either way.

Print Settings

  • Material: PLA or PETG (whatever you have)
  • Layer height: 0.2mm
  • Infill: 20%
  • Supports: None
  • Print time: ~40 minutes

Step 2: Plug It In

Plug the USB cable into the camera and the other end into your computer. That’s the entire wiring step.


Step 3: Install the Code — Pick Your Path

Two ways to get the code onto the camera. The first is how I actually do it. The second is the traditional way if you don’t use AI tools.

✅ The Easy Way: Claude Code

If you have Claude Code installed, this is the whole process:

  1. Open a terminal in any folder and run claude
  2. Paste this prompt, filling in your wifi name and password:
Write and flash an ESP32-CAM sketch to the board plugged into
my USB port. It should stream video over wifi so I can view
it on my phone in a browser at http://<ip>/stream.

My wifi name: YOUR_WIFI_NAME
My wifi password: YOUR_WIFI_PASSWORD

When it's ready to flash, tell me so I can press the reset
button on the camera. Print the IP address when it's done.
  1. Claude Code will install anything it needs (arduino-cli, ESP32 board support), write the sketch, compile it, and start uploading
  2. When it tells you to, press the small button on the back of the camera once
  3. It’ll finish flashing and print the IP address — type http://YOUR_IP/stream into your phone’s browser on the same wifi and the stream loads

End to end: about five minutes. You never touch Arduino IDE, board manager URLs, or any of the usual microcontroller ritual.

This is genuinely how I set up new ESP32 projects now. AI-assisted embedded dev is not a gimmick — the tooling is good enough that you can skip the learning curve entirely and still end up with clean, working firmware.

The Manual Way: Arduino App

If you don’t want to use Claude Code, here’s the traditional path. Takes longer but works the same.

  1. Download the Arduino app (free)
  2. Open it. Go to File → Preferences
  3. In “Additional Boards Manager URLs” paste this: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
  4. Click OK
  5. Open Tools → Board → Boards Manager, search for “esp32,” and click Install on the Espressif one
  6. Set Tools → Board → AI Thinker ESP32-CAM (this is the right board option for the ESP32-CAM-MB — ignore the name)
  7. Set Tools → PSRAM → Enabled
  8. Open a new sketch (File → New), delete everything in it, and paste the code below
  9. At the top of the sketch, put your wifi name and password between the quotes
  10. Under Tools → Port, pick the port that showed up when you plugged the camera in
  11. Click the Upload arrow in the top left
  12. When the console says “Connecting……” with dots, press the small button on the back of the camera once
  13. Wait for it to finish — about 30 seconds
  14. Open Tools → Serial Monitor and set the baud rate to 115200. The camera will print something like Stream at: http://192.168.1.47/stream. Type that full address into your phone’s browser on the same wifi — the stream loads.

The Sketch

Copy this into Arduino. The only two lines you need to edit are the SSID and password near the top.

#include "esp_camera.h"
#include <WiFi.h>
#include <WebServer.h>

const char* ssid     = "";
const char* password = "";

#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

WebServer server(80);

void handleStream() {
  WiFiClient client = server.client();
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: multipart/x-mixed-replace; boundary=frame");
  client.println();
  while (client.connected()) {
    camera_fb_t* fb = esp_camera_fb_get();
    if (!fb) continue;
    client.printf("--frame\r\nContent-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n", fb->len);
    client.write(fb->buf, fb->len);
    client.println();
    esp_camera_fb_return(fb);
  }
}

void setup() {
  Serial.begin(115200);
  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_sccb_sda = SIOD_GPIO_NUM;
  config.pin_sccb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn     = PWDN_GPIO_NUM;
  config.pin_reset    = RESET_GPIO_NUM;
  config.xclk_freq_hz = 24000000;
  config.pixel_format = PIXFORMAT_JPEG;
  config.frame_size   = FRAMESIZE_SVGA;
  config.grab_mode    = CAMERA_GRAB_LATEST;
  config.fb_location  = CAMERA_FB_IN_PSRAM;
  config.jpeg_quality = 10;
  config.fb_count     = 2;

  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) { Serial.printf("Camera init failed: 0x%x\n", err); return; }
  Serial.println("Camera initialized");

  // Sensor tuning
  sensor_t *s = esp_camera_sensor_get();
  s->set_exposure_ctrl(s, 1);
  s->set_aec2(s, 1);
  s->set_ae_level(s, 1);
  s->set_gain_ctrl(s, 1);
  s->set_gainceiling(s, (gainceiling_t)GAINCEILING_4X);
  s->set_whitebal(s, 1);
  s->set_awb_gain(s, 1);
  s->set_wb_mode(s, 0);
  s->set_saturation(s, -1);
  s->set_contrast(s, 1);
  s->set_bpc(s, 1);
  s->set_wpc(s, 1);
  s->set_raw_gma(s, 1);
  s->set_lenc(s, 1);
  s->set_dcw(s, 1);

  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
  Serial.println();
  Serial.print("Connected! Stream at: http://");
  Serial.print(WiFi.localIP());
  Serial.println("/stream");

  server.on("/stream", HTTP_GET, handleStream);
  server.begin();
}

void loop() { server.handleClient(); }

If Something Isn’t Working

Upload fails, says “Failed to connect”

You missed the button press. Start the upload again — when it’s waiting for the camera, press the button on the back one time. That puts it in flashing mode.

Upload finishes but the stream is blank or glitchy

If you went the manual route, go to Tools → PSRAM → Enabled and upload again. This fixes 90% of “stream doesn’t work” problems. Claude Code handles this automatically.

The IP address works but I only see a spinner

Make sure you added /stream to the end of the address. The full URL looks like http://192.168.1.47/stream — without the /stream part, there’s nothing to show.

No port shows up for the camera

Try a different USB cable. Some cables only carry power and not data — that’s the usual cause. The cable that came with the 4-pack works.


Get the Stand


FAQ

Can I see the stream when I’m away from home?

Only on your local network — but if you can reach your home network remotely (through a VPN, for example), the stream works fine from anywhere.

Do I have to use Claude Code?

No. The Arduino app path above produces the exact same result. Claude Code is just faster once you have it installed.

Do I have to buy your stand?

There are free ESP32-CAM stand models on MakerWorld and elsewhere — try those first if you want. I designed this one because none of them fit right for my setup. If you want a proven drop-in fit with the print profile already dialed in, grab mine; otherwise free ones might work for you.

Do I have to buy a 4-pack of cameras?

No. You can buy them one at a time. The 4-pack is just cheaper per camera if you want to put them on multiple printers.

Does this work with any printer?

It doesn’t care what printer you have. It’s just a camera on a stand pointing at it.


This post contains affiliate links. If you purchase through these links, I may earn a small commission at no extra cost to you. This helps support the channel and lets me keep building ridiculous things.