2020-12-01 00:33:47 +01:00
# ifndef BusManager_h
# define BusManager_h
/*
* Class for addressing various light types
*/
2021-01-15 15:43:11 +01:00
# include "const.h"
2021-01-16 00:50:43 +01:00
# include "pin_manager.h"
2020-12-07 01:39:42 +01:00
# include "bus_wrapper.h"
2021-01-17 00:34:34 +01:00
# include <Arduino.h>
2020-12-01 00:33:47 +01:00
2021-10-24 21:07:05 +02:00
//colors.cpp
2021-10-16 15:13:30 +02:00
uint32_t colorBalanceFromKelvin ( uint16_t kelvin , uint32_t rgb ) ;
2021-10-24 21:07:05 +02:00
void colorRGBtoRGBW ( byte * rgb ) ;
2021-10-16 15:13:30 +02:00
2021-09-21 22:18:55 +02:00
// enable additional debug output
# ifdef WLED_DEBUG
# ifndef ESP8266
# include <rom/rtc.h>
# endif
# define DEBUG_PRINT(x) Serial.print(x)
# define DEBUG_PRINTLN(x) Serial.println(x)
# define DEBUG_PRINTF(x...) Serial.printf(x)
# else
# define DEBUG_PRINT(x)
# define DEBUG_PRINTLN(x)
# define DEBUG_PRINTF(x...)
# endif
2021-02-27 18:46:35 +01:00
# define GET_BIT(var,bit) (((var)>>(bit))&0x01)
# define SET_BIT(var,bit) ((var)|=(uint16_t)(0x0001<<(bit)))
# define UNSET_BIT(var,bit) ((var)&=(~(uint16_t)(0x0001<<(bit))))
2021-02-26 16:05:05 +01:00
2021-10-26 20:35:45 +02:00
//color mangling macros
# define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b))))
# define R(c) (byte((c) >> 16))
# define G(c) (byte((c) >> 8))
# define B(c) (byte(c))
# define W(c) (byte((c) >> 24))
2021-01-30 20:51:36 +01:00
//temporary struct for passing bus configuration to bus
struct BusConfig {
2022-03-09 13:39:51 +01:00
uint8_t type ;
2021-04-01 12:53:01 +02:00
uint16_t count ;
uint16_t start ;
uint8_t colorOrder ;
bool reversed ;
uint8_t skipAmount ;
2021-10-07 22:57:07 +02:00
bool refreshReq ;
2022-03-09 13:39:51 +01:00
uint8_t autoWhite ;
2021-01-30 20:51:36 +01:00
uint8_t pins [ 5 ] = { LEDPIN , 255 , 255 , 255 , 255 } ;
2022-03-09 13:39:51 +01:00
BusConfig ( uint8_t busType , uint8_t * ppins , uint16_t pstart , uint16_t len = 1 , uint8_t pcolorOrder = COL_ORDER_GRB , bool rev = false , uint8_t skip = 0 , byte aw = RGBW_MODE_MANUAL_ONLY ) {
2021-10-07 22:57:07 +02:00
refreshReq = ( bool ) GET_BIT ( busType , 7 ) ;
type = busType & 0x7F ; // bit 7 may be/is hacked to include refresh info (1=refresh in off state, 0=no refresh)
2022-03-09 13:39:51 +01:00
count = len ; start = pstart ; colorOrder = pcolorOrder ; reversed = rev ; skipAmount = skip ; autoWhite = aw ;
2021-01-30 20:51:36 +01:00
uint8_t nPins = 1 ;
2021-10-06 14:30:41 +02:00
if ( type > = TYPE_NET_DDP_RGB & & type < 96 ) nPins = 4 ; //virtual network bus. 4 "pins" store IP address
2021-09-21 22:18:55 +02:00
else if ( type > 47 ) nPins = 2 ;
2021-05-22 00:13:49 +02:00
else if ( type > 40 & & type < 46 ) nPins = NUM_PWM_PINS ( type ) ;
2021-01-30 20:51:36 +01:00
for ( uint8_t i = 0 ; i < nPins ; i + + ) pins [ i ] = ppins [ i ] ;
}
2021-07-09 16:25:23 +02:00
//validates start and length and extends total if needed
bool adjustBounds ( uint16_t & total ) {
if ( ! count ) count = 1 ;
if ( count > MAX_LEDS_PER_BUS ) count = MAX_LEDS_PER_BUS ;
if ( start > = MAX_LEDS ) return false ;
//limit length of strip if it would exceed total permissible LEDs
if ( start + count > MAX_LEDS ) count = MAX_LEDS - start ;
//extend total count accordingly
if ( start + count > total ) total = start + count ;
return true ;
}
2021-01-30 20:51:36 +01:00
} ;
2021-12-31 21:35:27 +01:00
// Defines an LED Strip and its color ordering.
struct ColorOrderMapEntry {
uint16_t start ;
uint16_t len ;
uint8_t colorOrder ;
} ;
struct ColorOrderMap {
void add ( uint16_t start , uint16_t len , uint8_t colorOrder ) {
if ( _count > = WLED_MAX_COLOR_ORDER_MAPPINGS ) {
return ;
}
2022-01-01 22:08:07 +01:00
if ( len = = 0 ) {
return ;
}
if ( colorOrder > COL_ORDER_MAX ) {
return ;
}
2021-12-31 21:35:27 +01:00
_mappings [ _count ] . start = start ;
_mappings [ _count ] . len = len ;
_mappings [ _count ] . colorOrder = colorOrder ;
_count + + ;
}
uint8_t count ( ) const {
return _count ;
}
void reset ( ) {
_count = 0 ;
memset ( _mappings , 0 , sizeof ( _mappings ) ) ;
}
const ColorOrderMapEntry * get ( uint8_t n ) const {
if ( n > _count ) {
return nullptr ;
}
return & ( _mappings [ n ] ) ;
}
2022-02-01 12:02:04 +01:00
inline uint8_t IRAM_ATTR getPixelColorOrder ( uint16_t pix , uint8_t defaultColorOrder ) const {
2021-12-31 21:35:27 +01:00
if ( _count = = 0 ) return defaultColorOrder ;
2022-04-30 12:45:38 +02:00
// upper nibble containd W swap information
uint8_t swapW = defaultColorOrder > > 4 ;
2021-12-31 21:35:27 +01:00
for ( uint8_t i = 0 ; i < _count ; i + + ) {
if ( pix > = _mappings [ i ] . start & & pix < ( _mappings [ i ] . start + _mappings [ i ] . len ) ) {
2022-04-30 12:45:38 +02:00
return _mappings [ i ] . colorOrder | ( swapW < < 4 ) ;
2021-12-31 21:35:27 +01:00
}
}
return defaultColorOrder ;
}
private :
uint8_t _count ;
ColorOrderMapEntry _mappings [ WLED_MAX_COLOR_ORDER_MAPPINGS ] ;
} ;
2021-11-24 11:02:25 +01:00
//parent class of BusDigital, BusPwm, and BusNetwork
2020-12-01 00:33:47 +01:00
class Bus {
public :
2022-08-21 20:50:24 +02:00
Bus ( uint8_t type , uint16_t start , uint8_t aw )
: _bri ( 255 )
, _len ( 1 )
, _valid ( false )
, _needsRefresh ( false )
{
2021-10-16 15:13:30 +02:00
_type = type ;
_start = start ;
2022-03-09 13:39:51 +01:00
_autoWhiteMode = Bus : : isRgbw ( _type ) ? aw : RGBW_MODE_MANUAL_ONLY ;
2021-10-16 15:13:30 +02:00
} ;
2021-04-07 21:04:54 +02:00
2021-10-16 15:13:30 +02:00
virtual ~ Bus ( ) { } //throw the bus under the bus
2022-08-21 20:50:24 +02:00
virtual void show ( ) = 0 ;
2021-10-16 15:13:30 +02:00
virtual bool canShow ( ) { return true ; }
2021-11-30 16:28:26 +01:00
virtual void setStatusPixel ( uint32_t c ) { }
2022-08-21 20:50:24 +02:00
virtual void setPixelColor ( uint16_t pix , uint32_t c ) = 0 ;
2021-11-30 16:28:26 +01:00
virtual uint32_t getPixelColor ( uint16_t pix ) { return 0 ; }
2022-08-21 20:50:24 +02:00
virtual void setBrightness ( uint8_t b ) { _bri = b ; } ;
virtual void cleanup ( ) = 0 ;
2021-10-16 15:13:30 +02:00
virtual uint8_t getPins ( uint8_t * pinArray ) { return 0 ; }
2021-12-11 23:17:47 +01:00
virtual uint16_t getLength ( ) { return _len ; }
2021-10-16 15:13:30 +02:00
virtual void setColorOrder ( ) { }
virtual uint8_t getColorOrder ( ) { return COL_ORDER_RGB ; }
virtual uint8_t skippedLeds ( ) { return 0 ; }
2021-10-23 15:41:35 +02:00
inline uint16_t getStart ( ) { return _start ; }
inline void setStart ( uint16_t start ) { _start = start ; }
inline uint8_t getType ( ) { return _type ; }
inline bool isOk ( ) { return _valid ; }
inline bool isOffRefreshRequired ( ) { return _needsRefresh ; }
bool containsPixel ( uint16_t pix ) { return pix > = _start & & pix < _start + _len ; }
2021-10-16 15:13:30 +02:00
2021-10-24 21:07:05 +02:00
virtual bool isRgbw ( ) { return Bus : : isRgbw ( _type ) ; }
2021-10-16 15:13:30 +02:00
static bool isRgbw ( uint8_t type ) {
if ( type = = TYPE_SK6812_RGBW | | type = = TYPE_TM1814 ) return true ;
if ( type > TYPE_ONOFF & & type < = TYPE_ANALOG_5CH & & type ! = TYPE_ANALOG_3CH ) return true ;
2022-09-02 03:12:03 +02:00
if ( type = = TYPE_NET_DDP_RGBW ) return true ;
2021-10-16 15:13:30 +02:00
return false ;
}
2022-03-11 12:20:48 +01:00
virtual bool hasRGB ( ) {
if ( _type = = TYPE_WS2812_1CH | | _type = = TYPE_WS2812_WWA | | _type = = TYPE_ANALOG_1CH | | _type = = TYPE_ANALOG_2CH | | _type = = TYPE_ONOFF ) return false ;
return true ;
}
virtual bool hasWhite ( ) {
if ( _type = = TYPE_SK6812_RGBW | | _type = = TYPE_TM1814 | | _type = = TYPE_WS2812_1CH | | _type = = TYPE_WS2812_WWA | |
2022-09-02 03:12:03 +02:00
_type = = TYPE_ANALOG_1CH | | _type = = TYPE_ANALOG_2CH | | _type = = TYPE_ANALOG_4CH | | _type = = TYPE_ANALOG_5CH | | _type = = TYPE_NET_DDP_RGBW ) return true ;
2022-03-11 12:20:48 +01:00
return false ;
}
2021-11-28 01:21:17 +01:00
static void setCCT ( uint16_t cct ) {
2021-11-24 11:02:25 +01:00
_cct = cct ;
}
2021-11-27 16:49:42 +01:00
static void setCCTBlend ( uint8_t b ) {
if ( b > 100 ) b = 100 ;
_cctBlend = ( b * 127 ) / 100 ;
//compile-time limiter for hardware that can't power both white channels at max
# ifdef WLED_MAX_CCT_BLEND
if ( _cctBlend > WLED_MAX_CCT_BLEND ) _cctBlend = WLED_MAX_CCT_BLEND ;
# endif
}
2022-03-26 23:20:14 +01:00
inline void setAWMode ( uint8_t m ) { if ( m < 4 ) _autoWhiteMode = m ; }
inline uint8_t getAWMode ( ) { return _autoWhiteMode ; }
inline static void setAutoWhiteMode ( uint8_t m ) { if ( m < 4 ) _gAWM = m ; else _gAWM = 255 ; }
inline static uint8_t getAutoWhiteMode ( ) { return _gAWM ; }
2021-10-07 22:57:07 +02:00
2021-10-16 15:13:30 +02:00
bool reversed = false ;
2021-01-19 16:51:03 +01:00
2020-12-01 00:33:47 +01:00
protected :
2022-08-21 20:50:24 +02:00
uint8_t _type ;
uint8_t _bri ;
uint16_t _start ;
uint16_t _len ;
bool _valid ;
bool _needsRefresh ;
2022-03-26 23:20:14 +01:00
uint8_t _autoWhiteMode ;
static uint8_t _gAWM ; // definition in FX_fcn.cpp
static int16_t _cct ; // definition in FX_fcn.cpp
static uint8_t _cctBlend ; // definition in FX_fcn.cpp
2021-10-24 21:07:05 +02:00
uint32_t autoWhiteCalc ( uint32_t c ) {
2022-03-26 23:20:14 +01:00
uint8_t aWM = _autoWhiteMode ;
if ( _gAWM < 255 ) aWM = _gAWM ;
if ( aWM = = RGBW_MODE_MANUAL_ONLY ) return c ;
2021-11-24 11:02:25 +01:00
uint8_t w = W ( c ) ;
//ignore auto-white calculation if w>0 and mode DUAL (DUAL behaves as BRIGHTER if w==0)
2022-03-26 23:20:14 +01:00
if ( w > 0 & & aWM = = RGBW_MODE_DUAL ) return c ;
2021-10-27 08:17:51 +02:00
uint8_t r = R ( c ) ;
uint8_t g = G ( c ) ;
uint8_t b = B ( c ) ;
2021-11-24 11:02:25 +01:00
w = r < g ? ( r < b ? r : b ) : ( g < b ? g : b ) ;
2022-03-26 23:20:14 +01:00
if ( aWM = = RGBW_MODE_AUTO_ACCURATE ) { r - = w ; g - = w ; b - = w ; } //subtract w in ACCURATE mode
2021-10-27 08:17:51 +02:00
return RGBW32 ( r , g , b , w ) ;
2021-10-24 21:07:05 +02:00
}
2020-12-01 00:33:47 +01:00
} ;
2020-12-07 01:39:42 +01:00
2020-12-01 00:33:47 +01:00
class BusDigital : public Bus {
public :
2022-03-09 13:39:51 +01:00
BusDigital ( BusConfig & bc , uint8_t nr , const ColorOrderMap & com ) : Bus ( bc . type , bc . start , bc . autoWhite ) , _colorOrderMap ( com ) {
2021-01-30 20:51:36 +01:00
if ( ! IS_DIGITAL ( bc . type ) | | ! bc . count ) return ;
2021-08-23 14:14:48 +02:00
if ( ! pinManager . allocatePin ( bc . pins [ 0 ] , true , PinOwner : : BusDigital ) ) return ;
2021-01-30 20:51:36 +01:00
_pins [ 0 ] = bc . pins [ 0 ] ;
if ( IS_2PIN ( bc . type ) ) {
2021-08-23 14:14:48 +02:00
if ( ! pinManager . allocatePin ( bc . pins [ 1 ] , true , PinOwner : : BusDigital ) ) {
2021-01-16 19:53:08 +01:00
cleanup ( ) ; return ;
}
2021-07-06 22:08:04 +02:00
_pins [ 1 ] = bc . pins [ 1 ] ;
2021-01-16 19:53:08 +01:00
}
2021-01-30 20:51:36 +01:00
reversed = bc . reversed ;
2021-10-07 22:57:07 +02:00
_needsRefresh = bc . refreshReq | | bc . type = = TYPE_TM1814 ;
2021-04-01 12:53:01 +02:00
_skip = bc . skipAmount ; //sacrificial pixels
_len = bc . count + _skip ;
2021-10-08 08:30:06 +02:00
_iType = PolyBus : : getI ( bc . type , _pins , nr ) ;
2020-12-07 01:39:42 +01:00
if ( _iType = = I_NONE ) return ;
2021-07-03 13:52:23 +02:00
_busPtr = PolyBus : : create ( _iType , _pins , _len , nr ) ;
2021-01-16 00:50:43 +01:00
_valid = ( _busPtr ! = nullptr ) ;
2021-01-30 20:51:36 +01:00
_colorOrder = bc . colorOrder ;
2022-10-18 12:10:11 +02:00
DEBUG_PRINTF ( " %successfully inited strip %u (len %u) with type %u and pins %u,%u (itype %u) \n " , _valid ? " S " : " Uns " , nr , _len , bc . type , _pins [ 0 ] , _pins [ 1 ] , _iType ) ;
2020-12-01 00:33:47 +01:00
} ;
2021-02-25 09:54:10 +01:00
inline void show ( ) {
2020-12-07 01:39:42 +01:00
PolyBus : : show ( _busPtr , _iType ) ;
}
2021-02-25 09:54:10 +01:00
inline bool canShow ( ) {
2021-01-15 15:43:11 +01:00
return PolyBus : : canShow ( _busPtr , _iType ) ;
}
2021-01-16 00:50:43 +01:00
void setBrightness ( uint8_t b ) {
2021-01-19 17:22:37 +01:00
//Fix for turning off onboard LED breaking bus
2021-01-17 00:34:34 +01:00
# ifdef LED_BUILTIN
2021-01-19 16:51:03 +01:00
if ( _bri = = 0 & & b > 0 ) {
2021-02-17 18:03:57 +01:00
if ( _pins [ 0 ] = = LED_BUILTIN | | _pins [ 1 ] = = LED_BUILTIN ) PolyBus : : begin ( _busPtr , _iType , _pins ) ;
2021-01-19 16:51:03 +01:00
}
2021-01-17 00:34:34 +01:00
# endif
2022-08-21 20:50:24 +02:00
Bus : : setBrightness ( b ) ;
2021-01-16 00:50:43 +01:00
PolyBus : : setBrightness ( _busPtr , _iType , b ) ;
}
2021-11-30 22:52:17 +01:00
//If LEDs are skipped, it is possible to use the first as a status LED.
//TODO only show if no new show due in the next 50ms
void setStatusPixel ( uint32_t c ) {
2021-11-30 16:28:26 +01:00
if ( _skip & & canShow ( ) ) {
2021-12-31 21:35:27 +01:00
PolyBus : : setPixelColor ( _busPtr , _iType , 0 , c , _colorOrderMap . getPixelColorOrder ( _start , _colorOrder ) ) ;
2021-11-30 16:28:26 +01:00
PolyBus : : show ( _busPtr , _iType ) ;
}
}
2020-12-07 01:39:42 +01:00
void setPixelColor ( uint16_t pix , uint32_t c ) {
2021-11-28 01:21:17 +01:00
if ( _type = = TYPE_SK6812_RGBW | | _type = = TYPE_TM1814 ) c = autoWhiteCalc ( c ) ;
2021-11-24 11:02:25 +01:00
if ( _cct > = 1900 ) c = colorBalanceFromKelvin ( _cct , c ) ; //color correction from CCT
2021-01-19 16:51:03 +01:00
if ( reversed ) pix = _len - pix - 1 ;
2021-02-24 21:33:44 +01:00
else pix + = _skip ;
2021-12-31 21:35:27 +01:00
PolyBus : : setPixelColor ( _busPtr , _iType , pix , c , _colorOrderMap . getPixelColorOrder ( pix + _start , _colorOrder ) ) ;
2021-01-15 15:43:11 +01:00
}
2020-12-07 01:39:42 +01:00
2021-01-15 15:43:11 +01:00
uint32_t getPixelColor ( uint16_t pix ) {
2021-01-19 16:51:03 +01:00
if ( reversed ) pix = _len - pix - 1 ;
2021-02-24 21:33:44 +01:00
else pix + = _skip ;
2021-12-31 21:35:27 +01:00
return PolyBus : : getPixelColor ( _busPtr , _iType , pix , _colorOrderMap . getPixelColorOrder ( pix + _start , _colorOrder ) ) ;
2020-12-07 01:39:42 +01:00
}
2021-02-25 09:54:10 +01:00
inline uint8_t getColorOrder ( ) {
2020-12-01 00:33:47 +01:00
return _colorOrder ;
}
2021-12-11 23:17:47 +01:00
uint16_t getLength ( ) {
2021-02-24 20:23:32 +01:00
return _len - _skip ;
2021-01-15 15:43:11 +01:00
}
2021-01-19 16:51:03 +01:00
uint8_t getPins ( uint8_t * pinArray ) {
uint8_t numPins = IS_2PIN ( _type ) ? 2 : 1 ;
for ( uint8_t i = 0 ; i < numPins ; i + + ) pinArray [ i ] = _pins [ i ] ;
return numPins ;
}
2020-12-01 00:33:47 +01:00
void setColorOrder ( uint8_t colorOrder ) {
2022-04-30 12:45:38 +02:00
// upper nibble contains W swap information
if ( ( colorOrder & 0x0F ) > 5 ) return ;
2020-12-01 00:33:47 +01:00
_colorOrder = colorOrder ;
}
2021-05-17 16:23:46 +02:00
inline uint8_t skippedLeds ( ) {
2021-04-01 12:53:01 +02:00
return _skip ;
2021-03-29 23:12:19 +02:00
}
2021-02-25 09:54:10 +01:00
inline void reinit ( ) {
2021-02-17 18:03:57 +01:00
PolyBus : : begin ( _busPtr , _iType , _pins ) ;
2021-01-19 17:22:37 +01:00
}
2021-01-16 19:53:08 +01:00
void cleanup ( ) {
2021-10-08 08:30:06 +02:00
DEBUG_PRINTLN ( F ( " Digital Cleanup. " ) ) ;
2021-01-16 19:53:08 +01:00
PolyBus : : cleanup ( _busPtr , _iType ) ;
_iType = I_NONE ;
_valid = false ;
_busPtr = nullptr ;
2021-08-23 14:14:48 +02:00
pinManager . deallocatePin ( _pins [ 1 ] , PinOwner : : BusDigital ) ;
pinManager . deallocatePin ( _pins [ 0 ] , PinOwner : : BusDigital ) ;
2021-01-16 19:53:08 +01:00
}
2021-01-18 20:51:32 +01:00
~ BusDigital ( ) {
cleanup ( ) ;
}
2020-12-01 00:33:47 +01:00
private :
uint8_t _colorOrder = COL_ORDER_GRB ;
2020-12-07 01:39:42 +01:00
uint8_t _pins [ 2 ] = { 255 , 255 } ;
uint8_t _iType = I_NONE ;
2021-02-24 20:23:32 +01:00
uint8_t _skip = 0 ;
2020-12-07 01:39:42 +01:00
void * _busPtr = nullptr ;
2021-12-31 21:35:27 +01:00
const ColorOrderMap & _colorOrderMap ;
2020-12-01 00:33:47 +01:00
} ;
2020-12-07 01:39:42 +01:00
2020-12-01 00:33:47 +01:00
class BusPwm : public Bus {
public :
2022-03-09 13:39:51 +01:00
BusPwm ( BusConfig & bc ) : Bus ( bc . type , bc . start , bc . autoWhite ) {
2021-05-22 00:13:49 +02:00
_valid = false ;
2021-02-26 08:34:38 +01:00
if ( ! IS_PWM ( bc . type ) ) return ;
uint8_t numPins = NUM_PWM_PINS ( bc . type ) ;
2020-12-07 01:39:42 +01:00
# ifdef ESP8266
analogWriteRange ( 255 ) ; //same range as one RGB channel
2021-02-23 00:47:48 +01:00
analogWriteFreq ( WLED_PWM_FREQ ) ;
2020-12-07 01:39:42 +01:00
# else
_ledcStart = pinManager . allocateLedc ( numPins ) ;
if ( _ledcStart = = 255 ) { //no more free LEDC channels
deallocatePins ( ) ; return ;
}
# endif
2020-12-01 00:33:47 +01:00
for ( uint8_t i = 0 ; i < numPins ; i + + ) {
2021-07-09 20:06:48 +02:00
uint8_t currentPin = bc . pins [ i ] ;
2021-08-23 14:14:48 +02:00
if ( ! pinManager . allocatePin ( currentPin , true , PinOwner : : BusPwm ) ) {
2020-12-07 01:39:42 +01:00
deallocatePins ( ) ; return ;
}
2021-12-25 01:30:27 +01:00
_pins [ i ] = currentPin ; //store only after allocatePin() succeeds
2020-12-07 01:39:42 +01:00
# ifdef ESP8266
pinMode ( _pins [ i ] , OUTPUT ) ;
# else
2021-02-23 00:47:48 +01:00
ledcSetup ( _ledcStart + i , WLED_PWM_FREQ , 8 ) ;
2020-12-07 01:39:42 +01:00
ledcAttachPin ( _pins [ i ] , _ledcStart + i ) ;
# endif
}
2021-03-20 18:43:05 +01:00
reversed = bc . reversed ;
2020-12-07 01:39:42 +01:00
_valid = true ;
} ;
void setPixelColor ( uint16_t pix , uint32_t c ) {
if ( pix ! = 0 | | ! _valid ) return ; //only react to first pixel
2021-11-30 22:52:17 +01:00
if ( _type ! = TYPE_ANALOG_3CH ) c = autoWhiteCalc ( c ) ;
if ( _cct > = 1900 & & ( _type = = TYPE_ANALOG_3CH | | _type = = TYPE_ANALOG_4CH ) ) {
2021-11-24 11:02:25 +01:00
c = colorBalanceFromKelvin ( _cct , c ) ; //color correction from CCT
}
2021-10-26 20:35:45 +02:00
uint8_t r = R ( c ) ;
uint8_t g = G ( c ) ;
uint8_t b = B ( c ) ;
uint8_t w = W ( c ) ;
2021-11-24 11:02:25 +01:00
uint8_t cct = 0 ; //0 - full warm white, 255 - full cold white
if ( _cct > - 1 ) {
if ( _cct > = 1900 ) cct = ( _cct - 1900 ) > > 5 ;
else if ( _cct < 256 ) cct = _cct ;
} else {
cct = ( approximateKelvinFromRGB ( c ) - 1900 ) > > 5 ;
}
2021-10-20 20:29:13 +02:00
2021-11-27 16:49:42 +01:00
uint8_t ww , cw ;
2021-12-07 11:03:41 +01:00
# ifdef WLED_USE_IC_CCT
ww = w ;
cw = cct ;
# else
//0 - linear (CCT 127 = 50% warm, 50% cold), 127 - additive CCT blending (CCT 127 = 100% warm, 100% cold)
2021-11-27 16:49:42 +01:00
if ( cct < _cctBlend ) ww = 255 ;
else ww = ( ( 255 - cct ) * 255 ) / ( 255 - _cctBlend ) ;
if ( ( 255 - cct ) < _cctBlend ) cw = 255 ;
2021-12-01 14:51:45 +01:00
else cw = ( cct * 255 ) / ( 255 - _cctBlend ) ;
2021-11-27 16:49:42 +01:00
ww = ( w * ww ) / 255 ; //brightness scaling
cw = ( w * cw ) / 255 ;
2021-12-07 11:03:41 +01:00
# endif
2020-12-07 01:39:42 +01:00
switch ( _type ) {
2021-10-25 20:15:42 +02:00
case TYPE_ANALOG_1CH : //one channel (white), relies on auto white calculation
2021-11-24 11:02:25 +01:00
_data [ 0 ] = w ;
2021-10-20 20:29:13 +02:00
break ;
2021-10-16 15:13:30 +02:00
case TYPE_ANALOG_2CH : //warm white + cold white
2021-11-27 16:49:42 +01:00
_data [ 1 ] = cw ;
_data [ 0 ] = ww ;
2021-10-20 20:29:13 +02:00
break ;
case TYPE_ANALOG_5CH : //RGB + warm white + cold white
2021-11-27 16:49:42 +01:00
_data [ 4 ] = cw ;
w = ww ;
2021-10-20 20:29:13 +02:00
case TYPE_ANALOG_4CH : //RGBW
_data [ 3 ] = w ;
2020-12-07 01:39:42 +01:00
case TYPE_ANALOG_3CH : //standard dumb RGB
2021-10-20 20:29:13 +02:00
_data [ 0 ] = r ; _data [ 1 ] = g ; _data [ 2 ] = b ;
break ;
2020-12-07 01:39:42 +01:00
}
}
//does no index check
uint32_t getPixelColor ( uint16_t pix ) {
2021-05-22 00:13:49 +02:00
if ( ! _valid ) return 0 ;
2021-10-26 20:35:45 +02:00
return RGBW32 ( _data [ 0 ] , _data [ 1 ] , _data [ 2 ] , _data [ 3 ] ) ;
2020-12-07 01:39:42 +01:00
}
void show ( ) {
2021-05-22 00:13:49 +02:00
if ( ! _valid ) return ;
2020-12-07 01:39:42 +01:00
uint8_t numPins = NUM_PWM_PINS ( _type ) ;
for ( uint8_t i = 0 ; i < numPins ; i + + ) {
uint8_t scaled = ( _data [ i ] * _bri ) / 255 ;
2021-03-20 18:43:05 +01:00
if ( reversed ) scaled = 255 - scaled ;
2020-12-07 01:39:42 +01:00
# ifdef ESP8266
analogWrite ( _pins [ i ] , scaled ) ;
# else
ledcWrite ( _ledcStart + i , scaled ) ;
# endif
2020-12-01 00:33:47 +01:00
}
2020-12-07 01:39:42 +01:00
}
2021-01-19 16:51:03 +01:00
uint8_t getPins ( uint8_t * pinArray ) {
2021-10-06 14:30:41 +02:00
if ( ! _valid ) return 0 ;
2021-01-19 16:51:03 +01:00
uint8_t numPins = NUM_PWM_PINS ( _type ) ;
2021-02-28 22:54:30 +01:00
for ( uint8_t i = 0 ; i < numPins ; i + + ) {
pinArray [ i ] = _pins [ i ] ;
}
2021-01-19 16:51:03 +01:00
return numPins ;
}
2022-06-20 22:17:01 +02:00
void cleanup ( ) {
2020-12-07 01:39:42 +01:00
deallocatePins ( ) ;
2021-01-16 19:53:08 +01:00
}
2020-12-07 01:39:42 +01:00
2021-03-04 22:17:25 +01:00
~ BusPwm ( ) {
cleanup ( ) ;
}
2020-12-01 00:33:47 +01:00
private :
2021-01-16 19:53:08 +01:00
uint8_t _pins [ 5 ] = { 255 , 255 , 255 , 255 , 255 } ;
2021-12-25 01:30:27 +01:00
uint8_t _data [ 5 ] = { 0 } ;
2020-12-07 01:39:42 +01:00
# ifdef ARDUINO_ARCH_ESP32
uint8_t _ledcStart = 255 ;
# endif
void deallocatePins ( ) {
uint8_t numPins = NUM_PWM_PINS ( _type ) ;
for ( uint8_t i = 0 ; i < numPins ; i + + ) {
2021-08-26 09:09:41 +02:00
pinManager . deallocatePin ( _pins [ i ] , PinOwner : : BusPwm ) ;
2020-12-07 01:39:42 +01:00
if ( ! pinManager . isPinOk ( _pins [ i ] ) ) continue ;
# ifdef ESP8266
digitalWrite ( _pins [ i ] , LOW ) ; //turn off PWM interrupt
# else
2021-01-16 19:53:08 +01:00
if ( _ledcStart < 16 ) ledcDetachPin ( _pins [ i ] ) ;
2020-12-07 01:39:42 +01:00
# endif
}
# ifdef ARDUINO_ARCH_ESP32
pinManager . deallocateLedc ( _ledcStart , numPins ) ;
# endif
}
2020-12-01 00:33:47 +01:00
} ;
2021-09-20 22:24:58 +02:00
2022-06-20 22:17:01 +02:00
class BusOnOff : public Bus {
public :
BusOnOff ( BusConfig & bc ) : Bus ( bc . type , bc . start , bc . autoWhite ) {
_valid = false ;
if ( bc . type ! = TYPE_ONOFF ) return ;
uint8_t currentPin = bc . pins [ 0 ] ;
if ( ! pinManager . allocatePin ( currentPin , true , PinOwner : : BusOnOff ) ) {
2022-09-16 23:12:57 +02:00
return ;
2022-06-20 22:17:01 +02:00
}
_pin = currentPin ; //store only after allocatePin() succeeds
pinMode ( _pin , OUTPUT ) ;
reversed = bc . reversed ;
_valid = true ;
} ;
void setPixelColor ( uint16_t pix , uint32_t c ) {
if ( pix ! = 0 | | ! _valid ) return ; //only react to first pixel
c = autoWhiteCalc ( c ) ;
uint8_t r = R ( c ) ;
uint8_t g = G ( c ) ;
uint8_t b = B ( c ) ;
uint8_t w = W ( c ) ;
2022-08-09 15:20:00 +02:00
_data = bool ( ( r + g + b + w ) & & _bri ) ? 0xFF : 0 ;
2022-06-20 22:17:01 +02:00
}
uint32_t getPixelColor ( uint16_t pix ) {
if ( ! _valid ) return 0 ;
return RGBW32 ( _data , _data , _data , _data ) ;
}
void show ( ) {
if ( ! _valid ) return ;
digitalWrite ( _pin , reversed ? ! ( bool ) _data : ( bool ) _data ) ;
}
uint8_t getPins ( uint8_t * pinArray ) {
if ( ! _valid ) return 0 ;
pinArray [ 0 ] = _pin ;
return 1 ;
}
void cleanup ( ) {
2022-09-16 23:12:57 +02:00
pinManager . deallocatePin ( _pin , PinOwner : : BusOnOff ) ;
2022-06-20 22:17:01 +02:00
}
~ BusOnOff ( ) {
cleanup ( ) ;
}
private :
uint8_t _pin = 255 ;
uint8_t _data = 0 ;
} ;
2021-09-22 13:20:36 +02:00
class BusNetwork : public Bus {
2021-09-20 22:24:58 +02:00
public :
2022-03-09 13:39:51 +01:00
BusNetwork ( BusConfig & bc ) : Bus ( bc . type , bc . start , bc . autoWhite ) {
2021-09-20 22:24:58 +02:00
_valid = false ;
2021-09-27 16:29:38 +02:00
// switch (bc.type) {
// case TYPE_NET_ARTNET_RGB:
// _rgbw = false;
// _UDPtype = 2;
// break;
// case TYPE_NET_E131_RGB:
// _rgbw = false;
// _UDPtype = 1;
// break;
// case TYPE_NET_DDP_RGB:
// _rgbw = false;
// _UDPtype = 0;
// break;
2022-09-02 03:12:03 +02:00
// default: // TYPE_NET_DDP_RGB / TYPE_NET_DDP_RGBW
_rgbw = bc . type = = TYPE_NET_DDP_RGBW ;
_UDPtype = 0 ;
2021-09-27 16:29:38 +02:00
// break;
// }
_UDPchannels = _rgbw ? 4 : 3 ;
_data = ( byte * ) malloc ( bc . count * _UDPchannels ) ;
2021-09-20 22:24:58 +02:00
if ( _data = = nullptr ) return ;
2021-09-27 16:29:38 +02:00
memset ( _data , 0 , bc . count * _UDPchannels ) ;
2021-09-20 22:24:58 +02:00
_len = bc . count ;
_client = IPAddress ( bc . pins [ 0 ] , bc . pins [ 1 ] , bc . pins [ 2 ] , bc . pins [ 3 ] ) ;
_broadcastLock = false ;
_valid = true ;
} ;
2022-03-11 12:20:48 +01:00
bool hasRGB ( ) { return true ; }
bool hasWhite ( ) { return _rgbw ; }
2021-09-20 22:24:58 +02:00
void setPixelColor ( uint16_t pix , uint32_t c ) {
if ( ! _valid | | pix > = _len ) return ;
2021-12-07 13:37:28 +01:00
if ( isRgbw ( ) ) c = autoWhiteCalc ( c ) ;
2021-11-24 11:02:25 +01:00
if ( _cct > = 1900 ) c = colorBalanceFromKelvin ( _cct , c ) ; //color correction from CCT
2021-09-27 16:29:38 +02:00
uint16_t offset = pix * _UDPchannels ;
2021-10-26 20:35:45 +02:00
_data [ offset ] = R ( c ) ;
_data [ offset + 1 ] = G ( c ) ;
_data [ offset + 2 ] = B ( c ) ;
if ( _rgbw ) _data [ offset + 3 ] = W ( c ) ;
2021-09-20 22:24:58 +02:00
}
uint32_t getPixelColor ( uint16_t pix ) {
if ( ! _valid | | pix > = _len ) return 0 ;
2021-09-27 16:29:38 +02:00
uint16_t offset = pix * _UDPchannels ;
2021-10-26 20:35:45 +02:00
return RGBW32 ( _data [ offset ] , _data [ offset + 1 ] , _data [ offset + 2 ] , _rgbw ? ( _data [ offset + 3 ] < < 24 ) : 0 ) ;
2021-09-20 22:24:58 +02:00
}
void show ( ) {
2021-09-27 16:29:38 +02:00
if ( ! _valid | | ! canShow ( ) ) return ;
2021-09-20 22:24:58 +02:00
_broadcastLock = true ;
2021-10-04 13:44:44 +02:00
realtimeBroadcast ( _UDPtype , _client , _len , _data , _bri , _rgbw ) ;
2021-09-20 22:24:58 +02:00
_broadcastLock = false ;
}
inline bool canShow ( ) {
2021-09-27 16:29:38 +02:00
// this should be a return value from UDP routine if it is still sending data out
2021-09-20 22:24:58 +02:00
return ! _broadcastLock ;
}
2021-09-21 22:18:55 +02:00
uint8_t getPins ( uint8_t * pinArray ) {
for ( uint8_t i = 0 ; i < 4 ; i + + ) {
pinArray [ i ] = _client [ i ] ;
}
return 4 ;
}
2021-09-20 22:24:58 +02:00
inline bool isRgbw ( ) {
return _rgbw ;
}
inline uint16_t getLength ( ) {
return _len ;
}
void cleanup ( ) {
_type = I_NONE ;
_valid = false ;
if ( _data ! = nullptr ) free ( _data ) ;
_data = nullptr ;
}
2021-09-22 13:20:36 +02:00
~ BusNetwork ( ) {
2021-09-20 22:24:58 +02:00
cleanup ( ) ;
}
private :
IPAddress _client ;
2021-09-27 16:29:38 +02:00
uint8_t _UDPtype ;
uint8_t _UDPchannels ;
2021-09-20 22:24:58 +02:00
bool _rgbw ;
bool _broadcastLock ;
2021-10-04 13:44:44 +02:00
byte * _data ;
2021-09-20 22:24:58 +02:00
} ;
2021-01-15 15:43:11 +01:00
class BusManager {
public :
2022-03-26 23:20:14 +01:00
BusManager ( ) { } ;
2021-02-27 00:57:12 +01:00
//utility to get the approx. memory usage of a given BusConfig
2021-03-26 11:52:04 +01:00
static uint32_t memUsage ( BusConfig & bc ) {
2021-02-27 00:57:12 +01:00
uint8_t type = bc . type ;
2022-03-16 01:45:07 +01:00
uint16_t len = bc . count + bc . skipAmount ;
2021-09-22 13:20:36 +02:00
if ( type > 15 & & type < 32 ) {
2021-02-27 00:57:12 +01:00
# ifdef ESP8266
if ( bc . pins [ 0 ] = = 3 ) { //8266 DMA uses 5x the mem
if ( type > 29 ) return len * 20 ; //RGBW
return len * 15 ;
}
if ( type > 29 ) return len * 4 ; //RGBW
return len * 3 ;
# else //ESP32 RMT uses double buffer?
if ( type > 29 ) return len * 8 ; //RGBW
return len * 6 ;
# endif
}
2021-10-02 15:07:02 +02:00
if ( type > 31 & & type < 48 ) return 5 ;
2021-02-27 00:57:12 +01:00
if ( type = = 44 | | type = = 45 ) return len * 4 ; //RGBW
2021-09-22 13:20:36 +02:00
return len * 3 ; //RGB
2021-02-27 00:57:12 +01:00
}
2021-01-15 15:43:11 +01:00
2021-01-30 20:51:36 +01:00
int add ( BusConfig & bc ) {
2021-01-15 15:43:11 +01:00
if ( numBusses > = WLED_MAX_BUSSES ) return - 1 ;
2021-10-06 14:30:41 +02:00
if ( bc . type > = TYPE_NET_DDP_RGB & & bc . type < 96 ) {
2021-09-22 13:20:36 +02:00
busses [ numBusses ] = new BusNetwork ( bc ) ;
2021-09-20 22:24:58 +02:00
} else if ( IS_DIGITAL ( bc . type ) ) {
2021-12-31 21:35:27 +01:00
busses [ numBusses ] = new BusDigital ( bc , numBusses , colorOrderMap ) ;
2022-09-16 23:12:57 +02:00
} else if ( bc . type = = TYPE_ONOFF ) {
busses [ numBusses ] = new BusOnOff ( bc ) ;
2021-01-15 15:43:11 +01:00
} else {
2021-01-30 20:51:36 +01:00
busses [ numBusses ] = new BusPwm ( bc ) ;
2021-01-15 15:43:11 +01:00
}
2021-02-24 20:23:32 +01:00
return numBusses + + ;
2021-01-15 15:43:11 +01:00
}
2021-01-21 01:21:16 +01:00
//do not call this method from system context (network callback)
2021-01-15 15:43:11 +01:00
void removeAll ( ) {
2021-10-08 08:30:06 +02:00
DEBUG_PRINTLN ( F ( " Removing all. " ) ) ;
2021-01-21 01:21:16 +01:00
//prevents crashes due to deleting busses while in use.
while ( ! canAllShow ( ) ) yield ( ) ;
2021-01-15 15:43:11 +01:00
for ( uint8_t i = 0 ; i < numBusses ; i + + ) delete busses [ i ] ;
numBusses = 0 ;
}
void show ( ) {
for ( uint8_t i = 0 ; i < numBusses ; i + + ) {
busses [ i ] - > show ( ) ;
}
}
2021-11-30 22:52:17 +01:00
void setStatusPixel ( uint32_t c ) {
for ( uint8_t i = 0 ; i < numBusses ; i + + ) {
busses [ i ] - > setStatusPixel ( c ) ;
}
}
2021-11-30 16:28:26 +01:00
2022-02-01 12:02:04 +01:00
void IRAM_ATTR setPixelColor ( uint16_t pix , uint32_t c , int16_t cct = - 1 ) {
2021-01-15 15:43:11 +01:00
for ( uint8_t i = 0 ; i < numBusses ; i + + ) {
Bus * b = busses [ i ] ;
uint16_t bstart = b - > getStart ( ) ;
2021-01-18 20:51:32 +01:00
if ( pix < bstart | | pix > = bstart + b - > getLength ( ) ) continue ;
2021-12-02 13:09:53 +01:00
busses [ i ] - > setPixelColor ( pix - bstart , c ) ;
2021-01-15 15:43:11 +01:00
}
}
void setBrightness ( uint8_t b ) {
for ( uint8_t i = 0 ; i < numBusses ; i + + ) {
busses [ i ] - > setBrightness ( b ) ;
}
}
2021-11-24 11:02:25 +01:00
void setSegmentCCT ( int16_t cct , bool allowWBCorrection = false ) {
2021-11-27 01:33:48 +01:00
if ( cct > 255 ) cct = 255 ;
if ( cct > = 0 ) {
2021-11-24 11:02:25 +01:00
//if white balance correction allowed, save as kelvin value instead of 0-255
if ( allowWBCorrection ) cct = 1900 + ( cct < < 5 ) ;
} else cct = - 1 ;
Bus : : setCCT ( cct ) ;
}
2021-01-15 15:43:11 +01:00
uint32_t getPixelColor ( uint16_t pix ) {
for ( uint8_t i = 0 ; i < numBusses ; i + + ) {
Bus * b = busses [ i ] ;
uint16_t bstart = b - > getStart ( ) ;
if ( pix < bstart | | pix > = bstart + b - > getLength ( ) ) continue ;
return b - > getPixelColor ( pix - bstart ) ;
}
return 0 ;
}
bool canAllShow ( ) {
for ( uint8_t i = 0 ; i < numBusses ; i + + ) {
2021-01-18 20:51:32 +01:00
if ( ! busses [ i ] - > canShow ( ) ) return false ;
2021-01-15 15:43:11 +01:00
}
return true ;
}
2021-01-16 19:53:08 +01:00
Bus * getBus ( uint8_t busNr ) {
if ( busNr > = numBusses ) return nullptr ;
return busses [ busNr ] ;
}
2021-02-25 09:54:10 +01:00
inline uint8_t getNumBusses ( ) {
2021-01-16 19:53:08 +01:00
return numBusses ;
}
2021-11-03 16:14:01 +01:00
//semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit())
2021-02-27 12:06:14 +01:00
uint16_t getTotalLength ( ) {
uint16_t len = 0 ;
2021-11-03 16:14:01 +01:00
for ( uint8_t i = 0 ; i < numBusses ; i + + ) len + = busses [ i ] - > getLength ( ) ;
2021-02-27 12:06:14 +01:00
return len ;
}
2021-12-31 21:35:27 +01:00
void updateColorOrderMap ( const ColorOrderMap & com ) {
memcpy ( & colorOrderMap , & com , sizeof ( ColorOrderMap ) ) ;
}
const ColorOrderMap & getColorOrderMap ( ) const {
return colorOrderMap ;
}
2021-01-15 15:43:11 +01:00
private :
uint8_t numBusses = 0 ;
Bus * busses [ WLED_MAX_BUSSES ] ;
2021-12-31 21:35:27 +01:00
ColorOrderMap colorOrderMap ;
2021-01-15 15:43:11 +01:00
} ;
2021-08-23 14:14:48 +02:00
# endif