RainGlow: Make Your Umbrella Smarter With an LED Rain Alert

by taifur in Circuits > Wireless

1400 Views, 14 Favorites, 0 Comments

RainGlow: Make Your Umbrella Smarter With an LED Rain Alert

cover1.jpg
IMG_4087.JPG
RainGlow

Have you ever left home without an umbrella because the sky looked clear, only to get caught in an unexpected rain shower? RainGlow is a simple, battery-powered smart attachment that fits onto the handle of almost any umbrella and helps solve that problem.

Instead of checking the weather forecast every time you go outside, RainGlow provides a clear LED indication whenever there is a significant chance of rain. Before leaving home, simply look at your umbrella—if the LED lights up, it's a reminder that you may need it today.

The project is designed to be compact, low-power, and easy to build using affordable electronic components. It is also reusable, meaning you can attach it to your favorite umbrella without permanently modifying it. Whether you're a beginner looking for a fun electronics project or a maker interested in practical IoT gadgets, RainGlow is a great weekend build.

In this Instructable, I'll show you how to design, assemble, program, and test RainGlow step by step, so you can build your own smart umbrella companion and never be surprised by the weather again.

Supplies

Untitled.jpg
xiaoc6.jpg
4236-04.jpg
2p_magnetic_spring_loaded_pogo_pin_male_female_connector_socket_2.54_mm_pitch_dip_3_.jpg
Red-SMD-LED_6.jpg

The following hardware components were used in the project:

Hardware Components:

  1. Seeed Studio XIAO ESP32-C6 (buy from Seeed Studio): XIAO ESP32-C6 is a cost-effective MCU based on Espressif’s ESP32-C6. Designed with the thumb-size footprint and a single-sided mount of the XIAO Series, it's perfect for space-limited projects like an IoT umbrella.
  2. 1206 SMD LED Red (buy from AliExpress)
  3. 450mAh Li-ion Battery (buy from AliExpress)
  4. 2Pin DC Magnetic Pogo Pin Connector male and female (buy from AliExpress)
  5. Ribbon Cable Wire (buy from AliExpress)
  6. Clear Epoxy Resin (buy from AliExpress)

Tools:

  1. Adjustable Soldering Iron 60W (buy one from AliExpress)
  2. Electronic Diagonal Pliers/Side Cutting Nippers/Wire Cutter (buy one from AliExpress)
  3. Access to a 3D Printer (buy one from AliExpress)


Disclosure: A few of the product links provided are affiliate links. This means I may receive a small commission if you make a purchase through them, without affecting the price you pay. Your support helps fund future builds and documentation.

3D Design & Printing

3d-part-design.png
IMG_4002.JPG
IMG_4020.JPG
IMG_4016.JPG
IMG_4018.JPG

The first step in building RainGlow is designing a custom enclosure that can securely attach to an existing umbrella handle while protecting all of the electronics.

Since every umbrella has a different handle design, I wanted to create an attachment rather than modify the umbrella itself. The enclosure simply slides onto the top of the umbrella handle, making it easy to install or remove without damaging the umbrella.

The enclosure has been designed with several important features:

  1. Electronics compartment: A dedicated space to house the Seeed Studio XIAO ESP32-C6, battery, LED, switch, and other electronic components.
  2. Transparent LED window: A circular opening on the front of the enclosure where the status LED is mounted. After assembly, this opening will be filled with clear epoxy resin. The epoxy serves two purposes: it protects the LED from moisture and dust while also diffusing the light, making the rain alert visible from multiple angles.
  3. Umbrella handle slot: The top of the enclosure is left open so it can slide over the existing umbrella handle, allowing the attachment to fit securely without permanently altering the umbrella.
  4. Compact and lightweight design: Since the device will be carried every day, I kept the enclosure as small and lightweight as possible while still providing enough room for all the components.

I designed the enclosure using Tinkercad. Although it is a beginner-friendly CAD tool, it is powerful enough for creating simple functional enclosures like this one. The drag-and-drop interface makes it easy to adjust dimensions, add mounting features, and refine the design before printing.

