ESPHome RFID Tag Reader - not seeing the tag in HA

I am trying to get an RFID reader working. I finally overcame the issues with just getting the reader to give me data, now I need to get the data into HA.

I am using this reader (26-bit wiegand output), and I am using a D1 Mini ESP8266.

I initially was using the D0 and D1 pins on the mini (corresponding to GPIO 16 and GPIO 5), but was getting very wrong data. Every tag was either 7FF or 7FFF, no matter the actual contents, and bit counts of 11 or 15 for the raw data. Found another setup that used D1 and D2 - GPIO 4 and GPIO 5. After getting the Wiegand D0 and D1 lines in the right order, I was finally able to get good 26-bit reads from the tags.

I set up the permission in HA for the ESP to perform actions, (as indicated here), and have the Wiegand setup in the ESP to handle the on_tag event set to send the tag to HA. Here is the relevant YAML:

wiegand:
  - id: ShopDoor
    d0: GPIO5
    d1: GPIO4
    on_tag:
      - lambda: ESP_LOGI("TAG", "received tag %s", x.c_str());
      - homeassistant.tag_scanned: !lambda 'return x.c_str();'

This came from here, with the action from here. The !lambda in the action came from somewhere as well, but I cannot recall where.

I know the tags are being scanned. If I go into ESPHome in HA and look at the logs for the device, I can see the tag reads:

[19:22:45.926][D][wiegand:075]: received 26-bit tag: 13131893
[19:22:45.930][I][TAG:043]: received tag 13131893

I do not, however see anything in the tags in Home Assistant. I have not found anything in multiple searches that is both current and tells me what else I might need to do in HA to get the tags read. The closest I came is an almost 6 year old blog post that indicates tags should be automatically populated. I also tried creating an entry for a specific tag id in the tags configuration in HA, however it never showed as read - even when I was able to confirm it was read (see above).

I would be much appreciative if someone could point me in the right direction. My brain is about googled out trying to solve this one.

Thanks in advance!

Edited to add:
I do see what I believe to be the ESP connecting to the HA API, which is where I expect it would send the tag data:


[19:33:08.863][D][wiegand:075]: received 26-bit tag: 13131893
[19:33:08.863][I][TAG:043]: received tag 13131893
[19:33:09.420][D][api:220]: Accept 192.168.6.110
[19:33:11.990][D][api.connection:2409]: aioesphomeapi (192.168.6.110): connected
[19:33:11.990][D][api.connection:2409]: aioesphomeapi (192.168.6.110): disconnected

You've posted snippets of code gathered from diverse places. You've confirmed your hardware is working.
Do you have a plan, a strategy, a template that you will follow to get your code working? Will you use MQTT, ESPHome, what will you sense, what naming conventions will you use for your sensors? What conditions you will test for?
Post your whole yaml code and we will look for the missing bits to glue it together to work well.
While you are there, turn on VERY VERBOSE debugging and post your compile log as well.

Have you tried turning on ‘Allow the device to perform home assistant actions’? Found by clicking the the cogwheel next to the device on the ESPHome integration page.

Have you tried turning on ‘Allow the device to perform home assistant actions’? Found by clicking the the cogwheel next to the device on the ESPHome integration page.

This was in one of the examples/tutorials I found. It was something I had missed the first time around, but I addressed in troubleshooting and going back over the setup. I also restarted HA since making the change - there was another update that needed a restart. I verified the setting is still there.

Do you have a plan, a strategy, a template that you will follow to get your code working? Will you use MQTT, ESPHome, what will you sense, what naming conventions will you use for your sensors? What conditions you will test for?

No template, but there is a plan :slight_smile:

Currently, I just want to see the tag in HA. Once there, then I can further set up automations to handle the tag. The end game is to be able to open a lock and turn off an alert when a valid tag is scanned (valid based on the tag ID, which there can be 4 values presently). None of which is currently coded in HA.

There is no other code on the ESP other than the standard code to connect to my wireless network and the HA API. The entire purpose is for it to read a tag and send it to HA - the ESP has no other function, and is not intended to have any independent functions.

I cannot access my HA from here, I will have to pull the full code when I get home.

I'm glad you outlined your goalposts before starting to code.

You are possibly using intermixed code from two different types of communications technology, one being UART serial and the other SPI/I2C to talk from the RFID reader to the ESP chip.

RDM6300 NFC/RFID - ESPHome - Smart Home Made Simple may be the more appropriate one for you to work with, rather than the one found at PN532 NFC/RFID - ESPHome - Smart Home Made Simple - adapt your code for the actual devices, but the code flow should be the same.

The rest of the tutorial on the ESPHome documentation should provide the missing bits to achieve your goal.

Post the code and compile output anyway (correctly formatted for readability please), and we will see if it requires just a few tweaks or a major rehash.

The reader I am using uses a Wiegand interface. This uses 2 data lines - D0 and D1 along with a ground to transport the data. ESPHome does support the Wiegand interface. The YAML I posted above covers the Wiegand configuration for the ESP. Here is the full code, obfuscated as required of course.

esphome:
  name: workshopaccesscontrol
  friendly_name: WorkshopAccessControl

esp8266:
  board: esp12e

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: <redacted>
ota:
  - platform: esphome
    password: <redacted>

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  manual_ip: 
    static_ip: A.B.C.D
    subnet: A.B.C.D
    gateway: A.B.C.D
    dns1: A.B.C.D

