#include #include #include #include #include #include #define _STR(s) #s #define STR(s) _STR(s) const char* mqtt_server = "192.168.1.2"; //MQTT Server IP, your home MQTT server eg Mosquitto on RPi, or some public MQTT const int mqtt_port = 1883; //MQTT Server PORT, default is 1883 but can be anything. const char* mqtt_pingall_sub = "home/pingall"; const char* mqtt_pingall_pub = "home/pingall/response"; #define MQTT_BASE "home/livingroom/ledstripe" const char* mqtt_device_boot = MQTT_BASE "/device"; enum sensor_type : uint8_t { SENSOR_RELAY = (1u << 0), SENSOR_DIMMER = (1u << 1), SENSOR_BUTTON = (1u << 2), }; struct sensor_t { uint8_t type; struct { uint8_t pin; // relay pin const char *mqtt_sub; const char *mqtt_pub; } relay; struct { uint8_t pin; // push button pin Bounce bounce; } button; struct { uint8_t pin; // dimmer pin uint8_t level; // current dim level (0 to 100) const char *mqtt_sub; const char *mqtt_pub; } dimmer; }; struct sensor_t sensors[] = { { .type = SENSOR_RELAY | SENSOR_BUTTON | SENSOR_DIMMER, .relay = { .pin = D5, .mqtt_sub = MQTT_BASE, .mqtt_pub = MQTT_BASE "/status" }, .button = { .pin = D0, .bounce = Bounce(), }, .dimmer = { .pin = D8, .level = 100, .mqtt_sub = MQTT_BASE "/brightness", .mqtt_pub = MQTT_BASE "/brightness/status" }, } }; //#define SAVE_RESTORE #define NUM(a) (sizeof(a) / sizeof(*a)) #define RELAY_ON 1 // GPIO value to write to turn on attached relay #define RELAY_OFF 0 // GPIO value to write to turn off attached relay #define DIMMER_FADE_DELAY 40 // Delay in ms for each percentage fade up/down (10ms = 1s full-range dim) WiFiClient espClient; PubSubClient client(espClient); char convBuffer[10]; void callback(char* topic, byte* payload, unsigned int length); void blink(); bool relayRead(struct sensor_t *sensor); void relayWrite(struct sensor_t *sensor, bool state, bool send_update=false); void flipRelay(struct sensor_t *sensor, bool send_update=false); void checkButtons(void); inline uint8_t pwmValue(uint8_t level); void fadeDimmer(struct sensor_t *sensor, uint8_t level, bool send_update=false); void checkFirmwareUpdate(); void setup() { Serial.begin(115200); pinMode(LED_BUILTIN, OUTPUT); analogWriteRange(100); // set relay pins to output mode + restore to last known state for (uint8_t i = 0; i < NUM(sensors); i++) { struct sensor_t *sensor = &sensors[i]; if (sensor->type & SENSOR_RELAY) { pinMode(sensor->relay.pin, OUTPUT); #ifdef SAVE_RESTORE #error "not implemented" #else digitalWrite(sensor->relay.pin, RELAY_OFF); #endif } if (sensor->type & SENSOR_DIMMER) { pinMode(sensor->dimmer.pin, OUTPUT); #ifdef SAVE_RESTORE #error "not implemented" #else uint8_t level = sensor->dimmer.level; #endif if ((sensor->type & SENSOR_RELAY) && !relayRead(sensor)) level = 0; analogWrite(sensor->dimmer.pin, pwmValue(level)); } if (sensor->type & SENSOR_BUTTON) { pinMode(sensor->button.pin, INPUT); sensor->button.bounce.attach(sensor->button.pin); } } WiFiManager wifiManager; wifiManager.setConfigPortalTimeout(600); Serial.printf_P(PSTR("Setting up WiFi\n")); if (!wifiManager.autoConnect("ESP8266_LR_STRIPES")) { Serial.printf_P(PSTR("Failed to connect and hit timeout\n")); delay(5000); ESP.restart(); } yield(); checkFirmwareUpdate(); yield(); client.setServer(mqtt_server, mqtt_port); client.setCallback(callback); digitalWrite(LED_BUILTIN, HIGH); //Turn off led as default } void reconnect() { // Loop until we're reconnected while (!client.connected()) { // Create a random client ID String clientId = "ESP8266Client-"; clientId += String(random(0xffff), HEX); // Attempt to connect if (client.connect(clientId.c_str())) { // Once connected, publish an announcement... client.publish(mqtt_device_boot, "connected"); // ... and resubscribe client.subscribe(mqtt_pingall_sub); // publish states for (uint8_t i = 0; i < NUM(sensors); i++) { struct sensor_t *sensor = &sensors[i]; if (sensor->type & SENSOR_RELAY) { Serial.println(sensor->relay.mqtt_sub); client.subscribe(sensor->relay.mqtt_sub); client.publish(sensor->relay.mqtt_pub, relayRead(sensor) ? "1" : "0", true); } if (sensor->type & SENSOR_DIMMER) { Serial.println(sensor->dimmer.mqtt_sub); client.subscribe(sensor->dimmer.mqtt_sub); itoa(sensor->dimmer.level, convBuffer, 10); client.publish(sensor->dimmer.mqtt_pub, convBuffer, true); } } } else { // Wait 5 seconds before retrying delay(5000); } } } void callback(char* topic, byte* payload, unsigned int length) { char c_payload[length]; memcpy(c_payload, payload, length); c_payload[length] = '\0'; if (strcmp(topic, mqtt_pingall_sub) == 0) { blink(); client.publish(mqtt_pingall_pub, "{\"livingroom_ledstrip\":\"connected\"}"); return; } for (uint8_t i = 0; i < NUM(sensors); i++) { struct sensor_t *sensor = &sensors[i]; if (sensor->type & SENSOR_RELAY && length > 0 && strcmp(topic, sensor->relay.mqtt_sub) == 0) { blink(); relayWrite(sensor, payload[0] == '1', true); return; } if (sensor->type & SENSOR_DIMMER && length > 0 && strcmp(topic, sensor->dimmer.mqtt_sub) == 0) { blink(); uint8_t level = atoi((char *)payload); fadeDimmer(sensor, (level > 100) ? 100 : level, true); return; } } } void blink() { //Blink on received MQTT message digitalWrite(LED_BUILTIN, LOW); delay(25); digitalWrite(LED_BUILTIN, HIGH); } void loop() { if (!client.connected()) reconnect(); client.loop(); checkButtons(); } bool relayRead(struct sensor_t *sensor) { if (sensor->type & SENSOR_RELAY) return digitalRead(sensor->relay.pin) == RELAY_ON; return false; } void relayWrite(struct sensor_t *sensor, bool state, bool send_update) { if (!(sensor->type & SENSOR_RELAY)) return; Serial.print(F("Incoming change for relay: ")); Serial.print(sensor->relay.pin); Serial.print(F(", New state: ")); Serial.println(state); digitalWrite(sensor->relay.pin, state ? RELAY_ON : RELAY_OFF); if (sensor->type & SENSOR_DIMMER) analogWrite(sensor->dimmer.pin, state ? pwmValue(sensor->dimmer.level) : 0); #ifdef SAVE_RESTORE #error "not implemented" #endif if (send_update) client.publish(sensor->relay.mqtt_pub, state ? "1" : "0", true); } void flipRelay(struct sensor_t *sensor, bool send_update) { relayWrite(sensor, relayRead(sensor) ? RELAY_OFF : RELAY_ON, send_update); } inline void checkButtons(void) { for (uint8_t i = 0; i < NUM(sensors); i++) { struct sensor_t *sensor = &sensors[i]; if (sensor->type & SENSOR_BUTTON) { sensor->button.bounce.update(); if (sensor->button.bounce.fell()) flipRelay(sensor, true); } } } const uint8_t pwmtable[101] PROGMEM = { // value below 0 turns of the power supply 0, 10, 10, 11, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, 15, 16, 16, 17, 17, 17, 18, 18, 19, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 25, 25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32, 32, 33, 34, 35, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 54, 55, 56, 58, 59, 60, 62, 63, 65, 66, 68, 69, 71, 72, 74, 76, 78, 79, 81, 83, 85, 87, 89, 91, 93, 95, 98, 100 }; inline uint8_t pwmValue(uint8_t level) { //uint8_t lvl = 100 - (uint8_t)pgm_read_byte(&pwmtable[level]); //return (lvl < 10) 10 : lvl; //level = (level > 0 && level < 10) ? 10 : level; //return 100 - level; return 100 - (uint8_t)pgm_read_byte(&pwmtable[level]); } void fadeDimmer(struct sensor_t *sensor, uint8_t level, bool send_update) { if (!(sensor->type & SENSOR_DIMMER)) return; if (level > 100) level = 100; Serial.print(F("Incoming change for dimmer: ")); Serial.print(sensor->dimmer.pin); Serial.print(F(", New level: ")); Serial.println(level); if (level > 0 && sensor->type & SENSOR_RELAY && !relayRead(sensor)) relayWrite(sensor, RELAY_ON, send_update); int delta = ((int8_t)(level - sensor->dimmer.level) < 0) ? -1 : 1; while (sensor->dimmer.level != level) { sensor->dimmer.level += delta; analogWrite(sensor->dimmer.pin, pwmValue(sensor->dimmer.level)); delay(DIMMER_FADE_DELAY); } if (level == 0 && sensor->type & SENSOR_RELAY && relayRead(sensor)) relayWrite(sensor, RELAY_OFF, send_update); #ifdef SAVE_RESTORE #error "not implemented" #endif if (send_update) { itoa(level, convBuffer, 10); client.publish(sensor->dimmer.mqtt_pub, convBuffer, true); } } void checkFirmwareUpdate() { BearSSL::WiFiClientSecure update_client; update_client.setInsecure(); client.publish(mqtt_device_boot, "fwupdate running"); ESPhttpUpdate.setLedPin(LED_BUILTIN, HIGH); ESPhttpUpdate.rebootOnUpdate(true); t_httpUpdate_return ret = ESPhttpUpdate.update(update_client, FIRMWARE_URL, STR(FIRMWARE_VERSION)); switch(ret) { case HTTP_UPDATE_FAILED: { Serial.printf_P(PSTR("HTTP_UPDATE_FAILED Error (%d): %s\n"), ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); String tmp = String("fwupdate error: ") + ESPhttpUpdate.getLastErrorString(); client.publish(mqtt_device_boot, tmp.c_str()); } break; case HTTP_UPDATE_NO_UPDATES: Serial.printf_P(PSTR("HTTP_UPDATE_NO_UPDATES\n")); client.publish(mqtt_device_boot, "fwupdate noupdates"); break; case HTTP_UPDATE_OK: Serial.printf_P(PSTR("HTTP_UPDATE_OK\n")); client.publish(mqtt_device_boot, "fwupdate ok"); break; } }