Project

Solar-Powered Weather Station That Runs Forever

The dream of an outdoor sensor is install-once-and-forget. No batteries to swap, no cables to run. The maths actually works out: a 1 W solar panel charges a small LiPo enough during the day to power an ESP32 sensor through the night, every day, indefinitely. The trick is being honest about your power budget.

This article builds a self-powered weather station: temperature, humidity, pressure, wind speed, and rainfall, transmitted hourly over LoRa to a gateway. Total power consumption: under 1 mAh per hour. A 3500 mAh LiPo plus a 1 W solar panel keeps it going through cloudy weeks.

What we are measuring

  • Temperature, humidity, pressure — BME280 over I2C. Standard.
  • Wind speed — cup anemometer with a magnetic reed switch (clicks once per revolution). Frequency = wind speed.
  • Wind direction — vane with a potentiometer or set of reed switches at compass points.
  • Rainfall — tipping-bucket gauge: each tip = 0.2794 mm of rain (Davis and similar units; check your gauge).

For commercial-grade hardware, the SparkFun Weather Meter Kit ($75) gives all three of anemometer, vane, and rain gauge in one package, ready to wire to GPIO.

Bill of materials

  • ESP32 DevKit (or LoRa-equipped board for direct LoRa transmission) — $10–20
  • BME280 breakout — $5
  • SparkFun Weather Meter Kit (anemometer, vane, rain gauge) — $75
  • 3500 mAh LiPo cell — $12
  • 1 W 6V solar panel — $10
  • CN3791 MPPT solar charge controller (designed for LiPo from solar) — $5
  • IP65 enclosure — $15
  • UV-resistant cable, gland, mounting bracket — $10

About $140 total. The weather meters are the bulk of the cost; the electronics and power are a fraction. For a budget version with just temperature, humidity, and pressure, drop the weather meters and you are down to $50.

Power architecture

flowchart LR Sun[Sun] -->|light| Panel[1W 6V solar panel] Panel --> MPPT[CN3791
MPPT charge controller] MPPT -->|charging current| Bat[3500 mAh LiPo] Bat -->|3.7-4.2V| LDO[3.3V LDO regulator] LDO --> MCU[[ESP32]] MCU -->|GPIO| Sensors[BME280
anemometer
rain gauge] MCU -->|SPI| LoRa[LoRa radio] LoRa -->|RF| Gateway[Gateway
indoor]

Power flow: sun -> panel -> MPPT charger -> battery -> regulator -> MCU. The MPPT controller's job is to extract maximum power from the panel under any light condition; this matters because the panel's optimal load varies with sunlight intensity.

Power budget

The math that makes solar viable:

Daily energy budget                                      Calculation
----------------------------------------------------     ---------------
Deep sleep (23.5 hours/day @ 12 uA)                      0.28 mAh
Boot + sensor read + LoRa transmit (24 cycles)
  Active 5 sec/cycle @ 80 mA                             2.67 mAh
----------------------------------------------------     ---------------
Total daily consumption                                  ~3 mAh/day

Solar input (cloudy day, 1W panel @ 50% efficiency)
  3 hours effective sun @ 100 mA charge current          300 mAh/day

Margin: 100x

The margin is enormous. Even seven consecutive overcast days deliver more energy than the device consumes. The 3500 mAh battery is sized for several weeks of zero solar (deep winter at high latitudes).

Counting tips and pulses

Anemometer and rain gauge both produce pulses. The ESP32 should count them while sleeping or in the background.

For deep-sleep counting, use the ULP (Ultra Low Power) co-processor on the ESP32 classic or S3. It runs while the main CPU is in deep sleep and increments a counter on each GPIO transition.

For simpler counting (when the device wakes briefly), attach a hardware interrupt that increments a counter, debounce in software:

volatile uint32_t wind_pulses = 0;
volatile uint32_t rain_pulses = 0;
volatile unsigned long last_wind = 0;
volatile unsigned long last_rain = 0;
const unsigned long DEBOUNCE_MS = 10;  // mechanical reed switch bounce

void IRAM_ATTR onWindTick() {
    unsigned long now = millis();
    if (now - last_wind > DEBOUNCE_MS) {
        wind_pulses++;
        last_wind = now;
    }
}

void IRAM_ATTR onRainTick() {
    unsigned long now = millis();
    if (now - last_rain > DEBOUNCE_MS) {
        rain_pulses++;
        last_rain = now;
    }
}

