Esphome wiegand and S20-ID keypad

I got this keypad/rfid scanner from Aliexpress and have been trying to get it connected to HA using esphome using the wiegang esphome componenet as show here Wiegand keypad and tag reader — ESPHome . The device I have is exactly what is shown in the picture.
As I understand it I have to put the device in “head reading mode” to make it work with the integration. The manual says I have to press “* programming password # 0 31 #” to set WG26 mode. I have done that and I can tell it change mode ecause I can no longer get to internal relay to working with the default “door openng pin”. But I’m still unable to see keypresses if I look at the logs of my esphome device. Relevant section in my yaml file :

wiegand:
  - id: mykeypad
    d0: D2  #groen
    d1: D1  #wit
    on_key:
      - lambda: ESP_LOGI("KEY", "received key %d", x);
    on_tag:
      - lambda: ESP_LOGI("TAG", "received tag %s", x.c_str());
      - homeassistant.tag_scanned: !lambda 'return x.c_str();'

    on_raw:
      - lambda: ESP_LOGI("RAW", "received raw %d bits, value %llx", bits, value);

I have the green and white wires connected to D1 and D2 of the nodemcu.
ALso trief with an esp32 and other ports but nothing.
Anyone that got this working care to share some tips?

That device has many brands and they all look similar. I have the same one too and the instructions for putting it into wiegand output mode were totally different. I was also still able to toggle the relay in wiegand mode.

For what its worth, the instructions in the manual that came with mine weren’t even right. I got it into wiegand26 by trial and error.

What do the logs show when you scan a tag? What does the keypad show when you press a key?

Where is the link to the model? Where is the instructions that came with it?

Is it wired directly to nodemcu? Do you have the 12v and 5v ground tied together?

Where is any useful Information? Its not working, we got it. A copy of the example configuration is of 0 value with troubleshooting.

Yes, my doubts is also about 3.3/5/12V levels.
Most seen such readers powered by 12V, but ESP use 3.3V logic.
Probably level shifters / optocouplers to be used.

You shouldn’t have doubts because it’s not voodoo magic and a mystery, you can simply Google it and read the data sheet or 50 other sources. Also no one knows what you’re talking about when you make statements about the “ESP” that’s pretty vague. There are a dozen ESP boards and dozens of esp modules and they’re all different so some less vague terms would be helpful if you want accurate information. The esp8266 nodeMCU is 5v capable and the data lines on the reader are around 4.7v, at least mine and the other ones i’ve heard about are but, you should check your own and not rely on just the internet people. You need to wire the data lines directly to it if it’s an esp8266 nodeMCU and combine all the grounds 12v and all and i speak from experience.




Actually, I shouldn’t say 50 sources because thats the reason for all the bad misinformation. One person publishes something as “facts” online and lazy people just copy it over and over and theres a proliferation of misinformation or just completely wrong information. Its best to just go read the data sheets from the manufacturer yourself and not be a victim of others laziness.

1 Like

connect the grounds of the nodemcu and the 12v power supply was exactly the kind of tip I was looking for. It works now.
Thanks for all these slightly passive agressive answers…

For future reference , the English side of the “manual” that came with the device :

I apologize for that. I can be a little abrasive but, I meant well. I just wanted to help. I went through hell with mine. I would get it out and mess with it over a saturday, get frustrated and put it back on the shelf. This went on for 2 months probably. Those look similar to my instructions, the Chenglish was horrible and difficult to understand what they meant to say… I’m glad you got it working though! Mine has been solid since I installed it. I like it.

Here is my config if you want to check it out or use any of it. This Git repo is really good too. This guy basically duplicated the default firmware and the codes/tags can be added/deleted from HA or from the keypad itself, it’s very well done and has a lot of functionality. It was too much for me and my house but, it’s worth checking out. If you need any help, give me a shout. I’ll try to be nicer next time.

I kind of liked this simple text sensor. It just displays who was the last person to access the door. I need to get it to show something else besides “unknown” after I do a esp or HA restart. I just havn’t gotten around to it.

Screenshot from 2024-03-01 14-17-18

