Flashing Sonoff POW R3 to ESPHome

Good morning. I couldn’t find any information on how to hack a Sonoff POW R3 to make it an ESPHome device. Can someone please help me?
Thank you

Have you had any joy with this? I have a POW R3 on order and also want to intigrated with HA using ESPHome (I am migrating devices to ESPHome to avoid having to run a seperate MQTT server).

Almost… for some reason the switch want work, and the V showing 15v - but its a start, to get you online.

esphome:
name: sonoffvpkids

esp32:
board: nodemcu-32s

Enable logging

logger:

wifi:
ssid: xxxxx
password: xxxxx

Optional manual IP

manual_ip:
static_ip: x.x.x.x
gateway: x.x.x.x
subnet: 255.255.255.0
dns1: x.x.x.x

Enable fallback hotspot (captive portal) in case wifi connection fails

ap:
ssid: “sonoffvpkids Fallback Hotspot”
password: “xxxxxxxxxxxxx”

captive_portal:

Enable Home Assistant API

api:
password: “xxxxx”

ota:
password: “xxxxx”

uart:
rx_pin: GPIO16
baud_rate: 4800

sensor:

  • platform: cse7766
    update_interval: 2s
    current:
    name: sonoffvpkids Current
    id: a_sensor
    voltage:
    name: sonoffvpkids Voltage
    id: v_sensor
    power:
    name: sonoffvpkids Power
    id: w_sensor
    on_value_range:
    - above: 4.0
    then:
    - light.turn_on: switch_led
    - below: 3.0
    then:
    - light.turn_off: switch_led
    energy:
    name: sonoffvpkids Energy
    id: wh_sensor

output:

  • platform: ledc
    id: led
    pin:
    number: GPIO18
    inverted: True

light:

  • platform: monochromatic
    id: switch_led
    output: led
    internal: True
  • platform: status_led
    id: wifi_status_led
    internal: True
    pin:
    number: GPIO05
    inverted: True

switch:

  • platform: template
    name: sonoffvpkids sw
    optimistic: true
    id: relay_1
    lambda: |-
    if (isnan(id(w_sensor).state)) {
    return {};
    } else if (id(w_sensor).state > 4) {
    // Running
    return true;
    } else {
    // Not running
    return false;
    }
    turn_off_action:
    • switch.turn_on: relay_off
      turn_on_action:
    • switch.turn_on: relay_on
  • platform: gpio
    restore_mode: ALWAYS_OFF
    internal: true
    id: relay_off
    pin: GPIO04
    on_turn_on:
    • switch.turn_on: relay_off
    • delay: 500ms
    • switch.turn_off: relay_off # bi-stable relay so no need to keep on.
    • light.turn_off: switch_led
      interlock: [relay_on]
  • platform: gpio
    restore_mode: ALWAYS_OFF
    internal: true
    id: relay_on
    pin: GPIO02
    on_turn_on:
    • switch.turn_on: relay_on
    • delay: 500ms
    • switch.turn_off: relay_on # bi-stable relay so no need to keep on.
    • light.turn_on: switch_led
      interlock: [relay_off]
  • platform: restart
    name: sonoffvpkids Restart
2 Likes

Not sure if this is helpful, but I recently set up a Sonoff POW R3 with ESPHome for use with a hot water tank immersion heater.

Took various bits of code from various sources locally and online. In my case, I’ve added 3 Dallas temperature sensors connecting to GPIO4 so I can monitor the tank temperature.

I’m using this to utilise spare solar power after the car is charged. (better than selling to the grid for 4p a kWh :stuck_out_tongue: )

I initially used GPIO15 for the temperature sensors but that stopped everything from working.

substitutions:
  esphome_name: hotwatersonoff
  esphome_friendly_name: Hot Water Sonoff

esphome:
  name: ${esphome_name}
  friendly_name: Hot Water Heater Sonoff

esp8266:
  board: esp8285

# Enable logging
logger:
  baud_rate: 0

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

ota:
  password: "xxxxxxxxx"

wifi:
  output_power: 18db
  power_save_mode: light
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Hot-Water-Heater-Sonoff"
    password: "xxxxxxxxx"

captive_portal:

dallas:
  - pin: GPIO4

# Enable web server
web_server:
  port: 80

time:
  - platform: homeassistant
    timezone: Europe/London

#GPIO0 Button (inverted)
#GPIO12 Relay and Red LED
#GPIO13 Blue LED (inverted)


