Skip to content

Instantly share code, notes, and snippets.

@tony-fav
Last active October 7, 2021 05:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tony-fav/e7a92d2a35346227bbb9f473fefd7b25 to your computer and use it in GitHub Desktop.
Save tony-fav/e7a92d2a35346227bbb9f473fefd7b25 to your computer and use it in GitHub Desktop.
Linkind ESP32 Dimmer WS2400101
import string
### INITIALIZATION
# Create required configuration variables
var dim_min = 0
var dim_max = 255
var fade = false
var speed = 1
var fade_time = 500*speed
var log_level = 2
var led_table = true
### GET SETTINGS FROM TASMOTA
# Create callback functions for when DimmerRange, Fade, and Speed are called to change settings
def set_log_level(value, trigger, msg)
log_level = int(value)
if log_level > 2
print('LID: Log Level set to', log_level)
end
end
def set_dim_range(value, trigger, msg)
if int(value['Min']) > int(value['Max'])
tasmota.cmd('DimmerRange 0,255')
elif int(value['Min']) < 0
tasmota.cmd('DimmerRange 0,255')
elif int(value['Max']) > 255
tasmota.cmd('DimmerRange 0,255')
else
dim_min = int(value['Min'])
dim_max = int(value['Max'])
if log_level > 2
print('LID: DimmerRange set to', dim_min, ',', dim_max)
end
end
end
def set_fade(value, trigger, msg)
fade = (value == "ON")
if log_level > 2
print('LID: Fade set to', fade)
end
end
def set_speed(value, trigger, msg)
speed = int(value)
if log_level > 2
print('LID: Speed set to', speed)
end
end
def set_led_table(value, trigger, msg)
led_table = (value == "ON")
if log_level > 2
print('LID: LED Table set to', led_table)
end
end
# Tie callbacks to DimmerRange, Fade, and Speed are called.
tasmota.add_rule("WebLog", set_log_level)
tasmota.add_rule("DimmerRange", set_dim_range)
tasmota.add_rule("Fade", set_fade)
tasmota.add_rule("Speed", set_speed)
tasmota.add_rule("LEDTable", set_led_table)
# Call DimmerRange, Fade, Speed
tasmota.cmd("WebLog")
tasmota.cmd("DimmerRange")
tasmota.cmd("Fade")
tasmota.cmd("Speed")
tasmota.cmd("LEDTable")
### The good stuff
# Create variables required for tracking fade
var fade_end = tasmota.millis(fade_time)
var dim_val = int(dim_min + (1.0*light.get()['bri'])/(255.0)*(dim_max - dim_min))
var dim_old = dim_val
var dim_cur = dim_val
var dim_tar = dim_val
# Function to send dim_val over I2C to the secondary MCU
def send_dim_val()
var chk = 65403-dim_val
var out = string.format('095001%02X0000%04X',dim_val,chk)
wire1.write_bytes(0x50, 0x2A, bytes(out))
dim_cur = dim_val
if log_level > 2
print('LID: Sending Value =', dim_val, 'as', out, 'to Address 0x50 Register 0x2A')
end
end
def do_fade()
# This function only does things if fade is true
if fade
var time_left = fade_end - tasmota.millis()
# if time_left < 0
# if dim_cur != dim_tar
# dim_val = dim_tar
# send_dim_val()
# end
# else
var dim_next = int(dim_tar + 1.0*time_left/fade_time*(dim_old - dim_tar))
if time_left < 0
dim_next = dim_tar
end
var dc = dim_cur
if dim_next > dc
for i: 1 .. (dim_next - dc)
dim_val = dc + i
send_dim_val()
end
else
for i: 1 .. (dc - dim_next)
dim_val = dc - i
send_dim_val()
end
end
# end
end
end
# Driver to run every 100ms and check if dimming level is reached, if not set a new one.
class DimmerDriver : Driver
def every_100ms()
if dim_cur != dim_tar
do_fade()
tasmota.set_timer(20, do_fade)
tasmota.set_timer(40, do_fade)
tasmota.set_timer(60, do_fade)
tasmota.set_timer(80, do_fade)
end
end
end
var d1 = DimmerDriver()
tasmota.add_driver(d1)
# Callback function to run whenever dimmer level or power state is changed
def dimmer_call()
var pow = light.get()['power']
var bri = light.get()['bri']
var bri_c = bri
if led_table
bri_c = light.gamma8(bri)
end
var val = int(dim_min + (1.0*bri_c)/(255.0)*(dim_max - dim_min))
if log_level > 1
print('LID: Got Power State =', pow, '& Brightness =', bri, '& Gamma Corrected =', bri_c, '& Scaled =', val)
end
if !pow
val = 0
end
dim_old = dim_val
# If fade is true, set the target dimming level and record current time.
# Driver handles setting dim_val.
if fade
dim_tar = val
if dim_tar > dim_cur
fade_time = int((500.0*speed)/(dim_max - dim_min)*(dim_tar - dim_cur))
else
fade_time = int((500.0*speed)/(dim_max - dim_min)*(dim_cur - dim_tar))
end
fade_end = tasmota.millis(fade_time)
# If fade is false, send dim_val right away.
else
dim_val = val
send_dim_val()
# set these values for if fade is turned on
dim_old = dim_val
dim_cur = dim_val
dim_tar = dim_val
end
end
dimmer_call()
# Tie callbacks to tasmota events
tasmota.add_rule("Dimmer#State", dimmer_call)
tasmota.add_rule("Power1#State", dimmer_call)
def send_val(val)
wire1.write_bytes(0x50, 0x2A, bytes(string.format('095001%02X0000%04X',val,65403-val)))
end
def breath(dt, rp)
var old_val = dim_cur
for j : 1 .. rp
for i : old_val+1 .. dim_max
send_val(i)
tasmota.delay(dt)
tasmota.yield()
end
for i : dim_min+1 .. dim_max
send_val(dim_max+dim_min-i)
tasmota.delay(dt)
tasmota.yield()
end
for i : dim_min+1 .. old_val
send_val(i)
tasmota.delay(dt)
tasmota.yield()
end
end
end

