Electronics projects

Arduino and ESP32 projects to 3D print, with parts lists, wiring and code

Sixteen builds with a printed enclosure: ten click-together ESP32 gadgets you can flash from the browser, and six wired projects with a full parts list, a wiring diagram, working code and print settings.

Desk command slab: Three pages on a tiny desk brick: a clock, today's weather, and a message you type on your phone.
Desk command slab
Desk Pal plane radar: A radar scope that shows real planes flying near your home, with their callsigns.
Desk Pal plane radar
Desk Pal fortune 8-ball: A printed magic 8-ball: ask a question, shake or tap, and an answer swirls up.
Desk Pal fortune 8-ball
Split-flap departure slab: A desk-sized airport departures board where flights click in letter by letter.
Split-flap departure slab

Four of PrintPal's ESP32 builds. Each has printable files, a parts list and one-click flashing.

On this page
  1. The short answer
  2. Start here: ESP32 builds that click together
  3. Choosing a board
  4. Six wired projects, with code
  5. ESP32 rules that save hours of debugging
  6. Designing the printed enclosure
  7. Getting code onto the board
  8. FAQ
  9. Sources and method

Key takeaways

  • Start with a click-together build: an ESP32 board with a built-in screen, a printed case and firmware you flash from Chrome. No soldering, about 45 minutes, around $20.
  • For your first wired project, an Arduino Uno R4 is the forgiving choice: 5 V logic matches most hobby sensors. Move to an ESP32 for Wi-Fi and an ESP32-S3 for native USB or a screen.
  • ESP32 pins are 3.3 V. They tolerate 3.6 V at most, ADC2 pins stop working when Wi-Fi is on, and GPIO6 to 11 belong to the flash chip.
  • Budget power first: 16 NeoPixels at full white draw 960 mA, almost twice what a USB 2.0 port supplies. Cap the current in code and add a capacitor.
  • Match the filament to the place: PLA indoors, PETG near water, ASA in the sun. Measure every module with calipers before you design its case.

The short answer

Short answer

The best first Arduino or ESP32 project with a 3D-printed case is one that needs no wiring: a board with a built-in screen dropped into a printed stand, flashed from the browser. Then build a weather station (two modules, four wires) and a NeoPixel lamp (your first power budget). Every project below has a parts list, wiring, code and print settings.

The projects in this guide
ProjectBoardLevelSolderingWi-FiParts
10 click-together buildsWaveshare ESP32-C6 or S3 screen boardsBeginnerNoSome$18–32
Weather stationESP32BeginnerNoNo$20–35
NeoPixel lampESP32BeginnerYesNo$20–30
Parking sensorArduino Uno R4 MinimaBeginnerNoNo$30–35
Plant soil monitorESP32IntermediateNoYes$20–25
Useless boxESP32IntermediateOptionalNo$20–25
USB macro padESP32-S3IntermediateOr hot-swapNo$25–35

Parts costs are our estimates from board list prices plus typical module prices in November 2026. Build-guide costs are from each PrintPal guide.

Start here: ESP32 builds that click together

These PrintPal build guides use a single board with the screen already on it, so there is nothing to wire. Print the case, press the board in, and flash the program from Chrome or Edge.

Two boards cover all ten: the Waveshare ESP32-C6-LCD-1.47 (a 172 × 320 rectangular screen, Wi-Fi 6) and the Waveshare ESP32-S3-Touch-LCD-1.28 (a round 240 × 240 touch screen with a motion sensor). Each guide has the printable files, a parts list, step-by-step photos, the source code and a Flash to board button.

Show
No soldering Slab eyes Cartoon eyes in a tiny printed case that look around, blink, wink and doze off on their own. Beginner45 min$18ESP32-C6 slab Open the build guide No soldering Lucky slab slot machine A hand-sized slot machine with a real printed lever and three neon reels. Beginner45 min$18ESP32-C6 slab Open the build guide No soldering Split-flap departure slab A desk-sized airport departures board where flights click in letter by letter. Beginner45 min$20ESP32-C6 slab Open the build guide No soldering DVD screensaver slab The bouncing DVD logo plus a running dino and a space invader, in a printed stand. Beginner45 min$20ESP32-C6 slab Open the build guide No soldering Music Play Pal Shows the song playing on your PC with the album cover spinning like a record. One USB-C cable, no Wi-Fi. Beginner30 min$20ESP32-C6 slab Open the build guide No soldering Desk command slab Three pages on a tiny desk brick: a clock, today's weather, and a message you type on your phone. Intermediate1h 10m$20ESP32-C6 slab Open the build guide No soldering Desk Pal buddy A desk pet with big eyes and real moods that reacts to taps, pets and shakes, and reminds you to drink water. Beginner45 min$32ESP32-S3 round touch Open the build guide No soldering Desk Pal fortune 8-ball A printed magic 8-ball: ask a question, shake or tap, and an answer swirls up. Beginner45 min$30ESP32-S3 round touch Open the build guide No soldering Desk Pal touch doodle A round finger-painting pad: draw with a fingertip, hold still to change ink, shake to erase. Beginner40 min$30ESP32-S3 round touch Open the build guide No soldering Desk Pal plane radar A radar scope that shows real planes flying near your home, with their callsigns. Intermediate1h 30m$32ESP32-S3 round touch Open the build guide

