ESPHome modbus Growatt ShineWiFi-S

Hi! old thread, but I would like to try this. It`s a bit unclear to me if the blue and/or the black wire is connected to the Growat com-plug? Is the blue and black wire connected together? Regards, Gunnar

Thanks, @WilbertV , you made my day. I configured uart with 9600 baud, checked all modbus adresses without any output. After reading your post, I changed baud to 115200 and: well, got it :slight_smile:

Hi,
can you share how to connect the 2 esp devices?
Thanks, Gabor

1 Like

Sure, the shine wifi stick is in its regular spot, a usb-a plug with screw cap. The wemos is connected to a rs485 module which in turn is linked up to half a utp cable which i plugged into one of the 485 ports.

1 Like

Thank you, I will try it.

Those with of you with SPH 10k-TL3 BH-UP, does turning AC charing ON via modbus still work for you? I tried to enable it, I’m getting this error:

18:54:57	[D]	[switch:012]	
'Growatt AC Charging' Turning ON.
18:54:57	[D]	[switch:055]	
'Growatt AC Charging': Sending state ON
18:54:57	[D]	[modbus:119]	
Modbus error function code: 0x86 exception: 1
18:54:57	[E]	[modbus_controller:091]	
Modbus error function code: 0x6 exception: 1 
18:54:57	[E]	[modbus_controller:100]	
Modbus error - last command: function code=0x6  register address = 0x444  registers count=1 payload size=2

config:

switch:
 - platform: modbus_controller
   modbus_controller_id: growatt
   name: "${devicename} AC Charging"
   icon: mdi:battery-charging-100
   address: 1092
   register_type: holding

I can enable it from the Growatt web, the switch changes state, I can then disable it from ESPHome, but I cannot enable it locally…

Will try with my new firmware.

My firmware was updated today following some issues with the SoC , experienced sudden drops around 20% of SOC all the way to 10% as well as over 20% losses (eg. Gap between discharge and charge levels).

Anyone else experiencing the same issues?

Growatt have updated my BMS firmware with the following remark:

It’s important to understand, Ronald, that you have the right to raise concerns about your battery, whether it’s about programming issues, charging and discharging cycles, or any errors that occur without any changes made on your end.

However, if you’re changing the EMS commands daily, as mentioned, this is not advisable for the battery’s health. Constant changes can lead to degradation and imbalance over time, potentially necessitating battery replacement. Therefore, I strongly advise against altering the parameters frequently, whether every 5 minutes or daily.

To give you an analogy, imagine you buy an iPhone and modify its software and programming to suit your needs, but these changes damage the device beyond the manufacturer’s settings. If the phone is damaged, it’s not due to the phone itself but the software and modifications you’ve applied. That’s why I’m asking you to avoid making daily changes to the parameters.

Let’s ensure the longevity and efficiency of your battery system by sticking to recommended settings and updates.

Can confirm the ac on/off still works (sph8000 tl3 bh-up)

Hi all,
I’m attempting to flash my ShineWifi-X device with ESPHome.

I have bridged the GND and GP100 pins, and when connected the red and blue flights are lit.

I have installed the CH340 drivers on my Windows PC, and I’m using esphome web to flash (https://web.esphome.io/).

I am able to connect to the device and it shows ‘connected’, however when I click ‘initialize for first use’, it always fails to connect and displays ‘failed to initialize’.

I’ve tried different USB ports on my PC and am not using any cables, but the problem persists.

Alternatively, I also used the esphome-flasher exe tool, but it displays this error

“Unexpected error: could not open port ‘COM6’: PermissionError(13, ‘Access is denied.’, None, 5)”

I can see at least one other post here where someone had this same issue, and it looks like they just used a ESP board instead. I’d much prefer not to, given I’ve already purchased the dongle.

Has anyone else experienced this and were you able to overcome it?

Thanks!

Hi!
I finally was able to connect a ESP8266 board (DIY), and a RS485 → TTL card to my MID 10KTL3-X inverter today. The code is mostly collected here, and at a Norwegian forum. I get data from the inverter, but I also get a CRC-error for each line of data (I think…). See attached picture. I read somewhere that this can be corrected by adding this: esp32:
board: nodemcu-32s
framework:
type: arduino
version: 2.0.6

I don`t know if this is also correct for ESP8266? I used board: ESP12e in my code. Is the board type important? Can attach code later…

