#include #include #include #include #include #include #include "secrets.h" #define MQTT_BROKER "192.168.1.5" #define MQTT_BROKER_PORT 1883 //#define MQTT_USERNAME "" //#define MQTT_PASSWORD "" #define MQTT_BASE "home/livingroom/ledstripe" 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) // fw update // - increment number + build // - scp .pio/build/$ENV/firmware.bin manuel@mausz.at:public_html/coding/.firmware/olilrstripes.bin // - reboot device or send "fwupdate" to mqtt_topic_cmd // - fw update state is published in mqtt_topic_state #define FIRMWARE_VERSION 1 //#define FIRMWARE_URL "" #define _STR(s) #s #define STR(s) _STR(s) const String mqtt_clientId = "LrStripes-" + String(ESP.getChipId()) + "/v" + STR(FIRMWARE_VERSION); const char* mqtt_topic_ping = MQTT_BASE "/ping"; const char* mqtt_topic_cmd = MQTT_BASE "/command"; const String mqtt_topic_state = String(MQTT_BASE) + "/" + String(ESP.getChipId()); bool mqttLoop(); void mqttCallback(char *topic, byte *payload, unsigned int length); void checkFirmwareUpdate(); 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); WiFiClient wifi_client; PubSubClient mqtt(wifi_client); char convBuffer[10]; 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")); WiFi.hostname("lrstripes"); if (!wifiManager.autoConnect("ESP8266_LR_STRIPES")) { Serial.printf_P(PSTR("Failed to connect and hit timeout\n")); delay(5000); ESP.restart(); } yield(); checkFirmwareUpdate(); yield(); mqtt.setServer(MQTT_BROKER, MQTT_BROKER_PORT); mqtt.setCallback(mqttCallback); digitalWrite(LED_BUILTIN, HIGH); //Turn off led as default } bool mqttLoop() { if (!mqtt.connected()) { Serial.printf_P(PSTR("Connecting to MQTT\n")); if (!mqtt.connect(mqtt_clientId.c_str(), MQTT_USERNAME, MQTT_PASSWORD)) return false; Serial.printf_P(PSTR("MQTT connected\n")); mqtt.publish(mqtt_topic_ping, mqtt_clientId.c_str()); mqtt.subscribe(mqtt_topic_ping); mqtt.subscribe(mqtt_topic_cmd); // 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); mqtt.subscribe(sensor->relay.mqtt_sub); mqtt.publish(sensor->relay.mqtt_pub, relayRead(sensor) ? "1" : "0", true); } if (sensor->type & SENSOR_DIMMER) { Serial.println(sensor->dimmer.mqtt_sub); mqtt.subscribe(sensor->dimmer.mqtt_sub); itoa(sensor->dimmer.level, convBuffer, 10); mqtt.publish(sensor->dimmer.mqtt_pub, convBuffer, true); } } } yield(); mqtt.loop(); return true; } void mqttCallback(char *topic, byte *payload, unsigned int length) { char c_payload[length + 1]; memcpy(c_payload, payload, length); c_payload[length] = '\0'; if (!strcmp(topic, mqtt_topic_ping)) { if (!strcmp(c_payload, "ping")) mqtt.publish(mqtt_topic_ping, mqtt_clientId.c_str()); return; } else if (!strcmp(topic, mqtt_topic_cmd)) { if (!strcmp(c_payload, "fwupdate")) { checkFirmwareUpdate(); return; } else if (!strcmp(c_payload, "reset")) { Serial.printf_P(PSTR("Resetting\n")); ESP.reset(); return; } } else { 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) { relayWrite(sensor, payload[0] == '1', true); return; } if (sensor->type & SENSOR_DIMMER && length > 0 && strcmp(topic, sensor->dimmer.mqtt_sub) == 0) { uint8_t level = atoi((char *)payload); fadeDimmer(sensor, (level > 100) ? 100 : level, true); return; } } } } 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) mqtt.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); mqtt.publish(sensor->dimmer.mqtt_pub, convBuffer, true); } } void checkFirmwareUpdate() { BearSSL::WiFiClientSecure update_client; update_client.setInsecure(); mqtt.publish(mqtt_topic_state.c_str(), "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(); mqtt.publish(mqtt_topic_state.c_str(), tmp.c_str()); } break; case HTTP_UPDATE_NO_UPDATES: Serial.printf_P(PSTR("HTTP_UPDATE_NO_UPDATES\n")); mqtt.publish(mqtt_topic_state.c_str(), "fwupdate noupdates"); break; case HTTP_UPDATE_OK: Serial.printf_P(PSTR("HTTP_UPDATE_OK\n")); mqtt.publish(mqtt_topic_state.c_str(), "fwupdate ok"); break; } } void loop() { if (!mqttLoop()) delay(5000); checkButtons(); }