After finalizing the model, I exported it as an STL file and printed it using a standard FDM 3D printer. You can easily modify the dimensions of the design in Tinkercad to fit your own umbrella handle or accommodate different electronic components.

In the next step, we'll prepare the electronic components and assemble the hardware inside the enclosure.

Circuit Connection & Testing

IMG_4013.jpg
IMG_4025.jpg
IMG_402.jpg
IMG_4026.jpg

One of the goals of RainGlow was to keep the electronics as simple as possible. Fewer components mean lower power consumption, improved reliability, and an easier assembly process.

Components Used

  1. Seeed Studio XIAO ESP32-C6
  2. 2 × LEDs (Rain Status Indicators)
  3. 2 × 100 Ω resistors
  4. 3.7 V Li-Po battery
  5. Mini sliding switch
  6. Magnetic pogo pin charging connector
  7. Hook-up wire

LED Connections

The rain indication is provided by just two LEDs, keeping the design simple while minimizing battery consumption.

  1. LED 1
  2. Anode → D9 through a 100 Ω resistor
  3. Cathode → GND
  4. LED 2
  5. Anode → D10 through a 100 Ω resistor
  6. Cathode → GND

Using separate GPIO pins allows different indication patterns to be programmed in the future if desired.

Battery Connection

The Seeed Studio XIAO ESP32-C6 includes a built-in Li-Po battery charging circuit, eliminating the need for an external charging module.

The Li-Po battery is connected to the battery pads on the underside of the XIAO board. A small sliding switch is placed in series with the positive battery lead so the entire device can be powered off when it is not in use.

Battery Positive → Sliding Switch → BAT+ pad on the XIAO

Battery Negative → BAT− pad on the XIAO

This simple arrangement allows the battery to remain connected while providing an easy way to completely disconnect power.

Charging Connector

To avoid opening the enclosure every time the battery needs charging, I added a magnetic pogo pin connector.

The connections are straightforward:

  1. Pogo Pin Positive → VBUS on the XIAO ESP32-C6
  2. Pogo Pin Ground → GND

This allows the battery to be recharged externally without removing the electronics from the enclosure. The magnetic connector is also convenient because it automatically aligns during charging and helps maintain the water-resistant design.

Since the XIAO already includes the charging circuitry, supplying 5 V through the VBUS pin is all that's required to recharge the battery safely.

Overall, the entire circuit consists of only a handful of components, making RainGlow compact, power-efficient, and easy to reproduce.

Creating the Glowing LED Ring

IMG_4038.jpg
IMG_4039.jpg
IMG_4040.jpg
IMG_4044.jpg
IMG_4061.png

To make RainGlow look more attractive while also improving the visibility of the rain indicator, I designed the enclosure with a circular groove around the top. Instead of having the LEDs visible from only one direction, this groove creates a glowing ring that can be seen from almost any angle.

The process is simple:

Prepare the Light Ring

  1. The 3D-printed enclosure includes a circular channel around its upper section. Small holes were also designed to hold the two LEDs securely in place.

Apply a Reflective Coating

  1. Before installing the LEDs, I painted the inside of the circular groove with white paint. The white surface reflects the LED light throughout the ring, producing a brighter and more uniform glow.

Install the LEDs

  1. Next, I inserted the two LEDs into their mounting holes, making sure they were aligned with the circular channel. This allows the light from both LEDs to spread evenly around the ring.

Fill with Epoxy Resin

  1. After confirming everything worked correctly, I filled the circular groove with clear epoxy resin. The epoxy has two important functions:
  2. It protects the LEDs from dust, moisture, and accidental damage.
  3. It acts as a light diffuser, transforming the individual LED points into a smooth, continuous glowing ring.

Attach the Enclosure

  1. Once the epoxy had fully cured, I mounted the completed 3D-printed enclosure onto the umbrella handle. The result is a clean, modern-looking rain indicator that blends into the umbrella while remaining easy to notice.