sensor:

  - platform: rotary_encoder
    name: "Rotary Encoder"
    pin_a: 
      number: 1
      inverted: true
      mode:
        input: true
        pullup: true
    pin_b: 
      number: 3
      inverted: true
      mode:
        input: true
        pullup: true

  - platform: template
    name: Keypad Code
    id: keyCode    
    on_value:
      - if:
          condition:
            - lambda: 'return id(keyCode).state == 6722842;' 
          then:  
            - switch.toggle: OvrHead
            - text_sensor.template.publish:
                id: last_user
                state: "Mike's Tag"
      - if:
          condition:
            - lambda: 'return id(keyCode).state == 1050;' 
          then:  
            - switch.toggle: OvrHead
            - text_sensor.template.publish:
                id: last_user
                state: "Justin's Code"          

      - if:
          condition:
            - lambda: 'return id(keyCode).state == 6422842;' 
          then:  
            - switch.toggle: OvrHead
            - text_sensor.template.publish:
                id: last_user
                state: "Paula's Tag"
      - if:
          condition:
            - lambda: 'return id(keyCode).state == 1955;' 
          then:  
            - switch.toggle: OvrHead
            - text_sensor.template.publish:
                id: last_user
                state: "Paula's Code"    

      - if:
          condition:
            - lambda: 'return id(keyCode).state == 5553549;' 
          then:  
            - switch.toggle: OvrHead
            - text_sensor.template.publish:
                id: last_user
                state: "Golf Cart Tag"                

      - if:
          condition:
            - lambda: 'return id(keyCode).state == 6491970;' 
          then:  
            - switch.toggle: OvrHead
            - text_sensor.template.publish:
                id: last_user
                state: "Mike's Tag"
         
      - if:
          condition:
            - lambda: 'return id(keyCode).state != 5553549;' 
            - lambda: 'return id(keyCode).state != 6422842;' 
            - lambda: 'return id(keyCode).state != 6722842;'
            - lambda: 'return id(keyCode).state != 6491970;'
            - lambda: 'return id(keyCode).state != 6753957;'
            - lambda: 'return id(keyCode).state != 1050;'
            - lambda: 'return id(keyCode).state != 1955;'
          then:
            - text_sensor.template.publish:
                id: last_user
                state: "Invalid User Access"

wiegand:
  - id: mykeypad
    d0: D7
    d1: D6
    on_key:
      - lambda: ESP_LOGI("KEY", "received key %d", x);
    on_tag:
      - lambda: ESP_LOGI("TAG", "received tag %s", x.c_str());
      - sensor.template.publish:
         id: keyCode
         state: !lambda "return parse_number<float>(x).value();"   
     

     
          
    on_raw:
      - lambda: ESP_LOGI("RAW", "received raw %d bits, value %llx", bits, value);

key_collector:
  - id: pincode_reader
    source_id: mykeypad
    min_length: 4
    max_length: 5
    end_keys: "#"
    end_key_required: true
    clear_keys: "*"
    allowed_keys: "0123456789"
    timeout: 5s
    on_progress:
      - logger.log:
          format: "input progress: '%s', started by '%c'"
          args: [ 'x.c_str()', "(start == 0 ? '~' : start)" ]
    on_result:      
      then:
        - sensor.template.publish:
            id: keyCode
            state: !lambda "return parse_number<float>(x).value();"         
                
    on_timeout:
      - logger.log:
          format: "input timeout: '%s', started by '%c'"
          args: [ 'x.c_str()', "(start == 0 ? '~' : start)" ]    

binary_sensor: 
  
  - platform: status
    id: keypad_status
    name: Barn Keypad Status  

text_sensor:     

  - platform: template
    name: Last User
    id: last_user
    icon: mdi:clock-start    

button:
  - platform: restart
    name: "Keypad Restart"
  - platform: safe_mode
    name: "Keypad (Safe Mode)"  

ight:
  - platform: neopixelbus
    

    type: rgb
    variant: WS2811
    pin: D1
    num_leds: 20
    name: "Overhead LED Strip"    


  

switch:
  - platform: template  
    id: OvrHead
    optimistic: true 
    on_turn_on:
      - homeassistant.service:
          service: switch.toggle
          data: 
           entity_id: switch.overhead_door_barn_overhead 

  - platform: gpio
    name: 'Barn Outside Lights'
    id: barn_outside_lights
    pin: 
      number: D5
      mode:        
        output: true
    #inverted: true    
    inverted: true            

nice work. I see the benefit of having the the esp device do the unlocking as a failsafe for when home assistant is down. But I would like to do user management and automations from whiting homeassistant itself. Will always have the key as backup for when HA or power is down. Can already do that with tags using the tag scanned trigger in HA thanks to this line in the config.

      - homeassistant.tag_scanned: !lambda 'return x.c_str();'

