Closed erazor666 closed 6 years ago
Would like to know too. Like a button shield on a wemos for sk6812. Definitely WAF (wife accaptence factor) guaranteed!
It is possible, but question is how many gpio pins must be sacrificed for this future. We can use one or two. With one the behavior must be:
with two:
The first one sounds best of course. This would be a definite WAF thing, however gpio should not be wasted lightly that is true. A rotary encoder "wheel" combined with a button would also be the most elegant solution.i'm guessing that would use 2 gpios. The button uses one and the "wheel' uses one. Guess this could be an alternate sketch or option you enable in the start of it. Maybe it sounds like a step backwards since this is wifi based and all. On some lights i build this option would be handy.
Until you pasted you comment i made something quick (i take some code from switch sketch). The new code portion start from else
so you can identify the place.
if (in_transition) {
delay(6);
in_transition = false;
} else {
if (digitalRead(button1_pin) == HIGH) {
int i = 0;
while (digitalRead(button1_pin) == HIGH && i < 30) {
delay(20);
i++;
}
for (int light = 0; light < lightsCount; 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] > 255) {
// don't increase the brightness more then maximum value
bri[light] = 255;
}
}
}
}
if (digitalRead(button2_pin) == HIGH) {
int i = 0;
while (digitalRead(button2_pin) == HIGH && i < 30) {
delay(20);
i++;
}
for (int light = 0; light < lightsCount; 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;
}
}
}
}
}
i believe we can use TX and RX pins, that are available on all lights, except neopixel ones where we have all other GPIO pins available.
Don't forget to declare in header:
and on last stage of setup phase (becasue wifi manager initiate the pins for serial debugging) add:
pinMode(button1_pin, INPUT);
pinMode(button2_pin, INPUT);
Woha, you are a real magician. I will test this and report back, I have a testing rig made just yesterday :)
Oh and by the way, will this be reported to the bridge? So it didplays the current dim level and such? (This is a luxury thing of course, but very neat if it does)
Must be, because when hue application made a request to get lights status, the emulator will fetch all lights and update its config.
BTW if brightness change steps are too big, just replace +/- 56 value with something smaller.
I have a switch that commands a ww strip via http commands (espeasy). The bridge picks up the state change (seen in the official app) however HA does not pick up that state change when i use the switch. This can be a HA issue of course, just dont know where to attack it yet...
Not really, maybe HA grab entire configuration from a /api/ request every time, and when this happen the function to grab lights status is not executed. Try this:
under the line:
if len(url_pices) == 3 or (len(url_pices) == 4 and url_pices[3] == ""): #print entire config
add this line:
syncWithLights()
Any feedback? Today i have some free time and i can do things more faster.
i'm gonna start the testing of the local switch now. i'll edit when i get it going edit1: It compiles with the added code at least, time for some wires edit2: they sorta work but not; i can find them in hue app. however they come up as unreachable and the button does not do anything at the moment. I will post complete code
Oh shit, i had a Common Ground not present, gotta test again
Didnt seem to fix it, when i press them in the light setup dialog in hue app, they are supposed to flash once, they just turn on and never off again. Also they light up at full brightness and slowly just fade out without input. Also seem really "laggy" and these lights are usually very responsive. Seems like its running the button code by itself, i can see it stepping down the dim level without input, and still show as 'unreachable' in hue app.
Button pins shouldnt be 1 & 2 instead of 1 & 3?
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>
#include <EEPROM.h>
#include "pwm.c"
#define button1_pin 1
#define button2_pin 3
#define lightsCount 2
const uint32_t period = 1024;
//define pins
uint32 io_info[lightsCount][3] = {
// MUX, FUNC, PIN
//{PERIPHS_IO_MUX_MTDI_U, FUNC_GPIO12, 12},
//{PERIPHS_IO_MUX_MTDO_U, FUNC_GPIO15, 15},
//{PERIPHS_IO_MUX_MTCK_U, FUNC_GPIO13, 13},
//{PERIPHS_IO_MUX_MTMS_U, FUNC_GPIO14, 14},
{PERIPHS_IO_MUX_GPIO4_U, FUNC_GPIO4 , 4},
{PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5 , 5},
};
// initial duty: all off
uint32 pwm_duty_init[lightsCount] = {0};
// if you want to setup static ip uncomment these 3 lines and line 72
//IPAddress strip_ip ( 192, 168, 10, 95);
//IPAddress gateway_ip ( 192, 168, 10, 1);
//IPAddress subnet_mask(255, 255, 255, 0);
uint8_t scene;
bool light_state[lightsCount], in_transition;
int transitiontime[lightsCount], bri[lightsCount];
float step_level[lightsCount], current_bri[lightsCount];
byte mac[6];
ESP8266WebServer server(80);
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 == 0) {
bri[light] = 144;
} else if ( new_scene == 1) {
bri[light] = 254;
} else if ( new_scene == 2) {
bri[light] = 1;
}
}
void lightEngine() {
for (int i = 0; i < lightsCount; 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];
pwm_set_duty((int)(current_bri[i] * 4), i);
pwm_start();
}
} else {
if (current_bri[i] != 0 ) {
in_transition = true;
current_bri[i] -= step_level[i];
if (current_bri[i] < 0) current_bri[i] = 0;
pwm_set_duty((int)(current_bri[i] * 4), i);
pwm_start();
}
}
}
if (in_transition) {
delay(6);
in_transition = false;
} else {
if (digitalRead(button1_pin) == HIGH) {
int i = 0;
while (digitalRead(button1_pin) == HIGH && i < 30) {
delay(20);
i++;
}
for (int light = 0; light < lightsCount; 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] > 255) {
// don't increase the brightness more then maximum value
bri[light] = 255;
}
}
}
}
}
if (digitalRead(button2_pin) == HIGH) {
int i = 0;
while (digitalRead(button2_pin) == HIGH && i < 30) {
delay(20);
i++;
}
for (int light = 0; light < lightsCount; 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;
}
}
}
}
}
void setup() {
EEPROM.begin(512);
//WiFi.config(strip_ip, gateway_ip, subnet_mask);
for (uint8_t ch = 0; ch < lightsCount; ch++) {
pinMode(io_info[ch][2], OUTPUT);
}
pwm_init(period, pwm_duty_init, lightsCount, io_info);
pwm_start();
for (uint8_t light = 0; light < lightsCount; light++) {
apply_scene(EEPROM.read(2), light);
step_level[light] = bri[light] / 150.0;
}
if (EEPROM.read(1) == 1 || (EEPROM.read(1) == 0 && EEPROM.read(0) == 1)) {
for (int i = 0; i < lightsCount; i++) {
light_state[i] = true;
}
for (int j = 0; j < 200; j++) {
lightEngine();
}
}
WiFiManager wifiManager;
wifiManager.autoConnect("New Hue Light");
if (! light_state[0]) {
// Show that we are connected
pwm_set_duty(100, 1);
pwm_start();
delay(500);
pwm_set_duty(0, 1);
pwm_start();
pinMode(button1_pin, INPUT);
pinMode(button2_pin, INPUT);
}
WiFi.macAddress(mac);
// Port defaults to 8266
// ArduinoOTA.setPort(8266);
// Hostname defaults to esp8266-[ChipID]
// ArduinoOTA.setHostname("myesp8266");
// No authentication by default
// ArduinoOTA.setPassword((const char *)"123");
ArduinoOTA.begin();
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
server.on("/switch", []() {
server.send(200, "text/plain", "OK");
float transitiontime = 4;
int button;
for (uint8_t i = 0; i < server.args(); i++) {
if (server.argName(i) == "button") {
button = server.arg(i).toInt();
}
}
for (int i = 0; i < lightsCount; i++) {
if (button == 1000) {
if (light_state[i] == false) {
light_state[i] = true;
scene = 0;
} else {
apply_scene(scene, i);
scene++;
if (scene == 11) {
scene = 0;
}
}
} else if (button == 2000) {
if (light_state[i] == false) {
bri[i] = 30;
light_state[i] = true;
} else {
bri[i] += 30;
}
if (bri[i] > 255) bri[i] = 255;
} else if (button == 3000 && light_state[i] == true) {
bri[i] -= 30;
if (bri[i] < 1) bri[i] = 1;
} else if (button == 4000) {
light_state[i] = false;
}
if (light_state[i]) {
step_level[i] = ((float)bri[i] - current_bri[i]) / transitiontime;
} else {
step_level[i] = current_bri[i] / transitiontime;
}
}
});
server.on("/set", []() {
uint8_t light;
float transitiontime = 4;
for (uint8_t i = 0; i < server.args(); i++) {
if (server.argName(i) == "light") {
light = server.arg(i).toInt() - 1;
}
else if (server.argName(i) == "on") {
if (server.arg(i) == "True" || server.arg(i) == "true") {
light_state[light] = true;
if (EEPROM.read(1) == 0 && EEPROM.read(0) == 0) {
EEPROM.write(0, 1);
}
}
else {
light_state[light] = false;
if (EEPROM.read(1) == 0 && EEPROM.read(0) == 1) {
EEPROM.write(0, 0);
}
}
EEPROM.commit();
}
else if (server.argName(i) == "bri") {
light_state[light] = true;
if (server.arg(i).toInt() != 0)
bri[light] = server.arg(i).toInt();
}
else if (server.argName(i) == "bri_inc") {
bri[light] += server.arg(i).toInt();
if (bri[light] > 255) bri[light] = 255;
else if (bri[light] < 0) bri[light] = 0;
}
else if (server.argName(i) == "alert" && server.arg(i) == "select") {
if (light_state[light]) {
current_bri[light] = 0;
} else {
current_bri[light] = 255;
}
}
else if (server.argName(i) == "transitiontime") {
transitiontime = server.arg(i).toInt();
}
}
server.send(200, "text/plain", "OK, bri:" + (String)bri[light] + ", state:" + light_state[light]);
transitiontime *= 16;
if (light_state[light]) {
step_level[light] = (bri[light] - current_bri[light]) / transitiontime;
} else {
step_level[light] = current_bri[light] / transitiontime;
}
});
server.on("/get", []() {
uint8_t light;
if (server.hasArg("light"))
light = server.arg("light").toInt() - 1;
String power_status;
power_status = light_state[light] ? "true" : "false";
server.send(200, "text/plain", "{\"on\": " + power_status + ", \"bri\": " + (String)bri[light] + "}");
});
server.on("/detect", []() {
server.send(200, "text/plain", "{\"hue\": \"bulb\",\"lights\": " + (String)lightsCount + ",\"modelid\": \"LWB010\",\"mac\": \"" + String(mac[5], HEX) + ":" + String(mac[4], HEX) + ":" + String(mac[3], HEX) + ":" + String(mac[2], HEX) + ":" + String(mac[1], HEX) + ":" + String(mac[0], HEX) + "\"}");
});
server.on("/", []() {
float transitiontime = 4;
if (server.hasArg("startup")) {
if ( EEPROM.read(1) != server.arg("startup").toInt()) {
EEPROM.write(1, server.arg("startup").toInt());
EEPROM.commit();
}
}
for (int light = 0; light < lightsCount; light++) {
if (server.hasArg("scene")) {
if (server.arg("bri") == "" && server.arg("hue") == "" && server.arg("ct") == "" && server.arg("sat") == "") {
if ( EEPROM.read(2) != server.arg("scene").toInt()) {
EEPROM.write(2, server.arg("scene").toInt());
EEPROM.commit();
}
apply_scene(server.arg("scene").toInt(), light);
} else {
if (server.arg("bri") != "") {
bri[light] = server.arg("bri").toInt();
}
}
} else if (server.hasArg("on")) {
if (server.arg("on") == "true") {
light_state[light] = true; {
if (EEPROM.read(1) == 0 && EEPROM.read(0) == 0) {
EEPROM.write(0, 1);
}
}
} else {
light_state[light] = false;
if (EEPROM.read(1) == 0 && EEPROM.read(0) == 1) {
EEPROM.write(0, 0);
}
}
EEPROM.commit();
} else 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("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 += "<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>Light Setup</h3>";
http_content += "<form class=\"pure-form pure-form-aligned\" action=\"/\" method=\"post\">";
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[0]) http_content += " pure-button-primary"; http_content += "\" href=\"/?on=true\">ON</a>";
http_content += "<a class=\"pure-button"; if (!light_state[0]) http_content += " pure-button-primary"; http_content += "\" href=\"/?on=false\">OFF</a>";
http_content += "</div>";
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\">";
http_content += "<option "; if (EEPROM.read(1) == 0) http_content += "selected=\"selected\""; http_content += " value=\"0\">Last state</option>";
http_content += "<option "; if (EEPROM.read(1) == 1) http_content += "selected=\"selected\""; http_content += " value=\"1\">On</option>";
http_content += "<option "; if (EEPROM.read(1) == 2) 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\">";
http_content += "<option "; if (EEPROM.read(2) == 0) http_content += "selected=\"selected\""; http_content += " value=\"0\">Relax</option>";
http_content += "<option "; if (EEPROM.read(2) == 1) http_content += "selected=\"selected\""; http_content += " value=\"1\">Bright</option>";
http_content += "<option "; if (EEPROM.read(2) == 2) http_content += "selected=\"selected\""; http_content += " value=\"2\">Nightly</option>";
http_content += "</select>";
http_content += "</div>";
http_content += "<br>";
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\">Bri</label>";
http_content += "<input id=\"bri\" name=\"bri\" type=\"text\" placeholder=\"" + (String)bri[0] + "\">";
http_content += "</div>";
http_content += "<div class=\"pure-controls\">";
http_content += "<span class=\"pure-form-message\"><a href=\"/?alert=1\">alert</a> or <a href=\"/?reset=1\">reset</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.onNotFound(handleNotFound);
server.begin();
}
void loop() {
ArduinoOTA.handle();
server.handleClient();
lightEngine();
}
I believe the onboard serial adapter may keep the "button" pressed. If you use just 2 lights, then you have gpio12 to gpio15 free, so you can try to use them. Ensure you use pull down resistors, otherwise you must enable internal resistors with following code:
pinMode(button1_pin, INPUT);
pinMode(button2_pin, INPUT);
digitalWrite(button1_pin, LOW);
digitalWrite(button2_pin, LOW);
Here follows the sketch with the modifications you suggested, its still behaving exactly the same (button code seems to be running by itself, lights show up as unreachable, still sort of controlable however not working as they should) Have not added any resistors as of now, if the internal pullup can handle it.
I added:
pinMode(button1_pin, INPUT); pinMode(button2_pin, INPUT); digitalWrite(button1_pin, LOW); digitalWrite(button2_pin, LOW);
And changed:
define button1_pin 13
define button2_pin 14
Complete sketch:
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>
#include <EEPROM.h>
#include "pwm.c"
#define button1_pin 13
#define button2_pin 14
#define lightsCount 2
const uint32_t period = 1024;
//define pins
uint32 io_info[lightsCount][3] = {
// MUX, FUNC, PIN
//{PERIPHS_IO_MUX_MTDI_U, FUNC_GPIO12, 12},
//{PERIPHS_IO_MUX_MTDO_U, FUNC_GPIO15, 15},
//{PERIPHS_IO_MUX_MTCK_U, FUNC_GPIO13, 13},
//{PERIPHS_IO_MUX_MTMS_U, FUNC_GPIO14, 14},
{PERIPHS_IO_MUX_GPIO4_U, FUNC_GPIO4 , 4},
{PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5 , 5},
};
// initial duty: all off
uint32 pwm_duty_init[lightsCount] = {0};
// if you want to setup static ip uncomment these 3 lines and line 72
//IPAddress strip_ip ( 192, 168, 10, 95);
//IPAddress gateway_ip ( 192, 168, 10, 1);
//IPAddress subnet_mask(255, 255, 255, 0);
uint8_t scene;
bool light_state[lightsCount], in_transition;
int transitiontime[lightsCount], bri[lightsCount];
float step_level[lightsCount], current_bri[lightsCount];
byte mac[6];
ESP8266WebServer server(80);
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 == 0) {
bri[light] = 144;
} else if ( new_scene == 1) {
bri[light] = 254;
} else if ( new_scene == 2) {
bri[light] = 1;
}
}
void lightEngine() {
for (int i = 0; i < lightsCount; 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];
pwm_set_duty((int)(current_bri[i] * 4), i);
pwm_start();
}
} else {
if (current_bri[i] != 0 ) {
in_transition = true;
current_bri[i] -= step_level[i];
if (current_bri[i] < 0) current_bri[i] = 0;
pwm_set_duty((int)(current_bri[i] * 4), i);
pwm_start();
}
}
}
if (in_transition) {
delay(6);
in_transition = false;
} else {
if (digitalRead(button1_pin) == HIGH) {
int i = 0;
while (digitalRead(button1_pin) == HIGH && i < 30) {
delay(20);
i++;
}
for (int light = 0; light < lightsCount; 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] > 255) {
// don't increase the brightness more then maximum value
bri[light] = 255;
}
}
}
}
}
if (digitalRead(button2_pin) == HIGH) {
int i = 0;
while (digitalRead(button2_pin) == HIGH && i < 30) {
delay(20);
i++;
}
for (int light = 0; light < lightsCount; 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;
}
}
}
}
}
void setup() {
EEPROM.begin(512);
//WiFi.config(strip_ip, gateway_ip, subnet_mask);
for (uint8_t ch = 0; ch < lightsCount; ch++) {
pinMode(io_info[ch][2], OUTPUT);
}
pwm_init(period, pwm_duty_init, lightsCount, io_info);
pwm_start();
for (uint8_t light = 0; light < lightsCount; light++) {
apply_scene(EEPROM.read(2), light);
step_level[light] = bri[light] / 150.0;
}
if (EEPROM.read(1) == 1 || (EEPROM.read(1) == 0 && EEPROM.read(0) == 1)) {
for (int i = 0; i < lightsCount; i++) {
light_state[i] = true;
}
for (int j = 0; j < 200; j++) {
lightEngine();
}
}
WiFiManager wifiManager;
wifiManager.autoConnect("New Hue Light");
if (! light_state[0]) {
// Show that we are connected
pwm_set_duty(100, 1);
pwm_start();
delay(500);
pwm_set_duty(0, 1);
pwm_start();
pinMode(button1_pin, INPUT);
pinMode(button2_pin, INPUT);
digitalWrite(button1_pin, LOW);
digitalWrite(button2_pin, LOW);
}
WiFi.macAddress(mac);
// Port defaults to 8266
// ArduinoOTA.setPort(8266);
// Hostname defaults to esp8266-[ChipID]
// ArduinoOTA.setHostname("myesp8266");
// No authentication by default
// ArduinoOTA.setPassword((const char *)"123");
ArduinoOTA.begin();
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
server.on("/switch", []() {
server.send(200, "text/plain", "OK");
float transitiontime = 4;
int button;
for (uint8_t i = 0; i < server.args(); i++) {
if (server.argName(i) == "button") {
button = server.arg(i).toInt();
}
}
for (int i = 0; i < lightsCount; i++) {
if (button == 1000) {
if (light_state[i] == false) {
light_state[i] = true;
scene = 0;
} else {
apply_scene(scene, i);
scene++;
if (scene == 11) {
scene = 0;
}
}
} else if (button == 2000) {
if (light_state[i] == false) {
bri[i] = 30;
light_state[i] = true;
} else {
bri[i] += 30;
}
if (bri[i] > 255) bri[i] = 255;
} else if (button == 3000 && light_state[i] == true) {
bri[i] -= 30;
if (bri[i] < 1) bri[i] = 1;
} else if (button == 4000) {
light_state[i] = false;
}
if (light_state[i]) {
step_level[i] = ((float)bri[i] - current_bri[i]) / transitiontime;
} else {
step_level[i] = current_bri[i] / transitiontime;
}
}
});
server.on("/set", []() {
uint8_t light;
float transitiontime = 4;
for (uint8_t i = 0; i < server.args(); i++) {
if (server.argName(i) == "light") {
light = server.arg(i).toInt() - 1;
}
else if (server.argName(i) == "on") {
if (server.arg(i) == "True" || server.arg(i) == "true") {
light_state[light] = true;
if (EEPROM.read(1) == 0 && EEPROM.read(0) == 0) {
EEPROM.write(0, 1);
}
}
else {
light_state[light] = false;
if (EEPROM.read(1) == 0 && EEPROM.read(0) == 1) {
EEPROM.write(0, 0);
}
}
EEPROM.commit();
}
else if (server.argName(i) == "bri") {
light_state[light] = true;
if (server.arg(i).toInt() != 0)
bri[light] = server.arg(i).toInt();
}
else if (server.argName(i) == "bri_inc") {
bri[light] += server.arg(i).toInt();
if (bri[light] > 255) bri[light] = 255;
else if (bri[light] < 0) bri[light] = 0;
}
else if (server.argName(i) == "alert" && server.arg(i) == "select") {
if (light_state[light]) {
current_bri[light] = 0;
} else {
current_bri[light] = 255;
}
}
else if (server.argName(i) == "transitiontime") {
transitiontime = server.arg(i).toInt();
}
}
server.send(200, "text/plain", "OK, bri:" + (String)bri[light] + ", state:" + light_state[light]);
transitiontime *= 16;
if (light_state[light]) {
step_level[light] = (bri[light] - current_bri[light]) / transitiontime;
} else {
step_level[light] = current_bri[light] / transitiontime;
}
});
server.on("/get", []() {
uint8_t light;
if (server.hasArg("light"))
light = server.arg("light").toInt() - 1;
String power_status;
power_status = light_state[light] ? "true" : "false";
server.send(200, "text/plain", "{\"on\": " + power_status + ", \"bri\": " + (String)bri[light] + "}");
});
server.on("/detect", []() {
server.send(200, "text/plain", "{\"hue\": \"bulb\",\"lights\": " + (String)lightsCount + ",\"modelid\": \"LWB010\",\"mac\": \"" + String(mac[5], HEX) + ":" + String(mac[4], HEX) + ":" + String(mac[3], HEX) + ":" + String(mac[2], HEX) + ":" + String(mac[1], HEX) + ":" + String(mac[0], HEX) + "\"}");
});
server.on("/", []() {
float transitiontime = 4;
if (server.hasArg("startup")) {
if ( EEPROM.read(1) != server.arg("startup").toInt()) {
EEPROM.write(1, server.arg("startup").toInt());
EEPROM.commit();
}
}
for (int light = 0; light < lightsCount; light++) {
if (server.hasArg("scene")) {
if (server.arg("bri") == "" && server.arg("hue") == "" && server.arg("ct") == "" && server.arg("sat") == "") {
if ( EEPROM.read(2) != server.arg("scene").toInt()) {
EEPROM.write(2, server.arg("scene").toInt());
EEPROM.commit();
}
apply_scene(server.arg("scene").toInt(), light);
} else {
if (server.arg("bri") != "") {
bri[light] = server.arg("bri").toInt();
}
}
} else if (server.hasArg("on")) {
if (server.arg("on") == "true") {
light_state[light] = true; {
if (EEPROM.read(1) == 0 && EEPROM.read(0) == 0) {
EEPROM.write(0, 1);
}
}
} else {
light_state[light] = false;
if (EEPROM.read(1) == 0 && EEPROM.read(0) == 1) {
EEPROM.write(0, 0);
}
}
EEPROM.commit();
} else 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("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 += "<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>Light Setup</h3>";
http_content += "<form class=\"pure-form pure-form-aligned\" action=\"/\" method=\"post\">";
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[0]) http_content += " pure-button-primary"; http_content += "\" href=\"/?on=true\">ON</a>";
http_content += "<a class=\"pure-button"; if (!light_state[0]) http_content += " pure-button-primary"; http_content += "\" href=\"/?on=false\">OFF</a>";
http_content += "</div>";
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\">";
http_content += "<option "; if (EEPROM.read(1) == 0) http_content += "selected=\"selected\""; http_content += " value=\"0\">Last state</option>";
http_content += "<option "; if (EEPROM.read(1) == 1) http_content += "selected=\"selected\""; http_content += " value=\"1\">On</option>";
http_content += "<option "; if (EEPROM.read(1) == 2) 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\">";
http_content += "<option "; if (EEPROM.read(2) == 0) http_content += "selected=\"selected\""; http_content += " value=\"0\">Relax</option>";
http_content += "<option "; if (EEPROM.read(2) == 1) http_content += "selected=\"selected\""; http_content += " value=\"1\">Bright</option>";
http_content += "<option "; if (EEPROM.read(2) == 2) http_content += "selected=\"selected\""; http_content += " value=\"2\">Nightly</option>";
http_content += "</select>";
http_content += "</div>";
http_content += "<br>";
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\">Bri</label>";
http_content += "<input id=\"bri\" name=\"bri\" type=\"text\" placeholder=\"" + (String)bri[0] + "\">";
http_content += "</div>";
http_content += "<div class=\"pure-controls\">";
http_content += "<span class=\"pure-form-message\"><a href=\"/?alert=1\">alert</a> or <a href=\"/?reset=1\">reset</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.onNotFound(handleNotFound);
server.begin();
}
void loop() {
ArduinoOTA.handle();
server.handleClient();
lightEngine();
}
The code is identical with original one, the differences appear only if conditions if (digitalRead(button1_pin) == HIGH)
and if (digitalRead(button2_pin) == HIGH)
are true. I will create a prototype today and test this, but i suspect the issue is at hardware level here.
I'm not sure whats going on with them, the weird thing is the behavior in hue app. That the light shows up as unreachable and still sort of works. This modification seems to somewhat break the sketch. Its using wemos d1 mini as controller.
Is not strange for me and i will tell you why. In order to have a successful http request the function server.handleClient() must run from loop, but this is not executed fast enough because i add:
while (digitalRead(button2_pin) == HIGH && i < 30) {
delay(20);
i++;
This delay made made internal webserver to not be so reliable and ger request have a lower timeout than put request in my pyhthon script.
Did you connect some 10k resistors of these pins to ground to be sure there are pulled down, maybe internal resistors are not working?
I perform this test, i have exactly the same behavior like you described. After i connect my multi-meter to measure the voltage to the pin, it drop instant to 0V and the light start to work normally. So i believe internal pull down resistors are not working, you need to connect any resistor with value up to 500K to ground. I made some improvements on code:
if (in_transition) {
delay(6);
in_transition = false;
} else {
if (digitalRead(button1_pin) == HIGH) {
int i = 0;
while (digitalRead(button1_pin) == HIGH && i < 30) {
delay(20);
i++;
}
for (int light = 0; light < lightsCount; 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] > 255) {
// don't increase the brightness more then maximum value
bri[light] = 255;
}
}
}
} else if (digitalRead(button2_pin) == HIGH) {
int i = 0;
while (digitalRead(button2_pin) == HIGH && i < 30) {
delay(20);
i++;
}
for (int light = 0; light < lightsCount; 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;
}
}
}
}
}
remove:
digitalWrite(button1_pin, LOW);
digitalWrite(button2_pin, LOW);
because i found them useless
Awesome, i will test this out when i get a spare moment today. I have resistors in all sizes and shapes :)
In code i found one issue while changing the brightness, i will update the code once i successfully test this.
Is it a good idea to have this in the official code, commented out if it interferes with the normal functioning in any way. Or another sketch with this functionality enabled. Maybe some wiring diagrams or pcb layouts.
i made a test with SK6812 neopixel ring and is working. This is the commit https://github.com/mariusmotea/diyHue/commit/41f76e20156e148da82b0b205a06069d96464eed I will try to do the same for others, hope to not insert bugs.
Maybe commit a 2nd sketch with this functionality if there is danger of introducing bugs, not all users want/need this function i guess.
by default it will be disabled, if you need it then #define use_hardware_switch false
must be changed to true.
I have a test rig for the single color stipe running, now, is it much modification to get that updated? (For testing the button code) Edit: this would be the "generic dimmable" one
That is already done, it is located in develop branch. Remember to change define use_hardware_switch to true. Maybe you can test also with tx and rx pins (gpio 1 and 3) that are free in most of projects.
Hello, Buttons work now. Tested them a bit, Gonna build a PCB with the buttons on for further testing. its only on breadboard stage at the moment, but this is pretty much perfect for the lights that need a local button. Well done sir :)
EDIT: I maybe found a bug, when you have the light at say 80% and you wanna dim to 40% (just example %) you hold the OFF/down button till you have the desired brightness, when you let go of the button the light turns off instead staying on the requested %. If you turn the light on again it is at the correct dim level. Dont know if its a bug with my hardware here or its code related? It is also possible the ON button has the same bug, but not able to see it since the light is allready on.
EDIT2: Is there any way to have the switch activate on "low" instead? Seems less complicated, only need the gpio and gnd on the switch. No resistor or vcc needed in that case. On espeasy i always use these, push button active low type deals. But are there are reasons that "active high" switches are better or needed ?
Hi,
I did not saw your edits. True, reading the code in case of long press, when released it will trigger also the short press. It can be fixed. If you want to have trigger for low, then:
if (digitalRead(button1_pin) == LOW) {
int i = 0;
while (digitalRead(button1_pin) == LOW && i < 30) {
delay(20);
i++;
}
.....
I guess this will work:
} else if (use_hardware_switch == true) {
bool long_press;
if (digitalRead(button1_pin) == HIGH) {
int i = 0;
while (digitalRead(button1_pin) == HIGH && i < 30) {
delay(20);
i++;
}
for (int light = 0; light < lightsCount; light++) {
if (i < 30) {
// there was a short press
if (long_press) {
long_press = false;
} else {
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;
}
long_press = true;
}
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 < lightsCount; light++) {
if (i < 30) {
// there was a short press
if (long_press) {
long_press = false;
} else {
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;
}
long_press = true;
}
process_lightdata(light, 4);
}
}
}
I see, low switches seem simpler to implement in pcb and such. Not knowing much about the subtle differences, are there advantages or drawbacks on high vs low? Only ever used low ones as mentioned. I'll test it in a little while. Is the code bit simple to fix also?
Say you want a wired switch/dimmer connected to some gpio to dim for example the "generic dimmable light". Is this possible?