uart:
  rx_pin: RX
  baud_rate: 4800

binary_sensor:
  - platform: gpio
    pin:
      number: GPIO0
      mode: INPUT_PULLUP
      inverted: True
    name: "${esphome_friendly_name} Button"
    on_press:
      - switch.toggle: fakebutton

switch:
  - platform: template
    name: "Hot Water Relay"
    optimistic: true
    id: fakebutton
    turn_on_action:
    - switch.turn_on: relay
    - light.turn_on: led
    turn_off_action:
    - switch.turn_off: relay
    - light.turn_off: led
  - platform: gpio
    id: relay
    inverted: yes
    pin: GPIO12
    restore_mode: RESTORE_DEFAULT_ON

output:
  - platform: esp8266_pwm
    id: pow_blue_led
    pin:
      number: GPIO13
      inverted: True

light:
  - platform: monochromatic
    name: "${esphome_friendly_name} Blue LED"
    output: pow_blue_led
    id: led
    internal: true

sensor:
  - platform: wifi_signal
    name: "${esphome_friendly_name} WiFi Signal"
    update_interval: 60s
  - platform: uptime
    name: "${esphome_friendly_name} Uptime"
  - platform: cse7766
    update_interval: 2s
    current:
      name: "${esphome_friendly_name} Current"
    voltage:
      name: "${esphome_friendly_name} Voltage"
    power:
      name: "${esphome_friendly_name} Power"
    energy:
      name: "${esphome_friendly_name} Energy"
  - platform: dallas
    address: 0xb60000001284c428
    name: "${esphome_friendly_name} Temperature Sensor 1"
    id: temp_1
    icon: "mdi:water-thermometer"
    filters:
      - filter_out: nan
  - platform: dallas
    address: 0x4f00000012821c28
    name: "${esphome_friendly_name} Temperature Sensor 2"
    id: temp_2
    icon: "mdi:water-thermometer"
    filters:
      - filter_out: nan
  - platform: dallas
    address: 0xb10000001483f228
    name: "${esphome_friendly_name} Temperature Sensor 3"
    id: temp_3
    icon: "mdi:water-thermometer"
    filters:
      - filter_out: nan
  - platform: template
    name: "{esphome_friendly_name} Average Tank Temperature"
    icon: "mdi:water-thermometer"
    unit_of_measurement: "°C"
    lambda: |-
      return (id(temp_1).state + id(temp_2).state + id(temp_3).state)/3;
    update_interval: 60s
  - platform: template
    name: "{esphome_friendly_name} Max Tank Temperature"
    icon: "mdi:water-thermometer"
    unit_of_measurement: "°C"
    lambda: |-
      return ((id(temp_1).state > id(temp_2).state) ? ((id(temp_1).state > id(temp_3).state) ? id(temp_1).state : id(temp_3).state) : (id(temp_2).state > id(temp_3).state) ? id(temp_2).state : id(temp_3).state);
    update_interval: 60s
  - platform: template
    name: "{esphome_friendly_name} Min Tank Temperature"
    icon: "mdi:water-thermometer"
    unit_of_measurement: "°C"
    lambda: |-
      return ((id(temp_1).state < id(temp_2).state) ? ((id(temp_1).state < id(temp_3).state) ? id(temp_1).state : id(temp_3).state) : (id(temp_2).state < id(temp_3).state) ? id(temp_2).state : id(temp_3).state);
    update_interval: 60s
    
1 Like

Hi Rich/Thansen1980 , can any of you post the clean configuration that will work with pow r3 (25A) for esp home

Thank you so much :heartbeat:

+1, any clean fully working config?

define “clean”, pls :rofl:

This one should be undaerstandable, tho it has some extra lambdas, but all are explainded with comments. Working fawlessly here with my Sonoff pow origin (POWR316). The 3 dashes at the beginning are to make things easier for the yaml parser, but it’s not mandatory.

---
##### information #####
# model: Sonoff pow origin (POWR316)
# explication: https://templates.blakadder.com/sonoff_POWR316.html
# buy:  https://www.aliexpress.com/wholesale?SearchText=sonoff+powr316

# GPIO Pinout
# ---------------------------------------
# | Pin    | Component                  |
# |========|============================|
# | GPIO0  | Button                     |
# |--------|----------------------------|
# | GPIO05 | Status LED (inverted)      |
# |--------|----------------------------|
# | GPIO13 | Relay                      |
# |--------|----------------------------|
# | GPIO16 | CSE7766 Rx                 |
# |--------|----------------------------|
# | GPIO18 | LED (inverted)             |
# ---------------------------------------

