/* LED MODULE Copyright (C) 2016-2019 by Xose PĂ©rez */ // ----------------------------------------------------------------------------- // LED // ----------------------------------------------------------------------------- #if LED_SUPPORT #include "relay.h" #include "broker.h" typedef struct { unsigned char pin; bool reverse; unsigned char mode; unsigned char relay; } led_t; std::vector _leds; bool _led_update = false; // For relay-based modes // ----------------------------------------------------------------------------- bool _ledStatus(unsigned char id) { if (id >= _ledCount()) return false; bool status = digitalRead(_leds[id].pin); return _leds[id].reverse ? !status : status; } bool _ledStatus(unsigned char id, bool status) { if (id >=_ledCount()) return false; digitalWrite(_leds[id].pin, _leds[id].reverse ? !status : status); return status; } bool _ledToggle(unsigned char id) { if (id >= _ledCount()) return false; return _ledStatus(id, !_ledStatus(id)); } unsigned char _ledMode(unsigned char id) { if (id >= _ledCount()) return false; return _leds[id].mode; } void _ledMode(unsigned char id, unsigned char mode) { if (id >= _ledCount()) return; _leds[id].mode = mode; } unsigned char _ledRelay(unsigned char id) { if (id >= _ledCount()) return false; return _leds[id].relay; } void _ledRelay(unsigned char id, unsigned char relay) { if (id >= _ledCount()) return; _leds[id].relay = relay; } void _ledBlink(unsigned char id, unsigned long delayOff, unsigned long delayOn) { if (id >= _ledCount()) return; static unsigned long next = millis(); if (next < millis()) { next += (_ledToggle(id) ? delayOn : delayOff); } } #if WEB_SUPPORT bool _ledWebSocketOnKeyCheck(const char * key, JsonVariant& value) { return (strncmp(key, "led", 3) == 0); } void _ledWebSocketOnVisible(JsonObject& root) { if (_ledCount() > 0) { root["ledVisible"] = 1; } } void _ledWebSocketOnConnected(JsonObject& root) { if (_ledCount() == 0) return; JsonArray& leds = root.createNestedArray("ledConfig"); for (byte i=0; i<_ledCount(); i++) { JsonObject& led = leds.createNestedObject(); led["mode"] = getSetting({"ledMode", i}, _ledMode(i)); led["relay"] = getSetting({"ledRelay", i}, _ledRelay(i)); } } #endif #if BROKER_SUPPORT void _ledBrokerCallback(const String& topic, unsigned char, unsigned int) { // Only process status messages for switches if (topic.equals(MQTT_TOPIC_RELAY)) { ledUpdate(true); } } #endif // BROKER_SUPPORT #if MQTT_SUPPORT void _ledMQTTCallback(unsigned int type, const char * topic, const char * payload) { if (type == MQTT_CONNECT_EVENT) { char buffer[strlen(MQTT_TOPIC_LED) + 3]; snprintf_P(buffer, sizeof(buffer), PSTR("%s/+"), MQTT_TOPIC_LED); mqttSubscribe(buffer); } if (type == MQTT_MESSAGE_EVENT) { // Match topic String t = mqttMagnitude((char *) topic); if (!t.startsWith(MQTT_TOPIC_LED)) return; // Get led ID unsigned int ledID = t.substring(strlen(MQTT_TOPIC_LED)+1).toInt(); if (ledID >= _ledCount()) { DEBUG_MSG_P(PSTR("[LED] Wrong ledID (%d)\n"), ledID); return; } // Check if LED is managed if (_ledMode(ledID) != LED_MODE_MQTT) return; // get value const auto value = relayParsePayload(payload); // Action to perform if (value == RelayStatus::TOGGLE) { _ledToggle(ledID); } else { _ledStatus(ledID, (value == RelayStatus::ON)); } } } #endif unsigned char _ledCount() { return _leds.size(); } void _ledConfigure() { for (unsigned char i=0; i < _leds.size(); i++) { _ledMode(i, getSetting({"ledMode", i}, _ledMode(i))); _ledRelay(i, getSetting({"ledRelay", i}, _ledRelay(i))); } _led_update = true; } // ----------------------------------------------------------------------------- void ledUpdate(bool value) { _led_update = value; } void ledSetup() { #if LED1_PIN != GPIO_NONE _leds.push_back((led_t) { LED1_PIN, LED1_PIN_INVERSE, LED1_MODE, LED1_RELAY - 1 }); #endif #if LED2_PIN != GPIO_NONE _leds.push_back((led_t) { LED2_PIN, LED2_PIN_INVERSE, LED2_MODE, LED2_RELAY - 1 }); #endif #if LED3_PIN != GPIO_NONE _leds.push_back((led_t) { LED3_PIN, LED3_PIN_INVERSE, LED3_MODE, LED3_RELAY - 1 }); #endif #if LED4_PIN != GPIO_NONE _leds.push_back((led_t) { LED4_PIN, LED4_PIN_INVERSE, LED4_MODE, LED4_RELAY - 1 }); #endif #if LED5_PIN != GPIO_NONE _leds.push_back((led_t) { LED5_PIN, LED5_PIN_INVERSE, LED5_MODE, LED5_RELAY - 1 }); #endif #if LED6_PIN != GPIO_NONE _leds.push_back((led_t) { LED6_PIN, LED6_PIN_INVERSE, LED6_MODE, LED6_RELAY - 1 }); #endif #if LED7_PIN != GPIO_NONE _leds.push_back((led_t) { LED7_PIN, LED7_PIN_INVERSE, LED7_MODE, LED7_RELAY - 1 }); #endif #if LED8_PIN != GPIO_NONE _leds.push_back((led_t) { LED8_PIN, LED8_PIN_INVERSE, LED8_MODE, LED8_RELAY - 1 }); #endif for (unsigned char i=0; i < _leds.size(); i++) { if (!hasSetting({"ledMode", i})) setSetting({"ledMode", i}, _leds[i].mode); if (!hasSetting({"ledRelay", i})) setSetting({"ledRelay", i}, _leds[i].relay); pinMode(_leds[i].pin, OUTPUT); _ledStatus(i, false); } _ledConfigure(); #if MQTT_SUPPORT mqttRegister(_ledMQTTCallback); #endif #if WEB_SUPPORT wsRegister() .onVisible(_ledWebSocketOnVisible) .onConnected(_ledWebSocketOnConnected) .onKeyCheck(_ledWebSocketOnKeyCheck); #endif #if BROKER_SUPPORT StatusBroker::Register(_ledBrokerCallback); #endif DEBUG_MSG_P(PSTR("[LED] Number of leds: %d\n"), _leds.size()); // Main callbacks espurnaRegisterLoop(ledLoop); espurnaRegisterReload(_ledConfigure); } void ledLoop() { uint8_t wifi_state = wifiState(); for (unsigned char i=0; i<_leds.size(); i++) { if (_ledMode(i) == LED_MODE_WIFI) { if (wifi_state & WIFI_STATE_WPS || wifi_state & WIFI_STATE_SMARTCONFIG) { _ledBlink(i, 100, 100); } else if (wifi_state & WIFI_STATE_STA) { _ledBlink(i, 4900, 100); } else if (wifi_state & WIFI_STATE_AP) { _ledBlink(i, 900, 100); } else { _ledBlink(i, 500, 500); } } if (_ledMode(i) == LED_MODE_FINDME_WIFI) { if (wifi_state & WIFI_STATE_WPS || wifi_state & WIFI_STATE_SMARTCONFIG) { _ledBlink(i, 100, 100); } else if (wifi_state & WIFI_STATE_STA) { if (relayStatus(_leds[i].relay)) { _ledBlink(i, 4900, 100); } else { _ledBlink(i, 100, 4900); } } else if (wifi_state & WIFI_STATE_AP) { if (relayStatus(_leds[i].relay)) { _ledBlink(i, 900, 100); } else { _ledBlink(i, 100, 900); } } else { _ledBlink(i, 500, 500); } } if (_ledMode(i) == LED_MODE_RELAY_WIFI) { if (wifi_state & WIFI_STATE_WPS || wifi_state & WIFI_STATE_SMARTCONFIG) { _ledBlink(i, 100, 100); } else if (wifi_state & WIFI_STATE_STA) { if (relayStatus(_leds[i].relay)) { _ledBlink(i, 100, 4900); } else { _ledBlink(i, 4900, 100); } } else if (wifi_state & WIFI_STATE_AP) { if (relayStatus(_leds[i].relay)) { _ledBlink(i, 100, 900); } else { _ledBlink(i, 900, 100); } } else { _ledBlink(i, 500, 500); } } // Relay-based modes, update only if relays have been updated if (!_led_update) continue; if (_ledMode(i) == LED_MODE_FOLLOW) { _ledStatus(i, relayStatus(_leds[i].relay)); } if (_ledMode(i) == LED_MODE_FOLLOW_INVERSE) { _ledStatus(i, !relayStatus(_leds[i].relay)); } if (_ledMode(i) == LED_MODE_FINDME) { bool status = true; for (unsigned char k=0; k