{"id":135631,"date":"2018-11-09T11:39:00","date_gmt":"2018-11-09T09:39:00","guid":{"rendered":"https:\/\/ruuvi.com\/ruuvi-firmware-teil-16-ble-gatt-verbindung\/"},"modified":"2026-06-11T07:27:30","modified_gmt":"2026-06-11T04:27:30","slug":"ruuvi-firmware-teil-16-ble-gatt-verbindung","status":"publish","type":"post","link":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-teil-16-ble-gatt-verbindung\/","title":{"rendered":"Ruuvi Firmware \u2013 Teil 16: BLE GATT-Verbindung"},"content":{"rendered":"\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"536\" src=\"https:\/\/ruuvi.com\/i\/u\/ruuvi-firmware-series-part-16-1024x536.jpeg\" alt=\"Ruuvi Firmware Serie Teil 16 Intro-Bild\" class=\"wp-image-3930\" srcset=\"https:\/\/ruuvi.com\/i\/u\/ruuvi-firmware-series-part-16-1024x536.jpeg 1024w, https:\/\/ruuvi.com\/i\/u\/ruuvi-firmware-series-part-16-450x236.jpeg 450w, https:\/\/ruuvi.com\/i\/u\/ruuvi-firmware-series-part-16-768x402.jpeg 768w, https:\/\/ruuvi.com\/i\/u\/ruuvi-firmware-series-part-16-600x314.jpeg 600w, https:\/\/ruuvi.com\/i\/u\/ruuvi-firmware-series-part-16.jpeg 1400w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure><\/div>\n\n<p class=\"wp-block-paragraph\">In diesem Teil des Tutorials f\u00fcgen wir unserem Projekt ein <em>Generic Attributes (<\/em><strong>GATT)<\/strong>-Profil hinzu. Der finale Code dieses Blogbeitrags kann auf <a href=\"https:\/\/github.com\/ruuvi\/ruuvi.firmware.c\">Ruuvi GitHub<\/a> im <em>ruuviblog<\/em>-Branch, Tag <em>3.16.0-alpha<\/em>, heruntergeladen werden. <\/p>\n\n<p class=\"wp-block-paragraph\">Folge bitte <a href=\"https:\/\/ruuvi.com\/ruuvi-firmware-part-1-sleep\/\">Teil 1 der Serie<\/a> f\u00fcr Details zum Klonen des Repositorys und Kompilieren des Codes. Die finale Hex-Datei dieses Tutorials kann von <a href=\"http:\/\/jenkins.ruuvi.com\/job\/ruuvi.firmware.c\/41\/\">Ruuvi Jenkins<\/a> heruntergeladen werden. <\/p>\n\n<h2 class=\"wp-block-heading\"><strong>Generic Attributes f\u00fcr Ruuvi<\/strong><\/h2>\n\n<p class=\"wp-block-paragraph\"><em>\u201eDie Generic Attributes (GATT) definieren eine hierarchische Datenstruktur, die verbundenen Bluetooth Low Energy (LE)-Ger\u00e4ten zur Verf\u00fcgung gestellt wird.\u201c \u2014 Bluetooth.com<\/em><\/p>\n\n<p class=\"wp-block-paragraph\">Die <strong>GATT<\/strong>-Profile wurden von <a href=\"https:\/\/learn.adafruit.com\/introduction-to-bluetooth-low-energy\/gatt\">Adafruit<\/a> gut erkl\u00e4rt. Schau dir bitte deren Tutorial f\u00fcr eine detailliertere Erkl\u00e4rung an, was <strong>GATT<\/strong> ist.<\/p>\n\n<p class=\"wp-block-paragraph\">Auf dem RuuviTag implementieren wir ein einfaches <em>Profil<\/em> mit 3 <em>Services:<\/em><\/p>\n\n<ul class=\"wp-block-list\"><li>Device Firmware Update Service (<strong>DFU<\/strong>)<\/li><li>Device Information Service (<strong>DIS<\/strong>)<\/li><li>Nordic UART Service (<strong>NUS<\/strong>)<\/li><\/ul>\n\n<p class=\"wp-block-paragraph\">Der Device Firmware Update Service erm\u00f6glicht es dem Tag, den Bootloader ohne physische Benutzerinteraktion zu starten.<\/p>\n\n<p class=\"wp-block-paragraph\">Der Device Information Service stellt grundlegende Informationen \u00fcber das Ger\u00e4t bereit, wie Hersteller, Hardwareversion und Firmwareversion.<\/p>\n\n<p class=\"wp-block-paragraph\">Der Nordic UART Service wird verwendet, um eine bidirektionale Kommunikation zwischen RuuviTag und einem anderen Ger\u00e4t zu erm\u00f6glichen.<\/p>\n\n<h2 class=\"wp-block-heading\">GATT-Schnittstelle<\/h2>\n\n<p class=\"wp-block-paragraph\">Unsere <em>Schnittstelle<\/em> ist einfach genug: Der <strong>DFU<\/strong>-Service muss nur ohne Parameter initialisiert werden, der <strong>DIS<\/strong>-Service ben\u00f6tigt Strings f\u00fcr die anzuzeigenden Daten und <strong>NUS<\/strong> verwendet die zuvor definierte <a href=\"https:\/\/ruuvi.com\/ruuvi-firmware-part-9-bluetooth-broadcasting\/\"><em>Kommunikationskanal-Schnittstelle<\/em><\/a>. Zus\u00e4tzlich haben wir eine gemeinsame Initialisierungsfunktion, die die <strong>GATT<\/strong>-Parameter einrichtet. <\/p>\n\n<p class=\"wp-block-paragraph\">Unter der Haube steckt eine Menge Komplexit\u00e4t, etwa Verbindungsintervalle und deren Aushandlung, das Supervisory Timeout der Verbindung und so weiter. Aber wie zuvor wollen wir alles so einfach wie irgend m\u00f6glich halten und diese Einstellungen als \u201evern\u00fcnftige Standardwerte\u201c belassen. <\/p>\n\n<pre class=\"wp-block-code\"><code>#define HCI_ERROR_CODE_CONN_TERM_BY_LOCAL_HOST 0x16\n\ntypedef struct\n{\n char fw_version&#091;32];\n char model&#091;32];\n char hw_version&#091;32];\n char manufacturer&#091;32];\n char deviceid&#091;32];\n}ruuvi_interface_communication_ble4_gatt_dis_init_t;\n\n\/**\n * Initializes GATT stack. Uses default values from sdk_config.h, \n * these can be overridden in nrf5_sdk15_application_config.h\n *\n * Returns RUUVI_DRIVER_ERROR_INVALID_STATE \n *         if radio module is not initialized \n *         with handle RUUVI_INTERFACE_COMMUNICATION_RADIO_GATT\n *\n *\n *\/\nruuvi_driver_status_t ruuvi_interface_communication_ble4_gatt_init(void);\n\n\/**\n * Initialize Nordic UART Service as a communication channel.\n * ruuvi_interface_communication_radio_init(RUUVI_INTERFACE_COMMUNICATION_RADIO_GATT) \n * must be called before initializing service\n *\n * Parameter channel: Pointer to communication interface which will be populated. \n *                    Pointer will be copied, the structure\n *                    must be retained. Adding any event handler to structure after \n *                    initialization will take effect immediately\n * Returns RUUVI_DRIVER_SUCCESS on success\n * Returns RUUVI_DRIVER_ERROR_INVALID_STATE \n *         if radio module is not initialized with handle \n *         RUUVI_INTERFACE_COMMUNICATION_RADIO_GATT\n *         or if ruuvi_interface_communication_ble4_gatt_init\n *         has not been called.\n * Returns error code from stack in case there is other error.\n *\/\nruuvi_driver_status_t ruuvi_interface_communication_ble4_gatt_nus_init\n                      (ruuvi_interface_communication_t* const channel);\n\n\/**\n * Initialize BLE4 Device firmware update service.\n *\n * Returns RUUVI_DRIVER_SUCCESS on success\n * Returns error code from stack in case there is  error.\n *\/\nruuvi_driver_status_t ruuvi_interface_communication_ble4_gatt_dfu_init(void);\n\n\/**\n * Initialize BLE4 Device Information service\n *\n * parameter dis: pointer to data which should be presented over DIS.\n *                Memory will be deep-copied\n * Returns RUUVI_DRIVER_SUCCESS on success\n * Returns error code from stack in case there is  error.\n *\/\nruuvi_driver_status_t ruuvi_interface_communication_ble4_gatt_dis_init\n             (const ruuvi_interface_communication_ble4_gatt_dis_init_t* const dis);\n\n\/**\n * Start or stop advertising GATT connection.\n *\n * parameter connectable: True to start advertising connectablity \n *                        False to stop advertising connectablity\n * parameter name: Name of the device to be advertised.\n * parameter advertise_nus: True to enable advertising UUID of NUS in the scan response.\n * Returns RUUVI_DRIVER_SUCCESS on success\n * Returns error code from stack in case there is  error.\n *\n *\/\nruuvi_driver_status_t ruuvi_interface_communication_ble4_gatt_advertise_connectablity\n                      (const bool connectable, \n                       const char* const name, \n                       const bool advertise_nus);\n\n#endif<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/gist.githubusercontent.com\/ojousima\/42a1cc288cca377856a1e4c6bae10719\/raw\/fdfa8218922bc8d31974d3a44947a3eb419e37fa\/ruuvi_interface_communication_ble4_gatt.h\">Quelltext anzeigen<\/a><\/p>\n\n<h2 class=\"wp-block-heading\">GATT-Implementierung<\/h2>\n\n<p class=\"wp-block-paragraph\">Gl\u00fccklicherweise hat Nordic Semiconductor die Hauptarbeit geleistet. Die Initialisierung von <strong>GATT<\/strong> selbst erfordert etwas \u00dcberlegung. Vorerst hardcodieren wir Standardwerte f\u00fcr Dinge wie das Verbindungsintervall in <em>ruuvi_platform_communication_ble4_gatt.c<\/em>. <\/p>\n\n<p class=\"wp-block-paragraph\">Das Hinzuf\u00fcgen der <strong>DFU<\/strong>&#8211; und <strong>DIS<\/strong>-Services ist eine triviale Angelegenheit, bei der einfach die Funktionen des <em>Software Development Kit<\/em> (<strong>SDK)<\/strong> aufgerufen werden. <strong>NUS<\/strong> ist etwas kniffliger, da wir <strong>NUS<\/strong> mit unserer Anwendung verbinden m\u00fcssen. Wir verwenden die zuvor definierte <em>Kommunikationsschnittstelle<\/em>, die Folgendes erfordert: <\/p>\n\n<ul class=\"wp-block-list\"><li>Init \u2013 Zeile 1<\/li><li>Uninit \u2013 Zeile 25<\/li><li>Send \u2013 Zeile 43<\/li><li>Read \u2013 Zeile 64<\/li><li>Event Handler \u2013 Zeile 70<\/li><\/ul>\n\n<p class=\"wp-block-paragraph\">Zuvor wurde die Designentscheidung getroffen, nur einen Modus der Funkkommunikation gleichzeitig zuzulassen. Daher f\u00fcgen wir eine Funktion hinzu, um die Verbindung zu nahegelegenen Ger\u00e4ten zu bewerben, anstatt die zuvor erstellte Advertising-Schnittstelle zu verwenden \u2013 Zeile 113. <\/p>\n\n<pre class=\"wp-block-code\"><code>ruuvi_driver_status_t ruuvi_interface_communication_ble4_gatt_nus_init\n                     (ruuvi_interface_communication_t* const _channel)\n{\n  if(NULL == _channel) { return RUUVI_DRIVER_ERROR_NULL; }\n  uint32_t           err_code;\n  ble_nus_init_t     nus_init;\n\n  \/\/ Initialize NUS.\n  memset(&amp;nus_init, 0, sizeof(nus_init));\n  nus_init.data_handler = nus_data_handler;\n  err_code = ble_nus_init(&amp;m_nus, &amp;nus_init);\n  channel = _channel;\n  channel-&gt;init   = ruuvi_interface_communication_ble4_gatt_nus_init;\n  channel-&gt;uninit = ruuvi_interface_communication_ble4_gatt_nus_uninit;\n  channel-&gt;send   = ruuvi_interface_communication_ble4_gatt_nus_send;\n  channel-&gt;read   = ruuvi_interface_communication_ble4_gatt_nus_read;\n\n  m_gatt_is_init = true;\n  return ruuvi_platform_to_ruuvi_error(&amp;err_code);\n}\n\n\/**\n *\n *\/\nstatic ruuvi_driver_status_t ruuvi_interface_communication_ble4_gatt_nus_uninit\n                            (ruuvi_interface_communication_t* const _channel)\n{\n  if(NULL == _channel) { return RUUVI_DRIVER_ERROR_NULL; }\n  memset(_channel, 0, sizeof(ruuvi_interface_communication_t));\n  m_gatt_is_init = false;\n  \/\/ disconnect\n  if(BLE_CONN_HANDLE_INVALID != m_conn_handle)\n  {\n    sd_ble_gap_disconnect(m_conn_handle, HCI_ERROR_CODE_CONN_TERM_BY_LOCAL_HOST);\n  }\n  \/\/ Services cannot be uninitialized\n  return RUUVI_DRIVER_SUCCESS;\n}\n\n\/**\n *\n *\/\nstatic ruuvi_driver_status_t ruuvi_interface_communication_ble4_gatt_nus_send\n                            (ruuvi_interface_communication_message_t* const message)\n{\n  if(NULL == message) { return RUUVI_DRIVER_ERROR_NULL; }\n  if(BLE_NUS_MAX_DATA_LEN &lt; message-&gt;data_length) { return RUUVI_DRIVER_ERROR_DATA_SIZE; }\n  if(BLE_CONN_HANDLE_INVALID == m_conn_handle) { return RUUVI_DRIVER_ERROR_INVALID_STATE; }\n\n  if(message-&gt;repeat)\n  {\n    return RUUVI_DRIVER_ERROR_NOT_IMPLEMENTED;\n  }\n\n  ret_code_t err_code = NRF_SUCCESS;\n  uint16_t data_len = message-&gt;data_length;\n  err_code |= ble_nus_data_send(&amp;m_nus, message-&gt;data, &amp;data_len, m_conn_handle);\n  return ruuvi_platform_to_ruuvi_error(&amp;err_code);\n}\n\n\/**\n *\n *\/\nstatic ruuvi_driver_status_t ruuvi_interface_communication_ble4_gatt_nus_read\n                            (ruuvi_interface_communication_message_t* const message)\n{\n  return RUUVI_DRIVER_ERROR_NOT_SUPPORTED;\n}\n\nstatic void nus_data_handler(ble_nus_evt_t * p_evt)\n{\n  if(NULL == channel ||\n     NULL == channel-&gt;on_evt)\n  {\n    return;\n  }\n\n  switch(p_evt-&gt;type)\n  {\n    case BLE_NUS_EVT_RX_DATA:\n      channel-&gt;on_evt(RUUVI_INTERFACE_COMMUNICATION_RECEIVED, \n                      (void*)p_evt-&gt;params.rx_data.p_data, \n                      p_evt-&gt;params.rx_data.length);\n      break;\n\n    case BLE_NUS_EVT_COMM_STARTED:\n      channel-&gt;on_evt(RUUVI_INTERFACE_COMMUNICATION_CONNECTED, NULL, 0);\n      break;\n\n    case BLE_NUS_EVT_COMM_STOPPED:\n      channel-&gt;on_evt(RUUVI_INTERFACE_COMMUNICATION_DISCONNECTED, NULL, 0);\n      break;\n\n    case BLE_NUS_EVT_TX_RDY:\n      channel-&gt;on_evt(RUUVI_INTERFACE_COMMUNICATION_SENT, NULL, 0);\n      break;\n\n    default:\n      break;\n  }\n}\n\n\/**\n * Start or stop advertising GATT connection.\n *\n * parameter connectable:   True to start advertising connectablity \n *                          False to stop advertising connectablity\n * parameter name:          Name of the device to be advertised.\n * parameter advertise_nus: True to enable advertising \n *                          UUID of NUS in the scan response.\n *\n *\/\nruuvi_driver_status_t ruuvi_interface_communication_ble4_gatt_advertise_connectablity\n  (const bool connectable, \n   const char* const name, \n   const bool advertise_nus)\n{\n  ret_code_t err_code = NRF_SUCCESS;\n  if(!connectable)\n  {\n    err_code |= sd_ble_gap_adv_stop(m_adv_handle);\n    return ruuvi_platform_to_ruuvi_error(&amp;err_code);\n  }\n\n\/\/ Initialize advertising parameters (used when starting advertising).\n  memset(&amp;m_adv_params, 0, sizeof(m_adv_params));\n  m_adv_params.filter_policy   = BLE_GAP_ADV_FP_ANY;\n  m_adv_params.duration        = 0;       \/\/ Never time out.\n  m_adv_params.properties.type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED;\n  m_adv_params.p_peer_addr     = NULL;    \/\/ Undirected advertisement.\n  m_adv_params.interval        = \n    MSEC_TO_UNITS(APPLICATION_CONNECTION_ADVERTISEMENT_INTERVAL, UNIT_0_625_MS);\n\n  \/\/ OPEN security for name\n  ble_gap_conn_sec_mode_t security;\n  uint8_t len = strlen(name);\n  security.sm = 1;\n  security.lv = 1;\n  err_code |= sd_ble_gap_device_name_set(&amp;security, (uint8_t*)name, len);\n\n  memset(&amp;m_adv_data, 0, sizeof(m_adv_data));\n  memset(&amp;m_advertisement, 0, sizeof(m_advertisement));\n  memset(&amp;m_scanresp, 0, sizeof(m_scanresp));\n  ble_advdata_t advdata = {0};\n  \/\/ Only valid flag\n  advdata.flags     = BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED;\n  \/\/ Name will be read from the above GAP data\n  advdata.name_type = BLE_ADVDATA_FULL_NAME;\n\n  \/\/ Encode data\n  m_adv_data.adv_data.len = sizeof(m_advertisement);\n  err_code |= ble_advdata_encode(&amp;advdata, \n                                 m_advertisement, \n                                 &amp;m_adv_data.adv_data.len);\n  m_adv_data.adv_data.p_data = m_advertisement;\n\n  \/\/ Add scan response if requested\n  if(advertise_nus)\n  {\n    ble_advdata_t scanrsp = {0};\n    scanrsp.uuids_complete.uuid_cnt = 1;\n    scanrsp.uuids_complete.p_uuids = &amp;(m_adv_uuids&#091;0]);\n    \/\/ Encode data\n    m_adv_data.scan_rsp_data.len = sizeof(m_scanresp);\n    err_code |= ble_advdata_encode(&amp;scanrsp, \n                                   m_scanresp, \n                                   &amp;m_adv_data.scan_rsp_data.len);\n    m_adv_data.scan_rsp_data.p_data = m_scanresp;\n  }\n\n  \/\/ Configure advertisement and start\n  err_code |= sd_ble_gap_adv_set_configure(&amp;m_adv_handle, \n                                           &amp;m_adv_data, \n                                           &amp;m_adv_params);\n  err_code |= sd_ble_gap_adv_start(m_adv_handle, NRF5_SDK15_BLE4_STACK_CONN_TAG);\n\n  return ruuvi_platform_to_ruuvi_error(&amp;err_code);\n}<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/gist.githubusercontent.com\/ojousima\/631bea695f15e70966d99a29bcd3b530\/raw\/4a0b731f8309ad2a2c5907d3e72bf5dee46001fd\/ruuvi_platform_communication_ble4_gatt.c\">Quelltext anzeigen<\/a><\/p>\n\n<p class=\"wp-block-paragraph\">Es sollte beachtet werden, dass die <em>read<\/em>-Funktion vorerst nur einen Fehler zur\u00fcckgibt. Das liegt daran, dass wir das externe Ger\u00e4t nicht nach Daten abfragen k\u00f6nnen, sondern nur empfangene Datenereignisse verarbeiten k\u00f6nnen. Die Kanalspezifikation w\u00fcrde die Daten puffern und die Anwendung k\u00f6nnte sie zu einem sp\u00e4teren Zeitpunkt verarbeiten, aber das implementieren wir sp\u00e4ter. Wir haben auch noch keine wiederholten Sendevorg\u00e4nge implementiert.   <\/p>\n\n<h2 class=\"wp-block-heading\">Integration von GATT in die Anwendung<\/h2>\n\n<p class=\"wp-block-paragraph\">Zuvor haben wir angedeutet, <strong>GATT<\/strong> zu verwenden, um Daten vom Beschleunigungssensor mit h\u00f6herer Rate zu streamen. Lass uns das Beschleunigungssensor-<strong>FIFO<\/strong>-Streaming implementieren. Wir kommentieren die FIFO- und Aktivit\u00e4tsinterrupt-Initialisierung beim Beschleunigungssensor-Boot aus und f\u00fcgen Beschleunigungssensor-Tasks hinzu, um den <strong>FIFO<\/strong>-Puffer und Interrupt zu aktivieren und zu deaktivieren.  <\/p>\n\n<p class=\"wp-block-paragraph\">Der Beschleunigungssensor-<strong>FIFO<\/strong>-Task wird modifiziert, um die Daten im ASCII-Format mit explizitem Vorzeichen, einer Ziffer und zwei Nachkommastellen sowie Semikolon zu senden, z. B.<\/p>\n\n<pre class=\"wp-block-code\"><code>+0.08;+0.04;+1.04<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">Dies passt in die 20 Bytes einer einzelnen <strong>GATT<\/strong>-\u00dcbertragung.<\/p>\n\n<pre class=\"wp-block-code\"><code>static void task_acceleration_fifo_full_task(void *p_event_data, uint16_t event_size)\n{\n  ruuvi_driver_status_t err_code = RUUVI_DRIVER_SUCCESS;\n  ruuvi_interface_acceleration_data_t data&#091;32];\n  size_t data_len = sizeof(data);\n  err_code |= ruuvi_interface_lis2dh12_fifo_read(&amp;data_len, data);\n  char msg&#091;APPLICATION_LOG_BUFFER_SIZE] = { 0 };\n  for(int ii = 0; ii &lt; data_len; ii++)\n  {\n    memset(msg, 0, sizeof(msg));\n    snprintf(msg, 21,\"%+4.2f;%+4.2f;%+4.2f\", data&#091;ii].x_g, data&#091;ii].y_g, data&#091;ii].z_g );\n    ruuvi_interface_communication_message_t gatt_msg = { 0 };\n    memcpy(gatt_msg.data, msg, 20);\n    gatt_msg.data_length = 20;\n    \/\/ Loop here until data is sent\n    while(RUUVI_DRIVER_SUCCESS != task_gatt_send(&amp;gatt_msg));\n  }\n  RUUVI_DRIVER_ERROR_CHECK(err_code, RUUVI_DRIVER_SUCCESS);\n}\n\nruuvi_driver_status_t task_acceleration_fifo_use(const bool enable)\n{\n  ruuvi_driver_status_t err_code = RUUVI_DRIVER_SUCCESS;\n  if(true == enable)\n  {\n    err_code |= ruuvi_interface_lis2dh12_fifo_use(true);\n    err_code |= ruuvi_interface_lis2dh12_fifo_interrupt_use(true);\n  }\n  if(false == enable)\n  {\n    err_code |= ruuvi_interface_lis2dh12_fifo_use(false);\n    err_code |= ruuvi_interface_lis2dh12_fifo_interrupt_use(false);\n  }\n}<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/gist.githubusercontent.com\/ojousima\/30795020b74fc2dffb84c958599cb56f\/raw\/ae5626382448fd44ebd260c0941df60077fb3848\/ruuvi_firmware_task_acceleration_3.16.c\">Quelltext anzeigen<\/a><\/p>\n\n<p class=\"wp-block-paragraph\">Wir initialisieren den Advertising-Task nicht in unserer Hauptinitialisierung, sondern initialisieren stattdessen den <strong>GATT<\/strong>-Task und starten das Bewerben der Verbindung im <strong>GATT<\/strong>-Modul. Die Initialisierung erfordert Daten f\u00fcr den <strong>DIS<\/strong> sowie einen Event-Handler f\u00fcr die <strong>NUS<\/strong>-Events. <\/p>\n\n<p class=\"wp-block-paragraph\">Ein \u201eStolperstein\u201c, auf den du achten solltest, ist das Trennen der Verbindung. Das Advertising l\u00e4uft standardm\u00e4\u00dfig nicht weiter; wir m\u00fcssen das Advertising f\u00fcr die Verbindung in Zeile 80 manuell neu starten, wenn wir uns erneut mit dem Tag verbinden m\u00f6chten. <\/p>\n\n<p class=\"wp-block-paragraph\">Wenn wir schon dabei sind, lassen wir den <a href=\"https:\/\/ruuvi.com\/ruuvi-firmware-part-15-watchdog\/\"><em>Watchdog<\/em><\/a> etwas N\u00fctzliches tun: Wir <em>f\u00fcttern<\/em> den <em>Watchdog<\/em> beim Ereignis \u201edata sent\u201c in Zeile 85. Wenn wir die Verbindung zum Tag verlieren oder aus irgendeinem Grund keine Daten mehr gesendet werden k\u00f6nnen, startet das Tag nach 120 Sekunden neu. <\/p>\n\n<pre class=\"wp-block-code\"><code>\n\nstatic ruuvi_interface_communication_t channel;\n\nruuvi_driver_status_t task_gatt_init(void)\n{\n  ruuvi_driver_status_t err_code = RUUVI_DRIVER_SUCCESS;\n  ruuvi_interface_communication_ble4_gatt_dis_init_t dis = {0};\n  uint64_t id;\n  err_code |= ruuvi_interface_communication_id_get(&amp;id);\n  size_t index = 0;\n  for (size_t ii = 0; ii &lt; 8; ii ++)\n  {\n    index += snprintf(dis.deviceid + index, \n                      sizeof(dis.deviceid)-index, \n                      \"%02X\", \n                      (uint8_t)(id &gt;&gt; ((7 - ii) * 8)) &amp; 0xFF);\n    if(ii &lt; 7) \n    { \n      index += snprintf(dis.deviceid + index, \n                        sizeof(dis.deviceid)-index, \n                        \":\"); \n    }\n  }\n  memcpy(dis.fw_version, \n         APPLICATION_FW_VERSION, \n         sizeof(APPLICATION_FW_VERSION));\n  memcpy(dis.model, \n         RUUVI_BOARD_MODEL_STRING, \n         sizeof(RUUVI_BOARD_MODEL_STRING));\n  memcpy(dis.manufacturer, \n         RUUVI_BOARD_MANUFACTURER_STRING, \n         sizeof(RUUVI_BOARD_MANUFACTURER_STRING));\n\n  err_code |= ruuvi_interface_communication_radio_init\n    (RUUVI_INTERFACE_COMMUNICATION_RADIO_GATT);\n  \/\/ Init fails if something else - such as advertising - as reserved radio.\n  if(RUUVI_DRIVER_SUCCESS != err_code) { return err_code; }\n  err_code |= ruuvi_interface_communication_ble4_gatt_init();\n  RUUVI_DRIVER_ERROR_CHECK(err_code, RUUVI_DRIVER_SUCCESS);\n\n  err_code |= ruuvi_interface_communication_ble4_gatt_nus_init(&amp;channel);\n  channel.on_evt = task_gatt_on_gatt;\n  RUUVI_DRIVER_ERROR_CHECK(err_code, RUUVI_DRIVER_SUCCESS);\n\n  err_code |= ruuvi_interface_communication_ble4_gatt_dfu_init();\n  RUUVI_DRIVER_ERROR_CHECK(err_code, RUUVI_DRIVER_SUCCESS);\n\n  err_code |= ruuvi_interface_communication_ble4_gatt_dis_init(&amp;dis);\n  RUUVI_DRIVER_ERROR_CHECK(err_code, RUUVI_DRIVER_SUCCESS);\n\n  err_code |= \n    ruuvi_interface_communication_ble4_gatt_advertise_connectablity(true, \"Ruuvi\", true);\n  RUUVI_DRIVER_ERROR_CHECK(err_code, RUUVI_DRIVER_SUCCESS);\n\n  return err_code;\n}\n\nruuvi_driver_status_t \ntask_gatt_on_gatt(ruuvi_interface_communication_evt_t evt, \n                  void* p_data, \n                  size_t data_len)\n{\n  ruuvi_driver_status_t err_code = RUUVI_DRIVER_SUCCESS;\n  char str&#091;21] = { 0 };\n  switch(evt)\n  {\n    case RUUVI_INTERFACE_COMMUNICATION_CONNECTED:\n      ruuvi_platform_log(RUUVI_INTERFACE_LOG_INFO, \"Connected \\r\\n\");\n      err_code |= task_acceleration_fifo_use(true);\n      ruuvi_interface_communication_message_t msg = {0};\n      memcpy(msg.data, \"Hello! Here's data!\", 20);\n      msg.data_length = 20;\n      channel.send(&amp;msg);\n      break;\n\n    case RUUVI_INTERFACE_COMMUNICATION_DISCONNECTED:\n      ruuvi_platform_log(RUUVI_INTERFACE_LOG_INFO, \"Disonnected \\r\\n\");\n      err_code |= task_acceleration_fifo_use(false);\n      ruuvi_interface_communication_ble4_gatt_advertise_connectablity\n        (true, \"Ruuvi\", true);\n      break;\n\n    case RUUVI_INTERFACE_COMMUNICATION_SENT:\n      ruuvi_interface_watchdog_feed();\n      break;\n\n    case RUUVI_INTERFACE_COMMUNICATION_RECEIVED:\n      snprintf(str, data_len, \"%s\", p_data);\n      ruuvi_platform_log(RUUVI_INTERFACE_LOG_INFO, str);\n      break;\n\n    default:\n      break;\n\n  }\n  return err_code;\n}\n\nruuvi_driver_status_t \ntask_gatt_send(ruuvi_interface_communication_message_t* const msg)\n{\n  if(NULL == msg)          { return RUUVI_DRIVER_ERROR_NULL; }\n  if(NULL == channel.send) { return RUUVI_DRIVER_ERROR_INVALID_STATE; }\n\n  return channel.send(msg);\n}<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/gist.githubusercontent.com\/ojousima\/ceec514328c1b83cb194d10a1c9a9313\/raw\/5d6e1725267a26cd80e26db52a745fe81e35e21d\/ruuvi_firmware_task_gatt_3.16.c\">Quelltext anzeigen<\/a><\/p>\n\n<p class=\"wp-block-paragraph\">Probieren wir es aus. Das Tag wird als \u201eRuuvi\u201c beworben; wir k\u00f6nnen uns \u00fcber <a href=\"https:\/\/www.nordicsemi.com\/eng\/Products\/Nordic-mobile-Apps\/nRF-Connect-for-Mobile\"><em>nRF Connect<\/em><\/a> damit verbinden. <\/p>\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"576\" height=\"1024\" src=\"https:\/\/ruuvi.com\/i\/u\/nrf-connect-dis-service-576x1024.png\" alt=\"nRF Connect DIS-Service\" class=\"wp-image-3935\" srcset=\"https:\/\/ruuvi.com\/i\/u\/nrf-connect-dis-service-576x1024.png 576w, https:\/\/ruuvi.com\/i\/u\/nrf-connect-dis-service-253x450.png 253w, https:\/\/ruuvi.com\/i\/u\/nrf-connect-dis-service-600x1067.png 600w, https:\/\/ruuvi.com\/i\/u\/nrf-connect-dis-service.png 720w\" sizes=\"auto, (max-width: 576px) 100vw, 576px\" \/><figcaption>DIS-Service<\/figcaption><\/figure><\/div>\n\n<p class=\"wp-block-paragraph\">Der <strong>DFU<\/strong>-Service schafft es nicht, das neue Paket automatisch hochzuladen, aber es ist m\u00f6glich, in den Bootloader zu wechseln, indem man sich f\u00fcr Benachrichtigungen des Secure DFU Service registriert und den Befehl \u201eenter bootloader\u201c sendet.<\/p>\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"576\" height=\"1024\" src=\"https:\/\/ruuvi.com\/i\/u\/nrf-connect-dfu-service-576x1024.png\" alt=\"nRF Connect DFU-Service\" class=\"wp-image-3936\" srcset=\"https:\/\/ruuvi.com\/i\/u\/nrf-connect-dfu-service-576x1024.png 576w, https:\/\/ruuvi.com\/i\/u\/nrf-connect-dfu-service-253x450.png 253w, https:\/\/ruuvi.com\/i\/u\/nrf-connect-dfu-service-600x1067.png 600w, https:\/\/ruuvi.com\/i\/u\/nrf-connect-dfu-service.png 720w\" sizes=\"auto, (max-width: 576px) 100vw, 576px\" \/><figcaption>Update ohne physischen Zugriff birgt allerdings Sicherheitsrisiken<\/figcaption><\/figure><\/div>\n\n<p class=\"wp-block-paragraph\">Und schlie\u00dflich k\u00f6nnen wir den Beschleunigungsdatenstrom mit <a href=\"http:\/\/infocenter.nordicsemi.com\/index.jsp?topic=%2Fcom.nordic.infocenter.rds%2Fdita%2Frds%2Fuart_app.html\"><em>nRF UART<\/em><\/a> pr\u00fcfen. Kurz vor dem Trennen der Verbindung senden wir \u201eThanks for data!\u201c an den RuuviTag, um die bidirektionale Kommunikation zu verifizieren. <\/p>\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"576\" height=\"1024\" src=\"https:\/\/ruuvi.com\/i\/u\/10-hz-data-in-32-sample-sets-576x1024.png\" alt=\"10-Hz-Daten in 32-Sample-Sets\" class=\"wp-image-3940\" srcset=\"https:\/\/ruuvi.com\/i\/u\/10-hz-data-in-32-sample-sets-576x1024.png 576w, https:\/\/ruuvi.com\/i\/u\/10-hz-data-in-32-sample-sets-253x450.png 253w, https:\/\/ruuvi.com\/i\/u\/10-hz-data-in-32-sample-sets-600x1067.png 600w, https:\/\/ruuvi.com\/i\/u\/10-hz-data-in-32-sample-sets.png 720w\" sizes=\"auto, (max-width: 576px) 100vw, 576px\" \/><figcaption>10-Hz-Daten in 32-Sample-Sets<\/figcaption><\/figure><\/div>\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"494\" height=\"393\" src=\"https:\/\/ruuvi.com\/i\/u\/debug-terminal-ruuvi-tag-logs.png\" alt=\"RuuviTag-Logs\" class=\"wp-image-3941\" srcset=\"https:\/\/ruuvi.com\/i\/u\/debug-terminal-ruuvi-tag-logs.png 494w, https:\/\/ruuvi.com\/i\/u\/debug-terminal-ruuvi-tag-logs-450x358.png 450w\" sizes=\"auto, (max-width: 494px) 100vw, 494px\" \/><figcaption>RuuviTag-Logs<\/figcaption><\/figure><\/div>\n\n<p class=\"wp-block-paragraph\">In unserem RuuviTag-Log bemerken wir, dass die <strong>DFU<\/strong>-Service-Initialisierung uns vor dem fehlenden Bootloader auf dem Entwicklungs-Tag warnt. Die Verbindung, das <strong>FIFO<\/strong>-Streaming und der Datenempfang funktionieren einwandfrei, und eine Weile nach der Trennung greift unser Watchdog ein und setzt den Tag zur\u00fcck. Soweit, so gut.  <\/p>\n\n<h2 class=\"wp-block-heading\">Leistungsprofil<\/h2>\n\n<p class=\"wp-block-paragraph\">Schauen wir uns an, wie unser Leistungsprofil aussieht. Zuerst pr\u00fcfen wir das Advertising. <\/p>\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"605\" src=\"https:\/\/ruuvi.com\/i\/u\/power-profile-back-down-1024x605.png\" alt=\"Leistungsprofil wieder bei 24,4 &#x3BC;A\" class=\"wp-image-3945\" srcset=\"https:\/\/ruuvi.com\/i\/u\/power-profile-back-down-1024x605.png 1024w, https:\/\/ruuvi.com\/i\/u\/power-profile-back-down-450x266.png 450w, https:\/\/ruuvi.com\/i\/u\/power-profile-back-down-768x454.png 768w, https:\/\/ruuvi.com\/i\/u\/power-profile-back-down-1536x907.png 1536w, https:\/\/ruuvi.com\/i\/u\/power-profile-back-down-2048x1209.png 2048w, https:\/\/ruuvi.com\/i\/u\/power-profile-back-down-600x354.png 600w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Wir sind wieder bei 24,4 \u03bcA<\/figcaption><\/figure><\/div>\n\n<p class=\"wp-block-paragraph\">Da wir die zus\u00e4tzlichen <strong>FIFO<\/strong>-Lesevorg\u00e4nge losgeworden sind, sind wir wieder bei 24,4 <em>\u03bcA<\/em> statt der vorherigen 29,7 <em>\u03bcA<\/em>. Als wir den Beschleunigungssensor mit 1<em> Hz<\/em> betrieben haben, lag der Verbrauch bei <em>23 \u03bcA<\/em>, der Mehrverbrauch kommt wahrscheinlich von der aktuellen Abtastrate von 10 <em>Hz.<\/em> Wie sieht es w\u00e4hrend der Verbindung aus? <\/p>\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"621\" src=\"https:\/\/ruuvi.com\/i\/u\/power-profile-sending-data-1024x621.png\" alt=\"Leistungsprofil beim Senden von Daten\" class=\"wp-image-3949\" srcset=\"https:\/\/ruuvi.com\/i\/u\/power-profile-sending-data-1024x621.png 1024w, https:\/\/ruuvi.com\/i\/u\/power-profile-sending-data-450x273.png 450w, https:\/\/ruuvi.com\/i\/u\/power-profile-sending-data-768x466.png 768w, https:\/\/ruuvi.com\/i\/u\/power-profile-sending-data-1536x931.png 1536w, https:\/\/ruuvi.com\/i\/u\/power-profile-sending-data-2048x1242.png 2048w, https:\/\/ruuvi.com\/i\/u\/power-profile-sending-data-600x364.png 600w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Daten senden ist nicht so gut<\/figcaption><\/figure><\/div>\n\n<p class=\"wp-block-paragraph\">Ein Problem wird sofort aus dem Leistungsprofil ersichtlich: Unser Tag kann Daten nicht schnell genug senden und das Programm l\u00e4uft in Zeile 16 des task_acceleration.c-Gists oben in einer Schleife und wartet darauf, dass der Puffer geleert wird. Wir m\u00fcssen uns in Zukunft M\u00f6glichkeiten ansehen, den <strong>GATT<\/strong>-Durchsatz zu steigern und den Tag zwischen den Verbindungsintervallen schlafen zu lassen. <\/p>\n\n<h2 class=\"wp-block-heading\">Fazit<\/h2>\n\n<p class=\"wp-block-paragraph\">Wir haben jetzt das <strong>GATT<\/strong>-Profil implementiert und k\u00f6nnen Beschleunigungssensordaten dar\u00fcber streamen. Es bleibt jedoch noch mehr Arbeit, um den <strong>GATT<\/strong>-Durchsatz und Verbrauch zu optimieren. <\/p>\n\n<p class=\"wp-block-paragraph\">Im n\u00e4chsten Teil der Serie implementieren wir <strong>NFC<\/strong>-Schreibvorg\u00e4nge auf den Tag. Bald werden wir alle Features, die wir verwenden werden, zumindest auf Proof-of-Concept-Ebene implementiert haben, dann k\u00f6nnen wir einen Feature-Freeze durchf\u00fchren und unsere Firmware als Beta-Version betrachten. Dann k\u00f6nnen wir anfangen, Bugs zu beheben, Dokumentation und Tests zu verbessern und Dinge wie den <strong>GATT<\/strong>-Durchsatz zu optimieren.  <\/p>\n\n<p class=\"wp-block-paragraph\"><strong>Bleib dran und folge <\/strong><a href=\"https:\/\/twitter.com\/ojousima\"><strong>@ojousima<\/strong><\/a><strong> und <\/strong><a href=\"https:\/\/twitter.com\/ruuvicom\"><strong>@ruuvicom<\/strong><\/a><strong> auf Twitter f\u00fcr #FirmwareFriday-Posts!<\/strong><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In diesem Teil des Tutorials f\u00fcgen wir unserem Projekt ein Generic Attributes (GATT)-Profil hinzu. Der finale Code dieses Blogbeitrags kann auf Ruuvi GitHub im ruuviblog-Branch, Tag 3.16.0-alpha, heruntergeladen werden. Folge bitte Teil 1 der Serie f\u00fcr Details zum Klonen des Repositorys und Kompilieren des Codes. Die finale Hex-Datei dieses Tutorials kann von Ruuvi Jenkins heruntergeladen [&hellip;]<\/p>\n","protected":false},"author":6,"featured_media":135632,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[324],"tags":[],"class_list":["post-135631","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ruuvi-software-artikel"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.8 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Ruuvi Firmware \u2013 Teil 16: BLE GATT-Verbindung \u2013 Ruuvi<\/title>\n<meta name=\"description\" content=\"Implementierung eines einfachen Profils mit DFU, DIS und NUS zur GATT-Schnittstelle, um eine hierarchische Datenstruktur zu definieren.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/ruuvi.com\/de\/ruuvi-firmware-teil-16-ble-gatt-verbindung\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Ruuvi Firmware \u2013 Teil 16: BLE GATT-Verbindung \u2013 Ruuvi\" \/>\n<meta property=\"og:description\" content=\"Implementierung eines einfachen Profils mit DFU, DIS und NUS zur GATT-Schnittstelle, um eine hierarchische Datenstruktur zu definieren.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/ruuvi.com\/de\/ruuvi-firmware-teil-16-ble-gatt-verbindung\/\" \/>\n<meta property=\"og:site_name\" content=\"Ruuvi\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/ruuvi.cc\" \/>\n<meta property=\"article:published_time\" content=\"2018-11-09T09:39:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-06-11T04:27:30+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/ruuvi.com\/i\/u\/ruuvi-firmware-series-part-16.jpeg\" \/>\n\t<meta property=\"og:image:width\" content=\"1400\" \/>\n\t<meta property=\"og:image:height\" content=\"733\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Otso Jousimaa\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@ruuvicom\" \/>\n<meta name=\"twitter:site\" content=\"@ruuvicom\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Otso Jousimaa\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"14\u00a0Minute\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/ruuvi-firmware-teil-16-ble-gatt-verbindung\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/ruuvi-firmware-teil-16-ble-gatt-verbindung\\\/\"},\"author\":{\"name\":\"Otso Jousimaa\",\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/#\\\/schema\\\/person\\\/143b8e2a095f1e6484b9186673c9ec00\"},\"headline\":\"Ruuvi Firmware \u2013 Teil 16: BLE GATT-Verbindung\",\"datePublished\":\"2018-11-09T09:39:00+00:00\",\"dateModified\":\"2026-06-11T04:27:30+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/ruuvi-firmware-teil-16-ble-gatt-verbindung\\\/\"},\"wordCount\":1073,\"image\":{\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/ruuvi-firmware-teil-16-ble-gatt-verbindung\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/ruuvi.com\\\/i\\\/u\\\/ruuvi-firmware-series-part-16.jpeg\",\"articleSection\":[\"Ruuvi-Software-Artikel\"],\"inLanguage\":\"de\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/ruuvi-firmware-teil-16-ble-gatt-verbindung\\\/\",\"url\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/ruuvi-firmware-teil-16-ble-gatt-verbindung\\\/\",\"name\":\"Ruuvi Firmware \u2013 Teil 16: BLE GATT-Verbindung \u2013 Ruuvi\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/ruuvi-firmware-teil-16-ble-gatt-verbindung\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/ruuvi-firmware-teil-16-ble-gatt-verbindung\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/ruuvi.com\\\/i\\\/u\\\/ruuvi-firmware-series-part-16.jpeg\",\"datePublished\":\"2018-11-09T09:39:00+00:00\",\"dateModified\":\"2026-06-11T04:27:30+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/#\\\/schema\\\/person\\\/143b8e2a095f1e6484b9186673c9ec00\"},\"description\":\"Implementierung eines einfachen Profils mit DFU, DIS und NUS zur GATT-Schnittstelle, um eine hierarchische Datenstruktur zu definieren.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/ruuvi-firmware-teil-16-ble-gatt-verbindung\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/ruuvi.com\\\/de\\\/ruuvi-firmware-teil-16-ble-gatt-verbindung\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/ruuvi-firmware-teil-16-ble-gatt-verbindung\\\/#primaryimage\",\"url\":\"https:\\\/\\\/ruuvi.com\\\/i\\\/u\\\/ruuvi-firmware-series-part-16.jpeg\",\"contentUrl\":\"https:\\\/\\\/ruuvi.com\\\/i\\\/u\\\/ruuvi-firmware-series-part-16.jpeg\",\"width\":1400,\"height\":733,\"caption\":\"Ruuvi Firmware Serie Teil 16 Intro-Bild\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/ruuvi-firmware-teil-16-ble-gatt-verbindung\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/front\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Ruuvi Firmware \u2013 Teil 16: BLE GATT-Verbindung\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/#website\",\"url\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/\",\"name\":\"Ruuvi\",\"description\":\"Measure Your World\",\"potentialAction\":[],\"inLanguage\":\"de\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/#\\\/schema\\\/person\\\/143b8e2a095f1e6484b9186673c9ec00\",\"name\":\"Otso Jousimaa\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/fd52303e35b8b23c01cfeec7bb2636768de567cd33604f794ae86dd971e61645?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/fd52303e35b8b23c01cfeec7bb2636768de567cd33604f794ae86dd971e61645?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/fd52303e35b8b23c01cfeec7bb2636768de567cd33604f794ae86dd971e61645?s=96&d=mm&r=g\",\"caption\":\"Otso Jousimaa\"},\"url\":\"https:\\\/\\\/ruuvi.com\\\/de\\\/author\\\/ojousima\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Ruuvi Firmware \u2013 Teil 16: BLE GATT-Verbindung \u2013 Ruuvi","description":"Implementierung eines einfachen Profils mit DFU, DIS und NUS zur GATT-Schnittstelle, um eine hierarchische Datenstruktur zu definieren.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-teil-16-ble-gatt-verbindung\/","og_locale":"de_DE","og_type":"article","og_title":"Ruuvi Firmware \u2013 Teil 16: BLE GATT-Verbindung \u2013 Ruuvi","og_description":"Implementierung eines einfachen Profils mit DFU, DIS und NUS zur GATT-Schnittstelle, um eine hierarchische Datenstruktur zu definieren.","og_url":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-teil-16-ble-gatt-verbindung\/","og_site_name":"Ruuvi","article_publisher":"https:\/\/www.facebook.com\/ruuvi.cc","article_published_time":"2018-11-09T09:39:00+00:00","article_modified_time":"2026-06-11T04:27:30+00:00","og_image":[{"width":1400,"height":733,"url":"https:\/\/ruuvi.com\/i\/u\/ruuvi-firmware-series-part-16.jpeg","type":"image\/jpeg"}],"author":"Otso Jousimaa","twitter_card":"summary_large_image","twitter_creator":"@ruuvicom","twitter_site":"@ruuvicom","twitter_misc":{"Verfasst von":"Otso Jousimaa","Gesch\u00e4tzte Lesezeit":"14\u00a0Minute"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-teil-16-ble-gatt-verbindung\/#article","isPartOf":{"@id":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-teil-16-ble-gatt-verbindung\/"},"author":{"name":"Otso Jousimaa","@id":"https:\/\/ruuvi.com\/de\/#\/schema\/person\/143b8e2a095f1e6484b9186673c9ec00"},"headline":"Ruuvi Firmware \u2013 Teil 16: BLE GATT-Verbindung","datePublished":"2018-11-09T09:39:00+00:00","dateModified":"2026-06-11T04:27:30+00:00","mainEntityOfPage":{"@id":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-teil-16-ble-gatt-verbindung\/"},"wordCount":1073,"image":{"@id":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-teil-16-ble-gatt-verbindung\/#primaryimage"},"thumbnailUrl":"https:\/\/ruuvi.com\/i\/u\/ruuvi-firmware-series-part-16.jpeg","articleSection":["Ruuvi-Software-Artikel"],"inLanguage":"de"},{"@type":"WebPage","@id":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-teil-16-ble-gatt-verbindung\/","url":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-teil-16-ble-gatt-verbindung\/","name":"Ruuvi Firmware \u2013 Teil 16: BLE GATT-Verbindung \u2013 Ruuvi","isPartOf":{"@id":"https:\/\/ruuvi.com\/de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-teil-16-ble-gatt-verbindung\/#primaryimage"},"image":{"@id":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-teil-16-ble-gatt-verbindung\/#primaryimage"},"thumbnailUrl":"https:\/\/ruuvi.com\/i\/u\/ruuvi-firmware-series-part-16.jpeg","datePublished":"2018-11-09T09:39:00+00:00","dateModified":"2026-06-11T04:27:30+00:00","author":{"@id":"https:\/\/ruuvi.com\/de\/#\/schema\/person\/143b8e2a095f1e6484b9186673c9ec00"},"description":"Implementierung eines einfachen Profils mit DFU, DIS und NUS zur GATT-Schnittstelle, um eine hierarchische Datenstruktur zu definieren.","breadcrumb":{"@id":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-teil-16-ble-gatt-verbindung\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/ruuvi.com\/de\/ruuvi-firmware-teil-16-ble-gatt-verbindung\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-teil-16-ble-gatt-verbindung\/#primaryimage","url":"https:\/\/ruuvi.com\/i\/u\/ruuvi-firmware-series-part-16.jpeg","contentUrl":"https:\/\/ruuvi.com\/i\/u\/ruuvi-firmware-series-part-16.jpeg","width":1400,"height":733,"caption":"Ruuvi Firmware Serie Teil 16 Intro-Bild"},{"@type":"BreadcrumbList","@id":"https:\/\/ruuvi.com\/de\/ruuvi-firmware-teil-16-ble-gatt-verbindung\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/ruuvi.com\/de\/front\/"},{"@type":"ListItem","position":2,"name":"Ruuvi Firmware \u2013 Teil 16: BLE GATT-Verbindung"}]},{"@type":"WebSite","@id":"https:\/\/ruuvi.com\/de\/#website","url":"https:\/\/ruuvi.com\/de\/","name":"Ruuvi","description":"Measure Your World","potentialAction":[],"inLanguage":"de"},{"@type":"Person","@id":"https:\/\/ruuvi.com\/de\/#\/schema\/person\/143b8e2a095f1e6484b9186673c9ec00","name":"Otso Jousimaa","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/secure.gravatar.com\/avatar\/fd52303e35b8b23c01cfeec7bb2636768de567cd33604f794ae86dd971e61645?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/fd52303e35b8b23c01cfeec7bb2636768de567cd33604f794ae86dd971e61645?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/fd52303e35b8b23c01cfeec7bb2636768de567cd33604f794ae86dd971e61645?s=96&d=mm&r=g","caption":"Otso Jousimaa"},"url":"https:\/\/ruuvi.com\/de\/author\/ojousima\/"}]}},"_links":{"self":[{"href":"https:\/\/ruuvi.com\/de\/wp-json\/wp\/v2\/posts\/135631","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ruuvi.com\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ruuvi.com\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ruuvi.com\/de\/wp-json\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/ruuvi.com\/de\/wp-json\/wp\/v2\/comments?post=135631"}],"version-history":[{"count":1,"href":"https:\/\/ruuvi.com\/de\/wp-json\/wp\/v2\/posts\/135631\/revisions"}],"predecessor-version":[{"id":135638,"href":"https:\/\/ruuvi.com\/de\/wp-json\/wp\/v2\/posts\/135631\/revisions\/135638"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ruuvi.com\/de\/wp-json\/wp\/v2\/media\/135632"}],"wp:attachment":[{"href":"https:\/\/ruuvi.com\/de\/wp-json\/wp\/v2\/media?parent=135631"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ruuvi.com\/de\/wp-json\/wp\/v2\/categories?post=135631"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ruuvi.com\/de\/wp-json\/wp\/v2\/tags?post=135631"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}