# As the communication with the CSE7766 is done using UART, you need to have an UART bus in your configuration with the rx_pin connected to the CSE7766.
# Additionally, you need to set the baud rate to 4800.

##### basic configs #####

# set up variables
substitutions:
  my_name: 'POW316-WM'
  my_device: pow316-wm
  my_reboot_timeout: 30min
  my_update_interval: 12sec
  my_static_ip: 10.11.12.13

# define core configs
esphome:
  name: ${my_device}
  friendly_name: ${my_name}
  area: 'Waschküche'
  comment: I usually comment the last change here
  on_boot:
    priority: -100
    then:
      - switch.turn_on: relay

esp32:
  board: esp32dev

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  fast_connect: true
  reboot_timeout: ${my_reboot_timeout}
  manual_ip:
    static_ip: ${my_static_ip}
    gateway: 192.168.1.1
    subnet: 255.255.255.0
    dns1: 192.168.1.1
    dns2: 9.9.9.9
  ap:
    # after connecting to it, navigate to http://192.168.4.1/
    ssid: '${my_name} AP'
    password: !secret ap_wifi_password
    ap_timeout: 1min

captive_portal:

api:
  reboot_timeout: ${my_reboot_timeout}
  encryption:
    key: !secret native_api_key

ota:
  safe_mode: true
  password: !secret ota_password

logger:
  level: DEBUG
  # Disable logging to serial
  baud_rate: 0

web_server:
  port: 80
  auth:
    username: !secret web_srv_user
    password: !secret web_srv_pswd

time:
  - platform: homeassistant
    id: homeassistant_time

    on_time:
        # device restart (5 minutes after dhcp router and WiFi APs reboot)
      - days_of_week: THU
        hours: 4
        minutes: 35
        seconds: 0
        then:
          - button.press: reboot
        
        # shift values sortly before midnight reset
      - hours: 23
        minutes: 59
        seconds: 59
        then:
          - sensor.template.publish:
              id: power_today_minus_3
              state: !lambda return id(power_today_minus_2).state;
          - sensor.template.publish:
              id: power_today_minus_2
              state: !lambda return id(power_today_minus_1).state;
          - sensor.template.publish:
              id: power_today_minus_1
              state: !lambda return id(power_today).state;

#### specific configs #####
uart:
  rx_pin: GPIO16
  baud_rate: 4800
  data_bits: 8
  parity: EVEN
  stop_bits: 1

binary_sensor:
  # define button on device and it's press action
  - platform: gpio
    name: '${my_name} Taster'
    pin:
      number: GPIO0
      mode:
        input: true
        pullup: true
    filters:
      # eliminate bouncing noise
      - delayed_on_off: 25ms
    on_press:
      then:
        - switch.toggle: relay

  # show as active when power reading is above 0W, idle when it's 0W. Both if state persists for at least 2 update cicles.
  - platform: template
    name: '${my_name} aktivity'
    device_class: power
    filters:
      # eliminate bouncing noise
      - delayed_on_off: 24s
    lambda: 'if (id(power).state>(0)) return true; else return false;'

  - !include common/binary_sensors/status.yaml

switch:
  - platform: gpio
    name: '${my_name} Relais'
    icon: 'mdi:lightning-bolt-circle'
    id: relay
    pin: GPIO13
    restore_mode: RESTORE_DEFAULT_ON
    on_turn_on:
      light.turn_on: led
    on_turn_off:
      light.turn_off: led

button:
  - !include common/buttons/restart_button.yaml
  - !include common/buttons/restart_safe_mode.yaml