The finished glowing ring is bright enough to catch your attention before you leave home, yet subtle enough to complement the appearance of the umbrella. The diffused light also gives RainGlow a much more polished and professional look than exposing the LEDs directly.

Tip: Allow the epoxy resin to cure completely according to the manufacturer's instructions before handling or assembling the enclosure. This ensures maximum clarity, strength, and durability.

This glowing ring became one of my favorite parts of the project—it not only protects the electronics but also gives RainGlow a distinctive, premium appearance.

Programming & Testing

IMG_4067.JPG
IMG_4035.JPG
IMG_4034.JPG

The IoT Rain Alert Umbrella is designed to provide a visual indication of the probability of rainfall before the user leaves home. The system is built around the Seeed XIAO ESP32-C6, which periodically connects to the internet, retrieves the latest weather forecast from the Open-Meteo Weather API, and alerts the user using two LEDs mounted in the umbrella handle.

To maximize battery life, the ESP32-C6 connects to Wi-Fi only when downloading weather information. After the forecast is received, Wi-Fi is completely disabled and the LEDs continue blinking according to the predicted probability of rain for the next 15 minutes. At the end of the indication period, the microcontroller enters deep sleep for another 15 minutes before repeating the cycle.

System Architecture

Working Principle

The ESP32-C6 remains in deep sleep most of the time to minimize energy consumption. Every 15 minutes, the device wakes up automatically using the built-in RTC timer.

After waking up, it performs the following operations:

  1. Initialize the GPIO and PWM peripherals.
  2. Connect to the configured Wi-Fi network.
  3. Synchronize the internal clock using an NTP server.
  4. Download the latest weather forecast from the Open-Meteo API.
  5. Extract the precipitation probability for the current hour.
  6. Disconnect and completely disable the Wi-Fi module to reduce power consumption.
  7. Blink two LEDs according to the rain probability for the next 15 minutes.
  8. Turn the LEDs off.
  9. Enter deep sleep for another 15 minutes.

This cycle repeats continuously while consuming very little energy.

Rain Probability Decision Table

Power Management Strategy

To maximize battery life, several power-saving techniques are implemented:

  1. Wi-Fi Duty Cycling: The Wi-Fi radio is enabled only during weather data retrieval and is immediately turned off afterward.
  2. PWM LED Control: The LEDs are driven using PWM to reduce their average current consumption while maintaining sufficient visibility.
  3. CPU Frequency Scaling: After downloading the weather data, the CPU clock frequency is reduced since only LED blinking is required.
  4. Deep Sleep Mode: The ESP32-C6 enters deep sleep for 15 minutes after each notification cycle, reducing power consumption to only a few microamps.

These techniques significantly extend battery life while maintaining continuous weather monitoring.

Advantages of the Design

  1. Low power consumption through deep sleep and Wi-Fi duty cycling.
  2. No API key required by using the free Open-Meteo weather service.
  3. Simple and intuitive visual notification using two LEDs.
  4. Adaptive blink frequency provides an immediate indication of rainfall probability.
  5. Modular firmware design allows future integration of additional features such as battery monitoring, OTA firmware updates, or a companion mobile application.

This architecture provides a reliable, energy-efficient, and user-friendly solution for reminding users to carry an umbrella whenever the likelihood of rain is high.

The full Arduino Code:

/*
============================================================
IoT Rain Alert Umbrella
Hardware:
- Seeed XIAO ESP32-C6
- LED1 -> D9
- LED2 -> D10

Part 1
============================================================
*/

#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <time.h>
#include <esp_sleep.h>

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

//-----------------------------
// Location
//-----------------------------
const float LATITUDE = 23.8103;
const float LONGITUDE = 90.4125;

//-----------------------------
// LED Pins
//-----------------------------
const int LED1 = D9;
const int LED2 = D10;

//-----------------------------
// PWM Settings
//-----------------------------
const int PWM_FREQ = 5000;
const int PWM_RESOLUTION = 8;
const int LED_BRIGHTNESS = 60; // 0-255