Choosing a board

Rule of thumb

Pick an Arduino Uno R4 for 5 V sensors and first projects, a classic ESP32 dev board when you need Wi-Fi on a budget, and an ESP32-S3 for native USB (keyboards, macro pads) or a screen. Every ESP32 runs its pins at 3.3 V.

Boards for 3D-printed electronics projects (prices checked November 12, 2026)
BoardChipLogicWirelessUSBPrice
Arduino Uno R4 MinimaRenesas RA4M1, 48 MHz5 VNoneUSB-C$20 list
Arduino Uno R4 WiFiRA4M1 plus ESP32-S35 VWi-Fi, BluetoothUSB-C$27.50 list
Arduino Nano ESP32ESP32-S3 (u-blox NORA-W106)3.3 VWi-Fi, BluetoothUSB-C$18.30 list
ESP32-DevKitC-32EESP323.3 VWi-Fi, Bluetooth 4.2Micro-USB$15 (Adafruit)
ESP32-S3-DevKitC-1ESP32-S33.3 VWi-Fi, BLENative USB plus UART port$19.95 (Adafruit, N8R8)
ESP32-C6-DevKitC-1ESP32-C63.3 VWi-Fi 6, BLE 5, Zigbee, ThreadTwo USB-C$9.95 (Adafruit)
Seeed XIAO ESP32-S3ESP32-S3, 8 MB PSRAM3.3 VWi-Fi, BLE 5USB-C$7.49
Seeed XIAO ESP32-C6ESP32-C63.3 VWi-Fi 6, Zigbee, ThreadUSB-C$5.20
Raspberry Pi Pico 2 WRP2350, 150 MHz3.3 VWi-Fi, Bluetooth 5.2Micro-USB$7 at launch
Waveshare ESP32-S3-Touch-LCD-1.28ESP32-S3, round touch screen, IMU3.3 VWi-Fi, BLE 5USB-C$29.99 (Amazon, sold by Waveshare)
Waveshare ESP32-C6-LCD-1.47ESP32-C6, 172 × 320 screen3.3 VWi-Fi 6, Bluetooth 5USB-C$19.20 (Amazon, third-party listing)

Arduino's US store showed sale prices on November 12, 2026; we list the regular prices. Espressif boards are often out of stock at Adafruit, and other distributors carry them.

Six wired projects, with code

Each project lists every part, shows the wiring as a diagram and a table, and gives a complete Arduino sketch with the libraries and board settings it needs. Use the chips to filter.

Show
ESP32-DevKitC-32E or any ESP32 dev board

ESP32 weather station with a BME280 and an OLED

Temperature, humidity and air pressure on a 0.96-inch screen, updated every two seconds. Both modules share the same two I2C wires.

  • Beginner
  • 1 hour + printing
  • $20–35 in parts
What you learn
  • How I2C lets two modules share two wires, each with its own address
  • Reading a sensor library and printing formatted numbers
  • Why a sensor sitting next to the ESP32 reads warm

Parts list

QtyPartWhy
1ESP32 dev boardThe brain; 3.3 V logic and Wi-Fi if you log data later
1BME280 breakout, I2CTemperature ±1 °C, humidity ±3%, pressure ±1 hPa. Not a BMP280, which has no humidity
1SSD1306 OLED, 0.96", 128 × 64, I2CAbout 20 mA; address 0x3C (or 0x3D by jumper)
1Breadboard and 8 jumper wiresNo soldering if the modules come with headers fitted
1USB data cablePower and upload
30 gPETG or ASA filamentFor an outdoor case; PLA softens in the sun

Wiring

ESP32 DevKitC 3.3 V logic 3V3 GND GPIO21 GPIO22 BME280 Sensor · I2C 0x76 or 0x77 VIN GND SCL SDA OLED SSD1306 · I2C 0x3C VCC GND SCL SDA
Power Ground (GND) Signal Signal Wire colours are a convention, not a rule: what matters is which pin goes where.
FromToWireNote
ESP32 DevKitC 3V3BME280 VINPower3.3 V, not 5 V
ESP32 DevKitC GNDBME280 GNDGround
ESP32 DevKitC GPIO22BME280 SCLSignalI2C clock
ESP32 DevKitC GPIO21BME280 SDASignalI2C data
ESP32 DevKitC 3V3OLED VCCPower
ESP32 DevKitC GNDOLED GNDGround
ESP32 DevKitC GPIO22OLED SCLSignalSame clock line
ESP32 DevKitC GPIO21OLED SDASignalSame data line
  • Both modules hang off the same SDA and SCL lines; their addresses keep them apart.
  • Breakout boards usually include the pull-up resistors I2C needs, so you add none.

Code

weather-station.ino
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_BME280.h>

Adafruit_SSD1306 oled(128, 64, &Wire, -1);   // 128 x 64 pixels, no reset pin
Adafruit_BME280 bme;

