TagTuner music player for NFC tags

a few questions:

  1. which microcontroller? esp32 D1 or Atom Echo?
  2. Please paste link to yaml file you’ve used.
  3. Did you do wifi configuration with ESP Web Tools?

ESP32 D1

substitutions:

  wifi_ssid: "MY_SSID"
  wifi_password: "MY_PASSWORD"

packages:
  project: github://luka6000/TagTuner/tagtuner-project.yaml@main
  core-nfc: github://luka6000/TagTuner/tagtuner-core-nfc.yaml@main
  core-comms: github://luka6000/TagTuner/tagtuner-core-comms.yaml@main
  # core-nfc: !include tagtuner-core-nfc.yaml
  # core-comms: !include tagtuner-core-comms.yaml

esphome:
  name: "${name}"
  friendly_name: ${friendly_name}
  # Automatically add the mac address to the name
  # so you can use a single firmware for all devices
  name_add_mac_suffix: true

  # hello world
  on_boot:
    priority: -100
    then:
    - light.turn_on:
        id: led1
        effect: TagWrite
    - delay: 1000ms
    - light.turn_off: led1
    - wait_until:
        condition:
          api.connected:
        timeout: 20s
    - text_sensor.template.publish:
        id: status
        state: "Ready"

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

# To be able to get logs from the device via serial and api.
logger:
  # level: VERBOSE
  level: DEBUG
  # level: WARN
  logs:
    light: WARN
    # pn532: DEBUG
    # pn532_i2c: DEBUG

i2c:
  id: bus_i2c
  sda: 21
  scl: 22
  scan: False
  frequency: 100kHz
  timeout: 13ms #to prevent pn532 timeout

esp32_ble_tracker:
  scan_parameters:
    active: false
bluetooth_proxy:
  active: false

binary_sensor:
  - platform: gpio
    id: toggle
    pin:
      number: 23
      inverted: true
      mode:
        input: true
        pullup: true
    on_multi_click:
    - timing:
        - ON for at most 1s
        - OFF for at most 0.25s
        - ON for at most 1s
        - OFF for at most 0.25s
        - ON for at most 1s
      then:
        - script.execute: led_blink
        - logger.log: "Triple Clicked"
        - text_sensor.template.publish:
            id: status
            state: "clickTriple"
        - script.execute:
            id: action_event
            action: "clickTriple"
    - timing:
        - ON for at most 1s
        - OFF for at most 0.25s
        - ON for at most 1s
        - OFF for at least 0.25s
      then:
        - script.execute: led_blink
        - logger.log: "Double Clicked"
        - text_sensor.template.publish:
            id: status
            state: "clickDouble"
        - script.execute:
            id: action_event
            action: "clickDouble"
    - timing:
        - ON for 0.5s to 2s
        - OFF for at least 50ms
      then:
        - script.execute: led_blink
        - logger.log: "Single Long Clicked"
        - text_sensor.template.publish:
            id: status
            state: "clickLong"
        - script.execute:
            id: action_event
            action: "clickLong"
    - timing:
        - ON for at most 0.5s
        - OFF for at least 260ms
      then:
        - script.execute: led_blink
        - logger.log: "Single Short Clicked"
        - text_sensor.template.publish:
            id: status
            state: "clickSingle"
        - script.execute:
            id: tagtuner_event
            action: "clickSingle"
            uid: ""
            uri: ""
            artist: ""
            playlist: ""

output:
  - platform: gpio
    pin: 2
    id: led1_gpio

light:
  - platform: binary
    id: led1
    output: led1_gpio
    restore_mode: ALWAYS_OFF
    effects:
      - strobe:
          name: TagWrite
          colors:
            - state: true
              duration: 250ms
            - state: false
              duration: 50ms

script:
  - id: led_blink
    then:
    - light.turn_off: led1
    - light.turn_on:
        id: led1
        flash_length: 100ms
  - id: led_ok
    then:
    - light.turn_off: led1
    - light.turn_on:
        id: led1
        flash_length: 50ms
    - delay: 100ms
    - light.turn_on:
        id: led1
        flash_length: 50ms
  - id: led_success
    then:
    - light.turn_off: led1
    - light.turn_on:
        id: led1
        flash_length: 200ms
    - delay: 250ms
    - light.turn_on:
        id: led1
        flash_length: 200ms
    - delay: 250ms
    - light.turn_on:
        id: led1
        flash_length: 500ms
  - id: led_tagwrite
    then:
    - light.turn_on:
        id: led1
        effect: TagWrite