//-----------------------------
// Time
//-----------------------------
const char* ntpServer = "pool.ntp.org";
const long gmtOffset = 6 * 3600;
const int daylightOffset = 0;

//-----------------------------
// Sleep
//-----------------------------
const uint64_t SLEEP_TIME =
15ULL * 60ULL * 1000000ULL;

//-----------------------------
// Variables
//-----------------------------
int rainProbability = 0;
int blinkRate = 0;

//------------------------------------------------------------

bool connectWiFi()
{
Serial.println("Connecting WiFi");

WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);

unsigned long start = millis();

while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");

if (millis() - start > 20000)
{
Serial.println("WiFi Timeout");
return false;
}
}

Serial.println();
Serial.println("Connected");
Serial.println(WiFi.localIP());

return true;
}

//------------------------------------------------------------

void disconnectWiFi()
{
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);

Serial.println("WiFi OFF");
}

//------------------------------------------------------------

bool syncTime()
{
configTime(gmtOffset,
daylightOffset,
ntpServer);

struct tm timeinfo;

for (int i = 0; i < 20; i++)
{
if (getLocalTime(&timeinfo))
{
Serial.println("Time Synced");
return true;
}

delay(500);
}

return false;
}

//------------------------------------------------------------

int currentHour()
{
struct tm timeinfo;

if (!getLocalTime(&timeinfo))
return -1;

return timeinfo.tm_hour;
}

//------------------------------------------------------------

String createURL()
{
String url =
"https://api.open-meteo.com/v1/forecast?latitude=";

url += String(LATITUDE, 4);

url += "&longitude=";

url += String(LONGITUDE, 4);

url += "&hourly=precipitation_probability";

url += "&forecast_days=1";

return url;
}

/*
============================================================
Weather Download
JSON Parsing
PWM LED Control
============================================================
*/

//------------------------------------------------------------
// Download weather JSON
//------------------------------------------------------------
String downloadWeather()
{
WiFiClientSecure client;
client.setInsecure(); // Skip certificate validation

HTTPClient https;

String payload = "";

String url = createURL();

Serial.println("Requesting weather...");
Serial.println(url);

if (https.begin(client, url))
{
int httpCode = https.GET();

if (httpCode == HTTP_CODE_OK)
{
payload = https.getString();

Serial.println("Weather downloaded.");
}
else
{
Serial.print("HTTP Error: ");
Serial.println(httpCode);
}

https.end();
}
else
{
Serial.println("HTTPS Connection Failed");
}

return payload;
}

//------------------------------------------------------------
// Parse Open-Meteo JSON
//------------------------------------------------------------
bool parseWeather(String json)
{
if (json.length() == 0)
return false;

DynamicJsonDocument doc(24576);

DeserializationError error =
deserializeJson(doc, json);

if (error)
{
Serial.print("JSON Error: ");
Serial.println(error.c_str());
return false;
}

int hour = currentHour();

if (hour < 0 || hour > 23)
return false;

JsonArray probability =
doc["hourly"]["precipitation_probability"];

rainProbability = probability[hour];

Serial.print("Current Hour: ");
Serial.println(hour);

Serial.print("Rain Probability: ");
Serial.print(rainProbability);
Serial.println("%");

return true;
}

//------------------------------------------------------------
// Convert rain probability into blink rate
//------------------------------------------------------------
int getBlinkRate(int probability)
{
if (probability >= 90)
return 5;

if (probability >= 80)
return 3;

if (probability >= 70)
return 2;

if (probability >= 60)
return 1;

return 0;
}

//------------------------------------------------------------
// Initialize PWM
//------------------------------------------------------------
void setupPWM()
{
ledcAttach(LED1, PWM_FREQ, PWM_RESOLUTION);
ledcAttach(LED2, PWM_FREQ, PWM_RESOLUTION);

ledcWrite(LED1, 0);
ledcWrite(LED2, 0);
}

//------------------------------------------------------------
// LED ON
//------------------------------------------------------------
void ledON()
{
ledcWrite(LED1, LED_BRIGHTNESS);
ledcWrite(LED2, LED_BRIGHTNESS);
}

