From 009025f858416721e2c775713fe15503899a88f5 Mon Sep 17 00:00:00 2001 From: s3lph Date: Sun, 13 Oct 2024 02:14:21 +0200 Subject: [PATCH] feat: brightness scaling depending on photoresistor value --- esp32/platformio.ini | 1 + esp32/src/main.cpp | 297 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 236 insertions(+), 62 deletions(-) diff --git a/esp32/platformio.ini b/esp32/platformio.ini index 1ed0ce4..36a3e25 100644 --- a/esp32/platformio.ini +++ b/esp32/platformio.ini @@ -17,6 +17,7 @@ lib_deps = bblanchon/ArduinoJson@^7.1.0 sstaub/NTP@^1.6 tzapu/WiFiManager@^2.0.17 + khoih-prog/ESP32_C3_TimerInterrupt@^1.8.0 build_flags = -D ARDUINO_USB_MODE=1 -D ARDUINO_USB_CDC_ON_BOOT=1 diff --git a/esp32/src/main.cpp b/esp32/src/main.cpp index c873bf6..69c963d 100644 --- a/esp32/src/main.cpp +++ b/esp32/src/main.cpp @@ -6,16 +6,29 @@ #include #include #include +#include +#include +#include #include "spacemap.h" #define WS2812_PIN 0 #define WS2812_LEN (sizeof(spaces)/sizeof(char*)) #define WS2812_BS 5 + #define SPACEAPI_HOST "api.spaceapi.io" #define SPACEAPI_PORT 443 #define SPACEAPI_PATH "/" +#define BOOT_READ_PIN 5 +#define BOOT_GND_PIN 6 + +#define BRIGHTNESS_PIN 3 +#define DEFAULT_BRIGHTNESS_THRESH_LOWER 1000 +#define DEFAULT_BRIGHTNESS_THRESH_UPPER 400 +#define DEFAULT_BRIGHTNESS_MIN 10 +#define DEFAULT_BRIGHTNESS_MAX 10 + WiFiManager wifiManager; WiFiClientSecure client; @@ -24,9 +37,96 @@ NTP ntp(wifiUdp); Adafruit_NeoPixel pixels(WS2812_LEN, WS2812_PIN, NEO_RGB | NEO_KHZ800); JsonDocument json, filter; +DeserializationError error = DeserializationError::EmptyInput; +ESP32Timer ITimer0(0); + +volatile bool wmp_needs_config_save; +volatile bool timerExpired; + +enum SpaceState { + Closed, + Open, + Invalid, + Outdated, +}; + + +const uint8_t config_version = 2; +struct __attribute__ ((packed)) Config { + uint8_t version; + uint16_t brightness_thresh_lower; + uint16_t brightness_thresh_upper; + uint16_t brightness_min; + uint16_t brightness_max; +} spiffs_config; + +SpaceState states[WS2812_LEN]; + +bool timer0_isr(void *data) { + timerExpired = true; + return true; +} + +void writeConfig() { + File f = SPIFFS.open("/spaceapi.bin", FILE_WRITE); + f.write((uint8_t *) &spiffs_config, sizeof(spiffs_config)); + f.close(); + Serial.println("Wrote config to SPIFFS:/spaceapi.bin"); +} + +void writeDefaultConfig() { + spiffs_config.version = config_version; + spiffs_config.brightness_thresh_lower = DEFAULT_BRIGHTNESS_THRESH_LOWER; + spiffs_config.brightness_thresh_upper = DEFAULT_BRIGHTNESS_THRESH_UPPER; + spiffs_config.brightness_min = DEFAULT_BRIGHTNESS_MIN; + spiffs_config.brightness_max = DEFAULT_BRIGHTNESS_MAX; + writeConfig(); +} + +void loadConfig() { + Serial.println("Loading config from SPIFFS:/spaceapi.bin"); + File f = SPIFFS.open("/spaceapi.bin", FILE_READ); + if (!f) { + writeDefaultConfig(); + f = SPIFFS.open("/spaceapi.bin", FILE_READ); + } + f.read((uint8_t *) &spiffs_config.version, sizeof(spiffs_config.version)); + if (spiffs_config.version != config_version) { + f.close(); + writeDefaultConfig(); + f = SPIFFS.open("/spaceapi.bin", FILE_READ); + } + f.seek(0); + f.read((uint8_t *) &spiffs_config, sizeof(spiffs_config)); + f.close(); + Serial.println("Loaded config from SPIFFS:/spaceapi.bin"); + Serial.print(" version: "); + Serial.println(spiffs_config.version); + Serial.print(" brightness_thresh_lower: "); + Serial.println(spiffs_config.brightness_thresh_lower); + Serial.print(" brightness_thresh_upper: "); + Serial.println(spiffs_config.brightness_thresh_upper); + Serial.print(" brightness_min: "); + Serial.println(spiffs_config.brightness_min); + Serial.print(" brightness_max: "); + Serial.println(spiffs_config.brightness_max); +} + +void wmp_save_config_callback() { + wmp_needs_config_save = true; +} + void setup() { Serial.begin(115200); + pinMode(BOOT_READ_PIN, INPUT_PULLUP); + pinMode(BOOT_GND_PIN, OUTPUT); + digitalWrite(BOOT_GND_PIN, LOW); + if (!SPIFFS.begin(true)) { + Serial.println("SPIFFS mount failed"); + return; + } + loadConfig(); pixels.begin(); pixels.clear(); @@ -39,9 +139,36 @@ void setup() { wifiManager.setDebugOutput(true); wifiManager.setConnectTimeout(60); wifiManager.setConfigPortalTimeout(300); - if (!wifiManager.autoConnect("spaceapimap", "12345678")) { - ESP.restart(); - while (true); + wifiManager.setSaveConfigCallback(wmp_save_config_callback); + wmp_needs_config_save = false; + + WiFiManagerParameter wmp_brightness_thresh_lower("brightness_thresh_lower", "Lights out at ", String(spiffs_config.brightness_thresh_lower).c_str(), 6); + wifiManager.addParameter(&wmp_brightness_thresh_lower); + WiFiManagerParameter wmp_brightness_thresh_upper("brightness_thresh_upper", "Max brightness at ", String(spiffs_config.brightness_thresh_upper).c_str(), 6); + wifiManager.addParameter(&wmp_brightness_thresh_upper); + WiFiManagerParameter wmp_brightness_min("brightness_min", "Min brightness ", String(spiffs_config.brightness_min).c_str(), 4); + wifiManager.addParameter(&wmp_brightness_min); + WiFiManagerParameter wmp_brightness_max("brightness_max", "Max brightness (Warning: Too high might blow fuses) ", String(spiffs_config.brightness_max).c_str(), 4); + wifiManager.addParameter(&wmp_brightness_max); + + if (!digitalRead(BOOT_READ_PIN)) { + if (!wifiManager.startConfigPortal("spaceapimap", "12345678")) { + ESP.restart(); + while (true); + } + } else { + if (!wifiManager.autoConnect("spaceapimap", "12345678")) { + ESP.restart(); + while (true); + } + } + + if (wmp_needs_config_save) { + spiffs_config.brightness_thresh_lower = atol(wmp_brightness_thresh_lower.getValue()); + spiffs_config.brightness_thresh_upper = atol(wmp_brightness_thresh_upper.getValue()); + spiffs_config.brightness_min = atol(wmp_brightness_min.getValue()); + spiffs_config.brightness_max = atol(wmp_brightness_max.getValue()); + writeConfig(); } ntp.begin(); @@ -51,74 +178,120 @@ void setup() { filter[0]["lastSeen"] = true; filter[0]["data"]["state"]["open"] = true; filter[0]["data"]["state"]["lastchange"] = true; + + analogSetPinAttenuation(BRIGHTNESS_PIN, ADC_11db); + ITimer0.attachInterruptInterval(300 * 1000 * 1000, timer0_isr); + timerExpired = true; + memset(states, 0, sizeof(uint8_t) * WS2812_LEN); } void loop() { - uint32_t color0 = pixels.getPixelColor(0); - pixels.setPixelColor(0, color0); - - ntp.update(); - Serial.println(ntp.formattedTime("\nIt is %d.%m.%Y %H:%M UTC")); time_t now = ntp.epoch(); - Serial.println("Starting connection to server..."); - client.setInsecure(); - if (!client.connect(SPACEAPI_HOST, SPACEAPI_PORT)) { - Serial.println("Connection failed!"); - } else { - Serial.println("Connected to server!"); - client.print("GET "); - client.print(SPACEAPI_PATH); - client.println(" HTTP/1.0"); - client.print("Host: "); - client.println(SPACEAPI_HOST); - client.println("Connection: close"); - client.println(); + if (timerExpired) { + timerExpired = false; + Serial.println("Timer expired!"); + ntp.update(); + now = ntp.epoch(); + Serial.println(ntp.formattedTime("\nIt is %d.%m.%Y %H:%M UTC")); + Serial.println("Starting connection to server..."); + client.setInsecure(); + if (!client.connect(SPACEAPI_HOST, SPACEAPI_PORT)) { + Serial.println("Connection failed!"); + } else { + Serial.println("Connected to server!"); + client.print("GET "); + client.print(SPACEAPI_PATH); + client.println(" HTTP/1.0"); + client.print("Host: "); + client.println(SPACEAPI_HOST); + client.println("Connection: close"); + client.println(); - while (client.readStringUntil('\n') != "\r"); - } - DeserializationError error = deserializeJson(json, client, DeserializationOption::Filter(filter)); - if (error) { - Serial.print("deserializeJson() failed: "); - Serial.println(error.f_str()); - } else { - pixels.clear(); - for (uint16_t i = 0; i < json.size(); ++i) { - int16_t pi = -1; - for (uint16_t j = 0; j < WS2812_LEN; ++j) { - if (spaces[j] == json[i]["url"]) { - pi = j; - break; - } - } - if (pi < 0) { - continue; - } - if (json[i]["data"] == nullptr || json[i]["data"]["state"] == nullptr || json[i]["data"]["state"]["open"] == nullptr || !json[i]["data"]["state"]["open"].is()) { - pixels.setPixelColor(pi, pixels.Color(WS2812_BS*2, WS2812_BS, 0)); - Serial.println(": invalid!"); - } else { - Serial.print(spaces[pi]); - time_t last = json[i]["lastSeen"].as(); - time_t last2 = last; - if (json[i]["data"]["state"]["lastchange"] != nullptr) { - last2 = json[i]["data"]["state"]["lastchange"].as(); - } - if (now - last > 24*3600) { - pixels.setPixelColor(pi, pixels.Color(0, 0, WS2812_BS*2)); - Serial.println(": outdated!"); - } else { - if (json[i]["data"]["state"]["open"].as()) { - pixels.setPixelColor(pi, pixels.Color(0, WS2812_BS*2, 0)); - Serial.println(": open"); - } else { - pixels.setPixelColor(pi, pixels.Color(WS2812_BS*2, 0, 0)); - Serial.println(": closed"); + while (client.readStringUntil('\n') != "\r"); + } + error = deserializeJson(json, client, DeserializationOption::Filter(filter)); + if (error) { + Serial.print("deserializeJson() failed: "); + Serial.println(error.f_str()); + } else { + for (uint16_t i = 0; i < json.size(); ++i) { + int16_t pi = -1; + for (uint16_t j = 0; j < WS2812_LEN; ++j) { + if (spaces[j] == json[i]["url"]) { + pi = j; + break; } } + if (pi < 0) { + continue; + } + if (json[i]["data"] == nullptr || json[i]["data"]["state"] == nullptr || json[i]["data"]["state"]["open"] == nullptr || !json[i]["data"]["state"]["open"].is()) { + states[pi] = SpaceState::Invalid; + Serial.println(": invalid!"); + } else { + Serial.print(spaces[pi]); + time_t last = json[i]["lastSeen"].as(); + time_t last2 = last; + if (json[i]["data"]["state"]["lastchange"] != nullptr) { + last2 = json[i]["data"]["state"]["lastchange"].as(); + } + if (now - last > 24*3600) { + states[pi] = SpaceState::Outdated; + Serial.println(": outdated!"); + } else { + if (json[i]["data"]["state"]["open"].as()) { + states[pi] = SpaceState::Open; + Serial.println(": open"); + } else { + states[pi] = SpaceState::Closed; + Serial.println(": closed"); + } + } + } + } + + } + client.stop(); + } // endif timerExpired + + if (!error) { + uint16_t brightness = analogRead(BRIGHTNESS_PIN); + Serial.print("brightness: "); + Serial.print(brightness); + pixels.clear(); + if (brightness < spiffs_config.brightness_thresh_lower) { + uint16_t clamped = min(max(brightness, spiffs_config.brightness_thresh_upper), spiffs_config.brightness_thresh_lower); + uint8_t power = spiffs_config.brightness_min + (spiffs_config.brightness_max - spiffs_config.brightness_min) * pow(1.0f * (spiffs_config.brightness_thresh_lower - clamped) / (spiffs_config.brightness_thresh_lower - spiffs_config.brightness_thresh_upper), 2); + Serial.print(", clamped: "); + Serial.print(clamped); + Serial.print(", power: "); + Serial.println(power); + // safeguard! + if (power > spiffs_config.brightness_max) { + power = spiffs_config.brightness_max; + } + //power = 10; + for (uint16_t i = 0; i < WS2812_LEN; ++i) { + switch(states[i]) { + case SpaceState::Closed: + pixels.setPixelColor(i, pixels.Color(power, 0, 0)); + break; + case SpaceState::Open: + pixels.setPixelColor(i, pixels.Color(0, power, 0)); + break; + case SpaceState::Invalid: + pixels.setPixelColor(i, pixels.Color(power, power/2, 0)); + break; + case SpaceState::Outdated: + pixels.setPixelColor(i, pixels.Color(0, 0, power)); + break; + default: + break; + } } } pixels.show(); + Serial.println(); } - client.stop(); - delay(300 * 1000); + delay(10); }