Monochrome, print-ready documentation. Emphasis on wiring, I²C behavior, timing, and HTTP payloads.
This firmware reads illuminance (lux) from a BH1750 ambient light sensor over I²C and posts one JSON record to a Flask route every 60 seconds. The sketch initializes I²C on SDA=21 and SCL=22, probes both standard BH1750 addresses (0x23 and 0x5C), performs a single high‑resolution measurement per cycle via readLightLevel(), and submits the lux value as a floating‑point number.
| Item | Notes |
|---|---|
| ESP32 DevKit (ESP‑WROOM‑32 or equivalent) | 3.3 V logic, integrated Wi‑Fi |
| BH1750 light sensor module | Common breakouts include pull‑ups and allow 3.3–5 V input |
| Jumper wires | Male‑female for module headers |
| USB cable | Power and flashing |
| BH1750 | ESP32 | Notes |
|---|---|---|
| VCC | 3V3 | Use 3.3 V on ESP32. Many breakouts tolerate 5 V, but 3.3 V is safest. |
| GND | GND | Common ground required. |
| SDA | GPIO 21 | I²C data line. |
| SCL | GPIO 22 | I²C clock line. |
| ADDR | GND or 3V3 | GND → 0x23, 3V3 → 0x5C. Leave floating for 0x23 on many boards. |
Most BH1750 modules include ~10 kΩ pull‑ups on SDA/SCL. If using a bare IC, add pull‑ups to 3V3.
The sketch probes both and logs the detected address.
| Wi‑Fi SSID | YOUR_WIFI_SSID |
|---|---|
| Wi‑Fi Password | YOUR_WIFI_PASSWORD |
| HTTP Method | POST |
| Content‑Type | application/json |
| Endpoint | http://192.168.1.10:5001/add_data |
| Timeout | 5 s (client‑side) |
| Interval | Every 60 s (one POST per cycle) |
The Flask route expects the following JSON keys. plant_id is an integer in this build.
{
"sensor_type": "bh1750_lux",
"reading": 123.45, // float (lux)
"plant_id": 1 // int
}
Server success responses are HTTP 200 or 201. Other codes are logged to Serial.
| Parameter | Value | Notes |
|---|---|---|
| POST interval | 60 s | One HTTP request per interval |
| Mode | Continuous High‑Resolution | ~120–180 ms first conversion |
| HTTP timeout | 5 s | Client abort if server unresponsive |
| Loop idle | 50 ms | Cooperative multitasking |
/*
ESP32 DevKit + BH1750 -> Flask JSON (every 60s)
- Reads lux once per cycle using BH1750::readLightLevel()
- Posts JSON: {"sensor_type": "...", "reading": <float>, "plant_id": <int>}
- Pins: SDA=21, SCL=22 (per your hardware)
- Library: "BH1750" by Christopher Laws (Library Manager)
*/
#include <WiFi.h>
#include <HTTPClient.h>
#include <Wire.h>
#include <BH1750.h>
// ---------- USER CONFIG (edit these) ----------
const char* WIFI_SSID = "YOUR_WIFI_SSID"; // e.g. "YOUR_WIFI_SSID"
const char* WIFI_PASS = "YOUR_WIFI_PASSWORD"; // e.g. "YOUR_WIFI_PASSWORD"
const char* SERVER_URL = "http://192.168.1.10:5001/add_data"; // your Flask route
const char* SENSOR_TYPE = "bh1750_lux"; // keep as given
const int PLANT_ID = 1; // adjust for plant
// Post once per minute
constexpr unsigned long POST_INTERVAL_MS = 60UL * 1000UL;
// I2C wiring for ESP32 DevKit (per your note)
constexpr uint8_t I2C_SDA = 21;
constexpr uint8_t I2C_SCL = 22;
// ---------- Globals ----------
BH1750 lightMeter;
unsigned long lastPostMs = 0;
// ---------- Forward decls ----------
void connectWiFi();
void ensureWiFiConnected();
bool initBH1750();
float readLuxOnce();
bool postReading(float lux);
// ---------- Setup ----------
void setup() {
Serial.begin(115200);
delay(150);
Serial.println("\n[BOOT] ESP32 BH1750 -> Flask JSON (60s)");
// Init I2C on the pins you specified
Wire.begin(I2C_SDA, I2C_SCL);
Serial.printf("[I2C] SDA=%u SCL=%u\n", I2C_SDA, I2C_SCL);
// Bring up sensor. Try 0x23 then 0x5C to cover ADDR pin wiring.
if (!initBH1750()) {
Serial.println("[SENSOR] BH1750 init failed. Halting.");
while (true) { delay(1000); }
}
// Wi-Fi
connectWiFi();
// Trigger immediate first POST
lastPostMs = 0;
Serial.println("[BOOT] Ready.");
}
// ---------- Loop ----------
void loop() {
ensureWiFiConnected();
unsigned long now = millis();
if (lastPostMs == 0 || (now - lastPostMs) >= POST_INTERVAL_MS) {
lastPostMs = now;
// Single measurement per your requirement
float lux = readLuxOnce();
Serial.printf("[READ] lux=%.2f\n", lux);
// Push to Flask route
bool ok = postReading(lux);
Serial.printf("[POST] %s\n", ok ? "OK" : "FAIL");
}
delay(50); // keep CPU cool, maintain responsiveness
}
// ---------- Wi-Fi helpers ----------
void connectWiFi() {
Serial.printf("[WIFI] Connecting to SSID: %s\n", WIFI_SSID);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
uint32_t start = millis();
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
if (millis() - start > 20000) { // 20s retry window
Serial.println("\n[WIFI] Timeout. Re-attempting.");
WiFi.disconnect(true);
WiFi.begin(WIFI_SSID, WIFI_PASS);
start = millis();
}
}
Serial.printf("\n[WIFI] Connected. IP=%s\n", WiFi.localIP().toString().c_str());
}
void ensureWiFiConnected() {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("[WIFI] Disconnected. Reconnecting.");
connectWiFi();
}
}
// ---------- Sensor helpers ----------
bool initBH1750() {
// Give sensor power-up time
delay(150);
// Try default 0x23 first
if (lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE, 0x23, &Wire)) {
Serial.println("[SENSOR] BH1750 detected at 0x23");
// Spec: first valid conversion ~120–180 ms in high-res
delay(180);
return true;
}
// Fallback to 0x5C if ADDR tied high
if (lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE, 0x5C, &Wire)) {
Serial.println("[SENSOR] BH1750 detected at 0x5C");
delay(180);
return true;
}
Serial.println("[SENSOR] BH1750 not found on 0x23 or 0x5C. Check wiring/power.");
return false;
}
float readLuxOnce() {
// Simple single call per your request. No rolling average.
float lux = lightMeter.readLightLevel(); // returns lux as float
if (isnan(lux)) {
Serial.println("[READ] NaN from sensor. Returning -1.");
return -1.0f;
}
if (lux < 0.0f) lux = 0.0f; // clamp negatives
return lux;
}
// ---------- HTTP POST ----------
bool postReading(float lux) {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("[HTTP] No Wi-Fi. Abort.");
return false;
}
WiFiClient client; // plain HTTP
HTTPClient http;
http.setTimeout(5000); // 5s
if (!http.begin(client, SERVER_URL)) {
Serial.println("[HTTP] http.begin() failed");
return false;
}
http.addHeader("Content-Type", "application/json");
// Flask expects: sensor_type (string), reading (float), plant_id (int/string)
String payload = String("{\"sensor_type\":\"") + SENSOR_TYPE +
"\",\"reading\":" + String(lux, 2) +
",\"plant_id\":" + String(PLANT_ID) + "}";
Serial.print("[HTTP] POST payload: ");
Serial.println(payload);
int code = http.POST(payload);
Serial.printf("[HTTP] Status: %d\n", code);
String resp = http.getString();
Serial.print("[HTTP] Response: ");
Serial.println(resp);
http.end();
return (code == 201 || code == 200);
}
[BOOT] ESP32 BH1750 -> Flask JSON (60s)
[I2C] SDA=21 SCL=22
[SENSOR] BH1750 detected at 0x23
[WIFI] Connecting to SSID: YOUR_WIFI_SSID
....
[WIFI] Connected. IP=192.168.1.xxx
[BOOT] Ready.
[READ] lux=123.45
[HTTP] POST payload: {"sensor_type":"bh1750_lux","reading":123.45,"plant_id":1}
[HTTP] Status: 201
[HTTP] Response: {"success": true}
[POST] OK
| Property | Value |
|---|---|
| Illuminance range | ~1 to 65,535 lux |
| Resolution | ~1 lux (high‑res), ~4 lux (low‑res) |
| Supply | 2.4–3.6 V IC (many modules accept 3.3–5 V) |
| Interface | I²C up to 400 kHz |
| Typical current | ~0.12 mA active |
| Addresses | 0x23 (ADDR low), 0x5C (ADDR high) |
This document is intentionally black‑and‑white for clean printing and photocopying.