sensor:
  - platform: rotary_encoder
    id: rotary
    pin_a: 18
    pin_b: 19
    on_clockwise:
      - text_sensor.template.publish:
          id: status
          state: "Volume up"
      - script.execute:
          id: tagtuner_event
          action: "volumeUp"
          uid: ""
          uri: ""
          artist: ""
          playlist: ""
      - script.execute: led_blink
    on_anticlockwise:
      - text_sensor.template.publish:
          id: status
          state: "Volume down"
      - script.execute:
          id: tagtuner_event
          action: "volumeDown"
          uid: ""
          uri: ""
          artist: ""
          playlist: ""
      - script.execute: led_blink

How can I configure the wifi with ESP Tools?

Thank You!

Now I can See the TagTuner under esp devices :slight_smile:

I Have added a tag with the folowing details.

URI: 500 Random tracks

and I have Configuerd the bluprint , but nothing happening…

ok, so please use this yaml TagTuner/tagtuner-D1-custom1.factory.yaml at 004443c0be22e62305c413c92a4de6d493bbf74d · luka6000/TagTuner · GitHub without changes and then configure wifi with Improv over serial wire or bluetooth. Check out this video https://www.improv-wifi.com/
Actually when you use this yaml, you should open HA app on the phone and your TagTuner will be discovered over bluetooth. Give it a try and only if this won’t make it for you, use wire.
TagTuner wifi is made to be configured with Improv and your substitutions are not used in the included code.
I’m thinking about doing better “code paste” yaml file with placeholders for custom wifi configuration. For now you are better with this factory.yaml above.

Thank You very Much!

Hi Roeland. I like you Design very much. Is it possible to get config, blueprint and stl somewhere. There was an answer by lucca, but this was over one month ago…

Hi Luka,

thanks for the excellent work with this project! It’s really well documented and the setup is really smooth (building/flashing/etc.)!

Sadly I have one problem with the last step (triggering the playback on my Music Assistant player) I can’t fix :

  • The tagtuner is setup in Home Assistant and I see all the info in the device page.
  • I can write tags and also see the status changing to “Tag XXX” after reading a tag.
  • The automation (based on the blueprint v250105) is also triggered.
  • Sadly the song (I added an Apple Music track uri to the tag) is not added to the Music Assistant queue and nothing is played.
  • I see in the logbook (see screenshot) that the status changes from “Waiting for input” to the tag id but one second later to “Tag removed” again. But what is the root cause for that? The nfc card is laying flat on the pn523.

Any idead what could be the problem here? Or any further things I could debug?

Thanks in advance! :slight_smile:

Good to hear you like it!
So, I’ve seen such a flipping behavior only when multiple tags where placed on the reader. Or the tag was to far from the reader. Have you tried with different tag? Or laying the tag flat on TagTuner? Or turning it around if it’s rectangle? Some of my tags work only one way in the angle slot.
Nevertheless what you can do is go to blueprint automation for your TagTuner and in Additional settings turn off option „Stop music on tag removed” and see how that will work.

Thanks for the quick reply! I got it all working (although with a bit of a hack):

The flipping was probably caused by me just placing the tag directly on the pn532. with a little bit of spacing it works fine and doesn’t trigger the “Tag removed”.

I had the problem, that the playback still wasn’t triggered. In the traces I saw, that the variable source wasn’t set. Since I only use Apple Music as a source, I hardcoded the source to mass and it’s working fine now!
Maybe something in the source if-block in the automation doesn’t work correctly?

heh, ok, too close!
How do you set URI?
source is set based on URI

  source: >-
    {%if uri[0:5]=='https' %}
      http
    {%elif uri[0:10]=='sonos-2://' %}
      sonos
    {%elif uri[0:14]=='apple_music://'
      or uri[0:9]=='deezer://'
      or uri[0:19]=='filesystem_local://'
      or uri[0:17]=='filesystem_smb://'
      or uri[0:7]=='plex://'
      or uri[0:8]=='qobuz://'
      or uri[0:15]=='radiobrowser://'
      or uri[0:13]=='soundcloud://'
      or uri[0:10]=='spotify://'
      or uri[0:8]=='tidal://'
      or uri[0:9]=='tunein://'
      or uri[0:10]=='ytmusic://'
      or uri[0:10]=='library://'
      %}mass
    {%endif%}

Here’s an example of the variables set after tagScanned

device_id: e76fc3de9a201c1c8e5cb608e7b8172b
massplayer: media_player.mass_smallroom
httpplayer: media_player.smallroom_sonos
stop_on_tagremoved: true
action: tagScanned
uri: >-
  https://music.apple.com/pl/playlist/taylor-swift/pl.ca7c0fe7264749c5afbc68234cd03a18
source: http
text_playlist_uri: text.tagtuner_fd3aa4_playlist_uri
text_playlist_artist: text.tagtuner_fd3aa4_playlist_artist
text_playlist_name: text.tagtuner_fd3aa4_playlist_name_or_album_title
announce_playlist: true
shuffle_config: false
shuffle_playlist: false
max_volume: 1
notify_version: true
blprnt_version: 250105

Sorry for not getting back to you sooner…

I was setting the uri with the output from the mass action music_assistant.search, e.g. apple_music://track/1440922552.

When I tried it again with the source selection you posted, I used your example uri, I get basically the same variables set:

device_id: d7c7b0a9a575784cb41fe251afc326f4
massplayer: media_player.living_room
httpplayer: media_player.living_room
stop_on_tagremoved: true
action: tagScanned
uri: >-
  https://music.apple.com/pl/playlist/taylor-swift/pl.ca7c0fe7264749c5afbc68234cd03a18
source: http
text_playlist_uri: text.fietebox_playlist_uri
text_playlist_artist: text.fietebox_playlist_artist
text_playlist_name: text.fietebox_playlist_name_or_album_title
announce_playlist: false
shuffle_config: false
shuffle_playlist: false
max_volume: 1
notify_version: true
blprnt_version: 250105

Then I see in the traces, that the source is getting set to http and I get to the action media_player.play_media in the automation. But there I get the error Error: No playable items found.

Is it related to not finding a playable item at the uri or is this a probem with my media_play setup in mass (which is a homepod mini btw)?

well, you’ve waited long enough for MAss team to fix this :wink:
Use version 2.4 and check this out shortcut to copy URI from UI · music-assistant · Discussion #2311 · GitHub

In MAss 2.4 URI is available in the UI in Provider details. And there is even a button to copy!

Hi Luka,

