/

自制 NFC 读卡器接入 HA

nfc_reader_cover

NFC(Near-field communication,近距离无线通讯)可以作为一种很好的验证方式,以一种方便的形式实现一些自动化,比如:

  • 扫描 NFC 卡之后播放特定的音乐列表清单
  • 利用 NFC 标籤激活房间中的场景
  • 通过 NFC 卡开锁
  • 读取后 NFC 标签打开设备详情页
  • 等等

现在人人随身携带手机,手机也自带 NFC 功能,NFC 使用场景更加充满实用性和想象力。Home Assistant 在 App 中已为标籤编写特殊的 Home Assistant URL扫描 NFC 标籤后,可以触發 Home Assistant 应用并将标识符發送到 Home Assistant 实例处理。

不过不是所有手机都支持 NFC(如 iPhone 需要 iPhone XS、XR、iPhone 11 及以上可以后台读取 NFC 卡片)。有大大利用 ESPHome 自制了一个 NFC 读卡器,小弟跟着步骤踩了一遍坑,以下是步骤:

所需材料

1、ESP8266(或 D1 Mini,D1 mini 体积更小)
2、PN532 NFC 读卡器(PN532 可能是最好的 NFC 读写模块)
3、WS2812(非必须,用作读卡后的 LED 灯泡反馈)
4、蜂鸣器(非必须,作为读卡后的声音反馈)
5、3D 打印外(非必须),地址在这里
6、杜邦线若干

准备工作

如果使用 D1 Mini,可能需要焊接。

另外,确保将 PN532 上的开关设置为以下开关状态:
开关1:打开(向上)
开关2:关闭(向下)

nfc_reader_pn532

刷机

确保 ESPHome 版本在 1.16.0 及以上,在 ESPHome 中新增一个 ESP8266 设备,在配置中加入以下代码(若无 WS2812 和蜂鸣器可以删去相应代码):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
substitutions:
devicename: tagreader
friendly_name: TagReader


# 如果有蜂鸣器,连接成功后在 api 中通知
on_boot:
priority: -10
then:
- wait_until:
api.connected:
- logger.log: API is connected!
- rtttl.play: "success:d=24,o=5,b=100:c,g,b"
- light.turn_on:
id: activity_led
brightness: 100%
red: 0%
green: 0%
blue: 100%
flash_length: 500ms

# 在 HA 中虚拟一个可以控制 LED 灯和蜂鸣器的开关
switch:
- platform: template
name: "${friendly_name} Buzzer Enabled"
id: buzzer_enabled
icon: mdi:volume-high
optimistic: true
restore_state: true
- platform: template
name: "${friendly_name} LED enabled"
id: led_enabled
icon: mdi:alarm-light-outline
optimistic: true
restore_state: true


# 启用 Home Assistant API
api:
services:
- service: rfidreader_tag_ok
then:
- rtttl.play: "beep:d=16,o=5,b=100:b"

- service: rfidreader_tag_ko
then:
- rtttl.play: "beep:d=8,o=5,b=100:b"

- service: play_rtttl
variables:
song_str: string
then:
- rtttl.play: !lambda 'return song_str;'

- service: write_tag_random
then:
- lambda: |-
static const char alphanum[] = "0123456789abcdef";
std::string uri = "https://www.home-assistant.io/tag/";
for (int i = 0; i < 8; i++)
uri += alphanum[random_uint32() % (sizeof(alphanum) - 1)];
uri += "-";
for (int j = 0; j < 3; j++) {
for (int i = 0; i < 4; i++)
uri += alphanum[random_uint32() % (sizeof(alphanum) - 1)];
uri += "-";
}
for (int i = 0; i < 12; i++)
uri += alphanum[random_uint32() % (sizeof(alphanum) - 1)];
auto message = new nfc::NdefMessage();
message->add_uri_record(uri);
ESP_LOGD("tagreader", "Writing payload: %s", uri.c_str());
id(pn532_board).write_mode(message);
- service: write_tag_id
variables:
tag_id: string
then:
- lambda: |-
auto message = new nfc::NdefMessage();
std::string uri = "https://www.home-assistant.io/tag/";
uri += tag_id;
message->add_uri_record(uri);
id(pn532_board).write_mode(message);
- service: clean_tag
then:
- lambda: 'id(pn532_board).clean_mode();'

- service: cancel_writing
then:
- lambda: 'id(pn532_board).read_mode();'


i2c:
scan: False
frequency: 400kHz

pn532_i2c:
id: pn532_board
on_tag:
then:
- homeassistant.tag_scanned: !lambda |
if (!tag.has_ndef_message()) {
ESP_LOGD("tagreader", "No NDEF");
return x;
}
auto message = tag.get_ndef_message();
auto records = message->get_records();
for (auto &record : records) {
std::string payload = record->get_payload();
size_t pos = payload.find("https://www.home-assistant.io/tag/");
if (pos != std::string::npos) {
return payload.substr(pos + 34);
}
}
ESP_LOGD("tagreader", "Bad NDEF, fallback to uid");
return x;
- if:
condition:
switch.is_on: buzzer_enabled
then:
- rtttl.play: "success:d=24,o=5,b=100:c,g,b"
- if:
condition:
switch.is_on: led_enabled
then:
- light.turn_on:
id: activity_led
brightness: 100%
red: 0%
green: 100%
blue: 0%
flash_length: 500ms

# 定义蜂鸣器输出所在端口
output:
- platform: esp8266_pwm
pin: D7
id: buzzer

binary_sensor:
- platform: status
name: "${friendly_name} Status"

# 定义蜂鸣器作为 RTTTL 输出
rtttl:
output: buzzer

# LED 配置
light:
- platform: fastled_clockless
chipset: WS2812
pin: D8
default_transition_length: 10ms
num_leds: 1
rgb_order: GRB
id: activity_led
name: "${friendly_name} LED"
restore_mode: ALWAYS_OFF

之后编译并下载固件,使用 NodeMCU PyFlasher 将固件刷入 ESP8266/D1 Mini。

连线

按照以下将 ESP8266 与 PN532 连线:

PN532 ESP8266
GND G
VCC VIN
SDA D2
SCL D1

LED 等与蜂鸣器可以按照以下连线:

nfc_reader_schematics

最终效果如下:

nfc_reader_inside

使用

确保 Home Assistant版本在 0.115 及以上,待 NFC 读取器连接到与 HA 同一网络,Home Assistant 会自动發现 NFC 读取器。

之后从 Home Assistant 中的标籤界面中可以管理扫描的标籤(在 config->tag 下可以找到。

nfc_reader_ui