#include #include #include #include #include #include #include "secrets.h" const char* autoconf_ssid = AUTOCONF_SSID; //AP name for WiFi setup AP which your ESP will open when not able to connect to other WiFi const char* autoconf_pwd = AUTOCONF_PASSWORD; // AP password so noone else can connect to the ESP in case your router fails 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]; 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 setup() { Serial.begin(115200); pinMode(BUILTIN_LED, 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.autoConnect(autoconf_ssid, autoconf_pwd); setup_ota(); client.setServer(mqtt_server, mqtt_port); client.setCallback(callback); digitalWrite(BUILTIN_LED, HIGH); //Turn off led as default } void setup_ota() { // Set OTA Password, and change it in platformio.ini ArduinoOTA.setPassword(OTA_PASSWORD); ArduinoOTA.onStart([]() {}); ArduinoOTA.onEnd([]() {}); ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {}); ArduinoOTA.onError([](ota_error_t error) { if (error == OTA_AUTH_ERROR); // Auth failed else if (error == OTA_BEGIN_ERROR); // Begin failed else if (error == OTA_CONNECT_ERROR); // Connect failed else if (error == OTA_RECEIVE_ERROR); // Receive failed else if (error == OTA_END_ERROR); // End failed }); ArduinoOTA.begin(); } 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(BUILTIN_LED, LOW); delay(25); digitalWrite(BUILTIN_LED, HIGH); } void loop() { if (!client.connected()) reconnect(); client.loop(); ArduinoOTA.handle(); 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); } }