sensor:
  # energy measurement
  - platform: cse7766

    # voltage sensor
    voltage:
      name: '${my_name} voltage'
      id: voltage
      unit_of_measurement: 'V'
      accuracy_decimals: 1
      device_class: voltage
      state_class: measurement
      icon: 'mdi:alpha-v'
      filters:
        # agreggate values of polls
        - throttle_average: ${my_update_interval}
        # map from sensor -> measured value
        - calibrate_linear:
            - 0.647 -> 0.0
            - 221.1-> 219
            - 230 -> 226.5
        # set not a number and values below 5V to zero or state value in V
        - lambda: 'if (isnan(x)) return(0); else if (x<(5)) return(0); else return(x);'
        # don't report minor changes from previous value
        - delta: 1

    # current sensor
    current:
      name: '${my_name} current'
      id: current
      unit_of_measurement: 'A'
      accuracy_decimals: 3
      state_class: measurement
      icon: 'mdi:alpha-a'
      filters:
        # agreggate values of polls
        - throttle_average: ${my_update_interval}
        # map from sensor -> measured value
        - calibrate_linear:
           - 0.0 -> 0.004
           - 3.374 -> 3.37
           - 4.846 -> 4.864
           - 8.045 -> 8.097
        # set not a number and low values plus own consumption to zero or correct higher values with own consumption and state value
        - lambda: 'if (isnan(x)) return(0); else if (x<(0.025 + 0.008)) return(0); else return(x - 0.008);'
        # don't report minor changes from previous value
        - delta: 0.02

    # power sensor
    power:
      name: '${my_name} power'
      id: power
      device_class: power
      state_class: measurement
      unit_of_measurement: W
      accuracy_decimals: 1
      icon: 'mdi:alpha-w'
      filters:
        # agreggate values of polls
        - throttle_average: ${my_update_interval}
        # map from sensor -> measured value
        - calibrate_linear:
           - 0.0 -> 1.2
           - 768.4 -> 771.7
           - 1110.3 -> 1109
           - 1807.4 -> 1802
        # set not a number and values below 5W plus own consumption to zero or correct higher values with own consumption and state value
        - lambda: 'if (isnan(x)) return(0); else if (x<(5 + 1.2)) return(0); else return(x - 1.2);'
        # don't report minor changes from previous value
        - delta: 1

    # energy sensor
    energy:
      name: '${my_name} energy'
      id: energy
      device_class: energy
      state_class: measurement
      accuracy_decimals: 1
      unit_of_measurement: 'Wh'
      filters:
        # agreggate values of polls
        - throttle_average: ${my_update_interval}        
        # set not a number to zero or state value
        - lambda: 'if (isnan(x)) return(0); else return(x);'
        # don't report minor changes from previous value
        - delta: 0.5
  
  # power factor
  - platform: template
    update_interval: ${my_update_interval}
    name: '${my_name} power factor'
    unit_of_measurement: '%'
    device_class: power_factor
    state_class: measurement
    id: power_factor
    accuracy_decimals: 1
    icon: 'mdi:decagram-outline'
    lambda: 'return id(power).state / id(voltage).state / id(current).state;'
    filters:
      # set not a number to zero or state value
      - lambda: 'if (isnan(x)) return(0); else return(x*100);'
      # don't report changes from previous value lower than 1%
      - delta: 1

  # total daily energy 
  - platform: total_daily_energy
    name: '${my_name} total daily energy'
    id: power_today
    power_id: power
    accuracy_decimals: 1
    unit_of_measurement: 'Wh'
    state_class: total_increasing
    device_class: energy    
    icon: 'mdi:numeric-0-box-outline'
    filters:
      # set not a number to zero or state value
      - lambda: 'if (isnan(x)) return(0); else return(x);'

  # shifting total daily energy short before reset at midnight
  - platform: template
    name: '${my_name} total energy today -1'
    id: power_today_minus_1
    accuracy_decimals: 1
    unit_of_measurement: 'Wh'
    icon: 'mdi:numeric-1-box-outline'
    filters:
      # set not a number to zero or state value
      - lambda: 'if (isnan(x)) return(0); else return(x);'

  - platform: template
    name: '${my_name} total energy today -2'
    id: power_today_minus_2
    unit_of_measurement: 'Wh'
    accuracy_decimals: 1
    icon: 'mdi:numeric-2-box-outline'
    filters:
      # set not a number to zero or state value
      - lambda: 'if (isnan(x)) return(0); else return(x);'

  - platform: template
    name: '${my_name} total energy today -3'
    id: power_today_minus_3
    accuracy_decimals: 1
    unit_of_measurement: 'Wh'
    icon: 'mdi:numeric-3-box-outline'
    filters:
      # delete not a number or state value
      - lambda: 'if (isnan(x)) return(0); else return(x);'

  - platform: uptime
    id: uptime_sensor
    internal: true
    on_raw_value:
      then:
        - text_sensor.template.publish:
            id: uptime_human
            state: !lambda |-
              int seconds = round(id(uptime_sensor).raw_state);
              int days = seconds / (24 * 3600);
              seconds = seconds % (24 * 3600);
              int hours = seconds / 3600;
              seconds = seconds % 3600;
              int minutes = seconds /  60;
              seconds = seconds % 60;
              return (
                (days ? to_string(days) + "d " : "") +
                (hours ? to_string(hours) + "h " : "") +
                (minutes ? to_string(minutes) + "m " : "") +
                (to_string(seconds) + "s")
              ).c_str();

  # WiFi rssi in dBs
  - platform: wifi_signal
    id: wifi_rssi_dbs
    internal: true
    update_interval: 5min
    filters:
      # set not a number to zero or state value
      - lambda: 'if (isnan(x)) return(0); else return(x);'

  # WiFi rssi in percents
  - platform: template
    name: '${my_name} WiFi Signalstärke'
    id: wifi_rssi
    update_interval: 5min
    unit_of_measurement: '%'
    accuracy_decimals: 0
    icon: mdi:wifi-strength-2
    lambda: |-
      if (id(wifi_rssi_dbs).state < -92.0)
        return 1.0;
      if (id(wifi_rssi_dbs).state > -21.0)
        return 100.0;
      else
        return round(( -0.0154 * id(wifi_rssi_dbs).state * id(wifi_rssi_dbs).state ) - ( 0.3794 * id(wifi_rssi_dbs).state ) + 98.182 );
    filters:
      # set not a number to zero or state value
      - lambda: 'if (isnan(x)) return(0); else return(x);'
      # do not report changes lower than +-2%
      - delta: 2.0

output:
  - platform: ledc
    pin: GPIO18
    id: onboard_led
    inverted: true
    max_power: 0.66

light:
  - platform: monochromatic
    output: onboard_led
    id: led
    internal: true

text_sensor:
  - platform: wifi_info
    ip_address:
      name: '${my_name} IP'
      id: espip
      icon: mdi:ip-network-outline
    ssid:
      name: '${my_name} SSID'
      id: espssid
      icon: mdi:access-point-network
    bssid:
      name: '${my_name} BSSID'
      id: espbssid
      icon: mdi:plus-network-outline
  - platform: template
    name: '${my_name} MAC'
    id: espmac
    icon: mdi:minus-network-outline
    update_interval: 15min
    lambda: |-
      return {WiFi.macAddress().c_str()};
  - platform: version
    name: '${my_name} ESPHome Version'
  - platform: template
    name: '${my_name} Uptime'
    id: uptime_human
    icon: mdi:clock-start

status_led:
  # use blue LED as status indicator
  pin:
    number: GPIO05
    inverted: true

Is it possible to output to the serial port on the board? I need some help with the UART I think,

Hi,

You have a number of include files. Can you provide the code for these or a link to a repository where they have been sourced?

Thanks in advance.

Hi @RichShort

Could you please share pinout diagram? I don’t know how to identity free gpio pins on PCB? I also want to connect temp sensor and use in my water heater

Long delay, sorry didn’t get a notification. In case you still need it I used GPIO15

Other pins probably work too. Depends how many you need

0,12,13 are used by sonoff so those can’t be used.

Hi everyone,

I just published a detailed guide on how to flash a Sonoff POW R3 with ESPHome. This tutorial covers the entire process, from hardware preparation to firmware configuration, and includes tips for integrating the device into Home Assistant.

If you’re looking to customize your Sonoff POW R3 for better energy monitoring or local control, this guide might help!

Feel free to ask if you have any questions or need further clarification. Happy flashing! :rocket:

Hi everyone,

I’ve recently got a Sonoff POW R3 and attempting to flash it with ESPHome firmware.

However, I appear to be failing at the first hurdle! So far I’ve read a couple of guides on this but I can’t for the life of me get this thing into flashing mode.

It doesn’t seem to matter how long it hold the ‘Flashing’ button for, I just get no response. The FTDI tried to communicate but no get no response - I’ve tried switching the TX/RX pins to no avail.

I know the FTDI board and wires works, I’ve just checked the esptool flash-id command returns data with one of the SONOFF light switch I’ve got.

The guide by ‘sigalou’ suggests that the blue led should got off when it’s in flashing mode, but that just stays constantly on for me - though slightly dimmer that if I let it boot normally.

The board in my POW R3 appears to be dated 2022/12/26. I’m using an FTDI FT232RI which is fine for all the light switch I have - maybe I need a different one for this?

Any suggest/pointers as to were I’m going wrong with this would be much appreciated.

Many thanks