XY-ST30-W Temp Controller

I am using this Temperature Controller Module for my Bambu Lab Chamber heater. I would like to use ESPHome on it, instead of Sinilink App
I have flashed the ESP8285 without a problem to ESPHome.

Does someone already has write a program to read/write the modus data.

Model: XY-ST30-W or XY-ST10-W

If you can’t find the modbus registers for the device, you have to probe little bit.
I managed to get all registers from another XY-device:
slave address 1
Uart 115200 bps, 8N1
single holding registers starting from 0x0000

I have found a semi similar product. At the end of the datasheet the Modbus data structure is described.

I have no experience in programming, hopefully someone can help with a basic code then I can start testing the each registers.

Did you flash the chip through the pin header? I mean do you know the uart pins used there?

Yes, I flashed through the pin header holes.

Try if you get any response:

uart:
  tx_pin: GPIO1
  rx_pin: GPIO3
  baud_rate: 115200

modbus:
  id: modbus1

modbus_controller:
- id: modbus_device
  address: 0x1   ## address of the Modbus slave device on the bus
  modbus_id: modbus1
  
sensor:
- platform: modbus_controller
  modbus_controller_id: modbus_device
  name: "Temp"
  register_type: holding
  address: 0x0003    ## address of the register inside the Modbus slave device
  unit_of_measurement: "C"
  value_type: S_WORD

and set logger baudrate to 0

logger:
  baud_rate: 0

Temp sensor is working, actual temp on display is 21.2

afbeelding

Nice.

Add this to your temp sensor:

filters:
  - multiply: 0.1

Then just copy/paste the sensors for addresses you are interested in (hoping that the registers of your device are same with the one you linked).

Most of the sensors I have working.
But I am struggeling getting the relay to work in HA, I tried lots of different codes but notting works.
If I manual press the relay On/Off button on the device it show the correct status in HA.

afbeelding
afbeelding

switch:
  - platform: modbus_controller
    modbus_controller_id: modbus_device
    register_type: holding
    address: 0x0000
    name: "Relay"
    skip_updates: 5
    use_write_multiple: false
    bitmask: 1
    icon: "mdi:toggle-switch"

Is that bitmask required?
If you want to see what’s going on, add debug to your uart config and see what is sent when you change the state in HA and what’s received when you change state on the device.

uart:
  baud_rate: 115200
  debug:
    direction: BOTH

UART Debug when Pressing ON/OFF Button on Device.
[20:48:43.390][D][switch:065]: ‘Relay’: Sending state ON
[20:48:43.390][D][uart_debug:114]: <<< 01:03:02:00:01:79:84
[20:48:48.366][D][uart_debug:114]: >>> 01:03:00:00:00:01:84:0A
[20:48:48.481][D][uart_debug:114]: <<< 01:03:02:00:01:79:84
[20:48:54.339][D][uart_debug:114]: >>> 01:03:00:00:00:01:84:0A
[20:48:54.354][D][switch:065]: ‘Relay’: Sending state OFF
[20:48:54.454][D][uart_debug:114]: <<< 01:03:02:00:00:B8:44

UART Debug when Pressing ON/OFF Button in HA.
[20:50:30.135][D][switch:022]: ‘Relay’ Turning ON.
[20:50:30.137][D][switch:065]: ‘Relay’: Sending state ON
[20:50:30.242][D][uart_debug:114]: >>> 01:06:00:00:FF:FF:88:7A
[20:50:30.500][D][uart_debug:114]: >>> 01:06:00:00:FF:FF:88:7A
[20:50:30.694][D][uart_debug:114]: >>> 01:06:00:00:FF:FF:88:7A
[20:50:30.709][D][uart_debug:114]: <<< 01:06:00:00:FF:FF:88:7A
[20:50:30.823][D][uart_debug:114]: >>> 01:03:00:00:00:01:84:0A
[20:50:31.001][D][uart_debug:114]: >>> 01:03:00:00:00:01:84:0A
[20:50:31.014][D][switch:065]: ‘Relay’: Sending state OFF
[20:50:31.116][D][uart_debug:114]: <<< 01:03:02:00:00:B8:44

The problem is when switch to ON in HA it immediately turn OFF again without switching the relay.

No, the problem is that it never sends ON request to the device. Your visual switch state on HA is completely irrelevant.

