Skip to content

Instantly share code, notes, and snippets.

@wolph
Last active November 18, 2024 18:06
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
@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

@0xAhmed
Copy link

0xAhmed commented Nov 16, 2024

@wolph Mind giving me a hint on how to actually backup the stock firmware? I tried the read_flash command of esptool but it keeps throwing me the same error, even though I made sure that I'm in boot mode:

$ esptool.py -p /dev/cu.usbserial-210 -b 460800 read_flash 0 ALL tx-ultimate-stock.bin
esptool.py v4.8.1
Serial port /dev/cu.usbserial-210
Connecting.....
Detecting chip type... Unsupported detection protocol, switching and trying again...
Connecting...
Detecting chip type... ESP32
Chip is ESP32-D0WDR2-V3 (revision v3.1)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
Crystal is 40MHz
MAC: fe:b4:67:b8:f6:11
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 460800
Changed.
Configuring flash size...
Detected flash size: 8MB

A fatal error occurred: Invalid head of packet (0x7F): Possible serial noise or corruption.

@wolph
Copy link
Author

wolph commented Nov 16, 2024

@0xAhmed You can try lowering the baud rate, I've found that 460800 (or higher) only works with short high quality USB cables and even in those cases it's not guaranteed to work.
Try it with 115200 and see if you still get the error.

Alternatively... it could be that the flash size is detected incorrectly, but I think I remember the tx ultimate having 8MB so it should be correct.

You can read the flash size with this command:

esptool.py -p /dev/cu.usbserial-210 flash_id

[Edit]
I just remember another potential issue with these... they draw quite a bit of power so it could be that you need an extra/external 5v power supply. I can't get the tx ultimate to work reliably off my usb2serial adapter alone.

@0xAhmed
Copy link

0xAhmed commented Nov 16, 2024

Thank you so much @wolph. I completed overlooked the fact that the baud rate might be the problem. I just tried with an ESP32-WROOM chip (was getting the same error message on it) and it worked fine. Shall try with another TX Ultimate switch later.

@xHirscHx
Copy link

xHirscHx commented Nov 18, 2024

Anybody ever get the sound to be as loud as the stock firmware. mine works but its very , very low.
media_player:

  • platform: i2s_audio
    id: media_out
    name: ${friendly_name} Player
    dac_type: external
    i2s_dout_pin: ${audio_sdata_pin}
    i2s_audio_id: audio_i2s
    i2s_comm_fmt: lsb
    mode: mono

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