Skip to content

Commit

Permalink
Move LED types to individual bus type
Browse files Browse the repository at this point in the history
- (credit @netmindz & @willmmiles)
Tuning.
  • Loading branch information
blazoncek committed Sep 5, 2024
1 parent 55d98ff commit 9cc333b
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 67 deletions.
147 changes: 85 additions & 62 deletions wled00/bus_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,32 @@ void BusDigital::setColorOrder(uint8_t colorOrder) {
_colorOrder = colorOrder;
}

// credit @willmmiles & @netmindz https://github.com/Aircoookie/WLED/pull/4056
std::vector<LEDType> BusDigital::getLEDTypes() {
return {
{TYPE_WS2812_RGB, "D", PSTR("WS281x")},
{TYPE_SK6812_RGBW, "D", PSTR("SK6812/WS2814 RGBW")},
{TYPE_TM1814, "D", PSTR("TM1814")},
{TYPE_WS2811_400KHZ, "D", PSTR("400kHz")},
{TYPE_TM1829, "D", PSTR("TM1829")},
{TYPE_UCS8903, "D", PSTR("UCS8903")},
{TYPE_APA106, "D", PSTR("APA106/PL9823")},
{TYPE_TM1914, "D", PSTR("TM1914")},
{TYPE_FW1906, "D", PSTR("FW1906 GRBCW")},
{TYPE_UCS8904, "D", PSTR("UCS8904 RGBW")},
{TYPE_WS2805, "D", PSTR("WS2805 RGBCW")},
{TYPE_SM16825, "D", PSTR("SM16825 RGBCW")},
{TYPE_WS2812_1CH_X3, "D", PSTR("WS2811 White")},
//{TYPE_WS2812_2CH_X3, "D", PSTR("WS2811 CCT")}, // not implemented
//{TYPE_WS2812_WWA, "D", PSTR("WS2811 WWA")}, // not implemented
{TYPE_WS2801, "2P", PSTR("WS2801")},
{TYPE_APA102, "2P", PSTR("APA102")},
{TYPE_LPD8806, "2P", PSTR("LPD8806")},
{TYPE_LPD6803, "2P", PSTR("LPD6803")},
{TYPE_P9813, "2P", PSTR("PP9813")},
};
}