void setup() {
  Serial.begin(115200);
  Wire.begin(21, 22);                          // SDA, SCL: the ESP32 defaults
  if (!oled.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println("OLED not found at 0x3C");
    while (true) delay(1000);
  }
  if (!bme.begin(0x76) && !bme.begin(0x77)) {  // breakouts use one or the other
    Serial.println("BME280 not found at 0x76 or 0x77");
    while (true) delay(1000);
  }
  oled.setTextColor(SSD1306_WHITE);
}

void loop() {
  float t = bme.readTemperature();             // degrees C
  float h = bme.readHumidity();                // % relative humidity
  float p = bme.readPressure() / 100.0F;       // Pa to hPa

  oled.clearDisplay();
  oled.setTextSize(2);
  oled.setCursor(0, 0);
  oled.printf("%.1f C\n", t);
  oled.setTextSize(1);
  oled.setCursor(0, 28);
  oled.printf("Humidity %.0f %%\n", h);
  oled.printf("Pressure %.0f hPa\n", p);
  oled.display();

  Serial.printf("%.2f C  %.1f %%  %.1f hPa\n", t, h, p);
  delay(2000);
}

Libraries: Adafruit BME280, Adafruit SSD1306 and Adafruit GFX from the Library Manager. Board: ESP32 Dev Module.

Build it

  1. Install the board package

    In Arduino IDE, open Boards Manager, install esp32 by Espressif Systems and choose ESP32 Dev Module.

  2. Wire it on a breadboard

    Follow the table. Both modules take power from 3V3.

  3. Upload and open the Serial Monitor

    At 115200 baud. "Not found" usually means SDA and SCL are swapped.

  4. Check it reacts

    Breathe on the sensor: humidity should jump within seconds.

  5. Move the sensor away from the board

    Mount the BME280 at the end of a short cable or in its own vented chamber; the ESP32 warms the air around it by a few degrees.

Print settings: PETG or ASA, 0.2 mm layers, 3 walls, 20% infill. Print louvred rings flat, with no supports.

The printed enclosure

A Stevenson-screen style case: stacked louvred rings that let air through but keep sun and rain off the sensor, with the ESP32 and OLED in a separate box below. OLED module sizes vary between makers, so measure yours with calipers before designing the window. The CAD agent can draft both parts from a description with your measurements.

  • If bme.begin() fails at both addresses you may have a BMP280: it reports chip ID 0x58 where a BME280 reports 0x60.
ESP32-DevKitC-32E

NeoPixel desk lamp with a vase-mode shade

A ring of WS2812B LEDs under a printed shade, with warm white, rainbow and candle modes on one button. The code caps the current so a USB charger can run it safely.

  • Beginner
  • 1 hour + printing
  • $20–30 in parts
What you learn
  • Why addressable LEDs need a power budget
  • Protecting LEDs with a resistor and a capacitor
  • Modes and a debounced button

Parts list

QtyPartWhy
1ESP32 dev boardDrives the LEDs with FastLED; its BOOT button switches modes
1WS2812B ring, 12 or 16 LEDsUp to 60 mA per LED at full white: 960 mA for 16
1330 Ω resistorIn the data line, close to the ring, to protect the first LED
11000 µF capacitor, 6.3 V or higherAcross 5V and GND at the ring, to soak up the switch-on surge
174AHCT125 level shifter (optional)Lifts the 3.3 V data to 5 V if the first LED glitches
1USB charger, 5 VThe code limits the LEDs to 450 mA
40 gWhite or natural PLAFor the shade; thin translucent walls diffuse best

Wiring

ESP32 DevKitC USB powered 5V GND GPIO16 Resistor 330 Ω IN OUT Capacitor 1000 µF + - LED ring WS2812B × 16 5V GND DIN
Signal Power Ground (GND) Wire colours are a convention, not a rule: what matters is which pin goes where.
FromToWireNote
ESP32 DevKitC GPIO16Resistor INSignalData from the ESP32
Resistor OUTLED ring DINSignalInto the first LED
ESP32 DevKitC 5VLED ring 5VPowerFrom USB, through the board
ESP32 DevKitC GNDLED ring GNDGroundA shared ground is required
Capacitor +LED ring 5VPowerLong leg to 5V
Capacitor -LED ring GNDGroundStriped side to GND
  • Power the ring from 5V, never from 3V3, and always join the grounds.
  • The ESP32 sends 3.3 V data and WS2812B LEDs expect close to 5 V. It often works anyway, but Adafruit recommends a 74AHCT125 for reliable data.

Code

neopixel-lamp.ino
#include <FastLED.h>

#define DATA_PIN   16
#define NUM_LEDS   16
#define BUTTON_PIN 0        // the BOOT button on most ESP32 dev boards

CRGB leds[NUM_LEDS];
uint8_t mode = 0;           // 0 warm white, 1 rainbow, 2 candle
uint8_t hue = 0;

void setup() {
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setMaxPowerInVoltsAndMilliamps(5, 450);   // stay inside what USB can give
  FastLED.setBrightness(160);
}

