Build a Raspberry Pi Home Weather Station: Sensors, InfluxDB, Grafana, and Alerts
Project Overview
This project builds a comprehensive home weather and environmental monitoring station using a Raspberry Pi, multiple sensors, and a professional-grade data visualization stack. The system collects temperature, humidity, barometric pressure, air quality (PM2.5/PM10), and ambient light data, stores it in a time-series database, displays it on real-time dashboards, and sends alerts when conditions exceed thresholds.
Required Hardware
ComponentPricePurpose Raspberry Pi 4B (4GB)$55Main server and sensor hub SanDisk Max Endurance 64GB SD$15Reliable storage for 24/7 operation BME280 sensor (I2C)$5Temperature, humidity, pressure PMS5003 air quality sensor (UART)$15PM1.0, PM2.5, PM10 particulate matter BH1750 light sensor (I2C)$3Ambient light level (lux) ADS1115 ADC (I2C)$44-channel analog input expansion Dupont jumper wires$3Sensor connections Breadboard or perfboard$3Wiring Project case$8Enclosure Total$111Software Stack
ComponentPurpose InfluxDB 2Time-series database for sensor data GrafanaDashboards and visualization PythonSensor reading and data processing DockerContainer deployment Telegram Bot APIAlert notificationsSensor Wiring
BME280 (I2C) - Indoor Climate
BME280Pi GPIOPin # VCC3.3VPin 1 GNDGNDPin 6 SCLGPIO 3 (SCL)Pin 5 SDAGPIO 2 (SDA)Pin 3PMS5003 (UART) - Air Quality
PMS5003Pi GPIOPin # VCC5VPin 2 GNDGNDPin 6 TXDGPIO 15 (RXD)Pin 10 RXDGPIO 14 (TXD)Pin 8 SET3.3VPin 1 RSTGPIO 23Pin 16BH1750 (I2C) - Ambient Light
BH1750Pi GPIOPin # VCC3.3VPin 1 GNDGNDPin 9 SCLGPIO 3 (SCL)Pin 5 (shared) SDAGPIO 2 (SDA)Pin 3 (shared) ADDRGNDSets address to 0x23Step 1: Raspberry Pi Setup
- Install Raspberry Pi OS Lite (64-bit) using Raspberry Pi Imager
- Enable SSH, set username/password during imaging
- Boot and update: sudo apt update && sudo apt full-upgrade -y
- Install dependencies: sudo apt install python3-pip i2c-tools git -y
- Enable I2C and UART: sudo raspi-config (Interface Options)
- Install Docker: curl -fsSL https://get.docker.com | sh
- Add user to docker group: sudo usermod -aG docker $USER
Step 2: Verify I2C Sensors
sudo i2cdetect -y 1You should see addresses: 0x23 (BH1750), 0x48 (ADS1115), 0x76 or 0x77 (BME280).
Step 3: Python Sensor Reader
#!/usr/bin/env python3 import serial, time, board, busio import adafruit_bme280, adafruit_bh1750 from influxdb_client import InfluxDBClient, Point from influxdb_client.client.write_api import SYNCHRONOUS INFLUX_URL = "http://localhost:8086" INFLUX_TOKEN = "your-token-here" INFLUX_ORG = "home" INFLUX_BUCKET = "weather" i2c = busio.I2C(board.SCL, board.SDA) bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c, address=0x76) bh1750 = adafruit_bh1750.BH1750(i2c, address=0x23) pm_sensor = serial.Serial('/dev/ttyAMA0', baudrate=9600, timeout=2) def read_pms5003(): pm_sensor.reset_input_buffer() while True: data = pm_sensor.read(32) if len(data) >= 32 and data[0] == 0x42 and data[1] == 0x4d: pm10 = (data[10] << 8) + data[11] pm25 = (data[12] << 8) + data[13] pm100 = (data[14] << 8) + data[15] return {'pm1_0': pm10, 'pm2_5': pm25, 'pm10': pm100} time.sleep(0.1) client = InfluxDBClient(url=INFLUX_URL, token=INFLUX_TOKEN, org=INFLUX_ORG) write_api = client.write_api(write_options=SYNCHRONOUS) while True: try: temperature = bme280.temperature humidity = bme280.relative_humidity pressure = bme280.pressure light = bh1750.lux pm_data = read_pms5003() point = Point("environment").tag("location", "indoor") .field("temperature", round(temperature, 2)) .field("humidity", round(humidity, 2)) .field("pressure", round(pressure, 2)) .field("light", round(light, 2)) .field("pm1_0", pm_data['pm1_0']) .field("pm2_5", pm_data['pm2_5']) .field("pm10", pm_data['pm10']) write_api.write(bucket=INFLUX_BUCKET, record=point) print(f"Written: {temperature:.1f}C, {humidity:.1f}%, {pressure:.1f}hPa, PM2.5={pm_data['pm2_5']}") except Exception as e: print(f"Error: {e}") time.sleep(60)Step 4: Deploy with Docker Compose
version: '3.8' services: influxdb: image: influxdb:2.7 container_name: influxdb ports: - "8086:8086" volumes: - ./data/influxdb:/var/lib/influxdb2 environment: - DOCKER_INFLUXDB_INIT_MODE=setup - DOCKER_INFLUXDB_INIT_USERNAME=admin - DOCKER_INFLUXDB_INIT_PASSWORD=your-secure-password - DOCKER_INFLUXDB_INIT_ORG=home - DOCKER_INFLUXDB_INIT_BUCKET=weather - DOCKER_INFLUXDB_INIT_RETENTION=365d restart: unless-stopped grafana: image: grafana/grafana:latest container_name: grafana ports: - "3000:3000" volumes: - ./data/grafana:/var/lib/grafana environment: - GF_SECURITY_ADMIN_PASSWORD=your-grafana-password depends_on: - influxdb restart: unless-stoppedStep 5: Configure Grafana Dashboard
- Open http://your-pi-ip:3000, login with admin password
- Add Data Source: InfluxDB, Query Language: Flux
- URL: http://influxdb:8086, Org: home, Token: (from InfluxDB), Bucket: weather
- Create dashboard panels with Flux queries
Example Flux Queries
// Temperature and Humidity from(bucket: "weather") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == "environment") |> filter(fn: (r) => r._field == "temperature" or r._field == "humidity") |> aggregateWindow(every: v.windowPeriod, fn: mean) // Air Quality PM2.5 from(bucket: "weather") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._field == "pm2_5") |> aggregateWindow(every: v.windowPeriod, fn: mean)Step 6: Telegram Alerts
- Message @BotFather on Telegram, create bot, save API token
- Get chat_id from https://api.telegram.org/botYOUR_TOKEN/getUpdates
- Create Python alert script that queries InfluxDB and sends messages
- Schedule with cron: */5 * * * * /usr/bin/python3 /home/pi/weather-station/alerts.py
Step 7: Auto-Start Service
sudo systemctl enable weather-station.service sudo systemctl start weather-station.serviceRemote Access Options
MethodSetupSecurity Tailscalecurl -fsSL https://tailscale.com/install.sh | shExcellent (WireGuard mesh) Cloudflare TunnelInstall cloudflaredExcellent (no open ports) Local onlyhttp://raspberrypi.local:3000Home network onlyExpanding the System
SensorInterfacePurpose SCD40I2CTrue CO2 sensing (accurate NDIR) MQ-135Analog via ADS1115General air quality (CO2, NH3, benzene) DS18B201-WireFridge/freezer/pool temperature SHT40I2CPremium temp/humidity accuracy AnemometerPulse inputWind speedTroubleshooting
ProblemSolution I2C devices not detectedCheck wiring; verify I2C enabled with raspi-config; run i2cdetect PMS5003 not readingVerify serial not used by console; check /dev/ttyAMA0; check permissions InfluxDB connection refusedVerify container running with docker ps; wait for init to complete Grafana shows no dataCheck token, bucket name, time range; verify sensor script writing data Sensor readings erraticAdd 100nF cap across sensor power pins; check 3.3V rail stabilityLoading_