Imagine having real-time insights into temperature changes, accessible from anywhere in the world. With an ESP32 and a BME280 sensor, you can build a smart, cloud-connected monitoring system that logs environmental data and visualizes it dynamically.
In this project, you’ll configure an ESP32 to read temperature data from a BME280 sensor and transmit it to Telemetry Harbor, a powerful IoT data platform. From there, the data is stored, processed, and displayed on a Grafana dashboard, giving you a live view of temperature trends. Whether you’re tracking climate conditions, monitoring sensitive equipment, or just experimenting with IoT, this setup provides a scalable foundation for automation, alerts, and advanced analytics.
By the end of this guide, you’ll have a fully functional system that not only logs temperature readings but also lays the groundwork for expanding your IoT network with more sensors, automated triggers, and cloud-based intelligence. Let’s dive in!
Hardware Requirements
- ESP32 development board (NodeMCU ESP32, WROOM, etc.) – AliExpress Price 3.82USD | Amazon US Price 9.90USD
- BME280 temperature, humidity, and pressure sensor module – AliExpress Price: 4.20USD | Amazon US Price 8.99USD
- Jumper wires – AliExpress Price: 0.42USD | Amazon US Price 5.49USD
- Micro USB Power & Data – AliExpress Price: 3.00USD | Amazon US Price ~5.00USD
- Optional: 3.3V power supply for standalone deployment
Software Requirements
- Arduino IDE (1.8.13 or newer) or PlatformIO
- Required libraries:
- Adafruit BME280 Library
- Adafruit Unified Sensor
- WiFi Library (built into ESP32 core)
- HTTPClient Library (built into ESP32 core)
- Telemetry Harbor account (free tier available)
Step 1: Setting Up Your Development Environment
Installing ESP32 Board Support
- Open Arduino IDE
- Go to File > Preferences
- Add the following URL to the “Additional Boards Manager URLs”
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
- Go to Tools > Board > Boards Manager
- Search for “ESP32” and install the ESP32 by Espressif Systems
Installing Required Libraries
- Go to Sketch > Include Library > Manage Libraries
- Search for and install:
- Adafruit BME280 Library
- Adafruit Unified Sensor
Step 2: Wiring the BME280 Sensor to ESP32
The BME280 uses the I2C communication protocol, which requires just four connections:
BME280 Pin | ESP32 Pin | Function |
---|---|---|
VCC | 3.3V | Power |
GND | GND | Ground |
SDA | GPIO 21 | Data |
SCL | GPIO 22 | Clock |
Step 3: Setting Up Telemetry Harbor
Telemetry Harbor provides a straightforward platform for collecting, storing, and visualizing IoT sensor data. Follow these steps to set up your account:
- Create an Account:
- Visit Telemetry Harbor and sign up for a new account.
- Create a New Harbor:
- From your dashboard, click Create New Harbor
- Name it something descriptive like “ESP32_Environmental_Monitor“
- Select the harbor type as General and Specification Free
- Generate an API Key:
- Navigate to “View Details” for your created harbor
- Click “View API Key“
- Copy and save your API Key securely – you’ll need it for your ESP32 code
- Note your Harbor ID from the API Endpoint
Step 4: Uploading and Testing the Code
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <time.h> // For NTP time
// WiFi credentials
const char* ssid = "WIFI_SSID";
const char* password = "WIFI_PASSWORD";
// Telemetry Harbor API info
const String apiUrl = "https://telemetryharbor.com/api/v1/ingest/ingest/Harbor_ID";
const String apiKey = "API_KEY";
const String shipId = "Living Room";
const String cargoId = "Temperature";
// BME280 setup
Adafruit_BME280 bme; // I2C
#define SEALEVELPRESSURE_HPA (1013.25)
// LED setup
const int ledPin = 2; // Use built-in LED on most ESP32 boards (usually GPIO 2)
// NTP setup
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 0; // Adjust this for your timezone offset
const int daylightOffset_sec = 0;
// Time management
unsigned long previousMillis = 0;
const unsigned long intervalMillis = 60000; // 1-second interval
// Function to flash the LED
void flashLED(int times, int delayMs) {
for (int i = 0; i < times; i++) {
digitalWrite(ledPin, HIGH);
delay(delayMs);
digitalWrite(ledPin, LOW);
delay(delayMs);
}
}
// Function to connect to Wi-Fi
void connectWiFi() {
Serial.println("Connecting to WiFi...");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting...");
}
Serial.println("Connected to WiFi!");
flashLED(3, 200); // Flash LED 3 times quickly
}
// Function to initialize time using NTP
void initTime() {
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
Serial.println("Failed to obtain time from NTP server.");
while (1); // Stay here if time sync fails
}
Serial.println("Time synchronized successfully.");
}
void setup() {
Serial.begin(115200);
pinMode(ledPin, OUTPUT); // Set LED pin as output
// Initialize BME280 sensor
if (!bme.begin(0x76)) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
while (1);
}
// Connect to Wi-Fi and sync time
connectWiFi();
initTime();
}
void loop() {
// Check Wi-Fi connection
if (WiFi.status() != WL_CONNECTED) {
connectWiFi(); // Reconnect if disconnected
}
// Measure elapsed time to push data every second
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= intervalMillis) {
previousMillis = currentMillis;
const float temperatureOffset = 0; // Adjust temperature by -3°C
const float humidityOffset = 0; // Adjust humidity by +7.2%
// Read temperature and humidity
float temperature = bme.readTemperature() + temperatureOffset;
float humidity = bme.readHumidity() + humidityOffset;
// Get current time in ISO8601 format
time_t now = time(NULL);
struct tm timeinfo;
gmtime_r(&now, &timeinfo); // Convert to UTC time structure
char timeBuffer[30];
strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%dT%H:%M:%SZ", &timeinfo); // Format time
// Create JSON payload for temperature
String tempJsonPayload = "{";
tempJsonPayload += ""time": "" + String(timeBuffer) + "",";
tempJsonPayload += ""ship_id": "" + shipId + "",";
tempJsonPayload += ""cargo_id": "Temperature",";
tempJsonPayload += ""value": " + String(temperature, 2);
tempJsonPayload += "}";
// Create JSON payload for humidity
String humidityJsonPayload = "{";
humidityJsonPayload += ""time": "" + String(timeBuffer) + "",";
humidityJsonPayload += ""ship_id": "" + shipId + "",";
humidityJsonPayload += ""cargo_id": "Humidity",";
humidityJsonPayload += ""value": " + String(humidity, 2);
humidityJsonPayload += "}";
// Send temperature data to API
HTTPClient http;
http.begin(apiUrl);
http.addHeader("X-API-Key", apiKey);
http.addHeader("Content-Type", "application/json");
int tempResponseCode = http.POST(tempJsonPayload);
if (tempResponseCode > 0) {
Serial.println("Temperature data sent successfully! Response:");
Serial.println(http.getString());
flashLED(1, 300); // Flash LED for successful API request
} else {
Serial.print("Error sending temperature data. HTTP Response code: ");
Serial.println(tempResponseCode);
}
http.end();
// Send humidity data to API
http.begin(apiUrl); // Reinitialize HTTPClient for humidity request
http.addHeader("X-API-Key", apiKey);
http.addHeader("Content-Type", "application/json");
int humidityResponseCode = http.POST(humidityJsonPayload);
if (humidityResponseCode > 0) {
Serial.println("Humidity data sent successfully! Response:");
Serial.println(http.getString());
flashLED(1, 300); // Flash LED for successful API request
} else {
Serial.print("Error sending humidity data. HTTP Response code: ");
Serial.println(humidityResponseCode);
}
http.end();
}
}
- Select the Correct Board:
- Go to Tools > Board and select your ESP32 board model
- Select the correct COM port under Tools > Port
- Configure the Code:
- Replace the WiFi SSID and password with your network credentials
- Update the Telemetry Harbor API URL and API key with your account details
- Adjust the sensor reading interval if needed (default is 60 seconds)
- Upload the Code:
- Click the Upload button (→) in Arduino IDE
- Wait for the compilation and upload to complete
- Verify Operation:
- Open the Serial Monitor (Tools > Serial Monitor) set to 115200 baud
- Check for successful WiFi connection and sensor initialization
- Confirm data is being sent to Telemetry Harbor
Step 5: Visualizing Data with Telemetry Harbor
Once your ESP32 begins transmitting data, you can create powerful visualizations in Telemetry Harbor through its integrated Grafana dashboards:
- Access Grafana Credentials:
- Go back to the Harbor Details page
- Copy the Grafana Password shown on this page
- Click on the Grafana Endpoint link provided
- Login to Grafana:
- Use your Grafana Username (this will be the same as your Telemetry Harbor email)
- Enter the Grafana Password you copied earlier
- Navigate to Dashboards:
- In the left sidebar, click on Dashboards
- Select the Comprehensive Telemetry Dashboard (this is the demo dashboard provided by Telemetry Harbor)
- Configure Your Dashboard View:
- Choose your data source (which will be your harbor)
- Use the filters to view data based on
ship_id
andcargo_id
- Select appropriate time ranges to view your sensor history
Conclusion
Your ESP32 is now up and running with the BME280 sensor, streaming live temperature data to Telemetry Harbor. With this setup, you can keep an eye on environmental changes in real time. Want to take it further? Try adding more sensors, setting up alerts for temperature spikes, or even building a full monitoring system. The possibilities are endless.