void BusDigital::reinit(void) {
if (!_valid) return;
PolyBus::begin(_busPtr, _iType, _pins);
Expand Down Expand Up @@ -406,11 +432,11 @@ void BusDigital::cleanup(void) {
#endif

BusPwm::BusPwm(BusConfig &bc)
: Bus(bc.type, bc.start, bc.autoWhite, 1, bc.reversed, bc.refreshReq) // hijack Off refresh flag to indicate usage of phase shifting
: Bus(bc.type, bc.start, bc.autoWhite, 1, bc.reversed, bc.refreshReq) // hijack Off refresh flag to indicate usage of dithering
{
if (!isPWM(bc.type)) return;
unsigned numPins = numPWMPins(bc.type);
unsigned dithering = 0;
[[maybe_unused]] const bool dithering = _needsRefresh;
_frequency = bc.frequency ? bc.frequency : WLED_PWM_FREQ;
// duty cycle resolution (_depth) can be extracted from this formula: CLOCK_FREQUENCY > _frequency * 2^_depth
for (_depth = MAX_BIT_WIDTH; _depth > 8; _depth--) if (((CLOCK_FREQUENCY/_frequency) >> _depth) > 0) break;
Expand All @@ -430,10 +456,7 @@ BusPwm::BusPwm(BusConfig &bc)
return;
}
// if _needsRefresh is true (UI hack) we are using dithering (credit @dedehai & @zalatnaicsongor)
if (_needsRefresh) {
_depth = 12; // fixed 8 bit depth PWM with 4 bit dithering (ESP8266 has no hardware to support dithering)
dithering = 4;
}
if (dithering) _depth = 12; // fixed 8 bit depth PWM with 4 bit dithering (ESP8266 has no hardware to support dithering)
#endif

for (unsigned i = 0; i < numPins; i++) {
Expand All @@ -442,7 +465,7 @@ BusPwm::BusPwm(BusConfig &bc)
pinMode(_pins[i], OUTPUT);
#else
unsigned channel = _ledcStart + i;
ledcSetup(channel, _frequency, _depth - dithering);
ledcSetup(channel, _frequency, _depth - (dithering*4)); // with dithering _frequency doesn't really matter as resolution is 8 bit
ledcAttachPin(_pins[i], channel);
// LEDC timer reset credit @dedehai
uint8_t group = (channel / 8), timer = ((channel / 2) % 4); // same fromula as in ledcSetup()
Expand Down Expand Up @@ -521,7 +544,7 @@ void BusPwm::show() {
const bool dithering = _needsRefresh; // avoid working with bitfield
const unsigned numPins = getPins();
const unsigned maxBri = (1<<_depth); // possible values: 16384 (14), 8192 (13), 4096 (12), 2048 (11), 1024 (10), 512 (9) and 256 (8)
const unsigned bitShift = dithering * 4; // if dithering, _depth is 12 bit but LEDC channel is set to 8 bit (using 4 fractional bits)
[[maybe_unused]] const unsigned bitShift = dithering * 4; // if dithering, _depth is 12 bit but LEDC channel is set to 8 bit (using 4 fractional bits)

// use CIE brightness formula (cubic) to fit (or approximate linearity of) human eye perceived brightness
// the formula is based on 12 bit resolution as there is no need for greater precision
Expand Down Expand Up @@ -581,6 +604,18 @@ uint8_t BusPwm::getPins(uint8_t* pinArray) const {
return numPins;
}

// credit @willmmiles & @netmindz https://github.com/Aircoookie/WLED/pull/4056
std::vector<LEDType> BusPwm::getLEDTypes() {
return {
{TYPE_ANALOG_1CH, "A", PSTR("PWM White")},
{TYPE_ANALOG_2CH, "AA", PSTR("PWM CCT")},
{TYPE_ANALOG_3CH, "AAA", PSTR("PWM RGB")},
{TYPE_ANALOG_4CH, "AAAA", PSTR("PWM RGBW")},
{TYPE_ANALOG_5CH, "AAAAA", PSTR("PWM RGB+CCT")},
//{TYPE_ANALOG_6CH, "AAAAAA", PSTR("PWM RGB+DCCT")}, // unimplementable ATM
};
}

void BusPwm::deallocatePins(void) {
unsigned numPins = getPins();
for (unsigned i = 0; i < numPins; i++) {
Expand All @@ -602,7 +637,7 @@ BusOnOff::BusOnOff(BusConfig &bc)
: Bus(bc.type, bc.start, bc.autoWhite, 1, bc.reversed)
, _onoffdata(0)
{
if (bc.type != TYPE_ONOFF) return;
if (!Bus::isOnOff(bc.type)) return;

uint8_t currentPin = bc.pins[0];
if (!pinManager.allocatePin(currentPin, true, PinOwner::BusOnOff)) {
Expand Down Expand Up @@ -644,6 +679,12 @@ uint8_t BusOnOff::getPins(uint8_t* pinArray) const {
return 1;
}

// credit @willmmiles & @netmindz https://github.com/Aircoookie/WLED/pull/4056
std::vector<LEDType> BusOnOff::getLEDTypes() {
return {
{TYPE_ONOFF, "", PSTR("On/Off")},
};
}

BusNetwork::BusNetwork(BusConfig &bc)
: Bus(bc.type, bc.start, bc.autoWhite, bc.count)
Expand Down Expand Up @@ -701,6 +742,21 @@ uint8_t BusNetwork::getPins(uint8_t* pinArray) const {
return 4;
}

// credit @willmmiles & @netmindz https://github.com/Aircoookie/WLED/pull/4056
std::vector<LEDType> BusNetwork::getLEDTypes() {
return {
{TYPE_NET_DDP_RGB, "N", PSTR("DDP RGB (network)")}, // should be "NNNN" to determine 4 "pin" fields
{TYPE_NET_ARTNET_RGB, "N", PSTR("Art-Net RGB (network)")},
{TYPE_NET_DDP_RGBW, "N", PSTR("DDP RGBW (network)")},
{TYPE_NET_ARTNET_RGBW, "N", PSTR("Art-Net RGBW (network)")},
// hypothetical extensions
//{TYPE_VIRTUAL_I2C_W, "V", PSTR("I2C White (virtual)")}, // allows setting I2C address in _pin[0]
//{TYPE_VIRTUAL_I2C_CCT, "V", PSTR("I2C CCT (virtual)")}, // allows setting I2C address in _pin[0]
//{TYPE_VIRTUAL_I2C_RGB, "VVV", PSTR("I2C RGB (virtual)")}, // allows setting I2C address in _pin[0] and 2 additional values in _pin[1] & _pin[2]
//{TYPE_USERMOD, "VVVVV", PSTR("Usermod (virtual)")}, // 5 data fields (see https://github.com/Aircoookie/WLED/pull/4123)
};
}

void BusNetwork::cleanup(void) {
_type = I_NONE;
_valid = false;
Expand Down Expand Up @@ -748,63 +804,30 @@ int BusManager::add(BusConfig &bc) {
return numBusses++;
}

// idea by @netmindz https://github.com/Aircoookie/WLED/pull/4056
String BusManager::getLEDTypesJSONString(void) {
struct LEDType {
uint8_t id;
const char *type;
const char *name;
} types[] = {
{TYPE_WS2812_RGB, "D", PSTR("WS281x")},
{TYPE_SK6812_RGBW, "D", PSTR("SK6812/WS2814 RGBW")},
{TYPE_TM1814, "D", PSTR("TM1814")},
{TYPE_WS2811_400KHZ, "D", PSTR("400kHz")},
{TYPE_TM1829, "D", PSTR("TM1829")},
{TYPE_UCS8903, "D", PSTR("UCS8903")},
{TYPE_APA106, "D", PSTR("APA106/PL9823")},
{TYPE_TM1914, "D", PSTR("TM1914")},
{TYPE_FW1906, "D", PSTR("FW1906 GRBCW")},
{TYPE_UCS8904, "D", PSTR("UCS8904 RGBW")},
{TYPE_WS2805, "D", PSTR("WS2805 RGBCW")},
{TYPE_SM16825, "D", PSTR("SM16825 RGBCW")},
{TYPE_WS2812_1CH_X3, "D", PSTR("WS2811 White")},
//{TYPE_WS2812_2CH_X3, "D", PSTR("WS2811 CCT")}, // not implemented
//{TYPE_WS2812_WWA, "D", PSTR("WS2811 WWA")}, // not implemented
{TYPE_WS2801, "2P", PSTR("WS2801")},
{TYPE_APA102, "2P", PSTR("APA102")},
{TYPE_LPD8806, "2P", PSTR("LPD8806")},
{TYPE_LPD6803, "2P", PSTR("LPD6803")},
{TYPE_P9813, "2P", PSTR("PP9813")},
{TYPE_ONOFF, "", PSTR("On/Off")},
{TYPE_ANALOG_1CH, "A", PSTR("PWM White")},
{TYPE_ANALOG_2CH, "AA", PSTR("PWM CCT")},
{TYPE_ANALOG_3CH, "AAA", PSTR("PWM RGB")},
{TYPE_ANALOG_4CH, "AAAA", PSTR("PWM RGBW")},
{TYPE_ANALOG_5CH, "AAAAA", PSTR("PWM RGB+CCT")},
//{TYPE_ANALOG_6CH, "AAAAAA", PSTR("PWM RGB+DCCT")}, // unimplementable ATM
{TYPE_NET_DDP_RGB, "N", PSTR("DDP RGB (network)")},
{TYPE_NET_ARTNET_RGB, "N", PSTR("Art-Net RGB (network)")},
{TYPE_NET_DDP_RGBW, "N", PSTR("DDP RGBW (network)")},
{TYPE_NET_ARTNET_RGBW, "N", PSTR("Art-Net RGBW (network)")},
// hypothetical extensions
//{TYPE_VIRTUAL_I2C_W, "V", PSTR("I2C White (virtual)")}, // allows setting I2C address in _pin[0]
//{TYPE_VIRTUAL_I2C_CCT, "V", PSTR("I2C CCT (virtual)")}, // allows setting I2C address in _pin[0]
//{TYPE_VIRTUAL_I2C_RGB, "V", PSTR("I2C RGB (virtual)")}, // allows setting I2C address in _pin[0]
};
String json = "[";
// credit @willmmiles
static String LEDTypesToJson(const std::vector<LEDType>& types) {
String json;
for (const auto &type : types) {
String id = String(type.id);
// capabilities follows similar pattern as JSON API
// capabilities follows similar pattern as JSON API
int capabilities = Bus::hasRGB(type.id) | Bus::hasWhite(type.id)<<1 | Bus::hasCCT(type.id)<<2 | Bus::is16bit(type.id)<<4;
json += "{i:" + id
+ F(",c:") + String(capabilities)
+ F(",t:\"") + FPSTR(type.type)
+ F("\",n:\"") + FPSTR(type.name) + F("\"},");
char str[256];
sprintf_P(str, PSTR("{i:%d,c:%d,t:\"%s\",n:\"%s\"},"), type.id, capabilities, type.type, type.name);
json += str;
}
json.setCharAt(json.length()-1, ']'); // replace last comma with bracket
return json;
}

// credit @willmmiles & @netmindz https://github.com/Aircoookie/WLED/pull/4056
String BusManager::getLEDTypesJSONString(void) {
String json = "[";
json += LEDTypesToJson(BusDigital::getLEDTypes());
json += LEDTypesToJson(BusOnOff::getLEDTypes());
json += LEDTypesToJson(BusPwm::getLEDTypes());
json += LEDTypesToJson(BusNetwork::getLEDTypes());
//json += LEDTypesToJson(BusVirtual::getLEDTypes());
json.setCharAt(json.length()-1, ']'); // replace last comma with bracket
return json;
}

void BusManager::useParallelOutput(void) {
_parallelOutputs = 8; // hardcoded since we use NPB I2S x8 methods
Expand Down Expand Up @@ -844,7 +867,7 @@ void BusManager::esp32RMTInvertIdle(void) {
if (u >= _parallelOutputs + 8) return; // only 8 RMT channels
rmt = u - _parallelOutputs;
#endif
if (busses[u]->getLength()==0 || !Bus::isDigital(busses[u]->getType()) || IS_2PIN(busses[u]->getType())) continue;
if (busses[u]->getLength()==0 || !busses[u]->isDigital() || busses[u]->is2Pin()) continue;
//assumes that bus number to rmt channel mapping stays 1:1
rmt_channel_t ch = static_cast<rmt_channel_t>(rmt);
rmt_idle_level_t lvl;
Expand All @@ -863,7 +886,7 @@ void BusManager::on(void) {
if (pinManager.getPinOwner(LED_BUILTIN) == PinOwner::BusDigital) {
for (unsigned i = 0; i < numBusses; i++) {
uint8_t pins[2] = {255,255};
if (Bus::isDigital(busses[i]->getType()) && busses[i]->getPins(pins)) {
if (busses[i]->isDigital() && busses[i]->getPins(pins)) {
if (pins[0] == LED_BUILTIN || pins[1] == LED_BUILTIN) {
BusDigital *bus = static_cast<BusDigital*>(busses[i]);
bus->reinit();
Expand Down
23 changes: 18 additions & 5 deletions wled00/bus_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ struct ColorOrderMap {
};


typedef struct {
uint8_t id;
const char *type;
const char *name;
} LEDType;


//parent class of BusDigital, BusPwm, and BusNetwork
class Bus {
public:
Expand Down Expand Up @@ -109,6 +116,7 @@ class Bus {
inline bool isOffRefreshRequired(void) const { return _needsRefresh; }
inline bool containsPixel(uint16_t pix) const { return pix >= _start && pix < _start + _len; }

static std::vector<LEDType> getLEDTypes(void) { return {{TYPE_NONE, "", PSTR("None")}}; }
static constexpr uint8_t getNumberOfPins(uint8_t type) { return isVirtual(type) ? 4 : isPWM(type) ? numPWMPins(type) : is2Pin(type) + 1; } // credit @PaoloTK
static constexpr uint8_t getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); }
static constexpr bool hasRGB(uint8_t type) {
Expand Down Expand Up @@ -226,6 +234,8 @@ class BusDigital : public Bus {
void reinit(void);
void cleanup(void);

static std::vector<LEDType> getLEDTypes(void);

private:
uint8_t _skip;
uint8_t _colorOrder;
Expand Down Expand Up @@ -266,6 +276,8 @@ class BusPwm : public Bus {
void show(void) override;
void cleanup(void) { deallocatePins(); }

static std::vector<LEDType> getLEDTypes(void);

private:
uint8_t _pins[5];
uint8_t _pwmdata[5];
Expand All @@ -290,6 +302,8 @@ class BusOnOff : public Bus {
void show(void) override;
void cleanup(void) { pinManager.deallocatePin(_pin, PinOwner::BusOnOff); }

static std::vector<LEDType> getLEDTypes(void);

private:
uint8_t _pin;
uint8_t _onoffdata;
Expand All @@ -308,6 +322,8 @@ class BusNetwork : public Bus {
void show(void) override;
void cleanup(void);

static std::vector<LEDType> getLEDTypes(void);

private:
IPAddress _client;
uint8_t _UDPtype;
Expand Down Expand Up @@ -346,10 +362,7 @@ struct BusConfig {
{
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)
size_t nPins = 1;
if (Bus::isVirtual(type)) nPins = 4; //virtual network bus. 4 "pins" store IP address
else if (Bus::is2Pin(type)) nPins = 2;
else if (Bus::isPWM(type)) nPins = Bus::numPWMPins(type);
size_t nPins = Bus::getNumberOfPins(type);
for (size_t i = 0; i < nPins; i++) pins[i] = ppins[i];
}

Expand Down Expand Up @@ -420,7 +433,7 @@ class BusManager {
#endif
static uint8_t getNumVirtualBusses(void) {
int j = 0;
for (int i=0; i<numBusses; i++) if (busses[i]->getType() >= TYPE_NET_DDP_RGB && busses[i]->getType() < 96) j++;
for (int i=0; i<numBusses; i++) if (busses[i]->isVirtual()) j++;
return j;
}
};
Expand Down

0 comments on commit 9cc333b

Please sign in to comment.