Difficulty: Beginner
Time Required: 1-2 hours
Cost: $12-18
ESP Board: ESP32
What You’ll Need
| Component | Approximate Cost | Where to Buy |
|---|---|---|
| ESP32 Dev Board | $5-8 | Amazon / AliExpress |
| BME280 Sensor (I2C) | $3-5 | Amazon |
| OLED Display (SSD1306 128×64) | $2-4 | Amazon |
| Jumper Wires | $2 | Amazon |
| Breadboard | $2-3 | Amazon |
Circuit Diagram
Connections:
| ESP32 Pin | Component | Notes |
|---|---|---|
| 3.3V | BME280 VCC | Power |
| GND | BME280 GND | Ground |
| GPIO 21 | BME280 SDA | I2C Data |
| GPIO 22 | BME280 SCL | I2C Clock |
| 3.3V | OLED VCC | Power |
| GND | OLED GND | Ground |
| GPIO 21 | OLED SDA | I2C Data (shared) |
| GPIO 22 | OLED SCL | I2C Clock (shared) |
Step 1: Install Required Libraries
Open Arduino IDE and install these libraries:
- Adafruit BME280 Library – Official Docs
- Adafruit GFX Library – Official Docs
- Adafruit SSD1306 – Official Docs
Note: Both devices share the same I2C bus (GPIO 21 & 22), so no additional pins needed.
Step 2: The Complete Code
/*********************************************************************
* ESP32 WiFi Weather Station with OLED Display
*
* Displays: Temperature, Humidity, Pressure, Altitude
* Hardware: ESP32, BME280 Sensor, SSD1306 OLED 128x64
*
* Libraries Required:
* - Adafruit BME280 (install from Library Manager)
* - Adafruit GFX Library
* - Adafruit SSD1306
*
* I2C Pins: SDA=GPIO 21, SCL=GPIO 22
*********************************************************************/
#include
#include
#include
#include
// ============== DISPLAY CONFIGURATION ==============
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// ============== SENSOR CONFIGURATION ==============
#define SEALEVELPRESSURE_HPA (1013.25)
Adafruit_BME280 bme280;
// ============== BAUD RATE ==============
#define BAUD_RATE 115200
// ============== DISPLAY UPDATE INTERVAL ==============
#define UPDATE_INTERVAL 2000 // milliseconds
unsigned long lastUpdate = 0;
// ============== SETUP ==============
void setup() {
Serial.begin(BAUD_RATE);
Serial.println(F("\n======================================"));
Serial.println(F("ESP32 WiFi Weather Station Starting..."));
Serial.println(F("======================================"));
// Initialize OLED Display
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
while (true); // Halt
}
// Initialize BME280 Sensor
if (!bme280.begin(0x76)) { // Try 0x76 first (common address)
Serial.println(F("Could not find BME280 sensor at 0x76, trying 0x77..."));
if (!bme280.begin(0x77)) {
Serial.println(F("Could not find BME280 sensor!"));
while (true);
}
}
Serial.println(F("BME280 initialized successfully!"));
// Clear display and show startup message
display.clearDisplay();
display.display();
delay(1000);
// Test display
displayText("Weather Station", "Initializing...", 1000);
displayText("Weather Station", "Sensor OK!", 500);
Serial.println(F("\nSetup complete!"));
Serial.println(F("Reading sensor data...\n"));
}
// ============== MAIN LOOP ==============
void loop() {
// Update display every 2 seconds
if (millis() - lastUpdate > UPDATE_INTERVAL) {
lastUpdate = millis();
updateDisplay();
}
}
// ============== DISPLAY FUNCTION ==============
void updateDisplay() {
// Read sensor data
float temperature = bme280.readTemperature(); // Celsius
float humidity = bme280.readHumidity(); // Percentage
float pressure = bme280.readPressure() / 100.0F; // hPa
float altitude = bme280.readAltitude(SEALEVELPRESSURE_HPA); // Meters
// Print to Serial Monitor
Serial.println(F("========== WEATHER DATA =========="));
Serial.print(F("Temperature: ")); Serial.print(temperature); Serial.println(F(" °C"));
Serial.print(F("Humidity: ")); Serial.print(humidity); Serial.println(F(" %"));
Serial.print(F("Pressure: ")); Serial.print(pressure); Serial.println(F(" hPa"));
Serial.print(F("Altitude: ")); Serial.print(altitude); Serial.println(F(" m"));
Serial.println(F("================================\n"));
// Update OLED Display
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
// Title
display.setCursor(25, 0);
display.println(F("WEATHER"));
display.setCursor(30, 8);
display.println(F("STATION"));
// Divider line
display.drawLine(0, 18, 128, 18, SSD1306_WHITE);
// Weather data
display.setTextSize(1);
display.setCursor(0, 24);
display.print(F("Temp: ")); display.print(temperature, 1); display.println(F(" C"));
display.setCursor(0, 34);
display.print(F("Hum: ")); display.print(humidity, 0); display.println(F(" %"));
display.setCursor(0, 44);
display.print(F("Pres: ")); display.print(pressure, 0); display.println(F(" hPa"));
display.setCursor(0, 54);
display.print(F("Alt: ")); display.print(altitude, 0); display.println(F(" m"));
display.display();
}
// ============== HELPER FUNCTION ==============
void displayText(String line1, String line2, int delayMs) {
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.setCursor(10, 15);
display.println(line1);
display.setCursor(25, 35);
display.setTextSize(1);
display.println(line2);
display.display();
delay(delayMs);
}
// ============== END OF CODE ==============
Step 3: Upload and Test
- Connect ESP32 via USB
- Select Tools → Board → ESP32 Dev Module
- Select correct port
- Click Upload
- Open Serial Monitor (115200 baud)
- Watch weather data appear every 2 seconds
Step 4: Add WiFi for Remote Monitoring (Optional)
Add this code at the top to send data to IoT platforms:
// Add WiFi configuration
const char* wifi_ssid = "YOUR_WIFI_NAME";
const char* wifi_password = "YOUR_WIFI_PASSWORD";
// Add to setup():
WiFi.begin(wifi_ssid, wifi_password);
Serial.print("Connecting to WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println(" Connected!");
// Send to ThingSpeak (example)
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
String url = "https://api.thingspeak.com/update?api_key=YOUR_API_KEY";
url += "&field1=" + String(temperature);
url += "&field2=" + String(humidity);
url += "&field3=" + String(pressure);
http.begin(url);
http.GET();
http.end();
}
Troubleshooting
| Problem | Solution |
|---|---|
| OLED shows nothing | Check I2C address (0x3C or 0x3D); try both |
| BME280 not found | Check I2C address (0x76 or 0x77); update code accordingly |
| Temperature reading wrong | Keep sensor away from ESP32 heat; use header pins |
| No data on serial monitor | Select correct COM port; set baud rate to 115200 |
| Display flickers | Add 100nF capacitor between VCC and GND |
Official Documentation
- Adafruit BME280 Library – Official API reference
- Adafruit SSD1306 Library – Display documentation
- ESP32 GitHub Repository – Official ESP32 code
- Adafruit BME280 Guide – Detailed tutorial
- Adafruit OLED Guide – Display tutorial
Enhancement Ideas
- Add ThingSpeak for cloud logging
- Integrate with Home Assistant via MQTT
- Add NodeMCU for battery operation
- Add outdoor sensor with ESP-NOW
Frequently Asked Questions
Q: What is the difference between BMP280 and BME280?
A: BMP280 reads temperature and pressure only. BME280 adds humidity sensing. BME280 is recommended for weather stations.
Q: Why are temperature readings higher than expected?
A: ESP32 generates heat. Mount BME280 at least 2cm away or use shielded wires. The sensor measures its own temperature slightly.
Q: Can I use this with a battery?
A: Yes. Use a 3.7V LiPo battery with a step-up regulator. ESP32 deep sleep can run for weeks on a single charge.
Q: How accurate is the BME280?
A: Temperature: ±1°C, Humidity: ±3%, Pressure: ±1 hPa. Professional-grade accuracy for hobby projects.
Q: Can I display data in Fahrenheit?
A: Yes. Replace temperature reading with: float tempF = temperature * 9.0 / 5.0 + 32.0;

