ruuvi.drivers.c  ${PROJECT_VERSION}
Drivers for external sensors and peripherals on embedded systems.
ruuvi_nrf5_sdk15_communication_nfc.c
Go to the documentation of this file.
1 
10 #if RUUVI_NRF5_SDK15_NFC_ENABLED
11 
12 #include "ruuvi_driver_error.h"
13 #include "ruuvi_nrf5_sdk15_error.h"
15 #include "ruuvi_interface_log.h"
16 #include <stdint.h>
17 
18 #include "nfc_t4t_lib.h"
19 #include "nfc_ndef_msg.h"
20 #include "nfc_uri_rec.h"
21 #include "nfc_text_rec.h"
22 #include "nfc_launchapp_rec.h"
23 #include "nfc_ndef_msg_parser.h"
24 
25 #define RUUVI_NRF5_SDK15_COMM_NFC_MAX_RECORDS 4
26 #define BIN_PAY_DESC_HEADER_LEN 3U
27 
28 static struct
29 {
30  volatile uint8_t connected; // NFC field is active
31  uint8_t initialized;
32  volatile uint8_t rx_updated; // New data received
33  volatile uint8_t tx_updated; // New data should be written to buffer
34  volatile uint8_t configurable; // Allow NFC to write configuration
35  uint8_t nfc_ndef_msg[ (RUUVI_NRF5_SDK15_COMM_NFC_MAX_RECORDS + 1)
37  volatile size_t nfc_ndef_msg_len;
38  uint8_t desc_buf[NFC_NDEF_PARSER_REQIRED_MEMO_SIZE_CALC (
39  RUUVI_NRF5_SDK15_COMM_NFC_MAX_RECORDS)]; // Buffer to contain incoming data descriptors
40  size_t msg_index; // Store index of our record
41  ri_comm_evt_handler_fp_t * on_nfc_evt;
42 } nrf5_sdk15_nfc_state;
43 
47 static void nfc_callback (void * context,
48  nfc_t4t_event_t event,
49  const uint8_t * data,
50  size_t dataLength,
51  uint32_t flags)
52 {
53  (void) context;
54 
55  switch (event)
56  {
58  if (NULL != * (nrf5_sdk15_nfc_state.on_nfc_evt)
59  && false == nrf5_sdk15_nfc_state.connected)
60  {
61  (* (nrf5_sdk15_nfc_state.on_nfc_evt)) (RI_COMM_CONNECTED, NULL, 0);
62  }
63 
64  nrf5_sdk15_nfc_state.connected = true;
65  break;
66 
68  if (NULL != * (nrf5_sdk15_nfc_state.on_nfc_evt) && true == nrf5_sdk15_nfc_state.connected)
69  {
70  (* (nrf5_sdk15_nfc_state.on_nfc_evt)) (RI_COMM_DISCONNECTED, NULL,
71  0);
72  }
73 
74  nrf5_sdk15_nfc_state.connected = false;
75  break;
76 
78  if (NULL != * (nrf5_sdk15_nfc_state.on_nfc_evt)) { (* (nrf5_sdk15_nfc_state.on_nfc_evt)) (RI_COMM_SENT, NULL, 0); }
79 
80  break;
81 
82  // Update process generally sets length of field to 0 and
83  // then updates. Once done, length is updated again.
85  if (dataLength > 0)
86  {
87  nrf5_sdk15_nfc_state.nfc_ndef_msg_len = dataLength;
88  nrf5_sdk15_nfc_state.rx_updated = true;
89 
90  // If tag is not configurable by NFC, set flag to overwrite received data.
91  if (!nrf5_sdk15_nfc_state.configurable) { nrf5_sdk15_nfc_state.tx_updated = true;}
92 
93  // Do not process data in interrupt context, you should rather schedule data processing. Note: If incoming data is long, it might exceed max size.
94  if (NULL != * (nrf5_sdk15_nfc_state.on_nfc_evt)) { (* (nrf5_sdk15_nfc_state.on_nfc_evt)) (RI_COMM_RECEIVED, nrf5_sdk15_nfc_state.nfc_ndef_msg, dataLength); }
95  }
96 
97  break;
98 
99  default:
100  break;
101  }
102 }
103 
104 NFC_NDEF_MSG_DEF (nfc_ndef_msg,
105  RUUVI_NRF5_SDK15_COMM_NFC_MAX_RECORDS); // max NFC_MAX_NUMBER_OF_RECORDS records
106 
107 // Setup constant records
108 static uint8_t nfc_fw_buf[RI_COMM_DIS_STRLEN];
109 static size_t nfc_fw_length = 0;
110 static uint8_t nfc_addr_buf[RI_COMM_DIS_STRLEN];
111 static size_t nfc_addr_length = 0;
112 static uint8_t nfc_id_buf[RI_COMM_DIS_STRLEN];
113 static size_t nfc_id_length = 0;
114 static uint8_t nfc_tx_buf[RI_COMM_DIS_STRLEN];
115 static size_t nfc_tx_length = 0;
116 
117 rd_status_t ri_nfc_init (ri_comm_channel_t * const channel)
118 {
119  if (NULL == channel) { return RD_ERROR_NULL; }
120 
121  if (nrf5_sdk15_nfc_state.initialized) { return RD_ERROR_INVALID_STATE; }
122 
123  // Set up NFC
124  ret_code_t err_code = NRF_SUCCESS;
125  // Allow invalid state here as setup will return invalid state on reinit.
126  err_code |= nfc_t4t_setup (nfc_callback, NULL);
127 
128  if (NRF_ERROR_INVALID_STATE == err_code)
129  {
130  err_code = NRF_SUCCESS;
131  }
132 
133  memset (nrf5_sdk15_nfc_state.nfc_ndef_msg, 0, sizeof (nrf5_sdk15_nfc_state.nfc_ndef_msg));
134  // Run Read-Write mode for Type 4 Tag platform
135  err_code |= nfc_t4t_ndef_rwpayload_set (nrf5_sdk15_nfc_state.nfc_ndef_msg,
136  sizeof (nrf5_sdk15_nfc_state.nfc_ndef_msg));
137  // Setup communication abstraction fps
138  channel->init = ri_nfc_init;
139  channel->uninit = ri_nfc_uninit;
140  channel->read = ri_nfc_receive;
141  channel->send = ri_nfc_send;
142  channel->on_evt = NULL;
143  nrf5_sdk15_nfc_state.on_nfc_evt = & (channel->on_evt);
144  // Start sensing NFC field
145  err_code |= nfc_t4t_emulation_start();
146  nrf5_sdk15_nfc_state.initialized = true;
147  nrf5_sdk15_nfc_state.msg_index = 0;
148  return ruuvi_nrf5_sdk15_to_ruuvi_error (err_code);
149 }
150 
152 {
153  ret_code_t err_code = nfc_t4t_emulation_stop();
154  err_code |= nfc_t4t_done();
155  memset (&nrf5_sdk15_nfc_state, 0, sizeof (nrf5_sdk15_nfc_state));
156  memset (channel, 0, sizeof (ri_comm_channel_t));
157  return ruuvi_nrf5_sdk15_to_ruuvi_error (err_code);
158 }
159 
161 {
162  // State check
163  if (!nrf5_sdk15_nfc_state.initialized) { return RD_ERROR_INVALID_STATE; }
164 
165  if (nrf5_sdk15_nfc_state.connected) { return RD_ERROR_INVALID_STATE; }
166 
167  // Return success if there is nothing to do
168  if (! (nrf5_sdk15_nfc_state.tx_updated)) { return RD_SUCCESS; }
169 
170  // Create NFC NDEF text record description in English
171  ret_code_t err_code = NRF_SUCCESS;
172  uint8_t fw_code[] = RI_NFC_SW_FIELD_CODE;
173  NFC_NDEF_TEXT_RECORD_DESC_DEF (nfc_fw_rec,
174  UTF_8,
175  fw_code,
176  sizeof (fw_code),
177  nfc_fw_buf,
178  nfc_fw_length);
179  uint8_t addr_code[] = RI_NFC_ADDR_FIELD_CODE;
180  NFC_NDEF_TEXT_RECORD_DESC_DEF (nfc_addr_rec,
181  UTF_8,
182  addr_code,
183  sizeof (addr_code),
184  nfc_addr_buf,
185  nfc_addr_length);
186  uint8_t id_code[] = RI_NFC_ID_FIELD_CODE;
187  NFC_NDEF_TEXT_RECORD_DESC_DEF (nfc_id_rec,
188  UTF_8,
189  id_code,
190  sizeof (id_code),
191  nfc_id_buf,
192  nfc_id_length);
193  uint8_t data_code[] = RI_NFC_DATA_FIELD_CODE;
194  NFC_NDEF_TEXT_RECORD_DESC_DEF (nfc_bin_rec, \
195  UTF_8, \
196  data_code, sizeof (data_code), \
197  nfc_tx_buf, \
198  nfc_tx_length);
199  // Clear our record
200  nfc_ndef_msg_clear (&NFC_NDEF_MSG (nfc_ndef_msg));
201 
202  // Add new records if applicable
203  if (nfc_id_length)
204  {
205  err_code |= nfc_ndef_msg_record_add (&NFC_NDEF_MSG (nfc_ndef_msg),
206  &NFC_NDEF_TEXT_RECORD_DESC (nfc_id_rec));
207  }
208 
209  if (nfc_addr_length)
210  {
211  err_code |= nfc_ndef_msg_record_add (&NFC_NDEF_MSG (nfc_ndef_msg),
212  &NFC_NDEF_TEXT_RECORD_DESC (nfc_addr_rec));
213  }
214 
215  if (nfc_fw_length)
216  {
217  err_code |= nfc_ndef_msg_record_add (&NFC_NDEF_MSG (nfc_ndef_msg),
218  &NFC_NDEF_TEXT_RECORD_DESC (nfc_fw_rec));
219  }
220 
221  if (nfc_tx_length)
222  {
223  err_code |= nfc_ndef_msg_record_add (&NFC_NDEF_MSG (nfc_ndef_msg),
224  &NFC_NDEF_TEXT_RECORD_DESC (nfc_bin_rec));
225  }
226 
227  // Encode data to NFC buffer. NFC will transmit the buffer, i.e. data is updated immediately.
228  err_code |= nfc_t4t_emulation_stop();
229  uint32_t msg_len = sizeof (nrf5_sdk15_nfc_state.nfc_ndef_msg);
230  err_code |= nfc_ndef_msg_encode (&NFC_NDEF_MSG (nfc_ndef_msg),
231  nrf5_sdk15_nfc_state.nfc_ndef_msg,
232  &msg_len);
233  err_code |= nfc_t4t_ndef_rwpayload_set (nrf5_sdk15_nfc_state.nfc_ndef_msg,
234  sizeof (nrf5_sdk15_nfc_state.nfc_ndef_msg));
235  err_code |= nfc_t4t_emulation_start();
236  // TX Data processed, set update status to false
237  nrf5_sdk15_nfc_state.tx_updated = false;
238  return ruuvi_nrf5_sdk15_to_ruuvi_error (err_code);
239 }
240 
242 {
243  if (NULL == message) { return RD_ERROR_NULL; }
244 
245  if (message->data_length >= RI_COMM_DIS_STRLEN) { return RD_ERROR_INVALID_LENGTH; }
246 
247  nfc_tx_length = message->data_length;
248  memcpy (nfc_tx_buf, message->data, nfc_tx_length);
249  nrf5_sdk15_nfc_state.tx_updated = true;
250  return ri_nfc_data_set();
251 }
252 
253 rd_status_t ri_nfc_fw_version_set (const uint8_t * const version, const uint8_t length)
254 {
255  if (NULL == version && length) { return RD_ERROR_NULL; }
256 
257  if (length >= RI_COMM_DIS_STRLEN) { return RD_ERROR_INVALID_LENGTH; }
258 
259  nfc_fw_length = length;
260  memcpy (nfc_fw_buf, version, nfc_fw_length);
261  nrf5_sdk15_nfc_state.tx_updated = true;
262  return RD_SUCCESS;
263 }
264 
265 rd_status_t ri_nfc_address_set (const uint8_t * const address, const uint8_t length)
266 {
267  if (NULL == address && length) { return RD_ERROR_NULL; }
268 
269  if (length >= RI_COMM_DIS_STRLEN) { return RD_ERROR_INVALID_LENGTH; }
270 
271  nfc_addr_length = length;
272  memcpy (nfc_addr_buf, address, nfc_addr_length);
273  nrf5_sdk15_nfc_state.tx_updated = true;
274  return RD_SUCCESS;
275 }
276 
277 rd_status_t ri_nfc_id_set (const uint8_t * const id,
278  const uint8_t length)
279 {
280  if (NULL == id && length) { return RD_ERROR_NULL; }
281 
282  if (length >= RI_COMM_DIS_STRLEN) { return RD_ERROR_INVALID_LENGTH; }
283 
284  nfc_id_length = length;
285  memcpy (nfc_id_buf, id, nfc_id_length);
286  nrf5_sdk15_nfc_state.tx_updated = true;
287  return RD_SUCCESS;
288 }
289 
290 /* Read and parse RX buffer into records. Parse records into Ruuvi Communication messages.
291  * Restore original data after last record has been parsed
292  *
293  * parameter msg: Ruuvi Communication message, received record payload is copied into message payload field.
294  *
295  * Return RD_STATUS_MORE_AVAILABLE if payload was parsed into msg and more data is available
296  * Return RD_SUCCESS if payload was parsed into msg and no more data is available
297  * Return RD_ERROR_NOT_FOUND if no data was buffered and message could not be parsed.
298  * Return RD_ERROR_DATA_SIZE if received message could not fit into message payload
299  */
301 {
302  // Input check
303  //ruuvi_platform_log("Getting message, state check");
304  if (NULL == msg) { return RD_ERROR_NULL; }
305 
306  // State check. Do not process data while connection is active, i.e. while
307  // data might be received.
308  // if ( nrf5_sdk15_nfc_state.connected) { return RD_ERROR_INVALID_STATE; }
309  // If new data is not received, return not found
310  if (!nrf5_sdk15_nfc_state.rx_updated) { return RD_ERROR_NOT_FOUND; }
311 
312  rd_status_t err_code = RD_SUCCESS;
313 
314  // If we're at index 0, parse message into records
315  if (0 == nrf5_sdk15_nfc_state.msg_index)
316  {
317  uint32_t desc_buf_len = sizeof (nrf5_sdk15_nfc_state.desc_buf);
318  uint32_t data_lenu32 = sizeof (
319  nrf5_sdk15_nfc_state.nfc_ndef_msg); // Skip NFCT4T length bytes?
320  err_code = ndef_msg_parser (nrf5_sdk15_nfc_state.desc_buf,
321  &desc_buf_len,
322  nrf5_sdk15_nfc_state.nfc_ndef_msg + 2, // Skip NFCT4T length bytes
323  &data_lenu32);
324  // PLATFORM_LOG_INFO("Found %d messages", ((nfc_ndef_msg_desc_t*)desc_buf)->record_count);
325  // ndef_msg_printout((nfc_ndef_msg_desc_t*) nrf5_sdk15_nfc_state.desc_buf);
326  }
327 
328  // If there is a new message, parse the payload into Ruuvi Message.
329  if (nrf5_sdk15_nfc_state.msg_index < ( (nfc_ndef_msg_desc_t *)
330  nrf5_sdk15_nfc_state.desc_buf)->record_count)
331  {
332  // PLATFORM_LOG_INFO("Parsing message %d", msg_index);
333  nfc_ndef_record_desc_t * const p_rec_desc = ( (nfc_ndef_msg_desc_t *)
334  nrf5_sdk15_nfc_state.desc_buf)->pp_record[nrf5_sdk15_nfc_state.msg_index];
335  nfc_ndef_bin_payload_desc_t * p_bin_pay_desc = p_rec_desc->p_payload_descriptor;
336  // Data length check, skip header
337  size_t payload_len = (p_bin_pay_desc->payload_length - BIN_PAY_DESC_HEADER_LEN);
338 
339  if (payload_len > msg->data_length)
340  {
341  err_code = RD_ERROR_DATA_SIZE;
342  }
343  else
344  {
345  memcpy (msg->data, ( (uint8_t *) p_bin_pay_desc->p_payload + BIN_PAY_DESC_HEADER_LEN),
346  payload_len);
347  msg->data_length = payload_len;
348  }
349 
350  nrf5_sdk15_nfc_state.msg_index++;
351 
352  if (RD_SUCCESS == err_code) { err_code = RD_STATUS_MORE_AVAILABLE; }
353  }
354 
355  // If no more records could/can be parsed, reset buffer and message counter
356  if (RD_STATUS_MORE_AVAILABLE != err_code
357  || nrf5_sdk15_nfc_state.msg_index == ( (nfc_ndef_msg_desc_t *)
358  nrf5_sdk15_nfc_state.desc_buf)->record_count)
359  {
360  nrf5_sdk15_nfc_state.msg_index = 0;
361  nrf5_sdk15_nfc_state.rx_updated = false;
362 
363  // If tag is not writeable, restore original data
364  if (nrf5_sdk15_nfc_state.configurable)
365  {
366  ri_nfc_data_set();
367  }
368  }
369 
370  return err_code;
371 }
372 
373 #endif
#define RD_ERROR_NULL
Null Pointer.
uint32_t rd_status_t
bitfield for representing errors
#define RD_ERROR_INVALID_LENGTH
Invalid Length.
#define RD_STATUS_MORE_AVAILABLE
Driver has more data queued.
rd_status_t ruuvi_nrf5_sdk15_to_ruuvi_error(const ret_code_t error)
convert nrf5 sdk15 error code into Ruuvi error code.
#define RD_SUCCESS
Internal Error.
#define RD_ERROR_DATA_SIZE
Invalid Data size.
#define RD_ERROR_NOT_FOUND
Not found.
#define RD_ERROR_INVALID_STATE
Invalid state, operation disallowed in this state.
ret_code_t nfc_t4t_done(void)
Release reference to application callback.
ret_code_t nfc_t4t_ndef_rwpayload_set(uint8_t *p_emulation_buffer, size_t buffer_length)
Set emulation buffer and content for a NDEF Tag emulation that is Read/Writable.
ret_code_t nfc_t4t_setup(nfc_t4t_callback_t callback, void *p_context)
Register the application callback for event signaling.
nfc_t4t_event_t
Definition: nfc_t4t_lib.h:97
ret_code_t nfc_t4t_emulation_start(void)
Activate the NFC frontend.
ret_code_t nfc_t4t_emulation_stop(void)
Deactivate the NFC frontend.
@ NFC_T4T_EVENT_NDEF_UPDATED
External Reader has written to length information of NDEF-Data from Emulation.
Definition: nfc_t4t_lib.h:113
@ NFC_T4T_EVENT_FIELD_OFF
External Reader polling ended.
Definition: nfc_t4t_lib.h:104
@ NFC_T4T_EVENT_FIELD_ON
External Reader polling detected.
Definition: nfc_t4t_lib.h:101
@ NFC_T4T_EVENT_NDEF_READ
External Reader has read static NDEF-Data from Emulation.
Definition: nfc_t4t_lib.h:107
Header to enable and disable module compilation.
Ruuvi error codes and error check function.
rd_status_t(* ri_comm_evt_handler_fp_t)(const ri_comm_evt_t evt, void *p_data, size_t data_len)
Application event handler for communication events.
#define RI_COMM_DIS_STRLEN
Maximum length for device information strings.
@ RI_COMM_CONNECTED
Connection established, OK to send, may receive data.
@ RI_COMM_RECEIVED
New data received, available to read with read function.
@ RI_COMM_SENT
One queued message was sent with all repetitions.
@ RI_COMM_DISCONNECTED
Connection lost, cannot send, may not receive data.
rd_status_t ri_nfc_address_set(const uint8_t *const address, const uint8_t length)
#define RI_NFC_DATA_FIELD_CODE
#define RI_NFC_ID_FIELD_CODE
#define RI_NFC_ADDR_FIELD_CODE
rd_status_t ri_nfc_init(ri_comm_channel_t *const channel)
rd_status_t ri_nfc_data_set(void)
rd_status_t ri_nfc_send(ri_comm_message_t *messge)
rd_status_t ri_nfc_fw_version_set(const uint8_t *const version, const uint8_t length)
rd_status_t ri_nfc_receive(ri_comm_message_t *messge)
rd_status_t ri_nfc_uninit(ri_comm_channel_t *const channel)
#define RI_NFC_SW_FIELD_CODE
rd_status_t ri_nfc_id_set(const uint8_t *const id, const uint8_t length)
control API for communication via outside world
ri_comm_evt_handler_fp_t on_evt
Callback to application-level event handler, must be set in application.
ri_comm_xfer_fp_t send
Asynchronous send function.
ri_comm_init_fp_t init
Initialize and populate channel api control.
ri_comm_init_fp_t uninit
Uninitialize and depopulate channel api control.
ri_comm_xfer_fp_t read
Asynchronous read function.
Application message structure used for communication.
uint8_t data[RI_COMM_MESSAGE_MAX_LENGTH]
Data payload.
uint8_t data_length
Length of data.