Would like to do the same with pin code, just send them to home assistant and let it handle it. No big expert in yaml esphome stuff so your code and the other repo will come in handy to get this working!

Oh ya, I think its kind of silly to have HA do all the tag/pin processing then send an action back to the esp board to open the door, that kind lf seems like a no brainer. I agree with HA doing user management and setting access. That repo I sent you, it does both. Users and access can be done on the keypad just like it comes from the factory or you can do it in HA, it creates a number of input_select entities to give or remove permissions or add new users.

I guess there is no way to use the internal door opener (Relay) and or doorbell functions of the keypad when it is in wiegand (WG26) mode?

I think thats what theyre showing you in the wiring diagrams. I didnt plan to use mine so I didnt investigate a whole lot but, im pretty sure you can. I just dont know if the bell and relay are triggered with 5v?, 12v? 5v for one and 12v for the other? No clue on that one
.

I also want to know this. I want to primarily use the internal features of the device but also need to have HA receive the card ID’s for logging purposes. Seems like putting the reader in WG26 mode blocks it from triggering the relay when a valid card or PIN is inserted.

You realize the internal features can all be duplicated in HA as well as have it do logging. The problem with using the internal features is you have to be standing st the keypad to change anything or edit what users/codes have access. I didnt want to use the relay in mine but its simple enough to just add a relay with the esp board thats right there reading the rfid/key inputs. As far as the little goofy buzzer in them, well those are logic level and have wires for them.

I’m aware. I already have an NFC setup going using HA and some PN532 boards hooked up to D1 Mini’s, as well as Shelly 1’s for the door locks. Tag reading is very slow and user “unfriendly” but it works. My end goal with these readers is not only to improve the responsiveness of the system but also to have a separate system that doesn’t depend on HA, for if and when the network stack fails. I only mentioned logging but after thinking about it I might also use the system I already have set up with HA and adapt it to this reader.
As a last resort, if the readers lose access with HA I can just go into programming mode and disable wiegand so that they work standalone. I have no issue with adding and deleting individual tags manually.

I have done that. I made it so that the esphome file contains all the functionality for when Home Assistant or the network is down (open the lock with hard coded rfid tags and a number code) but it does send keypresses, read tags, doorbell button etc to home assistant for fancy automations (like make my knx switches beep when the doorbell button is pressed, set presence of the person whose tag was scanned etc…)
My current esphome yaml file wich is a mixture of Justin’s file and some tinkering :

esphome:
  name: a4
  friendly_name: A4

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "FYML16z8vXvo7Fh0pAbbXgBIDU1W6ufdmaJX2OEExcc="
  services:
    - service: play_rtttl
      variables:
        song_str: string
      then:
        - rtttl.play: !lambda 'return song_str;'

ota:
  password: "b8b168647db56209c357fb53e01d9a81"

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

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

captive_portal:

switch:
  - platform: gpio
    name: "lock gate"
    icon: "mdi:key-chain-variant"
    pin: 4
    inverted: false
    id: poortje
    on_turn_on:
    - delay: 3000ms
    - switch.turn_off: poortje


  - platform: restart
    name: "A4 Restart"



binary_sensor:
  - platform: status
    id: keypad_status
    name: Keypad front door status  



# Individual sensors
sensor:

  - platform: template
    name: Keypad Code
    id: keyCode    
    on_value:
      - if:
          condition:
            - lambda: 'return id(keyCode).state == 3317755;' 
          then:  
            - switch.toggle: poortje
            - text_sensor.template.publish:
                id: last_user
                state: "Steven's tag"
            - rtttl.play: "beep:d=16,o=5,b=100:b"
      - if:
          condition:
            - lambda: 'return id(keyCode).state == 3269822;' 
          then:  
            - switch.toggle: poortje
            - text_sensor.template.publish:
                id: last_user
                state: "other person tag"
            - rtttl.play: "beep:d=16,o=5,b=100:b"
      - if:
          condition:
            - lambda: 'return id(keyCode).state == 123654;' 
          then:  
            - switch.toggle: poortje
            - text_sensor.template.publish:
                id: last_user
                state: "code entered"
            - rtttl.play: "beep:d=16,o=5,b=100:b"
      - if:
          condition:
            - lambda: 'return id(keyCode).state != 3317755;' 
            - lambda: 'return id(keyCode).state != 123654;'
            - lambda: 'return id(keyCode).state != 3269822;'
          then:
            - text_sensor.template.publish:
                id: last_user
                state: "Unknown user"
            - rtttl.play: "beep:d=8,o=5,b=100:b"
  - platform: template
    name: "toets voordeur"
    id: keypress_voordeur
    on_value:
      - if:
          condition:
           - lambda: 'return id(keypress_voordeur).state == 11;' 
           #11 is the doorbel icon button
          then:  
            - homeassistant.service:
                service: button.press
                data: 
                  entity_id: button.all_knx_switches

