mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-30 06:06:36 +00:00
Redesign light driver phase 1
Redesign light driver phase 1
This commit is contained in:
parent
2bfa172324
commit
019dc76740
@ -315,9 +315,16 @@
|
||||
#define USE_ARMTRONIX_DIMMERS // Add support for Armtronix Dimmers (+1k4 code)
|
||||
#define USE_PS_16_DZ // Add support for PS-16-DZ Dimmer and Sonoff L1 (+2k code)
|
||||
//#define ROTARY_V1 // Add support for MI Desk Lamp
|
||||
#define USE_SM2135 // Add support for SM2135 RGBCW led control (+0k6 code)
|
||||
//#define USE_SHUTTER // Add Shutter support for up to 4 shutter with different motortypes (+6k code)
|
||||
|
||||
// -- Optional light modules ----------------------
|
||||
#define USE_WS2812 // WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by //
|
||||
// #define USE_WS2812_DMA // DMA supports only GPIO03 (= Serial RXD) (+1k mem). When USE_WS2812_DMA is enabled expect Exceptions on Pow
|
||||
#define USE_WS2812_HARDWARE NEO_HW_WS2812 // Hardware type (NEO_HW_WS2812, NEO_HW_WS2812X, NEO_HW_WS2813, NEO_HW_SK6812, NEO_HW_LC8812, NEO_HW_APA106)
|
||||
#define USE_WS2812_CTYPE NEO_GRB // Color type (NEO_RGB, NEO_GRB, NEO_BRG, NEO_RBG, NEO_RGBW, NEO_GRBW)
|
||||
#define USE_SM16716 // Add support for SM16716 RGB LED controller (+0k7 code)
|
||||
#define USE_SM2135 // Add support for SM2135 RGBCW led control as used in Action LSC (+0k6 code)
|
||||
|
||||
// -- Counter input -------------------------------
|
||||
#define USE_COUNTER // Enable inputs as counter (+0k8 code)
|
||||
|
||||
@ -513,11 +520,6 @@
|
||||
|
||||
// ------------------------------------------------
|
||||
|
||||
#define USE_WS2812 // WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by //
|
||||
// #define USE_WS2812_DMA // DMA supports only GPIO03 (= Serial RXD) (+1k mem). When USE_WS2812_DMA is enabled expect Exceptions on Pow
|
||||
#define USE_WS2812_HARDWARE NEO_HW_WS2812 // Hardware type (NEO_HW_WS2812, NEO_HW_WS2812X, NEO_HW_WS2813, NEO_HW_SK6812, NEO_HW_LC8812, NEO_HW_APA106)
|
||||
#define USE_WS2812_CTYPE NEO_GRB // Color type (NEO_RGB, NEO_GRB, NEO_BRG, NEO_RBG, NEO_RGBW, NEO_GRBW)
|
||||
|
||||
#define USE_ARILUX_RF // Add support for Arilux RF remote controller (+0k8 code, 252 iram (non 2.3.0))
|
||||
|
||||
#define USE_SR04 // Add support for HC-SR04 ultrasonic devices (+1k code)
|
||||
@ -536,8 +538,6 @@
|
||||
// #define USE_THEO_V2 // Add support for decoding Theo V2 sensors as documented on https://sidweb.nl using 434MHz RF sensor receiver (+1k4 code)
|
||||
// #define USE_ALECTO_V2 // Add support for decoding Alecto V2 sensors like ACH2010, WS3000 and DKW2012 weather stations using 868MHz RF sensor receiver (+1k7 code)
|
||||
|
||||
#define USE_SM16716 // Add support for SM16716 RGB LED controller (+0k7 code)
|
||||
|
||||
//#define USE_HRE // Add support for Badger HR-E Water Meter (+1k4 code)
|
||||
//#define USE_A4988_Stepper // Add support for A4988 stepper-motor-driver-circuit (+10k5 code)
|
||||
|
||||
|
@ -130,6 +130,7 @@ uint8_t led_power = 0; // LED power state
|
||||
uint8_t ledlnk_inverted = 0; // Link LED inverted flag (1 = (0 = On, 1 = Off))
|
||||
uint8_t pwm_inverted = 0; // PWM inverted flag (1 = inverted)
|
||||
uint8_t energy_flg = 0; // Energy monitor configured
|
||||
uint8_t light_flg = 0; // Light module configured
|
||||
uint8_t light_type = 0; // Light types
|
||||
uint8_t serial_in_byte; // Received byte
|
||||
uint8_t ota_retry_counter = OTA_ATTEMPTS; // OTA retry counter
|
||||
@ -1391,14 +1392,6 @@ void GpioInit(void)
|
||||
devices_present = 1;
|
||||
|
||||
light_type = LT_BASIC; // Use basic PWM control if SetOption15 = 0
|
||||
#ifdef USE_LIGHT
|
||||
if (Settings.flag.pwm_control) {
|
||||
for (uint32_t i = 0; i < MAX_PWMS; i++) {
|
||||
if (pin[GPIO_PWM1 +i] < 99) { light_type++; } // Use Dimmer/Color control for all PWM as SetOption15 = 1
|
||||
}
|
||||
}
|
||||
#endif // USE_LIGHT
|
||||
|
||||
if (XdrvCall(FUNC_MODULE_INIT)) {
|
||||
// Serviced
|
||||
}
|
||||
@ -1420,30 +1413,24 @@ void GpioInit(void)
|
||||
devices_present = 0;
|
||||
baudrate = 19200;
|
||||
}
|
||||
#ifdef USE_LIGHT
|
||||
else if (SONOFF_BN == my_module_type) { // PWM Single color led (White)
|
||||
light_type = LT_PWM1;
|
||||
|
||||
if (!light_type) {
|
||||
devices_present = 0;
|
||||
for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only
|
||||
if (pin[GPIO_PWM1 +i] < 99) {
|
||||
pwm_present = true;
|
||||
pinMode(pin[GPIO_PWM1 +i], OUTPUT);
|
||||
analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - Settings.pwm_value[i] : Settings.pwm_value[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (SONOFF_LED == my_module_type) { // PWM Dual color led (White warm and cold)
|
||||
light_type = LT_PWM2;
|
||||
}
|
||||
else if (AILIGHT == my_module_type) { // RGBW led
|
||||
light_type = LT_RGBW;
|
||||
}
|
||||
else if (SONOFF_B1 == my_module_type) { // RGBWC led
|
||||
light_type = LT_RGBWC;
|
||||
}
|
||||
#endif // USE_LIGHT
|
||||
else {
|
||||
if (!light_type) { devices_present = 0; }
|
||||
for (uint32_t i = 0; i < MAX_RELAYS; i++) {
|
||||
if (pin[GPIO_REL1 +i] < 99) {
|
||||
pinMode(pin[GPIO_REL1 +i], OUTPUT);
|
||||
devices_present++;
|
||||
if (EXS_RELAY == my_module_type) {
|
||||
digitalWrite(pin[GPIO_REL1 +i], bitRead(rel_inverted, i) ? 1 : 0);
|
||||
if (i &1) { devices_present--; }
|
||||
}
|
||||
for (uint32_t i = 0; i < MAX_RELAYS; i++) {
|
||||
if (pin[GPIO_REL1 +i] < 99) {
|
||||
pinMode(pin[GPIO_REL1 +i], OUTPUT);
|
||||
devices_present++;
|
||||
if (EXS_RELAY == my_module_type) {
|
||||
digitalWrite(pin[GPIO_REL1 +i], bitRead(rel_inverted, i) ? 1 : 0);
|
||||
if (i &1) { devices_present--; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1475,37 +1462,6 @@ void GpioInit(void)
|
||||
RotaryInit();
|
||||
#endif
|
||||
|
||||
#ifdef USE_LIGHT
|
||||
#ifdef USE_WS2812
|
||||
if (!light_type && (pin[GPIO_WS2812] < 99)) { // RGB led
|
||||
devices_present++;
|
||||
light_type = LT_WS2812;
|
||||
}
|
||||
#endif // USE_WS2812
|
||||
#ifdef USE_SM16716
|
||||
if (SM16716_ModuleSelected()) {
|
||||
light_type += 3;
|
||||
light_type |= LT_SM16716;
|
||||
}
|
||||
#endif // USE_SM16716
|
||||
|
||||
// post-process for lights
|
||||
if (Settings.flag3.pwm_multi_channels) {
|
||||
uint32_t pwm_channels = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7);
|
||||
if (0 == pwm_channels) { pwm_channels = 1; }
|
||||
devices_present += pwm_channels - 1; // add the pwm channels controls at the end
|
||||
}
|
||||
#endif // USE_LIGHT
|
||||
if (!light_type) {
|
||||
for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only
|
||||
if (pin[GPIO_PWM1 +i] < 99) {
|
||||
pwm_present = true;
|
||||
pinMode(pin[GPIO_PWM1 +i], OUTPUT);
|
||||
analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - Settings.pwm_value[i] : Settings.pwm_value[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetLedPower(Settings.ledstate &8);
|
||||
SetLedLink(Settings.ledstate &8);
|
||||
|
||||
|
@ -257,6 +257,7 @@ struct LIGHT {
|
||||
uint8_t wakeup_active = 0;
|
||||
uint8_t wakeup_dimmer = 0;
|
||||
uint8_t fixed_color_index = 1;
|
||||
uint8_t pwm_offset = 0; // Offset in color buffer
|
||||
|
||||
bool update = true;
|
||||
bool pwm_multi_channels = false; // SetOption68, treat each PWM channel as an independant dimmer
|
||||
@ -1269,102 +1270,66 @@ void LightMy92x1Duty(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b, uint8_t dut
|
||||
os_delay_us(12); // TStop > 12us.
|
||||
}
|
||||
|
||||
#ifdef USE_SM16716
|
||||
/*********************************************************************************************\
|
||||
* SM16716 - Controlling RGB over a synchronous serial line
|
||||
* Copyright (C) 2019 Gabor Simon
|
||||
*
|
||||
* Source: https://community.home-assistant.io/t/cheap-uk-wifi-bulbs-with-tasmota-teardown-help-tywe3s/40508/27
|
||||
*
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define D_LOG_SM16716 "SM16716: "
|
||||
|
||||
uint8_t sm16716_pin_clk = 100;
|
||||
uint8_t sm16716_pin_dat = 100;
|
||||
uint8_t sm16716_pin_sel = 100;
|
||||
uint8_t sm16716_enabled = 0;
|
||||
|
||||
void SM16716_SendBit(uint8_t v)
|
||||
{
|
||||
/* NOTE:
|
||||
* According to the spec sheet, max freq is 30 MHz, that is 16.6 ns per high/low half of the
|
||||
* clk square wave. That is less than the overhead of 'digitalWrite' at this clock rate,
|
||||
* so no additional delays are needed yet. */
|
||||
|
||||
digitalWrite(sm16716_pin_dat, (v != 0) ? HIGH : LOW);
|
||||
//delayMicroseconds(1);
|
||||
digitalWrite(sm16716_pin_clk, HIGH);
|
||||
//delayMicroseconds(1);
|
||||
digitalWrite(sm16716_pin_clk, LOW);
|
||||
}
|
||||
|
||||
void SM16716_SendByte(uint8_t v)
|
||||
{
|
||||
uint8_t mask;
|
||||
|
||||
for (mask = 0x80; mask; mask >>= 1) {
|
||||
SM16716_SendBit(v & mask);
|
||||
}
|
||||
}
|
||||
|
||||
void SM16716_Update(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b)
|
||||
{
|
||||
if (sm16716_pin_sel < 99) {
|
||||
uint8_t sm16716_should_enable = (duty_r | duty_g | duty_b);
|
||||
if (!sm16716_enabled && sm16716_should_enable) {
|
||||
DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "turning color on"));
|
||||
sm16716_enabled = 1;
|
||||
digitalWrite(sm16716_pin_sel, HIGH);
|
||||
// in testing I found it takes a minimum of ~380us to wake up the chip
|
||||
// tested on a Merkury RGBW with an SM726EB
|
||||
delayMicroseconds(1000);
|
||||
SM16716_Init();
|
||||
}
|
||||
else if (sm16716_enabled && !sm16716_should_enable) {
|
||||
DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "turning color off"));
|
||||
sm16716_enabled = 0;
|
||||
digitalWrite(sm16716_pin_sel, LOW);
|
||||
}
|
||||
}
|
||||
DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "Update; rgb=%02x%02x%02x"), duty_r, duty_g, duty_b);
|
||||
|
||||
// send start bit
|
||||
SM16716_SendBit(1);
|
||||
SM16716_SendByte(duty_r);
|
||||
SM16716_SendByte(duty_g);
|
||||
SM16716_SendByte(duty_b);
|
||||
|
||||
// send a 'do it' pulse
|
||||
// (if multiple chips are chained, each one processes the 1st '1rgb' 25-bit block and
|
||||
// passes on the rest, right until the one starting with 0)
|
||||
//SM16716_Init();
|
||||
SM16716_SendBit(0);
|
||||
SM16716_SendByte(0);
|
||||
SM16716_SendByte(0);
|
||||
SM16716_SendByte(0);
|
||||
}
|
||||
|
||||
bool SM16716_ModuleSelected(void)
|
||||
{
|
||||
sm16716_pin_clk = pin[GPIO_SM16716_CLK];
|
||||
sm16716_pin_dat = pin[GPIO_SM16716_DAT];
|
||||
sm16716_pin_sel = pin[GPIO_SM16716_SEL];
|
||||
DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "ModuleSelected; clk_pin=%d, dat_pin=%d)"), sm16716_pin_clk, sm16716_pin_dat);
|
||||
return (sm16716_pin_clk < 99) && (sm16716_pin_dat < 99);
|
||||
}
|
||||
|
||||
void SM16716_Init(void)
|
||||
{
|
||||
for (uint32_t t_init = 0; t_init < 50; ++t_init) {
|
||||
SM16716_SendBit(0);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ifdef USE_SM16716
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
void LightPwmOffset(uint32_t offset)
|
||||
{
|
||||
Light.pwm_offset = offset;
|
||||
}
|
||||
|
||||
bool LightModuleInit(void)
|
||||
{
|
||||
light_type = LT_BASIC; // Use basic PWM control if SetOption15 = 0
|
||||
|
||||
if (Settings.flag.pwm_control) {
|
||||
for (uint32_t i = 0; i < MAX_PWMS; i++) {
|
||||
if (pin[GPIO_PWM1 +i] < 99) { light_type++; } // Use Dimmer/Color control for all PWM as SetOption15 = 1
|
||||
}
|
||||
}
|
||||
|
||||
light_flg = 0;
|
||||
if (XlgtCall(FUNC_MODULE_INIT)) {
|
||||
// serviced
|
||||
}
|
||||
else if (SONOFF_BN == my_module_type) { // PWM Single color led (White)
|
||||
light_type = LT_PWM1;
|
||||
}
|
||||
else if (SONOFF_LED == my_module_type) { // PWM Dual color led (White warm and cold)
|
||||
if (!my_module.io[4]) { // Fix Sonoff Led instabilities
|
||||
pinMode(4, OUTPUT); // Stop floating outputs
|
||||
digitalWrite(4, LOW);
|
||||
}
|
||||
if (!my_module.io[5]) {
|
||||
pinMode(5, OUTPUT); // Stop floating outputs
|
||||
digitalWrite(5, LOW);
|
||||
}
|
||||
if (!my_module.io[14]) {
|
||||
pinMode(14, OUTPUT); // Stop floating outputs
|
||||
digitalWrite(14, LOW);
|
||||
}
|
||||
light_type = LT_PWM2;
|
||||
}
|
||||
else if (AILIGHT == my_module_type) { // RGBW led
|
||||
light_type = LT_RGBW;
|
||||
}
|
||||
else if (SONOFF_B1 == my_module_type) { // RGBWC led
|
||||
light_type = LT_RGBWC;
|
||||
}
|
||||
#ifdef USE_WS2812
|
||||
if (!light_type && (pin[GPIO_WS2812] < 99)) { // RGB led
|
||||
light_type = LT_WS2812;
|
||||
}
|
||||
#endif // USE_WS2812
|
||||
// post-process for lights
|
||||
if (Settings.flag3.pwm_multi_channels) {
|
||||
uint32_t pwm_channels = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7);
|
||||
if (0 == pwm_channels) { pwm_channels = 1; }
|
||||
devices_present += pwm_channels - 1; // add the pwm channels controls at the end
|
||||
}
|
||||
|
||||
return (light_type > 0);
|
||||
}
|
||||
|
||||
void LightInit(void)
|
||||
{
|
||||
uint8_t max_scheme = LS_MAX -1;
|
||||
@ -1402,20 +1367,6 @@ void LightInit(void)
|
||||
pinMode(pin[GPIO_PWM1 +i], OUTPUT);
|
||||
}
|
||||
}
|
||||
if (SONOFF_LED == my_module_type) { // Fix Sonoff Led instabilities
|
||||
if (!my_module.io[4]) {
|
||||
pinMode(4, OUTPUT); // Stop floating outputs
|
||||
digitalWrite(4, LOW);
|
||||
}
|
||||
if (!my_module.io[5]) {
|
||||
pinMode(5, OUTPUT); // Stop floating outputs
|
||||
digitalWrite(5, LOW);
|
||||
}
|
||||
if (!my_module.io[14]) {
|
||||
pinMode(14, OUTPUT); // Stop floating outputs
|
||||
digitalWrite(14, LOW);
|
||||
}
|
||||
}
|
||||
if (pin[GPIO_ARIRFRCV] < 99) {
|
||||
if (pin[GPIO_ARIRFSEL] < 99) {
|
||||
pinMode(pin[GPIO_ARIRFSEL], OUTPUT);
|
||||
@ -1429,32 +1380,6 @@ void LightInit(void)
|
||||
max_scheme = LS_MAX + WS2812_SCHEMES;
|
||||
}
|
||||
#endif // USE_WS2812 ************************************************************************
|
||||
#ifdef USE_SM16716
|
||||
else if (LT_SM16716 == light_type - Light.subtype) {
|
||||
// init PWM
|
||||
for (uint32_t i = 0; i < Light.subtype; i++) {
|
||||
Settings.pwm_value[i] = 0; // Disable direct PWM control
|
||||
if (pin[GPIO_PWM1 +i] < 99) {
|
||||
pinMode(pin[GPIO_PWM1 +i], OUTPUT);
|
||||
}
|
||||
}
|
||||
// init sm16716
|
||||
pinMode(sm16716_pin_clk, OUTPUT);
|
||||
digitalWrite(sm16716_pin_clk, LOW);
|
||||
|
||||
pinMode(sm16716_pin_dat, OUTPUT);
|
||||
digitalWrite(sm16716_pin_dat, LOW);
|
||||
|
||||
if (sm16716_pin_sel < 99) {
|
||||
pinMode(sm16716_pin_sel, OUTPUT);
|
||||
digitalWrite(sm16716_pin_sel, LOW);
|
||||
// no need to call SM16716_Init here, it will be called after sel goes HIGH
|
||||
} else {
|
||||
// no sel pin means you have an 'always on' chip, so init right away
|
||||
SM16716_Init();
|
||||
}
|
||||
}
|
||||
#endif // ifdef USE_SM16716
|
||||
else {
|
||||
light_pdi_pin = pin[GPIO_DI];
|
||||
light_pdcki_pin = pin[GPIO_DCKI];
|
||||
@ -2001,10 +1926,10 @@ void LightAnimate(void)
|
||||
|
||||
// now apply the actual PWM values, adjusted and remapped 10-bits range
|
||||
if (light_type < LT_PWM6) { // only for direct PWM lights, not for Tuya, Armtronix...
|
||||
for (uint32_t i = 0; i < Light.subtype; i++) {
|
||||
for (uint32_t i = 0; i < (Light.subtype - Light.pwm_offset); i++) {
|
||||
if (pin[GPIO_PWM1 +i] < 99) {
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "Cur_Col%d 10 bits %d, Pwm%d %d"), i, cur_col_10bits[i], i+1, cur_col[i]);
|
||||
analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - cur_col_10bits[i] : cur_col_10bits[i]);
|
||||
analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - cur_col_10bits[(i + Light.pwm_offset)] : cur_col_10bits[(i + Light.pwm_offset)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2018,7 +1943,10 @@ void LightAnimate(void)
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "R%d G%d B%d, C%d W%d"),
|
||||
cur_col[0], cur_col[1], cur_col[2], cur_col[3], cur_col[4]);
|
||||
|
||||
if (XdrvCall(FUNC_SET_CHANNELS)) {
|
||||
if (XlgtCall(FUNC_SET_CHANNELS)) {
|
||||
// Serviced
|
||||
}
|
||||
else if (XdrvCall(FUNC_SET_CHANNELS)) {
|
||||
// Serviced
|
||||
}
|
||||
#ifdef USE_WS2812 // ************************************************************************
|
||||
@ -2026,19 +1954,6 @@ void LightAnimate(void)
|
||||
Ws2812SetColor(0, cur_col[0], cur_col[1], cur_col[2], cur_col[3]);
|
||||
}
|
||||
#endif // USE_ES2812 ************************************************************************
|
||||
#ifdef USE_SM16716
|
||||
else if (LT_SM16716 == light_type - Light.subtype) {
|
||||
// handle any PWM pins, skipping the first 3 values for sm16716
|
||||
for (uint32_t i = 3; i < Light.subtype; i++) {
|
||||
if (pin[GPIO_PWM1 +i-3] < 99) {
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "Cur_Col%d 10 bits %d, Pwm%d %d"), i, cur_col[i], i+1, curcol);
|
||||
analogWrite(pin[GPIO_PWM1 +i-3], bitRead(pwm_inverted, i-3) ? Settings.pwm_range - cur_col_10bits[i] : cur_col_10bits[i]);
|
||||
}
|
||||
}
|
||||
// handle sm16716 update
|
||||
SM16716_Update(cur_col[0], cur_col[1], cur_col[2]);
|
||||
}
|
||||
#endif // ifdef USE_SM16716
|
||||
else if (light_type > LT_WS2812) {
|
||||
//AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION "Cur_Col %d,%d,%d,%d,%d"), cur_col[0], cur_col[1], cur_col[2], cur_col[3], cur_col[4]);
|
||||
LightMy92x1Duty(cur_col[0], cur_col[1], cur_col[2], cur_col[3], cur_col[4]);
|
||||
@ -2588,11 +2503,11 @@ bool Xdrv04(uint8_t function)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
if (light_type) {
|
||||
if (FUNC_MODULE_INIT == function) {
|
||||
return LightModuleInit();
|
||||
}
|
||||
else if (light_type) {
|
||||
switch (function) {
|
||||
case FUNC_PRE_INIT:
|
||||
LightInit();
|
||||
break;
|
||||
case FUNC_EVERY_50_MSECOND:
|
||||
LightAnimate();
|
||||
#ifdef USE_ARILUX_RF
|
||||
@ -2610,6 +2525,9 @@ bool Xdrv04(uint8_t function)
|
||||
case FUNC_COMMAND:
|
||||
result = DecodeCommand(kLightCommands, LightCommand);
|
||||
break;
|
||||
case FUNC_PRE_INIT:
|
||||
LightInit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
200
sonoff/xlgt_03_sm16716.ino
Normal file
200
sonoff/xlgt_03_sm16716.ino
Normal file
@ -0,0 +1,200 @@
|
||||
/*
|
||||
xlgt_03_sm16716.ino - sm16716 three channel led support for Sonoff-Tasmota
|
||||
|
||||
Copyright (C) 2019 Theo Arends
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef USE_LIGHT
|
||||
#ifdef USE_SM16716
|
||||
/*********************************************************************************************\
|
||||
* SM16716 - Controlling RGB over a synchronous serial line
|
||||
* Copyright (C) 2019 Gabor Simon
|
||||
*
|
||||
* Source: https://community.home-assistant.io/t/cheap-uk-wifi-bulbs-with-tasmota-teardown-help-tywe3s/40508/27
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define XLGT_03 3
|
||||
|
||||
#define D_LOG_SM16716 "SM16716: "
|
||||
|
||||
struct SM16716 {
|
||||
uint8_t pin_clk = 0;
|
||||
uint8_t pin_dat = 0;
|
||||
uint8_t pin_sel = 0;
|
||||
bool enabled = false;
|
||||
} Sm16716;
|
||||
|
||||
void SM16716_SendBit(uint8_t v)
|
||||
{
|
||||
/* NOTE:
|
||||
* According to the spec sheet, max freq is 30 MHz, that is 16.6 ns per high/low half of the
|
||||
* clk square wave. That is less than the overhead of 'digitalWrite' at this clock rate,
|
||||
* so no additional delays are needed yet. */
|
||||
|
||||
digitalWrite(Sm16716.pin_dat, (v != 0) ? HIGH : LOW);
|
||||
//delayMicroseconds(1);
|
||||
digitalWrite(Sm16716.pin_clk, HIGH);
|
||||
//delayMicroseconds(1);
|
||||
digitalWrite(Sm16716.pin_clk, LOW);
|
||||
}
|
||||
|
||||
void SM16716_SendByte(uint8_t v)
|
||||
{
|
||||
uint8_t mask;
|
||||
|
||||
for (mask = 0x80; mask; mask >>= 1) {
|
||||
SM16716_SendBit(v & mask);
|
||||
}
|
||||
}
|
||||
|
||||
void SM16716_Update(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b)
|
||||
{
|
||||
if (Sm16716.pin_sel < 99) {
|
||||
bool should_enable = (duty_r | duty_g | duty_b);
|
||||
if (!Sm16716.enabled && should_enable) {
|
||||
DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "turning color on"));
|
||||
Sm16716.enabled = true;
|
||||
digitalWrite(Sm16716.pin_sel, HIGH);
|
||||
// in testing I found it takes a minimum of ~380us to wake up the chip
|
||||
// tested on a Merkury RGBW with an SM726EB
|
||||
delayMicroseconds(1000);
|
||||
SM16716_Init();
|
||||
}
|
||||
else if (Sm16716.enabled && !should_enable) {
|
||||
DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "turning color off"));
|
||||
Sm16716.enabled = false;
|
||||
digitalWrite(Sm16716.pin_sel, LOW);
|
||||
}
|
||||
}
|
||||
DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "Update; rgb=%02x%02x%02x"), duty_r, duty_g, duty_b);
|
||||
|
||||
// send start bit
|
||||
SM16716_SendBit(1);
|
||||
SM16716_SendByte(duty_r);
|
||||
SM16716_SendByte(duty_g);
|
||||
SM16716_SendByte(duty_b);
|
||||
|
||||
// send a 'do it' pulse
|
||||
// (if multiple chips are chained, each one processes the 1st '1rgb' 25-bit block and
|
||||
// passes on the rest, right until the one starting with 0)
|
||||
//SM16716_Init();
|
||||
SM16716_SendBit(0);
|
||||
SM16716_SendByte(0);
|
||||
SM16716_SendByte(0);
|
||||
SM16716_SendByte(0);
|
||||
}
|
||||
|
||||
/*
|
||||
bool SM16716_ModuleSelected(void)
|
||||
{
|
||||
Sm16716.pin_clk = pin[GPIO_SM16716_CLK];
|
||||
Sm16716.pin_dat = pin[GPIO_SM16716_DAT];
|
||||
Sm16716.pin_sel = pin[GPIO_SM16716_SEL];
|
||||
DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "ModuleSelected; clk_pin=%d, dat_pin=%d)"), Sm16716.pin_clk, Sm16716.pin_dat);
|
||||
return (Sm16716.pin_clk < 99) && (Sm16716.pin_dat < 99);
|
||||
}
|
||||
*/
|
||||
|
||||
void SM16716_Init(void)
|
||||
{
|
||||
for (uint32_t t_init = 0; t_init < 50; ++t_init) {
|
||||
SM16716_SendBit(0);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
bool Sm16716SetChannels(void)
|
||||
{
|
||||
/*
|
||||
// handle any PWM pins, skipping the first 3 values for sm16716
|
||||
for (uint32_t i = 3; i < Light.subtype; i++) {
|
||||
if (pin[GPIO_PWM1 +i-3] < 99) {
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "Cur_Col%d 10 bits %d, Pwm%d %d"), i, cur_col[i], i+1, curcol);
|
||||
analogWrite(pin[GPIO_PWM1 +i-3], bitRead(pwm_inverted, i-3) ? Settings.pwm_range - cur_col_10bits[i] : cur_col_10bits[i]);
|
||||
}
|
||||
}
|
||||
*/
|
||||
// handle sm16716 update
|
||||
uint8_t *cur_col = (uint8_t*)XdrvMailbox.data;
|
||||
|
||||
SM16716_Update(cur_col[0], cur_col[1], cur_col[2]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Sm16716ModuleSelected(void)
|
||||
{
|
||||
if ((pin[GPIO_SM16716_CLK] < 99) && (pin[GPIO_SM16716_DAT] < 99)) {
|
||||
Sm16716.pin_clk = pin[GPIO_SM16716_CLK];
|
||||
Sm16716.pin_dat = pin[GPIO_SM16716_DAT];
|
||||
Sm16716.pin_sel = pin[GPIO_SM16716_SEL];
|
||||
|
||||
/*
|
||||
// init PWM
|
||||
for (uint32_t i = 0; i < Light.subtype; i++) {
|
||||
Settings.pwm_value[i] = 0; // Disable direct PWM control
|
||||
if (pin[GPIO_PWM1 +i] < 99) {
|
||||
pinMode(pin[GPIO_PWM1 +i], OUTPUT);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// init sm16716
|
||||
pinMode(Sm16716.pin_clk, OUTPUT);
|
||||
digitalWrite(Sm16716.pin_clk, LOW);
|
||||
|
||||
pinMode(Sm16716.pin_dat, OUTPUT);
|
||||
digitalWrite(Sm16716.pin_dat, LOW);
|
||||
|
||||
if (Sm16716.pin_sel < 99) {
|
||||
pinMode(Sm16716.pin_sel, OUTPUT);
|
||||
digitalWrite(Sm16716.pin_sel, LOW);
|
||||
// no need to call SM16716_Init here, it will be called after sel goes HIGH
|
||||
} else {
|
||||
// no sel pin means you have an 'always on' chip, so init right away
|
||||
SM16716_Init();
|
||||
}
|
||||
|
||||
LightPwmOffset(LST_RGB); // Handle any PWM pins, skipping the first 3 color values for sm16716
|
||||
light_type += LST_RGB; // Add RGB to be controlled by sm16716
|
||||
light_flg = XLGT_03;
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: SM16716 Found"));
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool Xlgt03(uint8_t function)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
switch (function) {
|
||||
case FUNC_SET_CHANNELS:
|
||||
result = Sm16716SetChannels();
|
||||
break;
|
||||
case FUNC_MODULE_INIT:
|
||||
Sm16716ModuleSelected();
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_SM16716
|
||||
#endif // USE_LIGHT
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
xdrv_26_sm2135.ino - sm2135 five channel led support for Sonoff-Tasmota
|
||||
xlgt_04_sm2135.ino - sm2135 five channel led support for Sonoff-Tasmota
|
||||
|
||||
Copyright (C) 2019 Theo Arends
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
* {"NAME":"LSC RGBCW LED","GPIO":[0,0,0,0,0,0,0,0,181,0,180,0,0],"FLAG":0,"BASE":18}
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define XDRV_26 26
|
||||
#define XLGT_04 4
|
||||
|
||||
#define SM2135_ADDR_MC 0xC0 // Max current register
|
||||
#define SM2135_ADDR_CH 0xC1 // RGB or CW channel select register
|
||||
@ -56,7 +56,6 @@ const uint8_t SM2135_CURRENT = (SM2135_20MA << 4) | SM2135_10MA;
|
||||
struct SM2135 {
|
||||
uint8_t clk = 0;
|
||||
uint8_t data = 0;
|
||||
bool found = true;
|
||||
} Sm2135;
|
||||
|
||||
uint8_t Sm2135Write(uint8_t data)
|
||||
@ -87,19 +86,21 @@ void Sm2135Send(uint8_t *buffer, uint8_t size)
|
||||
digitalWrite(Sm2135.data, HIGH);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
bool Sm2135SetChannels(void)
|
||||
{
|
||||
char *buffer = XdrvMailbox.data;
|
||||
uint8_t data[8];
|
||||
uint8_t *cur_col = (uint8_t*)XdrvMailbox.data;
|
||||
uint8_t data[6];
|
||||
|
||||
if (('\0' == buffer[0]) && ('\0' == buffer[1]) && ('\0' == buffer[2])) {
|
||||
if ((0 == cur_col[0]) && (0 == cur_col[1]) && (0 == cur_col[2])) {
|
||||
// No color so must be Cold/Warm
|
||||
/*
|
||||
if ((buffer[3] + buffer[4]) >= (1 * 256)) {
|
||||
if ((cur_col[3] + cur_col[4]) >= (1 * 256)) {
|
||||
// Scale down to 255 total to fix max power usage of 9W (=40mA)
|
||||
|
||||
// buffer[3] >>= 1; // Divide by 2
|
||||
// buffer[4] >>= 1; // Divide by 2
|
||||
// cur_col[3] >>= 1; // Divide by 2
|
||||
// cur_col[4] >>= 1; // Divide by 2
|
||||
}
|
||||
*/
|
||||
data[0] = SM2135_ADDR_MC;
|
||||
@ -108,13 +109,13 @@ bool Sm2135SetChannels(void)
|
||||
Sm2135Send(data, 3);
|
||||
delay(1);
|
||||
data[0] = SM2135_ADDR_C;
|
||||
data[1] = buffer[4]; // Warm
|
||||
data[2] = buffer[3]; // Cold
|
||||
data[1] = cur_col[4]; // Warm
|
||||
data[2] = cur_col[3]; // Cold
|
||||
Sm2135Send(data, 3);
|
||||
} else {
|
||||
// Color
|
||||
/*
|
||||
if ((buffer[0] + buffer[1] + buffer[2]) >= (3 * 256)) {
|
||||
if ((cur_col[0] + cur_col[1] + cur_col[2]) >= (3 * 256)) {
|
||||
// Scale down to 765 total to fix max power usage of 9W
|
||||
// Currently not needed with setting 3 x 15mA = 45mA = 11W = 765
|
||||
}
|
||||
@ -122,16 +123,16 @@ bool Sm2135SetChannels(void)
|
||||
data[0] = SM2135_ADDR_MC;
|
||||
data[1] = SM2135_CURRENT;
|
||||
data[2] = SM2135_RGB;
|
||||
data[3] = buffer[1]; // Green
|
||||
data[4] = buffer[0]; // Red
|
||||
data[5] = buffer[2]; // Blue
|
||||
data[3] = cur_col[1]; // Green
|
||||
data[4] = cur_col[0]; // Red
|
||||
data[5] = cur_col[2]; // Blue
|
||||
Sm2135Send(data, 6);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Sm2135ModuleSelected(void)
|
||||
void Sm2135ModuleSelected(void)
|
||||
{
|
||||
if ((pin[GPIO_SM2135_CLK] < 99) && (pin[GPIO_SM2135_DAT] < 99)) {
|
||||
Sm2135.clk = pin[GPIO_SM2135_CLK];
|
||||
@ -143,30 +144,26 @@ bool Sm2135ModuleSelected(void)
|
||||
digitalWrite(Sm2135.clk, HIGH);
|
||||
|
||||
light_type = LT_RGBWC;
|
||||
light_flg = XLGT_04;
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: SM2135 Found"));
|
||||
} else {
|
||||
Sm2135.found = false;
|
||||
}
|
||||
return Sm2135.found;
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool Xdrv26(uint8_t function)
|
||||
bool Xlgt04(uint8_t function)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
if (Sm2135.found) {
|
||||
switch (function) {
|
||||
case FUNC_SET_CHANNELS:
|
||||
result = Sm2135SetChannels();
|
||||
break;
|
||||
case FUNC_MODULE_INIT:
|
||||
result = Sm2135ModuleSelected();
|
||||
break;
|
||||
}
|
||||
switch (function) {
|
||||
case FUNC_SET_CHANNELS:
|
||||
result = Sm2135SetChannels();
|
||||
break;
|
||||
case FUNC_MODULE_INIT:
|
||||
Sm2135ModuleSelected();
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
114
sonoff/xlgt_interface.ino
Normal file
114
sonoff/xlgt_interface.ino
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
xlgt_interface.ino - Light driver interface support for Sonoff-Tasmota
|
||||
|
||||
Copyright (C) 2019 Theo Arends
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef USE_LIGHT
|
||||
|
||||
#ifdef XFUNC_PTR_IN_ROM
|
||||
bool (* const xlgt_func_ptr[])(uint8_t) PROGMEM = { // Light driver Function Pointers
|
||||
#else
|
||||
bool (* const xlgt_func_ptr[])(uint8_t) = { // Light driver Function Pointers
|
||||
#endif
|
||||
|
||||
#ifdef XLGT_01
|
||||
&Xlgt01,
|
||||
#endif
|
||||
|
||||
#ifdef XLGT_02
|
||||
&Xlgt02,
|
||||
#endif
|
||||
|
||||
#ifdef XLGT_03
|
||||
&Xlgt03,
|
||||
#endif
|
||||
|
||||
#ifdef XLGT_04
|
||||
&Xlgt04,
|
||||
#endif
|
||||
|
||||
#ifdef XLGT_05
|
||||
&Xlgt05,
|
||||
#endif
|
||||
|
||||
#ifdef XLGT_06
|
||||
&Xlgt06,
|
||||
#endif
|
||||
|
||||
#ifdef XLGT_07
|
||||
&Xlgt07,
|
||||
#endif
|
||||
|
||||
#ifdef XLGT_08
|
||||
&Xlgt08,
|
||||
#endif
|
||||
|
||||
#ifdef XLGT_09
|
||||
&Xlgt09,
|
||||
#endif
|
||||
|
||||
#ifdef XLGT_10
|
||||
&Xlgt10,
|
||||
#endif
|
||||
|
||||
#ifdef XLGT_11
|
||||
&Xlgt11,
|
||||
#endif
|
||||
|
||||
#ifdef XLGT_12
|
||||
&Xlgt12,
|
||||
#endif
|
||||
|
||||
#ifdef XLGT_13
|
||||
&Xlgt13,
|
||||
#endif
|
||||
|
||||
#ifdef XLGT_14
|
||||
&Xlgt14,
|
||||
#endif
|
||||
|
||||
#ifdef XLGT_15
|
||||
&Xlgt15,
|
||||
#endif
|
||||
|
||||
#ifdef XLGT_16
|
||||
&Xlgt16
|
||||
#endif
|
||||
};
|
||||
|
||||
const uint8_t xlgt_present = sizeof(xlgt_func_ptr) / sizeof(xlgt_func_ptr[0]); // Number of drivers found
|
||||
|
||||
uint8_t xlgt_active = 0;
|
||||
|
||||
bool XlgtCall(uint8_t function)
|
||||
{
|
||||
if (FUNC_MODULE_INIT == function) {
|
||||
for (uint32_t x = 0; x < xlgt_present; x++) {
|
||||
xlgt_func_ptr[x](function);
|
||||
if (light_flg) {
|
||||
xlgt_active = x;
|
||||
return true; // Stop further driver investigation
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (light_flg) {
|
||||
return xlgt_func_ptr[xlgt_active](function);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // USE_LIGHT
|
Loading…
x
Reference in New Issue
Block a user