Thanks to this thread, I figured out a ESPHome config that works for my ShineWiFi-X. Inverter is Growatt MID-15X-TL, no batteries, just solar.

Some sensors are hashed out because I was struggling with fallouts and resets. You might need to adjust some values for your own setup.

Config is built to make sure I’m able to tune production rate close to actual consumption, to avoid export when export price is negative.

Automations in Home Assistant takes care of the adjustment the production rate.

Here goes.

growatt.yaml:

substitutions:
  device_description: Growatt MID-nnX-TL
  friendly_name: Growatt MID-nnX-TL
  sensor_prefix: MID-nnX-TL-GW1
  name: Growatt
  publish_interval: 10s
  update_realtime: 20s

 # Interval in seconds
  interval0: "1"
  interval1: "5"
  interval2: "10"
  interval3: "20"
  interval4: "30"
  interval5: "60"

esphome:
  name: growatt
  friendly_name: Growatt

globals:
  - id: last_selected_interval
    type: int
    restore_value: true
    initial_value: "10"

esp8266:
  board: esp07s

# Enable logging
logger:
  baud_rate: 0

# Mqtt
mqtt:
  broker: xxx.xxx.xxx.xxx
  username: "growatt"
  password: "*************"
  birth_message:
    topic: sensor/${friendly_name}/status
    payload: online
  will_message:
    topic: sensor/${friendly_name}/status
    payload: offline

# Enable Home Assistant API
api:
  encryption:
    key: "****************************+**************="

ota:
  password: "************3458f3dbc***********"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Growatt Fallback Hotspot"
    password: "bkAhgdXX4p2T"

captive_portal:

web_server:
  port: 80

time:
  - platform: homeassistant
    id: homeassistant_time

uart:
  id: mod_bus
  tx_pin: 1
  rx_pin: 3
  baud_rate: 115200
  
modbus:
  id: modbus1
  uart_id: mod_bus
  
modbus_controller:
  - id: growatt
    address: 0x1
    modbus_id: modbus1
    update_interval: ${interval2}s  # use the substitution variable for the update interval
    setup_priority: -10  

output:
# Blue Led
  - id: light_bl
    platform: gpio
    pin: 16
# Green Led
  - id: light_gr
    platform: gpio
    pin: 0
# Red Led
  - id: light_rd
    platform: gpio
    pin: 2

interval:
  - interval: ${publish_interval}
    then:
        - wait_until:
            mqtt.connected:
        - mqtt.publish_json:
            topic: "sensor/${friendly_name}/data"
            payload:
              root["Pv1Power"] = id(Pv1Power).state;
              
        - logger.log: "MQTT Data published"

select:
  - platform: template
    id: modbus_interval_select
    name: "Update interval seconds"
    icon: mdi:update
    optimistic: true
    restore_value: true
    initial_option: $interval2
    options:
      - $interval0
      - $interval1
      - $interval2
      - $interval3
      - $interval4
    on_value:
      then:
        - globals.set:
            id: last_selected_interval
            value: !lambda 'return std::stoi(x);'
        - wait_until:
            condition:
              lambda: 'return id(last_selected_interval) == std::stoi(x);'
        - lambda: |-
            ESP_LOGD("main", "Value updated to %s", x.c_str());

        - delay: 2s 
        - if:
            condition:
              lambda: 'return id(last_selected_interval) != 0;'
            then:
              - lambda: |-
                  int interval_ms = std::stoi(id(modbus_interval_select).state) * 1000;
                  ESP_LOGD("main", "Selected interval: %d", interval_ms);
                  id(growatt).set_update_interval(interval_ms);
                  static bool first_change_after_restart = true;
                  if (!first_change_after_restart) {
                    delay(5000);
                    ESP.restart();
                  } else {
                    first_change_after_restart = false;
                  }

text_sensor:
  - platform: template
    name: "${sensor_prefix} status"
    icon: mdi:eye
    entity_category: diagnostic
    lambda: |-
      if ( (id(status).state = 1) ) {
        return {"Normal"};
      } else if ( (id(status).state = 0) )  {
        return {"Waiting"};
      } else {
        return {"Fault!"};
      }


#switch:
#  - platform: modbus_controller
#    name: "${sensor_prefix} OnOff"
#    address: 0
#    register_type: holding

button:
  - platform: restart
    name: "${friendly_name} Restart"