Linkind I2C Dimmer WS2400101

Firmware

Flashing the requires Tasmota32 Solo1 build is covered in Digiblur's blog post https://www.digiblur.com/p/linkind-esp32-smart-switch-how-to-flash.html

I am using a custom compiled firmware that includes all device group features, full rules features, TasMesh, and BLE. (Using TasMesh and BLE on this single core ESP32 may be unreliable. TasMesh is still a WIP.)

Follow digiblur's instructions to flash whichever variety of Tasmota32 Solo1 you want, but the following is only confirmed to work on the 9.5.0.9 commit 1b0de24. Come chat in the discord if you want to compile your own bin https://discord.digiblur.com/

Configuration

I have my device setup so the Green LED indicates the dimming level and the Red LED is on when the power state is off.

Step 1 - Send and Activate Template

Access the Tasmota console (Click Consoles then click Console) and execute the following command.

Backlog Template {"NAME":"Linkind Dimmer Berry POC WS2400101","GPIO":[544,0,0,0,640,0,0,0,0,0,416,0,0,0,0,0,0,0,608,0,0,0,320,0,0,0,0,0,161,160,0,0,0,0,0,0],"FLAG":0,"BASE":1}; Delay 1; Module 0

The device should restart.

Step 2 - Set SwitchModes, SetOption32, and Rules

In the console execute the following:

Backlog SwitchMode1 12; SwitchMode2 12; SetOption32 12; Rule1 On Switch2#State=2 Do Power ON ENDON On Switch1#State=2 Do Power OFF ENDON On Switch2#State=4 Do Dimmer + ENDON On Switch1#State=4 Do Dimmer - ENDON On Power1#State Do LEDPower %value% ENDON On Power1#Boot Do LEDPower %value% ENDON; Rule1 1

This configures single press of the top button to turn on, hold of top button to increase brightness, single press of the bottom button to turn off, and hold of the bottom button to decrease brightness.

You will likely want to play with SetOption32 and DimmerStep to find settings that work for your case.

Step 3 - Upload Berry Script

From the main Web UI page: click Consoles, click Manage File system, click Choose file, and upload the autoexec.be script attached to this gist.

After the script is uploaded, restart the device.

Step 4 - Tuning - Set Fade, Speed, SetOption32, DimmerStep, and DimmerRange

