diff --git a/.gitignore b/.gitignore index 139d3c5..f868d5a 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ server/photos/ # Secrets .env +firmware/.env # macOS .DS_Store diff --git a/firmware/.env.example b/firmware/.env.example new file mode 100644 index 0000000..b5c1e75 --- /dev/null +++ b/firmware/.env.example @@ -0,0 +1,5 @@ +# WiFi credentials (comma-separated "SSID:password" pairs) +WIFI_NETWORKS="MyNetwork:mypassword" + +# Photo server URL +PHOTO_SERVER_URL="http://nas.home.network:8473/photo" diff --git a/firmware/platformio.ini b/firmware/platformio.ini index b901692..a4cc04b 100644 --- a/firmware/platformio.ini +++ b/firmware/platformio.ini @@ -8,6 +8,9 @@ upload_speed = 921600 board_build.arduino.memory_type = qio_opi board_build.psram = enabled +extra_scripts = + pre:tools/load_env.py + build_flags = -DBOARD_HAS_PSRAM -DARDUINO_USB_CDC_ON_BOOT=1 diff --git a/firmware/src/config.h b/firmware/src/config.h index 4e8cc50..7c7959c 100644 --- a/firmware/src/config.h +++ b/firmware/src/config.h @@ -1,15 +1,15 @@ #pragma once -// WiFi credentials -#define WIFI_SSID "your-wifi-ssid" -#define WIFI_PASSWORD "your-wifi-password" +// WiFi credentials — injected from .env via load_env.py +// Format: "SSID:password" (comma-separated for multiple networks) +#ifndef WIFI_NETWORKS +#define WIFI_NETWORKS "" +#endif -// Photo server URL — should return a JPEG resized to 800x480 -// Use the LAN address to avoid OAuth (ESP32 hits the /photo and /heartbeat -// endpoints directly). If using Traefik, those paths are auth-exempt. +// Photo server URL — injected from .env via load_env.py +#ifndef PHOTO_SERVER_URL #define PHOTO_SERVER_URL "http://nas.home.network:8473/photo" -// Or via Traefik (HTTPS, auth-exempt paths): -// #define PHOTO_SERVER_URL "https://photos.haunt.house/photo" +#endif // How long to sleep between photo updates (in seconds) #define SLEEP_DURATION_SEC (60 * 60) // 1 hour diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp index 593e6db..773b665 100644 --- a/firmware/src/main.cpp +++ b/firmware/src/main.cpp @@ -46,8 +46,31 @@ static int jpegDrawCallback(JPEGDRAW* pDraw) { } bool connectWiFi() { - Serial.printf("Connecting to WiFi '%s'...\n", WIFI_SSID); - WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + // Parse "SSID:password" from WIFI_NETWORKS (takes first network) + char buf[512]; + strncpy(buf, WIFI_NETWORKS, sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; + + if (buf[0] == '\0') { + Serial.println("ERROR: WIFI_NETWORKS not set! Create firmware/.env"); + return false; + } + + // If multiple networks, take the first (comma-separated) + char* comma = strchr(buf, ','); + if (comma) *comma = '\0'; + + char* colon = strchr(buf, ':'); + if (!colon) { + Serial.println("ERROR: WIFI_NETWORKS must be in 'SSID:password' format"); + return false; + } + *colon = '\0'; + const char* ssid = buf; + const char* pass = colon + 1; + + Serial.printf("Connecting to WiFi '%s'...\n", ssid); + WiFi.begin(ssid, pass); int attempts = 0; while (WiFi.status() != WL_CONNECTED && attempts < 60) { diff --git a/firmware/tools/load_env.py b/firmware/tools/load_env.py new file mode 100644 index 0000000..988eccc --- /dev/null +++ b/firmware/tools/load_env.py @@ -0,0 +1,27 @@ +""" +PlatformIO pre-build script: reads .env and injects values as -D build flags. + +Handles quoting so that comma-separated WIFI_NETWORKS works as a single string. +""" + +Import("env") +import os + +env_file = os.path.join(env.get("PROJECT_DIR", "."), ".env") +if not os.path.exists(env_file): + print("WARNING: .env file not found, skipping credential injection") +else: + with open(env_file) as f: + for line in f: + line = line.strip() + if not line or line.startswith("#"): + continue + key, _, value = line.partition("=") + key = key.strip() + value = value.strip().strip('"').strip("'") + if not key or not value: + continue + # Escape for C string literal + escaped = value.replace("\\", "\\\\").replace('"', '\\"') + env.Append(CPPDEFINES=[(key, env.StringifyMacro(escaped))]) + print(f" .env: {key} = {value[:20]}...")