Skip to content

Instantly share code, notes, and snippets.

@wolph
Last active April 11, 2024 01:27
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save wolph/42024a983e4dfb0bc1dcbe6882979d21 to your computer and use it in GitHub Desktop.
Save wolph/42024a983e4dfb0bc1dcbe6882979d21 to your computer and use it in GitHub Desktop.
ESPHome Sonoff TX Ultimate T5-3C-86 Custom button component
light:
- platform: neopixelbus
type: GRB
variant: WS2812
pin: GPIO1
num_leds: 1
name: "NeoPixel 1"
internal: true
- platform: neopixelbus
type: GRB
variant: WS2812
pin: GPIO3
num_leds: 1
name: "NeoPixel 3"
internal: true
- platform: neopixelbus
type: GRB
variant: WS2812
pin: GPIO5
num_leds: 1
name: "NeoPixel 5"
internal: true
- platform: neopixelbus
type: GRB
variant: WS2812
pin: GPIO13
num_leds: 1
name: "NeoPixel 13"
internal: true
- platform: neopixelbus
type: GRB
variant: WS2812
pin: GPIO32
num_leds: 1
name: "NeoPixel 32"
internal: true
- platform: neopixelbus
type: GRB
variant: WS2812
# pin: GPIO20
pin: GPIO33
num_leds: 28
name: "Ambience Nightlight"
id: rgb_light
effects:
- addressable_twinkle:
- addressable_random_twinkle:
- addressable_rainbow:
name: 'rainbow slow'
speed: 5
- addressable_rainbow:
name: 'rainbow fast'
speed: 50
- addressable_rainbow:
name: 'rainbow superfast'
speed: 100
restore_mode: RESTORE_DEFAULT_OFF
# - platform: partition
# id: light_left_top
# name: "light left top"
# segments:
# - id: rgb_light
# from: 20
# to: 20
# - platform: partition
# id: light_middle_top
# name: "light middle top"
# segments:
# - id: rgb_light
# from: 23
# to: 23
# - platform: partition
# id: light_right_top
# name: "light right top"
# segments:
# - id: rgb_light
# from: 26
# to: 27
# - platform: partition
# id: light_left_bottom
# name: "light left bottom"
# segments:
# - id: rgb_light
# from: 6
# to: 7
# - platform: partition
# id: light_middle_bottom
# name: "light middle bottom"
# segments:
# - id: rgb_light
# from: 8
# to: 10
# - platform: partition
# id: light_right_bottom
# name: "light right bottom"
# segments:
# - id: rgb_light
# from: 11
# to: 12
- platform: partition
restore_mode: RESTORE_DEFAULT_OFF
id: light_left_both
name: "light left both"
segments:
- id: rgb_light
from: 11
to: 21
effects:
- addressable_twinkle:
- addressable_random_twinkle:
- addressable_rainbow:
speed: 5
- platform: partition
restore_mode: RESTORE_DEFAULT_OFF
id: light_middle_both
name: "light middle both"
segments:
- id: rgb_light
from: 22
to: 24
- id: rgb_light
from: 8
to: 10
effects:
- addressable_twinkle:
- addressable_random_twinkle:
- addressable_rainbow:
speed: 5
- platform: partition
restore_mode: RESTORE_DEFAULT_OFF
id: light_right_both
name: "light right both"
segments:
- id: rgb_light
from: 0
to: 7
- id: rgb_light
from: 25
to: 27
effects:
- addressable_twinkle:
- addressable_random_twinkle:
- addressable_rainbow:
speed: 5
- platform: partition
restore_mode: RESTORE_DEFAULT_OFF
id: light_right
name: "light right"
segments:
- id: rgb_light
from: 0
to: 5
# - platform: partition
# id: light_bottom
# name: "light bottom"
# segments:
# - id: rgb_light
# from: 6
# to: 12
- platform: partition
restore_mode: RESTORE_DEFAULT_OFF
id: light_left
name: "light left"
segments:
- id: rgb_light
from: 13
to: 19
# - platform: partition
# id: light_top
# name: "light top"
# segments:
# - id: rgb_light
# from: 20
# to: 26
condition:
switch.is_on: ${switch}
then:
- light.turn_on:
id: ${light}
brightness: ${on_brightness}
red: ${on_red}
green: ${on_green}
blue: ${on_blue}
transition_length: 500ms
else:
- if:
condition:
and:
- lambda: |-
return id(htime).now().hour < ${nightlight_on_hour};
- lambda: |-
return id(htime).now().hour > ${nightlight_off_hour};
then:
- light.turn_off:
id: ${light}
transition_length: 500ms
else:
- light.turn_on:
id: ${light}
brightness: ${nightlight_brightness}
red: ${nightlight_red}
green: ${nightlight_green}
blue: ${nightlight_blue}
transition_length: 500ms
substitutions:
name: 'sonoff_tx_ultimate_00'
on_brightness: 50%
on_red: 100%
on_green: 40%
on_blue: 0%
nightlight_brightness: 20%
nightlight_red: 100%
nightlight_green: 40%
nightlight_blue: 0%
nightlight_on_hour: '19' # turn on at 7 in the evening
nightlight_off_hour: '9' # turn off at 9 in the morning
esphome:
name: $name
platform: ESP32
board: esp32dev
on_boot:
priority: -100.0
then:
- script.execute: light_relays
includes:
- touch_panel.hpp
- touch_panel.cpp
uart:
id: uart_bus
tx_pin: GPIO19
rx_pin: GPIO22
baud_rate: 115200
script:
- id: light_relays
then:
- if: !include
file: relays_light.yaml
vars:
switch: relay1
light: light_left_both
- if: !include
file: relays_light.yaml
vars:
switch: relay2
light: light_middle_both
- if: !include
file: relays_light.yaml
vars:
switch: relay3
light: light_right_both
binary_sensor:
- platform: custom
lambda: |-
auto touch_panel = new touch_panel::TouchPanel(id(uart_bus));
App.register_component(touch_panel);
return {
touch_panel->left,
touch_panel->middle,
touch_panel->right,
touch_panel->two_finger,
touch_panel->dragged_ltr,
touch_panel->dragged_rtl,
};
binary_sensors:
- id: button_left
name: "Left Button"
on_press:
- switch.toggle: relay1
- switch.turn_on: haptics
- id: button_middle
name: "Middle Button"
on_press:
- switch.toggle: relay2
- switch.turn_on: haptics
- id: button_right
name: "Right Button"
on_press:
- switch.toggle: relay3
- switch.turn_on: haptics
- id: button_two_finger
name: "Two Fingers"
- id: button_dragged_ltr
name: "Dragged Left to Right"
- id: button_dragged_rtl
name: "Dragged Right to Left"
switch:
- platform: gpio
name: "relay left"
pin: GPIO18
id: relay1
on_turn_on:
script.execute: light_relays
on_turn_off:
script.execute: light_relays
- platform: gpio
name: "relay middle"
pin: GPIO17
id: relay2
on_turn_on:
script.execute: light_relays
on_turn_off:
script.execute: light_relays
- platform: gpio
name: "relay right"
pin: GPIO27
id: relay3
on_turn_on:
script.execute: light_relays
on_turn_off:
script.execute: light_relays
- platform: gpio
name: "relay right"
pin: GPIO23
id: relay4
on_turn_on:
script.execute: light_relays
on_turn_off:
script.execute: light_relays
- platform: gpio
name: "sound amplifier power"
pin: GPIO26
id: pa_sw
- platform: gpio
name: "touch panel power"
pin:
number: GPIO5
inverted: true
id: ca51_pow
restore_mode: ALWAYS_ON
- platform: gpio
pin: GPIO21
name: "Haptics"
id: "haptics"
restore_mode: ALWAYS_OFF
on_turn_on:
- delay: 300ms
- switch.turn_off: haptics
#include "touch_panel.hpp"
#include <stdio.h>
namespace esphome
{
namespace touch_panel
{
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
const uint8_t PREFIX[] = {0xAA, 0x55, 0x01, 0x02};
const unsigned int PREFIX_LENGTH = sizeof(PREFIX) / sizeof(PREFIX[0]);
// Messages consist of a prefix, a type of action and a position.
// There's probably other stuff but that's not relevant for us
const unsigned int MESSAGE_SIZE = PREFIX_LENGTH + 4;
const unsigned int BUFFER_SIZE = 32;
TouchPanel::TouchPanel(uart::UARTComponent *parent) : uart::UARTDevice(parent) {}
void TouchPanel::setup()
{
for (int i = 0; i < button_map.size(); i++)
button_map[i]->publish_state(i < TwoFinger);
}
void TouchPanel::loop()
{
static uint8_t buffer[BUFFER_SIZE];
while (available() >= MESSAGE_SIZE)
{
read_array(buffer, MIN(available(), BUFFER_SIZE));
bool match = true;
if (memcmp(buffer, PREFIX, PREFIX_LENGTH) != 0)
{
continue;
}
if (match)
{
TouchEvent event = static_cast<TouchEvent>(buffer[4]);
uint8_t released_position = buffer[5];
uint8_t pressed_position = buffer[6];
if (event == Pressed)
{
// ESP_LOGD("custom", "Pressed: %d", pressed_position);
button_map[pressed_position]->publish_state(true);
// Always trigger the two-fingered and drag events since we can only detect when they are released
two_finger->publish_state(true);
dragged_ltr->publish_state(true);
dragged_rtl->publish_state(true);
}
else if (event == Released || event == Dragged)
{
// Make sure to reset all buttons
for (int i = 0; i < TwoFinger; i++)
button_map[i]->publish_state(false);
button_map[released_position]->publish_state(false);
}
else
{
ESP_LOGD("custom", "Unknown event: %d", event);
}
}
}
}
} // namespace touch_panel
} // namespace esphome
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/uart/uart.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
#include <vector>
namespace esphome
{
namespace touch_panel
{
enum TouchEvent
{
Released = 1,
Pressed = 2,
Dragged = 3
};
enum TouchPosition
{
Left = 1,
Middle = 4,
Right = 7,
TwoFinger = 11,
DraggedLTR = 12,
DraggedRTL = 13
};
class TouchPanel : public Component, public uart::UARTDevice
{
public:
TouchPanel(uart::UARTComponent *parent);
void setup() override;
void loop() override;
float get_setup_priority() const override { return esphome::setup_priority::BUS; }
binary_sensor::BinarySensor *left = new binary_sensor::BinarySensor();
binary_sensor::BinarySensor *middle = new binary_sensor::BinarySensor();
binary_sensor::BinarySensor *right = new binary_sensor::BinarySensor();
binary_sensor::BinarySensor *two_finger = new binary_sensor::BinarySensor();
binary_sensor::BinarySensor *dragged_ltr = new binary_sensor::BinarySensor();
binary_sensor::BinarySensor *dragged_rtl = new binary_sensor::BinarySensor();
protected:
// For positional tracking
std::vector<binary_sensor::BinarySensor *> button_map = {
left, // 0 never occurs but is here for completeness
left, // 1-3 is left
left,
left,
middle, // 4-6 is middle
middle,
middle,
right, // 7-10 is right
right,
right,
right,
two_finger, // 11 is two fingers (released only)
dragged_ltr, // 12 is drag left to right
dragged_rtl, // 13 is drag right to left
};
};
} // namespace adalight
} // namespace esphome
@chris2172
Copy link