Use the DimmerRange, DimmerStep, Speed, and Fade commands as normal.

DimmerRange is on a scale of 0 to 255. In my case, my bulb didn't turn on until 23 and had no brightness change above 230.

For me and the bulb I am testing with, this is

Backlog Fade 1; Speed 2; SetOption32 12; DimmerStep 10; DimmerRange 23,230

With the settings I've set, pressing and holding a button takes 1.2s (SetOption32 12) to send a command to change the dimming level by 10% (DimmerStep 10) which then takes 1s to fully change (Fade 1; Speed 2)

Optional - A Faster Parameter Set

Backlog Fade 1; Speed 1; SetOption32 6; DimmerStep 25; DimmerRange 23,230

Optional - Enable Bluetooth

Click Configuration then Configure BLE then check Enable Bluetooth then Save.

Alternate Config using Buttons and Full Rules

If light is off

  • Single press either button turns on.
  • Hold top button turns on at 67%
  • Hold bottom button turns on at 33%

If light is on

  • Single presses do dimming increments (DimmerStep) (top +, bottom -)
  • Hold top goes to 100%
  • Hold bottom button turns off.

This leaves double and higher multipresses available for other automations.

template {"NAME":"Linkind Dimmer Berry POC WS2400101","GPIO":[544,0,0,0,640,0,0,0,0,0,416,0,0,0,0,0,0,0,608,0,0,0,320,0,0,0,0,0,33,32,0,0,0,0,0,0],"FLAG":0,"BASE":1}

SO73 1

Rule1
ON Power1#Boot DO Backlog LEDPower %value%; Var1 %value% ENDON
ON Power1#State DO Backlog LEDPower %value%; Var1 %value% ENDON

Rule1 1

Rule2
ON Button1#State=3 DO IF (Var1==0) Dimmer 33 ENDIF ENDON
ON Button2#State=3 DO IF (Var1==0) Dimmer 67 ENDIF ENDON
ON Button1#State=3 DO IF (Var1==1) Power1 OFF ENDIF ENDON
ON Button2#State=3 DO IF (Var1==1) Dimmer 100 ENDIF ENDON
ON Button1#State=10 DO IF (Var1==0) Power1 ON ENDIF ENDON
ON Button2#State=10 DO IF (Var1==0) Power1 ON ENDIF ENDON
ON Button1#State=10 DO IF (Var1==1) Dimmer - ENDIF ENDON
ON Button2#State=10 DO IF (Var1==1) Dimmer + ENDIF ENDON

Rule2 1

Favorite Config

This config requires a build with conditional rules and doesn't leave any functionality for other automations but makes a very functional dimmer.

Backlog Template {"NAME":"Linkind Dimmer Berry POC WS2400101","GPIO":[544,0,0,0,640,0,0,0,0,0,416,0,0,0,0,0,0,0,608,0,0,0,320,0,0,0,0,0,161,160,0,0,0,0,0,0],"FLAG":0,"BASE":1}; Delay 1; Module 0

Backlog SwitchMode1 5; SwitchMode2 5; SetOption114 1

Rule1
ON Power1#Boot DO Backlog LEDPower %value%; Var1 %value% ENDON
ON Power1#State DO Backlog LEDPower %value%; Var1 %value% ENDON
Rule2
ON Switch1#State=3 DO IF (Var1==0) Dimmer 33 ENDIF ENDON
ON Switch2#State=3 DO IF (Var1==0) Dimmer 67 ENDIF ENDON
ON Switch1#State=3 DO IF (Var1==1) Power1 OFF ENDIF ENDON
ON Switch2#State=3 DO IF (Var1==1) Dimmer 100 ENDIF ENDON
ON Switch1#State=2 DO IF (Var1==0) Power1 ON ENDIF ENDON
ON Switch2#State=2 DO IF (Var1==0) Power1 ON ENDIF ENDON
ON Switch1#State=2 DO IF (Var1==1) Dimmer - ENDIF ENDON
ON Switch2#State=2 DO IF (Var1==1) Dimmer + ENDIF ENDON

