logzly. CraftCode Academy

How to Build a Personal Weather Station with Node.js and ESP32

Read this article in clean Markdown format for LLMs and AI context.

Ever looked out the window, wondered why the temperature jumps just before you step outside, and thought “I could measure that myself”? A tiny weather station lets you capture real‑time data right from your balcony, and you get to play with code while you’re at it. In this guide I’ll walk you through the whole process – from wiring the sensors to serving the data on a Node.js dashboard – using only tools most makers already have at home.

What You’ll Need

Hardware

  • ESP32 development board – the little Wi‑Fi brain that talks to your sensors.
  • DHT22 (or DHT11) temperature & humidity sensor – cheap, reliable, and perfect for hobby projects.
  • BMP280 barometric pressure sensor – gives you pressure and altitude.
  • Breadboard and jumper wires – for a quick, solder‑free prototype.
  • USB‑C cable – to power and flash the ESP32.

Software

  • Node.js (v14 or later) – runs the server that collects and displays data.
  • Arduino IDE or VS Code with PlatformIO – to compile and upload code to the ESP32.
  • npm packages: express, socket.io, johnny-five (optional for extra hardware control).

All of these are free, and you can find the hardware on any electronics store or online marketplace.

Step 1 – Set Up the ESP32 Development Environment

  1. Install the Arduino IDE if you haven’t already. Open File → Preferences and add the ESP32 board URL: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json.
  2. Go to Tools → Board → ESP32 Arduino and select “ESP32 Dev Module”.
  3. Plug the board into your computer, choose the correct COM port, and hit the Upload button on a simple “blink” sketch to confirm everything works.

That first blink is like the first sip of coffee – it tells you the system is alive.

Step 2 – Wire the Sensors

Sensor ESP32 Pin Connection
DHT22 4 (GPIO4) VCC → 3.3 V, GND → GND, DATA → GPIO4 (add a 10 kΩ pull‑up to 3.3 V)
BMP280 21 (SDA) / 22 (SCL) VCC → 3.3 V, GND → GND, SDA → GPIO21, SCL → GPIO22

Use a breadboard so you can rearrange things later. Double‑check that the power pins go to 3.3 V, not 5 V – the ESP32 can be picky.

Step 3 – Write the ESP32 Firmware

Create a new sketch called weather_station.ino. Below is a minimal version that reads the sensors and sends JSON over Wi‑Fi to a Node.js server.

#include <WiFi.h>
#include <ArduinoJson.h>
#include <DHT.h>
#include <Wire.h>
#include <Adafruit_BMP280.h>

#define DHTPIN 4
#define DHTTYPE DHT22

const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";
const char* host = "YOUR_PC_IP";   // e.g. 192.168.1.50
const uint16_t port = 3000;        // same as Node server

DHT dht(DHTPIN, DHTTYPE);
Adafruit_BMP280 bmp; // I2C

void setup() {
  Serial.begin(115200);
  dht.begin();
  bmp.begin();

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWiFi connected");
}

void loop() {
  float temp = dht.readTemperature();
  float hum  = dht.readHumidity();
  float pres = bmp.readPressure() / 100.0; // hPa

  if (isnan(temp) || isnan(hum) || isnan(pres)) {
    Serial.println("Failed to read sensors");
    delay(2000);
    return;
  }

  // Build JSON payload
  StaticJsonDocument<200> doc;
  doc["temperature"] = temp;
  doc["humidity"]    = hum;
  doc["pressure"]    = pres;
  char buffer[200];
  serializeJson(doc, buffer);

  // Send to server
  WiFiClient client;
  if (!client.connect(host, port)) {
    Serial.println("Connection failed");
    delay(2000);
    return;
  }
  client.println("POST /data HTTP/1.1");
  client.println("Content-Type: application/json");
  client.print("Content-Length: ");
  client.println(strlen(buffer));
  client.println();
  client.println(buffer);
  client.stop();

  Serial.println("Data sent");
  delay(60000); // send once a minute
}