Thanks for this it works well after a few personalisations. Any chance you will be looking at getting the sound working?

@blakadder
Copy link

I've been trying out the code on a 1 gang version but I always get touch press trigger together with two finger or drag gestures.

Do you have any notes on the UART protocol used?

Sound is working but it's quite terrible https://twitter.com/blakadder_/status/1661863695788433409?s=20

On the other hand you can use the motor as a buzzer ;) https://twitter.com/blakadder_/status/1661866776303480832?s=20

@chris2172
Copy link

@blakadder I got the motor working perfectly. Just want to add a bit of sarcasm to the switch.
When user turns on the toilet light a breaking wind sound. Not essential, just me being quirky.

As like you with the swipe left & swipe right I also get the left or right button on my 3 gang

@ccorderor
Copy link

I've been trying out the code on a 1 gang version but I always get touch press trigger together with two finger or drag gestures.

Do you have any notes on the UART protocol used?

Sound is working but it's quite terrible https://twitter.com/blakadder_/status/1661863695788433409?s=20

On the other hand you can use the motor as a buzzer ;) https://twitter.com/blakadder_/status/1661866776303480832?s=20

Hi blakadder,

I'm currently trying to make the sound work. Can you share you code snippet for the sound? Thanks!!

@wolph
Copy link
Author