void loop() {
  static bool wasDown = false;
  bool down = digitalRead(BUTTON_PIN) == LOW;
  if (down && !wasDown) {
    mode = (mode + 1) % 3;
    delay(30);                                      // crude debounce
  }
  wasDown = down;

  if (mode == 0) {
    fill_solid(leds, NUM_LEDS, CRGB(255, 147, 41)); // warm white, like a candle bulb
  } else if (mode == 1) {
    fill_rainbow(leds, NUM_LEDS, hue++, 255 / NUM_LEDS);
  } else {
    for (int i = 0; i < NUM_LEDS; i++) {
      leds[i] = CRGB(255, 120, 20);
      leds[i].nscale8(random8(120, 255));           // each LED flickers on its own
    }
  }
  FastLED.show();
  delay(mode == 2 ? 60 : 20);
}

Library: FastLED. setMaxPowerInVoltsAndMilliamps dims everything just enough to stay under 450 mA.

Build it

  1. Solder three wires to the ring

    To 5V, GND and DIN. Put the resistor in the data wire, at the ring end.

  2. Add the capacitor

    Across 5V and GND at the ring. The striped side is negative.

  3. Upload

    Install FastLED, choose ESP32 Dev Module and upload. The ring glows warm white.

  4. Press BOOT to change mode

    Warm white, rolling rainbow, candle flicker.

  5. Print the shade

    Make one with the vase generator and print it in spiral vase mode.

Print settings: Shade: spiral vase mode with a 0.6 to 0.8 mm line width. Base: PLA, 0.2 mm, 3 walls, 15% infill.

The printed enclosure

Two parts: a round base that holds the ring face up with the board underneath, and a translucent shade printed in vase mode. Design the shade with the vase generator. Ring sizes differ between makers, so measure yours with calipers. Want no wiring at all? The LED lamp maker designs shades for Bambu Lab's LED Lamp Kit.

Current for a 16-LED WS2812B ring
16 LEDs at full white 960 mA
One USB 2.0 port 500 mA
This sketch's limit 450 mA

Adafruit gives up to 60 mA per LED at full-brightness white. FastLED scales brightness down to stay under the limit you set.

Arduino Uno R4 Minima

Garage parking sensor with an HC-SR04

An ultrasonic sensor on the garage wall beeps faster as the car gets closer, then holds a solid tone and a red light when it is time to stop.

  • Beginner
  • 1 hour + printing
  • $30–35 in parts
What you learn
  • Timing an echo to measure distance
  • Mapping one range of numbers onto another
  • Why pin current limits matter

Parts list

QtyPartWhy
1Arduino Uno R4 Minima5 V logic, so the HC-SR04 connects directly; $20 list
1HC-SR04 ultrasonic sensor5 V, measures 2 cm to 4 m; most reliable from 10 to 250 cm
1Passive piezo buzzertone() sets the pitch; an active buzzer beeps at one pitch only
1Red LEDThe stop light
11 kΩ resistorAbout 3 mA for the LED, well under the R4's 8 mA per pin
1Breadboard and jumper wires
1USB-C cable and 5 V chargerPower in the garage

Wiring

Uno R4 Minima 5 V logic 5V GND D9 D10 D6 D5 Resistor 1 kΩ A B HC-SR04 Ultrasonic sensor VCC TRIG ECHO GND LED Red + - Buzzer Passive piezo + -
Power Ground (GND) Signal Signal Signal PWM / signal Wire colours are a convention, not a rule: what matters is which pin goes where.
FromToWireNote
Uno R4 Minima 5VHC-SR04 VCCPower
Uno R4 Minima GNDHC-SR04 GNDGround
Uno R4 Minima D9HC-SR04 TRIGSignalA 10 µs pulse starts a reading
Uno R4 Minima D10HC-SR04 ECHOSignalA 5 V pulse: fine on an Uno
Uno R4 Minima D6Resistor ASignal
Resistor BLED +SignalLong leg
Uno R4 Minima GNDLED -GroundShort leg
Uno R4 Minima D5Buzzer +PWM / signaltone() output
Uno R4 Minima GNDBuzzer -Ground
  • Using an ESP32 instead? Its pins take 3.6 V at most, so divide the ECHO signal: Adafruit uses two 10 kΩ resistors to bring 5 V down to 2.5 V.
  • A magnetic buzzer can draw more than a pin should supply. Use a piezo, or drive it through a transistor.

Code

parking-sensor.ino
const int TRIG = 9;
const int ECHO = 10;
const int LED = 6;
const int BUZZER = 5;       // a passive piezo buzzer: tone() sets the pitch

void setup() {
  pinMode(TRIG, OUTPUT);
  pinMode(ECHO, INPUT);
  pinMode(LED, OUTPUT);
  Serial.begin(115200);
}

long readCm() {
  digitalWrite(TRIG, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG, HIGH);
  delayMicroseconds(10);                             // a 10 us pulse starts a reading
  digitalWrite(TRIG, LOW);
  unsigned long us = pulseIn(ECHO, HIGH, 30000UL);   // give up after 30 ms (about 5 m)
  if (us == 0) return -1;                            // nothing in range
  return us / 58;                                    // out and back: about 58 us per cm
}

void loop() {
  long cm = readCm();
  Serial.println(cm);

  if (cm < 0 || cm > 150) {                          // far away: stay quiet
    noTone(BUZZER);
    digitalWrite(LED, LOW);
    delay(100);
    return;
  }
  if (cm < 20) {                                     // stop here
    tone(BUZZER, 2000);
    digitalWrite(LED, HIGH);
    delay(100);
    return;
  }
  int gap = map(cm, 20, 150, 60, 600);               // closer means faster beeps
  tone(BUZZER, 2000, 50);
  digitalWrite(LED, HIGH);
  delay(50);
  digitalWrite(LED, LOW);
  delay(gap);
}

No libraries needed. Board: Arduino UNO R4 Minima (install Arduino UNO R4 Boards in Boards Manager).

Build it

  1. Wire it on a breadboard

    Follow the table. Mind the LED's long leg (+).

  2. Upload and test on the bench

    Move your hand toward the sensor: beeps speed up below 150 cm and turn solid under 20 cm.

  3. Set your stop distance

    Park where you want to stop, read the distance in the Serial Monitor, and replace both 20s in the code with it.

  4. Mount it

    At bumper height on the wall, pointing straight at the car.

Print settings: PLA or PETG, 0.2 mm, 3 walls, 15% infill. Print the front plate face down.

The printed enclosure

A front plate with two round holes for the transducers and a box behind it for the Uno. Measure the transducer diameter and spacing of your module with calipers before you design; clones vary. Keep the transducers flush with the front so the edges of the holes do not reflect the pulse.

ESP32-DevKitC-32E

Plant soil monitor with its own web page

A capacitive sensor in the pot and an ESP32 that serves a page to any phone on your Wi-Fi: moisture as a percentage, and a "water me" warning.

  • Intermediate
  • 1 hour + printing
  • $20–25 in parts
What you learn
  • Analog readings and calibration
  • Why ADC2 and Wi-Fi do not mix
  • A tiny web server on a microcontroller

Parts list

QtyPartWhy
1ESP32 dev boardWi-Fi, plus ADC1 pins that keep working while Wi-Fi is on
1Capacitive soil moisture sensor (SEN0193 design)3.3 to 5.5 V in, 0 to 3.0 V out, no exposed metal to corrode
3Female-to-female jumper wires
1USB charger and cable
25 gPETG filamentCopes with damp better than PLA

Wiring

ESP32 DevKitC ADC1 pin 3V3 GND GPIO34 Soil sensor Capacitive, analog out VCC GND AOUT
Power Ground (GND) Analog Wire colours are a convention, not a rule: what matters is which pin goes where.
FromToWireNote
ESP32 DevKitC 3V3Soil sensor VCCPower
ESP32 DevKitC GNDSoil sensor GNDGround
ESP32 DevKitC GPIO34Soil sensor AOUTAnalogInput-only pin on ADC1
  • Use an ADC1 pin (GPIO32 to 39). ADC2 pins cannot be read while Wi-Fi is on.
  • The reading goes down as the soil gets wetter.

Code

soil-monitor.ino
#include <WiFi.h>
#include <WebServer.h>

const char* WIFI_NAME = "your-network";       // 2.4 GHz only
const char* WIFI_PASS = "your-password";

const int SENSOR = 34;      // ADC1, so it keeps working while Wi-Fi is on
const int DRY = 3000;       // your reading in dry air
const int WET = 1300;       // your reading in a glass of water

WebServer server(80);

int moisture() {
  long sum = 0;
  for (int i = 0; i < 16; i++) { sum += analogRead(SENSOR); delay(5); }
  int raw = sum / 16;                                 // average out the noise
  return constrain(map(raw, DRY, WET, 0, 100), 0, 100);
}

void handleRoot() {
  int m = moisture();
  String html = "<!doctype html><meta name=viewport content='width=device-width'>"
                "<meta http-equiv=refresh content=10><title>Plant</title>"
                "<body style='font:24px system-ui;text-align:center;padding:40px'>"
                "<h1>" + String(m) + "%</h1><p>" +
                (m < 30 ? "Water me!" : "I'm fine, thanks.") + "</p>";
  server.send(200, "text/html", html);
}

void setup() {
  Serial.begin(115200);
  analogSetAttenuation(ADC_11db);                    // read the full 0 to ~3.1 V range
  WiFi.begin(WIFI_NAME, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print('.'); }
  Serial.printf("\nOpen http://%s\n", WiFi.localIP().toString().c_str());
  server.on("/", handleRoot);
  server.begin();
}

void loop() {
  server.handleClient();
  static unsigned long last = 0;
  if (millis() - last > 2000) {                      // raw readings, for calibrating
    last = millis();
    Serial.println(analogRead(SENSOR));
  }
}

Built-in libraries only. Replace DRY and WET with the readings the Serial Monitor shows for your sensor.

Build it

  1. Calibrate dry and wet

    Upload, open the Serial Monitor and note the reading with the sensor in air (DRY) and in a glass of water up to its line (WET). Put both in the code.

  2. Add your Wi-Fi

    Fill in WIFI_NAME and WIFI_PASS. The ESP32 joins 2.4 GHz networks only.

  3. Open the page

    The Serial Monitor prints an address such as http://192.168.1.42. Open it on a phone on the same network.

  4. Seal the top edge

    The electronics at the top of the sensor are not waterproof. Coat them with nail varnish or heat-shrink, and push the sensor in only up to its line.

  5. Fit the printed cap

    It covers the electronics and the cable so watering cannot reach them.

