Community project
WiFi DeskBuddy Monitor
Generated with AIWiFi DeskBuddy Monitor is a compact, battery-powered desktop display that shows real-time temperature, barometric pressure, and altitude readings. Built around an ESP32 microcontroller, the device combines an SSD1306 OLED screen with a BMP180 environmental sensor, all powered by a protected 18650 Li-ion cell and boost converter for reliable operation.
This guide provides a complete parts list, wiring diagram, and step-by-step assembly instructions for building the DeskBuddy in its custom 3D-printed enclosure. Readers will also receive the full firmware with animated display rendering, sensor polling, and I²C communication code ready to upload to the ESP32.
Wiring diagram
Interactive · read-only
Pan and zoom to explore the wiring. Remix the project to edit it in your own workspace.
Parts list
Bill of materials| Component | Qty | Notes |
|---|---|---|
| SSD1306 OLED0.96 in, 128x64, I2C (address 0x3C) | 1 | 0.96 inch 128x64 OLED display with I2C interface |
| 18650 Li-ion Cell3.7 V nominal, 2500 mAh, protected | 1 | 18650 lithium-ion cell, nominal 3.7 V, ~2500 mAh. Common for higher-capacity portable / battery-bank style projects; needs a holder and protection / charger circuit. |
| TP4056 Li-Ion/LiPo charger module with protection5 V USB input, protected output | 1 | TP4056 single-cell Li-Ion/LiPo linear charger module, 5V USB input, 1A charge current (programmable). Common variants ship with DW01 protection. Pair with battery_lipo_storage for the cell. |
| Boost ConverterSet to 5.0 V output | 1 | Small adjustable MT3608-style DC-DC boost converter module for stepping a lower DC input up to a higher rail such as 5V, 9V, or 12V. It is a power-path module with VIN/VOUT terminals, not a GPIO peripheral. |
| BMP180 Barometric Pressure and Temperature Sensor ModuleBMP180 I²C pressure/temperature module (address 0x77) | 1 | BMP180 I²C barometric-pressure and temperature breakout module. Use its 3.3 V supply option with the ESP32 so I²C logic remains at 3.3 V. |
Assembly
5 stepsPrint the two-piece DeskBuddy enclosure
Print a base and removable front/lid in PETG or PLA. Start at 115 × 70 × 32 mm with 2.0–2.4 mm walls, then verify all openings against your actual modules. Provide M2/M2.5 mounting posts for the ESP32 and OLED, a separate restrained bay or commercial holder for the 18650 cell, a 28 × 14 mm OLED window, a TP4056 USB cutout, and a small vent pattern close to the BMP180.
- Tip: Make the OLED opening 0.5–1 mm larger than its visible glass area only after measuring your module.
- Tip: Use four M3 lid screws or a serviceable friction-fit lid so the battery remains inspectable.
- ⚠ Do not make a tight crush-fit compartment around a bare Li-ion cell.
- ⚠ Keep the TP4056 USB charging opening accessible with the enclosure closed.
Lay out the enclosure internals
Dry-fit the OLED behind the front window and the ESP32 behind it. Place the BMP180 near a small vented opening but away from direct airflow, the boost converter, and the ESP32 voltage regulator. Place the TP4056 USB connector at its external cutout. Keep the protected 18650 in its own bay along the long enclosure side and keep the ESP32 antenna end free of battery and metal obstructions.
- Tip: Use nylon standoffs, small screws, or thin foam tape so PCB solder joints cannot touch the battery.
- Tip: Provide internal wire channels or tie points so the lid cannot pinch a wire.
- ⚠ The BMP180 measures pressure and temperature, not humidity.
- ⚠ Avoid placing the BMP180 in a sealed heated pocket; this biases its temperature reading.
Build and test the protected battery supply
With the battery disconnected, wire battery_1 +V to charger_1 B+ and battery_1 GND to charger_1 B-. Wire charger_1 OUT+ to boost_1 VIN+ and charger_1 OUT- to boost_1 VIN-. USB charging connects only to the TP4056 input. Measure and adjust the boost converter to exactly 5.0 V at VOUT+/VOUT− before connecting it to the ESP32 VIN/5V and GND.
- Tip: Use short insulated wires and strain relief for battery and charger leads.
- Tip: Choose a TP4056 board with separate B+/B− battery terminals and OUT+/OUT− protected-load terminals.
- ⚠ Reverse polarity or an incorrect boost voltage can damage equipment or heat the battery.
- ⚠ Never connect the 5 V boost output to the ESP32 3V3 pin.
Install the shared I²C sensor and display wiring
Connect boost_1 VOUT+ to ESP32 VIN/5V and boost_1 VOUT− to ESP32 GND. Connect both oled_1 and bmp180_1 VCC to ESP32 3V3, and both grounds to ESP32 GND. Connect OLED SDA and BMP180 SDA together to GPIO21; connect OLED SCL and BMP180 SCL together to GPIO22. Mount the BMP180 board behind its vented opening without covering its pressure port or metal sensor package.
- Tip: The BMP180 and OLED intentionally share the same two I²C wires because their normal addresses differ: BMP180 0x77 and OLED 0x3C.
- Tip: Keep I²C leads short and route them away from the boost converter inductor.
- ⚠ Use the BMP180 module's 3.3 V supply configuration so its I²C pull-ups never drive the ESP32 pins to 5 V.
- ⚠ Do not seal the BMP180 pressure port with glue, foam, tape, or conformal coating.
Close the enclosure and perform the final check
Secure modules and wire bundles so no bare conductor or board underside can touch the battery. Insert the protected 18650 cell last, close the enclosure, and confirm the OLED window is clear, the BMP180 vent is clear, and the TP4056 USB port remains reachable. Use Schematik’s Deploy button to flash the prepared firmware.
- Tip: Altitude is calculated using standard sea-level pressure; it is useful as a relative trend unless you calibrate it for your local weather pressure.
- Tip: Expected runtime remains roughly 10 hours, depending on Wi-Fi use, display activity, battery condition, and boost efficiency.
- ⚠ Charge Li-ion cells on a nonflammable surface and do not leave charging unattended.
- ⚠ Disconnect the battery before opening the enclosure or changing wiring.
Pin assignments
Board wiring reference| Pin | Connection | Type |
|---|---|---|
| 3V3 | oled_1 VCC | power |
| GND | oled_1 GND | ground |
| GPIO 21 | oled_1 SDA | i2c |
| GPIO 22 | oled_1 SCL | i2c |
| EXT | battery_1 +V → TP4056 Li-Ion/LiPo charger module with protection B+ | power |
| EXT | battery_1 GND → TP4056 Li-Ion/LiPo charger module with protection B- | ground |
| EXT | charger_1 IN+ → 5 V USB charging connector | power |
| EXT | charger_1 IN- → USB charging connector ground | ground |
| EXT | charger_1 OUT+ → Boost Converter VIN+ | power |
| EXT | charger_1 OUT- → Boost Converter VIN- | ground |
| VIN | boost_1 VOUT+ | power |
| GND | boost_1 VOUT- | ground |
| 3V3 | bmp180_1 VCC | power |
| GND | bmp180_1 GND | ground |
| GPIO 21 | bmp180_1 SDA | i2c |
| GPIO 22 | bmp180_1 SCL | i2c |
Firmware
ESP32#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_BMP085.h>
void drawFace(int16_t centerX, int16_t centerY, uint8_t frame);
void drawScreen();
void readSensor();
constexpr uint8_t OLED_SDA_PIN = 21;
constexpr uint8_t OLED_SCL_PIN = 22;
constexpr uint8_t SCREEN_WIDTH = 128;
constexpr uint8_t SCREEN_HEIGHT = 64;
constexpr unsigned long SENSOR_INTERVAL_MS = 2000;
constexpr unsigned long ANIMATION_INTERVAL_MS = 50;
constexpr float SEA_LEVEL_PRESSURE_PA = 101325.0f;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
Adafruit_BMP085 bmp;
float temperatureC = NAN;
float pressureHpa = NAN;
float altitudeM = NAN;
float previousTemperatureC = NAN;
float previousPressureHpa = NAN;
uint8_t animationFrame = 0;
unsigned long lastSensorRead = 0;
unsigned long lastAnimation = 0;
bool displayReady = false;
bool bmpReady = false;
void drawFace(int16_t centerX, int16_t centerY, uint8_t frame) {
const int8_t bob = (frame % 20 < 10) ? 0 : 1;
display.drawCircle(centerX, centerY + bob, 10, SSD1306_WHITE);
display.fillCircle(centerX - 3, centerY - 2 + bob, 1, SSD1306_WHITE);
display.fillCircle(centerX + 3, centerY - 2 + bob, 1, SSD1306_WHITE);
display.drawLine(centerX - 4, centerY + 4 + bob, centerX + 4, centerY + 4 + bob, SSD1306_WHITE);
}
void drawScreen() {
if (!displayReady) return;
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.setCursor(0, 0);
display.print("DeskBuddy BMP180");
display.drawFastHLine(0, 10, SCREEN_WIDTH, SSD1306_WHITE);
drawFace(116, 21, animationFrame);
display.setTextSize(2);
display.setCursor(0, 15);
if (isnan(temperatureC)) {
display.print("--.-C");
} else {
display.print(temperatureC, 1);
display.print("C");
}
display.setTextSize(1);
display.setCursor(0, 38);
if (isnan(pressureHpa)) {
display.print("Pressure: ----.- hPa");
} else {
display.print("Pressure: ");
display.print(pressureHpa, 1);
display.print(" hPa");
}
display.setCursor(0, 54);
if (!bmpReady) {
display.print("BMP180 not found");
} else if (isnan(altitudeM)) {
display.print("Reading sensor...");
} else {
display.print("Altitude: ");
display.print(altitudeM, 0);
display.print(" m");
}
display.display();
}
void readSensor() {
if (!bmpReady) {
bmpReady = bmp.begin();
return;
}
const float nextTemperature = bmp.readTemperature();
const float nextPressureHpa = bmp.readPressure() / 100.0f;
if (!isnan(nextTemperature) && nextPressureHpa > 0.0f) {
temperatureC = nextTemperature;
pressureHpa = nextPressureHpa;
altitudeM = bmp.readAltitude(SEA_LEVEL_PRESSURE_PA);
}
}
void setup() {
Wire.begin(OLED_SDA_PIN, OLED_SCL_PIN);
displayReady = display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
bmpReady = bmp.begin();
if (displayReady) {
drawScreen();
}
}
void loop() {
const unsigned long now = millis();
if (now - lastSensorRead >= SENSOR_INTERVAL_MS) {
lastSensorRead = now;
previousTemperatureC = temperatureC;
previousPressureHpa = pressureHpa;
readSensor();
if (temperatureC != previousTemperatureC || pressureHpa != previousPressureHpa) {
drawScreen();
}
}
if (now - lastAnimation >= ANIMATION_INTERVAL_MS) {
lastAnimation = now;
animationFrame++;
drawScreen();
}
}“Deploy to device” opens this project in Schematik, where you can flash it to your board over USB.
Remix this project
Make it yours in one click
Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.