wolph commented May 26, 2023

I've been trying out the code on a 1 gang version but I always get touch press trigger together with two finger or drag gestures.

Do you have any notes on the UART protocol used?

As far as I've been able to decode, all UART messages start with the same prefix and after that you get an event with value 1, 2 or 3 for released, pressed or dragged respectively.

The pressed event is always sent when you touch the device and depending on what you do after that you either get a released or dragged event.

Additionally the released event can either return a position from 1 to 10, or 11 for two fingered release.
When dragging it's 12 or 13 for left-to-right or right-to-left respectively.

Sound is working but it's quite terrible https://twitter.com/blakadder_/status/1661863695788433409?s=20

Awesome! I'd love to see how you got it working. I don't have any specific use for the sounds currently but it could be fun :)

Thanks for this it works well after a few personalisations. Any chance you will be looking at getting the sound working?

I don't have any concrete plans for it yet. But if blakadder shares his code (I'm assuming he's using tasmota) I might be able to port it.

@blakadder
Copy link

There is no code, I'm not really a coder. I have difficulties reading this one 😃

Here's the sound snippet. It sounds equally terrible in Tasmota and esphome. I have no idea what Sonoff did extra to make it sound good. Maybe its just that the volume is too high and its clipping af but I can't lower it

# I2S audio component
i2s_audio:
  i2s_bclk_pin: GPIO2   # BCK
  i2s_lrclk_pin: GPIO4  # WS

