diff --git a/usermods/BME280_v2/README.md b/usermods/BME280_v2/README.md new file mode 100644 index 000000000..216ca6300 --- /dev/null +++ b/usermods/BME280_v2/README.md @@ -0,0 +1,40 @@ +Hello! I have written a v2 usermod for the BME280/BMP280 sensor based on the [existing v1 usermod](https://github.com/Aircoookie/WLED/blob/master/usermods/Wemos_D1_mini%2BWemos32_mini_shield/usermod_bme280.cpp). It is not just a refactor, there are many changes which I made to fit my use case, and I hope they will fit the use cases of others as well! Most notably, this usermod is *just* for the BME280 and does not control a display like in the v1 usermod designed for the WeMos shield. + +- Requires libraries `BME280@~3.0.0` (by [finitespace](https://github.com/finitespace/BME280)) and `Wire`. Please add these under `lib_deps` in your `platform.ini` (or `platform_override.ini`). +- Data is published over MQTT so make sure you've enabled the MQTT sync interface. +- This usermod also writes to serial (GPIO1 on ESP8266). Please make sure nothing else listening on the serial TX pin of your board will get confused by log messages! + +To enable, compile with `USERMOD_BME280` defined (i.e. `platformio_override.ini`) +```ini +build_flags = + ${common.build_flags_esp8266} + -D USERMOD_BME280 +``` +or define `USERMOD_BME280` in `my_config.h` +```c++ +#define USERMOD_BME280 +``` + +Changes include: +- Adjustable measure intervals + - Temperature and pressure have separate intervals due to pressure not frequently changing at any constant altitude +- Adjustment of number of decimal places in published sensor values + - Separate adjustment for temperature, humidity and pressure values + - Values are rounded to the specified number of decimal places +- Pressure measured in units of hPa instead of Pa +- Calculation of heat index (apparent temperature) and dew point + - These, along with humidity measurements, are disabled if the sensor is a BMP280 +- 16x oversampling of sensor during measurement +- Values are only published if they are different from the previous value +- Values are published on startup (continually until the MQTT broker acknowledges a successful publication) + +Adjustments are made through preprocessor definitions at the start of the class definition. + +MQTT topics are as follows: +Measurement type | MQTT topic +--- | --- +Temperature | `/temperature` +Humidity | `/humidity` +Pressure | `/pressure` +Heat index | `/heat_index` +Dew point | `/dew_point` \ No newline at end of file diff --git a/usermods/BME280_v2/usermod_bme280.h b/usermods/BME280_v2/usermod_bme280.h new file mode 100644 index 000000000..80a31a4f0 --- /dev/null +++ b/usermods/BME280_v2/usermod_bme280.h @@ -0,0 +1,212 @@ +#pragma once + +#include "wled.h" +#include +#include +#include // BME280 sensor +#include // BME280 extended measurements + +class UsermodBME280 : public Usermod +{ +private: +// User-defined configuration +#define Celsius // Show temperature mesaurement in Celcius. Comment out for Fahrenheit +#define TemperatureDecimals 1 // Number of decimal places in published temperaure values +#define HumidityDecimals 0 // Number of decimal places in published humidity values +#define PressureDecimals 2 // Number of decimal places in published pressure values +#define TemperatureInterval 5 // Interval to measure temperature (and humidity, dew point if available) in seconds +#define PressureInterval 300 // Interval to measure pressure in seconds + +// Sanity checks +#if !defined(TemperatureDecimals) || TemperatureDecimals < 0 + #define TemperatureDecimals 0 +#endif +#if !defined(HumidityDecimals) || HumidityDecimals < 0 + #define HumidityDecimals 0 +#endif +#if !defined(PressureDecimals) || PressureDecimals < 0 + #define PressureDecimals 0 +#endif +#if !defined(TemperatureInterval) || TemperatureInterval < 0 + #define TemperatureInterval 1 +#endif +#if !defined(PressureInterval) || PressureInterval < 0 + #define PressureInterval TemperatureInterval +#endif + +#ifdef ARDUINO_ARCH_ESP32 // ESP32 boards + uint8_t SCL_PIN = 22; + uint8_t SDA_PIN = 21; +#else // ESP8266 boards + uint8_t SCL_PIN = 5; + uint8_t SDA_PIN = 4; + //uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8 +#endif + + // BME280 sensor settings + BME280I2C::Settings settings{ + BME280::OSR_X16, // Temperature oversampling x16 + BME280::OSR_X16, // Humidity oversampling x16 + BME280::OSR_X16, // Pressure oversampling x16 + // Defaults + BME280::Mode_Forced, + BME280::StandbyTime_1000ms, + BME280::Filter_Off, + BME280::SpiEnable_False, + BME280I2C::I2CAddr_0x76 // I2C address. I2C specific. Default 0x76 + }; + + BME280I2C bme{settings}; + + uint8_t SensorType; + + // Measurement timers + long timer; + long lastTemperatureMeasure = 0; + long lastPressureMeasure = 0; + + // Current sensor values + float SensorTemperature; + float SensorHumidity; + float SensorHeatIndex; + float SensorDewPoint; + float SensorPressure; + // Track previous sensor values + float lastTemperature; + float lastHumidity; + float lastHeatIndex; + float lastDewPoint; + float lastPressure; + + // Store packet IDs of MQTT publications + uint16_t mqttTemperaturePub = 0; + uint16_t mqttPressurePub = 0; + + void UpdateBME280Data(int SensorType) + { + float _temperature, _humidity, _pressure; + #ifdef Celsius + BME280::TempUnit tempUnit(BME280::TempUnit_Celsius); + EnvironmentCalculations::TempUnit envTempUnit(EnvironmentCalculations::TempUnit_Celsius); + #else + BME280::TempUnit tempUnit(BME280::TempUnit_Fahrenheit); + EnvironmentCalculations::TempUnit envTempUnit(EnvironmentCalculations::TempUnit_Fahrenheit); + #endif + BME280::PresUnit presUnit(BME280::PresUnit_hPa); + + bme.read(_pressure, _temperature, _humidity, tempUnit, presUnit); + + SensorTemperature = _temperature; + SensorHumidity = _humidity; + SensorPressure = _pressure; + if (SensorType == 1) + { + SensorHeatIndex = EnvironmentCalculations::HeatIndex(_temperature, _humidity, envTempUnit); + SensorDewPoint = EnvironmentCalculations::DewPoint(_temperature, _humidity, envTempUnit); + } + } + +public: + void setup() + { + Wire.begin(SDA_PIN, SCL_PIN); + + if (!bme.begin()) + { + SensorType = 0; + Serial.println("Could not find BME280I2C sensor!"); + } + else + { + switch (bme.chipModel()) + { + case BME280::ChipModel_BME280: + SensorType = 1; + Serial.println("Found BME280 sensor! Success."); + break; + case BME280::ChipModel_BMP280: + SensorType = 2; + Serial.println("Found BMP280 sensor! No Humidity available."); + break; + default: + SensorType = 0; + Serial.println("Found UNKNOWN sensor! Error!"); + } + } + } + + void loop() + { + // BME280 sensor MQTT publishing + // Check if sensor present and MQTT Connected, otherwise it will crash the MCU + if (SensorType != 0 && mqtt != nullptr) + { + // Timer to fetch new temperature, humidity and pressure data at intervals + timer = millis(); + + if (timer - lastTemperatureMeasure >= TemperatureInterval * 1000 || mqttTemperaturePub == 0) + { + lastTemperatureMeasure = timer; + + UpdateBME280Data(SensorType); + + float Temperature = roundf(SensorTemperature * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals); + float Humidity, HeatIndex, DewPoint; + + // If temperature has changed since last measure, create string populated with device topic + // from the UI and values read from sensor, then publish to broker + if (Temperature != lastTemperature) + { + String topic = String(mqttDeviceTopic) + "/temperature"; + mqttTemperaturePub = mqtt->publish(topic.c_str(), 0, false, String(Temperature, TemperatureDecimals).c_str()); + } + + lastTemperature = Temperature; // Update last sensor temperature for next loop + + if (SensorType == 1) // Only if sensor is a BME280 + { + Humidity = roundf(SensorHumidity * pow(10, HumidityDecimals)) / pow(10, HumidityDecimals); + HeatIndex = roundf(SensorHeatIndex * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals); + DewPoint = roundf(SensorDewPoint * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals); + + if (Humidity != lastHumidity) + { + String topic = String(mqttDeviceTopic) + "/humidity"; + mqtt->publish(topic.c_str(), 0, false, String(Humidity, HumidityDecimals).c_str()); + } + + if (HeatIndex != lastHeatIndex) + { + String topic = String(mqttDeviceTopic) + "/heat_index"; + mqtt->publish(topic.c_str(), 0, false, String(HeatIndex, TemperatureDecimals).c_str()); + } + + if (DewPoint != lastDewPoint) + { + String topic = String(mqttDeviceTopic) + "/dew_point"; + mqtt->publish(topic.c_str(), 0, false, String(DewPoint, TemperatureDecimals).c_str()); + } + + lastHumidity = Humidity; + lastHeatIndex = HeatIndex; + lastDewPoint = DewPoint; + } + } + + if (timer - lastPressureMeasure >= PressureInterval * 1000 || mqttPressurePub == 0) + { + lastPressureMeasure = timer; + + float Pressure = roundf(SensorPressure * pow(10, PressureDecimals)) / pow(10, PressureDecimals); + + if (Pressure != lastPressure) + { + String topic = String(mqttDeviceTopic) + "/pressure"; + mqttPressurePub = mqtt->publish(topic.c_str(), 0, true, String(Pressure, PressureDecimals).c_str()); + } + + lastPressure = Pressure; + } + } + } +}; \ No newline at end of file diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h new file mode 100644 index 000000000..4cc16f0c3 --- /dev/null +++ b/wled00/bus_manager.h @@ -0,0 +1,245 @@ +#ifndef BusManager_h +#define BusManager_h + +/* + * Class for addressing various light types + */ + +#include "wled.h" +#include "bus_wrapper.h" + +class BusManager { + public: + BusManager() { + + }; + + int add(uint8_t busType, uint8_t* pins, uint16_t len = 1) { + if (numBusses >= WLED_MAX_BUSSES) return -1; + if (IS_DIGITAL(busType)) { + busses[numBusses] = new BusDigital(busType, pins, len, numBusses); + } else { + busses[numBusses] = new BusPwm(busType, pins); + } + numBusses++; + return numBusses -1; + } + + void removeAll() { + for (uint8_t i = 0; i < numBusses; i++) delete busses[i]; + numBusses = 0; + } + //void remove(uint8_t id); + + void show() { + for (uint8_t i = 0; i < numBusses; i++) { + busses[i]->show(); + } + } + + void setPixelColor(uint16_t pix, uint32_t c) { + for (uint8_t i = 0; i < numBusses; i++) { + Bus* b = busses[i]; + uint16_t bstart = b->getStart(); + if (pix < bstart) continue; + busses[i]->setPixelColor(pix - bstart, c); + } + } + + void setBrightness(uint8_t b) { + for (uint8_t i = 0; i < numBusses; i++) { + busses[i]->setBrightness(b); + } + } + + private: + uint8_t numBusses = 0; + Bus* busses[WLED_MAX_BUSSES]; +}; + +//parent class of BusDigital and BusPwm +class Bus { + public: + Bus(uint8_t type) { + _type = type; + }; + + virtual void show() {} + + virtual void setPixelColor(uint16_t pix, uint32_t c) {}; + + virtual void setBrightness(uint8_t b) { _bri = b; }; + + virtual uint32_t getPixelColor(uint16_t pix) { return 0; }; + + virtual ~Bus() { //throw the bus under the bus + + } + + uint16_t getStart() { + return _start; + } + + void setStart(uint16_t start) { + _start = start; + } + + virtual uint8_t getColorOrder() { + return COL_ORDER_RGB; + } + + virtual void setColorOrder() {} + + uint8_t getType() { + return _type; + } + + bool isOk() { + return _valid; + } + + protected: + uint8_t _type = TYPE_NONE; + uint8_t _bri = 255; + uint16_t _start; + bool _valid = false; +}; + + +class BusDigital : public Bus { + public: + BusDigital(uint8_t type, uint8_t* pins, uint16_t len, uint8_t nr) : Bus(type) { + if (!IS_DIGITAL(type) || !len) return; + _pins[0] = pins[0]; + if (IS_2PIN(type)) _pins[1] = pins[1]; + _len = len; + _iType = PolyBus::getI(type, _pins, nr); + if (_iType == I_NONE) return; + _valid = true; + }; + + void show() { + PolyBus::show(_busPtr, _iType); + } + + void setPixelColor(uint16_t pix, uint32_t c) { + + } + + uint8_t getColorOrder() { + return _colorOrder; + } + + void setColorOrder(uint8_t colorOrder) { + if (colorOrder > 5) return; + _colorOrder = colorOrder; + } + + private: + uint8_t _colorOrder = COL_ORDER_GRB; + uint8_t _pins[2] = {255, 255}; + uint8_t _iType = I_NONE; + uint16_t _len = 0; + void * _busPtr = nullptr; +}; + + +class BusPwm : public Bus { + public: + BusPwm(uint8_t type, uint8_t* pins) : Bus(type) { + if (!IS_PWM(type)) return; + uint8_t numPins = NUM_PWM_PINS(type); + + #ifdef ESP8266 + analogWriteRange(255); //same range as one RGB channel + analogWriteFreq(WLED_PWM_FREQ_ESP8266); + #else + _ledcStart = pinManager.allocateLedc(numPins); + if (_ledcStart == 255) { //no more free LEDC channels + deallocatePins(); return; + } + #endif + + for (uint8_t i = 0; i < numPins; i++) { + _pins[i] = pins[i]; + if (!pinManager.allocatePin(_pins[i])) { + deallocatePins(); return; + } + #ifdef ESP8266 + pinMode(_pins[i], OUTPUT); + #else + ledcSetup(_ledcStart + i, WLED_PWM_FREQ_ESP32, 8); + ledcAttachPin(_pins[i], _ledcStart + i); + #endif + } + + _valid = true; + }; + + void setPixelColor(uint16_t pix, uint32_t c) { + if (pix != 0 || !_valid) return; //only react to first pixel + uint8_t r = c >> 16; + uint8_t g = c >> 8; + uint8_t b = c ; + uint8_t w = c >> 24; + + switch (_type) { + case TYPE_ANALOG_1CH: //one channel (white), use highest RGBW value + _data[0] = max(r, max(g, max(b, w))); break; + + case TYPE_ANALOG_2CH: //warm white + cold white, we'll need some nice handling here, for now just R+G channels + case TYPE_ANALOG_3CH: //standard dumb RGB + case TYPE_ANALOG_4CH: //RGBW + case TYPE_ANALOG_5CH: //we'll want the white handling from 2CH here + RGB + _data[0] = r; _data[1] = g; _data[2] = b; _data[3] = w; _data[4] = 0; break; + + default: return; + } + } + + //does no index check + uint32_t getPixelColor(uint16_t pix) { + return ((_data[3] << 24) | (_data[0] << 16) | (_data[1] << 8) | (_data[2])); + } + + void show() { + uint8_t numPins = NUM_PWM_PINS(_type); + for (uint8_t i = 0; i < numPins; i++) { + uint8_t scaled = (_data[i] * _bri) / 255; + #ifdef ESP8266 + analogWrite(_pins[i], scaled); + #else + ledcWrite(_ledcStart + i, scaled); + #endif + } + } + + ~BusPwm() { + deallocatePins(); + }; + + private: + uint8_t _pins[5]; + uint8_t _data[5] = {255, 255, 255, 255, 255}; + #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++) { + if (!pinManager.isPinOk(_pins[i])) continue; + #ifdef ESP8266 + digitalWrite(_pins[i], LOW); //turn off PWM interrupt + #else + if (_ledcStart < 16) ledcDetachPin(_pins[i], _ledcStart + i); + #endif + pinManager.deallocatePin(_pins[i]); + } + #ifdef ARDUINO_ARCH_ESP32 + pinManager.deallocateLedc(_ledcStart, numPins); + #endif + } +}; + +#endif diff --git a/wled00/bus_wrapper.h b/wled00/bus_wrapper.h new file mode 100644 index 000000000..98b3c789f --- /dev/null +++ b/wled00/bus_wrapper.h @@ -0,0 +1,248 @@ +#ifndef BusWrapper_h +#define BusWrapper_h + +#include "wled.h" + +//Hardware SPI Pins +#define P_8266_HS_MOSI 13 +#define P_8266_HS_CLK 14 +#define P_32_HS_MOSI 13 +#define P_32_HS_CLK 14 +#define P_32_VS_MOSI 23 +#define P_32_VS_CLK 18 + +//The dirty list of possible bus types. Quite a lot... +#define I_NONE 0 +//ESP8266 RGB +#define I_8266_U0_NEO_3 1 +#define I_8266_U1_NEO_3 2 +#define I_8266_DM_NEO_3 3 +#define I_8266_BB_NEO_3 4 +//RGBW +#define I_8266_U0_NEO_4 5 +#define I_8266_U1_NEO_4 6 +#define I_8266_DM_NEO_4 7 +#define I_8266_BB_NEO_4 8 +//400Kbps +#define I_8266_U0_400_3 9 +#define I_8266_U1_400_3 10 +#define I_8266_DM_400_3 11 +#define I_8266_BB_400_3 12 +//TM1418 (RGBW) +#define I_8266_U0_TM1_4 13 +#define I_8266_U1_TM1_4 14 +#define I_8266_DM_TM1_4 15 +#define I_8266_BB_TM1_4 16 + +/*** ESP32 Neopixel methods ***/ +//RGB +#define I_32_R0_NEO_3 17 +#define I_32_R1_NEO_3 18 +#define I_32_R2_NEO_3 19 +#define I_32_R3_NEO_3 20 +#define I_32_R4_NEO_3 21 +#define I_32_R5_NEO_3 22 +#define I_32_R6_NEO_3 23 +#define I_32_R7_NEO_3 24 +#define I_32_I0_NEO_3 25 +#define I_32_I1_NEO_3 26 +//RGBW +#define I_32_R0_NEO_4 27 +#define I_32_R1_NEO_4 28 +#define I_32_R2_NEO_4 29 +#define I_32_R3_NEO_4 30 +#define I_32_R4_NEO_4 31 +#define I_32_R5_NEO_4 32 +#define I_32_R6_NEO_4 33 +#define I_32_R7_NEO_4 34 +#define I_32_I0_NEO_4 35 +#define I_32_I1_NEO_4 36 +//400Kbps +#define I_32_R0_400_3 37 +#define I_32_R1_400_3 38 +#define I_32_R2_400_3 39 +#define I_32_R3_400_3 40 +#define I_32_R4_400_3 41 +#define I_32_R5_400_3 42 +#define I_32_R6_400_3 43 +#define I_32_R7_400_3 44 +#define I_32_I0_400_3 45 +#define I_32_I1_400_3 46 +//TM1418 (RGBW) +#define I_32_R0_TM1_4 47 +#define I_32_R1_TM1_4 48 +#define I_32_R2_TM1_4 49 +#define I_32_R3_TM1_4 50 +#define I_32_R4_TM1_4 51 +#define I_32_R5_TM1_4 52 +#define I_32_R6_TM1_4 53 +#define I_32_R7_TM1_4 54 +#define I_32_I0_TM1_4 55 +#define I_32_I1_TM1_4 56 +//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S) + +//APA102 +#define I_HS_DOT_3 57 //hardware SPI +#define I_SS_DOT_3 58 //soft SPI + +//LPD8806 +#define I_HS_LPD_3 59 +#define I_SS_LPD_3 60 + +//WS2801 +#define I_HS_WS1_3 61 +#define I_SS_WS1_3 62 + +//P9813 +#define I_HS_P98_3 63 +#define I_SS_P98_3 64 + + +/*** ESP8266 Neopixel methods ***/ +#ifdef ESP8266 +//RGB +#define B_8266_U0_NEO_3 NeoPixelBrightnessBus //3 chan, esp8266, gpio1 +#define B_8266_U1_NEO_3 NeoPixelBrightnessBus //3 chan, esp8266, gpio2 +#define B_8266_DM_NEO_3 NeoPixelBrightnessBus //3 chan, esp8266, gpio3 +#define B_8266_BB_NEO_3 NeoPixelBrightnessBus //3 chan, esp8266, bb (any pin) +//RGBW +#define B_8266_U0_NEO_4 NeoPixelBrightnessBus //4 chan, esp8266, gpio1 +#define B_8266_U1_NEO_4 NeoPixelBrightnessBus //4 chan, esp8266, gpio2 +#define B_8266_DM_NEO_4 NeoPixelBrightnessBus //4 chan, esp8266, gpio3 +#define B_8266_BB_NEO_4 NeoPixelBrightnessBus //4 chan, esp8266, bb (any pin) +//400Kbps +#define B_8266_U0_400_3 NeoPixelBrightnessBus //3 chan, esp8266, gpio1 +#define B_8266_U1_400_3 NeoPixelBrightnessBus //3 chan, esp8266, gpio2 +#define B_8266_DM_400_3 NeoPixelBrightnessBus //3 chan, esp8266, gpio3 +#define B_8266_BB_400_3 NeoPixelBrightnessBus //3 chan, esp8266, bb (any pin) +//TM1418 (RGBW) +#define B_8266_U0_TM1_4 NeoPixelBrightnessBus +#define B_8266_U1_TM1_4 NeoPixelBrightnessBus +#define B_8266_DM_TM1_4 NeoPixelBrightnessBus +#define B_8266_BB_TM1_4 NeoPixelBrightnessBus +#endif + +/*** ESP32 Neopixel methods ***/ +#ifdef ARDUINO_ARCH_ESP32 +//RGB +#define B_32_R0_NEO_3 NeoPixelBrightnessBus +#define B_32_R1_NEO_3 NeoPixelBrightnessBus +#define B_32_R2_NEO_3 NeoPixelBrightnessBus +#define B_32_R3_NEO_3 NeoPixelBrightnessBus +#define B_32_R4_NEO_3 NeoPixelBrightnessBus +#define B_32_R5_NEO_3 NeoPixelBrightnessBus +#define B_32_R6_NEO_3 NeoPixelBrightnessBus +#define B_32_R7_NEO_3 NeoPixelBrightnessBus +#define B_32_I0_NEO_3 NeoPixelBrightnessBus +#define B_32_I1_NEO_3 NeoPixelBrightnessBus +//RGBW +#define B_32_R0_NEO_4 NeoPixelBrightnessBus +#define B_32_R1_NEO_4 NeoPixelBrightnessBus +#define B_32_R2_NEO_4 NeoPixelBrightnessBus +#define B_32_R3_NEO_4 NeoPixelBrightnessBus +#define B_32_R4_NEO_4 NeoPixelBrightnessBus +#define B_32_R5_NEO_4 NeoPixelBrightnessBus +#define B_32_R6_NEO_4 NeoPixelBrightnessBus +#define B_32_R7_NEO_4 NeoPixelBrightnessBus +#define B_32_I0_NEO_4 NeoPixelBrightnessBus +#define B_32_I1_NEO_4 NeoPixelBrightnessBus +//400Kbps +#define B_32_R0_400_3 NeoPixelBrightnessBus +#define B_32_R1_400_3 NeoPixelBrightnessBus +#define B_32_R2_400_3 NeoPixelBrightnessBus +#define B_32_R3_400_3 NeoPixelBrightnessBus +#define B_32_R4_400_3 NeoPixelBrightnessBus +#define B_32_R5_400_3 NeoPixelBrightnessBus +#define B_32_R6_400_3 NeoPixelBrightnessBus +#define B_32_R7_400_3 NeoPixelBrightnessBus +#define B_32_I0_400_3 NeoPixelBrightnessBus +#define B_32_I1_400_3 NeoPixelBrightnessBus +//TM1418 (RGBW) +#define B_32_R0_TM1_4 NeoPixelBrightnessBus +#define B_32_R1_TM1_4 NeoPixelBrightnessBus +#define B_32_R2_TM1_4 NeoPixelBrightnessBus +#define B_32_R3_TM1_4 NeoPixelBrightnessBus +#define B_32_R4_TM1_4 NeoPixelBrightnessBus +#define B_32_R5_TM1_4 NeoPixelBrightnessBus +#define B_32_R6_TM1_4 NeoPixelBrightnessBus +#define B_32_R7_TM1_4 NeoPixelBrightnessBus +#define B_32_I0_TM1_4 NeoPixelBrightnessBus +#define B_32_I1_TM1_4 NeoPixelBrightnessBus +//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S) + +#endif + +//APA102 +#define B_HS_DOT_3 NeoPixelBrightnessBus //hardware SPI +#define B_SS_DOT_3 NeoPixelBrightnessBus //soft SPI + +//LPD8806 +#define B_HS_LPD_3 NeoPixelBrightnessBus +#define B_SS_LPD_3 NeoPixelBrightnessBus + +//WS2801 +#define B_HS_WS1_3 NeoPixelBrightnessBus +#define B_SS_WS1_3 NeoPixelBrightnessBus + +//P9813 +#define B_HS_P98_3 NeoPixelBrightnessBus +#define B_SS_P98_3 NeoPixelBrightnessBus + +//handles pointer type conversion for all possible bus types +class PolyBus { + public: + static void show(void* busPtr, uint8_t busType) { + (static_cast*>(busPtr))->Show(); + }; + //gives back the internal type index (I_XX_XXX_X above) for the input + static uint8_t getI(uint8_t busType, uint8_t* pins, uint8_t num = 0) { + if (!IS_DIGITAL(busType)) return I_NONE; + if (IS_2PIN(busType)) { //SPI LED chips + bool isHSPI = false; + #ifdef ESP8266 + if (pins[0] == P_8266_HS_MOSI && pins[1] == P_8266_HS_CLK) isHSPI = true; + #else + if (pins[0] == P_32_HS_MOSI && pins[1] == P_32_HS_CLK) isHSPI = true; + if (pins[0] == P_32_VS_MOSI && pins[1] == P_V2_HS_CLK) isHSPI = true; + #endif + uint8_t t = I_NONE; + switch (busType) { + case TYPE_APA102: t = I_SS_DOT_3; + case TYPE_LPD8806: t = I_SS_LPD_3; + case TYPE_WS2801: t = I_SS_WS1_3; + case TYPE_P9813: t = I_SS_P98_3; + } + if (t > I_NONE && isHSPI) t--; //hardware SPI has one smaller ID than software + return t; + } else { + #ifdef ESP8266 + uint8_t offset = pins[0] -1; //for driver: 0 = uart0, 1 = uart1, 2 = dma, 3 = bitbang + if (offset > 3) offset = 3; + switch (busType) { + case TYPE_WS2812_RGB: + case TYPE_WS2812_WWA: + return I_8266_U0_NEO_3 + offset; + case TYPE_SK6812_RGBW: + return I_8266_U0_NEO_4 + offset; + case TYPE_WS2811_400KHZ: + return I_8266_U0_400_3 + offset; + } + #else //ESP32 + uint8_t offset = num; //RMT bus # == bus index in BusManager + if (offset > 9) return I_NONE; + switch (busType) { + case TYPE_WS2812_RGB: + case TYPE_WS2812_WWA: + return I_32_R0_NEO_3 + offset; + case TYPE_SK6812_RGBW: + return I_32_R0_NEO_4 + offset; + case TYPE_WS2811_400KHZ: + return I_32_R0_400_3 + offset; + } + #endif + } + return I_NONE; + } +}; + +#endif \ No newline at end of file diff --git a/wled00/const.h b/wled00/const.h index ee6755366..3b74bb6f6 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -13,6 +13,12 @@ //increase if you need more #define WLED_MAX_USERMODS 4 +#ifdef ESP8266 +#define WLED_MAX_BUSSES 2 +#else +#define WLED_MAX_BUSSES 8 +#endif + //Usermod IDs #define USERMOD_ID_RESERVED 0 //Unused. Might indicate no usermod present #define USERMOD_ID_UNSPECIFIED 1 //Default value for a general user mod that does not specify a custom ID @@ -89,6 +95,7 @@ #define TYPE_WS2812_RGB 22 #define TYPE_GS8608 23 //same driver as WS2812, but will require signal 2x per second (else displays test pattern) #define TYPE_WS2811_400KHZ 24 //half-speed WS2812 protocol, used by very old WS2811 units +#define TYPE_TM1814 25 #define TYPE_SK6812_RGBW 30 //"Analog" types (PWM) (32-47) #define TYPE_ONOFF 40 //binary output (relays etc.) @@ -102,8 +109,11 @@ #define TYPE_APA102 51 #define TYPE_LPD8806 52 #define TYPE_P9813 53 -#define TYPE_TM1814 54 +#define IS_DIGITAL(t) (t & 0x10) //digital are 16-31 and 48-63 +#define IS_PWM(t) (t > 40 && t < 46) +#define NUM_PWM_PINS(t) (t - 40) //for analog PWM 41-45 only +#define IS_2PIN(t) (t > 47) //Color orders #define COL_ORDER_GRB 0 //GRB(w),defaut @@ -171,7 +181,11 @@ #define E131_MAX_UNIVERSE_COUNT 9 -#define ABL_MILLIAMPS_DEFAULT 850; // auto lower brightness to stay close to milliampere limit +#define ABL_MILLIAMPS_DEFAULT 850 // auto lower brightness to stay close to milliampere limit + +// PWM settings +#define WLED_PWM_FREQ_ESP8266 880 //PWM frequency proven as good for LEDs +#define WLED_PWM_FREQ_ESP32 5000 #define TOUCH_THRESHOLD 32 // limit to recognize a touch, higher value means more sensitive diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index a1c60eeef..d52d5edf0 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -155,6 +155,7 @@ class PinManagerClass { uint8_t pinAlloc[3] = {0x00, 0x00, 0x00}; //24bit, 1 bit per pin, we use first 17bits #else uint8_t pinAlloc[5] = {0x00, 0x00, 0x00, 0x00, 0x00}; //40bit, 1 bit per pin, we use all bits + uint8_t ledcAlloc[2] = {0x00, 0x00}; //16 LEDC channels #endif public: @@ -162,6 +163,10 @@ class PinManagerClass { bool allocatePin(byte gpio, bool output = true); bool isPinAllocated(byte gpio); bool isPinOk(byte gpio, bool output = true); + #ifdef ARDUINO_ARCH_ESP32 + byte allocateLedc(byte channels); + void deallocateLedc(byte pos, byte channels); + #endif }; //playlist.cpp diff --git a/wled00/json.cpp b/wled00/json.cpp index 7c3ce05f2..755f80965 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -341,7 +341,6 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme if (errorFlag) root[F("error")] = errorFlag; root[F("ps")] = currentPreset; - root[F("pss")] = savedPresets; root[F("pl")] = (presetCyclingEnabled) ? 0: -1; usermods.addToJsonState(root); @@ -603,7 +602,7 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient) for (uint16_t i= 0; i < used; i += n) { - olen += sprintf(obuf + olen, "\"%06X\",", strip.getPixelColor(i)); + olen += sprintf(obuf + olen, "\"%06X\",", strip.getPixelColor(i) & 0xFFFFFF); } olen -= 1; oappend((const char*)F("],\"n\":")); diff --git a/wled00/pin_manager.cpp b/wled00/pin_manager.cpp index 66986fa87..919ece0e8 100644 --- a/wled00/pin_manager.cpp +++ b/wled00/pin_manager.cpp @@ -51,4 +51,42 @@ bool PinManagerClass::isPinOk(byte gpio, bool output) #endif return false; -} \ No newline at end of file +} + +#ifdef ARDUINO_ARCH_ESP32 +byte PinManagerClass::allocateLedc(byte channels) +{ + if (channels > 16 || channels == 0) return 255; + byte ca = 0; + for (byte i = 0; i < 16; i++) { + byte by = i >> 3; + byte bi = i - 8*by; + if (bitRead(ledcAlloc[by], bi)) { //found occupied pin + ca = 0; + } else { + ca++; + } + if (ca >= channels) { //enough free channels + byte in = (i + 1) - ca; + for (byte j = 0; j < ca; j++) { + byte b = in + j; + byte by = b >> 3; + byte bi = b - 8*by; + bitWrite(ledcAlloc[by], bi, true); + } + return in; + } + } + return 255; //not enough consecutive free LEDC channels +} + +void PinManagerClass::deallocateLedc(byte pos, byte channels) +{ + for (byte j = pos; j < pos + channels; j++) { + if (j > 16) return; + byte by = j >> 3; + byte bi = j - 8*by; + bitWrite(ledcAlloc[by], bi, false); + } +} +#endif \ No newline at end of file diff --git a/wled00/usermods_list.cpp b/wled00/usermods_list.cpp index 810127a7d..ecd733a9e 100644 --- a/wled00/usermods_list.cpp +++ b/wled00/usermods_list.cpp @@ -13,11 +13,18 @@ #ifdef USERMOD_DALLASTEMPERATURE #include "../usermods/Temperature/usermod_temperature.h" #endif + //#include "usermod_v2_empty.h" + #ifdef USERMOD_BUZZER #include "../usermods/buzzer/usermod_v2_buzzer.h" #endif +// BME280 v2 usermod. Define "USERMOD_BME280" in my_config.h +#ifdef USERMOD_BME280 +#include "../usermods/BME280_v2/usermod_bme280.h" +#endif + void registerUsermods() { /* @@ -26,11 +33,18 @@ void registerUsermods() * \/ \/ \/ */ //usermods.add(new MyExampleUsermod()); + #ifdef USERMOD_DALLASTEMPERATURE usermods.add(new UsermodTemperature()); #endif + //usermods.add(new UsermodRenameMe()); + #ifdef USERMOD_BUZZER usermods.add(new BuzzerUsermod()); #endif + + #ifdef USERMOD_BME280 + usermods.add(new UsermodBME280()); + #endif } \ No newline at end of file diff --git a/wled00/wled.h b/wled00/wled.h index 789016336..319ebe018 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -501,7 +501,6 @@ WLED_GLOBAL JsonDocument* fileDoc; WLED_GLOBAL bool doCloseFile _INIT(false); // presets -WLED_GLOBAL uint16_t savedPresets _INIT(0); WLED_GLOBAL int16_t currentPreset _INIT(-1); WLED_GLOBAL bool isPreset _INIT(false);