Initial commit
This commit is contained in:
commit
143c037dd3
7 changed files with 958 additions and 0 deletions
36
README.md
Normal file
36
README.md
Normal file
|
@ -0,0 +1,36 @@
|
|||
# Custom light instruction
|
||||
|
||||
This build is for: https://github.com/mariusmotea/diyHue/tree/master/Lights/Arduino/Generic_Dimmable_Light
|
||||
|
||||
1. try out your parts and plan pathways
|
||||
|
||||
2. solder gnd/- pathway and the 1k OHM resistors in place
|
||||
|
||||
3. fasten wemos headers, and connect G pin to ground pathway
|
||||
|
||||
4. Bring a wires from GPIO pins to the paths with resistors
|
||||
|
||||
5. Fasten the blue cables (these go to the - of your strip(s)) (the board in the picture will have 2 individual controlable lights, you can have multiple strips on each)
|
||||
|
||||
7. Fasten 3 pin headers for mosfets
|
||||
|
||||
8. Connect the various paths to 3 pin headers
|
||||
|
||||
9. Done
|
||||
|
||||
|
||||
This build is very simular to the Generic RGBW light build. (didnt take picture with the mosfets and wemos inserted, but i guess this can be imagined) make sure the G pin on the wemos hits the grounded path.
|
||||
|
||||
![Top](https://github.com/mariusmotea/diyHue/blob/master/Lights/Arduino/Generic_Dimmable_Light/images/Over.jpg?raw=true)
|
||||
![Back](https://github.com/mariusmotea/diyHue/blob/master/Lights/Arduino/Generic_Dimmable_Light/images/Under.jpg?raw=true)
|
||||
|
||||
## Components
|
||||
[IRLB8721-TO220 MOSFETS (2x)](https://www.aliexpress.com/item/10PCS-IRLB8721-TO220-IRLB8721PBF-TO-220-free-shipping/32714364118.html)
|
||||
|
||||
[Wemos D1 mini (1x)](https://www.aliexpress.com/item/ESP8266-ESP12-ESP-12-WeMos-D1-Mini-WIFI-Dev-Kit-Development-Board-NodeMCU-Lua/32653918483.html)
|
||||
|
||||
[1k OHM resistor (2x)](https://www.aliexpress.com/item/100pcs-1-4W-Metal-Film-Resistor-1K-ohm-1KR-1-Tolerance-Precision-RoHS-Lead-Free-In/1851964338.html)
|
||||
|
||||
[Female Headers (2x)](https://www.aliexpress.com/item/10-10-pcs-Single-Row-Pin-Female-Header-Socket-2-54mm-Pitch-1-10p-12p-20p/32783590196.html)
|
||||
|
||||
[Prototyping board (1x)](https://www.aliexpress.com/item/20pcs-5x7-4x6-3x7-2x8-cm-double-Side-Copper-prototype-pcb-Universal-Board-for-Arduino/1847727667.html)
|
19
firmware/config.h
Normal file
19
firmware/config.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
|
||||
#define USE_STATIC_IP //! uncomment to enable Static IP Adress
|
||||
|
||||
#define SERIAL_BAUD_RATE 115200
|
||||
|
||||
#define light_name "Dimmable Hue Light" //default light name
|
||||
|
||||
#define LIGHTS_COUNT 4 // do not modify because luminie p30 only ha 4 channel
|
||||
|
||||
#define EEPROM_LAST_STATE_STARTUP_ADDRESS 0 // startup behavior is used for all lights
|
||||
#define EEPROM_SCENE_ADDRESS 1 // scene is used for all of the lights
|
||||
#define EEPROM_LAST_STATE_ADDRESS 2 // the first "last state" information for the first light
|
||||
#define EEPROM_TIMING_CONTROL_ENABLED_ADDRESS 3 // the first "last state" information for the first light
|
||||
#define EEPROM_DYNAMIC_IP_ADDRESS 4
|
||||
#define EEPROM_LAST_DEFAULT_BLOCK_ADDRESS 20
|
||||
#define EEPROM_TIMING_DATA_ADDRESS (EEPROM_TIMING_CONTROL_ENABLED_ADDRESS + LIGHTS_COUNT) // Stored data date per light ELE_USED; HH; MM; CH1; CH2; CH3; CH4;
|
||||
|
||||
|
||||
#define TC_TIME_CHECK_INTERVAL_MS 600000
|
649
firmware/firmware.ino
Normal file
649
firmware/firmware.ino
Normal file
|
@ -0,0 +1,649 @@
|
|||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <ESP8266HTTPUpdateServer.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <WiFiManager.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <EEPROM.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
//********* Config block *********//
|
||||
|
||||
uint8_t pins[LIGHTS_COUNT] = {12, 15, 13, 14};
|
||||
|
||||
#define use_hardware_switch false // To control on/off state and brightness using GPIO/Pushbutton, set this value to true.
|
||||
//For GPIO based on/off and brightness control, it is mandatory to connect the following GPIO pins to ground using 10k resistor
|
||||
#define button1_pin 4 // on and brightness up
|
||||
#define button2_pin 5 // off and brightness down
|
||||
|
||||
#ifdef USE_STATIC_IP
|
||||
IPAddress strip_ip ( 192, 168, 0, 26); // choose an unique IP Adress
|
||||
IPAddress gateway_ip ( 192, 168, 0, 1); // Router IP
|
||||
IPAddress subnet_mask( 255, 255, 255, 0);
|
||||
#endif
|
||||
|
||||
//********************************//
|
||||
|
||||
#define LIGHT_VERSION 2.1
|
||||
|
||||
#define LAST_STATE_STARTUP_LIGHT_LAST_STATE 0
|
||||
#define LAST_STATE_STARTUP_LIGHT_ON_STATE 1
|
||||
#define LAST_STATE_STARTUP_LIGHT_OFF_STATE 2
|
||||
|
||||
#define LIGHT_STATE_ON 1
|
||||
#define LIGHT_STATE_OFF 0
|
||||
|
||||
#define TIMING_CONTROL_ENABLED 1
|
||||
#define TIMING_CONTROL_DISABLED 0
|
||||
|
||||
#define SCENE_RELEAX 0
|
||||
#define SCENE_BRIGHT 1
|
||||
#define SCENE_NIGHTLY 2
|
||||
|
||||
// 10 bit PWM
|
||||
#define PWM_OFF 0 // 0V
|
||||
#define PWM_MIN 640 // 15V - minimum light amount (~1%)
|
||||
#define PWM_MAX 1023 // 24V - maximum light amount (100%)
|
||||
#define PWM_INC 4 // 24V-15V = 9V range; 9V ≙ 1024/640 = 383 counts; 383/100% = 3,83 counts (1%) / % => round up 4 counts / % (~1%)
|
||||
|
||||
uint8_t scene;
|
||||
uint8_t tc_enabled;
|
||||
|
||||
bool light_state[LIGHTS_COUNT];
|
||||
bool in_transition;
|
||||
|
||||
int transitiontime[LIGHTS_COUNT];
|
||||
int bri[LIGHTS_COUNT];
|
||||
|
||||
float step_level[LIGHTS_COUNT];
|
||||
float current_bri[LIGHTS_COUNT];
|
||||
byte mac[6];
|
||||
|
||||
ESP8266WebServer server(80);
|
||||
ESP8266HTTPUpdateServer httpUpdateServer;
|
||||
|
||||
void handleNotFound()
|
||||
{
|
||||
String message = "File Not Found\n\n";
|
||||
message += "URI: ";
|
||||
message += server.uri();
|
||||
message += "\nMethod: ";
|
||||
message += (server.method() == HTTP_GET) ? "GET" : "POST";
|
||||
message += "\nArguments: ";
|
||||
message += server.args();
|
||||
message += "\n";
|
||||
|
||||
for (uint8_t i = 0; i < server.args(); i++)
|
||||
{
|
||||
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
|
||||
}
|
||||
server.send(404, "text/plain", message);
|
||||
}
|
||||
|
||||
void apply_scene(uint8_t new_scene, uint8_t light)
|
||||
{
|
||||
if (new_scene == SCENE_RELEAX)
|
||||
{
|
||||
bri[light] = 144;
|
||||
} else if (new_scene == SCENE_BRIGHT)
|
||||
{
|
||||
bri[light] = 254;
|
||||
} else if (new_scene == SCENE_NIGHTLY)
|
||||
{
|
||||
bri[0] = 25;
|
||||
bri[1] = 0;
|
||||
bri[2] = 0;
|
||||
bri[3] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void process_lightdata(uint8_t light, float transitiontime)
|
||||
{
|
||||
transitiontime *= 16;
|
||||
if (light_state[light])
|
||||
{
|
||||
step_level[light] = (bri[light] - current_bri[light]) / transitiontime;
|
||||
} else {
|
||||
step_level[light] = current_bri[light] / transitiontime;
|
||||
}
|
||||
}
|
||||
|
||||
void lightEngine()
|
||||
{
|
||||
for (int i = 0; i < LIGHTS_COUNT; i++)
|
||||
{
|
||||
if (light_state[i])
|
||||
{
|
||||
if (bri[i] != current_bri[i])
|
||||
{
|
||||
in_transition = true;
|
||||
current_bri[i] += step_level[i];
|
||||
if ((step_level[i] > 0.0 && current_bri[i] > bri[i]) || (step_level[i] < 0.0 && current_bri[i] < bri[i]))
|
||||
{
|
||||
current_bri[i] = bri[i];
|
||||
}
|
||||
analogWrite(pins[i], (int)(current_bri[i] * 4));
|
||||
}
|
||||
} else {
|
||||
|
||||
if (current_bri[i] != 0 )
|
||||
{
|
||||
in_transition = true;
|
||||
current_bri[i] -= step_level[i];
|
||||
if (current_bri[i] < 0)
|
||||
{
|
||||
current_bri[i] = 0;
|
||||
}
|
||||
analogWrite(pins[i], (int)(current_bri[i] * 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (in_transition)
|
||||
{
|
||||
delay(6);
|
||||
in_transition = false;
|
||||
|
||||
} else if (use_hardware_switch == true)
|
||||
{
|
||||
if (digitalRead(button1_pin) == HIGH)
|
||||
{
|
||||
int i = 0;
|
||||
while (digitalRead(button1_pin) == HIGH && i < 30)
|
||||
{
|
||||
delay(20);
|
||||
i++;
|
||||
}
|
||||
|
||||
for (int light = 0; light < LIGHTS_COUNT; light++)
|
||||
{
|
||||
if (i < 30)
|
||||
{
|
||||
// there was a short press
|
||||
light_state[light] = true;
|
||||
}
|
||||
else {
|
||||
// there was a long press
|
||||
bri[light] += 56;
|
||||
if (bri[light] > 254)
|
||||
{
|
||||
// don't increase the brightness more then maximum value
|
||||
bri[light] = 254;
|
||||
}
|
||||
}
|
||||
process_lightdata(light, 4);
|
||||
}
|
||||
|
||||
} else if (digitalRead(button2_pin) == HIGH)
|
||||
{
|
||||
int i = 0;
|
||||
while (digitalRead(button2_pin) == HIGH && i < 30)
|
||||
{
|
||||
delay(20);
|
||||
i++;
|
||||
}
|
||||
|
||||
for (int light = 0; light < LIGHTS_COUNT; light++)
|
||||
{
|
||||
if (i < 30)
|
||||
{
|
||||
// there was a short press
|
||||
light_state[light] = false;
|
||||
|
||||
} else {
|
||||
// there was a long press
|
||||
bri[light] -= 56;
|
||||
if (bri[light] < 1)
|
||||
{
|
||||
// don't decrease the brightness less than minimum value.
|
||||
bri[light] = 1;
|
||||
}
|
||||
}
|
||||
process_lightdata(light, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
EEPROM.begin(512);
|
||||
|
||||
Serial.begin(SERIAL_BAUD_RATE);
|
||||
|
||||
#ifdef USE_STATIC_IP
|
||||
WiFi.config(strip_ip, gateway_ip, subnet_mask);
|
||||
#endif
|
||||
|
||||
for (uint8_t light = 0; light < LIGHTS_COUNT; light++)
|
||||
{
|
||||
apply_scene(EEPROM.read(EEPROM_SCENE_ADDRESS), light);
|
||||
step_level[light] = bri[light] / 150.0;
|
||||
|
||||
if (EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS) == LAST_STATE_STARTUP_LIGHT_LAST_STATE ||
|
||||
(EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS) == LAST_STATE_STARTUP_LIGHT_ON_STATE &&
|
||||
EEPROM.read(EEPROM_LAST_STATE_ADDRESS + light) == LIGHT_STATE_ON))
|
||||
{
|
||||
light_state[light] = true; // set the light state to on
|
||||
}
|
||||
}
|
||||
|
||||
if (EEPROM.read(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS) == TIMING_CONTROL_DISABLED)
|
||||
{
|
||||
tc_enabled = TIMING_CONTROL_DISABLED;
|
||||
|
||||
} else if (EEPROM.read(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS) == TIMING_CONTROL_ENABLED)
|
||||
{
|
||||
tc_enabled = TIMING_CONTROL_ENABLED;
|
||||
|
||||
} else {
|
||||
|
||||
EEPROM.write(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS, TIMING_CONTROL_DISABLED);
|
||||
EEPROM.commit();
|
||||
tc_enabled = TIMING_CONTROL_DISABLED;
|
||||
}
|
||||
|
||||
if (EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS) == 255)
|
||||
{
|
||||
// set the default value on uninitialized EEPROM
|
||||
EEPROM.write(EEPROM_LAST_STATE_STARTUP_ADDRESS, 0);
|
||||
EEPROM.commit();
|
||||
}
|
||||
|
||||
#ifdef USE_STATIC_IP
|
||||
if (EEPROM.read(EEPROM_DYNAMIC_IP_ADDRESS) == 255)
|
||||
{
|
||||
EEPROM.write(EEPROM_DYNAMIC_IP_ADDRESS, 0);
|
||||
EEPROM.commit();
|
||||
}
|
||||
#else
|
||||
if (EEPROM.read(EEPROM_DYNAMIC_IP_ADDRESS) == 255)
|
||||
{
|
||||
EEPROM.write(EEPROM_DYNAMIC_IP_ADDRESS, 1);
|
||||
EEPROM.commit();
|
||||
}
|
||||
#endif
|
||||
|
||||
for (int j = 0; j < 200; j++)
|
||||
{
|
||||
lightEngine();
|
||||
}
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFiManager wifiManager;
|
||||
wifiManager.setConfigPortalTimeout(120);
|
||||
wifiManager.autoConnect(light_name);
|
||||
|
||||
IPAddress myIP = WiFi.localIP();
|
||||
Serial.print("IP: ");
|
||||
Serial.println(myIP);
|
||||
|
||||
if (!light_state[0])
|
||||
{
|
||||
// Show that we are connected
|
||||
analogWrite(pins[0], 100);
|
||||
delay(500);
|
||||
analogWrite(pins[0], 0);
|
||||
}
|
||||
|
||||
WiFi.macAddress(mac);
|
||||
|
||||
pinMode(LED_BUILTIN, OUTPUT); // Initialize the LED_BUILTIN pin as an output
|
||||
digitalWrite(LED_BUILTIN, HIGH); // Turn the LED off by making the voltage HIGH
|
||||
|
||||
httpUpdateServer.setup(&server); // start http server
|
||||
|
||||
if (use_hardware_switch == true)
|
||||
{
|
||||
pinMode(button1_pin, INPUT);
|
||||
pinMode(button2_pin, INPUT);
|
||||
}
|
||||
|
||||
server.on("/state", HTTP_PUT, []()
|
||||
{ // HTTP PUT request used to set a new light state
|
||||
DynamicJsonDocument root(1024);
|
||||
DeserializationError error = deserializeJson(root, server.arg("plain"));
|
||||
|
||||
if (error)
|
||||
{
|
||||
server.send(404, "text/plain", "FAIL. " + server.arg("plain"));
|
||||
} else {
|
||||
|
||||
for (JsonPair state : root.as<JsonObject>())
|
||||
{
|
||||
const char* key = state.key().c_str();
|
||||
int light = atoi(key) - 1;
|
||||
JsonObject values = state.value();
|
||||
int transitiontime = 4;
|
||||
|
||||
if (values.containsKey("on"))
|
||||
{
|
||||
if (values["on"])
|
||||
{
|
||||
light_state[light] = true;
|
||||
if (EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS) == LAST_STATE_STARTUP_LIGHT_LAST_STATE && EEPROM.read(EEPROM_LAST_STATE_ADDRESS + light) == LIGHT_STATE_OFF)
|
||||
{
|
||||
EEPROM.write(EEPROM_LAST_STATE_ADDRESS + light, LIGHT_STATE_ON);
|
||||
}
|
||||
} else {
|
||||
light_state[light] = false;
|
||||
if (EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS) == LAST_STATE_STARTUP_LIGHT_LAST_STATE && EEPROM.read(EEPROM_LAST_STATE_ADDRESS + light) == LIGHT_STATE_ON)
|
||||
{
|
||||
EEPROM.write(EEPROM_LAST_STATE_ADDRESS + light, LIGHT_STATE_OFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (values.containsKey("bri"))
|
||||
{
|
||||
bri[light] = values["bri"];
|
||||
}
|
||||
|
||||
if (values.containsKey("bri_inc"))
|
||||
{
|
||||
bri[light] += (int) values["bri_inc"];
|
||||
if (bri[light] > 255) bri[light] = 255;
|
||||
else if (bri[light] < 1) bri[light] = 1;
|
||||
}
|
||||
|
||||
if (values.containsKey("transitiontime"))
|
||||
{
|
||||
transitiontime = values["transitiontime"];
|
||||
}
|
||||
process_lightdata(light, transitiontime);
|
||||
}
|
||||
String output;
|
||||
serializeJson(root, output);
|
||||
server.send(200, "text/plain", output);
|
||||
}
|
||||
});
|
||||
|
||||
server.on("/state", HTTP_GET, []()
|
||||
{ // HTTP GET request used to fetch current light state
|
||||
uint8_t light = server.arg("light").toInt() - 1;
|
||||
DynamicJsonDocument root(1024);
|
||||
root["on"] = light_state[light];
|
||||
root["bri"] = bri[light];
|
||||
String output;
|
||||
serializeJson(root, output);
|
||||
server.send(200, "text/plain", output);
|
||||
});
|
||||
|
||||
server.on("/detect", []()
|
||||
{ // HTTP GET request used to discover the light type
|
||||
char macString[32] = {0};
|
||||
sprintf(macString, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
DynamicJsonDocument root(1024);
|
||||
root["name"] = light_name;
|
||||
root["lights"] = LIGHTS_COUNT;
|
||||
root["protocol"] = "native_multi";
|
||||
root["modelid"] = "LWB010";
|
||||
root["type"] = "dimmable_light";
|
||||
root["mac"] = String(macString);
|
||||
root["version"] = LIGHT_VERSION;
|
||||
String output;
|
||||
serializeJson(root, output);
|
||||
server.send(200, "text/plain", output);
|
||||
});
|
||||
|
||||
server.on("/", []()
|
||||
{
|
||||
float transitiontime = 4;
|
||||
|
||||
if (server.hasArg("startup"))
|
||||
{
|
||||
int startup = server.arg("startup").toInt();
|
||||
EEPROM.write(EEPROM_LAST_STATE_STARTUP_ADDRESS, startup);
|
||||
EEPROM.commit();
|
||||
Serial.print("Startup behavior set to "); Serial.println(EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS));
|
||||
}
|
||||
|
||||
if (server.hasArg("tc"))
|
||||
{
|
||||
if (server.arg("tc") == "true")
|
||||
{
|
||||
if (tc_enabled == TIMING_CONTROL_DISABLED)
|
||||
{
|
||||
tc_enabled = TIMING_CONTROL_ENABLED;
|
||||
EEPROM.write(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS, TIMING_CONTROL_ENABLED);
|
||||
EEPROM.commit();
|
||||
Serial.print("Timing control = "); Serial.println(EEPROM.read(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS));
|
||||
}
|
||||
|
||||
} else { // tc is set to false or something else
|
||||
|
||||
if (tc_enabled == TIMING_CONTROL_ENABLED)
|
||||
{
|
||||
tc_enabled = TIMING_CONTROL_DISABLED;
|
||||
EEPROM.write(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS, TIMING_CONTROL_DISABLED);
|
||||
EEPROM.commit();
|
||||
Serial.print("Timing control = "); Serial.println(EEPROM.read(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (server.hasArg("scene"))
|
||||
{
|
||||
scene = server.arg("scene").toInt();
|
||||
EEPROM.write(EEPROM_SCENE_ADDRESS, scene);
|
||||
EEPROM.commit();
|
||||
Serial.print("Scene set to "); Serial.println(EEPROM.read(EEPROM_SCENE_ADDRESS));
|
||||
|
||||
}
|
||||
|
||||
for (int light = 0; light < LIGHTS_COUNT; light++)
|
||||
{
|
||||
|
||||
if (server.hasArg("bri" + (String)light))
|
||||
{
|
||||
bri[light] = server.arg("bri" + (String)light).toInt();
|
||||
Serial.print("Brightness set to "); Serial.println(bri[light]);
|
||||
|
||||
}
|
||||
|
||||
if (server.hasArg("on" + (String)light))
|
||||
{
|
||||
if (server.arg("on" + (String)light) == "true")
|
||||
{
|
||||
light_state[light] = true;
|
||||
if (EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS) == 0 && EEPROM.read(EEPROM_LAST_STATE_ADDRESS + light) == 0)
|
||||
{
|
||||
EEPROM.write(EEPROM_LAST_STATE_ADDRESS + light, LIGHT_STATE_ON);
|
||||
}
|
||||
|
||||
} else {
|
||||
light_state[light] = false;
|
||||
if (EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS) == 0 && EEPROM.read(EEPROM_LAST_STATE_ADDRESS + light) == 1)
|
||||
{
|
||||
EEPROM.write(EEPROM_LAST_STATE_ADDRESS + light, LIGHT_STATE_OFF);
|
||||
}
|
||||
}
|
||||
|
||||
Serial.print("Light "); Serial.print(light); Serial.print(" state set to "); Serial.println(light_state[light]);
|
||||
|
||||
EEPROM.commit();
|
||||
|
||||
}
|
||||
|
||||
if (server.hasArg("alert")) {
|
||||
|
||||
if (light_state[light])
|
||||
{
|
||||
current_bri[light] = 0;
|
||||
} else {
|
||||
current_bri[light] = 255;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (light_state[light])
|
||||
{
|
||||
step_level[light] = ((float)bri[light] - current_bri[light]) / transitiontime;
|
||||
|
||||
} else {
|
||||
step_level[light] = current_bri[light] / transitiontime;
|
||||
}
|
||||
}
|
||||
|
||||
if (server.hasArg("resettc"))
|
||||
{
|
||||
tc_write_default();
|
||||
ESP.reset();
|
||||
}
|
||||
|
||||
if (server.hasArg("reset"))
|
||||
{
|
||||
ESP.reset();
|
||||
}
|
||||
|
||||
String http_content = "<!doctype html>";
|
||||
http_content += "<html>";
|
||||
http_content += "<head>";
|
||||
http_content += "<meta charset=\"utf-8\">";
|
||||
http_content += "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">";
|
||||
http_content += "<meta http-equiv=\"refresh\" content=\"15\">"; // Reload the page every 15 seconds automatically
|
||||
http_content += "<title>Light Setup</title>";
|
||||
http_content += "<link rel=\"stylesheet\" href=\"https://unpkg.com/purecss@0.6.2/build/pure-min.css\">";
|
||||
http_content += "</head>";
|
||||
http_content += "<body>";
|
||||
http_content += "<fieldset>";
|
||||
http_content += "<h3>Setup of " + (String)LIGHTS_COUNT + " light"; if (LIGHTS_COUNT > 1) http_content += "'s"; http_content += "</h3>";
|
||||
|
||||
http_content += "<form class=\"pure-form pure-form-aligned\" action=\"/\" method=\"post\">";
|
||||
|
||||
// timer data processing, startup state and scene for all of the lights
|
||||
http_content += "<div class=\"pure-control-group\">";
|
||||
http_content += "<label for=\"startup\">Startup</label>";
|
||||
http_content += "<select onchange=\"this.form.submit()\" id=\"startup\" name=\"startup\">";
|
||||
int ls_val = EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS);
|
||||
http_content += "<option "; if (ls_val == LAST_STATE_STARTUP_LIGHT_LAST_STATE) http_content += "selected=\"selected\""; http_content += " value=\"0\">Last state</option>";
|
||||
http_content += "<option "; if (ls_val == LAST_STATE_STARTUP_LIGHT_ON_STATE) http_content += "selected=\"selected\""; http_content += " value=\"1\">On</option>";
|
||||
http_content += "<option "; if (ls_val == LAST_STATE_STARTUP_LIGHT_OFF_STATE) http_content += "selected=\"selected\""; http_content += " value=\"2\">Off</option>";
|
||||
http_content += "</select>";
|
||||
http_content += "</div>";
|
||||
|
||||
http_content += "<div class=\"pure-control-group\">";
|
||||
http_content += "<label for=\"scene\">Scene</label>";
|
||||
http_content += "<select onchange = \"this.form.submit()\" id=\"scene\" name=\"scene\">";
|
||||
int sc_val = EEPROM.read(EEPROM_SCENE_ADDRESS);
|
||||
http_content += "<option "; if (sc_val == SCENE_RELEAX) http_content += "selected=\"selected\""; http_content += " value=\"0\">Relax</option>";
|
||||
http_content += "<option "; if (sc_val == SCENE_BRIGHT) http_content += "selected=\"selected\""; http_content += " value=\"1\">Bright</option>";
|
||||
http_content += "<option "; if (sc_val == SCENE_NIGHTLY) http_content += "selected=\"selected\""; http_content += " value=\"2\">Night</option>";
|
||||
http_content += "</select>";
|
||||
http_content += "</div>";
|
||||
|
||||
http_content += "<div class=\"pure-control-group\">";
|
||||
http_content += "<label for=\"power\"><strong>Enable timing control</strong></label>";
|
||||
int tc_val = EEPROM.read(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS);
|
||||
http_content += "<a class=\"pure-button"; if (tc_val == TIMING_CONTROL_ENABLED) http_content += " pure-button-primary"; http_content += "\" href=\"/?tc=true\">ON</a>";
|
||||
http_content += "<a class=\"pure-button"; if (tc_val == TIMING_CONTROL_DISABLED) http_content += " pure-button-primary"; http_content += "\" href=\"/?tc=false\">OFF</a>";
|
||||
http_content += "</div>";
|
||||
http_content += "<br>";
|
||||
|
||||
// Light control
|
||||
|
||||
for (uint8 light_num = 0; light_num < LIGHTS_COUNT; light_num++)
|
||||
{
|
||||
http_content += "<h3>Light " + (String)(light_num+1) + "</h3>";
|
||||
http_content += "<div class=\"pure-control-group\">";
|
||||
http_content += "<label for=\"power\"><strong>Power</strong></label>";
|
||||
http_content += "<a class=\"pure-button"; if ( light_state[light_num]) http_content += " pure-button-primary"; http_content += "\" href=\"/?on" + (String)light_num + "=true\">ON</a>";
|
||||
http_content += "<a class=\"pure-button"; if (!light_state[light_num]) http_content += " pure-button-primary"; http_content += "\" href=\"/?on" + (String)light_num + "=false\">OFF</a>";
|
||||
http_content += "</div>";
|
||||
http_content += "<div class=\"pure-control-group\">";
|
||||
http_content += "<label for=\"state\"><strong>State</strong></label>";
|
||||
http_content += "</div>";
|
||||
http_content += "<div class=\"pure-control-group\">";
|
||||
http_content += "<label for=\"bri\"" + (String)light_num + ">Bri</label>";
|
||||
http_content += "<input id=\"bri" + (String)light_num + "\" name=\"bri" + (String)light_num + "\" type=\"range\" min=\"0\" max=\"100\" value=\"" + (String)bri[light_num] + "\">";
|
||||
http_content += "<label id=\"bri" + (String)light_num + "_val\" name=\"bri" + (String)light_num + "\">" + (String)bri[light_num] + "</label>";
|
||||
http_content += "<script>";
|
||||
http_content += "var slider" + (String)light_num + " = document.getElementById(\"bri" + (String)light_num + "\");";
|
||||
http_content += "var output" + (String)light_num + " = document.getElementById(\"bri" + (String)light_num + "\_val\");";
|
||||
http_content += "output" + (String)light_num + ".innerHTML = slider" + (String)light_num + ".value;";
|
||||
http_content += "slider" + (String)light_num + ".oninput = function() {";
|
||||
http_content += "output" + (String)light_num + ".innerHTML = this.value;";
|
||||
http_content += "}";
|
||||
http_content += "</script>";
|
||||
http_content += "</div>";
|
||||
}
|
||||
|
||||
// Wifi settings
|
||||
http_content += "<br>";
|
||||
http_content += "<h3>Wifi</h3>";
|
||||
http_content += "<div class=\"pure-control-group\">";
|
||||
http_content += "<label for=\"ip\">SSID</label>";
|
||||
http_content += "<input id=\"ssid\" name=\"ssid\" type=\"text\" value=\"" + WiFi.SSID() + "\">";
|
||||
http_content += "</div>";
|
||||
http_content += "<div class=\"pure-control-group\">";
|
||||
http_content += "<label for=\"wpw\">Passphrase</label>";
|
||||
http_content += "<input id=\"wpw\" name=\"wpw\" type=\"text\" placeholder=\"1234password\">";
|
||||
http_content += "</div>";
|
||||
|
||||
// Network settings
|
||||
uint8_t dip = EEPROM.read(EEPROM_DYNAMIC_IP_ADDRESS);
|
||||
http_content += "<br>";
|
||||
http_content += "<h3>Network</h3>";
|
||||
http_content += "<div class=\"pure-control-group\">";
|
||||
http_content += "<label for=\"dip\"><strong>Dynamic-IP</strong></label>";
|
||||
http_content += "<a class=\"pure-button"; if ( dip) http_content += " pure-button-primary"; http_content += "\" href=\"/?dip=true\">ON</a>";
|
||||
http_content += "<a class=\"pure-button"; if (!dip) http_content += " pure-button-primary"; http_content += "\" href=\"/?dip=false\">OFF</a>";
|
||||
http_content += "</div>";
|
||||
|
||||
if (dip == 0)
|
||||
{
|
||||
http_content += "<div class=\"pure-control-group\">";
|
||||
http_content += "<label for=\"ip\">IP</label>";
|
||||
http_content += "<input id=\"ip\" name=\"ip\" type=\"text\" value=\"" + WiFi.localIP().toString() + "\">";
|
||||
http_content += "</div>";
|
||||
http_content += "<div class=\"pure-control-group\">";
|
||||
http_content += "<label for=\"gwip\">Gateway IP</label>";
|
||||
http_content += "<input id=\"gwip\" name=\"gwip\" type=\"text\" value=\"" + WiFi.gatewayIP().toString() + "\">";
|
||||
http_content += "</div>";
|
||||
http_content += "<div class=\"pure-control-group\">";
|
||||
http_content += "<label for=\"ip\">Netmask</label>";
|
||||
http_content += "<input id=\"netmask\" name=\"netmas\" type=\"text\" value=\"" + WiFi.subnetMask().toString() + "\">";
|
||||
http_content += "</div>";
|
||||
}
|
||||
|
||||
http_content += "<div class=\"pure-controls\">";
|
||||
http_content += "<span class=\"pure-form-message\"><a href=\"/?alert=1\">alert</a> <a href=\"/?reset=1\">reset</a> <a href=\"/?resettc\">reset timing control data</a> <a href=\"/update\">update</a></span>";
|
||||
http_content += "<label for=\"cb\" class=\"pure-checkbox\">";
|
||||
http_content += "</label>";
|
||||
http_content += "<button type=\"submit\" class=\"pure-button pure-button-primary\">Save</button>";
|
||||
http_content += "</div>";
|
||||
|
||||
http_content += "</fieldset>";
|
||||
http_content += "</form>";
|
||||
http_content += "</body>";
|
||||
http_content += "</html>";
|
||||
|
||||
server.send(200, "text/html", http_content);
|
||||
|
||||
});
|
||||
|
||||
server.on("/reset", []()
|
||||
{ // trigger manual reset
|
||||
server.send(200, "text/html", "reset");
|
||||
delay(1000);
|
||||
ESP.restart();
|
||||
});
|
||||
|
||||
server.onNotFound(handleNotFound);
|
||||
|
||||
tc_init();
|
||||
|
||||
server.begin();
|
||||
} // end of setup
|
||||
|
||||
void loop()
|
||||
{
|
||||
server.handleClient();
|
||||
lightEngine();
|
||||
|
||||
if (tc_enabled == TIMING_CONTROL_ENABLED)
|
||||
{
|
||||
tc_update();
|
||||
}
|
||||
}
|
BIN
firmware/images/Over.jpg
Executable file
BIN
firmware/images/Over.jpg
Executable file
Binary file not shown.
After Width: | Height: | Size: 112 KiB |
BIN
firmware/images/Under.jpg
Executable file
BIN
firmware/images/Under.jpg
Executable file
Binary file not shown.
After Width: | Height: | Size: 94 KiB |
219
firmware/timing_control.ino
Normal file
219
firmware/timing_control.ino
Normal file
|
@ -0,0 +1,219 @@
|
|||
|
||||
#include <NTPClient.h>
|
||||
#include <WiFiUdp.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
//***********************************//
|
||||
|
||||
// 7 byte
|
||||
#define TIMER_DATA_ENSTATE 0
|
||||
#define TIMER_DATA_HH 1
|
||||
#define TIMER_DATA_MM 2
|
||||
#define TIMER_DATA_CH1 3 // the brightness of the channel
|
||||
#define TIMER_DATA_CH2 4 // the brightness of the channel
|
||||
#define TIMER_DATA_CH3 5 // the brightness of the channel
|
||||
#define TIMER_DATA_CH4 6 // the brightness of the channel
|
||||
|
||||
#define LENGTH_OF_TIMER_DATA_BLOCK (TIMER_DATA_CH4 + 1)
|
||||
#define NUMBER_OF_TIMER_DATA_BLOCKS 10
|
||||
|
||||
#define ENSTATE_ENABLED 1
|
||||
#define ENSTATE_DISABLED 0
|
||||
|
||||
//***********************************//
|
||||
|
||||
const long utcOffsetInSeconds = 3600; // Europe/Berlin (not summer time)
|
||||
|
||||
// Define NTP Client to get time
|
||||
WiFiUDP ntpUDP;
|
||||
NTPClient timeClient(ntpUDP, "pool.ntp.org", utcOffsetInSeconds);
|
||||
|
||||
static uint32_t tc_last_time_check;
|
||||
|
||||
struct tc_data_st {
|
||||
uint8_t enstate;
|
||||
uint8_t hh;
|
||||
uint8_t mm;
|
||||
uint8_t ch1;
|
||||
uint8_t ch2;
|
||||
uint8_t ch3;
|
||||
uint8_t ch4;
|
||||
};
|
||||
|
||||
struct tc_data_st tc_data[LIGHTS_COUNT];
|
||||
|
||||
uint8_t example_timer_data_block[] = {
|
||||
// state hour min ch1 ch2 ch3 ch3
|
||||
ENSTATE_ENABLED, 8, 0, 0, 0, 0, 0, // off
|
||||
ENSTATE_ENABLED, 8, 30, 25, 0, 0, 0, // 10% ch1 blues
|
||||
ENSTATE_ENABLED, 9, 0, 25, 0, 25, 0, // 10% all blues
|
||||
ENSTATE_ENABLED, 13, 0, 205, 205, 205, 205, // 80% all
|
||||
ENSTATE_ENABLED, 18, 0, 50, 50, 50, 50, // 20% blue
|
||||
ENSTATE_ENABLED, 20, 0, 50, 0, 50, 0, // 20% all blues
|
||||
ENSTATE_ENABLED, 20, 0, 25, 0, 0, 0, // 10% ch1 blues
|
||||
ENSTATE_ENABLED, 21, 0, 0, 0, 0, 0, // 0% all
|
||||
ENSTATE_DISABLED, 20, 0, 0, 0, 0, 0, // disabled
|
||||
ENSTATE_DISABLED, 20, 0, 0, 0, 0, 0, // disabled
|
||||
};
|
||||
|
||||
//***********************************//
|
||||
|
||||
void tc_init()
|
||||
{
|
||||
while ( WiFi.status() != WL_CONNECTED )
|
||||
{
|
||||
delay ( 500 );
|
||||
Serial.print ( "." );
|
||||
}
|
||||
|
||||
timeClient.begin();
|
||||
|
||||
if (tc_check_no_data_block() == true)
|
||||
{
|
||||
//Serial.println("TC: No data block found, writing example block to EEPROM.");
|
||||
tc_write_default();
|
||||
}
|
||||
|
||||
//Serial.println("TC: Read data block from eeprom");
|
||||
tc_readConfig();
|
||||
|
||||
tc_last_time_check = millis();
|
||||
}
|
||||
|
||||
void tc_readConfig()
|
||||
{
|
||||
for (uint8_t i = 0; i < NUMBER_OF_TIMER_DATA_BLOCKS; i++)
|
||||
{
|
||||
/*Serial.print("Reading from address: "); Serial.print(EEPROM_TIMING_DATA_ADDRESS);
|
||||
Serial.print(" + (");
|
||||
Serial.print(i);
|
||||
Serial.print(" * ");
|
||||
Serial.print(LENGTH_OF_TIMER_DATA_BLOCK);
|
||||
Serial.print(") + ");
|
||||
Serial.println(TIMER_DATA_ENSTATE);*/
|
||||
|
||||
tc_data[i].enstate = EEPROM.read(EEPROM_TIMING_DATA_ADDRESS + i * LENGTH_OF_TIMER_DATA_BLOCK + TIMER_DATA_ENSTATE);
|
||||
tc_data[i].hh = EEPROM.read(EEPROM_TIMING_DATA_ADDRESS + i * LENGTH_OF_TIMER_DATA_BLOCK + TIMER_DATA_HH);
|
||||
tc_data[i].mm = EEPROM.read(EEPROM_TIMING_DATA_ADDRESS + i * LENGTH_OF_TIMER_DATA_BLOCK + TIMER_DATA_MM);
|
||||
tc_data[i].ch1 = EEPROM.read(EEPROM_TIMING_DATA_ADDRESS + i * LENGTH_OF_TIMER_DATA_BLOCK + TIMER_DATA_CH1);
|
||||
tc_data[i].ch2 = EEPROM.read(EEPROM_TIMING_DATA_ADDRESS + i * LENGTH_OF_TIMER_DATA_BLOCK + TIMER_DATA_CH2);
|
||||
tc_data[i].ch3 = EEPROM.read(EEPROM_TIMING_DATA_ADDRESS + i * LENGTH_OF_TIMER_DATA_BLOCK + TIMER_DATA_CH3);
|
||||
tc_data[i].ch4 = EEPROM.read(EEPROM_TIMING_DATA_ADDRESS + i * LENGTH_OF_TIMER_DATA_BLOCK + TIMER_DATA_CH4);
|
||||
|
||||
/*Serial.print("data block: "); Serial.print(i);
|
||||
Serial.print(" @ ");
|
||||
Serial.println((EEPROM_TIMING_DATA_ADDRESS + (i * LENGTH_OF_TIMER_DATA_BLOCK) + TIMER_DATA_ENSTATE));
|
||||
|
||||
Serial.print(" es: "); Serial.println(tc_data[i].enstate);
|
||||
Serial.print(" hh: "); Serial.println(tc_data[i].hh);
|
||||
Serial.print(" mm: "); Serial.println(tc_data[i].mm);
|
||||
Serial.print(" ch1: "); Serial.println(tc_data[i].ch1);
|
||||
Serial.print(" ch2: "); Serial.println(tc_data[i].ch2);
|
||||
Serial.print(" ch3: "); Serial.println(tc_data[i].ch3);
|
||||
Serial.print(" ch4: "); Serial.println(tc_data[i].ch4);*/
|
||||
}
|
||||
}
|
||||
|
||||
void tc_updateTime()
|
||||
{
|
||||
timeClient.update();
|
||||
|
||||
Serial.print("Local time: ");
|
||||
Serial.print(timeClient.getHours());
|
||||
Serial.print(":");
|
||||
Serial.println(timeClient.getMinutes());
|
||||
|
||||
}
|
||||
|
||||
void tc_update()
|
||||
{
|
||||
uint8_t target_data_block = 255;
|
||||
|
||||
if (millis() > tc_last_time_check + TC_TIME_CHECK_INTERVAL_MS)
|
||||
{
|
||||
tc_last_time_check = millis();
|
||||
tc_updateTime();
|
||||
}
|
||||
|
||||
if ((timeClient.getMinutes() % 10) != 0)
|
||||
{
|
||||
return; // only run every 10 minutes
|
||||
}
|
||||
|
||||
//tc_updateTime();
|
||||
|
||||
// 2. find current active time slot
|
||||
for (uint8_t i = 0; i < NUMBER_OF_TIMER_DATA_BLOCKS-1; i++)
|
||||
{
|
||||
if (tc_data[i].hh == timeClient.getHours() && tc_data[i].mm == timeClient.getMinutes() && tc_data[i].enstate == ENSTATE_ENABLED)
|
||||
{ // we have a new time data slot reached
|
||||
for (uint8_t j = i+1; j < NUMBER_OF_TIMER_DATA_BLOCKS; j++)
|
||||
{ // search for the next enabled successor
|
||||
if (tc_data[j].enstate == ENSTATE_ENABLED)
|
||||
{
|
||||
target_data_block = j; // get the next block to activate
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (target_data_block == 255)
|
||||
{
|
||||
// no new successor found
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. set the channels brightness
|
||||
bri[0] = tc_data[target_data_block].ch1;
|
||||
bri[1] = tc_data[target_data_block].ch2;
|
||||
bri[2] = tc_data[target_data_block].ch3;
|
||||
bri[3] = tc_data[target_data_block].ch4;
|
||||
|
||||
// 4. enable/disable the lights
|
||||
light_state[0] = tc_data[target_data_block].ch1 > 0 ? true : false;
|
||||
light_state[0] = tc_data[target_data_block].ch2 > 0 ? true : false;
|
||||
light_state[0] = tc_data[target_data_block].ch3 > 0 ? true : false;
|
||||
light_state[0] = tc_data[target_data_block].ch4 > 0 ? true : false;
|
||||
|
||||
// 5. set the transition time
|
||||
int t_time = 0;
|
||||
if (target_data_block > 0)
|
||||
{
|
||||
t_time = (tc_data[target_data_block].hh * 60 * 30) - (tc_data[target_data_block-1].hh * 60 * 30); // hours as seconds from now on to the next enabled block
|
||||
t_time += (tc_data[target_data_block].mm * 60) - (tc_data[target_data_block-1].mm * 60); // add the left over seconds to the next enabled block
|
||||
}
|
||||
transitiontime[0] = t_time;
|
||||
transitiontime[1] = t_time;
|
||||
transitiontime[2] = t_time;
|
||||
transitiontime[3] = t_time;
|
||||
|
||||
}
|
||||
|
||||
void tc_write_default()
|
||||
{
|
||||
//Serial.print("-----\nWrite data block starting from address EEPROM_TIMING_DATA_ADDRESS = "); Serial.println(EEPROM_TIMING_DATA_ADDRESS);
|
||||
for (int i = 0; i < NUMBER_OF_TIMER_DATA_BLOCKS * LENGTH_OF_TIMER_DATA_BLOCK; i++)
|
||||
{
|
||||
//Serial.print(EEPROM_TIMING_DATA_ADDRESS + i); Serial.print(" <= "); Serial.print(example_timer_data_block[i]); Serial.print(" <= ");
|
||||
|
||||
EEPROM.write(EEPROM_TIMING_DATA_ADDRESS + i, example_timer_data_block[i]);
|
||||
EEPROM.commit();
|
||||
|
||||
//Serial.println(EEPROM.read(EEPROM_TIMING_DATA_ADDRESS + i));
|
||||
}
|
||||
//Serial.println("-----");
|
||||
}
|
||||
|
||||
bool tc_check_no_data_block()
|
||||
{
|
||||
//Serial.print("Check data block address EEPROM_TIMING_DATA_ADDRESS = "); Serial.println(EEPROM_TIMING_DATA_ADDRESS);
|
||||
uint8_t e = EEPROM.read(EEPROM_TIMING_DATA_ADDRESS);
|
||||
//Serial.println(e);
|
||||
|
||||
if (e == 255)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
35
lp3ctrl.sh
Executable file
35
lp3ctrl.sh
Executable file
|
@ -0,0 +1,35 @@
|
|||
#!/bin/bash
|
||||
|
||||
LIGHT=$1
|
||||
STATE=$2
|
||||
BRI=$3
|
||||
|
||||
echo ---
|
||||
curl http://192.168.0.26/state\?light=1
|
||||
echo
|
||||
curl http://192.168.0.26/state\?light=2
|
||||
echo
|
||||
curl http://192.168.0.26/state\?light=3
|
||||
echo
|
||||
curl http://192.168.0.26/state\?light=4
|
||||
echo
|
||||
echo ---
|
||||
|
||||
if [ "$1" ]; then
|
||||
curl -i -X PUT -d '{ "'$LIGHT'": { "on": '$STATE', "bri": '$BRI' } }' http://192.168.0.26/state
|
||||
|
||||
echo
|
||||
echo ---
|
||||
|
||||
curl http://192.168.0.26/state\?light=1
|
||||
echo
|
||||
curl http://192.168.0.26/state\?light=2
|
||||
echo
|
||||
curl http://192.168.0.26/state\?light=3
|
||||
echo
|
||||
curl http://192.168.0.26/state\?light=4
|
||||
|
||||
echo
|
||||
echo ---
|
||||
fi
|
||||
|
Loading…
Reference in a new issue