If you want to know how to create a weather station with Arduino that actually yields reliable, lab-grade data, skip the ubiquitous DHT11 and DHT22 sensors. They suffer from severe humidity drift and slow thermal response times. The definitive upgrade for 2026 is the Bosch BME280, which measures temperature, humidity, and barometric pressure on a single I2C bus. Paired with the Arduino Nano 33 IoT, you get native WiFi for cloud logging and 3.3V logic that plays perfectly with modern sensor architectures without requiring logic level shifters.

The Sensor and Board Decision Path

Before buying parts, run your requirements through this decision matrix. The wrong sensor choice is the number one reason DIY weather stations end up in the junk drawer.

If your requirement is... Choose this Sensor Why / Trade-off
Basic indoor temp/humidity on a strict budget DHT22 / AM2302 Cheap, but uses single-bus protocol (timing sensitive) and drifts above 80% RH.
Accurate temp, humidity, AND barometric pressure Bosch BME280 (Default Pick) I2C/SPI, fast response, ±1 hPa pressure accuracy. Best all-rounder for weather.
All of the above PLUS indoor air quality (VOCs) Bosch BME688 Includes gas sensor, but requires complex BSEC library compilation and burns more current.
Extreme outdoor precision (meteorology grade) Sensirion SHT4x + BMP390 Overkill for 95% of makers; requires managing two separate I2C addresses and higher cost.

The Concrete Pick: For 90% of DIY weather stations, the BME280 is the definitive choice. For the microcontroller, the Arduino Nano 33 IoT beats the classic Uno R4 or Nano Every because it features an onboard NINA-W102 WiFi module and operates natively at 3.3V, eliminating the need for bidirectional logic level shifters when wiring to 3.3V I2C sensors.

Parts List & Hardware Specifications

Difficulty: 2/5 (Solderless I2C) | Time to Build: 45 minutes | Estimated Cost: $45 USD
Component Exact Variant / Part Number Notes & Pricing (2026)
Microcontroller Arduino Nano 33 IoT (ABX00027) Ensure it's the 33 IoT, not the 33 BLE. ~$22
Sensor Breakout Adafruit BME280 I2C/SPI (PID 2652) Includes onboard 3.3V LDO and 10k pull-ups. ~$20. (Generic clones are $4 but often lack pull-ups).
Power Supply 5V/2A USB-C Wall Adapter Standard phone charger works; ensure it can sustain WiFi TX bursts.
Wiring 22 AWG Solid Core Jumper Wires 4 wires needed for I2C + Power.
Enclosure (Outdoor) Stevenson Screen (Louvered) Mandatory for outdoor use to block solar radiation while allowing airflow.
Pro-Tip on Enclosures: If you mount a BME280 in a sealed waterproof box, the humidity will read 99% and temperature will bake in the sun. The World Meteorological Organization (WMO) mandates louvered Stevenson screens for accurate ambient readings. You can 3D print a Stevenson screen design from Thingiverse or buy an off-the-shelf solar radiation shield for ~$15.

Wiring Procedure and Pin Mapping

The BME280 communicates via I2C. The Arduino Nano 33 IoT maps its primary I2C bus to analog pins A4 (SDA) and A5 (SCL). Because the Nano 33 IoT is a strictly 3.3V logic board, we wire the sensor's VIN directly to the 3V3 pin.

BME280 Breakout Pin Arduino Nano 33 IoT Pin Function
VIN (or VCC) 3V3 Power (3.3V)
GND GND Common Ground
SCK (or SCL) A5 I2C Clock
SDI (or SDA) A4 I2C Data
  1. Prep the Breadboard: Insert the Nano 33 IoT into the breadboard, ensuring pins straddle the center trench.
  2. Establish Power Rails: Jumper the Nano's 3V3 pin to the red power rail, and GND to the blue ground rail.
  3. Wire the Sensor: Connect the BME280 breakout to the power rails (VIN to Red, GND to Blue).
  4. Route I2C Lines: Connect BME280 SCK to Nano A5, and BME280 SDI to Nano A4. Keep these wires under 12 inches to prevent I2C bus capacitance issues.
  5. Verify: Before plugging in USB, use a multimeter in continuity mode to ensure VCC and GND are not shorted.

Complete Compilable Code (Nano 33 IoT Target)

This sketch targets the Arduino Nano 33 IoT. It initializes the BME280, reads the environmental data, and attempts to log it via WiFi. If WiFi fails, it falls back to local Serial output so your sensor data isn't lost.

Required Libraries (Install via Arduino Library Manager): Adafruit BME280 Library, Adafruit Unified Sensor, and WiFiNINA.

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <WiFiNINA.h>

// --- PIN & CONFIG DEFINITIONS ---
#define SDA_PIN A4
#define SCL_PIN A5
#define SEALEVELPRESSURE_HPA (1013.25) // Adjust to your local elevation
#define BME_I2C_ADDRESS 0x77           // Adafruit is 0x77, generic clones often 0x76

// WiFi Credentials
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";

Adafruit_BME280 bme;
unsigned long lastReadTime = 0;
const long readInterval = 10000; // Read every 10 seconds