(I know I could do ELSE's but I wanted to get each case written out cleanly first before condensing.)

[env:tasmota32solo1-bluetooth]
extends = env:tasmota32_base
platform_packages = framework-arduinoespressif32 @ https://github.com/tasmota/arduino-esp32/releases/download/1.0.7.4/tasmota-arduinoespressif32-solo1-release_v3.3.5.tar.gz
platformio/tool-esptoolpy @ ~1.30100
platformio/tool-mklittlefs @ ~1.203.200522
build_flags = ${env:tasmota32_base.build_flags} -DFIRMWARE_SOLO1BLE
lib_extra_dirs = lib/libesp32, lib/libesp32_div, lib/lib_basic, lib/lib_i2c, lib/lib_ssl
/*
user_config_override.h - user configuration overrides my_user_config.h for Tasmota
Copyright (C) 2021 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/>.
*/
#ifndef _USER_CONFIG_OVERRIDE_H_
#define _USER_CONFIG_OVERRIDE_H_
/*****************************************************************************************************\
* USAGE:
* To modify the stock configuration without changing the my_user_config.h file:
* (1) copy this file to "user_config_override.h" (It will be ignored by Git)
* (2) define your own settings below
*
******************************************************************************************************
* ATTENTION:
* - Changes to SECTION1 PARAMETER defines will only override flash settings if you change define CFG_HOLDER.
* - Expect compiler warnings when no ifdef/undef/endif sequence is used.
* - You still need to update my_user_config.h for major define USE_MQTT_TLS.
* - All parameters can be persistent changed online using commands via MQTT, WebConsole or Serial.
\*****************************************************************************************************/
/*
Examples :
// -- Master parameter control --------------------
#undef CFG_HOLDER
#define CFG_HOLDER 4617 // [Reset 1] Change this value to load SECTION1 configuration parameters to flash
// -- Setup your own Wifi settings ---------------
#undef STA_SSID1
#define STA_SSID1 "YourSSID" // [Ssid1] Wifi SSID
#undef STA_PASS1
#define STA_PASS1 "YourWifiPassword" // [Password1] Wifi password
// -- Setup your own MQTT settings ---------------
#undef MQTT_HOST
#define MQTT_HOST "your-mqtt-server.com" // [MqttHost]
#undef MQTT_PORT
#define MQTT_PORT 1883 // [MqttPort] MQTT port (10123 on CloudMQTT)
#undef MQTT_USER
#define MQTT_USER "YourMqttUser" // [MqttUser] Optional user
#undef MQTT_PASS
#define MQTT_PASS "YourMqttPass" // [MqttPassword] Optional password
// You might even pass some parameters from the command line ----------------------------
// Ie: export PLATFORMIO_BUILD_FLAGS='-DUSE_CONFIG_OVERRIDE -DMY_IP="192.168.1.99" -DMY_GW="192.168.1.1" -DMY_DNS="192.168.1.1"'
#ifdef MY_IP
#undef WIFI_IP_ADDRESS
#define WIFI_IP_ADDRESS MY_IP // Set to 0.0.0.0 for using DHCP or enter a static IP address
#endif
#ifdef MY_GW
#undef WIFI_GATEWAY
#define WIFI_GATEWAY MY_GW // if not using DHCP set Gateway IP address
#endif
#ifdef MY_DNS
#undef WIFI_DNS
#define WIFI_DNS MY_DNS // If not using DHCP set DNS IP address (might be equal to WIFI_GATEWAY)
#endif
// !!! Remember that your changes GOES AT THE BOTTOM OF THIS FILE right before the last #endif !!!
*/
// device groups and light features
#define USE_DEVICE_GROUPS // Add support for device groups (+5k5 code)
#define USE_DEVICE_GROUPS_SEND // Add support for the DevGroupSend command (+0k6 code)
#define USE_LIGHT_VIRTUAL_CT // Add support for Virtual White Color Temperature (+1.1k code)
#define USE_DGR_LIGHT_SEQUENCE // Add support for device group light sequencing (requires USE_DEVICE_GROUPS) (+0k2 code)
#define USE_PWM_DIMMER // Add support for MJ-SD01/acenx/NTONPOWER PWM dimmers (+2k3 code, DGR=0k7)
#define USE_PWM_DIMMER_REMOTE // Add support for remote switches to PWM Dimmer (requires USE_DEVICE_GROUPS) (+0k6 code)
#define USE_LIGHT_PALETTE // Add support for color palette (+0k9 code)
// fullrules style includes
#define USE_RULES
#define USE_EXPRESSION // Add support for expression evaluation in rules (+3k2 code, +64 bytes mem)
#define SUPPORT_IF_STATEMENT // Add support for IF statement in rules (+4k2 code, -332 bytes mem)
#ifndef SUPPORT_MQTT_EVENT
#define SUPPORT_MQTT_EVENT
#endif
// TASMESH Because Why Not
#define USE_TASMESH
#ifdef FIRMWARE_SOLO1BLE
#undef CODE_IMAGE_STR
#define CODE_IMAGE_STR "solo1-bluetooth"
#define USE_ENHANCED_GUI_WIFI_SCAN
#undef MODULE
#define MODULE WEMOS // [Module] Select default module from tasmota_template.h
#undef FALLBACK_MODULE
#define FALLBACK_MODULE WEMOS // [Module2] Select default module on fast reboot where USER_MODULE is user template
#define USE_INFLUXDB // Enable influxdb support (+5k code)
#define USE_TASMOTA_DISCOVERY
#undef USE_HOME_ASSISTANT
#define USE_SDCARD
#define USE_ADC
// #undef USE_BERRY // Disable Berry scripting language
#define USE_BLE_ESP32 // Enable new BLE driver
#define USE_EQ3_ESP32
#define USE_MI_ESP32 // (ESP32 only) Add support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash)
#define USE_IBEACON_ESP32 // Add support for Bluetooth LE passive scan of iBeacon devices
// I2C For Linkind Dimmer
#define USE_I2C // I2C using library wire (+10k code, 0k2 mem, 124 iram)
// undefs from FIRMWARE_TASMOTA32, I guess this just saves some code
#undef USE_ARMTRONIX_DIMMERS // Disable support for Armtronix Dimmers (+1k4 code)
#undef USE_SONOFF_IFAN // Disable support for Sonoff iFan02 and iFan03 (+2k code)
#undef USE_EXS_DIMMER // Disable support for EX-Store WiFi Dimmer
#undef USE_KEELOQ // Disable support for Jarolift rollers by Keeloq algorithm (+4k5 code)
#undef USE_SONOFF_D1 // Disable support for Sonoff D1 Dimmer (+0k7 code)
#undef USE_SHELLY_DIMMER // Disable support for Shelly Dimmer (+3k code)
#endif
#endif // _USER_CONFIG_OVERRIDE_H_
@tony-fav
Copy link
Author

tony-fav commented Oct 4, 2021

Major update from yesterday.

  • Figured out how to get settings from Tasmota proper into Berry (using the global variable, callback functions, and rules).
  • Implemented Fade.
  • Provided useful set of configuration parameters and rules.

@tony-fav
Copy link
Author

tony-fav commented Oct 5, 2021

  • Using every_100ms driver to launch essentially an every_50ms function to make dimming a bit smoother. (Thanks dgdrr on digiblurDIY discord for the prodding)
  • Logging prints now only performed if Weblog 3 or 4.

@tony-fav
Copy link
Author

tony-fav commented Oct 5, 2021

  • Implemented using the LEDTable parameter.
  • Changed log level of dimmer_call() function.
  • Added some protections to DimmerRange

@tony-fav
Copy link
Author

tony-fav commented Oct 5, 2021

  • Added an alternate config using Buttons instead of Switches. Requires a firmware with conditional rules.

@tony-fav
Copy link
Author

tony-fav commented Oct 5, 2021

  • Fixed interpretation of the Speed parameter. (Noticed in Device Group testing it was wrong.)
  • Slight mod to the alternate config.

@tony-fav
Copy link
Author

tony-fav commented Oct 7, 2021

  • Reworked fade a bit so that no matter what it hits each brightness level and gets called a bit more often. May be "slow" at lower Speed settings, but it will be more reliable at larger Speed settings.
  • Added a trial breath effect function. Call from Tasmota console with br breath(1, 3). First argument is delay time between each brightness step; second argument is repetitions.
  • Added the switch configuration I'm currently running.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment