This repository contains firmware for the Flory Smart-Pot — an ESP32-based automated plant watering device. The firmware exposes a small HTTP API, performs sensor reads, controls a pump, logs to an SD card, and supports OTA.
This README is a compact reference for webapp developers and maintainers.
- Build & flash the firmware to an ESP32 (use Arduino IDE or PlatformIO).
- Insert SD card (optional but required for static web UI and logs).
- On first boot, the device runs a WiFiManager AP if no saved credentials are present.
- After connecting to WiFi, the device configures NTP for Europe/Prague time, starts OTA, and serves the HTTP API on port 80.
00_globals.ino— global settings and defaults01_prefs.ino— persistence helpers (save/load settings)03_sensors.ino— sensor task, auto-watering logic, SD logging04_pump.ino— pump control task (LEDC PWM), manual/auto control05_network.ino— WiFiManager, NTP config, HTTP routes06_sd.ino— SD helpers: read/write, upload, log truncationglobals.h— public symbols and prototypes
All endpoints run on port 80. The README includes examples for common interactions.
GET /api/status— runtime snapshot (soil_percent, water_percent, temperature, humidity, pump_on)GET /api/calibration— detailed settings, calibration map, OTA infoGET /api/settings— small settings payload for UIPOST /api/settings— update persisted settings (JSON body; partial updates allowed)POST /api/pump— manual control:{"action":"start","durationMs":3000}or{"action":"stop"}POST /api/restart— restart the ESP32POST /api/logs/rollover— truncate/log/log.txt(useful for testing)GET /sd/list?path=/log— list SD contentsPOST /sd/upload— multipart upload to/app(emergency uploader)POST /sd/wipe?force=1— wipe/app(dangerous; requires force=1)
SD file manager (bash-like):
POST /sd/cd— change directory, body: {"path":".."} or {"path":"/log"}GET /sd/list— list contents of the current directory (CWD) whenpathis omittedGET /sd/open?path=...&offset=0&max=16384— open a slice of a file as text/plain (headers expose size/offset/truncated)POST /sd/rm— remove file or directory; body: {"path":"...","recursive":true}
See API.md or the full README in the repo for details and JSON examples.
- Single file:
/log/log.txtCSV format per line:timestamp,soilPercent,waterPercent,temp,hum,pumpOn,timeSyncedThe firmware writes a CSV header row to/log/log.txtwhen the file is created or truncated, so parsers can rely on column order. Device truncateslog.txtat month rollover (and providesPOST /api/logs/rolloverto test truncation immediately) Logs persist across device restarts and power cycles; the firmware will not delete or rotate existing log files on boot. Truncation happens only on month rollover (when time is known) or when/api/logs/rolloveris called.
There is a separate Next.js uploader at uploader-app/ for pushing the exported web UI (out/ folder) to the device.
Highlights:
- Drag-and-drop the
out/folder or select it; no ZIP required - Uploads files sequentially to
/sd/upload, waits for per-file 200 (basic ACK) - Automatically disables SD logging during upload (reduces SD contention), wipes
/app, uploads, restores logging, and restarts device - Device URL is fixed to
http://flory.local
Run it:
cd uploader-app
npm install
npm run devThen open the printed URL and drop your out/ folder.
The web app’s Files page provides a terminal-like interface for SD navigation using these commands:
ls [path]— list directory (defaults to your CWD)cd [path]— change directory (..supported)open <path> [--max N]— open file contents (first 16KB by default)rm [-r] <path>— remove file or directory (use-rfor directories)
Two auxiliary buttons are available above the terminal:
- Force month rollover (POST
/api/logs/rollover) - Wipe
/app(POST/sd/wipe?force=1)
- Settings are stored in Preferences under
smartpot_v1as a JSON string (see01_prefs.ino) saveCalibrationToPrefs()is called after settings POSTs to persist changes
- Automated watering is fail-closed: it will not trigger until device has valid local time (Prague). This prevents accidental watering during unknown-time windows.
- Manual pump control is always available via
/api/pump.
If you want, I can:
- Add
API.mdwith full endpoint docs (complete JSON schemas and examples) - Implement a small React component library (settings form, pump control, log viewer)
- Add time-set API (
POST /api/time) for provisioning
For full API details and integration examples see API.md (I can add it if you want).
The firmware uses the Arduino SD library in SPI mode with SD_CS_PIN = 4.
Recommended wiring for an ESP32 DevKit (VSPI / default SPI pins):
3V3(ESP32) ->VCC(SD module 3.3V)GND->GNDGPIO18->SCK(CLK)GPIO23->MOSI(DI)GPIO19->MISO(DO)GPIO4->CS(chip select)
Notes:
- Use 3.3V power for the SD card/module. Supplying 5V to a plain micro-SD socket can damage it.
- Many SD breakout boards include a 3.3V regulator and level shifters. If yours does not, wire only 3.3V signals or add level shifting.
- Keep wiring short and solid. If the SD module exposes
CD/CARD_DETECTorWP, you can leave them disconnected unless you implement logic for them.
How to test:
- Insert a micro-SD card and power the device.
- Open serial monitor at 115200 baud.
- On boot you should see either
SD initialized.orSD init failed!.
If you see SD init failed! verify VCC/GND, CS pin (GPIO4), and SPI wiring; try another card or shorter wires.
The device uses WiFiManager and the ESP32 WiFi stack (NVS) to store network credentials. There are two convenient ways to wipe the saved Wi‑Fi credentials so the device re-enters the setup AP (Flory-Setup) on next boot.
- Quick USB flash (recommended when you have physical access)
Flash this small sketch (Arduino IDE / PlatformIO / arduino-cli). It erases Wi‑Fi data and restarts:
#include <WiFi.h>
#include <WiFiManager.h>
void setup() {
Serial.begin(115200);
delay(500);
Serial.println("Wiping stored Wi-Fi credentials...");
WiFiManager wm;
wm.resetSettings(); // clears WiFiManager data in NVS
WiFi.disconnect(true, true); // erase WiFi credentials from WiFi stack
delay(500);
Serial.println("Done. Restarting...");
ESP.restart();
}
void loop() {}After flashing and reboot the device will start the AP Flory-Setup (password flory123) so you can reconfigure Wi‑Fi.
- Remote wipe via HTTP endpoint (add to firmware)
If you prefer to trigger a wipe remotely, add an authenticated endpoint to the firmware that calls WiFiManager::resetSettings() and WiFi.disconnect(true,true) and then restarts. Example handler to register in startWebRoutes() or where routes are created:
server.on("/wifi/reset", HTTP_POST, []() {
Serial.println("[HTTP] POST /wifi/reset received - wiping Wi-Fi settings");
WiFiManager wm;
wm.resetSettings();
WiFi.disconnect(true, true);
sendCors(200, "application/json", "{\"ok\":true, \"restarting\":true}");
delay(200);
ESP.restart();
});Important: protect this endpoint (e.g., require a token or restrict to local network) before exposing it on untrusted networks.
If no Wi‑Fi is configured, the device runs a setup AP:
- SSID:
Flory-Setup - Password:
flory123
You can change those values by modifying the wifiManager.autoConnect("Flory-Setup", "flory123") call in 05_network.ino and reflashing.