feat: brightness scaling depending on photoresistor value
This commit is contained in:
parent
b872de9d25
commit
009025f858
2 changed files with 236 additions and 62 deletions
|
@ -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
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue