HID Proxpro 5352 RFID Card Reader on ESPHome

Parts List:

Solder the D1 Mini parts together, being careful to get the orientation right. So it looks like this:

Instead of this:

Flash a base ESPHome image on the D1 Mini via USB and then remove the USB and plug in the 12V power supply via the pigtail. Test that it is showing up on the network and ESPHome can work with it.

Wire the RS232-TTL converter by hooking GND to the D1 Mini GND, VCC to the D1 Mini 5V, and OUT ARROW of the TTL side to the TX pin of D1 Mini and IN ARROW of the TTL side to the RX pin of the D1 Mini. The reason I reverse this is that supposedly the D1 Mini will spew some debug upon every boot out the TX pin. You don’t want anything connected to it reacting to this. I don’t think the ProxPro receives anything, but it doesn’t hurt to reverse these and set them up proper in ESPHome later.

When done, it should look something like this:


Now wire the male RS-232 pins to the ProxPro:

  • DB9 pin 2 DCE TXD to TB1 7 - RX-/RD
  • DB9 pin 3 DCE RXD to TB1 6 - RX+/TD
  • DB9 pin 5 to TB1 3 SIG GND
  • 12V+ to TB1 1 DC+
  • 12V- to TB1 2 GROUND

Everything hooked up:

Make sure the DIP switch settings on the ProxPro are for RS232 communications. The RS232-TTL converter will convert its signals down to what the D1 Mini understands.

While working on this, I originally tried to find a “custom component” for ESPHome that would send all serial communications to a TCP port. This stream server component (serial-over-wifi) does just that, and does it well. However, I decided that I wanted something integrated into Home Assistant, so I wrote my own custom component to grab the readings from the ProxPro and send them to a sensor.

Basic ESPhome config:

esphome:
  name: door_rfid
  platform: ESP8266
  board: d1_mini
  includes:
    - uartline.h

wifi:
  networks:
    - ssid: "My Wireless Network"
      password: "my-wireless-network-password"

logger:
  baud_rate: 0

api:

ota:
  
uart:
  id: uart_bus
  tx_pin: RX
  rx_pin: TX
  baud_rate: 9600

text_sensor:
- platform: custom
  lambda: |-
    auto rfid = new RFIDSensor(id(uart_bus));
    App.register_component(rfid);
    return {rfid};

  text_sensors:
    name: "Door RFID"

uartline.h:

#include "esphome.h"

class RFIDSensor : public PollingComponent,  public UARTDevice, public TextSensor {
 public:
  RFIDSensor(UARTComponent *parent) : PollingComponent(100), UARTDevice(parent) {}

  String line;
  String lastpublished = line;
  
  void setup() override {
    // nothing to do here
  }
  void loop() override {
    while (available() > 0) {
      line = readStringUntil('\n');
      line.trim();
      ESP_LOGD("custom", "RFID read %s", line.c_str());
      yield();
    }
  }

  void update() override {
    if (line != lastpublished) {
      publish_state(line.c_str());
      lastpublished = line;
    }
    else {
      if (lastpublished != "WAITING") {
        delay(1000);  
        publish_state("WAITING");
        line = "WAITING";
        lastpublished = line;
      }
    }
  }
};

I put the “WAITING” state in, because if someone needs to scan their card twice, the sensor won’t register the change unless there is something in between. The ProxPro has a pretty slow cycle, like 2 seconds, so I figured a one-second state was plenty for Home Assistant.

I run 12V directly from the green wire connector on the Power Shield to the Relay Shield of the D1 Mini. This is what I use to power/unpower the maglock on the door. The D1 Mini could also support a PIR motion sensor for unlocking the door when people come to one side of it, and/or environmental monitors.

This is an example of what the Home Assistant automation looks like:

- alias: RFID Multiple
  trigger:
    - platform: state
      entity_id: sensor.rfid
      to: '012345678901FF'
    - platform: state
      entity_id: sensor.rfid
      to: '012345678902FE''
  condition:
    - condition: state
      entity_id: switch.frontdoor_maglock_power
      state: 'on'
  action:
    - service: notify.notify
      data_template:
        message: >
          {% if trigger.to_state.state == '012345678901FF' %}
             Pete opened Front Door
          {% elif trigger.to_state.state == '012345678902FE' %}
             Bob Dobbs opened Front Door
          {% endif %}
    - service: switch.turn_off
      entity_id:
        - switch.frontdoor_maglock_power
    - delay: "00:00:08"
    - service: switch.turn_on
      entity_id:
        - switch.frontdoor_maglock_power

This automation gets unwieldy if you have hundreds of cards to read. I welcome suggestions as to how to convert this type of thing to an external db or other simplification.

To make it slightly less unwieldy, you shouldn’t need to specify each trigger to state as long as you have an else condition in your actions. e.g.

  trigger:
    - platform: state
      entity_id: sensor.rfid # triggers on any change of state / attribute change
  action:
    - service: notify.notify
      data_template:
        message: >
          {% if trigger.to_state.state == '012345678901FF' %}
             Pete opened Front Door
          {% elif trigger.to_state.state == '012345678902FE' %}
             Bob Dobbs opened Front Door
          {% else %}
             Unknown card 
          {% endif %}
1 Like

Unfortunately, I found that this has the unintended side-effect of allowing any RFID card to unlock the door. You need to be specific on the trigger.

Yeah ok, that’s bad. You could add a condition after the message action if you wanted to log unknown cards trying to access, but not allow them access (which could be handy for security, debugging and setup). Otherwise your way is just as easy, but wont log unknown cards.

  trigger:
    - platform: state
      entity_id: sensor.rfid # triggers on any change of state / attribute change
  action:
    - service: notify.notify
      data_template:
        message: >
          {% if trigger.to_state.state == '012345678901FF' %}
             Pete opened Front Door
          {% elif trigger.to_state.state == '012345678902FE' %}
             Bob Dobbs opened Front Door
          {% else %}
             Unknown card - {{ trigger.to_state.state }}
          {% endif %}
    - condition: template
      value_template: {{ trigger.to_state.state in ['012345678901FF', '012345678902FE'] }}
    - service: switch.turn_off
      entity_id:
        - switch.frontdoor_maglock_power
    - delay: "00:00:08"
    - service: switch.turn_on
      entity_id:
        - switch.frontdoor_maglock_power

Thanks for that. Do you know if the upcoming “tag” function will simplify any of this?

I’m not really sure what the tag function use case is. It’s just a web link encoded in a QR code from what I’ve seen.