Howto make a smart touch lamp with esp32s3 and a relay

A couple days ago I converted a 3way bulb lamp to a smart touch controlled 3way bulb lamp. After doing that I realized we had a lamp on our desk that was made smart using an smart outlet and a smart button.

The wife wasn’t crazy about the button laying around, so I figured I should convert this lamp to a touch lamp. I’d just received a couple of M5stampS3 with the esp32-S3 chip. I originally looked to use them on the 3way bulb lamp but failed to get the touch sensor to work. I posted the issue and fortunately peter-s had the answer.

So with the S3 chip and the smallest single channel relay I had sitting around I started out converting the lamp. For the most part I left the lamp intact. The only modification was to cut the hot wire and run it through the relay connected to the esp chip and to attach a touch sensor wire to the lamp.

I picked up these small boxes from amazon. The inside of the box had 4 elevated mounts screw spots on the top and bottom of the box. These took away too much room so I needed to cut them out of the box. I had to put three holes in the box, two for the lamp cord and one for the USB cable that powers the esp chip. It was tight in the box but I managed to get it all to fit.

I put tie-wraps on the lamp cord to keep it from shifting in the case. The esp had pins on both sides. I cut the pins off the side I wasn’t using to make it easier to slide the chip into the box. I put the extra cable running from the esp to the relay between them to keep the devices separated. I ran the touch wire out the same slot as the lamp cord. I actually had to cut of the slotted edge off the box top of part of the box because it was hitting the ESP and preventing the box from closing. As you can see I got it all in the box.

I drilled a hole in the lamp and put a screw in it to hold the touch wire in place.

Last thing to do was to burn the following program into the chip from HA ESPHOME add-on. I use a widows laptop with the esp connected to the laptop USB port. In the ESPHOME interface I
used the “Plug into this computer” option to write the esp flash. I’ve recently updated this code to include a switch that allows disabling the touch feature on the lamp and the ability to report a long press to HA. So the code below is an update. Details on these features were also added to the end of this post.

esphome:
  name: "lamp-computer-room"
  friendly_name: Lamp comp room

esp32:
  board: esp32-s3-devkitc-1
  framework:
    type: arduino

# Enable logging
logger:
  level: info
#  level: debug

# Enable Home Assistant API
api:
  encryption:
    key: !secret api_key

ota:
  password: !secret ota_password

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "FR-Lamp-Touch"
    password: !secret wifi_password

# Enable Web server.
web_server:
  port: 80
  
captive_portal:

sensor:
  # hack so the touch sensor doesn't kick off at power on
  - platform: uptime
    name: Uptime Sensor
    id: time_since_boot
    update_interval: 3s
    internal: True

text_sensor:
  - platform: template
    name: "comp room lamp"
    id: comp_room_lamp
    icon: "mdi:toggle-switch"
    on_value:
      then:
        - if:
            condition:
              text_sensor.state:
                id: comp_room_lamp
                state: ""
            else:
              - delay: 200ms
              - text_sensor.template.publish:
                  id: comp_room_lamp
                  state: !lambda 'return "";'


esp32_touch:
  setup_mode: false
  measurement_duration: 0.25ms
  
binary_sensor:
  - platform: esp32_touch
    name: "Touch Pad Pin 13"
    pin: GPIO13
    threshold: 550000
    filters:
      # Small filter, to debounce the spurious events.
      - delayed_on: 10ms
      - delayed_off: 10ms
    #on_press:
    on_click:
    - min_length: 10ms
      max_length: 500ms
      # Short touch to turn light on and off
      then:
        - logger.log: 
            level: info
            format: "Touch turn switch on"

        - if:
          # test to ignore random event on boot and when touch is disabled
            condition:
              - lambda: 'return  (id(time_since_boot).raw_state > 1 && (!(id(touch_disabled).state)));'
            then:
              if:
                  condition:
                      # if light is off
                      - switch.is_off: power_switch
                  then:
                    # then we turn it on
                    - switch.turn_on: power_switch
                    - logger.log: 
                        level: info
                        format: "Touch turn switch on"
                  else:
                    # else it's on so we turn it off
                    - switch.turn_off: power_switch
                    - logger.log: 
                        level: info
                        format: "Touch turn switch off"

    - min_length: 500ms
      max_length: 2000ms
      then:
        - logger.log: "${devicename} long click"
        - if:
          # test to ignore random event on boot and when touch is disabled
            condition:
              - lambda: 'return  (id(time_since_boot).raw_state > 1 && (!(id(touch_disabled).state)));'
            then:
              text_sensor.template.publish:
                id: comp_room_lamp
                state: !lambda 'return "long_press";'
        

