
In diesem Teil des Tutorials fügen wir das Schreiben von Near Field Communication (NFC) vom Smartphone auf den RuuviTag hinzu. Den finalen Code dieses Blogbeitrags kannst du auf Ruuvi GitHub im ruuviblog-Branch, Tag 3.17.0-alpha, herunterladen.
Bitte folge Teil 1 der Serie für Details zum Klonen des Repositories und Kompilieren des Codes. Die finale Hex-Datei dieses Tutorials kann vom Ruuvi Jenkins heruntergeladen werden.
NFC-Schreibvorgänge
Zuvor haben wir das NFC-Lesen vom Tag in unserem Projekt implementiert. Das Hinzufügen des Schreibens auf den Tag ist eine relativ einfache Angelegenheit, bei der lediglich die Ereignisbehandlung und das Parsen von Daten hinzugefügt werden müssen. Die Struktur von NFC-Nachrichten erschwert die Arbeit jedoch ein wenig: Eine einzelne NFC–Nachricht hat mehrere Datensätze, und wir müssen die Datensätze aus der Nachricht parsen.
Nordic Semiconductor hat eine NFC-Parsing-Bibliothek, die uns die meiste Arbeit abnimmt. Der Einfachheit halber werden wir jeden Datensatz als Binärdatensatz behandeln. Eine zusätzliche Komplexität ergibt sich durch den Nordic NFC-Treiber: Die Daten auf dem Tag befinden sich in einem einzigen Puffer, und das Schreiben in den Puffer modifiziert Daten, die für den Leser eingerichtet sind.
Anhängen des NFC-Treibers
Unser NFC-Treiber hat eine Platzhalterfunktion zum Lesen empfangener Daten, die jedoch bisher einfach RUUVI_DRIVER_ERROR_NOT_IMPLEMENTED zurückgegeben hat.
Unsere implementierte Empfangsfunktion parst zuerst die Nachrichtenstruktur, um die Datensätze zu finden, und parst dann die Daten des ersten Datensatzes in einen gegebenen Zeiger. Die Funktion verfolgt den Datensatzindex und gibt bei nachfolgenden Aufrufen den nächsten Datensatz zurück, bis der letzte Datensatz erreicht ist. Wenn der letzte Datensatz geparst ist, werden die ursprünglichen Daten wiederhergestellt und der Datensatz-Zähler zurückgesetzt.
/* Read and parse RX buffer into records. Parse records into Ruuvi
* communication messages.
* Restore original data after last record has been parsed
*
* parameter msg: Ruuvi Communication message, received record payload is
* copied into message payload field.
*
* Return RUUVI_DRIVER_STATUS_MORE_AVAILABLE if payload was parsed into
* msg and more data is available
* Return RUUVI_SUCCESS if payload was parsed into msg
* and no more data is available
* Return RUUVI_DRIVER_ERROR_NOT_FOUND if no data was buffered and
* message could not be parsed.
* Return RUUVI_ERROR_DATA_SIZE if received message could not
* fit into message payload
*/
ruuvi_driver_status_t
ruuvi_interface_communication_nfc_receive(ruuvi_interface_communication_message_t* msg)
{
//Input check
if (NULL == msg) { return RUUVI_DRIVER_ERROR_NULL; }
// If new data is not received, return not found
if (!nrf5_sdk15_nfc_state.rx_updated) { return RUUVI_DRIVER_ERROR_NOT_FOUND; }
ruuvi_driver_status_t err_code = RUUVI_DRIVER_SUCCESS;
// If we're at index 0, parse message into records
if (0 == nrf5_sdk15_nfc_state.msg_index)
{
uint32_t desc_buf_len = sizeof(nrf5_sdk15_nfc_state.desc_buf);
uint32_t data_lenu32 = sizeof(nrf5_sdk15_nfc_state.nfc_ndef_msg);
//Skip NFCT4T length bytes with +2
err_code = ndef_msg_parser(nrf5_sdk15_nfc_state.desc_buf,
&desc_buf_len,
nrf5_sdk15_nfc_state.nfc_ndef_msg+2,
&data_lenu32);
//Debug print records
//ndef_msg_printout((nfc_ndef_msg_desc_t*) nrf5_sdk15_nfc_state.desc_buf);
}
// If there is a new message, parse the payload into Ruuvi Message.
if (nrf5_sdk15_nfc_state.msg_index <
((nfc_ndef_msg_desc_t*)nrf5_sdk15_nfc_state.desc_buf)->record_count)
{
// PLATFORM_LOG_INFO("Parsing message %d", msg_index);
nfc_ndef_record_desc_t* const p_rec_desc =
((nfc_ndef_msg_desc_t*)
nrf5_sdk15_nfc_state.desc_buf)->pp_record[nrf5_sdk15_nfc_state.msg_index];
nfc_ndef_bin_payload_desc_t* p_bin_pay_desc = p_rec_desc->p_payload_descriptor;
// Data length check
if (p_bin_pay_desc->payload_length > msg->data_length)
{ err_code = RUUVI_DRIVER_ERROR_DATA_SIZE; }
else {
memcpy(msg->data,
(uint8_t*)p_bin_pay_desc->p_payload,
p_bin_pay_desc->payload_length);
msg->data_length = p_bin_pay_desc->payload_length;
}
nrf5_sdk15_nfc_state.msg_index++;
if(RUUVI_DRIVER_SUCCESS == err_code)
{ err_code = RUUVI_DRIVER_STATUS_MORE_AVAILABLE; }
}
// If no more records could/can be parsed, reset buffer and message counter
if (RUUVI_DRIVER_STATUS_MORE_AVAILABLE != err_code ||
nrf5_sdk15_nfc_state.msg_index ==
((nfc_ndef_msg_desc_t*)nrf5_sdk15_nfc_state.desc_buf)->record_count)
{
nrf5_sdk15_nfc_state.msg_index = 0;
nrf5_sdk15_nfc_state.rx_updated = false;
// If tag is not writeable, restore original data
if(nrf5_sdk15_nfc_state.configurable)
{
ruuvi_interface_communication_nfc_data_set();
}
}
return err_code;
}Umgang mit Schreibvorgängen in der Anwendung
Die Integration der Änderungen in unsere Anwendung ist einfach. Wir behandeln den Callback vom Treiber und planen das Parsen der Daten in task_nfc.c. Sobald die Daten geparst sind, geben wir sie aus. Der aufmerksame Leser bemerkt, dass wir auch den Watchdog füttern, wenn Daten über NFC vom Tag gelesen werden.
void task_acceleration_scheduler_task(void *p_event_data, uint16_t event_size)
{
// Message + null + <\r>\<n>
char str[APPLICATION_COMMUNICATION_NFC_TEXT_BUFFER_SIZE + 3] = { 0 };
ruuvi_interface_communication_message_t message = {0};
ruuvi_driver_status_t err_code = RUUVI_DRIVER_SUCCESS;
do{
message.data_length = sizeof(message.data);
memset(&(message.data), 0, sizeof(message.data));
err_code = channel.read(&message);
snprintf(str, sizeof(str), "%s\r\n", (char *)message.data);
ruuvi_platform_log(RUUVI_INTERFACE_LOG_INFO, str);
}while(RUUVI_DRIVER_SUCCESS == err_code ||
RUUVI_DRIVER_STATUS_MORE_AVAILABLE == err_code);
}
ruuvi_driver_status_t
task_nfc_on_nfc(ruuvi_interface_communication_evt_t evt, void* p_data, size_t data_len)
{
ruuvi_driver_status_t err_code = RUUVI_DRIVER_SUCCESS;
switch(evt)
{
case RUUVI_INTERFACE_COMMUNICATION_CONNECTED:
ruuvi_platform_log(RUUVI_INTERFACE_LOG_INFO, "NFC connected \r\n");
break;
case RUUVI_INTERFACE_COMMUNICATION_DISCONNECTED:
ruuvi_platform_log(RUUVI_INTERFACE_LOG_INFO, "NFC disconnected \r\n");
break;
case RUUVI_INTERFACE_COMMUNICATION_SENT:
ruuvi_platform_log(RUUVI_INTERFACE_LOG_INFO, "NFC data sent\r\n");
ruuvi_interface_watchdog_feed();
break;
case RUUVI_INTERFACE_COMMUNICATION_RECEIVED:
ruuvi_platform_log(RUUVI_INTERFACE_LOG_INFO, "NFC data received\r\n");
ruuvi_platform_scheduler_event_put(NULL, 0, task_acceleration_scheduler_task);
break;
default:
break;
}
return err_code;
}Um das Programm zu testen, verwenden wir NFC Tools, um 4 Textdatensätze mit der Nachricht „Hello world“ zu schreiben.