# Define buzzer as output for RTTTL
rtttl:
  output: buzzer

    
output:
  - platform: esp32_dac
    pin: GPIO25
    id: dac_output
  - platform: ledc
    pin: GPIO18
    id: buzzer


text_sensor:
  - platform: template
    name: "pin voordeur"
    id: pinvoordeur
  - platform: template
    name: Last User
    id: last_user
    icon: mdi:clock-start    


wiegand:
  - id: mykeypad
    d0: 23  #green
    d1: 21  #white
    on_key:
      - lambda: ESP_LOGI("KEY", "received key %d", x);
      - sensor.template.publish:
          id: keypress_voordeur
          state: !lambda 'return x;'
          #state: !lambda 'return std::to_string(x);'

    on_tag:
      - lambda: ESP_LOGI("TAG", "received tag %s", x.c_str());
      - homeassistant.tag_scanned: !lambda 'return x.c_str();'
      - sensor.template.publish:
         id: keyCode
         state: !lambda "return parse_number<float>(x).value();"  

    on_raw:
      - lambda: ESP_LOGI("RAW", "received raw %d bits, value %llx", bits, value);

# Example configuration entry
key_collector:
  - id: pincode_reader
    source_id: mykeypad
    min_length: 6
    max_length: 6
    end_keys: "*"
    end_key_required: false
    back_keys: "B"
    clear_keys: "C"
    allowed_keys: "0123456789"
    timeout: 3s
    on_progress:
      - logger.log:
          format: "input progress: '%s', started by '%c'"
          args: [ 'x.c_str()', "(start == 0 ? '~' : start)" ]
    on_result:
      - logger.log:
          format: "input result: '%s', started by '%c', ended by '%c'"
          args: [ 'x.c_str()', "(start == 0 ? '~' : start)", "(end == 0 ? '~' : end)" ]
      - text_sensor.template.publish:
          id: pinvoordeur
          state: !lambda 'return x.c_str();'
      - sensor.template.publish:
          id: keyCode
          state: !lambda "return parse_number<float>(x).value();"  
      - delay: 3s
      - text_sensor.template.publish:
          id: pinvoordeur
          state: '000000'
#this 000000 after 3 secs is to be able to use state change in an automation in HA, it wouldn't trigger if the same code was entered twice otherwise

    on_timeout:
      - logger.log:
          format: "input timeout: '%s', started by '%c'"
          args: [ 'x.c_str()', "(start == 0 ? '~' : start)" ]



1 Like

That’s the idea I came to as well in the last couple of days. My problem is that this reader will be placed in an office (I know what you’re thinking, and it’s out of my hands), so I’ll need to add/remove tags somewhat regularly. Having them hardcoded into the esp’s firmware is less than ideal, but I couldn’t think of anything better honestly. I’m trying to create a global array that I can place the tags in to be processed by a for loop. But that I can probably figure out on my own.
Another issue that I found just now is that while the keypad is in wiegand mode, the doorbell won’t ring. I know it works in standalone mode. Do you know of any way to get that working directly on the access controller or will I need to add a relay to the esp board?

I’ve asked chatgpt for some advice, and it came back with some interesting improvement, I’ll see if I can find the time to implement.
First is to use a global variable to store all the allowed codes and tags and second is to have this list managed in home assistant and synced back to the the esphome device.

I skimmed through your ChatGPT convo. The fact that global variables aren’t persistent is kind of a big issue for me. If the building loses power and HA can’t recover afterwards, the office will be innaccessible. I think I’ll just stick to using the keypad’s standalone mode as the primary failsafe, at least for now. I do like the idea of syncing a list of valid tags between esphome and HA, I just have other features to worry about now.
As for the doorbell chime, unless I come across a better way to do it, I guess I’ll have to add a relay to the esp board and do it from there.

Thank you for all your help so far by the way!