//------------------------------------------------------------
// LED OFF
//------------------------------------------------------------
void ledOFF()
{
ledcWrite(LED1, 0);
ledcWrite(LED2, 0);
}

//------------------------------------------------------------
// Blink once
//------------------------------------------------------------
void blinkOnce(unsigned long onTime,
unsigned long offTime)
{
ledON();
delay(onTime);

ledOFF();
delay(offTime);
}

//------------------------------------------------------------
// Decide blink timing
//------------------------------------------------------------
void blinkPattern()
{
switch (blinkRate)
{
case 1:
blinkOnce(500, 500);
break;

case 2:
blinkOnce(250, 250);
break;

case 3:
blinkOnce(166, 167);
break;

case 5:
blinkOnce(100, 100);
break;

default:
delay(1000);
break;
}
}

/*
============================================================
Main Program
============================================================
*/

#include <esp32-hal-cpu.h>

//------------------------------------------------------------
// Blink LEDs for 15 minutes
//------------------------------------------------------------
void blinkFor15Minutes()
{
if (blinkRate == 0)
{
Serial.println("Rain probability below 60%");
Serial.println("LED OFF for 15 minutes");

ledOFF();
delay(15UL * 60UL * 1000UL);
return;
}

Serial.println("Start LED indication");

unsigned long startTime = millis();

while (millis() - startTime < (15UL * 60UL * 1000UL))
{
blinkPattern();
}

ledOFF();

Serial.println("15 minute indication finished");
}

//------------------------------------------------------------
// Enter Deep Sleep
//------------------------------------------------------------
void goToSleep()
{
Serial.println("Entering Deep Sleep...");

ledOFF();

esp_sleep_enable_timer_wakeup(SLEEP_TIME);

Serial.flush();

delay(100);

esp_deep_sleep_start();
}

//------------------------------------------------------------

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

delay(1000);

Serial.println();
Serial.println("==============================");
Serial.println(" IoT Rain Alert Umbrella");
Serial.println("==============================");

pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);

setupPWM();

//--------------------------------------------------------
// Connect WiFi
//--------------------------------------------------------

if (!connectWiFi())
{
Serial.println("Cannot connect WiFi");

blinkRate = 0;

disconnectWiFi();

blinkFor15Minutes();

goToSleep();
}

//--------------------------------------------------------
// Sync Time
//--------------------------------------------------------

if (!syncTime())
{
Serial.println("Time Sync Failed");

blinkRate = 0;

disconnectWiFi();

blinkFor15Minutes();

goToSleep();
}

//--------------------------------------------------------
// Download Weather
//--------------------------------------------------------

String weather = downloadWeather();

if (!parseWeather(weather))
{
Serial.println("Weather Parsing Failed");

blinkRate = 0;
}
else
{
blinkRate = getBlinkRate(rainProbability);

Serial.print("Blink Rate = ");
Serial.print(blinkRate);
Serial.println(" Hz");
}

//--------------------------------------------------------
// Turn WiFi completely OFF
//--------------------------------------------------------

disconnectWiFi();

//--------------------------------------------------------
// Reduce CPU Frequency
//--------------------------------------------------------

setCpuFrequencyMhz(80);

Serial.println("CPU Frequency = 80 MHz");

//--------------------------------------------------------
// Blink LEDs for 15 minutes
//--------------------------------------------------------

blinkFor15Minutes();

//--------------------------------------------------------
// Deep Sleep
//--------------------------------------------------------

goToSleep();
}

//------------------------------------------------------------

void loop()
{
// Never used

delay(1000);
}


Downloads

Adding a Waterproof Magnetic Charging Port

IMG_4016.jpg
IMG_4026.jpg
IMG_4030.JPG
IMG_4032.JPG

Since RainGlow is designed to be attached to an umbrella, protecting the electronics from water was one of my top priorities. At the same time, I wanted charging the battery to be quick and convenient without having to open the enclosure.

To achieve this, I used a 2-pin magnetic pogo pin connector as the charging interface.