# restart-button
button:
  - platform: restart
    name: "restart-esp32-dim-touch"


switch:
  - platform: gpio
    name: power_switch
    id: power_switch
    restore_mode: RESTORE_DEFAULT_OFF
    pin: GPIO7

  - platform: template
    name: "touch disabled"
    id: touch_disabled
    restore_mode: ALWAYS_OFF
    optimistic: true

At this point the last thing to do is set the touch detection value. Up above the lines:

logger:
#  level: info
  level: debug

and

esp32_touch:
  setup_mode: true

This result in esphome logging the values being detected by the touch sensor. You have to look at the values being reported without touch and with touch. You want to select a value some where in the middle of these two reading to set the threshold value under the binary_sensor esp32_touch platform:

binary_sensor:
  - platform: esp32_touch
    name: "Touch Pad Pin 13"
    pin: GPIO13
    threshold: XXXXXX

You’ll burn your update to flash again. Once you’ve done the initial burn you can do all subsequent burns via wifi.

Once you have a threshold value set to something that gives a good touch response you need to change “logging” to info and set “setup_mode” to false and reinstall the ESP code.

I like the touch sensor on the esp32-s3. It works a lot better than the older esp32 that I used on the 3 way bulb lamp. I have a couple more three way bulb lamps I’m going to convert and I’m planning on using the M5stamp chip for those. If you look at the thread for the 3 way bulb lamp you’ll see I actually had to write some lambda code to get a satisfactory touch response.

My wife is very satisfied with this update. I hope the write-up is useful.

The ESP32 code listed above has been updated with a couple of features. First It includes a switch that can be used to disable the touch feature on the lamp. This is so that HA can have an automation that will disable the touch feature of all of the touch lamp so the lamps can be moved around during cleaning, and you don’t have to worry you’ll turn the lights on and off by this movement. The second update includes a text sensor that will report “long_press” if you hold your touch on the lamp for a few seconds. The room this lamp sits in also has an overhead lamp that is controlled by a Sonoff T0 G2 switch. I use this with an HA automation to toggle the overhead light by doing a long touch on the lamp.

Here’s the HA automation that toggles the touch feature on all of the touch enabled lamps before and after a cleaning session. This automation handles short, medium and long press events from the bottom touch pad of a Sonoff T0 G2 switch that controls multiple lights in the family room. The long touch on the Sonoff touch pad will flash the light in the family room so I know I’ve toggled this option and then it toggles the disable touch feature in the touch lamps. A short press will toggle the touch lamp in the family room on and off. A medium press will change the brightness of the family room touch lamp.

alias: Family room bottom switch toggle bs lamp
description: ""
trigger:
  - platform: state
    entity_id:
      - sensor.family_room_lamps_ctrl_bottom_pad_event