This is incorrect:

it should be 01:06:00:00:00:01…

Can’t get it to work.

switch:
  - platform: modbus_controller
    modbus_controller_id: modbus_device
    register_type: holding
    address: 0x0000
    name: "Relay"
    skip_updates: 5
    use_write_multiple: false 
    write_lambda: |-
      payload.push_back(0x01);
      payload.push_back(0x06);
      payload.push_back(0x00);
      payload.push_back(0x00);
      
      if (x) {
        payload.push_back(0x00);
        payload.push_back(0x01);
      } else {
        payload.push_back(0x00);
        payload.push_back(0x00);
      }
      return true;
    lambda: |-
      return (x == 1);

UART Log:
[switch:022]: ‘Relay’ Turning ON.
[switch:065]: ‘Relay’: Sending state ON
[uart_debug:114]: >>> 01:06:00:00:00:01:48:0A
[uart_debug:114]: >>> 01:06:00:00:00:01:48:0A
[uart_debug:114]: >>> 01:06:00:00:00:01:48:0A
[uart_debug:114]: <<< 01:06:00:00:00:01:48:0A
[uart_debug:114]: >>> 01:03:00:00:00:01:84:0A
[switch:065]: ‘Relay’: Sending state OFF

Too vague.
Explain what happens. And post the logs always with timing.

What we really know is that device replies to read request with 0x0000 (off) and 0x0001(on).
Now your lambda matches to that. Before your switch was using FFFF for ON (esphome default).

But we don’t know for sure if that register is accepting write request in the first place.
Or there is some weird logic on that device that it is looking for different write command that it uses for response. Could be FFFF, FF00, 000A for example.
Ideally any other than 0000 would be ON, but world is not ideal…

Finally find some time to troubleshoot.
I have made it working, the ON/OFF address is: 0x0011

  - platform: modbus_controller
    modbus_controller_id: modbus_device
    name: "Relay Control"
    address: 0x0011
    register_type: holding
    use_write_multiple: false
    icon: "mdi:power-cycle"
     # SENDING the command to 0x0011
    write_lambda: |-
      payload.push_back(0x01);
      payload.push_back(0x06);
      payload.push_back(0x00);
      payload.push_back(0x11); 

      if (x) {
        payload.push_back(0x00);
        payload.push_back(0x00);
      } else {
        payload.push_back(0x00);
        payload.push_back(0x01);
      }
      return true;
    # MATCHING the UI toggle to the status at 0x0000
    lambda: |-
      return (id(relay_raw_state).state > 0);

That makes sense! :+1:

Hi Rufan,

Would you mind posting your entire yaml for the temp controller?
I am also trying to make it work and have some difficulties.

Thank you very much.

This is my code.
For sure it can be improved as I am not a programmer.
Thanks to Karosm and ChatGPT

substitutions:
  # General Settings
  device_name: xyst30w-thermostat
  friendly_name: Sinilink Thermostat
  baud_rate: "9600"

  temp_multiplier: "0.1"

esphome:
  name: ${device_name}

esp8266:
  board: esp01_1m
  restore_from_flash: true

logger:
  level: WARN #DEBUG
  # Disable logging via UART, since we're using this for modbus communication
  baud_rate: 0 #Serial logging can be disabled by setting baud_rate: 0.

# Enable status LED
status_led:
  pin:
    number: GPIO2
    inverted: true

# Enable Home Assistant API
api:
  encryption:
    key: "......"

ota:
  - platform: esphome
    password: "......."

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  min_auth_mode: WPA2
  power_save_mode: none
  fast_connect: on
  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "xyst30w-thermostat"
    password: "......."

uart:
  - id: uart_bus
    tx_pin: GPIO1
    rx_pin: GPIO3
    baud_rate: ${baud_rate}
    stop_bits: 1
    parity: NONE
    debug:
      direction: BOTH
      dummy_receiver: true

modbus:
  send_wait_time: 1s
  uart_id: uart_bus
  id: modbus1

modbus_controller:
- id: modbus_device
  address: 0x1   ## address of the Modbus slave device on the bus
  modbus_id: modbus1
  #command_throttle: 0ms
  setup_priority: -10
  update_interval: 10s


captive_portal:
    
