Help writing a component to read UART data

Hi, I’m an experienced dev, but new to ESPHome and rusty on C++

I have a UHF RFID reader that just outputs a series of bytes via UART on successful read. I’ve go this working using the generic UART bus with a debug call.

I’d like to create a custom component that listens for UART, then on a delimiter e.g. 0x00 (or possibly a 100ms timeout) takes the buffer (which will contain the last read card ID as a list of bytes). Then publishes this as a hex string to a given MQTT topic, as a JSON string with some other stuff.

I want to be able to configure it with the following:

optional UART bus ID - If none configured then just default to UART bus
mqtt topic - the topic to publish the data to
device-name - can this be gotten from somewhere?

example:

uhf-rfid:
  topic: example/rfid/read

This would publish {"device":"my-card-reader","card":"23-23-64-62-14"}

I made a start here → GitHub - NemiahUK/esphome-uhf-rfid but it’s just based on the very limited example in the docs. Any help would be massively appreciated.

Thanks!

Your component should do the UART RX only. The mqtt publish part should be done using existing ESPHome components and automations.

See: MQTT Client Component — ESPHome

You can probably do this without a component if you want.

There is a way which I can’t seem to find right now. But substitutions might do the job.

Edit: have a read of this thread.

Good point, I’m using this methodology elsewhere.

So I need the component to have an event like on_tag that passes the card ID as x then use lamda to do the rest e.g.

uhf_rfid:
  on_tag:
    then:
      - mqtt.publish:
          topic: ${namespace}/rfid/read
          payload: !lambda return "{\"device\":\"${device_name}\",\"tag\":\""+x+"\"}";

Thanks for the info. I’m currently using the UART debug and a lambda call but the delimiter option just doesn’t seem to work properly.

The UART device returns a fixed number of bytes per read, each one is separated by a few 100ms and they contain the same start and end bytes.

I’ve tried setting the delimiter to time based, a specific byte and a fixed length, however it seems to wait until an arbitrary amount of data is in the buffer then dumps it all in one go.

uart:
  tx_pin: 14
  rx_pin: 13
  baud_rate: 9600
  debug:
    direction: RX
    dummy_receiver: true
    after:
      delimiter: [0x00]
    sequence:
      - lambda:
          UARTDebug::log_hex(direction, bytes, ' ');
          id(mqtt_client).publish("${namespace}/rfid/read", "{\"device\":\"${device_name}\",\"tag\":\""+format_hex(bytes)+"\"}");
      - lock.unlock: door_lock

That’s what I have now, but it just returns around 3-4 tag reads with the last one truncated.