Configuration¶
All settings are stored in NVS (Non-Volatile Storage) and survive firmware and filesystem updates. Configure via the web UI Settings page or the REST API.
Full Configuration Reference¶
{
"deviceName": "SQM-ESP32",
"timezone": "UTC",
"primaryTimeSource": 0,
"secondaryTimeSource": 1,
"wifi": {
"ssid": "YourWiFiSSID",
"password": "YourWiFiPassword",
"hostname": "sqm-esp32",
"autoReconnect": true,
"reconnectDelayMs": 1000,
"maxReconnectDelayMs": 300000
},
"ntp": {
"enabled": true,
"server1": "pool.ntp.org",
"server2": "time.nist.gov",
"timezone": "UTC0",
"gmtOffsetSec": 0,
"daylightOffsetSec": 0,
"syncIntervalMs": 600000
},
"gps": {
"enabled": false,
"rxPin": 17,
"txPin": 16,
"baudRate": 9600
},
"mqtt": {
"enabled": false,
"broker": "mqtt.example.com",
"port": 1883,
"username": "",
"password": "",
"topic": "sqm/data",
"publishIntervalMs": 60000
},
"ota": {
"enabled": false,
"password": ""
},
"auth": {
"enabled": false,
"username": "admin",
"password": ""
},
"rain": {
"enabled": false,
"rxPin": 18,
"txPin": 19,
"baudRate": 9600,
"debugUart": false,
"mode": "polling",
"resolution": "high",
"units": "metric",
"pollIntervalMs": 5000,
"rainClearDelayMs": 900000,
"dailyResetEnabled": false,
"dailyResetHour": 0,
"dailyResetMinute": 0
},
"sensor": {
"readIntervalMs": 5000,
"i2cSDA": 21,
"i2cSCL": 22,
"i2cFrequency": 100000
},
"skyAveraging": {
"windowSeconds": 90
},
"skyCalibration": {
"enabled": false,
"sqmOffset": 0,
"darkVisibleOffset": 0,
"darkFullOffset": 0,
"darkIrOffset": 0,
"darkSampleCount": 0,
"darkCalibratedAt": 0
}
}
Fields¶
Device¶
| Field | Type | Default | Description |
|---|---|---|---|
deviceName |
string | "SQM-ESP32" |
Friendly name shown in the web UI |
timezone |
string | "UTC" |
Display timezone |
primaryTimeSource |
int | 0 |
Primary time source: 0 = NTP, 1 = GPS |
secondaryTimeSource |
int | 1 |
Fallback time source: 0 = NTP, 1 = GPS |
WiFi¶
| Field | Type | Default | Description |
|---|---|---|---|
ssid |
string | — | Network name (2.4 GHz only) |
password |
string | — | Network password |
hostname |
string | "sqm-esp32" |
mDNS hostname (hostname.local) |
autoReconnect |
bool | true |
Reconnect on WiFi drop |
reconnectDelayMs |
int | 1000 |
Initial reconnect delay (ms) |
maxReconnectDelayMs |
int | 300000 |
Max reconnect backoff — 5 min |
NTP¶
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true |
Enable NTP time sync |
server1 |
string | "pool.ntp.org" |
Primary NTP server |
server2 |
string | "time.nist.gov" |
Fallback NTP server |
timezone |
string | "UTC0" |
POSIX timezone string |
gmtOffsetSec |
int | 0 |
UTC offset in seconds (e.g. -28800 for PST) |
daylightOffsetSec |
int | 0 |
DST offset in seconds |
syncIntervalMs |
int | 600000 |
Re-sync interval — default 10 minutes |
POSIX timezone strings
Examples: GMT0, EST5EDT,M3.2.0,M11.1.0, PST8PDT,M3.2.0,M11.1.0, CET-1CEST,M3.5.0,M10.5.0/3.
A full list is available at timezonedb.com.
GPS¶
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
bool | false |
Enable GPS module |
rxPin |
int | 17 |
ESP32 RX pin (connects to GPS TX) |
txPin |
int | 16 |
ESP32 TX pin (connects to GPS RX) |
baudRate |
int | 9600 |
GPS module baud rate (typically 9600) |
When GPS is enabled and has a fix, it can serve as the primary time source for accurate timestamps independent of network connectivity.
MQTT¶
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
bool | false |
Enable MQTT publishing |
broker |
string | — | Broker hostname or IP |
port |
int | 1883 |
Broker port |
username |
string | "" |
Auth username (leave empty if none) |
password |
string | "" |
Auth password |
topic |
string | "sqm/data" |
Publish topic |
publishIntervalMs |
int | 60000 |
Publish interval — default 1 min |
ArduinoOTA¶
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
bool | false |
Enable command-line ArduinoOTA uploads |
password |
string | "" |
Required when command-line ArduinoOTA is enabled |
ArduinoOTA is disabled unless both ota.enabled is true and ota.password is set. The web UI OTA upload page is separate from command-line ArduinoOTA.
HTTP Authentication¶
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
bool | false |
Require credentials for mutation endpoints |
username |
string | "admin" |
Username for HTTP Basic Auth |
password |
string | "" |
Password (required when auth is enabled) |
When auth.enabled is true, the following endpoints require HTTP Basic Auth credentials:
POST /api/config— save configurationPOST /api/restart— restart devicePOST /api/update— firmware OTA uploadPOST /api/update/fs— filesystem OTA uploadPOST /api/wifi/connect— change WiFi networkPOST /api/mqtt/test— test MQTT connection
Read-only endpoints remain accessible without credentials:
GET /api/sensors,GET /api/status,GET /api/config- WebSocket streams (
/ws/sensors,/ws/status)
The auth.password field is masked in GET /api/config responses. Send the mask placeholder or an empty string to preserve the stored password; send null to clear it.
Rain Sensor¶
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
bool | false |
Enable Hydreon RG-15 rain sensor |
rxPin |
int | 18 |
ESP32 RX pin (connects to RG-15 TX) |
txPin |
int | 19 |
ESP32 TX pin (connects to RG-15 RX) |
baudRate |
int | 9600 |
RG-15 serial baud rate |
debugUart |
bool | false |
Log RG-15 UART command/response traffic |
mode |
string | "polling" |
Compatibility field; RG-15 communication is polling-only |
resolution |
string | "high" |
"high", "low", or "switch" |
units |
string | "metric" |
"metric", "imperial", or "switch" |
pollIntervalMs |
int | 5000 |
Interval between RG-15 R commands in polling mode |
rainClearDelayMs |
int | 900000 |
Local rain latch clear delay after intensity returns to zero |
dailyResetEnabled |
bool | false |
Reset RG-15 total accumulation once per day |
dailyResetHour |
int | 0 |
Local hour for scheduled total reset |
dailyResetMinute |
int | 0 |
Local minute for scheduled total reset |
Sensor¶
| Field | Type | Default | Description |
|---|---|---|---|
readIntervalMs |
int | 5000 |
How often sensors are polled (ms) |
i2cSDA |
int | 21 |
SDA GPIO pin |
i2cSCL |
int | 22 |
SCL GPIO pin |
i2cFrequency |
int | 100000 |
I2C clock speed (Hz) |
The TSL2591 light sensor is sampled separately at approximately 600 ms cadence so dark-sky rolling averages are not limited by readIntervalMs.
Sky Averaging¶
| Field | Type | Default | Description |
|---|---|---|---|
windowSeconds |
int | 90 |
Rolling raw-count averaging window for night SQM readings. Valid range is 10-300 seconds. |
Sky Calibration¶
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
bool | false |
Apply sqmOffset to the rolling SQM result |
sqmOffset |
float | 0 |
Absolute SQM correction after comparison with a reference SQM/SQM-L |
darkVisibleOffset |
float | 0 |
Saved dark visible-count floor subtracted before lux/SQM conversion |
darkFullOffset |
float | 0 |
Reserved dark full-channel statistic |
darkIrOffset |
float | 0 |
Reserved dark IR-channel statistic |
darkSampleCount |
int | 0 |
Number of rolling samples used when dark calibration was stored |
darkCalibratedAt |
int | 0 |
Epoch timestamp when available, otherwise device milliseconds |
To set the dark floor, cover the lens/baffle aperture with an opaque cap, wait for skyAveraging.windowSeconds, then call POST /api/sensors/tsl2591/calibrate-dark.
Security Notes¶
SQMeter is designed for a trusted LAN environment. See the Security Guide for a full threat model and setup recommendations.
- By default there is no HTTP authentication; read-only and mutation endpoints are both accessible to any LAN device.
- Enable
auth.enabledwith a strong password to protect mutation endpoints (config save, OTA, restart, WiFi change). GET /api/configredacts stored secrets (WiFi, MQTT, OTA, and auth passwords), but still exposes non-secret settings.- HTTP traffic is plaintext, so credentials sent via config updates are visible to LAN observers. TLS is not supported on the ESP32 in this firmware.
- Command-line ArduinoOTA is disabled unless
ota.enabledistrueandota.passwordis set. rain.debugUartonly affects serial logging for RG-15 command/response traffic. It does not log WiFi, MQTT, or OTA secrets.
Keep the device on a private network or isolated observatory VLAN. Do not expose port 80 or ArduinoOTA to the public internet.
Some settings are applied immediately, but hardware bus settings such as I2C pins, GPS pins, and RG-15 pins may require a restart because the sensors are initialised during firmware startup.
Updating via API¶
# Read current config
curl http://sqmeter.local/api/config
# Partial update — only the fields you include are changed
curl -X POST http://sqmeter.local/api/config \
-H "Content-Type: application/json" \
-d '{"deviceName": "backyard-sqm", "ntp": {"server1": "time.google.com"}}'
Password fields returned by GET /api/config are masked as ********. Sending a masked or empty password back to POST /api/config preserves the stored value; send null for a password field only when you intentionally want to clear it.