condition: []
action:
  - if:
      - condition: state
        entity_id: sensor.family_room_lamps_ctrl_bottom_pad_event
        state: short
    then:
      - service: light.toggle
        metadata: {}
        data: {}
        target:
          entity_id: light.bs_family_room_lamp
    else:
      - if:
          - condition: state
            entity_id: sensor.family_room_lamps_ctrl_bottom_pad_event
            state: medium
        then:
          - service: button.press
            metadata: {}
            data: {}
            target:
              entity_id: button.fr_lamp_touch_bs_brightness_step
        else:
          - if:
              - condition: state
                entity_id: sensor.family_room_lamps_ctrl_bottom_pad_event
                state: long
            then:
              - service: light.toggle
                target:
                  entity_id: light.bs_family_room_lamp
                data: {}
              - delay:
                  hours: 0
                  minutes: 0
                  seconds: 1
                  milliseconds: 0
              - service: light.toggle
                target:
                  entity_id: light.bs_family_room_lamp
                data: {}
              - delay:
                  hours: 0
                  minutes: 0
                  seconds: 1
                  milliseconds: 0
              - service: light.toggle
                target:
                  entity_id: light.bs_family_room_lamp
                data: {}
              - delay:
                  hours: 0
                  minutes: 0
                  seconds: 1
                  milliseconds: 0
              - service: light.toggle
                target:
                  entity_id: light.bs_family_room_lamp
                data: {}
              - service: switch.toggle
                target:
                  entity_id: switch.fr_lamp_touch_bs_touch_disabled
                data: {}
              - service: switch.toggle
                metadata: {}
                data: {}
                target:
                  entity_id: switch.master_bed_touch_lamp_touch_disabled
              - service: switch.toggle
                metadata: {}
                data: {}
                target:
                  entity_id: switch.lamp_computer_room_touch_disabled
mode: single

Here’s the HA automation that handles the long_press event from the lamp, toggling the overhead light:

alias: computer room light toggle on lamp long press
description: ""
trigger:
  - platform: state
    entity_id:
      - sensor.lamp_computer_room_comp_room_lamp
    to: long_press
condition: []
action:
  - service: switch.toggle
    target:
      entity_id: switch.light_relay1_comp_room
    data: {}
mode: single

For completeness I’m including the Sonoff T0 G2 code for the family room switch so you can see the complete thread tied to toggling the touch feature. While the Sonoff includes two relays, I do not use them with this wall switch. The Switch is used as a dumb switch that simply reports short, medium and long press events for the two touch pads. Automation in HA then determine what to do with these touches. About I included the automation that handles the bottom touch pad events.

esphome:
  name: "family-room-lamps-ctrl"
  friendly_name: "family room lamps ctrl"

esp8266:
  board: esp8285

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Fam-Room-Switch"
    password: !secret wifi_password

captive_portal:

api:
  encryption:
    key: !secret api_key

# Enable logging
logger:
  level: info

ota:
  password: !secret ota_password

# Enable Web server.
web_server:
  port: 80

text_sensor:
  - platform: template
    name: "top_pad_event"
    id: top_pad_event
    icon: "mdi:toggle-switch"
    on_value:
      then:
        - if:
            condition:
              text_sensor.state:
                id: top_pad_event
                state: ""
            else:
              - delay: 200ms
              - text_sensor.template.publish:
                  id: top_pad_event
                  state: !lambda 'return "";'

  - platform: template
    name: "bottom_pad_event"
    id: bottom_pad_event
    icon: "mdi:toggle-switch"
    on_value:
      then:
        - if:
            condition:
              text_sensor.state:
                id: bottom_pad_event
                state: ""
            else:
              - delay: 200ms
              - text_sensor.template.publish:
                  id: bottom_pad_event
                  state: !lambda 'return "";'


# restart-button
button:
  - platform: restart
    name: "restart_switch"