number:
  - platform: modbus_controller
    name: "${sensor_prefix} Active Power Rate"
    id: poweroutput
    address: 3
    value_type: U_WORD
    min_value: 0
    max_value: 100
    entity_category: config

sensor:
  - platform: mqtt_subscribe
    name: "${sensor_prefix} Configured Power Rate"
    id: config_power_rate
    topic: sensor/${friendly_name}/config 
    on_value:
      then:
        - lambda: |-
            esphome::modbus_controller::ModbusController *controller = id(growatt);
            uint16_t reg = 3; // Register: Max output active power (in %)
            float value = id(config_power_rate).state;
            uint16_t register_value = static_cast<uint16_t>(value);
            modbus_controller::ModbusCommandItem setOutputPower_command = modbus_controller::ModbusCommandItem::create_write_single_command(controller, reg, register_value);
            controller->queue_command(setOutputPower_command);
#        - delay: 5s 
    
  - platform: wifi_signal
    name: "WiFi Signal Sensor"
    update_interval: 60s

  - platform: modbus_controller
    address: 0
    register_type: "read"
    internal: True
    id: status

  - platform: modbus_controller
    name: "${sensor_prefix} Curr_Energy"
    address: 1
    register_type: "read"
    unit_of_measurement: W
    device_class: energy
    icon: mdi:flash
    value_type: U_DWORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1
    register_count: 2


  - platform: modbus_controller
    name: "${sensor_prefix} Pv1 Volt"
    address: 3
    register_type: "read"
    unit_of_measurement: V
    device_class: voltage
    entity_category: diagnostic
    icon: mdi:flash
    value_type: U_WORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1
    register_count: 2

#  - platform: modbus_controller
#    name: "${sensor_prefix} Pv1 Amps"
#    address: 4
#    register_type: "read"
#    unit_of_measurement: A
#    device_class: current
#    entity_category: diagnostic
#    icon: mdi:flash
#    value_type: U_WORD
#    accuracy_decimals: 1
#    filters:
#    - multiply: 0.1

  - platform: modbus_controller
    name: "${sensor_prefix} Pv1 Power"
    id: Pv1Power
    address: 5
    register_type: "read"
    unit_of_measurement: W
    device_class: power
    entity_category: diagnostic
    icon: mdi:flash
    value_type: U_DWORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1
#    register_count: 2

  - platform: modbus_controller
    name: "${sensor_prefix} Pv2 Volt"
    address: 7
    register_type: "read"
    unit_of_measurement: V
    device_class: voltage
    entity_category: diagnostic
    icon: mdi:flash
    value_type: U_WORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1
    register_count: 2

#  - platform: modbus_controller
#    name: "${sensor_prefix} Pv2 Amps"
#    address: 8
#    register_type: "read"
#    unit_of_measurement: A
#    device_class: current
#    entity_category: diagnostic
#    icon: mdi:flash
#    value_type: U_WORD
#    accuracy_decimals: 1
#    filters:
#    - multiply: 0.1

  - platform: modbus_controller
    name: "${sensor_prefix} Pv2 Power"
    address: 9
    register_type: "read"
    unit_of_measurement: W
    device_class: power
    entity_category: diagnostic
    icon: mdi:flash
    value_type: U_DWORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1
    register_count: 2

  - platform: modbus_controller
    name: "${sensor_prefix} Pv3 Volt"
    address: 11
    register_type: "read"
    unit_of_measurement: V
    device_class: voltage
    entity_category: diagnostic
    icon: mdi:flash
    value_type: U_WORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1
#    register_count: 2

#  - platform: modbus_controller
#    name: "${sensor_prefix} Pv3 Amps"
#    address: 12
#    register_type: "read"
#    unit_of_measurement: A
#    device_class: current
#    entity_category: diagnostic
#    icon: mdi:flash
#    value_type: U_WORD
#    accuracy_decimals: 1
#    filters:
#    - multiply: 0.1

  - platform: modbus_controller
    name: "${sensor_prefix} Pv3 Power"
    address: 13
    register_type: "read"
    unit_of_measurement: W
    device_class: power
    entity_category: diagnostic
    icon: mdi:flash
    value_type: U_DWORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1
