Format of Dynamic data: typedef struct { uint8_t dlen1 : 2; uint8_t tag : 6; uint8_t dlen0; } aci_priv_dynamic_data_tag_header_t; Remember: The bitfields within a struct are little end first so "dlen1" is the 2 lower order bits of the byte. These are the Tags used in the dynamic data: typedef enum { ACI_DD_TAG_INVALID = 0x00, ACI_DD_TAG_DEVSETT = 0x01, ACI_DD_TAG_ATTDB = 0x02, //uses the number of att handles instead of the length i.e. count instead of length ACI_DD_TAG_REM_ATTRS = 0x03, ACI_DD_TAG_MRG_LIN = 0x04, ACI_DD_TAG_CRC = 0x05, ACI_DD_TAG_COMPLETE = 0x06 } case ACI_DD_TAG_DEVSETT: Uses length of data in tag case ACI_DD_TAG_ATTDB: Count of handle value pairs that are present. Each handle value pair is of the format. case ACI_DD_TAG_REM_ATTRS: Count of handle of value, handle of CCCD and state that are present on the peer GATT Server and the nRF8001 GATT client has discovered. Each tuple is of the format as below typedef struct { uint8_t attr_idx; //8 bits attdb_handle_t handle_value; //16 bits attdb_handle_t handle_cccd; //16 bits uint8_t state; //8 bits } aci_priv_dynamic_data_tag_rem_attr_t; case ACI_DD_TAG_MRG_LIN: Uses length of data in tag Data is formatted in the below structure #define BTLE_DEVICE_ADDRESS_SIZE (6) uint48_le_t is 48 bits uint128_le_t is 128 bits typedef struct { uint8_t addr_self[BTLE_DEVICE_ADDRESS_SIZE]; uint8_t addr_self_type; uint8_t init_bdaddr_type; <----------------------------- Address type of the peer (will be ACI_BD_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE for an iPhone) uint48_le_t init_bdaddr; <---------------------------------- Address of the iPhone dm_peer_state_t peer_state; <----------------------------------- This is what is used to trigger re-discovery uint16_le_t bonded_div; uint8_t bonded_key_size; //Removed parts of the struct that are conditionally compiled out uint128_le_t bonded_a_irk; <--------------------------------- IRK of the peer device uint8_t bonded_a_bdaddr_type; uint48_le_t bonded_a_bdaddr; uint128_le_t bonded_a_csrk; uint32_t bonded_a_sign_cnt; uint32_t bonded_b_sign_cnt; dm_pipe_bitmaps_t pipe_bitmaps; } dm_mrg_lin_data_t; case ACI_DD_TAG_CRC: Uses length of data in tag Contains only the 2 bytes CRC as data in this tag ===================== typedef struct { uint8_t bonded_keys_bitmap; /* 0-2 LSbits Responder keys, 3-5 Initiator Keys, 6 STK valid, 7 bonded */ uint8_t peer_perm: 2; /* gatt_dm_perm_t->perm 2 bits */ uint8_t authenticated: 1; /* man in the middle protection present (TK != 0) */ uint8_t svc_done: 1; /* Remote pipes discovery complete (temp variable, during disc) */ uint8_t sc_busy: 1; /* Remote Service Change enabling busy (temp variable, during disc) */ uint8_t discovered: 1; /* Service discovery procedure complete */ <---------------------------- Set this to 0 to re-trigger service discovery uint8_t whitelist_disable: 1; /* BTHOST-1193 */ uint8_t reserved: 1; } dm_peer_state_t; ======================= To get to the peer_state.discovered, we need to get to the ACI_DD_TAG_MRG_LIN, to do this we need to start from the first tag in the dynamic data and work our way until we get to ACI_DD_TAG_MRG_LIN. Then we modify peer_state.discovered. Then we recompute the CRC and update the CRC. After this the dynamic data can be downloaded to the nRF8001. Additional info: ================ /** * @def ACI_PRIV_DYNAMIC_DATA_CHUNK_MAX_LEN * @brief Maximum chunk length for private dynamic data transfer: * ACI TX packet size - len - evt_opcode - cmd_opcode - cmd_status - seq_no */ #define ACI_PRIV_DYNAMIC_DATA_CHUNK_MAX_LEN ((ACI_PACKET_MAX_LEN - 1) - 1 - 1 - 1 - 1 - 1) /** * @def BTLE_DEVICE_ADDRESS_SIZE * @brief Size in bytes of a Bluetooth Address */ #define BTLE_DEVICE_ADDRESS_SIZE (6) /** * @def ACI_PACKET_MAX_LEN * @brief Maximum length in bytes of a full ACI packet, including length prefix, opcode and payload */ #define ACI_PACKET_MAX_LEN (32) /** * @struct dm_pipe_bitmaps_t * @brief Bitmap of open and closed pipes */ typedef struct { uint8_t pipes_open[8]; uint8_t pipes_closed[8]; uint8_t pipes_adv[8]; } dm_pipe_bitmaps_t; ============== Calculation Recalculate the CRC when changing the dynamic data. Note: The last 2 bytes in the dynamic data are the CRC and must not be fed into the CRC generator. /* Iterate CRC calculation */ crc_16_ccitt = base_crc_16_ccitt(crc_16_ccitt, &p_aci_priv_evt->params.cmd_rsp.params.read_dynamic_data.seq_no, dd_len + 1); // dd_len+1 is used to as the seq_no is part of the CRC computation Polynomial for CRC-16-CCITT (X.25, V.41, HDLC, XMODEM, Bluetooth, PACTOR, SD, many others; known as CRC-CCITT) Initial seed value for the CRC is FFFF Example 1 dymanic data below of proximity fob with a GATT Server and GATT client ========= Read the dynamic data C1 :1, 7, E :1E, 84, 7, 1, 1, 4, 2, 3, 9, 8, 6, 0, B, 1, 0, 0, E, 1, 0, 0, 11, 1, 2, 0, 14, 1, 64, 0, 15, 2, 1, 4, 2 is Tag ACI_DD_TAG_DEVSETT has 2 bytes. 8, 6 is Tag ACI_DD_TAG_ATTDB has 6 handle,length, value tuples. C1 :1, 7, E :19, 84, 7, 1, 2, 0, 0, 18, 9, 15, 0, 2, 3, 1, 0, 0, 0, 0, C, 2, 5, 0, C, 0, D, 1, C, 2 is ACI_DD_TAG_REM_ATTRS has 2 entries. Entry 1 = 5, 0, C, 0, D, 1 C1 :1, 7, E :1E, 84, 7, 1, 3, 6, 0, 8, 0, 9, 1, 10, 5A, 7, F4, A7, B9, FF, DC, 1, 1, 63, CB, F9, F0, 15, 67, B7, 78, D5, B9, Entry 2 = 6, 0, 8, 0, 9, 1 10, 5A is Tag ACI_DD_TAG_MRG_LIN, the length of the data associated with the tag is 5A bytes long C1 :1, 7, E :1E, 84, 7, 1, 4, 10, F8, D1, 4F, 73, 60, 28, 48, 18, 4C, D3, D6, 63, BB, 97, 3C, 29, 0, 74, 84, B9, A1, CB, F0, E1, B8, C1 :1, 7, E :1E, 84, 7, 1, 5, 44, E7, 29, 2F, 94, FA, DE, B5, 12, 83, 2A, C5, 75, C, 0, 0, 0, 0, 0, 0, 0, 0, FE, 1, 0, 0, C1 :1, 7, E :1C, 84, 7, 2, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 2, 76, 89, 14,2 is the ACI_DD_TAG_CRC and has 2 bytes of length of data. ==== Example 2 TX - 01 07 RX - 01 00 TX - 00 07 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 RX - 01 1E 84 07 01 01 04 02 03 04 08 0C 00 03 11 6E 52 46 72 65 61 64 79 20 4B 65 79 62 6F 61 72 64 TX - 01 07 RX - 01 00 TX - 00 07 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 RX - 01 1C 84 07 01 02 00 0B 08 00 00 00 00 00 00 00 00 00 0C 02 01 00 00 11 01 01 00 13 01 00 TX - 01 07 RX - 01 00 TX - 00 07 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 RX - 01 1C 84 07 01 03 00 17 01 00 00 1A 08 00 00 00 00 00 00 00 00 00 1B 02 01 00 00 1D 01 00 TX - 01 07 RX - 01 00 TX - 00 07 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 RX - 01 1E 84 07 01 04 00 20 01 32 00 22 01 2F 00 25 04 00 00 00 00 0C 00 10 5A EF 1B 2B D9 E3 E9 01 TX - 01 07 RX - 01 00 TX - 00 07 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 RX - 01 1E 84 07 01 05 00 69 79 A5 50 84 54 81 24 81 A7 10 40 00 FD 7F 0F 9E 81 A1 22 22 CB FB FF FF TX - 01 07 RX - 01 00 TX - 00 07 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 RX - 01 1E 84 07 01 06 21 90 00 69 79 A5 50 84 54 06 04 06 FF EF ED EF 08 82 80 20 EA FB FB FE 00 00 TX - 01 07 RX - 01 00 TX - 00 07 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 RX - 01 1E 84 07 01 07 00 00 00 00 00 00 00 FE 3F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 TX - 01 07 RX - 01 00 TX - 00 07 01 00 00 00 00 00 00 00 00 00 00 00 00 RX - 01 0D 84 07 02 08 00 00 00 00 00 14 02 D3 75