binary_sensor:
  - platform: gpio
    internal: True
    pin:
      number: GPIO0
      mode:
        input: true
        pullup: true
      inverted: true
    name: "top_touch_pad"
    on_click:
      - min_length: 50ms
        max_length: 500ms
        then:
          - text_sensor.template.publish:
              id: top_pad_event
              state: !lambda 'return "short";'
          - logger.log: 
              level: info
              format: "short - FR top touch"

      - min_length: 500ms
        max_length: 1500ms
        then:
          - text_sensor.template.publish:
              id: top_pad_event
              state: !lambda 'return "medium";'
          - logger.log: 
              level: info
              format: "medium - FR top touch"

      - min_length: 1500ms
        max_length: 10000ms
        then:
          - text_sensor.template.publish:
              id: top_pad_event
              state: !lambda 'return "long";'
          - logger.log: 
              level: info
              format: "long - FR top touch"


  - platform: gpio
    internal: True
    pin:
      number: GPIO9
      mode:
        input: true
        pullup: true
      inverted: true
    name: "bottom_touch_pad"
    on_click:
      - min_length: 50ms
        max_length: 500ms
        then:
          - text_sensor.template.publish:
              id: bottom_pad_event
              state: !lambda 'return "short";'
          - logger.log: 
              level: info
              format: "short - FR bottom touch"

      - min_length: 500ms
        max_length: 1500ms
        then:
          - text_sensor.template.publish:
              id: bottom_pad_event
              state: !lambda 'return "medium";'
          - logger.log: 
              level: info
              format: "medium - FR bottom touch"

      - min_length: 1500ms
        max_length: 10000ms
        then:
          - text_sensor.template.publish:
              id: bottom_pad_event
              state: !lambda 'return "long";'
          - logger.log: 
              level: info
              format: "long - FR bottom touch"
 
output:
  # Register the blue LED as a dimmable output ....
  - platform: esp8266_pwm
    id: blue_led
    pin: GPIO13
    inverted: true

# Example configuration entry
light:
  - platform: status_led
    id: status_light
    name: "family_room_Switch_led"
    output: blue_led
4 Likes

This is EXACTLY what I need.
Any chance you might put some of these together and sell them somewhere? I have the need, but not sure I am that confident in my wiring skills, beyond putting a contact on my existing touchable 3-way lamp.

I probably need to touch the lamp to get things working. I can’t see an easy way to provide what you would require. For the 3 way lamp I replace the wiring and socket in the lamp. For a simple one way lamp it would also most be possible, as you only challenge would be to split the cord and connect it to the relay, and then connect the wire lead to the metal service. Anyway I think it’s probable something I’d probably need to touch the lamp in order to make sure it worked. If you use the chip I suggested so that you can use wires with the connectors, it’s not that hard to wire it up. You should give it a go. If you look at the three way bulb write up, It shows my initial set up, which was just the lamp internals, without the lamp housing. It would be a good starting point for figuring out the wiring.

My lamp is only a 3-way touchable one due to the bubl in it and an add-on in between the lamp and bulb. If only they made a “smart” version of that add on, but seems they do not.

I put 3 smart bulbs in each of my lamps and they can be turned on via IR, voice, or a button and rotary encoder in a easy to access area. I think it offers a lot more flexibility without damaging lamp cords or hearing those clicking relays. Also, im pretty sure its not recommended to use the neutral as the switch(relay). It may be acceptable wherever you live of course but, here in the US we switch the hot(L)

It’s a self contained lamp, but I should have recommended cutting the hot wire. I’ll update that above. The bulb only pulls a few watts. It’s not a problem for heat in the relay. My objective was to remove the local disconnected button required to turn on the lamp and make the lamp function like lamps historically functioned. I could have told my spouse to pull out the phone to turn on the light, but it turns out this solution is was a lot better option that was immediately accepted.

Its doesnt have anything to do with the only so many watts or heat generated. Its about safety and no one getting electrocuted. The neutral carries extra/unused electricity back to the breaker panel and hot goes to the “thing” lamp in your case. When that relay is Open(off) you’re still feeding the lamp from the hot wire and any accident that may need the wiring looked at or a bulb breaks and someone goes in there with pliars or yoir dog knocks it over on a wet floor or whatever. That lamp and bulb are still hot, even when you have your relay turned Off.

Im 100% with you on not pulling the phone out at home to control lights. I get tired of the phone and want to put it away when i get home.

Your correct running the hot wire through the relay is the best option.

I wasnt trying to be right or argue. You posted a project and did a great job with with it if i may say so. I only meant to point out a safety/fire hazzard mainly because its a guide thats meant for others to follow…

On a side note. I have 3 brothers, they’re all union electricians and i hear this all the time. Not only will most electricians not want the liability and refuse to do any work at your house if they see any backwards or DIY wiring in violation of code but, there could be violations of some insurance policies too. Its serious stuff.