Intercept, Echo, Filter Modbus communications from 1 UART hub to another? (VS Pool Pump Solar Speed Override Project)

TLDR Version:
I have a Variable Speed pool pump that is controlled by a cloud-based automation interface over Modbus/RS485. Using an ESP8266 and 2 MAX3485 boards, I’d like to (normally) directly echo requests sent from the interface (Master), received by one MAX3485, to the other MAX3485, transmitting to the pump (Slave). And vice versa, with the responses from the slave back to the master. However, when an external switch is closed (Solar heating diverter ON), I’d like to stop the Master’s pump speed requests from being transmitted and instead send requests for a higher Solar Override speed. I believe that the pump’s responses could continue being relayed to the interface in this Solar Override state. Once the Solar switch opens again, the system would return to the normal, non-overridden state.
Detailed Version:
My pool and VS pump is controlled by a Hayward Aqualogic type system. My Hayward Ecostar pump’s drive recently died so I replaced it with a Century V-green 270 VS motor/drive (https://www.regalbeloit.com/products/electric-motors/ac-motors-nema/pump-motors/pool-pump-motors/2-7-max-hp-pool-pump-motor-1-phase-3600-rpm-230-v-48y-frame-tefc-ecm27squ) . I also got Century’s Vlink automation interface (https://www.regalbeloit.com/brands/Century/products/pool-and-spa/Vlink) that provides cloud-based mobile app control and scheduling of the pump. The interface communicates, as Master, via RS485/Modbus, with the pump as Slave, via a published protocol (https://www.troublefreepool.com/attachments/gen3-epc-modbus-communication-protocol-_rev4-17-pdf.358807/).
My original intent was to override the scheduled pump speed to a higher speed during periods when my solar heater was activated, ensuring that the pump would generate enough pressure to send water to my second storey high solar collectors.The pump would then return to the slower, more efficent, scheduled speed once the solar heater went offline.
So, I started off with Nodemcu ESP8266 and a MAX3845 TTL to RS485 interface board. A simple pushbutton switch was used as a temporary stand-in for a magnetic reed switch that would read the solar diverter valve’s position when solar heating was on. When the switch was closed, a UART Switch in esphome would send the request for a pump speed of 3250 rpm every few seconds. This worked as planned, immediately increasing the pump speed to the desired speed. However, after about 2 to 6 seconds later the Vlink interface would send its own request for the original lower speed, according to it’s preprogrammed schedule. This would start to slow the pump down again for a second or two until the next high speed request was sent from the ESP8266, repeat, etc. I doubt all that bouncing around would be good for the pump in the long-term. All the while, the pump is reporting back acknowledgements of speed requests and the actual speed at the moment.
I was unable to neutralize the interface’s unnecessary requests by changing the repeat rate of the override speed requests; the interface’s requests kept getting through intermittently. So my current thought is to add another MAX3485 UART hub to the system, such that one MAX3485 would be connected on the Vlink-facing side and the other would be on the pump-facing side. Then my device could intercept the communications between the Master and Slave, directly echoing requests and responses from one UART hub to the other. While the Solar Heating was online, the device would stop echoing requests from the Vlink to the pump but instead send the override speed requests. Responses from the pump would still be relayed back to the Vlink though.
I’ve tried to figure out how to program this in esphome but I’m at a loss. I don’t have any experience with C++/lambda or Python progamming, though I’ve done a couple of simple esphome projects already. Could someone please point me in the proper direction? Thanks!
Here’s what I have so far:


external_components:
  # use  GitHub
  - source:
      type: git
      url: https://github.com/martgras/esphome
      ref: modbus_component
    components: [ modbus_controller ]

esphome:
  name: vgreen-modbus
  platform: ESP8266
  board: nodemcuv2

wifi:
  ssid: !secret_wifi_ssid
  password: !secret_wifi_password
  manual_ip:
    static_ip: 192.168.1.95
    gateway: 192.168.1.1
    subnet: 255.255.255.0
 
  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Vgreen Fallback Hotspot"
    password: "h3fGzKx4dDIs"

captive_portal:

# Enable logging
logger:
  hardware_uart: uart1
  level: VERBOSE


api:
    


ota:




mqtt:
  broker: 192.168.1.10
  username: ronkmd
  password: Airway1010

uart:
  - id: mod_bus_pump
    tx_pin: D8
    rx_pin: D7
    baud_rate: 9600
  - id: mod_bus_vgreen
    tx_pin: D6
    rx_pin: D5
    baud_rate: 9600

modbus_controller:
  command_throttle: 100ms #100ms
  id: modbus_vgreen
  # Modbus device addr
  address: 0x15
  #ctrl_pin: 4 # if you need to set the driver enable (DE) pin high before transmitting data configure it here
  uart_id: mod_bus_pump
  
  
switch:
  platform: uart
  uart_id: mod_bus_pump
  id: pump_demand_3250
  data: [0x15, 0x44, 0x20, 0x00, 0xC8, 0x32, 0x2F, 0x04] # request pump speed of 3250 rpm
  
  
  
binary_sensor:
  - platform: gpio
    pin:
      number: D2
      mode: INPUT_PULLUP
      inverted: True
    name: "Reed Switch"
    id: reed_switch
    on_press:
      while:
        condition:
           binary_sensor.is_on: reed_switch
        then:
          - switch.turn_on: pump_demand_3250
          - logger.log: "Solar is ON"
          - delay: 1.5s
    on_release:
      - switch.turn_off: pump_demand_3250
      - while:
          condition:
            binary_sensor.is_off: reed_switch
          then:
          - logger.log: "Solar is OFF"
          - delay: 10s
          

It sound like you can control directly so why not remove schedule from local interface and control in HA?

I buy Ethernet to RS485 for my pump today. Maybe when I get I can check this as well

Yes, I thought about that but I’ve already purchased the Vlink interface and it’s pretty nice using its mobile app, even though it’s cloud-based. Otherwise I’d have to figure out how to do multiple schedules in esphome that would get their times and speeds from input.datetime and input.number from HA, as well as having to calculate CRC 16 (MODBUS) on the fly. Maybe a Blynk mobile app too. Definitely a more elegant and complex solution but pretty over my head at this point.

Would a UART Text Sensor get me where I need to be? Looks promising but not sure about exactly how to implement it for 2 UARTs.

So I went back to my 1 UART yaml code:

external_components:
  # use  GitHub
  - source:
      type: git
      url: https://github.com/martgras/esphome
      ref: modbus_component
    components: [ modbus_controller ]

esphome:
  name: vgreen_modbus
  platform: ESP8266
  board: nodemcuv2

wifi:
  ssid: ##
  password: ##
  manual_ip:
    static_ip: 192.168.1.95
    gateway: 192.168.1.1
    subnet: 255.255.255.0
 
  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Vgreen Fallback Hotspot"
    password: "h3fGzKx4dDIs"

captive_portal:

# Enable logging
logger:
  hardware_uart: uart1
  level: VERBOSE

# Enable Home Assistant API
api:



ota:




mqtt:
  broker: 192.168.1.10
  username: ##
  password: ##

uart:
  id: mod_bus
  tx_pin: D8
  rx_pin: D7
  baud_rate: 9600

modbus_controller:
  command_throttle: 20ms #100ms
  id: modbus_vgreen
  # Modbus device addr
  address: 0x15
  #ctrl_pin: 4 # if you need to set the driver enable (DE) pin high before transmitting data configure it here
  uart_id: mod_bus
  
  
  
switch:
  platform: uart
  uart_id: mod_bus
  id: pump_demand_3250
  data: [0x15, 0x44, 0x20, 0x00, 0xC8, 0x32, 0x2F, 0x04]
  #send_every: 4s
 
 
  
binary_sensor:
  - platform: gpio
    pin:
      number: D2
      mode: INPUT_PULLUP
      inverted: True
    name: "Reed Switch"
    id: reed_switch
    on_press:
      while:
        condition:
           binary_sensor.is_on: reed_switch
        then:
         #- logger.log: "Solar is ON"
         # - uart.write:
      # #set pump demand to 3250rpm
             # id: mod_bus
             # data: [0x15, 0x44, 0x20, 0x00, 0xC8, 0x32, 0x2F, 0x04]
             # #sets rpm to 3250 rpm 
         # - delay: 10s
          - switch.turn_on: pump_demand_3250
          - logger.log: "Solar is ON"
          - delay: 1.5s
    on_release:
      - switch.turn_off: pump_demand_3250
      - while:
          condition:
            binary_sensor.is_off: reed_switch
          then:
          - logger.log: "Solar is OFF"
          - delay: 10s
          

and this is a sample of my debug log"


INFO Waiting for result...
INFO OTA successful
INFO Successfully uploaded program.
INFO Starting log output from 192.168.1.95 using esphome API
INFO Successfully connected to 192.168.1.95
[16:57:22][I][app:102]: ESPHome version 2021.9.1 compiled on Sep 23 2021, 16:56:50
[16:57:22][C][wifi:501]: WiFi:
[16:57:22][C][wifi:361]:   SSID: [redacted]
[16:57:22][C][wifi:362]:   IP Address: 192.168.1.95
[16:57:22][C][wifi:364]:   BSSID: [redacted]
[16:57:22][C][wifi:365]:   Hostname: 'vgreen_modbus'
[16:57:22][C][wifi:369]:   Signal strength: -55 dB ▂▄▆█
[16:57:22][V][wifi:371]:   Priority: 0.0
[16:57:22][C][wifi:373]:   Channel: 8
[16:57:22][C][wifi:374]:   Subnet: 255.255.255.0
[16:57:22][C][wifi:375]:   Gateway: 192.168.1.1
[16:57:22][C][wifi:376]:   DNS1: (IP unset)
[16:57:22][C][wifi:377]:   DNS2: (IP unset)
[16:57:22][C][uart_esp8266:097]: UART Bus:
[16:57:22][C][uart_esp8266:099]:   TX Pin: GPIO15
[16:57:22][C][uart_esp8266:102]:   RX Pin: GPIO13
[16:57:22][C][uart_esp8266:103]:   RX Buffer Size: 256
[16:57:22][C][uart_esp8266:105]:   Baud Rate: 9600 baud
[16:57:22][C][uart_esp8266:106]:   Data Bits: 8
[16:57:22][C][uart_esp8266:107]:   Parity: NONE
[16:57:22][C][uart_esp8266:108]:   Stop bits: 1
[16:57:22][C][uart_esp8266:110]:   Using hardware serial interface.
[16:57:22][C][ModbusController:257]: EPSOLAR:
[16:57:22][C][ModbusController:258]:   Address: 0x15
[16:57:22][C][gpio.binary_sensor:015]: GPIO Binary Sensor 'Reed Switch'
[16:57:22][C][gpio.binary_sensor:016]:   Pin: GPIO4 (Mode: INPUT_PULLUP, INVERTED)
[16:57:22][C][logger:193]: Logger:
[16:57:22][C][logger:194]:   Level: VERBOSE
[16:57:22][C][logger:195]:   Log Baud Rate: 115200
[16:57:22][C][logger:196]:   Hardware UART: UART1
[16:57:22][C][uart.switch:040]: UART Switch 'pump_demand_3250'
[16:57:22][C][captive_portal:148]: Captive Portal:
[16:57:22][C][ota:029]: Over-The-Air Updates:
[16:57:22][C][ota:030]:   Address: 192.168.1.95:8266
[16:57:22][C][api:135]: API Server:
[16:57:22][C][api:136]:   Address: 192.168.1.95:6053
[16:57:22][C][mqtt:061]: MQTT:
[16:57:22][C][mqtt:063]:   Server Address: 192.168.1.10:1883 (192.168.1.10)
[16:57:22][C][mqtt:064]:   Username: [redacted]
[16:57:22][C][mqtt:065]:   Client ID: [redacted]
[16:57:22][C][mqtt:067]:   Discovery prefix: 'homeassistant'
[16:57:22][C][mqtt:068]:   Discovery retain: YES
[16:57:22][C][mqtt:070]:   Topic Prefix: 'vgreen_modbus'
[16:57:22][C][mqtt:072]:   Log Topic: 'vgreen_modbus/debug'
[16:57:22][C][mqtt:075]:   Availability: 'vgreen_modbus/status'
[16:57:22][C][mqtt.switch:038]: MQTT Switch 'pump_demand_3250': 
[16:57:22][C][mqtt.switch:039]:   State Topic: 'vgreen_modbus/switch/pump_demand_3250/state'
[16:57:22][C][mqtt.switch:039]:   Command Topic: 'vgreen_modbus/switch/pump_demand_3250/command'
[16:57:22][C][mqtt.binary_sensor:018]: MQTT Binary Sensor 'Reed Switch':
[16:57:22][C][mqtt.binary_sensor:019]:   State Topic: 'vgreen_modbus/binary_sensor/reed_switch/state'
[16:57:25][V][modbus_base:048]: Modbus recieved Byte  21 (0X15)
[16:57:25][V][modbus_base:048]: Modbus recieved Byte  67 (0X43)
[16:57:25][V][modbus_base:048]: Modbus recieved Byte  32 (0X20)
[16:57:25][V][modbus_base:048]: Modbus recieved Byte  80 (0X50)
[16:57:25][V][modbus_base:048]: Modbus recieved Byte  236 (0Xec)
[16:57:25][V][modbus_base:048]: Modbus recieved Byte  21 (0X15)
[16:57:25][V][modbus_base:048]: Modbus recieved Byte  67 (0X43)
[16:57:25][V][modbus_base:048]: Modbus recieved Byte  16 (0X10)
[16:57:25][V][modbus_base:048]: Modbus recieved Byte  11 (0Xb)
[16:57:25][V][modbus_base:048]: Modbus recieved Byte  185 (0Xb9)
[16:57:25][V][modbus_base:048]: Modbus recieved Byte  251 (0Xfb)
[16:57:25][V][component:207]: Component esphome.coroutine took a long time for an operation (0.05 s).
[16:57:25][V][component:208]: Components should block for at most 20-30ms.
[16:57:25][V][modbus_base:048]: Modbus recieved Byte  21 (0X15)
[16:57:25][V][modbus_base:048]: Modbus recieved Byte  69 (0X45)
[16:57:25][V][modbus_base:048]: Modbus recieved Byte  32 (0X20)
[16:57:25][V][modbus_base:048]: Modbus recieved Byte  0 (0X0)
[16:57:25][V][modbus_base:048]: Modbus recieved Byte  5 (0X5)
[16:57:25][V][modbus_base:048]: Modbus recieved Byte  253 (0Xfd)
[16:57:25][V][modbus_base:048]: Modbus recieved Byte  6 (0X6)
[16:57:25][V][modbus_base:048]: Modbus recieved Byte  21 (0X15)
[16:57:25][V][modbus_base:048]: Modbus recieved Byte  69 (0X45)
[16:57:25][V][modbus_base:048]: Modbus recieved Byte  16 (0X10)
[16:57:25][V][modbus_base:048]: Modbus recieved Byte  0 (0X0)
[16:57:25][V][modbus_base:048]: Modbus recieved Byte  5 (0X5)
[16:57:25][V][modbus_base:048]: Modbus recieved Byte  164 (0Xa4)
[16:57:25][V][modbus_base:048]: Modbus recieved Byte  3 (0X3)
[16:57:25][V][modbus_base:048]: Modbus recieved Byte  186 (0Xba)
[16:57:25][V][modbus_base:048]: Modbus recieved Byte  87 (0X57)
[16:57:25][V][component:207]: Component esphome.coroutine took a long time for an operation (0.08 s).
[16:57:25][V][component:208]: Components should block for at most 20-30ms.

The modbus requests from the Master begin with “0x15 0x20” and the responses from the Slave begin with “0x15 0x10”.

So, how can I capture the hex bytes and echo them to a second UART? I’ve tried to figure out how to use UART Text Sensor but no luck. I just don’t get it.