Print settings: PETG, 0.2 mm, 3 walls, 20% infill.

The printed enclosure

A stake-shaped cap that slides over the top of the sensor and covers the electronics, with the cable leaving downward so water runs off. Keep the ESP32 in a small box out of the pot.

ESP32-DevKitC-32E

Useless box: flip the switch, a finger flips it back

The classic desk toy. Flip the toggle and a servo arm pops out of the lid to switch itself off, in one of three moods so it never gets old.

  • Intermediate
  • 2 hours + printing
  • $20–25 in parts
What you learn
  • Driving a servo with PWM
  • Reading a switch with a pull-up resistor
  • Random behavior that feels alive

Parts list

QtyPartWhy
1ESP32 dev boardAny ESP32 with a 5V pin
1SG90 micro servo4.8 V, 1.8 kg·cm stall torque: plenty to flip a toggle
1SPST toggle switchWired between GPIO14 and GND
1470 µF capacitor (optional)Across the servo supply if the board resets when the servo moves
1USB charger, 1 A or moreLaptop ports can sag when the servo starts
80 gPLA filamentBox, lid, flap and arm

Wiring

ESP32 DevKitC USB powered 5V GND GPIO13 GPIO14 SG90 Servo: red, brown, orange VCC GND SIG Switch Toggle A B
Power Ground (GND) PWM / signal Signal Wire colours are a convention, not a rule: what matters is which pin goes where.
FromToWireNote
ESP32 DevKitC 5VSG90 VCCPowerRed wire
ESP32 DevKitC GNDSG90 GNDGroundBrown wire
ESP32 DevKitC GPIO13SG90 SIGPWM / signalOrange wire, PWM
ESP32 DevKitC GPIO14Switch ASignalINPUT_PULLUP
ESP32 DevKitC GNDSwitch BGround
  • Power the servo from 5V, not 3V3. The 3.3 V signal from the ESP32 drives an SG90 fine.
  • TowerPro rates the SG90 at 4.8 V and says 4.8 to 6 V is fine.

Code

useless-box.ino
#include <ESP32Servo.h>
#include <esp_random.h>

const int SERVO_PIN = 13;
const int SWITCH_PIN = 14;  // switch to GND: LOW means "on"
const int REST = 10;        // arm tucked inside the box, in degrees
const int PUSH = 150;       // arm far enough to flip the switch: tune this

Servo arm;

void setup() {
  pinMode(SWITCH_PIN, INPUT_PULLUP);
  randomSeed(esp_random());                     // a different mood order every power-up
  arm.setPeriodHertz(50);
  arm.attach(SERVO_PIN, 500, 2400);             // SG90 pulse range in microseconds
  arm.write(REST);
}

void sweep(int from, int to, int stepDelay) {
  int dir = from < to ? 1 : -1;
  for (int a = from; a != to; a += dir) { arm.write(a); delay(stepDelay); }
  arm.write(to);
}

void loop() {
  if (digitalRead(SWITCH_PIN) == HIGH) return;  // switch is off: nothing to do

  switch (random(3)) {
    case 0:                                     // polite
      sweep(REST, PUSH, 4);
      break;
    case 1:                                     // sneaky: peek, wait, strike
      sweep(REST, 90, 12);
      delay(random(600, 1800));
      sweep(90, PUSH, 2);
      break;
    default:                                    // annoyed: twice, fast
      arm.write(PUSH); delay(250);
      arm.write(110);  delay(150);
      arm.write(PUSH); delay(250);
  }
  delay(200);
  sweep(PUSH, REST, 3);
}

Library: ESP32Servo from the Library Manager. Raise PUSH until the arm just flips the switch without straining.

Build it

  1. Print the box first

    You need it to find the servo angles.

  2. Mount the servo and switch

    Servo under the flap, horn pointing at the switch; switch through the lid.

  3. Find REST and PUSH

    Start with PUSH at 120 and raise it until the arm flips the switch cleanly.

  4. Upload and flip

    Each flip picks one of three moods at random.

  5. Close it up

    The arm lifts the hinged flap on its way out.

Print settings: PLA, 0.2 mm, 3 walls, 15% infill. Print the arm flat so the layers run along it.

The printed enclosure

A box with a hinged flap in the lid, a servo mount under the flap and a hole for the toggle. A print-in-place hinge saves hardware. SG90 clones vary slightly, so measure yours before designing the mount.

ESP32-S3-DevKitC-1

Three-key USB macro pad with an ESP32-S3

Three mechanical keys that type shortcuts on any computer with no driver: the ESP32-S3 shows up as a USB keyboard.

  • Intermediate
  • 1.5 hours + printing
  • $25–35 in parts
What you learn
  • USB HID: how a board pretends to be a keyboard
  • Edge detection for buttons
  • Standard keyboard dimensions for printed plates