void setup() {
  Serial.begin(115200);
  while (!Serial && millis() < 5000); // Wait for serial monitor (max 5s)

  Serial.println("--- Arduino Nano 33 IoT Weather Station ---");

  // Initialize I2C with explicit pins for Nano 33 IoT
  Wire.begin(SDA_PIN, SCL_PIN);

  // Initialize BME280 with error handling
  if (!bme.begin(BME_I2C_ADDRESS, &Wire)) {
    Serial.println("ERROR: Could not find a valid BME280 sensor, check wiring, address, sensor ID!");
    Serial.println("Halting execution. Reset board to retry.");
    while (1) {
      delay(100); // Blink onboard LED or just hang to prevent bus spam
    }
  }
  
  Serial.println("BME280 initialized successfully.");
  
  // Configure sensor sampling (Weather monitoring preset)
  bme.setSampling(Adafruit_BME280::MODE_FORCED,
                  Adafruit_BME280::SAMPLING_X1, // Temp
                  Adafruit_BME280::SAMPLING_X1, // Pressure
                  Adafruit_BME280::SAMPLING_X1, // Humidity
                  Adafruit_BME280::FILTER_OFF);

  // Non-blocking WiFi Connection
  connectToWiFi();
}

void loop() {
  // Reconnect WiFi if dropped
  if (WiFi.status() != WL_CONNECTED) {
    connectToWiFi();
  }

  unsigned long currentTime = millis();
  if (currentTime - lastReadTime >= readInterval) {
    lastReadTime = currentTime;

    // Must call takeForcedMeasurement() in MODE_FORCED
    bme.takeForcedMeasurement(); 

    float tempC = bme.readTemperature();
    float hum = bme.readHumidity();
    float pres = bme.readPressure() / 100.0F;
    float alt = bme.readAltitude(SEALEVELPRESSURE_HPA);

    // Output to Serial
    Serial.printf("Temp: %.2f C | Hum: %.2f %% | Pres: %.2f hPa | Alt: %.2f m\n", tempC, hum, pres, alt);
    
    // TODO: Add MQTT or HTTP POST to your home automation server here
  }
}

void connectToWiFi() {
  if (WiFi.status() == WL_CONNECTED) return;
  
  Serial.print("Connecting to WiFi: ");
  Serial.println(ssid);
  
  WiFi.begin(ssid, password);
  int attempts = 0;
  while (WiFi.status() != WL_CONNECTED && attempts < 20) {
    delay(500);
    Serial.print(".");
    attempts++;
  }
  
  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("\nWiFi Connected.");
    Serial.print("IP Address: ");
    Serial.println(WiFi.localIP());
  } else {
    Serial.println("\nWiFi Connection Failed. Continuing with local Serial logging.");
  }
}

Debugging: "Could not find a valid BME280 sensor"

The most common point of failure in I2C sensor builds is the initialization handshake. If your serial monitor outputs the exact error string: Could not find a valid BME280 sensor, check wiring, address, sensor ID!, do not guess. Follow this ranked troubleshooting path.

Ranked Causes for I2C Init Failure

  1. Wrong I2C Address (80% of cases): The Adafruit BME280 uses address 0x77. Most cheap generic Amazon/AliExpress clones use 0x76. If using a clone, change #define BME_I2C_ADDRESS 0x77 to 0x76 in the code above.
  2. Missing Pull-Up Resistors (15% of cases): I2C requires pull-up resistors on SDA and SCL. The Adafruit board has 10k onboard pull-ups. Raw generic modules often omit them. If using a bare module, solder 4.7kΩ resistors between VCC and SDA, and VCC and SCL.
  3. Logic Level Frying (5% of cases): If you previously wired a 3.3V BME280 to a 5V Arduino Uno without a level shifter, you likely burned out the sensor's internal I2C transceiver. The Nano 33 IoT prevents this by running at 3.3V natively.

The First Three Things to Check When It Fails

  1. Run an I2C Scanner: Upload the default Arduino "I2C Scanner" example sketch. If it returns "No I2C devices found", your wiring is physically broken or swapped. If it returns an address (e.g., 0x76), update your code's address define.
  2. Measure VCC with a Multimeter: Put your meter probes on the VIN and GND pins of the breakout board while powered. You must read between 3.0V and 5.0V. If it reads 0V, your breadboard power rail is disconnected.
  3. Verify SDA/SCL Swap: It is incredibly easy to swap A4 and A5. Physically trace the wires from the sensor's SCK (Clock) to A5, and SDI (Data) to A4.

Extending or Simplifying the Build

Once you have the base station running on your workbench, you will inevitably want to change the scope of the project. Here is how to pivot without rewriting your entire architecture.

How to Simplify (Ultra-Low Power Remote Node)

If you want to run the weather station on a battery in a remote corner of your yard, drop the Nano 33 IoT. WiFi drains too much current for coin-cell or small LiPo setups.
The Pivot: Use an ATtiny85 on a Digispark board. Swap the WiFi library for TinyWireM (a lightweight I2C library for ATtinys). Put the ATtiny to deep sleep (sleep_mode()) between 15-minute readings. A single CR2032 coin cell will power this simplified BME280 node for over 6 months.

How to Extend (Long-Range LoRa Transmission)

If your WiFi router doesn't reach the backyard garden, LoRa (Long Range) RF is the solution.
The Pivot: Keep the Nano 33 IoT, but add a Dragino LoRa Bee (RFM95W 915MHz) shield. Wire it via SPI (pins 10-13 on the Nano). Use the RadioHead or LoRa.h library to transmit a compressed byte array of the temp/humidity/pressure floats to a base station receiver inside your house. This setup easily achieves 1km+ line-of-sight range through trees and walls, entirely bypassing your home WiFi network.

Building a weather station is an exercise in managing environmental variables. By locking in the BME280 for sensor accuracy and the Nano 33 IoT for native 3.3V WiFi connectivity, you eliminate the hardware gremlins that plague lesser configurations. Mount it in a louvered Stevenson screen, verify your I2C pull-ups, and let the data flow.