Daten kommen gut durch!
Die Firmware ein wenig stylen
Um die Serie mit etwas Brauchbarem abzuschließen, stellen wir die Datenformat-5-Broadcasts wieder her und kommentieren das GATT aus. Wir fügen auch einen Watchdog-Feed bei gesendeten Advertisements hinzu, wenn keine Fehler aufgetreten sind.
Mal sehen, wie der Stromverbrauch aussieht!

Der Verbrauch von 27 μA ist sehr gut vergleichbar mit dem 2.X-Zweig, der mit den gleichen Funktionen 29 μA verbraucht. Ein so geringer Unterschied liegt innerhalb der Fehlermarge der Messung, sodass wir nicht mit Sicherheit sagen können, ob es eine echte Energieeinsparung gibt.
Fazit
In den letzten 17 Wochen haben wir eine komplette Sensor-Beacon-Software von Grund auf neu aufgebaut, mit leicht erweiterbaren Schnittstellen für verschiedene Sensoren und Boards. Die Arbeit ist jedoch noch lange nicht abgeschlossen. Unsere nächsten Schritte werden sein, eine bessere Dokumentation und Tests zu erstellen sowie sicherzustellen, dass das Benennungsschema und das Verhalten der Funktionen konsistent sind.
Sobald wir ziemlich sicher sein können, dass die definierten Schnittstellen stabil sein werden, können wir die Firmware aus der Alpha-Phase herausbringen und die verbleibenden Fehler beheben – das könnte eine Weile dauern. In der Zwischenzeit freuen wir uns immer über deine Erfahrungen beim Aufbau deines Projekts auf Basis von Ruuvi.
Dies ist der letzte wöchentliche Teil des Blogs, aber die Serie wird hin und wieder aktualisiert.
Bleib dran und folge @ojousima und @ruuvicom auf Twitter für #FirmwareFriday-Beiträge!