Parts list

QtyPartWhy
1ESP32-S3 dev board with native USBThe S3 can act as a USB keyboard; the classic ESP32 cannot
3MX-compatible mechanical switchesThey clip into a 14 mm square plate cutout
3MX-stem keycaps
1Hookup wire, or hot-swap socketsOne wire per key plus a shared ground
40 gPLA or PETGPlate and case

Wiring

ESP32-S3 DevKitC-1 Native USB GPIO4 GPIO5 GPIO6 GND Key 1 Copy A B Key 2 Paste A B Key 3 Type a snippet A B
Signal Ground (GND) Signal Signal Wire colours are a convention, not a rule: what matters is which pin goes where.
FromToWireNote
ESP32-S3 DevKitC-1 GPIO4Key 1 ASignal
ESP32-S3 DevKitC-1 GNDKey 1 BGround
ESP32-S3 DevKitC-1 GPIO5Key 2 ASignal
ESP32-S3 DevKitC-1 GNDKey 2 BGround
ESP32-S3 DevKitC-1 GPIO6Key 3 ASignal
ESP32-S3 DevKitC-1 GNDKey 3 BGround
  • Each switch connects its pin to ground; INPUT_PULLUP holds the pin HIGH until you press.
  • GPIO4, 5 and 6 are not strapping pins on the S3, so they are safe for keys.

Code

macro-pad.ino
#include "USB.h"
#include "USBHIDKeyboard.h"

USBHIDKeyboard Keyboard;

const int KEYS[3] = {4, 5, 6};     // each switch connects its pin to GND
bool wasDown[3] = {false, false, false};

void setup() {
  for (int i = 0; i < 3; i++) pinMode(KEYS[i], INPUT_PULLUP);
  Keyboard.begin();
  USB.begin();
}

void press(int i) {
  switch (i) {
    case 0:                                       // copy
      Keyboard.press(KEY_LEFT_CTRL); Keyboard.press('c');
      break;
    case 1:                                       // paste
      Keyboard.press(KEY_LEFT_CTRL); Keyboard.press('v');
      break;
    case 2:                                       // type a snippet
      Keyboard.print("Printed on my own macro pad!");
      return;
  }
  delay(20);
  Keyboard.releaseAll();
}

void loop() {
  for (int i = 0; i < 3; i++) {
    bool down = digitalRead(KEYS[i]) == LOW;
    if (down && !wasDown[i]) press(i);
    wasDown[i] = down;
  }
  delay(5);                                       // simple debounce
}

Board: ESP32S3 Dev Module, USB Mode: USB-OTG (TinyUSB). On a Mac use KEY_LEFT_GUI (Command) instead of KEY_LEFT_CTRL.

Build it

  1. Print the plate and case

    The 1.5 mm plate is what the switch clips grip.

  2. Snap in the switches

    Push from the top until they click.

  3. Wire the keys

    One leg of each switch to its GPIO, the other legs together to GND.

  4. Set USB mode and upload

    USB Mode: USB-OTG (TinyUSB). Upload through the UART port, then plug the port labelled USB into the computer.

  5. Try it

    Open a text editor and press key 3.

Print settings: PLA or PETG, 0.2 mm, 4 walls; print the plate flat so the cutouts stay square.

The printed enclosure

A 1.5 mm plate with three 14 × 14 mm square cutouts spaced 19.05 mm apart, the standard key pitch, on a case deep enough for the board and wires. Leave an opening for the USB port and the BOOT button.

ESP32 rules that save hours of debugging

Pin rules for the classic ESP32 (and one for the Uno R4)
RuleWhyDo this
Pins are 3.3 V, not 5 V tolerantEspressif rates GPIO at 3.6 V maximumDivide or level-shift 5 V signals, such as an HC-SR04 echo
ADC2 stops when Wi-Fi is onWi-Fi uses the ADC2 hardwareRead analog sensors on ADC1: GPIO32 to 39
GPIO34 to 39 are input onlyNo output driver, no internal pull-upsUse them for sensors; add external pull-ups
Strapping pins: GPIO0, 2, 5, 12, 15Their level at power-up sets the boot modeAvoid parts that pull them high or low at boot
GPIO6 to 11 belong to the flashThey connect to the SPI flash chip on most modulesNever use them
Default I2C is SDA 21, SCL 22Set in the Arduino ESP32 coreUse them unless you need others
Uno R4 pins supply 8 mAArduino's specification for the R4 MinimaUse 1 kΩ for LEDs; a transistor for more

Designing the printed enclosure

  • Measure, do not trust drawings. Module sizes vary between makers. Measure every board with calipers, then add clearance: 0.2 to 0.4 mm per side is a good start on FDM printers.
  • Make the USB opening generous. Plugs have overmoulds that are bigger than the port.
  • Plan for heat. Give regulators and the ESP32 some air, and keep temperature sensors away from both.
  • Print faces down. Front plates and windows come out cleanest printed face-first on the bed.
  • Use inserts or bosses for screws. Heat-set inserts survive repeated opening; self-tapping screws into bare plastic wear out.
  • Pick the material for the place. See the table below.