Replace YOUR_SSID, YOUR_PASSWORD, and YOUR_PC_IP with your own values. The code uses the ArduinoJson library to keep the payload tiny – a good habit when you’re on a low‑power device.

Step 4 – Spin Up the Node.js Server

Create a folder called weather-server and run npm init -y. Then install the needed packages:

npm install express socket.io body-parser

The Node.js server will handle incoming sensor data and broadcast it via websockets.

Now create server.js:

const express = require('express');
const bodyParser = require('body-parser');
const http = require('http');
const socketIo = require('socket.io');

const app = express();
app.use(bodyParser.json());

const server = http.createServer(app);
const io = socketIo(server);

let latestData = {};

app.post('/data', (req, res) => {
  latestData = req.body;
  io.emit('update', latestData);
  res.sendStatus(200);
});

app.get('/', (req, res) => {
  res.sendFile(__dirname + '/index.html');
});

io.on('connection', socket => {
  socket.emit('update', latestData);
});

const PORT = 3000;
server.listen(PORT, () => console.log(`Server listening on ${PORT}`));

Create a simple index.html in the same folder:

<!DOCTYPE html>
<html>
<head>
  <title>My Weather Station</title>
  <style>
    body { font-family: Arial, sans-serif; margin: 2rem; }
    .card { border: 1px solid #ccc; padding: 1rem; margin-bottom: 1rem; }
  </style>
</head>
<body>
  <h1>Live Weather Data</h1>
  <div id="temp" class="card"></div>
  <div id="hum" class="card"></div>
  <div id="pres" class="card"></div>

  <script src="/socket.io/socket.io.js"></script>
  <script>
    const socket = io();
    function render(data) {
      document.getElementById('temp').innerText = `Temperature: ${data.temperature.toFixed(1)} °C`;
      document.getElementById('hum').innerText = `Humidity: ${data.humidity.toFixed(1)} %`;
      document.getElementById('pres').innerText = `Pressure: ${data.pressure.toFixed(1)} hPa`;
    }
    socket.on('update', render);
  </script>
</body>
</html>

Run the server with node server.js. Open a browser at http://localhost:3000 and you should see live numbers appear as the ESP32 pushes data every minute.

Step 5 – Make It Your Own

Now that the basics work, you can add a few personal touches:

  • Data logging – write each JSON payload to a file or a tiny SQLite DB for later analysis.
  • Graphing – use a library like Chart.js on the front‑end for interactive visualizations with p5.js to plot temperature over the day.
  • Alerts – have the ESP32 send an email or a push notification when humidity spikes.
  • Power – move the board to a solar‑charged LiPo pack for a truly off‑grid station.

I started with the bare‑bones version, then added a simple line chart. Watching the sunrise temperature climb on a graph felt oddly satisfying – like seeing your code breathe.

Troubleshooting Tips

Symptom Likely Cause Fix
No Wi‑Fi connection Wrong SSID/password or ESP32 in deep sleep Double‑check credentials; add a short delay before Wi‑Fi.begin
JSON payload empty Sensor wiring error or missing pull‑up resistor Verify connections; add 10 kΩ resistor on DHT data line
Browser shows “404” Server not serving index.html from correct path Ensure index.html sits next to server.js and you run node server.js from that folder

A quick serial print of the sensor values before sending can save you a lot of head‑scratching.

Wrap‑Up Thoughts

Building a personal weather station is a perfect blend of hardware tinkering and web development. You get to see raw sensor data, push it through a network, and turn it into a live dashboard – all with tools that are free and widely supported. The best part? Once you have the pipeline working, you can reuse it for any other IoT project – soil moisture, light levels, you name it.

So grab an ESP32, solder a couple of pins, and let the weather come to you. The sky’s not the limit; it’s just the first data point.

Reactions
Do you have any feedback or ideas on how we can improve this page?