Parts list
| Part | Qty | Approx. | Buy |
|---|---|---|---|
| ESP32-C3 SuperMini | 1 | ₹230 | Buy ESP32-C3 SuperMini |
| BH1750 light sensor module (GY-302) | 1 | ₹120 | Buy BH1750 light sensor module (GY-302) |
| 0.96" SSD1306 I2C OLED (4-pin) | 1 | ₹220 | Buy 0.96" SSD1306 I2C OLED (4-pin) |
| Clear food container with lid (weather cover) | 1 | ₹60 | Buy Clear food container with lid (weather cover) |
| 4-core cable, 1 m (sensor on the roof edge) | 1 | ₹40 | Buy 4-core cable, 1 m (sensor on the roof edge) |
| USB power bank (10,000 mAh) or 5 V adapter | 1 | ₹650 | Buy USB power bank (10,000 mAh) or 5 V adapter |
| Total | ₹1,320 |
Affiliate disclosure: “Buy” links may earn FlashReady a small commission at no extra cost to you. Prices are indicative.
Wiring
| ESP32-C3 SuperMini | Part · pin | Wire | Notes |
|---|---|---|---|
| 7 | BH1750 lux sensor (GY-302) · SCL | yellow | GPIO7 |
| 6 | BH1750 lux sensor (GY-302) · SDA | blue | GPIO6 — keeps I2C off strapping pins 8 and 9 |
| 3V3 | BH1750 lux sensor (GY-302) · VCC | red | |
| GND | BH1750 lux sensor (GY-302) · GND | black | |
| GND | BH1750 lux sensor (GY-302) · ADDR | black | ADDR low = address 0x23 |
| 7 | SSD1306 OLED 0.96″ (128×64, I2C) · SCL | yellow | Same I2C bus |
| 6 | SSD1306 OLED 0.96″ (128×64, I2C) · SDA | blue | |
| 3V3 | SSD1306 OLED 0.96″ (128×64, I2C) · VCC | red | |
| GND | SSD1306 OLED 0.96″ (128×64, I2C) · GND | black |
Board quirks: The sensor must face straight up, level, with nothing above it but the clear lid. Tilted, it measures a different thing. The BH1750 normally tops out at about 65,000 lux, and Indian noon sun is around 100,000. The sketch shortens the measuring time (MTreg 32) to stretch the range to about 117,000 lux. The kWh/m² figure uses a rough 120 lux per W/m² for sunlight. It is good for comparing spots and days, not for sizing a system to the watt — use the solar calculator and real irradiance data for that. “Bright sun” here means more than 30,000 lux on a level sensor — about when shadows are sharp. Weather services define sunshine more strictly; treat the hours as comparative.
Code
// Sunshine hours logger — ESP32-C3 SuperMini + BH1750 (GY-302) + SSD1306 OLED
// BH1750 and OLED share I2C: SDA GPIO6, SCL GPIO7, both at 3V3; BH1750 ADDR to GND (0x23).
// Logs how many hours of bright sun a spot gets and a rough daily solar energy figure — handy
// before you put up a solar panel, a polyhouse or a kitchen garden. No Wi-Fi or clock needed:
// three hours of darkness close the day, and the next sunrise starts a new one.
#include <Wire.h>
#include <BH1750.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
constexpr uint8_t SDA_PIN = 6, SCL_PIN = 7;
constexpr float SUN_LUX = 30000; // "bright sun" threshold (approximate; see the project page)
constexpr float DARK_LUX = 10;
constexpr float LUX_PER_WM2 = 120; // rough sunlight conversion: ~120 lux per W/m²
constexpr float CALIBRATION = 1.0; // raise if a diffuser cap makes it read low
constexpr unsigned long SAMPLE_MS = 10000;
constexpr unsigned long NIGHT_AFTER_S = 3UL * 3600UL;
BH1750 meter(0x23);
Adafruit_SSD1306 oled(128, 64, &Wire, -1);
unsigned long lastSample = 0;
unsigned long sunSeconds = 0, darkSeconds = 0, daySeconds = 0;
float energyWh = 0, peakLux = 0;
bool dayOpen = false;
uint16_t dayNumber = 0;
void closeDay() {
Serial.printf("DAY %u done: sun %.2f h, %.2f kWh/m2 (rough), peak %.0f lux, daylight %.1f h\n", dayNumber, sunSeconds / 3600.0, energyWh / 1000.0,
peakLux, daySeconds / 3600.0);
sunSeconds = daySeconds = 0;
energyWh = peakLux = 0;
dayOpen = false;
}
void draw(float lux) {
oled.clearDisplay();
oled.setTextColor(SSD1306_WHITE);
oled.setTextSize(1);
oled.setCursor(0, 0);
oled.printf("Now %.0f lux", lux);
oled.setTextSize(2);
oled.setCursor(0, 14);
oled.printf("%luh %02lum", sunSeconds / 3600, (sunSeconds / 60) % 60);
oled.setTextSize(1);
oled.setCursor(0, 34);
oled.print("of bright sun today");
oled.setCursor(0, 46);
oled.printf("~%.1f kWh/m2", energyWh / 1000.0);
oled.setCursor(0, 56);
oled.printf("peak %.0fk lux", peakLux / 1000.0);
oled.display();
}
void setup() {
Serial.begin(115200);
Wire.begin(SDA_PIN, SCL_PIN);
if (!oled.begin(SSD1306_SWITCHCAPVCC, 0x3C)) Serial.println("OLED not found at 0x3C");
if (!meter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE, 0x23, &Wire)) Serial.println("BH1750 not found at 0x23 — check SDA GPIO6, SCL GPIO7, ADDR to GND.");
meter.setMTreg(32); // shorter integration: the range reaches ~117,000 lux, enough for Indian noon sun
Serial.println("minutes,lux,sun_minutes,wh_per_m2");
}
void loop() {
if (millis() - lastSample < SAMPLE_MS) return;
lastSample = millis();
float lux = meter.readLightLevel();
if (lux < 0) {
Serial.println("BH1750 read error");
return;
}
lux *= CALIBRATION;
const unsigned long dt = SAMPLE_MS / 1000;
if (lux < DARK_LUX) {
darkSeconds += dt;
if (dayOpen && darkSeconds >= NIGHT_AFTER_S) closeDay();
} else {
darkSeconds = 0;
if (!dayOpen) {
dayOpen = true;
dayNumber++;
Serial.printf("DAY %u started\n", dayNumber);
}
daySeconds += dt;
if (lux >= SUN_LUX) sunSeconds += dt;
energyWh += lux / LUX_PER_WM2 * dt / 3600.0f;
peakLux = max(peakLux, lux);
}
static uint8_t samples = 0;
if (samples == 0) // a CSV line every 10 minutes (60 samples)
Serial.printf("%lu,%.0f,%lu,%.0f\n", millis() / 60000UL, lux, sunSeconds / 60, energyWh);
samples = (samples + 1) % 60;
draw(lux);
}// Sunshine hours logger — ESP32-C3 SuperMini + BH1750 (GY-302) + SSD1306 OLED
// BH1750 and OLED share I2C: SDA GPIO6, SCL GPIO7, both at 3V3; BH1750 ADDR to GND (0x23).
// Logs how many hours of bright sun a spot gets and a rough daily solar energy figure — handy
// before you put up a solar panel, a polyhouse or a kitchen garden. No Wi-Fi or clock needed:
// three hours of darkness close the day, and the next sunrise starts a new one.
#include <Wire.h>
#include <BH1750.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
constexpr uint8_t SDA_PIN = 6, SCL_PIN = 7;
constexpr float SUN_LUX = 30000; // "bright sun" threshold (approximate; see the project page)
constexpr float DARK_LUX = 10;
constexpr float LUX_PER_WM2 = 120; // rough sunlight conversion: ~120 lux per W/m²
constexpr float CALIBRATION = 1.0; // raise if a diffuser cap makes it read low
constexpr unsigned long SAMPLE_MS = 10000;
constexpr unsigned long NIGHT_AFTER_S = 3UL * 3600UL;
BH1750 meter(0x23);
Adafruit_SSD1306 oled(128, 64, &Wire, -1);
unsigned long lastSample = 0;
unsigned long sunSeconds = 0, darkSeconds = 0, daySeconds = 0;
float energyWh = 0, peakLux = 0;
bool dayOpen = false;
uint16_t dayNumber = 0;
void closeDay() {
Serial.printf("DAY %u done: sun %.2f h, %.2f kWh/m2 (rough), peak %.0f lux, daylight %.1f h\n", dayNumber, sunSeconds / 3600.0, energyWh / 1000.0,
peakLux, daySeconds / 3600.0);
sunSeconds = daySeconds = 0;
energyWh = peakLux = 0;
dayOpen = false;
}
void draw(float lux) {
oled.clearDisplay();
oled.setTextColor(SSD1306_WHITE);
oled.setTextSize(1);
oled.setCursor(0, 0);
oled.printf("Now %.0f lux", lux);
oled.setTextSize(2);
oled.setCursor(0, 14);
oled.printf("%luh %02lum", sunSeconds / 3600, (sunSeconds / 60) % 60);
oled.setTextSize(1);
oled.setCursor(0, 34);
oled.print("of bright sun today");
oled.setCursor(0, 46);
oled.printf("~%.1f kWh/m2", energyWh / 1000.0);
oled.setCursor(0, 56);
oled.printf("peak %.0fk lux", peakLux / 1000.0);
oled.display();
}
void setup() {
Serial.begin(115200);
Wire.begin(SDA_PIN, SCL_PIN);
if (!oled.begin(SSD1306_SWITCHCAPVCC, 0x3C)) Serial.println("OLED not found at 0x3C");
if (!meter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE, 0x23, &Wire)) Serial.println("BH1750 not found at 0x23 — check SDA GPIO6, SCL GPIO7, ADDR to GND.");
meter.setMTreg(32); // shorter integration: the range reaches ~117,000 lux, enough for Indian noon sun
Serial.println("minutes,lux,sun_minutes,wh_per_m2");
}
void loop() {
if (millis() - lastSample < SAMPLE_MS) return;
lastSample = millis();
float lux = meter.readLightLevel();
if (lux < 0) {
Serial.println("BH1750 read error");
return;
}
lux *= CALIBRATION;
const unsigned long dt = SAMPLE_MS / 1000;
if (lux < DARK_LUX) {
darkSeconds += dt;
if (dayOpen && darkSeconds >= NIGHT_AFTER_S) closeDay();
} else {
darkSeconds = 0;
if (!dayOpen) {
dayOpen = true;
dayNumber++;
Serial.printf("DAY %u started\n", dayNumber);
}
daySeconds += dt;
if (lux >= SUN_LUX) sunSeconds += dt;
energyWh += lux / LUX_PER_WM2 * dt / 3600.0f;
peakLux = max(peakLux, lux);
}
static uint8_t samples = 0;
if (samples == 0) // a CSV line every 10 minutes (60 samples)
Serial.printf("%lu,%.0f,%lu,%.0f\n", millis() / 60000UL, lux, sunSeconds / 60, energyWh);
samples = (samples + 1) % 60;
draw(lux);
}Compiled for c3supermini on esp32 3.3.12 with BH1750 1.3.0 · 3 Nov 2026
How it works
Before you spend on rooftop solar, a polyhouse, or even move the tulsi and tomato pots, it helps to know how much sun a spot actually gets. A water tank, a neighbour’s new floor or a coconut tree can shade a corner for hours that you never notice while at work. This logger sits at the spot and counts.
What it measures
Every 10 seconds the BH1750 gives the light level in lux. The ESP32-C3 adds up:
- Hours of bright sun — time above 30,000 lux.
- A rough solar energy figure in kWh/m² — the lux reading divided by about 120 lux per W/m², added up over the day. For comparison, most of India gets roughly 4–6 kWh/m² a day on open ground, less in the monsoon.
- Peak lux of the day.
The OLED shows them all, and a CSV line goes to Serial every 10 minutes.
No clock needed
The board doesn’t need Wi-Fi or a real-time clock to know when a day starts. When light drops below 10 lux for three hours in a row, it’s night: the day’s totals are printed and reset, and the first light of the next morning starts a new day. That also handles a power bank running out and being replaced at noon — you lose only that morning.
Why these parts
The BH1750 is a digital lux meter with a response close to the human eye and a range far beyond cheap LDRs, which saturate in daylight and drift with temperature. The ESP32-C3 SuperMini is small enough to tape inside a lunch box with the OLED and a power bank; both I2C devices share GPIO6/GPIO7, keeping the strapping pins 8 and 9 free.
Set-up
Put the sensor in a clear, level container on the exact spot — the roof edge where panels would go, the balcony rail, the garden bed. Leave it for several days and compare the totals, ideally on clear days. A spot with 6 sun hours beats one with 4 by half as much energy again.
Troubleshooting
Lux is stuck near 54,000 or 65,000 at noon
The sensor saturates: check the MTreg line ran (Serial shows no BH1750 error), or add a piece of white diffuser plastic and raise CALIBRATION to compensate.
A new day starts in the middle of the afternoon
Three hours of darkness close the day. A thick monsoon storm doesn’t get that dark (still hundreds of lux), but a sensor under a tarpaulin will — keep it clear.
The lid fogs up in the morning
Drill two small holes at the bottom of the container and add a silica gel sachet.
Upgrades
- Log each day’s totals to Google Sheets over Wi-Fi to build a month of data.
- Put two loggers on two candidate roof spots and compare them side by side.
- Add a real pyranometer-style calibration against a known solar panel’s output.