sensor:
  - platform: modbus_controller
    modbus_controller_id: modbus_device
    name: "Relay Cycle Status"
    register_type: holding
    address: 0x0000
    id: relay_raw_state
    icon: "mdi:state-machine"
    entity_category: diagnostic

  - platform: modbus_controller
    modbus_controller_id: modbus_device
    register_type: holding
    address: 0x0001 
    name: "System Status"
    icon: "mdi:information-outline"
    entity_category: diagnostic

  - platform: modbus_controller
    modbus_controller_id: modbus_device
    name: "Current Temperature"
    device_class: temperature
    state_class: measurement
    register_type: holding
    address: 0x0003
    unit_of_measurement: "°C"
    value_type: S_WORD
    accuracy_decimals: 1
    filters:
      - multiply: ${temp_multiplier}
    icon: "mdi:thermometer"

  - platform: modbus_controller
    modbus_controller_id: modbus_device
    register_type: holding
    address: 0x0008
    name: "High Temp Alarm"
    icon: "mdi:temperature-celsius"
    entity_category: diagnostic

  - platform: modbus_controller
    modbus_controller_id: modbus_device
    register_type: holding
    address: 0x0009
    name: "Low Temp Alarm"
    icon: "mdi:temperature-celsius"
    entity_category: diagnostic

  - platform: modbus_controller
    modbus_controller_id: modbus_device
    register_type: holding
    address: 0x000C
    name: "Alarm Status"
    #icon: "mdi:"
    entity_category: diagnostic

number:
  # Using entity_category: config puts these in a separate section in HA
  - platform: modbus_controller
    modbus_controller_id: modbus_device
    name: "Threshold Start"
    address: 0x0006
    icon: "mdi:play-circle-outline"
    min_value: 20
    max_value: 60
    step: 0.1
    lambda: "return x * 0.1;"
    write_lambda: "return x * 10.0;"

  - platform: modbus_controller
    modbus_controller_id: modbus_device
    name: "Threshold Stop"
    address: 0x0007
    icon: "mdi:stop-circle-outline"
    min_value: 20
    max_value: 60
    step: 0.1
    lambda: "return x * 0.1;"
    write_lambda: "return x * 10.0;"

switch:
    
  - platform: modbus_controller
    modbus_controller_id: modbus_device
    name: "Relay Control"
    address: 0x0011
    register_type: holding
    use_write_multiple: false
    icon: "mdi:power-cycle"
     # SENDING the command to 0x0011
    write_lambda: |-
      payload.push_back(0x01);
      payload.push_back(0x06);
      payload.push_back(0x00);
      payload.push_back(0x11); 

      if (x) {
        payload.push_back(0x00);
        payload.push_back(0x00);
      } else {
        payload.push_back(0x00);
        payload.push_back(0x01);
      }
      return true;
    # MATCHING the UI toggle to the status at 0x0000
    lambda: |-
      return (id(relay_raw_state).state > 0);

  - platform: modbus_controller
    modbus_controller_id: modbus_device
    name: "Buzzer"
    address: 0x000D
    register_type: holding
    use_write_multiple: false
    entity_category: diagnostic
    #icon: "mdi:power-cycle"
  
  - platform: modbus_controller
    modbus_controller_id: modbus_device
    name: "High Temp Alarm Enable"
    address: 0x000E
    register_type: holding
    use_write_multiple: false
    entity_category: diagnostic
    icon: "mdi:power-cycle"

  - platform: modbus_controller
    modbus_controller_id: modbus_device
    name: "Low Temp Alarm Enable"
    address: 0x000F
    register_type: holding
    use_write_multiple: false
    entity_category: diagnostic
    icon: "mdi:power-cycle"
  
  - platform: modbus_controller
    modbus_controller_id: modbus_device
    name: "BL"
    address: 0x0015
    register_type: holding
    use_write_multiple: false
    entity_category: diagnostic
    icon: "mdi:power-cycle"

    
button:
  # this provides for a possibility to restart from the web console or Home automation should we ever need it
  - platform: restart
    name: "Restart"
  - platform: safe_mode
    name: "Restart Safe Mode"
    
# Web server for manual control (optional)
web_server:
  port: 80
  #version: 1
2 Likes

Hi Rufan,

thank you very much!
I highly appreciate it, actually there are a couple of things I can learn from your code.

Thanks for sharing. Confirmed working on XY-ST10-W (with XY-WFPOW).