Thanks for this huge work. I’ve tried to set up an 8266 with pnc532 (without all the rest but can’t get the tag_scanned event, DId you encounter that ?

The tagRemoved works well…

Thanks !

tag_scanned homeassistant events are sent only for plain empty or read-only tags. For tags written with uri data, TagTuner sends esphome.tagtuner events with proper action (tagScanned). See examples below

read-only tag, not configured for TagTuner (notice plain name, TagTuner blueprint automation will not catch this event but it can be used for any other automation):

event_type: tag_scanned
data:
  tag_id: 04-DE-82-22-04-10-90
  name: Tag 04-DE-82-22-04-10-90
  device_id: e76fc3de9a201c1c8e5cb608e7b8172b
origin: LOCAL

read-only tag configured in HA Tags panel for usage with TagTuner (notice name containing playlist http URI so this is for Sonos players):

event_type: tag_scanned
data:
  tag_id: 04-E6-1E-CA-4B-13-90
  name: >-
    https://music.apple.com/pl/playlist/babymetal-essentials/pl.fb557a2d5e9b4be3af6a94d62b45a6a8
  device_id: e76fc3de9a201c1c8e5cb608e7b8172b
origin: LOCAL

TagTuner written NTAG http playlist used with Sonos players (directly, no MAss):

event_type: esphome.tagtuner
data:
  device_id: e76fc3de9a201c1c8e5cb608e7b8172b
  magic: "250308"
  action: tagScanned
  uid: 04-BE-43-D2-A7-59-80
  uri: >-
    https://music.apple.com/pl/playlist/taylor-swift/pl.ca7c0fe7264749c5afbc68234cd03a18
  artist: Taylor Swift
  playlist: Glitter Gel Pen Songs
origin: LOCAL

TagTuner written NTAG MAss playlist used with any MAss media player

event_type: esphome.tagtuner
data:
  device_id: e76fc3de9a201c1c8e5cb608e7b8172b
  magic: "250308"
  action: tagScanned
  uid: 04-A0-43-D2-A7-59-80
  uri: apple_music://playlist/pl.ca7c0fe7264749c5afbc68234cd03a18
  artist: Taylor Swift
  playlist: Glitter Gel Pen Songs
origin: LOCAL

Hope this helps.

Celebrating MAR10 day! One time offer till Friday :wink:
D1 and HAVPE cases in my ko-fi/shop
https://ko-fi.com/s/1ae4cfa0fb
https://ko-fi.com/s/78a1981562

And by the way, I’ve just released v250308

Thanks for your answer.

I’ve just bought an esp32 d1 mini and it works like a charm. Do you mind if I try to contribute to the github project for the 8266 ?

sure I don’t mind.
Here’s a working version for 8266 tagreader

Remember to test everything as this configuration for 8266 is very delicate and memory constrained. Even if it compiles and boots up, you can’t be sure till you test every feature.

Hi Luka, thanks for the guide

I’m trying to use it ESP32 D1. I was able to connect the tuner to HA, all volume controls work but the tag reader doesn’t work.

The led on PN532 is yellow, the wiring seems ok. How to debug this?

I figured out, If you see similar logs like below, most likely PN532 pins set in wrong position

Logs

[C][pn532:018]: Setting up PN532...
[W][pn532:022]: Error sending version command, trying again...
[E][pn532:024]: Error sending version command
[E][component:119]: Component pn532 was marked as failed.
[E][component:164]: Component pn532 set Error flag: unspecified

I tried to install the latest version, it did fail. So I decided to make a fresh install.
But I’m totally lost.

  1. I create a new ESP Device in ESPHome Builder (Name: TagTuner) and install the inital config from ESP Home to the atom groove via cable
  2. now i have a ESP Home Device named TagTuner, with tagtuner.yaml
  3. I take the content from TagTuner/tagtuner-atom-grove-ble.factory.yaml at 29989ed045dcb5f80b491d804a64f8af85f8dc0c · luka6000/TagTuner · GitHub and replace everything in my config except ota and api
  4. I try to install (via cable), but always will get an error
INFO ESPHome 2025.3.0
INFO Reading configuration /config/esphome/tagtuner.yaml...
ERROR Error while reading config: Invalid YAML syntax:

Error reading file /config/esphome/tagtuner-atom-grove-ble.yaml: [Errno 2] No such file or directory: '/config/esphome/tagtuner-atom-grove-ble.yaml'

What am I missing? Can someone point me to the wrong direction I take at some point of the journey :slight_smile:

Which controller are you using? m5 Atom? Have you tried installing from here TagTuner music player | Powered by ESPHome and ESP Web Tools
I’d recommend installing with the link above with usb cable. Then set WiFi configuration: right after flashing there will be option to do that. And only then go to HA and discover your new TagTuner - it should pop up as new device.
Unless you want to change the firmware code you don’t need esp device builder. You will want to have builder config for upgrades but first things first.

Just checked again. That error is my fault. Replace this

packages:
  # Include all of the core configuration
  core: !include tagtuner-atom-grove-ble.yaml
  # core: 
  #   url: https://github.com/luka6000/TagTuner
  #   files: tagtuner-atom-grove-ble.yaml
  #   ref: main
  #   refresh: 0d

with this

packages:
  # Include all of the core configuration
  #core: !include tagtuner-atom-grove-ble.yaml
  core: 
    url: https://github.com/luka6000/TagTuner
    files: tagtuner-atom-grove-ble.yaml
    ref: main
    refresh: 0d
1 Like