# ShopDoor Reader
wiegand:
  - id: ShopDoor
    d0: GPIO5
    d1: GPIO4
    on_tag:
      - lambda: ESP_LOGI("TAG", "received tag %s", x.c_str());
      - homeassistant.tag_scanned: lambda 'return x.c_str();'

There is no I2C nor is there a binary output. The Wiegand code provides for an on_key, on_tag, and/or on_raw - on_key for keypad inputs (my device does not have a keypad), on_tag for a compatible tag read/decode (which applies to me) and on_raw which provide the bits exactly as sent from the device - no processing or decoding. The latter was immensely helpful in figuring out how to get the reader wired correctly. I now get a reliable 26-bit read, like I should. The log snippet I showed above is from the ESP device and shows a valid tag read and, what I presume, is a connection to the HA API following said read.

Here is the compile/install log, in all its glory:

Ensuring ESPHome 2026.4.5 is available...                                                                                                                                                                                                           
ESPHome 2026.4.5 ready.                                                                                                                                                                                                                             
Invoking: /data/esphome-versions/2026.4.5/bin/esphome run /data/esphome-versions/slots/1/workshopaccesscontrol/workshopaccesscontrol.yaml --no-logs --device A.B.C.D                                                                         
INFO ESPHome 2026.4.5                                                                                                                                                                                                                               
INFO Reading configuration /data/esphome-versions/slots/1/workshopaccesscontrol/workshopaccesscontrol.yaml...                                                                                                                                       
WARNING The minimum WiFi authentication mode (wifi -> min_auth_mode) is not set. This controls the weakest encryption your device will accept when connecting to WiFi. Currently defaults to WPA (less secure), but will change to WPA2 (more secure
) in 2026.6.0. WPA uses TKIP encryption which has known security vulnerabilities and should be avoided. WPA2 uses AES encryption which is significantly more secure. To silence this warning, explicitly set min_auth_mode under 'wifi:'. If your ro
uter supports WPA2 or WPA3, set 'min_auth_mode: WPA2'. If your router only supports WPA, set 'min_auth_mode: WPA'.                                                                                                                                  
INFO Generating C++ source...                                                                                                                                                                                                                       
INFO Compiling app... Build path: /data/esphome-versions/slots/1/workshopaccesscontrol/.esphome/build/workshopaccesscontrol                                                                                                                         
Processing workshopaccesscontrol (board: esp12e; framework: arduino; platform: platformio/[email protected])                                                                                                                                      
--------------------------------------------------------------------------------                                                                                                                                                                    
HARDWARE: ESP8266 80MHz, 80KB RAM, 4MB Flash                                                                                                                                                                                                        
ESPHome: Excluding Updater.cpp from build (using native OTA backend)                                                                                                                                                                                
ESPHome: Excluding core_esp8266_waveform_phase.cpp from build (waveform not required)                                                                                                                                                               
ESPHome: Excluding core_esp8266_waveform_pwm.cpp from build (waveform not required)                                                                                                                                                                 
Dependency Graph                                                                                                                                                                                                                                    
|-- ESP8266WiFi @ 1.0                                                                                                                                                                                                                               
|-- ESP8266mDNS @ 1.2                                                                                                                                                                                                                               
|-- noise-c @ 0.1.11                                                                                                                                                                                                                                
Compiling .pioenvs/workshopaccesscontrol/src/main.cpp.o                                                                                                                                                                                             
remove_scanf_float_flag([".pioenvs/workshopaccesscontrol/firmware.elf"], [".pioenvs/workshopaccesscontrol/src/esphome/components/api/api_buffer.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/api/api_connection.cpp.o", ".pioenvs/
workshopaccesscontrol/src/esphome/components/api/api_frame_helper.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/api/api_frame_helper_noise.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/api/api_overflow_buffer.c
pp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/api/api_pb2.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/api/api_pb2_service.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/api/api_server.cpp.o", "
.pioenvs/workshopaccesscontrol/src/esphome/components/api/list_entities.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/api/proto.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/api/subscribe_state.cpp.o", ".pioenv
s/workshopaccesscontrol/src/esphome/components/esp8266/core.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/esp8266/crash_handler.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/esp8266/gpio.cpp.o", ".pioenvs/works
hopaccesscontrol/src/esphome/components/esp8266/helpers.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/esp8266/preferences.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/esp8266/printf_stubs.cpp.o", ".pioenvs/wor
kshopaccesscontrol/src/esphome/components/esp8266/waveform_stubs.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/esphome/ota/ota_esphome.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/key_provider/key_provider.cpp
.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/logger/logger.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/logger/logger_esp8266.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/md5/md5.cpp.o", ".pioe
nvs/workshopaccesscontrol/src/esphome/components/mdns/mdns_component.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/mdns/mdns_esp8266.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/network/util.cpp.o", ".pioenvs/
workshopaccesscontrol/src/esphome/components/ota/ota_backend.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/ota/ota_backend_esp8266.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/ota/ota_backend_host.cpp.o", ".pi
oenvs/workshopaccesscontrol/src/esphome/components/safe_mode/safe_mode.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/sha256/sha256.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/socket/lwip_raw_tcp_impl.cpp.o", 
".pioenvs/workshopaccesscontrol/src/esphome/components/socket/socket.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/wiegand/wiegand.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/wifi/wifi_component.cpp.o", ".pio
envs/workshopaccesscontrol/src/esphome/components/wifi/wifi_component_esp8266.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/core/application.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/core/color.cpp.o", ".pioenvs/workshopaccessco
ntrol/src/esphome/core/component.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/core/component_iterator.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/core/controller_registry.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/core/e
ntity_base.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/core/gpio.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/core/helpers.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/core/log.cpp.o", ".pioenvs/workshopaccesscontrol/src/e
sphome/core/scheduler.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/core/time.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/core/time_64.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/core/util.cpp.o", ".pioenvs/workshopaccessc
ontrol/src/esphome/core/wake.cpp.o", ".pioenvs/workshopaccesscontrol/src/main.cpp.o"])                                                                                                                                                              
ESPHome: Removing _scanf_float (saves ~8KB flash)                                                                                                                                                                                                   
Linking .pioenvs/workshopaccesscontrol/firmware.elf                                                                                                                                                                                                 
RAM:   [====      ]  37.9% (used 31036 bytes from 81920 bytes)                                                                                                                                                                                      
Flash: [====      ]  40.3% (used 420575 bytes from 1044464 bytes)                                                                                                                                                                                   
Building .pioenvs/workshopaccesscontrol/firmware.bin                                                                                                                                                                                                
esp8266_copy_factory_bin([".pioenvs/workshopaccesscontrol/firmware.bin"], [".pioenvs/workshopaccesscontrol/firmware.elf"])                                                                                                                          
esp8266_copy_ota_bin([".pioenvs/workshopaccesscontrol/firmware.bin"], [".pioenvs/workshopaccesscontrol/firmware.elf"])                                                                                                                              
========================= [SUCCESS] Took 5.29 seconds =========================                                                                                                                                                                     
INFO Build Info: config_hash=0xe2dd9207 build_time_str=2026-05-09 19:31:47 -0400                                                                                                                                                                    
INFO Successfully compiled program.                                                                                                                                                                                                                 
INFO Connecting to A.B.C.D port 8266...                                                                                                                                                                                                       
INFO Connected to A.B.C.D                                                                                                                                                                                                                     
INFO Uploading /data/esphome-versions/slots/1/workshopaccesscontrol/.esphome/build/workshopaccesscontrol/.pioenvs/workshopaccesscontrol/firmware.bin (424720 bytes)                                                                                 
INFO Compressed to 304385 bytes                                                                                                                                                                                                                     
Uploading: [============================================================] 100% Done...                                                                                                                                                              
                                                                                                                                                                                                                                                    
INFO Upload took 1.78 seconds, waiting for result...                                                                                                                                                                                                
INFO OTA successful                                                                                                                                                                                                                                 
INFO Successfully uploaded program.                                                                                                                                                                                                                 

The big block in the middle is exactly what the compiler spit out - not sure where the breaks should be there to make it more readable.

After the compile/install (to get the above log), I monitored the log while scanning 3 different fobs. The log showed this:


[17:41:21.007][D][wiegand:075]: received 26-bit tag: 13235842
[17:41:21.007][I][TAG:036]: received tag 13235842
[17:41:37.430][D][wiegand:075]: received 26-bit tag: 13131891
[17:41:37.440][I][TAG:036]: received tag 13131891
[17:41:43.665][D][wiegand:075]: received 26-bit tag: 13131893
[17:41:43.665][I][TAG:036]: received tag 13131893
[17:41:44.756][D][wiegand:075]: received 26-bit tag: 13131893
[17:41:44.756][I][TAG:036]: received tag 13131893
[17:42:53.955][D][api:220]: Accept 192.168.6.110
[17:42:56.058][D][api.connection:2409]: aioesphomeapi (192.168.6.110): connected
[17:42:56.059][D][api.connection:2409]: aioesphomeapi (192.168.6.110): disconnected

Looking at those time stamps, there was over a minute from the time tag 3 was scanned until the API connection is shown - then 3 seconds from the Accept message to the connected message, and 1mS from connected to disconnected.

The reader I am using is for 125kHz RFID access tags. It takes a 12V power supply, and provides 5V TTL logic outputs for the D0 and D1 lines. Given the data I see in the log for the 'received tag' message and the data encoded in the tag I scanned matching, I feel fairly safe in assuming the reading of the tag is working as it should. Sending that data to HA, however, does not appear to be working.

The RDM6300 link is where I got the output format from for sending the tag data to HA, modified to fit the data returned by my specific reader. Specifically the second entry under this section.

rdm6300:
  # ...
  on_tag:
    then:
      - homeassistant.tag_scanned: !lambda 'return to_string(x);'

The UART settings do not apply, as this does not use a UART. The on_tag format I used is from the Wiegand instructions, I just added the tag_scanned action from the rdm6300 page.

The example from the ESPHome Wiegand page:


# Example configuration entry
wiegand:
  - id: mykeypad
    d0: GPIOXX
    d1: GPIOXX
    on_key:
      - lambda: ESP_LOGI("KEY", "received key %d", x);
    on_tag:
      - lambda: ESP_LOGI("TAG", "received tag %s", x.c_str());
    on_raw:
      - lambda: ESP_LOGI("RAW", "received raw %d bits, value %llx", bits, value);

I dropped the key and raw sections, as they do not apply.

I am sure I am missing something simple here - which is usually the case when I get this far in. Been working on this for over a week now, so it is entirely possible (likely even!) I am now too close to see errors that a fresh set of eyes might catch.

This is the hardware setup:


The reader is the silver component on the left. The red/black wires on the reader (and going off to the right) are the 12V reader power. The white and green from the reader are the Wiegand D0 and D1, the brown and yellow from the reader are supposed to be for LED and buzzer, neither of which I plan to use. They are stuck in the breadboard to keep them from flopping about. The USB cable on the ESP D1 mini is for power for the ESP. I have all grounds tied together (brown Dupont wire).

And, I may be getting somewhere. Not sure where, but somewhere.

After my post above, I noticed that I had the homeassistant.tag_scanned: different than what I used as a source, so I went back and looked at a few things.

The Wiegand component in ESPHome places the scanned tag value in x. This is seen in the lambda from the on_tag example on the ESPHome Wiegand page. In the tag_scanned section, I added the homeassistant.tag_scanned: part, but used a different return value - I used the same value as in the Wiegand tag_scanned example. (Note: The missing ! before the lambda is from something I tried in an effort to fix the problem. It failed.)

I modified the YAML to use the homeassistant.tag_scanned action exactly as found in the RDM6300 example. I now get compile errors, but am hoping that someone finds meaning in them (I do not exactly, someone much more familiar with ESPHome might)

The YAML (Starting with the Wiegand component):

# ShopDoor Reader
wiegand:
  - id: ShopDoor
    d0: GPIO5
    d1: GPIO4
    on_tag:
      - lambda: ESP_LOGI("TAG", "received tag %s", x.c_str());
      - homeassistant.tag_scanned: !lambda 'return to_string(x);'

And the log:

Ensuring ESPHome 2026.4.5 is available...
ESPHome 2026.4.5 ready.
Invoking: /data/esphome-versions/2026.4.5/bin/esphome run /data/esphome-versions/slots/1/workshopaccesscontrol/workshopaccesscontrol.yaml --no-logs --device 192.168.3.195
INFO ESPHome 2026.4.5
INFO Reading configuration /data/esphome-versions/slots/1/workshopaccesscontrol/workshopaccesscontrol.yaml...
WARNING The minimum WiFi authentication mode (wifi -> min_auth_mode) is not set. This controls the weakest encryption your device will accept when connecting to WiFi. Currently defaults to WPA (less secure), but will change to WPA2 (more secure) in 2026.6.0. WPA uses TKIP encryption which has known security vulnerabilities and should be avoided. WPA2 uses AES encryption which is significantly more secure. To silence this warning, explicitly set min_auth_mode under 'wifi:'. If your router supports WPA2 or WPA3, set 'min_auth_mode: WPA2'. If your router only supports WPA, set 'min_auth_mode: WPA'.
INFO Generating C++ source...
INFO Compiling app... Build path: /data/esphome-versions/slots/1/workshopaccesscontrol/.esphome/build/workshopaccesscontrol
Processing workshopaccesscontrol (board: esp12e; framework: arduino; platform: platformio/[email protected])
--------------------------------------------------------------------------------
HARDWARE: ESP8266 80MHz, 80KB RAM, 4MB Flash
ESPHome: Excluding Updater.cpp from build (using native OTA backend)
ESPHome: Excluding core_esp8266_waveform_phase.cpp from build (waveform not required)
ESPHome: Excluding core_esp8266_waveform_pwm.cpp from build (waveform not required)
Dependency Graph
|-- ESP8266WiFi @ 1.0
|-- ESP8266mDNS @ 1.2
|-- noise-c @ 0.1.11
Compiling .pioenvs/workshopaccesscontrol/src/esphome/core/application.cpp.o
Compiling .pioenvs/workshopaccesscontrol/src/main.cpp.o
/data/esphome-versions/slots/1/workshopaccesscontrol/workshopaccesscontrol.yaml: In lambda function:
/data/esphome-versions/slots/1/workshopaccesscontrol/workshopaccesscontrol.yaml:36:25: error: no matching function for call to 'to_string(std::string&)'
   36 |       - homeassistant.tag_scanned: !lambda 'return to_string(x);'
      |                         ^
In file included from /data/esphome-versions/pio-slot-1/packages/toolchain-xtensa/xtensa-lx106-elf/include/c++/10.3.0/string:55,
                 from /data/esphome-versions/pio-slot-1/packages/toolchain-xtensa/xtensa-lx106-elf/include/c++/10.3.0/bits/locale_classes.h:40,
                 from /data/esphome-versions/pio-slot-1/packages/toolchain-xtensa/xtensa-lx106-elf/include/c++/10.3.0/bits/ios_base.h:41,
                 from /data/esphome-versions/pio-slot-1/packages/toolchain-xtensa/xtensa-lx106-elf/include/c++/10.3.0/streambuf:41,
                 from /data/esphome-versions/pio-slot-1/packages/toolchain-xtensa/xtensa-lx106-elf/include/c++/10.3.0/bits/streambuf_iterator.h:35,
                 from /data/esphome-versions/pio-slot-1/packages/toolchain-xtensa/xtensa-lx106-elf/include/c++/10.3.0/iterator:66,
                 from /data/esphome-versions/pio-slot-1/packages/toolchain-xtensa/xtensa-lx106-elf/include/c++/10.3.0/bits/ranges_algobase.h:36,
                 from /data/esphome-versions/pio-slot-1/packages/toolchain-xtensa/xtensa-lx106-elf/include/c++/10.3.0/bits/ranges_algo.h:35,
                 from /data/esphome-versions/pio-slot-1/packages/toolchain-xtensa/xtensa-lx106-elf/include/c++/10.3.0/algorithm:64,
                 from /data/esphome-versions/pio-slot-1/packages/framework-arduinoespressif8266/cores/esp8266/Arduino.h:229,
                 from src/esphome/core/macros.h:7,
                 from src/esphome.h:2,
                 from src/main.cpp:3:
/data/esphome-versions/pio-slot-1/packages/toolchain-xtensa/xtensa-lx106-elf/include/c++/10.3.0/bits/basic_string.h:6678:3: note: candidate: 'std::string std::__cxx11::to_string(long double)'
 6678 |   to_string(long double __val)
      |   ^~~~~~~~~
/data/esphome-versions/pio-slot-1/packages/toolchain-xtensa/xtensa-lx106-elf/include/c++/10.3.0/bits/basic_string.h:6678:25: note:   no known conversion for argument 1 from 'std::string' {aka 'std::__cxx11::basic_string<char>'} to 'long double'
 6678 |   to_string(long double __val)
      |             ~~~~~~~~~~~~^~~~~
/data/esphome-versions/pio-slot-1/packages/toolchain-xtensa/xtensa-lx106-elf/include/c++/10.3.0/bits/basic_string.h:6669:3: note: candidate: 'std::string std::__cxx11::to_string(double)'
 6669 |   to_string(double __val)
      |   ^~~~~~~~~
/data/esphome-versions/pio-slot-1/packages/toolchain-xtensa/xtensa-lx106-elf/include/c++/10.3.0/bits/basic_string.h:6669:20: note:   no known conversion for argument 1 from 'std::string' {aka 'std::__cxx11::basic_string<char>'} to 'double'
 6669 |   to_string(double __val)
      |             ~~~~~~~^~~~~
/data/esphome-versions/pio-slot-1/packages/toolchain-xtensa/xtensa-lx106-elf/include/c++/10.3.0/bits/basic_string.h:6660:3: note: candidate: 'std::string std::__cxx11::to_string(float)'
 6660 |   to_string(float __val)
      |   ^~~~~~~~~
/data/esphome-versions/pio-slot-1/packages/toolchain-xtensa/xtensa-lx106-elf/include/c++/10.3.0/bits/basic_string.h:6660:19: note:   no known conversion for argument 1 from 'std::string' {aka 'std::__cxx11::basic_string<char>'} to 'float'
 6660 |   to_string(float __val)
      |             ~~~~~~^~~~~
/data/esphome-versions/pio-slot-1/packages/toolchain-xtensa/xtensa-lx106-elf/include/c++/10.3.0/bits/basic_string.h:6649:3: note: candidate: 'std::string std::__cxx11::to_string(long long unsigned int)'
 6649 |   to_string(unsigned long long __val)
      |   ^~~~~~~~~
/data/esphome-versions/pio-slot-1/packages/toolchain-xtensa/xtensa-lx106-elf/include/c++/10.3.0/bits/basic_string.h:6649:32: note:   no known conversion for argument 1 from 'std::string' {aka 'std::__cxx11::basic_string<char>'} to 'long long unsigned int'
 6649 |   to_string(unsigned long long __val)
      |             ~~~~~~~~~~~~~~~~~~~^~~~~
/data/esphome-versions/pio-slot-1/packages/toolchain-xtensa/xtensa-lx106-elf/include/c++/10.3.0/bits/basic_string.h:6637:3: note: candidate: 'std::string std::__cxx11::to_string(long long int)'
 6637 |   to_string(long long __val)
      |   ^~~~~~~~~
/data/esphome-versions/pio-slot-1/packages/toolchain-xtensa/xtensa-lx106-elf/include/c++/10.3.0/bits/basic_string.h:6637:23: note:   no known conversion for argument 1 from 'std::string' {aka 'std::__cxx11::basic_string<char>'} to 'long long int'
 6637 |   to_string(long long __val)
      |             ~~~~~~~~~~^~~~~
/data/esphome-versions/pio-slot-1/packages/toolchain-xtensa/xtensa-lx106-elf/include/c++/10.3.0/bits/basic_string.h:6629:3: note: candidate: 'std::string std::__cxx11::to_string(long unsigned int)'
 6629 |   to_string(unsigned long __val)
      |   ^~~~~~~~~
/data/esphome-versions/pio-slot-1/packages/toolchain-xtensa/xtensa-lx106-elf/include/c++/10.3.0/bits/basic_string.h:6629:27: note:   no known conversion for argument 1 from 'std::string' {aka 'std::__cxx11::basic_string<char>'} to 'long unsigned int'
 6629 |   to_string(unsigned long __val)
      |             ~~~~~~~~~~~~~~^~~~~
/data/esphome-versions/pio-slot-1/packages/toolchain-xtensa/xtensa-lx106-elf/include/c++/10.3.0/bits/basic_string.h:6618:3: note: candidate: 'std::string std::__cxx11::to_string(long int)'
 6618 |   to_string(long __val)
      |   ^~~~~~~~~
/data/esphome-versions/pio-slot-1/packages/toolchain-xtensa/xtensa-lx106-elf/include/c++/10.3.0/bits/basic_string.h:6618:18: note:   no known conversion for argument 1 from 'std::string' {aka 'std::__cxx11::basic_string<char>'} to 'long int'
 6618 |   to_string(long __val)
      |             ~~~~~^~~~~
/data/esphome-versions/pio-slot-1/packages/toolchain-xtensa/xtensa-lx106-elf/include/c++/10.3.0/bits/basic_string.h:6610:3: note: candidate: 'std::string std::__cxx11::to_string(unsigned int)'
 6610 |   to_string(unsigned __val)
      |   ^~~~~~~~~
/data/esphome-versions/pio-slot-1/packages/toolchain-xtensa/xtensa-lx106-elf/include/c++/10.3.0/bits/basic_string.h:6610:22: note:   no known conversion for argument 1 from 'std::string' {aka 'std::__cxx11::basic_string<char>'} to 'unsigned int'
 6610 |   to_string(unsigned __val)
      |             ~~~~~~~~~^~~~~
/data/esphome-versions/pio-slot-1/packages/toolchain-xtensa/xtensa-lx106-elf/include/c++/10.3.0/bits/basic_string.h:6599:3: note: candidate: 'std::string std::__cxx11::to_string(int)'
 6599 |   to_string(int __val)
      |   ^~~~~~~~~
/data/esphome-versions/pio-slot-1/packages/toolchain-xtensa/xtensa-lx106-elf/include/c++/10.3.0/bits/basic_string.h:6599:17: note:   no known conversion for argument 1 from 'std::string' {aka 'std::__cxx11::basic_string<char>'} to 'int'
 6599 |   to_string(int __val)
      |             ~~~~^~~~~
*** [.pioenvs/workshopaccesscontrol/src/main.cpp.o] Error 1
========================== [FAILED] Took 2.93 seconds ==========================

If I am reading this correctly, it has heartburn with the 'to_string()' function.

Open to ideas here. Thanks for the help thus far...

Ok, now it compiles again but seems to be back where I started - not seeing the tag in HA.

# ShopDoor Reader
wiegand:
  - id: ShopDoor
    d0: GPIO5
    d1: GPIO4
    on_tag:
      - lambda: ESP_LOGI("TAG", "received tag %s", x.c_str());
      - homeassistant.tag_scanned: !lambda 'return x;'

And the logs

Ensuring ESPHome 2026.4.5 is available...                                                                                                                                                                                                           
ESPHome 2026.4.5 ready.                                                                                                                                                                                                                             
Invoking: /data/esphome-versions/2026.4.5/bin/esphome run /data/esphome-versions/slots/1/workshopaccesscontrol/workshopaccesscontrol.yaml --no-logs --device 192.168.3.195                                                                          
INFO ESPHome 2026.4.5                                                                                                                                                                                                                               
INFO Reading configuration /data/esphome-versions/slots/1/workshopaccesscontrol/workshopaccesscontrol.yaml...                                                                                                                                       
WARNING The minimum WiFi authentication mode (wifi -> min_auth_mode) is not set. This controls the weakest encryption your device will accept when connecting to WiFi. Currently defaults to WPA (less secure), but will change to WPA2 (more secure
) in 2026.6.0. WPA uses TKIP encryption which has known security vulnerabilities and should be avoided. WPA2 uses AES encryption which is significantly more secure. To silence this warning, explicitly set min_auth_mode under 'wifi:'. If your ro
uter supports WPA2 or WPA3, set 'min_auth_mode: WPA2'. If your router only supports WPA, set 'min_auth_mode: WPA'.                                                                                                                                  
INFO Generating C++ source...                                                                                                                                                                                                                       
INFO Compiling app... Build path: /data/esphome-versions/slots/1/workshopaccesscontrol/.esphome/build/workshopaccesscontrol                                                                                                                         
Processing workshopaccesscontrol (board: esp12e; framework: arduino; platform: platformio/[email protected])                                                                                                                                      
--------------------------------------------------------------------------------                                                                                                                                                                    
HARDWARE: ESP8266 80MHz, 80KB RAM, 4MB Flash                                                                                                                                                                                                        
ESPHome: Excluding Updater.cpp from build (using native OTA backend)                                                                                                                                                                                
ESPHome: Excluding core_esp8266_waveform_phase.cpp from build (waveform not required)                                                                                                                                                               
ESPHome: Excluding core_esp8266_waveform_pwm.cpp from build (waveform not required)                                                                                                                                                                 
Dependency Graph                                                                                                                                                                                                                                    
|-- ESP8266WiFi @ 1.0                                                                                                                                                                                                                               
|-- ESP8266mDNS @ 1.2                                                                                                                                                                                                                               
|-- noise-c @ 0.1.11                                                                                                                                                                                                                                
Compiling .pioenvs/workshopaccesscontrol/src/esphome/core/application.cpp.o                                                                                                                                                                         
Compiling .pioenvs/workshopaccesscontrol/src/main.cpp.o                                                                                                                                                                                             
remove_scanf_float_flag([".pioenvs/workshopaccesscontrol/firmware.elf"], [".pioenvs/workshopaccesscontrol/src/esphome/components/api/api_buffer.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/api/api_connection.cpp.o", ".pioenvs/
workshopaccesscontrol/src/esphome/components/api/api_frame_helper.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/api/api_frame_helper_noise.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/api/api_overflow_buffer.c
pp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/api/api_pb2.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/api/api_pb2_service.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/api/api_server.cpp.o", "
.pioenvs/workshopaccesscontrol/src/esphome/components/api/list_entities.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/api/proto.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/api/subscribe_state.cpp.o", ".pioenv
s/workshopaccesscontrol/src/esphome/components/esp8266/core.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/esp8266/crash_handler.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/esp8266/gpio.cpp.o", ".pioenvs/works
hopaccesscontrol/src/esphome/components/esp8266/helpers.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/esp8266/preferences.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/esp8266/printf_stubs.cpp.o", ".pioenvs/wor
kshopaccesscontrol/src/esphome/components/esp8266/waveform_stubs.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/esphome/ota/ota_esphome.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/key_provider/key_provider.cpp
.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/logger/logger.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/logger/logger_esp8266.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/md5/md5.cpp.o", ".pioe
nvs/workshopaccesscontrol/src/esphome/components/mdns/mdns_component.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/mdns/mdns_esp8266.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/network/util.cpp.o", ".pioenvs/
workshopaccesscontrol/src/esphome/components/ota/ota_backend.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/ota/ota_backend_esp8266.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/ota/ota_backend_host.cpp.o", ".pi
oenvs/workshopaccesscontrol/src/esphome/components/safe_mode/safe_mode.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/sha256/sha256.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/socket/lwip_raw_tcp_impl.cpp.o", 
".pioenvs/workshopaccesscontrol/src/esphome/components/socket/socket.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/wiegand/wiegand.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/components/wifi/wifi_component.cpp.o", ".pio
envs/workshopaccesscontrol/src/esphome/components/wifi/wifi_component_esp8266.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/core/application.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/core/color.cpp.o", ".pioenvs/workshopaccessco
ntrol/src/esphome/core/component.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/core/component_iterator.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/core/controller_registry.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/core/e
ntity_base.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/core/gpio.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/core/helpers.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/core/log.cpp.o", ".pioenvs/workshopaccesscontrol/src/e
sphome/core/scheduler.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/core/time.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/core/time_64.cpp.o", ".pioenvs/workshopaccesscontrol/src/esphome/core/util.cpp.o", ".pioenvs/workshopaccessc
ontrol/src/esphome/core/wake.cpp.o", ".pioenvs/workshopaccesscontrol/src/main.cpp.o"])                                                                                                                                                              
ESPHome: Removing _scanf_float (saves ~8KB flash)                                                                                                                                                                                                   
Linking .pioenvs/workshopaccesscontrol/firmware.elf                                                                                                                                                                                                 
RAM:   [====      ]  37.9% (used 31036 bytes from 81920 bytes)                                                                                                                                                                                      
Flash: [====      ]  40.3% (used 420659 bytes from 1044464 bytes)                                                                                                                                                                                   
Building .pioenvs/workshopaccesscontrol/firmware.bin                                                                                                                                                                                                
esp8266_copy_factory_bin([".pioenvs/workshopaccesscontrol/firmware.bin"], [".pioenvs/workshopaccesscontrol/firmware.elf"])                                                                                                                          
esp8266_copy_ota_bin([".pioenvs/workshopaccesscontrol/firmware.bin"], [".pioenvs/workshopaccesscontrol/firmware.elf"])                                                                                                                              
========================= [SUCCESS] Took 5.14 seconds =========================                                                                                                                                                                     
INFO Build Info: config_hash=0x9b20776b build_time_str=2026-05-11 18:29:49 -0400                                                                                                                                                                    
INFO Successfully compiled program.                                                                                                                                                                                                                 
INFO Connecting to 192.168.3.195 port 8266...                                                                                                                                                                                                       
INFO Connected to 192.168.3.195                                                                                                                                                                                                                     
INFO Uploading /data/esphome-versions/slots/1/workshopaccesscontrol/.esphome/build/workshopaccesscontrol/.pioenvs/workshopaccesscontrol/firmware.bin (424816 bytes)                                                                                 
INFO Compressed to 304409 bytes                                                                                                                                                                                                                     
Uploading: [============================================================] 100% Done...                                                                                                                                                              
                                                                                                                                                                                                                                                    
INFO Upload took 1.69 seconds, waiting for result...                                                                                                                                                                                                
INFO OTA successful                                                                                                                                                                                                                                 
INFO Successfully uploaded program.

I see the read:

[18:34:11.989][D][wiegand:075]: received 26-bit tag: 13131891
[18:34:11.989][I][TAG:036]: received tag 13131891

Still nothing appearing in HA, though.

Doing some more digging into this. I went back to the Home Assistant ESPHome integration page to see if there is something I missed - particularly with the ESPHome -> Home Assistant communications and came across this tidbit under the 'Updating Data' section:

Rather than polling for sensor values or device states, Home Assistant maintains a persistent connection to each ESPHome device using the native API. This allows state changes—such as a temperature sensor update, a button press, or a binary sensor trigger—to be sent immediately as they happen, reducing latency and improving responsiveness in automations.

(emphasis added)
Below that, in the 'Additional Technical Details' section, I see this:

Efficient Communication Protocol: ESPHome uses a lightweight, bi-directional protocol over TCP, optimized for microcontrollers. This protocol is implemented in aioesphomeapi, the async Python library used by Home Assistant to handle real-time communication with ESPHome devices. It enables low-latency updates and near instant command execution.

(again, emphasis added)

I go to the linked aioesphomeapi Github page, and see that there is a CLI tool available for watching logs:
aioesphomeapi-logs --help
This could be helpful - see what logging I can see when attempting to scan a tag and have it appear in HA. One slight problem - aioesphomeapi does not seem to exist.

[core-ssh ~]$ aioesphomeapi-logs --help
bash: aioesphomeapi-logs: command not found
[core-ssh ~]$

This is a bit of a problem. What happens if I try to install it?

[core-ssh ~]$ pip3 install aioesphomeapi
bash: pip3: command not found
[core-ssh ~]$

Huh? Is Python even installed?

The answer appears to be - no, it is not. At least, not anywhere I can find it.

[core-ssh ~]$ which python3
[core-ssh ~]$ ls -al /usr/bin/ | grep -i pyth
[core-ssh ~]$

This brings up another question - how is it I am seeing sensor data from two other ESPHome devices if they use the aioesphomeapi to communicate, as indicated by the Home Assistant ESPHome page?

What about the API connect/disconnect messages I am seeing in the logs for this ESPHome device? Where are they coming from?
As a reference, here is the ESPHome YAML from another device, from which I DO see data:

esphome:
  name: compressor_monitor
  friendly_name: CompressorMonitor
  min_version: 2025.11.0
  name_add_mac_suffix: false
  area:
    id: workshop
    name: "Workshop"
    
esp32:
  variant: esp32
  framework:
    type: esp-idf

# Enable logging
logger:

# Enable Home Assistant API
api:

# Allow Over-The-Air updates
ota:
- platform: esphome

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  power_save_mode: none
  manual_ip: 
    static_ip: X.X.X.X
    subnet: X.X.X.X
    gateway: X.X.X.X
    dns1: X.X.X.X

sensor:
# Signal Strength
  - platform: wifi_signal
    name: "Wireless Signal Strength"
    id: wifi_signal_db
    update_interval: 5s
    filters:
      - median:
          window_size: 60
          send_every: 5
          send_first_at: 1

# Air Pressure
  - platform: adc
    pin: 36
    name: "Air Pressure"
    unit_of_measurement: PSI
    update_interval: 1s
    filters:
      - median: 
          window_size: 60
          send_every: 5
          send_first_at: 1
#      - calibrate_polynomial:
#          degree: 2
#          datapoints:
#            - 0.219 -> 0.0 
#            - 0.413 -> 10.0
#            - 0.524 -> 15.0
#            - 0.599 -> 20.0        
            
# Motor Temperature
  - platform: adc
    pin: 39
    name: "Compressor Motor"
    unit_of_measurement: "°C"
    update_interval: 5s
    filters:
      - median:
          window_size: 60
          send_every: 5
          send_first_at: 1
      - calibrate_polynomial:
          degree: 2
          datapoints:
            - 0.226 -> -1 
            - 0.254 -> 12
            - 0.284 -> 18
            - 0.3385 -> 31
            - 0.365 -> 39     

# Compressor Temperature
  - platform: adc
    pin: 34
    name: "Compressor"
    unit_of_measurement: "°C"
    update_interval: 5s
    filters:
      - median:
          window_size: 60
          send_every: 5
          send_first_at: 1
      - calibrate_polynomial:
          degree: 2
          datapoints:
            - 0.216 -> -1 
            - 0.250 -> 12
            - 0.280 -> 18
            - 0.3345 -> 31
            - 0.365 -> 39

There are some configuration differences, of course - plus this is not the same ESP module as the D1 mini has.

So I managed to get this working by using the nuclear option.

I started over from scratch (mostly). I ditched the mini and went with an ESP32 dev board. Worked on the first try. Must be something with the mini and the underlying code. The only change was adding a level shifter between the reader outputs and the ESP inputs. A hardware change that should have had no effect on the ESP -> HA data transfer (recall, I was seeing the tag reads in the ESP logs - so the reader to ESP part was working fine).

I will be posting a full build in the future. In the meantime, here is the final (as it gets) YAML. I did add some additional I/O - since I had it available (after getting the tags into HA). There are two status LEDs, I am controlling the reader LED, and I am adding a local manual lock/unlock trigger (inside, not outside - would sort of defeat the purpose of needing a correct tag). That is all reflected in the YAML.

sphome:
  name: workshopaccesscontrol
  friendly_name: Workshop_Access_Control

esp32:
  board: esp32dev
  framework:
    type: esp-idf

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: <redacted>

ota:
  - platform: esphome
    password: <redacted>

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  power_save_mode: none
  manual_ip: 
    static_ip: 192.168.3.194
    subnet: 255.255.255.0
    gateway: 192.168.3.1
    dns1: 192.168.1.251

# ShopDoor Reader
wiegand:
  - id: ShopDoor
    d0: GPIO18
    d1: GPIO19
    on_tag:
      - lambda: ESP_LOGI("TAG", "received tag %s", x.c_str());
      - homeassistant.tag_scanned: !lambda 'return x;'

switch:
# Reader LED
  - platform: gpio
    pin: 
      number: GPIO21
    restore_mode: ALWAYS_OFF
    id: ReaderLED
    name: "Tag Reader LED"

# Lock Status Locked
  - platform: gpio
    pin: 
      number: GPIO22
    restore_mode: ALWAYS_ON
    id: DeadboltLocked
    name: "Deadbolt Locked LED"
    
# Lock Status Unlocked
  - platform: gpio
    pin: 
      number: GPIO23
    restore_mode: ALWAYS_OFF
    id: DeadboltUnlocked
    name: "Deadbolt Unlocked LED"

binary_sensor:
# Manual Lock/Unlock
  - platform: gpio
    pin:
      number: GPIO13
      mode:
        input: True
        pullup: True
    id: ManualLockControl
    name: "Manual Lock Control"
    filters: 
      - delayed_on: 10ms

This should mostly be self explanatory. The delayed_on for the manual input is a debounce for the button. It may or may not need to change. I will determine that once I have the actual button connected instead of a wire I touch to ground to test.