feat: brightness scaling depending on photoresistor value

This commit is contained in:
s3lph 2024-10-13 02:14:21 +02:00
parent b872de9d25
commit 009025f858
Signed by: s3lph
GPG key ID: 0AA29A52FB33CFB5
2 changed files with 236 additions and 62 deletions

View file

@ -17,6 +17,7 @@ lib_deps =
bblanchon/ArduinoJson@^7.1.0 bblanchon/ArduinoJson@^7.1.0
sstaub/NTP@^1.6 sstaub/NTP@^1.6
tzapu/WiFiManager@^2.0.17 tzapu/WiFiManager@^2.0.17
khoih-prog/ESP32_C3_TimerInterrupt@^1.8.0
build_flags = build_flags =
-D ARDUINO_USB_MODE=1 -D ARDUINO_USB_MODE=1
-D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_CDC_ON_BOOT=1

View file

@ -6,16 +6,29 @@
#include <NTP.h> #include <NTP.h>
#include <Adafruit_NeoPixel.h> #include <Adafruit_NeoPixel.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <ESP32_C3_TimerInterrupt.h>
#include <FS.h>
#include <SPIFFS.h>
#include "spacemap.h" #include "spacemap.h"
#define WS2812_PIN 0 #define WS2812_PIN 0
#define WS2812_LEN (sizeof(spaces)/sizeof(char*)) #define WS2812_LEN (sizeof(spaces)/sizeof(char*))
#define WS2812_BS 5 #define WS2812_BS 5
#define SPACEAPI_HOST "api.spaceapi.io" #define SPACEAPI_HOST "api.spaceapi.io"
#define SPACEAPI_PORT 443 #define SPACEAPI_PORT 443
#define SPACEAPI_PATH "/" #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; WiFiManager wifiManager;
WiFiClientSecure client; WiFiClientSecure client;
@ -24,9 +37,96 @@ NTP ntp(wifiUdp);
Adafruit_NeoPixel pixels(WS2812_LEN, WS2812_PIN, NEO_RGB | NEO_KHZ800); Adafruit_NeoPixel pixels(WS2812_LEN, WS2812_PIN, NEO_RGB | NEO_KHZ800);
JsonDocument json, filter; 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() { void setup() {
Serial.begin(115200); 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.begin();
pixels.clear(); pixels.clear();
@ -39,9 +139,36 @@ void setup() {
wifiManager.setDebugOutput(true); wifiManager.setDebugOutput(true);
wifiManager.setConnectTimeout(60); wifiManager.setConnectTimeout(60);
wifiManager.setConfigPortalTimeout(300); wifiManager.setConfigPortalTimeout(300);
if (!wifiManager.autoConnect("spaceapimap", "12345678")) { wifiManager.setSaveConfigCallback(wmp_save_config_callback);
ESP.restart(); wmp_needs_config_save = false;
while (true);
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(); ntp.begin();
@ -51,74 +178,120 @@ void setup() {
filter[0]["lastSeen"] = true; filter[0]["lastSeen"] = true;
filter[0]["data"]["state"]["open"] = true; filter[0]["data"]["state"]["open"] = true;
filter[0]["data"]["state"]["lastchange"] = 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() { 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(); time_t now = ntp.epoch();
Serial.println("Starting connection to server..."); if (timerExpired) {
client.setInsecure(); timerExpired = false;
if (!client.connect(SPACEAPI_HOST, SPACEAPI_PORT)) { Serial.println("Timer expired!");
Serial.println("Connection failed!"); ntp.update();
} else { now = ntp.epoch();
Serial.println("Connected to server!"); Serial.println(ntp.formattedTime("\nIt is %d.%m.%Y %H:%M UTC"));
client.print("GET "); Serial.println("Starting connection to server...");
client.print(SPACEAPI_PATH); client.setInsecure();
client.println(" HTTP/1.0"); if (!client.connect(SPACEAPI_HOST, SPACEAPI_PORT)) {
client.print("Host: "); Serial.println("Connection failed!");
client.println(SPACEAPI_HOST); } else {
client.println("Connection: close"); Serial.println("Connected to server!");
client.println(); 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"); while (client.readStringUntil('\n') != "\r");
} }
DeserializationError error = deserializeJson(json, client, DeserializationOption::Filter(filter)); error = deserializeJson(json, client, DeserializationOption::Filter(filter));
if (error) { if (error) {
Serial.print("deserializeJson() failed: "); Serial.print("deserializeJson() failed: ");
Serial.println(error.f_str()); Serial.println(error.f_str());
} else { } else {
pixels.clear(); for (uint16_t i = 0; i < json.size(); ++i) {
for (uint16_t i = 0; i < json.size(); ++i) { int16_t pi = -1;
int16_t pi = -1; for (uint16_t j = 0; j < WS2812_LEN; ++j) {
for (uint16_t j = 0; j < WS2812_LEN; ++j) { if (spaces[j] == json[i]["url"]) {
if (spaces[j] == json[i]["url"]) { pi = j;
pi = j; break;
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<bool>()) {
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>();
time_t last2 = last;
if (json[i]["data"]["state"]["lastchange"] != nullptr) {
last2 = json[i]["data"]["state"]["lastchange"].as<time_t>();
}
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<bool>()) {
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");
} }
} }
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<bool>()) {
states[pi] = SpaceState::Invalid;
Serial.println(": invalid!");
} else {
Serial.print(spaces[pi]);
time_t last = json[i]["lastSeen"].as<time_t>();
time_t last2 = last;
if (json[i]["data"]["state"]["lastchange"] != nullptr) {
last2 = json[i]["data"]["state"]["lastchange"].as<time_t>();
}
if (now - last > 24*3600) {
states[pi] = SpaceState::Outdated;
Serial.println(": outdated!");
} else {
if (json[i]["data"]["state"]["open"].as<bool>()) {
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(); pixels.show();
Serial.println();
} }
client.stop(); delay(10);
delay(300 * 1000);
} }