Redesign light driver phase 1

Redesign light driver phase 1
This commit is contained in:
Theo Arends 2019-10-06 17:19:05 +02:00
parent 2bfa172324
commit 019dc76740
6 changed files with 438 additions and 253 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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
View 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

View File

@ -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
View 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