Filament for electronics enclosures
MaterialUse it forAvoid
PLAIndoor gadgets, desk toys, prototypesCars, windowsills, outdoors: it softens in heat
PETGDamp places, planters, garagesVery fine detail (it strings more)
ASAOutdoor cases in sunlightPrinting without an enclosure and ventilation
TPUFeet, bumpers, gaskets, cable strain reliefRigid parts

Do not want to model it yourself? Describe the box with your measurements to the CAD agent, or start from a PrintPal maker for the round ESP32-S3 screen.

Getting code onto the board

  1. Install Arduino IDE

    Version 2.3.11 is current (November 12, 2026). Arduino Cloud's free plan also has an online editor.

  2. Add the board package

    esp32 by Espressif Systems for ESP32 boards, Arduino UNO R4 Boards for the Uno R4.

  3. Use a data cable

    If no port appears, swap the cable before anything else.

  4. Enter download mode if needed

    Hold BOOT, tap RESET, release BOOT, then upload again.

  5. Or skip the IDE

    PrintPal build guides include precompiled firmware for the ESP32 web flasher: plug in, click, done.

  6. Simulate first

    Wokwi simulates ESP32 and Arduino projects in the browser, free with unlimited public projects, so you can test code before the parts arrive.

Frequently asked questions

What is a good first ESP32 project?

One with no wiring: an ESP32 board with a built-in screen in a printed case, flashed from the browser, such as PrintPal's Slab eyes or Desk Pal buddy builds. After that, an ESP32 weather station with a BME280 and an OLED teaches I2C with just four wires.

Should I start with Arduino or ESP32?

Start with an Arduino Uno R4 if your sensors are 5 V parts, because its pins run at 5 V. Choose an ESP32 for Wi-Fi or Bluetooth, and an ESP32-S3 for native USB or a screen. Both use the Arduino IDE.

Can I connect a 5 V sensor to an ESP32?

Power it from 5 V if it needs that, but never feed a 5 V signal into an ESP32 pin: they are rated for 3.6 V at most. Use a voltage divider (Adafruit uses two 10 kΩ resistors on the HC-SR04 echo pin) or a level shifter.

Why does my ESP32 reset when the servo moves?

The servo pulls a burst of current that makes the supply voltage dip. Power the servo from 5V rather than 3V3, use a stronger USB charger, and add a large capacitor (around 470 µF) across the servo supply.

What filament is best for electronics enclosures?

PLA for indoor projects, PETG for damp places such as planters and garages, and ASA for outdoor cases in sunlight. PLA softens in heat, so keep it out of cars and windowsills.

Do I need to solder for these projects?

Not for the ten click-together builds, the weather station, the parking sensor or the soil monitor if your modules come with headers fitted. The NeoPixel lamp needs three wires soldered to the ring, and the macro pad needs soldering or hot-swap sockets.

How do I flash an ESP32 from the browser?

Open a web flasher in Chrome or Edge, connect the board with a USB data cable, pick its port and click Install. PrintPal's ESP32 web flasher and every PrintPal build guide work this way. If no port appears, hold BOOT, tap RESET and try again.

Sources and method

Board specifications and prices come from the makers' and distributors' own pages (Arduino, Espressif, Adafruit, Seeed, Raspberry Pi, Waveshare) on November 12, 2026. Electrical limits come from Espressif's documentation and the part makers' datasheets listed below. The code was written for this guide against the current Arduino cores and the libraries named in each project. Parts costs are estimates. The ten click-together builds are PrintPal's own build guides.

  1. Arduino Uno R4 Minima · 5 V logic, 8 mA per pin
  2. Arduino Uno R4 WiFi
  3. Arduino Nano ESP32
  4. ESP32-DevKitC user guide
  5. ESP32-S3-DevKitC-1 user guide
  6. ESP32-C6-DevKitC-1 user guide
  7. Adafruit ESP32-DevKitC-32E
  8. Seeed XIAO ESP32-S3
  9. Seeed XIAO ESP32-C6
  10. Raspberry Pi Pico 2 W launch
  11. Waveshare ESP32-S3-Touch-LCD-1.28 wiki
  12. Waveshare ESP32-C6-LCD-1.47 wiki
  13. ESP32-S3-Touch-LCD-1.28 on Amazon (B0CM68M8LR)
  14. Espressif ESP-FAQ: hardware design · 3.6 V GPIO limit
  15. ESP-IDF GPIO reference · ADC2, input-only and strapping pins
  16. Arduino ESP32 core pin map
  17. Adafruit: powering NeoPixels
  18. Adafruit: NeoPixel best practices
  19. SparkFun HC-SR04
  20. Adafruit HC-SR04 · echo divider
  21. TowerPro SG90
  22. Adafruit BME280
  23. Adafruit SSD1306 OLED
  24. DFRobot SEN0193 datasheet
  25. TP4056 datasheet
  26. Adafruit: Li-ion and LiPoly protection
  27. Arduino IDE releases
  28. Wokwi pricing

Spotted something out of date? Tell us and we will re-check it.

Build one this weekend

Ten ESP32 build guides with printable cases, parts lists, source code and one-click flashing from your browser.