Fonkan FM-507 (FM-503, etc) - UHF RFID Module

Am in the process of setting up an RFID access door for my pets. Picked up one of these of AliExpress for about $50. I thought I had seen someone who had made it work, but it turns out that was for a different board. So I whipped up this piece of garbage and figured I would share it for anyone who was looking for it. It’s reliably reading tags from about 10-20 inches for me.

The module has the antenna and a built in serial connection that can interface with an ESP32 via UART. Commands and responses can be found HERE

The code below does a few things as proof of concept more than anything usable just yet. It will read the firmware and serial of the antenna and is setup to be able to send debug commands from the web interface. There is a very rudimentary polling setup, but I haven’t put it into production so it needs refinement.

Hope it helps someone!

esphome:
  name: rfid-sensor
  friendly_name: rfid-sensor

# Get initial information from RFID board

  on_boot:
    - priority: -100
      then:
        - delay: 3s
        - uart.write: "N0,00\r\n"
        - delay: 1s            
        - uart.write: "V\r\n"
        - delay: 1s            
        - uart.write: "S\r\n"

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:
  baud_rate: 115200

# Enable Home Assistant API
api:
  encryption:
    key:

ota:
  - platform: esphome
    password: 

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

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

captive_portal:

web_server:

# Set Allowed RFID Tags Here

globals:
    - id: tags
      type: std::vector<std::string>
      restore_value: no
      initial_value: '{"3000E28011606000020786EDD9E6D3E0",
                       "3000E28011606000020786EE223B5F5B", 
                       "3000E28011606000020786EDF0ECCED4", 
                       "3000E28011606000020786EE0BE8183B",
                       "3000E28011606000020786EDE0853028"}'

# Setup RFID Polling

interval: 
  - interval: .25s
    then:
      - if: 
          condition: 
            - switch.is_on: rfid_poll_enable
          then: 
            - uart.write: "Q\r\n"

# Configure UART Debugging Sensor

uart:
  baud_rate: 38400
  tx_pin: 17
  rx_pin: 16
  id: UART3
  debug:
    direction: BOTH
    dummy_receiver: true
    after:
      delimiter: "\r\n"
    sequence:
      - lambda: |-
          UARTDebug::log_string(direction, bytes);  //Still log the data
          std::string str(bytes.begin(), bytes.end());
          str.erase(std::remove(str.begin(), str.end(), '\n'), str.cend());
          str.erase(std::remove(str.begin(), str.end(), '\r'), str.cend());		  
          if (direction == UART_DIRECTION_TX) //Store the Last Sent Command To Interpret Results
            {
              id(uart_tx_command).publish_state(str.c_str());
            }
          else // Process Results
            {
              std::string prefix_code = str.substr(0, 1);
              str = str.erase(0,1);
              if (prefix_code[0] == 'Q') 
                { 
                  bool found = std::find(id(tags).begin(), id(tags).end(), str) != id(tags).end();
                  if (found) 
                    {
                      id(rfid_access_granted).publish_state(true);
                      id(rfid_last_access).publish_state(str.c_str());
                    }
                }
              else if (prefix_code[0] == 'V')  // Major Minor Version
                {
                  id(info_firmware).publish_state(str.c_str());
                }
              else if (prefix_code[0] == 'S') // Reader ID
                { 
                  id(info_receiver_id).publish_state(str.c_str());
                } 
              else if (prefix_code[0] == 'N')
                {
                  if (id(uart_tx_command).state == "N0,00")
                    {
                      int decimalValue = ((std::stoi(str, nullptr, 16)) - 2);
                      id(info_receiver_gain).publish_state(decimalValue);            
                    } 
                  else
                    {
                      id(uart_rx_response).publish_state(str.c_str());
                    }
                }
              else 
                {          
                  if (str.length() > 1) 
                    {
                      id(uart_rx_response).publish_state(str.c_str());
                    }
                }
            }

text_sensor:
  - platform: template
    id: "info_firmware"
    name: "info_firmware"
  - platform: template
    id: "info_receiver_id"    
    name: "info_receiver_id"
  - platform: template
    id: "rfid_last_access"
    name: "rfid_last_access"
  - platform: template
    id: "uart_rx_response"
    name: "uart_rx_response"
  - platform: template
    id: "uart_tx_command"
    name: "uart_tx_command"


text:
  - platform: template
    name: "uart_tx_debug"
    id: "uart_tx_debug"
    mode: text
    optimistic: true
    on_value:
      then:
        - uart.write: 
            data: !lambda |-
              std::string str = ("\n" + id(uart_tx_debug).state + "\r\n");
              std::vector<uint8_t> vec(str.begin(), str.end());
              return vec;

sensor:
  - platform: template
    id: "info_receiver_gain"
    name: "info_receiver_gain"
    unit_of_measurement: "dB"
    accuracy_decimals: 0

button:
  - platform: template
    name: "button_scan_tag"
    id: "button_scan_tag"
    on_press:
      - uart.write: "Q\r\n"
  - platform: template
    name: "button_resend_tx_debug"
    id: "button_resend_tx_debug"
    on_press:
      then:
        - uart.write: 
            data: !lambda |-
              std::string str = ("\n" + id(uart_tx_debug).state + "\r\n");
              std::vector<uint8_t> vec(str.begin(), str.end());
              return vec;

switch:
  - platform: template
    name: "rfid_poll_enable"
    id: "rfid_poll_enable"
    optimistic: True
    on_turn_on: 
      then:
        - switch.turn_off: rfid_access_granted
  - platform: template
    name: "rfid_access_granted"
    id: "rfid_access_granted"
    optimistic: True
    on_turn_on:
      then:
        - if: 
            condition: 
              - switch.is_on: rfid_poll_enable
            then: 
              - delay: 
                  seconds: 3
              - switch.turn_off: rfid_access_granted
1 Like