#    register_count: 2

  - platform: modbus_controller
    name: "${sensor_prefix} Pv4 Volt"
    address: 15
    register_type: "read"
    unit_of_measurement: V
    device_class: voltage
    entity_category: diagnostic
    icon: mdi:flash
    value_type: U_WORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1    
#    register_count: 2

#  - platform: modbus_controller
#    name: "${sensor_prefix} Pv4 Amps"
#    address: 16
#    register_type: "read"
#    unit_of_measurement: A
#    device_class: current
#    entity_category: diagnostic
#    icon: mdi:flash
#    value_type: U_WORD
#    accuracy_decimals: 1
#    filters:
#    - multiply: 0.1        

  - platform: modbus_controller
    name: "${sensor_prefix} Pv4 Power"
    address: 17
    register_type: "read"
    unit_of_measurement: W
    device_class: power
    entity_category: diagnostic
    icon: mdi:flash
    value_type: U_DWORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1


#  - platform: modbus_controller
#    name: "${sensor_prefix} GW1 P1 Volt"
#    address: 50
#    register_type: "read"
#    unit_of_measurement: V
#    device_class: voltage
#    entity_category: diagnostic
#    icon: mdi:flash
#    value_type: U_WORD
#    accuracy_decimals: 1
#    filters:
#    - multiply: 0.1

#  - platform: modbus_controller
#    name: "${sensor_prefix} GW1_P2_Volt"
#    address: 51
#    register_type: "read"
#    unit_of_measurement: V
#    device_class: voltage
#    entity_category: diagnostic
#    icon: mdi:flash
#    value_type: U_WORD
#    accuracy_decimals: 1
#    filters:
#    - multiply: 0.1

#  - platform: modbus_controller
#    name: "${sensor_prefix} GW1_P3_Volt"
#    address: 52
#    register_type: "read"
#    unit_of_measurement: V
#    device_class: voltage
#    entity_category: diagnostic
#    icon: mdi:flash
#    value_type: U_WORD
#    accuracy_decimals: 1
#    filters:
#    - multiply: 0.1

  - platform: modbus_controller
    name: "${sensor_prefix} PV_Energy_Today"
    address: 53
    register_type: "read"
    unit_of_measurement: kWh
    device_class: energy
    icon: mdi:flash
    value_type: U_DWORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1
#    register_count: 2

  - platform: modbus_controller
    name: "${sensor_prefix} PV_Energy_Lifetime"
    address: 55
    register_type: "read"
    unit_of_measurement: kWh
    state_class: total_increasing
    device_class: energy
    icon: mdi:flash
    value_type: U_DWORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1

#  - platform: modbus_controller
#    name: "${sensor_prefix} Inverter_Temp"
#    address: 93
#    register_type: "read"
#    unit_of_measurement: C
#    device_class: temperature
#    icon: mdi:thermometer
#    value_type: U_WORD
#    accuracy_decimals: 1
#    filters:
#    - multiply: 0.1

Hi Ronald,

This is my first ESP project and would love to know how you did the Export Limit?
I understand its not only a read but you also need a Write.
How to do?

Thanks

Regards,
Bjorn

Hi Bjorn, which growatt inverter do you have? The mod, min and sph all use different addresses.

Hi, I have the Growatt MIN 3000TL-XE inverter.

Best to Google a bit on this specific make, im not sure if it supports export control out of the box. Also good to check the modbus registers for this type.

Mine, an sph 8000, responds as follows:

   - platform: modbus_controller
     name: "${devicename} Export Control"
     icon: mdi:home-import-outline
     address: 122
     value_type: U_WORD
     optionsmap:
       "Disabled": 0
       "Enabled": 1

Thanks,

i’m still confused. If i want to use the ShineWifi’-X stick,
Do i need to add a 1K resistor between SW1 and the GPI00?? like they mention on the openinvertergateway github

I would not know. I use a wemos with an rs485 module. No resistor in my setup.

1 Like

will never do what is is supposed to do unless you type == instead of =. The way it is written now, the statement is always true.

1 Like

Thank you!

I’ve now got my SPH5000 set up to my satisfaction - thank you very much to @scudderfish, @Plawa and @RT1080, your posts here were invaluable.

For the record - I’m using a mini_d1 connected to the serial port (as per @scudderfish) and am no longer using the shine dongle. Switching between Battery First / Load First (I’ve not needed grid first yet) using the code here is working flawlessly, so I’ve no need for the growatt servers.

Cheers,
Tim

3 Likes