void setup() {
    pinMode(WIND_PIN, INPUT_PULLUP);
    pinMode(RAIN_PIN, INPUT_PULLUP);
    attachInterrupt(WIND_PIN, onWindTick, FALLING);
    attachInterrupt(RAIN_PIN, onRainTick, FALLING);
}

Computing wind and rain values

The complete sketch — full setup, error handling, retries, deep-sleep wake, and configuration — is in the project package. Request it free using the form at the bottom of this article. We email it back within 24 hours.

The transmit cycle

Once an hour: wake from deep sleep, take a BME280 reading, accumulate the wind/rain counters, transmit a LoRa packet to the gateway, sleep again.

RTC_DATA_ATTR uint32_t cumulative_rain = 0;  // survives deep sleep
RTC_DATA_ATTR uint32_t cumulative_wind = 0;
RTC_DATA_ATTR unsigned long last_xmit_ms = 0;

void loop() {
    // ... read BME280 ...
    // ... read wind direction ADC ...

    unsigned long now = millis();
    unsigned long elapsed = now - last_xmit_ms;
    float wind_kmh = windKmh(cumulative_wind, elapsed);
    float rain_mm  = rainMm(cumulative_rain);

    transmitLoRa(temp, humidity, pressure, wind_kmh, wind_dir, rain_mm);

    cumulative_wind = 0;
    cumulative_rain = 0;
    last_xmit_ms = now;

    esp_sleep_enable_timer_wakeup(60ULL * 60 * 1000000);  // 1 hour
    esp_deep_sleep_start();
}

Mounting and weatherproofing

  • Solar panel angle. Tilt to roughly your latitude minus 10°, facing equator (south in northern hemisphere, north in southern). Adjust seasonally if you care; for fixed installations, latitude minus 10° is a good year-round compromise.
  • Sensor placement. BME280 in a Stevenson screen (radiation-shielded ventilated housing) for accurate temperature. Mounting in direct sun gives readings 10°C+ above actual.
  • Anemometer. Mount in clear air, away from trees and buildings. Even a small obstruction creates eddies that throw off readings.
  • Rain gauge. Level it. A few degrees off level changes calibration.
  • Cable entries. Use cable glands; otherwise water tracks down the cable into the enclosure.
  • Battery in the cold. LiPos lose 30–50% capacity below freezing. For climates with hard winters, oversize the battery and consider an insulated enclosure.

Frequently Asked Questions

Will this work in winter?

Yes, with caveats. Daylight is shorter; sun angle is lower; clouds are more common. Winter solar yield is typically 20–30% of summer. The 100× design margin is what makes the project robust in winter; with a 10× margin you would have problems.

Why LoRa instead of WiFi?WiFi alone consumes 5–10× more energy per transmission than LoRa, requires a router within range (often the limiting factor for outdoor installations), and the antenna pattern is poor at distance. For an outdoor sensor reporting once an hour, LoRa is the right radio.

Can I extend this with PM2.5 or UV sensing?Yes. PMS5003 for particulate matter; ML8511 or VEML6075 for UV. Both are I2C or simple analog and add modest power requirements. The PMS5003 is power-hungry when running but only needs to run for 30 seconds per reading.

How do I calibrate the anemometer?Either trust the manufacturer's spec, or compare against a reference (a handheld anemometer like the Kestrel 1000) on a windy day at multiple speeds. Adjust the multiplier in code.

What is the lifetime of the LiPo battery?About 500–1000 charge cycles before significant capacity loss. With one charge cycle per day, that is 1.5–3 years before you should swap. LiFePO4 cells (different chemistry) last 5–10× longer in this application but are more expensive.

Project package

Get the complete project package

The article above shows the core firmware and the principles behind it. The complete project package — assembled, tested, and ready to flash — is available by email request. We send it manually, and we read every request.

  • Complete Arduino sketch (.ino) with full error handling
  • List of required libraries with version numbers
  • Printable wiring diagram (PDF)
  • Bill of materials with current part numbers
  • Build guide and troubleshooting tips
  • Configuration template (WiFi, MQTT, etc.)

We send the package by email within 24 hours, usually faster. Free, no spam, no mailing list. Your email is used once, for this reply.

Share your thoughts

Worked with this in production and have a story to share, or disagree with a tradeoff? Email us at support@mybytenest.com — we read everything.