# Player component for I2S
media_player:
  - platform: i2s_audio
    name: Speaker
    dac_type: external
    mode: mono
    i2s_dout_pin: GPIO15

@ccorderor
Copy link

Thank you, i'll give it a try!

@wolph
Copy link
Author

wolph commented May 26, 2023

There's still something that I need to figure out about the device though... for some reason it mostly stopped working for me now.

That is... with the original firmware it works great, with the esphome version the touchpanel doesn't respond and the lights won't go on. It is fully repeatable as well. I've gone back and forth between the firmwares and I have even reverted to earlier versions of my esphome configuration and it does nothing...

@wolph
Copy link
Author

wolph commented May 27, 2023

Here's the sound snippet. It sounds equally terrible in Tasmota and esphome. I have no idea what Sonoff did extra to make it sound good. Maybe its just that the volume is too high and its clipping af but I can't lower it

This might be related but I'm not sure. I don't notice any real difference with different encodings: esphome/issues#4106

@ccorderor
Copy link

Can somebody with the original firmware verify how much the volume can be pushed to?
I've been able to play music in the TX, but volume is a little bit too low: https://streamable.com/mxw6ms

@wolph
Copy link
Author

wolph commented Jun 4, 2023

Can somebody with the original firmware verify how much the volume can be pushed to? I've been able to play music in the TX, but volume is a little bit too low: https://streamable.com/mxw6ms

I've got a backup of the original firmware that I can restore, but I'm not entirely sure what you want me to do. I have a sound level meter that I can keep at a fixed distance before I click the button but I'm not sure it's loud enough to give any useful results at 1 meter

@ccorderor
Copy link

Can somebody with the original firmware verify how much the volume can be pushed to? I've been able to play music in the TX, but volume is a little bit too low: https://streamable.com/mxw6ms

I've got a backup of the original firmware that I can restore, but I'm not entirely sure what you want me to do. I have a sound level meter that I can keep at a fixed distance before I click the button but I'm not sure it's loud enough to give any useful results at 1 meter

Not sure either... hahaha
I will try to finish the modifications in the esphome branch and push a PR with the new config option

@ccorderor
Copy link

If you want to test, I've pushed a PR with enables a new yaml parameter to be able to use the sound output.
esphome/esphome#4918

@blakadder
Copy link

blakadder commented Jun 4, 2023

loudish https://twitter.com/blakadder_/status/1664434497826312192?s=20

have you tried setting i2s to stereo

@wolph
Copy link
Author

wolph commented Jun 4, 2023

loudish https://twitter.com/blakadder_/status/1664434497826312192?s=20

have you tried setting i2s to stereo

Very nice! Can you share your configuration so I can try as well? I'm planning to install these permanently in about a week or so and I would like to have them include sound as well :)

@grrraham
Copy link

I've been trying out the code on a 1 gang version but I always get touch press trigger together with two finger or drag gestures.

Do you have any notes on the UART protocol used?

Sound is working but it's quite terrible https://twitter.com/blakadder_/status/1661863695788433409?s=20

On the other hand you can use the motor as a buzzer ;) https://twitter.com/blakadder_/status/1661866776303480832?s=20

I got the sound working ok and commented on https://www.youtube.com/watch?v=naDLhX89enQ&t
Copy his i2saudio and media player settings, then add "i2s_comm_fmt: lsb"
media_player:

  • platform: i2s_audio
    i2s_comm_fmt: lsb

@iUnstable0
Copy link

does this work with the 4 gang switch?

@wolph
Copy link
Author

wolph commented Dec 18, 2023

does this work with the 4 gang switch?

You'll have to modify it slightly to do that. Currently it goes up to relay3 but relay4 is already defined because I suspected that they would come out with a 4-relay version. To modify it you'll have to adjust which section of the lights you want to turn on and what part of the button it should respond to.

@MantasGSXR
Copy link

Can somebody with the original firmware verify how much the volume can be pushed to? I've been able to play music in the TX, but volume is a little bit too low: https://streamable.com/mxw6ms

I've got a backup of the original firmware that I can restore, but I'm not entirely sure what you want me to do. I have a sound level meter that I can keep at a fixed distance before I click the button but I'm not sure it's loud enough to give any useful results at 1 meter

Please, please could you share your backup with me. I wasn't able to make a backup before I flashed my test unit and would like to go back to factory for a bit.

@wolph
Copy link
Author

wolph commented Apr 11, 2024

@MantasGSXR sorry to tell you but that backup won't help you. The firmwares are locked to the mac address so the backup won't work on your device unless it was made by your device

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