@ -8,29 +8,27 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
# if LED_SUPPORT
# include <algorithm>
# include "broker.h"
# include "relay.h"
# include "rpc.h"
# include "led.h"
# include "led_pattern.h"
# include "led_config.h"
// LED helper class
led_t : : led_t ( ) :
pin ( GPIO_NONE ) ,
inverse ( false ) ,
mode ( LED_MODE_MANUAL ) ,
relayID ( 0 )
{ }
led_t : : led_t ( unsigned char id ) :
pin ( _ledPin ( id ) ) ,
inverse ( _ledInverse ( id ) ) ,
mode ( _ledMode ( id ) ) ,
relayID ( _ledRelay ( id ) )
led_t : : led_t ( unsigned char pin , bool inverse , unsigned char mode , unsigned char relayID ) :
pin ( pin ) ,
inverse ( inverse ) ,
mode ( mode ) ,
relayID ( relayID )
{
if ( pin ! = GPIO_NONE ) {
pinMode ( pin , OUTPUT ) ;
status ( false ) ;
}
}
@ -48,11 +46,44 @@ bool led_t::toggle() {
return status ( ! status ( ) ) ;
}
led_delay_t : : led_delay_t ( unsigned long on_ms , unsigned long off_ms ) :
led_delay_t : : led_delay_t ( unsigned long on_ms , unsigned long off_ms , unsigned char repeats ) :
type ( repeats ? led_delay_mode_t : : Finite : led_delay_mode_t : : Infinite ) ,
on ( microsecondsToClockCycles ( on_ms * 1000 ) ) ,
off ( microsecondsToClockCycles ( off_ms * 1000 ) )
off ( microsecondsToClockCycles ( off_ms * 1000 ) ) ,
repeats ( repeats ? repeats : 0 )
{ }
led_delay_t : : led_delay_t ( unsigned long on_ms , unsigned long off_ms ) :
led_delay_t ( on_ms , off_ms , 0 )
{ }
led_pattern_t : : led_pattern_t ( const std : : vector < led_delay_t > & delays ) :
delays ( delays ) ,
queue ( ) ,
clock_last ( ESP . getCycleCount ( ) ) ,
clock_delay ( delays . size ( ) ? delays . back ( ) . on : 0 )
{ }
bool led_pattern_t : : started ( ) {
return queue . size ( ) > 0 ;
}
bool led_pattern_t : : ready ( ) {
return delays . size ( ) > 0 ;
}
void led_pattern_t : : start ( ) {
clock_last = ESP . getCycleCount ( ) ;
clock_delay = 0 ;
queue = {
delays . rbegin ( ) , delays . rend ( )
} ;
}
void led_pattern_t : : stop ( ) {
queue . clear ( ) ;
}
// For relay-based modes
bool _led_update = false ;
@ -71,10 +102,51 @@ std::vector<led_t> _leds;
// -----------------------------------------------------------------------------
unsigned char _ ledCount( ) {
unsigned char ledCount ( ) {
return _leds . size ( ) ;
}
bool _ledStatus ( led_t & led ) {
return led . pattern . started ( ) | | led . status ( ) ;
}
bool _ledStatus ( led_t & led , bool status ) {
bool result = false ;
// when led has pattern, status depends on whether it's running
if ( led . pattern . ready ( ) ) {
if ( status ) {
if ( ! led . pattern . started ( ) ) {
led . pattern . start ( ) ;
}
result = true ;
} else {
led . pattern . stop ( ) ;
led . status ( false ) ;
result = false ;
}
// if not, simply proxy status directly to the led pin
} else {
result = led . status ( status ) ;
}
return result ;
}
bool _ledToggle ( led_t & led ) {
return _ledStatus ( led , ! _ledStatus ( led ) ) ;
}
bool ledStatus ( unsigned char id , bool status ) {
if ( id > = ledCount ( ) ) return false ;
return _ledStatus ( _leds [ id ] , status ) ;
}
bool ledStatus ( unsigned char id ) {
if ( id > = ledCount ( ) ) return false ;
return _ledStatus ( _leds [ id ] ) ;
}
const led_delay_t & _ledModeToDelay ( LedMode mode ) {
static_assert (
( sizeof ( _ledDelays ) / sizeof ( _ledDelays [ 0 ] ) ) < = static_cast < int > ( LedMode : : None ) ,
@ -83,6 +155,32 @@ const led_delay_t& _ledModeToDelay(LedMode mode) {
return _ledDelays [ static_cast < int > ( mode ) ] ;
}
void _ledPattern ( led_t & led ) {
const auto clock_current = ESP . getCycleCount ( ) ;
if ( clock_current - led . pattern . clock_last > = led . pattern . clock_delay ) {
const bool status = led . toggle ( ) ;
auto & current = led . pattern . queue . back ( ) ;
switch ( current . type ) {
case led_delay_mode_t : : Finite :
if ( status & & ! - - current . repeats ) {
led . pattern . queue . pop_back ( ) ;
if ( ! led . pattern . queue . size ( ) ) {
led . status ( false ) ;
return ;
}
}
break ;
case led_delay_mode_t : : Infinite :
case led_delay_mode_t : : None :
default :
break ;
}
led . pattern . clock_delay = status ? current . on : current . off ;
led . pattern . clock_last = ESP . getCycleCount ( ) ;
}
}
void _ledBlink ( led_t & led , const led_delay_t & delays ) {
static auto clock_last = ESP . getCycleCount ( ) ;
static auto delay_for = delays . on ;
@ -105,18 +203,29 @@ bool _ledWebSocketOnKeyCheck(const char * key, JsonVariant& value) {
}
void _ledWebSocketOnVisible ( JsonObject & root ) {
if ( _ ledCount( ) > 0 ) {
if ( ledCount ( ) > 0 ) {
root [ " ledVisible " ] = 1 ;
}
}
void _ledWebSocketOnConnected ( JsonObject & root ) {
if ( ! _ledCount ( ) ) return ;
JsonArray & leds = root . createNestedArray ( " ledConfig " ) ;
for ( unsigned char id = 0 ; id < _ledCount ( ) ; + + id ) {
JsonObject & led = leds . createNestedObject ( ) ;
led [ " mode " ] = getSetting ( { " ledMode " , id } , _leds [ id ] . mode ) ;
led [ " relay " ] = getSetting < unsigned char > ( { " ledRelay " , id } , _leds [ id ] . relayID ) ;
if ( ! ledCount ( ) ) return ;
JsonObject & module = root . createNestedObject ( " led " ) ;
JsonArray & schema = module . createNestedArray ( " schema " ) ;
schema . add ( " GPIO " ) ;
schema . add ( " Inv " ) ;
schema . add ( " Mode " ) ;
schema . add ( " Relay " ) ;
JsonArray & leds = module . createNestedArray ( " list " ) ;
for ( unsigned char index = 0 ; index < ledCount ( ) ; + + index ) {
JsonArray & led = leds . createNestedArray ( ) ;
led . add ( getSetting ( { " ledGPIO " , index } , _ledPin ( index ) ) ) ;
led . add ( static_cast < int > ( getSetting ( { " ledInv " , index } , _ledInverse ( index ) ) ) ) ;
led . add ( getSetting ( { " ledMode " , index } , _ledMode ( index ) ) ) ;
led . add ( getSetting ( { " ledRelay " , index } , _ledRelay ( index ) ) ) ;
}
}
@ -150,7 +259,7 @@ void _ledMQTTCallback(unsigned int type, const char * topic, const char * payloa
// Get led ID from after the slash when t is `led/<LED_ID>`
unsigned int ledID = magnitude . substring ( strlen ( MQTT_TOPIC_LED ) + 1 ) . toInt ( ) ;
if ( ledID > = _ ledCount( ) ) {
if ( ledID > = ledCount ( ) ) {
DEBUG_MSG_P ( PSTR ( " [LED] Wrong ledID (%d) \n " ) , ledID ) ;
return ;
}
@ -158,14 +267,21 @@ void _ledMQTTCallback(unsigned int type, const char * topic, const char * payloa
// Check if LED is managed
if ( _leds [ ledID ] . mode ! = LED_MODE_MANUAL ) return ;
// Get value based on relays payload logic (0 / off, 1 / on, 2 / toggle)
const auto value = relayParsePayload ( payload ) ;
// Action to perform is also based on relay constants ... TODO generic enum?
if ( value = = RelayStatus : : TOGGLE ) {
_leds [ ledID ] . toggle ( ) ;
} else {
_leds [ ledID ] . status ( value = = RelayStatus : : ON ) ;
// Get value based on rpc payload logic (see rpc.ino)
const auto value = rpcParsePayload ( payload ) ;
switch ( value ) {
case PayloadStatus : : On :
case PayloadStatus : : Off :
_ledStatus ( _leds [ ledID ] , ( value = = PayloadStatus : : On ) ) ;
break ;
case PayloadStatus : : Toggle :
_ledToggle ( _leds [ ledID ] ) ;
break ;
case PayloadStatus : : Unknown :
default :
_ledLoadPattern ( _leds [ ledID ] , payload ) ;
_ledStatus ( _leds [ ledID ] , true ) ;
break ;
}
}
@ -176,7 +292,9 @@ void _ledMQTTCallback(unsigned int type, const char * topic, const char * payloa
void _ledConfigure ( ) {
for ( unsigned char id = 0 ; id < _leds . size ( ) ; + + id ) {
_leds [ id ] . mode = getSetting ( { " ledMode " , id } , _ledMode ( id ) ) ;
_leds [ id ] . relayID = getSetting < unsigned char > ( { " ledRelay " , id } , _ledRelay ( id ) ) ;
_leds [ id ] . relayID = getSetting ( { " ledRelay " , id } , _ledRelay ( id ) ) ;
_leds [ id ] . pattern . stop ( ) ;
_ledLoadPattern ( _leds [ id ] , getSetting ( { " ledPattern " , id } ) . c_str ( ) ) ;
}
_led_update = true ;
}
@ -218,11 +336,20 @@ void ledSetup() {
_leds . reserve ( leds ) ;
for ( unsigned char id = 0 ; id < leds ; + + id ) {
_leds . emplace_back ( id ) ;
for ( unsigned char index = 0 ; index < leds ; + + index ) {
const auto pin = getSetting ( { " ledGPIO " , index } , _ledPin ( index ) ) ;
if ( ! gpioValid ( pin ) ) {
break ;
}
_leds . emplace_back (
pin ,
getSetting ( { " ledInv " , index } , _ledInverse ( index ) ) ,
getSetting ( { " ledMode " , index } , _ledMode ( index ) ) ,
getSetting ( { " ledRelay " , index } , _ledRelay ( index ) )
) ;
}
_ledConfigure ( ) ;
_led_update = true ;
# if MQTT_SUPPORT
mqttRegister ( _ledMQTTCallback ) ;
@ -254,103 +381,111 @@ void ledLoop() {
for ( auto & led : _leds ) {
if ( led . mode = = LED_MODE_WIFI ) {
if ( ( wifi_state & WIFI_STATE_WPS ) | | ( wifi_state & WIFI_STATE_SMARTCONFIG ) ) {
_ledBlink ( led , LedMode : : NetworkAutoconfig ) ;
} else if ( wifi_state & WIFI_STATE_STA ) {
_ledBlink ( led , LedMode : : NetworkConnected ) ;
} else if ( wifi_state & WIFI_STATE_AP ) {
_ledBlink ( led , LedMode : : NetworkConfig ) ;
} else {
_ledBlink ( led , LedMode : : NetworkIdle ) ;
}
}
if ( led . mode = = LED_MODE_FINDME_WIFI ) {
if ( ( wifi_state & WIFI_STATE_WPS ) | | ( wifi_state & WIFI_STATE_SMARTCONFIG ) ) {
_ledBlink ( led , LedMode : : NetworkAutoconfig ) ;
} else if ( wifi_state & WIFI_STATE_STA ) {
if ( relayStatus ( led . relayID ) ) {
switch ( led . mode ) {
case LED_MODE_WIFI :
if ( ( wifi_state & WIFI_STATE_WPS ) | | ( wifi_state & WIFI_STATE_SMARTCONFIG ) ) {
_ledBlink ( led , LedMode : : NetworkAutoconfig ) ;
} else if ( wifi_state & WIFI_STATE_STA ) {
_ledBlink ( led , LedMode : : NetworkConnected ) ;
} else {
_ledBlink ( led , LedMode : : NetworkConnectedInverse ) ;
}
} else if ( wifi_state & WIFI_STATE_AP ) {
if ( relayStatus ( led . relayID ) ) {
} else if ( wifi_state & WIFI_STATE_AP ) {
_ledBlink ( led , LedMode : : NetworkConfig ) ;
} else {
_ledBlink ( led , LedMode : : NetworkConfigInvers e ) ;
_ledBlink ( led , LedMode : : NetworkIdle ) ;
}
} else {
_ledBlink ( led , LedMode : : NetworkIdle ) ;
}
}
if ( led . mode = = LED_MODE_RELAY_WIFI ) {
if ( ( wifi_state & WIFI_STATE_WPS ) | | ( wifi_state & WIFI_STATE_SMARTCONFIG ) ) {
_ledBlink ( led , LedMode : : NetworkAutoconfig ) ;
} else if ( wifi_state & WIFI_STATE_STA ) {
if ( relayStatus ( led . relayID ) ) {
_ledBlink ( led , LedMode : : NetworkConnected ) ;
break ;
case LED_MODE_FINDME_WIFI :
if ( ( wifi_state & WIFI_STATE_WPS ) | | ( wifi_state & WIFI_STATE_SMARTCONFIG ) ) {
_ledBlink ( led , LedMode : : NetworkAutoconfig ) ;
} else if ( wifi_state & WIFI_STATE_STA ) {
if ( relayStatus ( led . relayID ) ) {
_ledBlink ( led , LedMode : : NetworkConnected ) ;
} else {
_ledBlink ( led , LedMode : : NetworkConnectedInverse ) ;
}
} else if ( wifi_state & WIFI_STATE_AP ) {
if ( relayStatus ( led . relayID ) ) {
_ledBlink ( led , LedMode : : NetworkConfig ) ;
} else {
_ledBlink ( led , LedMode : : NetworkConfigInverse ) ;
}
} else {
_ledBlink ( led , LedMode : : NetworkConnectedInverse ) ;
_ledBlink ( led , LedMode : : NetworkIdl e ) ;
}
} else if ( wifi_state & WIFI_STATE_AP ) {
if ( relayStatus ( led . relayID ) ) {
_ledBlink ( led , LedMode : : NetworkConfig ) ;
break ;
case LED_MODE_RELAY_WIFI :
if ( ( wifi_state & WIFI_STATE_WPS ) | | ( wifi_state & WIFI_STATE_SMARTCONFIG ) ) {
_ledBlink ( led , LedMode : : NetworkAutoconfig ) ;
} else if ( wifi_state & WIFI_STATE_STA ) {
if ( relayStatus ( led . relayID ) ) {
_ledBlink ( led , LedMode : : NetworkConnected ) ;
} else {
_ledBlink ( led , LedMode : : NetworkConnectedInverse ) ;
}
} else if ( wifi_state & WIFI_STATE_AP ) {
if ( relayStatus ( led . relayID ) ) {
_ledBlink ( led , LedMode : : NetworkConfig ) ;
} else {
_ledBlink ( led , LedMode : : NetworkConfigInverse ) ;
}
} else {
_ledBlink ( led , LedMode : : NetworkConfigInverse ) ;
_ledBlink ( led , LedMode : : NetworkIdl e ) ;
}
} else {
_ledBlink ( led , LedMode : : NetworkIdle ) ;
}
}
// Relay-based modes, update only if relays have been updated
if ( ! _led_update ) continue ;
if ( led . mode = = LED_MODE_FOLLOW ) {
led . status ( relayStatus ( led . relayID ) ) ;
}
if ( led . mode = = LED_MODE_FOLLOW_INVERSE ) {
led . status ( ! relayStatus ( led . relayID ) ) ;
}
if ( led . mode = = LED_MODE_FINDME ) {
bool status = true ;
for ( unsigned char relayID = 0 ; relayID < relayCount ( ) ; + + relayID ) {
if ( relayStatus ( relayID ) ) {
status = false ;
break ;
break ;
case LED_MODE_FOLLOW :
if ( ! _led_update ) break ;
_ledStatus ( led , relayStatus ( led . relayID ) ) ;
break ;
case LED_MODE_FOLLOW_INVERSE :
if ( ! _led_update ) break ;
led . status ( ! relayStatus ( led . relayID ) ) ;
_ledStatus ( led , ! relayStatus ( led . relayID ) ) ;
break ;
case LED_MODE_FINDME : {
if ( ! _led_update ) break ;
bool status = true ;
for ( unsigned char relayID = 0 ; relayID < relayCount ( ) ; + + relayID ) {
if ( relayStatus ( relayID ) ) {
status = false ;
break ;
}
}
_ledStatus ( led , status ) ;
break ;
}
led . status ( status ) ;
}
if ( led . mode = = LED_MODE_RELAY ) {
bool status = false ;
for ( unsigned char relayID = 0 ; relayID < relayCount ( ) ; + + relayID ) {
if ( relayStatus ( relayID ) ) {
status = true ;
break ;
case LED_MODE_RELAY : {
if ( ! _led_update ) break ;
bool status = false ;
for ( unsigned char relayID = 0 ; relayID < relayCount ( ) ; + + relayID ) {
if ( relayStatus ( relayID ) ) {
status = true ;
break ;
}
}
_ledStatus ( led , status ) ;
break ;
}
led . status ( status ) ;
}
if ( led . mode = = LED_MODE_ON ) {
led . status ( true ) ;
case LED_MODE_ON :
if ( ! _led_update ) break ;
_ledStatus ( led , true ) ;
break ;
case LED_MODE_OFF :
if ( ! _led_update ) break ;
_ledStatus ( led , false ) ;
break ;
}
if ( led . mode = = LED_MODE_OFF ) {
led . status ( false ) ;
if ( led . pattern . started ( ) ) {
_ledPattern ( led ) ;
continue ;
}
}