From 3e08b2a584461fa6df8d68ef6b99b080a21d3957 Mon Sep 17 00:00:00 2001 From: manuel Date: Sat, 15 Apr 2017 23:45:51 +0200 Subject: oliver/ledstripe --- oliver/lr_stripes/lr_stripes.ino | 369 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 369 insertions(+) create mode 100644 oliver/lr_stripes/lr_stripes.ino (limited to 'oliver') diff --git a/oliver/lr_stripes/lr_stripes.ino b/oliver/lr_stripes/lr_stripes.ino new file mode 100644 index 0000000..11563c0 --- /dev/null +++ b/oliver/lr_stripes/lr_stripes.ino @@ -0,0 +1,369 @@ +#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.0.10"; //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); + } +} + -- cgit v1.2.3