Why a Magnetic Pogo Pin?

A magnetic pogo pin connector offers several advantages over a traditional USB connector:

  1. No need to open the enclosure for charging.
  2. Better protection against rain and moisture.
  3. Magnetic alignment makes charging quick and easy.
  4. Fewer exposed openings help maintain the clean appearance of the enclosure.

Preparing the Enclosure

During the CAD design stage, I created a rectangular opening on the bottom of the enclosure that matches the dimensions of the magnetic pogo pin connector.

After 3D printing, the connector fit neatly into this slot with its contact pins exposed on the outside, making them easily accessible for the magnetic charging cable.

Installing the Connector

Once I confirmed the connector fit properly, I inserted it into the opening from the outside of the enclosure.

To secure it permanently, I applied hot glue from the inside of the enclosure. This keeps the connector firmly in place while preventing it from moving or being pushed inward when the charging cable is attached or removed.

The electrical connections are simple:

  1. Positive pin → VBUS on the Seeed Studio XIAO ESP32-C6
  2. Ground pin → GND

Since the XIAO ESP32-C6 includes a built-in Li-Po charging circuit, connecting the pogo pins to VBUS and GND allows the battery to be recharged safely without any additional charging hardware.

The result is a compact, reliable, and water-resistant charging solution that complements the overall design of RainGlow. Simply snap the magnetic charging cable onto the pogo pins whenever the battery needs recharging—no disassembly required.

Tip: Before permanently gluing the connector, test it with the magnetic charging cable to ensure the polarity is correct. Reversing the positive and ground connections may prevent charging or damage the circuit.

This simple addition makes the project much more practical for everyday use while helping to keep the enclosure protected from the elements.

Battery Lifetime Calculation

para.jpg
nor.jpg
rn90.jpg
estb.jpg

Below is a practical battery life estimation for your RainGlow project. Since the exact current depends on Wi-Fi signal strength and firmware, I've used realistic values for the Seeed Studio XIAO ESP32-C6.

Base System Consumption (No Rain)






Final Assembly

IMG_4063.JPG
IMG_4064.JPG
IMG_4066.JPG
IMG_4088.JPG

With all of the individual parts prepared, it was time to assemble everything inside the 3D-printed enclosure.

Preparing the LED Assembly

I first soldered a 100 Ω resistor in series with each LED to limit the current and protect both the LEDs and the ESP32-C6 GPIO pins. After soldering, I covered all exposed connections with heat-shrink tubing to provide electrical insulation and prevent accidental short circuits inside the enclosure.

Next, I soldered the LED wires to the D9 and D10 pins of the Seeed Studio XIAO ESP32-C6, along with the common ground connection.

Installing the Electronics

Since the enclosure is very compact, I chose not to use a PCB. Instead, all of the components were connected using direct wiring, which saved space and simplified the build.

I carefully arranged the components inside the enclosure in the following order:

  1. Seeed Studio XIAO ESP32-C6
  2. Li-Po battery
  3. Sliding power switch
  4. LED wiring
  5. Magnetic pogo pin charging connector

Before closing the enclosure, I powered the circuit to verify that both LEDs, the switch, and the charging connector were functioning correctly.

Securing the Components

Without a PCB, the components could move around inside the enclosure during everyday use. To prevent this, I placed small pieces of cotton between the components. The cotton acts as a soft spacer, keeping each part securely in place while also preventing the battery, wires, and circuit board from rubbing against each other.

This simple solution helps absorb small vibrations and keeps the electronics organized inside the enclosure without adding extra weight or requiring custom mounting brackets.

After arranging all of the components, I carefully tucked the wires into the remaining space and closed the enclosure. The result is a compact, lightweight, and neatly assembled smart umbrella attachment.

Tip: Before sealing the enclosure, double-check all solder joints and test the circuit one final time. Fixing a wiring issue is much easier before the enclosure is fully assembled.

The final assembly is surprisingly compact, proving that a practical IoT device can be built with only a few components and thoughtful internal layout.