Wireless Connectivity Knowledge Base

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Wireless Connectivity Knowledge Base

Discussions

Sort by:
The image below shows the different types of devices in a Thread Network. Router Routers provide routing services to network devices. Routers also provide joining and security services for devices trying to join the network. Routers are not designed to sleep. Routers can downgrade their functionality and become REEDs (Router-eligible End Devices). A Router can become a Leader and start a Thread network. Border Router A Border Router is a type of Router that provides connectivity from the 802.15.4 network to adjacent networks on other physical layers (for example, Wi-Fi and Ethernet). Border Routers provide services for devices within the 802.15.4 network, including routing services for off-network operations. There may be one or more Border Routers in a Thread Network. The Border Router also serves as an interface point for the Commissioner when the Commissioner is on a non-Thread Network; it requires a Thread interface and may be combined in any device with other Thread roles except the Joiner. Leader A Router or Border Router can assume a Leader role for certain functions in the Thread Network. This Leader is required to make decisions within the network. For example, the Leader assigns Router addresses and allows new Router requests. The Leader role is elected and if the Leader fails, another Router or Border Router assumes the Leader role. It is this autonomous operation that ensures there is no single point of failure. Router-eligible End Device REEDs have the capability to become Routers but due to the network topology or conditions these devices are not acting as Routers. These devices do not generally forward messages or provide joining or security services for other devices in the Thread Network. The Thread Network manages REEDs becoming Routers if necessary without user interaction. Sleepy End Device Sleepy end devices are host devices. They communicate only through their Parent Router and cannot forward messages for other devices References: Thread Whitepapers available at http://threadgroup.org 
View full article
Introduction The FRDM-KW36 includes an RSIM (Radio System Integration Module) module with an external 32 MHz crystal oscillator. This clock source reference is mainly intended to supply the Bluetooth LE Radio peripheral, but it can be used as the main clock source of the MCU as well. This oscillator includes a set of programmable capacitors to support crystals with different load capacitance needs. Changing the value of these capacitors can modify the frequency the oscillator provides, that way, the central frequency can be tuned to meet the wireless protocol standards. This configurable capacitance range is from C1: 5.7pF - C2: 7.1pF to C1: 22.6pF - C2: 28.2pF and it is configured through the BB_XTAL_TRIM field at the ANA_TRIM. The KW36 comes preprogrammed with a default load capacitance value. However, since there is variance in devices due to tolerances and parasite effects, the correct load capacitance should be checked by verifying that the optimal central frequency is attained.  You will need a spectrum analyzer to measure the central frequency. To find the most accurate value for the load capacitance, it is recommended to use the Connectivity Test demo application. Adjusting Frequency Example Program the KW36 Connectivity Test software on the device. This example can be found in wireless_examples -> genfsk -> conn_test folder from your SDK package. Baremetal and FreeRTOS versions are available. In case that FRDM-KW36 board is being used to perform the test, you should move the 10pF capacitor populated in C55 to C57, to direct the RF signal on the SMA connector. Connect the board to a serial terminal software. When you start the application, you will be greeted by the NXP logo screen:  Press the enter key to start the test. Then press "1" to select "Continuous tests": Finally, select "6" to start a continuous unmodulated RF test. At this point, you should be able to measure the signal in the spectrum analyzer. You can change the RF channel from 0 to 127 ("q" Ch+ and "w" Ch- keys), which represents the bandwidth from 2.360GHz to 2.487GHz, stepping of 1MHz between two consecutive channels. To demonstrate the trimming procedure, this document will make use of channel 42 (2.402GHz) which corresponds to the Bluetooth LE channel 37. In this case, with the default capacitance value, our oscillator is not exactly placed at the center of the 2.402GHz, instead, it is slightly deflected to 2.40200155 GHz, as depicted in the following figure: The capacitance can be adjusted with the "d" XtalTrim+ and "f" XtalTrim- keys. Increasing the capacitance bank means a lower frequency. In our case, we need to increase the capacitance to decrease the frequency. The nearest frequency of 2.402 GHz was 2.40199940 GHz  Once the appropriate XTAL trim value has been found, it can be programmed as default in any Bluetooth LE example, changing the mXtalTrimDefault constant located in the board.c file: static const uint8_t mXtalTrimDefault‍ = 0x36;‍‍‍
View full article
Regarding to the "Reprogramming a KW36 device using the OTAP Client Software" and "Reprogramming a KW35 device using the OTAP Client Software" documents, there are some additional steps to debug the OTAP client software in the specific case when you use MCUXpresso together with a P&E micro debug probe. Just before to program the OTAP client project (the second software), the user must do the following: Open the "Debug Configurations" view clicking on the green bug as depicted below. Go to the "Debugger" perspective and search the "Advanced Options" button. Enable the "Preserve this range (Memory Range 0)" checkbox, and edit the textbox "From: 0" To: 1fff" for the KW36 device or "From: 0 To: 3fff" for the KW35 device. After to flash the device, disconnect and connect again. If everything it's OK, the RGB LED must blink (If you are using an FRDM board). Then, test the demo as described in the document.
View full article
Introduction When a Bluetooth LE Central and Peripheral devices are in connection, data within the payload can be encrypted. Encryption of the channel can be achieved through pairing with others. Once the communication has been encrypted, the Bluetooth LE devices could distribute the keys to save it for future connections. The last is better known as bonding. When two Bluetooth LE devices are bonded, in a future connection, they do not need to exchange the keys since they already know the shared secret, thus, they can encrypt the channel directly, saving time and power. However, if an attacker is listening to the first time that both (Central and Peripheral) Bluetooth LE devices enter into a connection state, the security of the link could be vulnerated, since the attacker could decipher the original message. Fortunately, Out Of Band (OOB) provides the ability (obviously, if both devices support it) to share the keys on an unknown medium for an attacker listening Bluetooth LE (for instance, NFC, SPI, UART, CAN, etc), increasing the security of the communication. This document explains how to enable OOB pairing on Bluetooth LE connectivity examples, basing on FRDM-KW36 SDK HID Host and HID Device examples.   Dedicated Macros and APIs for OOB Pairing The connectivity software stack contains macros and APIs that developers should implement to interact with the host stack and handle the events necessary for OOB. The following sections explain the main macros, variables, and APIs that manage OOB in our software.   Definitions and Variables gAppUsePairing_d It is used to enable or disable pairing to encrypt the link. Values Result 0 Pairing Disabled 1 Pairing Enabled   gAppUseBonding_d It is used to enable or disable bonding to request and save the keys for future connections. Values Result 0 Bonding Disabled 1 Bonding Enabled   gBleLeScOobHasMitmProtection_c This flag must be set if the application requires Man In the Middle protection, in other words, if the link must be authenticated. You can determine whether your software needs to set or clear this flag from the GAP Security Mode and Level. Red instances of the following table indicate that gBleLeScOobHasMitmProtection_c must be set to 1.   gPairingParameters This struct contains the pairing request or the pairing response (depending on the device's GAP role) payload. To enable and configure OOB pairing, oobAvailable field of the struct must be set to 1.   APIs bleResult_t Gap_ProvideOob (deviceId_t deviceId, uint8_t* aOob) This API must be implemented in response of gConnEvtOobRequest_c event in BleConnManager_GapPeripheralEvent or BleConnManager_GapCentralEvent functions (depending of the GAP role). This event only will be triggered if OOB is enabled and LE Legacy pairing is used. The gConnEvtOobRequest_c event occurs when the stack request the OOB data received from the peer device just after the gConnEvtPairingRequest_c or gConnEvtPairingResponse_c (depending of the GAP role). This API is valid only for LE Legacy pairing. Name of the Parameter Input/Output Description deviceId Input ID of the peer device aOob Input Pointer to OOB data previously received from the peer.   bleResult_t Gap_LeScGetLocalOobData (void) This API must be implemented either in response of gConnEvtPairingRequest_c or gConnEvtPairingResponse_c events  in BleConnManager_GapPeripheralEvent or BleConnManager_GapCentralEvent functions (depending of the GAP role) to get the local OOB data generated from the controller and in response of gLeScPublicKeyRegenerated_c event at BleConnManager_GenericEvent. Each time that Gap_LeScGetLocalOobData is executed in the application to obtain the OOB data, it triggers the gLeScLocalOobData_c generic event to inform that OOB data must be read from pGenericEvent->eventData.localOobData to send it to the peer device. This API is valid only for LE Secure Connections pairing.   bleResult_t Gap_LeScSetPeerOobData (deviceId_t deviceId, gapLeScOobData_t* pPeerOobData) This API must be implemented in response of gConnEvtLeScOobDataRequest_c event in BleConnManager_GapPeripheralEvent or BleConnManager_GapCentralEvent functions(depending of the GAP role). This event occurs when the stack requires the OOB data previously recieved from the peer. This API is valid only for LE Secure Connections pairing. Name of the Parameter Input/Output Description deviceId Input ID of the peer device aOob Input Pointer to gapLeScOobData_t struct that contains the OOB data received from the peer.   Enabling OOB on KW36 Bluetooth LE Peripheral Device The following example is based on the HID Device software included in the FRDM-KW36 SDK. It explains the minimum code needed to enable OOB. In the following sections, brown color indicates that such definition or API takes part in the stack and violet color indicates that such definition does not take part in the stack and its use is only for explanation purposes in this document.   Changes in app_preinclude.h file The app_preinclude.h header file contains definitions for the management of the application. To enable OOB pairing, you must ensure that gAppUseBonding_d and gAppUsePairing_d are set to 1. You can also set the value of the gBleLeScOobHasMitmProtection_c in this file, depending on the security mode and level needed in your application.  This example makes use of two custom definitions: gAppUseOob_d and gAppUseSecureConnections_d. Such definitions are used to explain how to enable/disable OOB and, if OOB is enabled, how to switch between LE Secure Connections pairing or LE Legacy paring.   /*! Enable/disable use of bonding capability */ #define gAppUseBonding_d 1 /*! Enable/disable use of pairing procedure */ #define gAppUsePairing_d 1 /*! Enable/disable use of privacy */ #define gAppUsePrivacy_d 0 #define gPasskeyValue_c 999999 /*! Enable/disable use of OOB pairing */ #define gAppUseOob_d 1 /*! Enable MITM protection when using OOB pairing */ #if (gAppUseOob_d) #define gBleLeScOobHasMitmProtection_c TRUE #endif /*! Enable/disable Secure Connections */ #define gAppUseSecureConnections_d 1‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍   Using the code above, you can enable or disable OOB using gAppUseOob_d, also you can decide whether to use LE Secure Connections (gAppUseSecureConnections_d = 1) or LE Legacy (gAppUseSecureConnections_d = 0)     Changes in app_config.c file The following portion fo code depicts how to fill gPairingParameters struct depending on which pairing method is used by the application.   /* SMP Data */ gapPairingParameters_t gPairingParameters = { .withBonding = (bool_t)gAppUseBonding_d, /* If Secure Connections pairing is supported, then set Security Mode 1 Level 4 */ /* If Legacy pairing is supported, then set Security Mode 1 Level 3 */ #if (gAppUseSecureConnections_d) .securityModeAndLevel = gSecurityMode_1_Level_4_c, #else .securityModeAndLevel = gSecurityMode_1_Level_3_c, #endif .maxEncryptionKeySize = mcEncryptionKeySize_c, .localIoCapabilities = gIoKeyboardDisplay_c, /* OOB Available enabled when app_preinclude.h file gAppUseOob_d macro is true */ .oobAvailable = (bool_t)gAppUseOob_d, #if (gAppUseSecureConnections_d) .centralKeys = (gapSmpKeyFlags_t) (gIrk_c), .peripheralKeys = (gapSmpKeyFlags_t) (gIrk_c), #else .centralKeys = (gapSmpKeyFlags_t) (gLtk_c | gIrk_c), .peripheralKeys = (gapSmpKeyFlags_t) (gLtk_c | gIrk_c), #endif /* Secure Connections enabled when app_preinclude.h file gAppUseSecureConnections_d macro is true */ .leSecureConnectionSupported = (bool_t)gAppUseSecureConnections_d, .useKeypressNotifications = FALSE, };‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍   Additionally, the serviceSecurity struct registers which are the security mode and level of each Bluetooth LE service, so if Secure Connections is selected (gAppUseSecureConnections_d = 1), mode = 1 level = 4.   static const gapServiceSecurityRequirements_t serviceSecurity[3] = { { .requirements = { #if (gAppUseSecureConnections_d) .securityModeLevel = gSecurityMode_1_Level_4_c, #else .securityModeLevel = gSecurityMode_1_Level_3_c, #endif .authorization = FALSE, .minimumEncryptionKeySize = gDefaultEncryptionKeySize_d }, .serviceHandle = (uint16_t)service_hid }, { .requirements = { #if (gAppUseSecureConnections_d) .securityModeLevel = gSecurityMode_1_Level_4_c, #else .securityModeLevel = gSecurityMode_1_Level_3_c, #endif .authorization = FALSE, .minimumEncryptionKeySize = gDefaultEncryptionKeySize_d }, .serviceHandle = (uint16_t)service_battery }, { .requirements = { #if (gAppUseSecureConnections_d) .securityModeLevel = gSecurityMode_1_Level_4_c, #else .securityModeLevel = gSecurityMode_1_Level_3_c, #endif .authorization = FALSE, .minimumEncryptionKeySize = gDefaultEncryptionKeySize_d }, .serviceHandle = (uint16_t)service_device_info } };‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍     Changes in ble_conn_manager.c file LE Legacy Pairing If your application will use LE Legacy Pairing, then you have to implement Gap_ProvideOob in response to the gConnEvtOobRequest_c event at the BleConnManager_GapPeripheralEvent function. In this example, gOobReceivedTKDataFromPeer is an array that stores the data previously received OOB from the peer device (using SPI, UART, I2C, etc), therefore, the procedure to fill this array with the data received from the peer depends entirely on your application. Notice that gOobReceivedTKDataFromPeer must contain the data received from the peer before to execute Gap_ProvideOob.   static uint8_t gOobReceivedTKDataFromPeer[16]; void BleConnManager_GapPeripheralEvent(deviceId_t peerDeviceId, gapConnectionEvent_t* pConnectionEvent) { switch (pConnectionEvent->eventType) { case gConnEvtConnected_c: { ... ... ... } break; ... ... ... #if (gAppUseOob_d && !gAppUseSecureConnections_d) case gConnEvtOobRequest_c: { /* The stack has requested the LE Legacy OOB data*/ (void)Gap_ProvideOob(peerDeviceId, &gOobReceivedTKDataFromPeer[0]); } break; #endif ... ... ... } }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍     LE Secure Connections Pairing When using Secure Connections Pairing, the application must handle two events at the BleConnManager_GapPeripheralEvent function. In gConnEvtPairingRequest_c event, you must implement Gap_LeScGetLocalOobData API to generate the local (r, Cr) values. The gConnEvtLeScOobDataRequest_c event indicates that the application is requesting the (r, Cr) values previously received OOB from the peer device (using SPI, UART, I2C, etc). Such values are contained into gOobReceivedRandomValueFromPeer and gOobReceivedConfirmValueFromPeer buffers. You must implement Gap_LeScSetPeerOobData in response to gConnEvtLeScOobDataRequest_c, This function has two parameters, the device ID of the peer and a pointer to a gapLeScOobData_t type struct. This struct is filled with the data contained in gOobReceivedRandomValueFromPeer and gOobReceivedConfirmValueFromPeer buffers.   gapLeScOobData_t gPeerOobData; static uint8_t gOobReceivedRandomValueFromPeer[gSmpLeScRandomValueSize_c]; /*!< LE SC OOB r (Random value) */ static uint8_t gOobReceivedConfirmValueFromPeer[gSmpLeScRandomConfirmValueSize_c]; /*!< LE SC OOB Cr (Random Confirm value) */ void BleConnManager_GapPeripheralEvent(deviceId_t peerDeviceId, gapConnectionEvent_t* pConnectionEvent) { switch (pConnectionEvent->eventType) { case gConnEvtConnected_c: { ... ... ... } break; case gConnEvtPairingRequest_c: { #if (defined(gAppUsePairing_d) && (gAppUsePairing_d == 1U)) gPairingParameters.centralKeys = pConnectionEvent->eventData.pairingEvent.centralKeys; (void)Gap_AcceptPairingRequest(peerDeviceId, &gPairingParameters); #if (gAppUseOob_d && gAppUseSecureConnections_d) /* The central has requested pairing, get local LE Secure Connections OOB data */ (void)Gap_LeScGetLocalOobData(); #endif #else (void)Gap_RejectPairing(peerDeviceId, gPairingNotSupported_c); #endif } break; ... ... ... #if (gAppUseOob_d && gAppUseSecureConnections_d) case gConnEvtLeScOobDataRequest_c: { /* The stack has requested the peer LE Secure Connections OOB data. Fill the gPeerOobData struct and provide it to the stack */ FLib_MemCpy(gPeerOobData.randomValue, &gOobReceivedRandomValueFromPeer[0], gSmpLeScRandomValueSize_c); FLib_MemCpy(gPeerOobData.confirmValue, &gOobReceivedConfirmValueFromPeer[0], gSmpLeScRandomConfirmValueSize_c); Gap_LeScSetPeerOobData(peerDeviceId, &gPeerOobData); } break; #endif ... ... ... } }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍   The gLeScPublicKeyRegenerated_c event in the BleConnManager_GenericEvent function must be handled using the Gap_LeScGetLocalOobData API as depicted below. Each time that Gap_LeScGetLocalOobData is executed by the software, it generates, asynchronously, the gLeScLocalOobData_c event (also handled in the BleConnManager_GenericEvent function) indicating that the local (r, Cr) values were successfully generated and you can read them using the pGenericEvent->eventData.localOobData pointer to send it OOB to the peer device. In this example, Oob_SendLocalRandomValueToPeer and Oob_SendLocalConfirmValueToPeer  are custom synchronous functions that demonstrate how you can implement a custom API that sends the local (r, Cr) read from pGenericEvent->eventData.localOobData pointer to the peer device using other protocols (SPI, UART, I2C, etc).   void BleConnManager_GenericEvent(gapGenericEvent_t* pGenericEvent) { switch (pGenericEvent->eventType) { case gInitializationComplete_c: { ... ... ... } break; ... ... ... #if (defined(gAppUsePairing_d) && (gAppUsePairing_d == 1U)) case gLeScPublicKeyRegenerated_c: { /* Key pair regenerated -> reset pairing counters */ mFailedPairings = mSuccessfulPairings = 0; /* Local Secure Connections OOB data must be refreshed whenever this event occurs */ #if (gAppUseOob_d && gAppUseSecureConnections_d) (void)Gap_LeScGetLocalOobData(); #endif } break; #endif ... ... ... #if (gAppUseOob_d && gAppUseSecureConnections_d) case gLeScLocalOobData_c: { /* Get the local Secure Connections OOB data and send to the peer */ Oob_SendLocalRandomValueToPeer((uint8_t*)pGenericEvent->eventData.localOobData.randomValue); Oob_SendLocalConfirmValueToPeer((uint8_t*)pGenericEvent->eventData.localOobData.confirmValue); } break; #endif ... ... ... } }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍     Enabling OOB on KW36 Bluetooth LE Central Device The following example is based on the HID Host software included in the FRDM-KW36 SDK. It explains the minimum code needed to enable OOB. In the following sections, brown color indicates that such definition or API takes part in the stack and violet color indicates that such definition does not take part in the stack and its use is only for explanation purposes in this document.   Changes in app_preinclude.h file The app_preinclude.h header file contains definitions for the management of the application. To enable OOB pairing, you must ensure that gAppUseBonding_d and gAppUsePairing_d are set to 1. You can also set the value of the gBleLeScOobHasMitmProtection_c in this file, depending on the security mode and level needed in your application.  This example makes use of two custom definitions: gAppUseOob_d and gAppUseSecureConnections_d. Such definitions are used to explain how to enable/disable OOB and, if OOB is enabled, how to switch between LE Secure Connections pairing or LE Legacy paring.   /*! Enable/disable use of bonding capability */ #define gAppUseBonding_d 1 /*! Enable/disable use of pairing procedure */ #define gAppUsePairing_d 1 /*! Enable/disable use of privacy */ #define gAppUsePrivacy_d 0 #define gPasskeyValue_c 999999 /*! Enable/disable use of OOB pairing */ #define gAppUseOob_d 1 /*! Enable MITM protection when using OOB pairing */ #if (gAppUseOob_d) #define gBleLeScOobHasMitmProtection_c TRUE #endif /*! Enable/disable Secure Connections */ #define gAppUseSecureConnections_d 1‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍   Using the code above, you can enable or disable OOB using gAppUseOob_d, also you can decide whether to use LE Secure Connections (gAppUseSecureConnections_d = 1) or LE Legacy (gAppUseSecureConnections_d = 0)     Changes in app_config.c file The following portion fo code depicts how to fill gPairingParameters struct depending on which pairing method is used by the application.   /* SMP Data */ gapPairingParameters_t gPairingParameters = { .withBonding = (bool_t)gAppUseBonding_d, /* If Secure Connections pairing is supported, then set Security Mode 1 Level 4 */ /* If Legacy pairing is supported, then set Security Mode 1 Level 3 */ #if (gAppUseSecureConnections_d) .securityModeAndLevel = gSecurityMode_1_Level_4_c, #else .securityModeAndLevel = gSecurityMode_1_Level_3_c, #endif .maxEncryptionKeySize = mcEncryptionKeySize_c, .localIoCapabilities = gIoKeyboardDisplay_c, /* OOB Available enabled when app_preinclude.h file gAppUseOob_d macro is true */ .oobAvailable = (bool_t)gAppUseOob_d, #if (gAppUseSecureConnections_d) .centralKeys = (gapSmpKeyFlags_t) (gIrk_c), .peripheralKeys = (gapSmpKeyFlags_t) (gIrk_c), #else .centralKeys = (gapSmpKeyFlags_t) (gLtk_c | gIrk_c), .peripheralKeys = (gapSmpKeyFlags_t) (gLtk_c | gIrk_c), #endif /* Secure Connections enabled when app_preinclude.h file gAppUseSecureConnections_d macro is true */ .leSecureConnectionSupported = (bool_t)gAppUseSecureConnections_d, .useKeypressNotifications = FALSE, };‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍     Changes in ble_conn_manager.c file LE Legacy Pairing If your application will use LE Legacy Pairing, then you have to implement Gap_ProvideOob in response to the gConnEvtOobRequest_c event at the BleConnManager_GapCentralEvent function. In this example, gOobOwnTKData is an array that stores the TK data which will be sent OOB to the peer device (using SPI, UART, I2C, etc)  and, at the same time, is the TK data that will be provided to the stack using Gap_ProvideOob. This data must be common on both Central and Peripheral devices, so the procedure to share the TK depends entirely on your application. Oob_SendLocalTKValueToPeer is a custom synchronous function that demonstrates how you can implement a custom API that sends the local TK to the peer device using other protocols (SPI, UART, I2C, etc).   static uint8_t gOobOwnTKData[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; void BleConnManager_GapCentralEvent(deviceId_t peerDeviceId, gapConnectionEvent_t* pConnectionEvent) { switch (pConnectionEvent->eventType) { case gConnEvtConnected_c: { ... ... ... } break; ... ... ... case gConnEvtPairingResponse_c: { /* Send Legacy OOB data to the peer */ #if (gAppUseOob_d & !gAppUseSecureConnections_d) Oob_SendLocalTKValueToPeer(&gOobOwnTKData[0]); #endif } break; ... ... ... #if (gAppUseOob_d && !gAppUseSecureConnections_d) case gConnEvtOobRequest_c: { /* The stack has requested the LE Legacy OOB data*/ (void)Gap_ProvideOob(peerDeviceId, &gOobOwnTKData[0]); } break; #endif‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ ... ... ... } }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍     LE Secure Connections Pairing When using Secure Connections Pairing, the application must handle two events at the BleConnManager_GapCentralEvent function. In gConnEvtPairingResponse_c event, you must implement Gap_LeScGetLocalOobData API to generate the local (r, Cr) values. The gConnEvtLeScOobDataRequest_c event indicates that the application is requesting the (r, Cr) values previously received OOB from the peer device (using SPI, UART, I2C, etc). Such values are contained into gOobReceivedRandomValueFromPeer and gOobReceivedConfirmValueFromPeer buffers. You must implement Gap_LeScSetPeerOobData in response to gConnEvtLeScOobDataRequest_c, This function has two parameters, the device ID of the peer and a pointer to a gapLeScOobData_t type struct. This struct is filled with the data contained in gOobReceivedRandomValueFromPeer and gOobReceivedConfirmValueFromPeer buffers.   gapLeScOobData_t gPeerOobData; static uint8_t gOobReceivedRandomValueFromPeer[gSmpLeScRandomValueSize_c]; /*!< LE SC OOB r (Random value) */ static uint8_t gOobReceivedConfirmValueFromPeer[gSmpLeScRandomConfirmValueSize_c]; /*!< LE SC OOB Cr (Random Confirm value) */ void BleConnManager_GapCentralEvent(deviceId_t peerDeviceId, gapConnectionEvent_t* pConnectionEvent) { switch (pConnectionEvent->eventType) { case gConnEvtConnected_c: { ... ... ... } break; ... ... ... case gConnEvtPairingResponse_c: { /* The peripheral has acepted pairing, get local LE Secure Connections OOB data */ #if (gAppUseOob_d && gAppUseSecureConnections_d) (void)Gap_LeScGetLocalOobData(); #endif } break; ... ... ... #if (gAppUseOob_d && gAppUseSecureConnections_d) case gConnEvtLeScOobDataRequest_c: { /* The stack has requested the peer LE Secure Connections OOB data. Fill the gPeerOobData struct and provide it to the stack */ FLib_MemCpy(gPeerOobData.randomValue, &gOobReceivedRandomValueFromPeer[0], gSmpLeScRandomValueSize_c); FLib_MemCpy(gPeerOobData.confirmValue, &gOobReceivedConfirmValueFromPeer[0], gSmpLeScRandomConfirmValueSize_c); Gap_LeScSetPeerOobData(peerDeviceId, &gPeerOobData); } break; #endif ... ... ... } }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍   The gLeScPublicKeyRegenerated_c event in the BleConnManager_GenericEvent function must be handled using the Gap_LeScGetLocalOobData API as depicted below. Each time that Gap_LeScGetLocalOobData is executed by the software, it generates, asynchronously, the gLeScLocalOobData_c event (also handled in the BleConnManager_GenericEvent function) indicating that the local (r, Cr) values were successfully generated and you can read them using the pGenericEvent->eventData.localOobData pointer to send it OOB to the peer device. In this example, Oob_SendLocalRandomValueToPeer and Oob_SendLocalConfirmValueToPeer  are custom synchronous functions that demonstrate how you can implement a custom API that sends the local (r, Cr) read from pGenericEvent->eventData.localOobData pointer to the peer device using other protocols (SPI, UART, I2C, etc).   void BleConnManager_GenericEvent(gapGenericEvent_t* pGenericEvent) { switch (pGenericEvent->eventType) { case gInitializationComplete_c: { ... ... ... } break; ... ... ... #if (defined(gAppUsePairing_d) && (gAppUsePairing_d == 1U)) case gLeScPublicKeyRegenerated_c: { /* Key pair regenerated -> reset pairing counters */ mFailedPairings = mSuccessfulPairings = 0; /* Local LE Secure Connections OOB data must be refreshed whenever this event occurs */ #if (gAppUseOob_d && gAppUseSecureConnections_d) (void)Gap_LeScGetLocalOobData(); #endif } break; #endif ... ... ... #if (gAppUseOob_d && gAppUseSecureConnections_d) case gLeScLocalOobData_c: { /* Get the local LE Secure Connections OOB data and send to the peer */ Oob_SendLocalRandomValueToPeer((uint8_t*)pGenericEvent->eventData.localOobData.randomValue); Oob_SendLocalConfirmValueToPeer((uint8_t*)pGenericEvent->eventData.localOobData.confirmValue); } break; #endif ... ... ... } }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍     Simplified Flow Diagram of OOB Central and Peripheral Events LE Legacy Pairing The following figure shows a simplified flow diagram of the LE Legacy OOB pairing example in this document. The LE Central device is the one that contains the OOB TK data that will be shared OOB using the custom Oob_SendLocalTKValueToPeer function. It must be implemented at the gConnEvtPairingResponse_c event to ensure that both devices know the OOB TK before to execute Gap_ProvideOob since this function requests this data. If the OOB data is correct on both sides, the pairing procedure ends, and it is noticed through gConnEvtPairingComplete_c. LE Secure Connections Pairing The following figure shows a simplified flow diagram of the LE Secure Connections OOB pairing example in this document. After both devices enter in connection, the data that will be shared OOB using the custom Oob_SendLocalRandomValueToPeer and Oob_SendLocalConfirmValueToPeer  functions is yielded by Gap_LeScGetLocalOobData on both sides. The last one must be implemented at gConnEvtPairingResponse_c and gConnEvtPairingRequest_c events to ensure that both devices know the Peripheral and Central (r, Cr) OOB data before to execute Gap_LeScSetPeerOobData since this function requests this data. If the OOB data is correct on both sides, the pairing procedure ends, and it is noticed through gConnEvtPairingComplete_c. This is how OOB pairing can be implemented in your project. I hope this document will be useful to you. Please, let us know any questions or comments. 
View full article
Introduction HCI Application is a Host Controller Interface application which provides a serial communication to interface with the KW40/KW41/KW35/KW36/QN9080 BLE radio part. It enables the user to have a way to control the radio through serial commands. The format of the HCI Command Packet it’s composed of the following parts:     Each command is assigned a 2 byte Opcode which it’s divided into two fields, called the OpCode Group Field (OGF) and OpCode Command Field (OCF). The OGF uses the upper 6 bits of the Opcode, while the OCF corresponds to the remaining 10 bits. The OGF of 0x3F is reserved for vendor-specific debug commands. The organization of the opcodes allows additional information to be inferred without fully decoding the entire Opcode. For further information regarding this topic, please check the BLUETOOTH SPECIFICATION Version 5.0 | Vol 2, Part E, 5.4 EXCHANGE OF HCI-SPECIFIC INFORMATION.   Adding HCI Custom Commands Example This document will guide you through the implementation of custom HCI commands in the KW36. For this example, we will include the following set of custom commands: 01 50 FC 00 – This command is to send a continuous unmodulated wave using a defined channel and output power (default: frequency 2.402GHz and PA_POWER register set to 0x3E).  01 4F FC 00 – This command is to stop the continuous unmodulated wave and configure the radio in Bluetooth LE mode again. This way you can continue sending adopted HCI commands. 01 00 FC 00 – Set the Channel 0 Freq 2402 MHz 01 01 FC 00 – Set the Channel 19 Freq 2440 MHz 01 02 FC 00 – Set the Channel 39 Freq 2480 MHz 01 10 FC 00 – Set the PA_POWER 1 01 11 FC 00 – Set the PA_POWER 32 01 12 FC 00 – Set the PA_POWER 62 The changes described in the following sections were based on the HCI Black Box SDK example (it is located at wireless_examples -> bluetooth -> hci_bb)   Changes in hci_transport.h The "hci_transport.h" file is located at bluetooth->hci_transport->interface folder. Include the following macros in ''Public constants and macros" #define gHciCustomCommandOpcodeUpper (0xFC50) #define gHciCustomCommandOpcodeLower (0xFC00) #define gHciInCustomVendorCommandsRange(x) (((x) <= gHciCustomCommandOpcodeUpper) && \ ((x) >= gHciCustomCommandOpcodeLower))‍‍‍‍‍‍‍‍ Declare a function to install the custom command as follows: void Hcit_InstallCustomCommandHandler(hciTransportInterface_t mCustomInterfaceHandler);‍   Changes in hcit_serial_interface.c The "hci_transport.h" file is located at bluetooth->hci_transport->source folder. Add the following in "Private memory declarations" static hciTransportInterface_t mCustomTransportInterface = NULL;‍ Modify the Hcit_SendMessage function as follows: static inline void Hcit_SendMessage(void) { uint16_t opcode = 0; /* verify if this is an event packet */ if(mHcitData.pktHeader.packetTypeMarker == gHciEventPacket_c) { /* verify if this is a command complete event */ if(mHcitData.pPacket->raw[0] == gHciCommandCompleteEvent_c) { /* extract the first opcode to verify if it is a custom command */ opcode = mHcitData.pPacket->raw[3] | (mHcitData.pPacket->raw[4] << 8); } } /* verify if command packet */ else if(mHcitData.pktHeader.packetTypeMarker == gHciCommandPacket_c) { /* extract opcode */ opcode = mHcitData.pPacket->raw[0] | (mHcitData.pPacket->raw[1] << 8); } if(gHciInCustomVendorCommandsRange(opcode)) { if(mCustomTransportInterface) { mCustomTransportInterface( mHcitData.pktHeader.packetTypeMarker, mHcitData.pPacket, mHcitData.bytesReceived); } } else { /* Send the message to HCI */ (void)mTransportInterface(mHcitData.pktHeader.packetTypeMarker, mHcitData.pPacket, mHcitData.bytesReceived); } mHcitData.pPacket = NULL; mPacketDetectStep = mDetectMarker_c; }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ Develop the function to install the custom command as follows:   void Hcit_InstallCustomCommandHandler(hciTransportInterface_t mCustomInterfaceHandler) { OSA_InterruptDisable(); mCustomTransportInterface = mCustomInterfaceHandler; OSA_InterruptEnable(); }‍‍‍‍‍‍   Changes in hci_black_box.c This is the main application file, and it is located at source folder. Include the following files to support our HCI custom commands #include "hci_transport.h" #include "fsl_xcvr.h"‍‍ Define the following macros which represent the opcode for each custom command #define CUSTOM_HCI_CW_ON (0xFC50) #define CUSTOM_HCI_CW_OFF (0xFC4F) #define CUSTOM_HCI_CW_SET_CHN_0 (0xFC00) /*Channel 0 Freq 2402 MHz*/ #define CUSTOM_HCI_CW_SET_CHN_19 (0xFC01) /*Channel 19 Freq 2440 MHz*/ #define CUSTOM_HCI_CW_SET_CHN_39 (0xFC02) /*Channel 39 Freq 2480 MHz*/ #define CUSTOM_HCI_CW_SET_PA_PWR_1 (0xFC10) /*PA_POWER 1 */ #define CUSTOM_HCI_CW_SET_PA_PWR_32 (0xFC11) /*PA_POWER 32 */ #define CUSTOM_HCI_CW_SET_PA_PWR_62 (0xFC12) /*PA_POWER 62 */ #define CUSTOM_HCI_CW_EVENT_SIZE (0x04) #define CUSTOM_HCI_EVENT_SUCCESS (0x00) #define CUSTOM_HCI_EVENT_FAIL (0x01)‍‍‍‍‍‍‍‍‍‍‍ Add the following application variables static uint16_t channelCC = 2402; static uint8_t powerCC = 0x3E; uint8_t eventPacket[6] = {gHciCommandCompleteEvent_c, CUSTOM_HCI_CW_EVENT_SIZE, 1, 0, 0, 0 };‍‍‍‍‍‍ Declare the handler for our custom commands bleResult_t BleApp_CustomCommandsHandle(hciPacketType_t packetType, void* pPacket, uint16_t packetSize);‍ Find the "main_task" function, and register the handler for the custom commands through "Hcit_InstallCustomCommandHandler" function. You can include it just after BleApp_Init(); /* Initialize peripheral drivers specific to the application */ BleApp_Init(); /* Register the callback for the custom commands */ Hcit_InstallCustomCommandHandler((hciTransportInterface_t)&BleApp_CustomCommandsHandle); /* Create application event */ mAppEvent = OSA_EventCreate(TRUE); if( NULL == mAppEvent ) { panic(0,0,0,0); return; }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ Develop the handler of our custom commands as follows: bleResult_t BleApp_CustomCommandsHandle(hciPacketType_t packetType, void* pPacket, uint16_t packetSize) { uint16_t opcode = 0; if(gHciCommandPacket_c == packetType) { opcode = ((uint8_t*)pPacket)[0] | (((uint8_t*)pPacket)[1] << 8); switch(opcode) { /*@CC: Set Channel */ case CUSTOM_HCI_CW_SET_CHN_0: /*@CC: Set Channel 0 Freq 2402 MHz */ channelCC=2402; break; case CUSTOM_HCI_CW_SET_CHN_19: /*@CC: Channel 19 Freq 2440 MHz*/ channelCC=2440; break; case CUSTOM_HCI_CW_SET_CHN_39: /*@CC: Channel 39 Freq 2480 MHz */ channelCC=2480; break; /*@CC: Set PA_POWER */ case CUSTOM_HCI_CW_SET_PA_PWR_1: /*@CC: Set PA_POWER 1 */ powerCC=0x01; break; case CUSTOM_HCI_CW_SET_PA_PWR_32: /*@CC: Set PA_POWER 32 */ powerCC=0x20; break; case CUSTOM_HCI_CW_SET_PA_PWR_62: /*@CC: Set PA_POWER 62 */ powerCC=0x3E; break; /*@CC: Generate a Continuous Unmodulated Signal ON / OFF */ case CUSTOM_HCI_CW_ON: /*@CC: Generate a Continuous Unmodulated Signal when pressing SW3 */ XCVR_DftTxCW(channelCC, 6); XCVR_ForcePAPower(powerCC); break; case CUSTOM_HCI_CW_OFF: /*@CC: Turn OFF the transmitter */ XCVR_ForceTxWd(); /* Initialize the PHY as BLE */ XCVR_Init(BLE_MODE, DR_1MBPS); break; default: eventPacket[5] = CUSTOM_HCI_EVENT_FAIL; break; } eventPacket[3] = (uint8_t)opcode; eventPacket[4] = (uint8_t)(opcode >> 8); eventPacket[5] = CUSTOM_HCI_EVENT_SUCCESS; Hcit_SendPacket(gHciEventPacket_c, eventPacket, sizeof(eventPacket)); } else { return gBleUnexpectedError_c; } return gBleSuccess_c; }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍   Testing Custom HCI Commands Using NXP Test Tool 12 To test HCI Black Box software, we need to install NXP Test Tool 12, from the NXP Semiconductors | Automotive, Security, IoT official web site. Once you have installed Test Tool, attach the FRDM-KW36 board to your PC and open the serial port enumerated in the start page clicking twice on the icon. Then, select "Raw Data" checkbox and type any of our custom commands, for instance, "01 01 FC 00" (Set the Channel 19 Freq 2440 MHz). Shift out the command clicking on the "Send Raw..." button. You will see the HCI Tx and Rx in the right upper corner of your screen
View full article
This patch fixes some minor issues with the Connectivity Software v1.0.2 when working with the Kinetis BLE Toolbox application for smartphones. Following issues are fixed. BLE OTAP Application: Fixes application failing to download the new image when the previous image upload has been interrupted due a disconnection. BLE Wireless UART: Fixes MTU exchange issue causing some characters not bein shown in the smartphone application in iOS and Android. Hybrid BLE + Thread console: Fixes MTU exchange issue causing some characters not bein shown in the smartphone application console in iOS and Android. Make sure the Connectivity Software version 1.0.2 is installed in your computer before proceeding to install this application.
View full article
Introduction This document guides to load a new software image in a KW41 device through Over The Air Programming bootloader. Also, are explained the details of how to set up the client software to change the storage method of the image. Software Requirements IAR Embedded Workbench IDE or MCUXpresso IDE Download both, SDK FRDM-KW41Z and SDK USB-KW41Z. Hardware Requirements FRDM-KW41Z board OTAP Memory Management During the Update Process The KW41 has a 512KB Program Flash with a flash address range from 0x0000_0000 to 0x0007_FFFF.     The OTAP application splits the flash into two independent parts, the OTAP Bootloader, and the OTAP Client. The OTAP Bootloader verifies if there is a new image available at the OTAP Client to reprogram the device. The OTAP Client software provides the Bluetooth LE custom service needed to communicate the OTAP Client device with the OTAP Server that contains the new image file (The OTAP Server device could be another FRDM-KW41Z connected to a PC with Test Tool or a Smartphone with IoT Toolbox app). Therefore, the OTAP Client device needs to be programmed twice, first with the OTAP Bootloader, then with the Bluetooth LE application supporting OTAP Client. The mechanism created to have two different software coexisting in the same device is storing each one in different memory regions. This functionality is implemented by the linker file. In the KW41 device, the bootloader has reserved a 16 KB slot of memory from 0x0000_0000 to 0x0003_FFFF, thus the left memory is reserved among other things, by the OTAP Client demo. To create a new image file for the client device, the developer needs to specify to the linker file that the code will be built with an offset of 16 KB since the first addresses must be reserved for the OTAP Bootloader. In connection state, the OTAP server sends the image packets (known as chunks) to the OTAP Client device via Bluetooth LE. The OTAP Client device can store these chunks, in first instance, at the external SPI flash or the On-Chip Flash. The destination of the code is selectable in the OTAP Client software. When the connection has finished and all chunks were sent from the OTAP Server to the OTAP Client device, the OTAP Client software writes information, such as the source of the image update (external flash or internal flash) in a portion of memory known as Bootloader Flags and then resets the MCU to execute the OTAP Bootloader code. The OTAP Bootloader reads the Bootloader Flags to get the information needed to program the device and triggers a commando to reprogram the MCU with the new application. Due to the new application was built with an offset of 16 KB, the OTAP Bootloader programs the device starting from the 0x0000_4000 address and the OTAP Client application is overwritten by the new image, therefore, after the device has been reprogrammed through this method, cannot be programmed a second time as same. Finally, the OTAP Bootloader triggers a command to start the execution of the new code automatically.     Preparing the Software to Test the OTAP Client for KW41Z Device Using IAR Embedded Workbench Program the OTAP Bootloader on the FRDM-KW41Z. Program the OTAP Bootloader software from the project included in the SDK FRDM-KW41Z at the following path, or you can simply drag and drop the pre-built binary from the following path.           OTAP Bootloader Project:          <SDK_2.2.0_FRDM-KW41Z_download_path>\boards\frdmkw41z\wireless_examples\framework\bootloader_otap\bm\iar\bootloader_otap_bm.eww            OTAP Bootloader pre-built binary:            <SDK_2.2.0_FRDM-KW41Z_download_path>\tools\wireless\binaries\bootloader_otap_frdmkw41z.bin   Open the OTAP Client project included in the SDK FRDM-KW41Z located in the following path.          <SDK_2.2.0_FRDM-KW41Z_download_path>\boards\frdmkw41z\wireless_examples\bluetooth\otap_client_att\freertos\iar\otap_client_att_freertos.eww   Customize the OTAP Client software to select the storage method. Locate the app_preinclude.h header file inside the source folder at the workspace. To select the External Flash storage method, set the "gEepromType_d" define to "gEepromDevice_AT45DB041E_c"                      To select the Internal Flash storage method, set the "gEepromType_d" define to "gEepromDevice_InternalFlash_c"   Configure the linker flags. Open the project options window (Alt + F7). In "Linker->Config" window, locate the "Configuration file symbol definitions" pane. To select the External Flash storage method, remove the "gUseInternalStorageLink_d=1" linker flag To select the Internal Flash storage method, add the "gUseInternalStorageLink_d=1" linker flag     Load the OTAP Client software on the FRDM-KW41Z board (Ctrl + D). Stop the debug session (Ctrl + Shift + D). The default linker configurations of the project allow the OTAP Client application to be stored with the proper memory offset.   Preparing the Software to Test the OTAP Client for KW41Z Device Using MCUXpresso IDE Program the OTAP Bootloader on the FRDM-KW41Z. Program the OTAP Bootloader software from the project included in the SDK FRDM-KW41Z at the following path, or you can simply drag and drop the pre-built binary from the following path.           OTAP Bootloader Project:          wireless_examples->framework->bootloader_otap->bm            OTAP Bootloader pre-built binary:            <SDK_2.2.0_FRDM-KW41Z_download_path>\tools\wireless\binaries\bootloader_otap_frdmkw41z.bin   Click on "Import SDK examples(s)" option in the "Quickstart Panel" view. Click twice on the frdmkw41z icon.     Open the OTAP Client project included in the SDK FRDM-KW41Z located in the following path.wireless_examples->bluetooth->otap_client_att->freertos     Customize the OTAP Client software to select the storage method. Locate the app_preinclude.h header file inside the source folder at the workspace. To select the External Flash storage method, set the "gEepromType_d" define to "gEepromDevice_AT45DB041E_c"                      To select the Internal Flash storage method, set the "gEepromType_d" define to "gEepromDevice_InternalFlash_c"   Configure the linker file. To select the External Flash storage method, are not required any changes in the project from this point. You can skip this step. To select the Internal Flash storage method, search the linker file located in the SDK USB-KW41Z at the following path and replace instead of the default linker file at the source folder in the OTAP Client project. You can copy (Ctrl + C) the linker file from SDK USB-KW41Z and paste (Ctrl + V) on the workspace directly. A warning message will be displayed, select "Overwrite".           Linker file at the SDK USB-KW41Z:        <SDK_2.2.0_USB-KW41Z_download_path>\boards\usbkw41z_kw41z\wireless_examples\bluetooth\otap_client_att\freertos\MKW41Z512xxx4_connectivity.ld     Save the changes in the project. Select "Debug" in the "Quickstart Panel". Once the project is already loaded on the device, stop the debug session.   Creating an S-Record Image File for FRDM-KW41Z OTAP Client in IAR Embedded Workbench Open the connectivity project that you want to program using the OTAP Bootloader from your SDK FRDM-KW41Z. This example will make use of the glucose sensor project, this is located at the following path. <SDK_2.2.0_FRDM-KW41Z_download_path>\boards\frdmkw41z\wireless_examples\bluetooth\glucose_sensor\freertos\iar\glucose_sensor_freertos.eww   Open the project options window (Alt+F7). In Linker->Config window, add the following linker flag in the “Configuration file symbol definitions” textbox.         gUseBootloaderLink_d=1     Go to the “Output Converter” window. Deselect the “Override default" checkbox, expand the “Output format” combo box and select Motorola S-records format. Click the OK button.     Rebuild the project. Search the S-Record file (.srec) in the following path.<SDK_2.2.0_FRDM-KW41Z_download_path>\boards\frdmkw41z\wireless_examples\bluetooth\glucose_sensor\freertos\iar\debug   Creating an S-Record Image File for FRDM-KW41Z OTAP Client in MCUXpresso IDE Open the connectivity project that you want to program using the OTAP Bootloader from MCUXpresso IDE. This example will make use of the glucose sensor project, this is located at the following path.        wireless_examples->bluetooth->glucose_sensor->freertos   Search the linker file located in the SDK FRDM-KW41Z at the path below and replace instead of the default linker file at the source folder in the Glucose Sensor project. You can copy (Ctrl + C) the linker file from SDK FRDM-KW41Z and paste (Ctrl + V) on the workspace directly. A warning message will be displayed, select "Overwrite".          Linker file at the SDK FRDM-KW41Z:        <SDK_2.2.0_FRDM-KW41Z_download_path>\boards\frdmkw41z\wireless_examples\bluetooth\otap_client_att\freertos\MKW41Z512xxx4_connectivity.ld     Open the new "MKW41Z512xxx4_connectivity.ld" linker file. Locate the section placement of the figure below and remove the "FILL" and the "BYTE" statements.         Build the project. Deploy the “Binaries” icon in the workspace. Click the right mouse button on the “.axf” file. Select the “Binary Utilities/Create S-Record” option. The S-Record file will be saved at “Debug” folder in the workspace with “.s19” extension.     Testing OTAP Client Demo Using IoT Toolbox App Save the S-Record file created with the steps in the last section in your smartphone at a known location. Open the IoT Toolbox App and select OTAP demo. Press “SCAN” to start scanning for a suitable advertiser. Press the “SW4” button on the FRDM-KW41Z board to start advertising. Create a connection with the found device. Press “Open” and search the S-Record file. Press “Upload” to start the transfer. Once the transfer is complete, wait a few seconds until the bootloader has finished programming the new image. The new application will start automatically. 
View full article
This guide will show a way to set up and enable an I2C Serial Interface to send a string of data instances using one of the Wireless Bluetooth SDK examples and the Serial Manager API.
View full article
I got a question related to best practices to configure a GPIO if the pin is not used. To make it short, the recommendation is to leave the GPIO floating on the PCB and leave the GPIO in its "Default" state as shown in the Signal Multiplexing table in the Reference Manual. The Default state is either “Disabled” or an analog function.   Some Kinetis devices have analog only pins (PGAx/ADCx) while most have GPIO pins with analog functions (PTx/ADCx) or digital GPIO pins   Unused pins, whether analog only or GPIO, should be left floating. Analog only pins do not have input buffers that will cause shoot-through currents when the input floats. GPIO pins with analog functions default to analog functions, which disables the digital input buffer – no shoot-through current.   The digital GPIO pins default to "Disabled", which disables the input buffers - no shoot-through currents with floating inputs.   Finally, unused pins shall not be tied to VDD or VSS. Hence, when designing your board and there are some unused pins, leave them floating on the PCB and then make sure that the software leaves the GPIO in its Default state in the MUX register. 
View full article
Introduction The MTU (Maximum Transmission Unit) in Bluetooth LE, is an informational parameter that indicates to the remote device, the maximum number of bytes that the local can handle in such channel, for example, the ATT_MTU for KW36 is fixed in 247 bytes. A few applications require to have long characteristics defined in the GATT database, and sometimes the length of the characteristic exceeds the MTU negotiated by the client and server Bluetooth LE devices. For this scenario, the Bluetooth LE specification defines a procedure to write and read the characteristic of interest. In summary, it consists in perform multiple writes and reads on the same characteristic value, using specific commands. For the "write long characteristic value" procedure, these commands are ATT_PREPARE_WRITE_REQ and ATT_EXECUTE_WRITE_REQ. For the "read long characteristic value" procedure, these commands are ATT_READ_REQ and ATT_READ_BLOB_REQ. This document provides an example of how to write and read long characteristic values, from the perspective of Client and Server devices.   APIs to Write and Read Characteristic Values Write Characteristic Values The GattClient_WriteCharacteristicValue API is used to perform any write operation. It is implemented by the GATT Client device. The following table describes the input parameters. Input Parameters Description deviceId_t deviceId Device ID of the peer device. gattCharacteristic_t * pCharacteristic Pointer to a gattCharacteristic struct type. This struct must contain a valid handle of the characteristic value in the "value.handle" field. The handle of the characteristic value that you want to write is commonly obtained after the service discovery procedure.  uint16_t valueLength This value indicates the length of the array pointed by aValue. const uint8_t * aValue Pointer to an array containing the value that will be written to the GATT database. bool_t withoutResponse If true, it means that the application wishes to perform a "Write Without Response", in other words, when the command will be ATT_WRITE_CMD or ATT_SIGNED_WRITE_CMD. bool_t signedWrite If withoutResponse and signedWrite are both true, the command will be ATT_SIGNED_WRITE_CMD. If withoutResponse is false, this parameter is ignored. bool_t doReliableLongCharWrites This field must be set to true if the application needs to write a long characteristic value. const uint8_t * aCsrk If withoutResponse and signedWrite are both true, this pointer must contain the CSRK to sign the data. Otherwise, this parameter is ignored.   Read Characteristic Values The GattClient_ReadCharacteristicValue API is used to perform read operations. It is implemented by the GATT Client device. The following table describes the input parameters. Input Parameters Description deviceId_t deviceId Device ID of the peer device. gattCharacteristic_t * pIoCharacteristic Pointer to a gattCharacteristic struct type. This struct must contain a valid handle of the characteristic value in the "value.handle" field. The handle of the characteristic value that you want to write is commonly obtained after the service discovery procedure. As well, the "value.paValue" field of this struct, must point to an array which will contain the characteristic value read from the peer. unit16_t maxReadBytes The length of the characteristic value that should be read. This API takes care of the long characteristics, so there is no need to worry about a special parameter or configuration. The following sections provide a functional example of how to write and read long characteristics. This example was based on the temperature collector and temperature sensor SDK examples. The example also shows how to create a custom service at the GATT database and how to discover its characteristics.   Bluetooth LE Server (Temperature Sensor) Modifications in gatt_uuid128.h Define the 128 bit UUID of the "custom service" which will be used for this example. Add the following code: /* Custom service */ UUID128(uuid_service_custom, 0xE0, 0x1C, 0x4B, 0x5E, 0x1E, 0xEB, 0xA1, 0x5C, 0xEE, 0xF4, 0x5E, 0xBA, 0x00, 0x01, 0xFF, 0x01) UUID128(uuid_char_custom, 0xE0, 0x1C, 0x4B, 0x5E, 0x1E, 0xEB, 0xA1, 0x5C, 0xEE, 0xF4, 0x5E, 0xBA, 0x01, 0x01, 0xFF, 0x01)‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ Modifications in gatt_db.h Define the characteristics of the "custom service", for this example, our service will have just one characteristic, it can be written or read, and it has a variable-length limited to 400 bytes (remember that the ATT_MTU of KW36 is 247 byte, so with this length, we ensure long writes and reads). Add the following code: PRIMARY_SERVICE_UUID128(service_custom, uuid_service_custom) CHARACTERISTIC_UUID128(char_custom, uuid_char_custom, (gGattCharPropWrite_c | gGattCharPropRead_c)) VALUE_UUID128_VARLEN(value_custom, uuid_char_custom, (gPermissionFlagWritable_c | gPermissionFlagReadable_c), 400, 1)‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ Modifications in app_preinclude.h One of the most important considerations to write and read long characteristics is the memory allocation needed for this. You must increment the current "AppPoolsDetails_c" configuration, the "_block_size_" and "_number_of_blocks_". Please ensure that "_block_size_" is aligned with 4 bytes. Once you have found the configuration that works in your application, please follow the steps in Memory Pool Optimizer on MKW3xA/KW3xZ Application Note, to found the best configuration without waste memory resources. For this example, configure "AppPoolsDetails_c" as follows: /* Defines pools by block size and number of blocks. Must be aligned to 4 bytes.*/ #define AppPoolsDetails_c \ _block_size_ 264 _number_of_blocks_ 8 _eol_‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍   Bluetooth LE Client (Temperature Collector) Modifications in gatt_uuid128.h Define the 128 bit UUID of the "custom service" which will be used for this example. Add the following code: /* Custom service */ UUID128(uuid_service_custom, 0xE0, 0x1C, 0x4B, 0x5E, 0x1E, 0xEB, 0xA1, 0x5C, 0xEE, 0xF4, 0x5E, 0xBA, 0x00, 0x01, 0xFF, 0x01) UUID128(uuid_char_custom, 0xE0, 0x1C, 0x4B, 0x5E, 0x1E, 0xEB, 0xA1, 0x5C, 0xEE, 0xF4, 0x5E, 0xBA, 0x01, 0x01, 0xFF, 0x01)‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ Modifications in temperature_collector.c 1. Define the following variables at the "Private type definitions" section: typedef struct customServiceConfig_tag { uint16_t hService; uint16_t hCharacteristic; } customServiceConfig_t; typedef struct appCustomInfo_tag { tmcConfig_t tempClientConfig; customServiceConfig_t customServiceClientConfig; }appCustomInfo_t; typedef enum { mCustomServiceWrite = 0, mCustomServiceRead }customServiceState_t;‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 2. Add two arrays of 400 bytes, one to send and the other to receive the data from the server in "Private memory declarations" section: /* Dummy array for custom service */ uint8_t mWriteDummyArray[400]; uint8_t mReadDummyArray[400];‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 3. Define a new function in "Private functions prototypes" section, to write and read the characteristic value: static void BleApp_SendReceiveCustomService (customServiceState_t state);‍‍‍‍ 4. Locate the "BleApp_Config" function, add the following code here to fill the "mWriteDummyArray" with a known pattern before to write our custom characteristic. static void BleApp_Config(void) { uint16_t fill_pattern; /* Fill pattern to write long characteristic */ for (fill_pattern = 0; fill_pattern<400; fill_pattern++) { mWriteDummyArray[fill_pattern] = (uint8_t)fill_pattern; } /* Configure as GAP Central */ BleConnManager_GapCommonConfig(); ... ... }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 5. Locate the "BleApp_StoreServiceHandles" function. Modify this function to include our custom service in the service discovery procedure. This is to save the handle of the custom characteristic since it is used by GattClient_WriteCharacteristicValue and GattClient_ReadCharacteristicValue APIs. static void BleApp_StoreServiceHandles ( gattService_t *pService ) { uint8_t i,j; if ((pService->uuidType == gBleUuidType128_c) && FLib_MemCmp(pService->uuid.uuid128, uuid_service_temperature, 16)) { /* Found Temperature Service */ mPeerInformation.customInfo.tempClientConfig.hService = pService->startHandle; for (i = 0; i < pService->cNumCharacteristics; i++) { if ((pService->aCharacteristics[i].value.uuidType == gBleUuidType16_c) && (pService->aCharacteristics[i].value.uuid.uuid16 == gBleSig_Temperature_d)) { /* Found Temperature Char */ mPeerInformation.customInfo.tempClientConfig.hTemperature = pService->aCharacteristics[i].value.handle; for (j = 0; j < pService->aCharacteristics[i].cNumDescriptors; j++) { if (pService->aCharacteristics[i].aDescriptors[j].uuidType == gBleUuidType16_c) { switch (pService->aCharacteristics[i].aDescriptors[j].uuid.uuid16) { /* Found Temperature Char Presentation Format Descriptor */ case gBleSig_CharPresFormatDescriptor_d: { mPeerInformation.customInfo.tempClientConfig.hTempDesc = pService->aCharacteristics[i].aDescriptors[j].handle; break; } /* Found Temperature Char CCCD */ case gBleSig_CCCD_d: { mPeerInformation.customInfo.tempClientConfig.hTempCccd = pService->aCharacteristics[i].aDescriptors[j].handle; break; } default: ; /* No action required */ break; } } } } } } else if ((pService->uuidType == gBleUuidType128_c) && FLib_MemCmp(pService->uuid.uuid128, uuid_service_custom, 16)) { /* Found Custom Service */ mPeerInformation.customInfo.customServiceClientConfig.hService = pService->startHandle; if (pService->cNumCharacteristics > 0U && pService->aCharacteristics != NULL) { /* Found Custom Characteristic */ mPeerInformation.customInfo.customServiceClientConfig.hCharacteristic = pService->aCharacteristics[0].value.handle; } } else { ; } }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 6. Develop the "BleApp_SendReceiveCustomService" as shown below. This function is used to write and read the custom characteristic values using long operations. Focus your attention in this function, here is the example of how to use GattClient_WriteCharacteristicValue and GattClient_ReadCharacteristicValue APIs to write and read long characteristic values. Note that the "characteristic" struct was filled before to use the last APIs, with the handle of our custom characteristic and a destination address to receive the value read from the peer. Note that the "doReliableLongCharWrites" field must be TRUE to allow long writes using GattClient_WriteCharacteristicValue.  static void BleApp_SendReceiveCustomService (customServiceState_t state) { bleResult_t bleResult; gattCharacteristic_t characteristic; /* Verify if there is a valid peer */ if (gInvalidDeviceId_c != mPeerInformation.deviceId) { /* Fill the characteristic struct with a read destiny and the custom service handle */ characteristic.value.handle = mPeerInformation.customInfo.customServiceClientConfig.hCharacteristic; characteristic.value.paValue = &mReadDummyArray[0]; /* Try to write the custom characteristic value */ if(mCustomServiceWrite == state) { bleResult = GattClient_WriteCharacteristicValue(mPeerInformation.deviceId, &characteristic, (uint16_t)400, &mWriteDummyArray[0], FALSE, FALSE, TRUE, NULL); /* An error occurred while trying to write the custom characteristic value, disconnect */ if(gBleSuccess_c != bleResult) { (void)Gap_Disconnect(mPeerInformation.deviceId); } } /* Try to read the custom characteristic value */ else { bleResult = GattClient_ReadCharacteristicValue(mPeerInformation.deviceId, &characteristic, (uint16_t)400); /* An error occurred while trying to read the custom characteristic value, disconnect */ if(gBleSuccess_c != bleResult) { (void)Gap_Disconnect(mPeerInformation.deviceId); } } } }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 7. Modify the "BleApp_GattClientCallback" as shown below. In this function, we implement the "BleApp_SendReceiveCustomService" which writes or reads the characteristic depending on the input parameter "state". The expected behavior of this example is, first, write the 400-byte pattern contained in the mWriteDummyArray to our custom characteristic value, just after to write the characteristic descriptor of the temperature service (which is indicated by this callback in the gGattProcWriteCharacteristicDescriptor_c event). When the write has been executed successfully, it is indicated in this callback, by the "gGattProcWriteCharacteristicValue_c" event, therefore, here we can execute our function to read the characteristic value. Then "gGattProcReadCharacteristicValue_c" event is triggered if the read has been completed, here, we compare the value written with the value read from the GATT server and, if both are the same, the green RGB led should turn on indicating that our long characteristic has been written and read successfully, otherwise, the GATT client disconnects from the GATT server.   static void BleApp_GattClientCallback( deviceId_t serverDeviceId, gattProcedureType_t procedureType, gattProcedureResult_t procedureResult, bleResult_t error ) { if (procedureResult == gGattProcError_c) { attErrorCode_t attError = (attErrorCode_t)(uint8_t)(error); if (attError == gAttErrCodeInsufficientEncryption_c || attError == gAttErrCodeInsufficientAuthorization_c || attError == gAttErrCodeInsufficientAuthentication_c) { /* Start Pairing Procedure */ (void)Gap_Pair(serverDeviceId, &gPairingParameters); } BleApp_StateMachineHandler(serverDeviceId, mAppEvt_GattProcError_c); } else { if (procedureResult == gGattProcSuccess_c) { switch(procedureType) { case gGattProcReadCharacteristicDescriptor_c: { if (mpCharProcBuffer != NULL) { /* Store the value of the descriptor */ BleApp_StoreDescValues(mpCharProcBuffer); } break; } case gGattProcWriteCharacteristicDescriptor_c: { /* Try to write to the custom service */ BleApp_SendReceiveCustomService(mCustomServiceWrite); } break; case gGattProcWriteCharacteristicValue_c: { /* If write to the custom service was completed, try to read the custom service */ BleApp_SendReceiveCustomService(mCustomServiceRead); } break; case gGattProcReadCharacteristicValue_c: { /* If read to the custom service was completed, compare write and read buffers */ if(FLib_MemCmp(&mWriteDummyArray[0], &mReadDummyArray[0], 400)) { Led3On(); } else { (void)Gap_Disconnect(mPeerInformation.deviceId); } } break; default: { ; /* No action required */ break; } } BleApp_StateMachineHandler(serverDeviceId, mAppEvt_GattProcComplete_c); } } /* Signal Service Discovery Module */ BleServDisc_SignalGattClientEvent(serverDeviceId, procedureType, procedureResult, error); }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ Modifications in app_preinclude.h One of the most important considerations to write and read long characteristics is the memory allocation needed for this. You must increment the current "AppPoolsDetails_c" configuration, the "_block_size_" and "_number_of_blocks_". Please ensure that "_block_size_" is aligned with 4 bytes. You can know when the current configuration of pools do not satisfy the application requirements if the return value of either "GattClient_WriteCharacteristicValue" or "GattClient_ReadCharacteristicValue " is "gBleOutOfMemory_c" instead of "gBleSuccess_c" (If it is the case, the device will disconnect to the peer according to the code in step 6 in "Modifications in temperature_collector.c"). Once you have found the configuration that works in your application, please follow the steps in Memory Pool Optimizer on MKW3xA/KW3xZ Application Note, to found the best configuration without waste memory resources. For this example, configure "AppPoolsDetails_c" as follows: /* Defines pools by block size and number of blocks. Must be aligned to 4 bytes.*/ #define AppPoolsDetails_c \ _block_size_ 112 _number_of_blocks_ 6 _eol_ \ _block_size_ 256 _number_of_blocks_ 3 _eol_ \ _block_size_ 280 _number_of_blocks_ 2 _eol_ \ _block_size_ 432 _number_of_blocks_ 1 _eol_‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍   Please let us know any question regarding this topic.
View full article
The TWR-KW2x board's OpenSDA is programmed with PE Micro's OpenSDA firmware which enables MSD, debugging and CDC Serial port. This firmware can be easily modified by putting the K20 part in bootloader mode and load another firmware to it with a simple drag and drop. Follow these steps to modify the OpenSDA firmware on the TWR-KW2x board. Segger's OpenSDA v2.1 will be used as an example of the new OpenSDA firmware (Instead of the default PE Micro's) 1. Unplug the board 2. Insert a Jumper in J30 to put the device in Bootloader mode 3. Plug in the board (Mini-USB) 4. Device will be enumerated as a "Drive Disk" But now with a "Bootloader" label 5. Drag and Drop the Segger's JLink_OpenSDA_V2_1.bin firmware (https://segger.com/opensda.html) into the Bootloader unit 6. Unplug the board 7. Remove Jumper 8. Plug in the board (Mini-USB) Now you should see the board being enumerated as "JLink CDC UART Port", allowing serial port communication. You should also be able to debug your application using J-Link debugging interface through the OpenSDA interface, no need of external hardware. Note1: Drivers can be found at Segger's website (https://segger.com/opensda.html) Note2: Jumper has to be in place in J29 for debugging Note3: IDE options must be set to use J-Link Driver
View full article
This document describes the implementation of the Connected Home Gateway for the Internet of Things (IoT) and its controller implemented in a Smart device (tablet) running Android OS. The gateway is intended to serve as a communication bridge between WiFi/Ethernet and ZigBee Protocol, making every ZigBee-enabled device accessible and controllable from any smart device with Wi-Fi capabilities such as a smart phone or tablet. This will remove the need of having a ZigBee transceiver in every mobile device attempting to control the house appliances. In general, users will be able to: Remote control of Home Appliances using ZigBee protocol Any WiFi-enabled device could control the appliances without a ZigBee transceiver Achieve bi-directional communication between users and appliances Real system implementation would require a powerful MCU to manage all WiFi/Ethernet communication and a second MCU to manage all ZigBee communications. The Kinetis K60 and KW24 were selected among the different options available.
View full article
This video shows how to load the Open SDA software from PE micro to the TWR-KW2x in order to debug applications using USB port and without needing external JTAG debuggers. Required downloads: TWR-KW2x Board Support Package:Kinetis KW2x Tower System Modules|Freescale PE Micro - Open SDA: P&E Microcomputer Systems
View full article
This document and the attached files are maintained up to date in collaboration with Dragos Musoiu. This document is a supplement for USB MSC device bootloader revision for FRDM-KL25Z (IAR) written by Kai Liu and describes the bootloader support for USB-KW24D512. How to use 1) Connect the USB-KW24D512 to the PC USB port; 2) Download the attached file ‘USB_KW24D512_MSD_Bootloader.bin’ to the flash memory of the MKW24D512 SiP following the next steps: Connect a J-Link programmer to the PC USB port (other than the one used for the USB-KW24D512 dongle); Navigate to your J-Link driver folder using a command console and type ‘jlink.exe’ followed by enter; After the apparition of the J-Link prompter, type ‘unlock kinetis’ followed by enter; Wait for the unlock command confirmation and after, type ‘device mkw24d512xxx5’ followed by enter; After the J-Link prompter appears type ‘loadbin USB_KW24D512_MSD_Bootloader.bin 0’ followed by enter; (Be sure you copied the ‘USB_KW24D512_MSD_Bootloader.bin’ file in the same directory with jlink.exe otherwise, type the command specifying the full path of the binary file); After the flashing process successfully finished type ‘exit’ followed by enter. 3) Reset or reconnect the USB-KW24D512; 4) The OS will prompt MSD device connecting and then BOOTLOADER drive will appear. The bootloader software was tested on Microsoft Windows 10, Microsoft Windows 8.1, Microsoft Windows 7, Ubuntu 14.04 and MAC operating systems. 5) Copy and paste any user application .SREC or .bin file into BOOTLOADER drive; 6) If a valid .SREC or .bin file was given, the board restarts and starts to run the user application. Please refer to the Notes section in order to create valid .SREC or .bin files. Note:            The bootloader has conditional jump to user application. The condition is the state of the SW1 button (PTC4). If the button is pressed (PTC4 grounded) during reset, the bootloader sequence will start, installing BOOTLOADER drive, as described before. Else if the button is released during reset, the SP and PC will be updated from address 0xC000. This means, the user application has to use a linker file which forces the application start address to 0xC000. If a valid SP and PC value is found at address 0xC000, the user application is launched. The bootloader application is located in the flash memory of the MKW24D512 SiP, from address 0x0000 to 0xBFFF, so the user application should not put any code in this memory region. Avoid using .SREC or .bin files having program bytes or fill patterns in the bootloader section. Attached files: USB_KW24D512_MSD_Bootloader.bin – bootloader binary file for USB-KW24D512; Pflash_512KB_0xC000.icf – IAR linker file for user application development; 802.15.4SnifferOnUSB.bin – user application demo binary file for KW24D512-USB. Be aware that the file ‘802.15.4SnifferOnUSB.srec’ is linked according to the above memory restrictions and is working only with the bootloader presented in this document.
View full article
QTool is a PC software tool that works with QN9080 USB dongle to assist in the development of BLE projects with the QN9080. You control the dongle via the QTool software, which issues and receives FSCI (Framework Serial Communication Interface) formatted commands over a virtual COM port. The dongle can then act either as a master or a slave to a QN9080DK board over BLE.  Before using the BLE dongle with QTool though, the firmware on the QN9080 Dongle must be updated. The updated firmware can be found inside the QTool installation directory, and you will need to put the dongle into bootloader mode to drag-and-drop new firmware on it. Updating the Firmware on the QN9080 Dongle. 1. Install QTool: https://www.nxp.com/webapp/sps/download/license.jsp?colCode=Connectivity-QTool-Setup   2. Plug the QN9080 Dongle into a USB port on your computer 3. Using a wire, connect TP5 to ground. You can use either TP4 or the USB shield for GND. 4. While that wire is connected, press the reset button on the dongle. This will now put the dongle into bootloader mode. 5. A drive will enumerate on your computer named “CRP_DISABLD”     6. You can now remove the wire 7. Delete the firmware.bin file found in that drive 8. Drag-and-drop the firmware.bin file found in C:\NXP\Connectivity QTool\bin files into that enumerated drive. 9. Once done copying, unplug and replug in the USB Dongle, and the new firmware will now be running.  Installing the QN9080 Dongle Driver The dongle will enumerate as a USB CDC COM device. If the CDC driver is not automatically detected, you will need to manually install the driver. 1. Right-click Computer and choose Properties, the System Management window appears. 2. Click Device Manager and navigate to MCU VIRTUAL COM DEMO      3. Right-click the device MCU VIRTUAL COM DEMO and choose Update Driver Software 4. Click the  Browse my computer for driver software option in the window. 5. Click Browse button to go to the folder  C:\NXP\Connectivity QTool\drivers 6. Click the Next button at the bottom to install the driver.  7. After the driver is installed you will see the Virtual Com Port device under the Ports category    Using QTool: Now that the QN9080 dongle has the updated firmware and has the correct driver installed, you can follow the instructions in the QTool documentation found at C:\NXP\Connectivity QTool\UM11085.pdf Related documentation: QN908x Quick Start Guide QN908x DK User's Guide
View full article
FRDM-KW36 Software Development Kit (SDK) includes drivers and examples of FlexCAN module for KW36 which can be easily configured for a custom communication. For example, if user want to change the default baud rate from FlexCAN driver demo examples then the only needed change is the default value on "config->baudRate" and "config->baudRateFD" from "FLEXCAN_GetDefaultConfig" function (See Figure 1). Segments within a bit time will be automatically configured to obtain the desired baud rate. By default, demos are configured to work with CAN FD communication. Figure 1. FRDM-KW36's default baudrate from flexcan_interrupt_transfer driver example Even so, there are cases where segments within a bit time are not well configured and it's necessary that user configure segments manually. An example occurs by setting the maximum FD baud rate "3.2MHz" using the 32MHz xtal or "2.6MHz" using a 26MHz xtal where demo reports an error. See Figure 2. Figure 2. Error by setting maximum baud rate When this error occurs, the fix is on setting the timing config parameters correctly by including the definition of SET_CAN_QUANTUM on application source file (see Figure 3) and then declare and initialize the timing config parameters shown in Figure 4. Figure 3. SET_CAN_QUANTUM define Figure 4. Custom timing config parameters For this example we are going to show how to calculate timing config parameters in an scenario where a CAN FD communication is used with baud rate of 500kHz on nominal phase and 3.2MHz on FD phase. See Figure 5.  To do it, we need to calculate Time Quanta and value of segments within the bit time.    Figure 5. Custom CAN FD baudrate KW36 Reference Manual in chapter "37.4.8.7 Protocol timing" shows the segments within a bit time for CAN nominal phase configured in "CAN_CTRL1" register (see Figure 6), and segments for FD phase configured in CAN_FDCBT register (see Figure 7). Figure 6. Segment within a bit time for CAN nominal phase Figure 7. Segment within a bit time for CAN FD phase Before calculating the value of segments, first we need to calculate the Time Quanta which is the atomic number of time handled by the CAN engine. The formula to calculate Time Quanta is shown in Figure 8 taken from KW36 Reference Manual. Figure 8. Time Quanta Formula CANCLK can be selected by CLKSRC bits on CAN_CTRL1 register as shown in Figure 9, where the options are Peripheral clock=20MHz or Oscillator clock (16MHz if using 32MHz xtal or 13MHz if using 26MHz xtal). The recomiendation is to use the Oscillator clock due to peripheral clock can have jitter that affect communication.  Figure 9. CAN clocks To select the Oscillator clock, search for flexcanConfig.clkSrc definition and set it to kFLEXCAN_ClkSrcOsc as shown in Figure 10. Figure 10. CANCLK selection Next step is selecting the PRESDIV value for nominal phase and FPRESDIV for FD phase. You have to select the right value to achieve the TQ needed to obtain the configured baudrate. For this example, let's set FPRESDIV value to 0 and PRESDIV value to 3. TQ calculation for nominal phase: TQ = (PRESDIV + 1) / CANCLK = (3 + 1) / 16000000 = 0.00000025 TQ calculation for FD phase: TQ = (FPRESDIV + 1) / CANCLK = (0 + 1) / 16000000 = 0.0000000625 The bit rate, which defines the rate of CAN message is given by formula shown in Figure 11 taken from KW36 Reference Manual. Figure 11. CAN Bit Time and Bit Rate Formulas With this info and with our TQ calculated, we can deduce that we need: For Nominal phase: 8 = Number of Time Quanta in 1 bit time For FD phase: 5 = Number of Time Quanta in 1 bit time Now, let's define the value of segments. For nominal phase: Bit Time =  (number of Tq in 1 bit time) x Tq CAN Bit Time = (1 + (PROPSEG + PSEG1 + 2) + (PSEG2 + 1) ) x Tq CAN Bit Time = (1 + (1 + 2  + 2) + (1 + 1) ) x Tq = 8 x 0.00000025 =  Baud rate = 1/ CAN Bit Time = 500KHz For FD phase: CAN Bit Time = (number of Tq in 1 bit time) x Tq CAN Bit Time = (1 + (FPROPSEG + FPSEG1 + 1) + (FPSEG2 + 1) ) x Tq CAN Bit Time = (1 + (0 + 1 + 1) + (1 + 1) ) x Tq = 5 x Tq =  0.0000003125 Bit Rate = 1/CAN Bit Time = 1 / 0.0000003125 =  3.2MHz To finish, just update the calculated values on your firmware on flexcanConfig.timingConfig structure.  Notes: FRDM-KW36 Software Development Kit (SDK) can be downloaded from MCUXpresso webpage. FlexCAN driver examples are located in path: "SDK_2.2.0_FRDM-KW36\boards\frdmkw36\driver_examples" from your downloaded FRDM-KW36 SDK. Take in consideration that not all the baud rates are achievables and will depend on the flexcan clock and segment values used.
View full article
[中文翻译版] 见附件   原文链接: https://community.nxp.com/docs/DOC-332703
View full article
When having several ZigBee Networks in the same area, and therefore several potential parents, it may become necessary to join one of them and discard the rest. While having a mechanism to only accept joining devices when desired is the best method (like using a button to trigger the joining), it might not always be possible since the parent nodes could be commercial devices or another vendor’s product without this feature. Below are some mechanisms that could be used for this purpose. In general, when searching for suitable parents, the process is as follows: ZDO of device to join sends a MAC scan request. The MAC layer starts scan. For every beacon it receives, it sends a beacon notify indication that is processed in ParseBeaconNotifincaiton() function from AppStackImpl.c The ParseBeaconNotifincaiton() function will add the relevant information in the discovery table and for this it needs a free entry, so it calls GetFreeEntryInDiscoveryTable() function with reuse parameter as FALSE. If the table is full, it will call GetFreeEntryInDiscoveryTable() with reuse set to TRUE to literally re-use low priority entries. When the MAC scan has finished, it will send a MAC scan confirm. When this reaches ZDO, the SearchForSuitableParent() function is called. At this point, there are several approaches that could be used: Use a specific Extended PAN ID to search only for a specific parent node Use a specific PAN ID to prioritize the network’s ID Search in a specific Channel where network is supposed to be operating in All these parameters are configurable in ApplicationConf.h file of the project’s Configure Folder and used in SearchForSuitableParent() function to filter Discovery table entries. Nevertheless, those solutions are not always the best for all applications since it may require hard-coding the network’s parameters. Fortunately, BeeStack leaves all this open for any modification in case it is necessary. In brief, if the discovery table gets full with suitable parents that you DO NOT want to use, you should update the "if(reuse)" statement of the GetFreeEntryInDiscoveryTable() function to replace an entry. In other words, if you think that the desired parent is not present in the discovery table (due to its size limitation or other reason), you should update the GetFreeEntryInDiscoveryTable() function to make sure discovery table contains only devices that are of interest to your node. Please note that the criteria used to select the desired parents is totally application specific. As mentioned, it is always best having a way to trigger the joining such as a button so the rest of parents have permit join set to FALSE and therefore join only to the desired parent without having to implement custom code. Anyway, you may select the solution that meets your application’s requirements the most.
View full article
Bluetooth Low Energy, through the Generic Attribute Profile (GATT), supports various ways to send and receive data between clients and servers. Data can be transmitted through indications, notifications, write requests and read requests. Data can also be transmitted through the Generic Access Profile (GAP) by using broadcasts. Here however, I'll focus on write and read requests. Write and read requests are made by a client to a server, to ask for data (read request) or to send data (write request). In these cases, the client first makes the request, and the server then responds, by either acknowledging the write request (and thus, writing the data) or by sending back the value requested by the client. To be able to make write and read requests, we must first understand how BLE handles the data it transmits. To transmit data back and forth between devices, BLE uses the GATT protocol. The GATT protocol handles data using a GATT database. A GATT database implements profiles, and each profile is made from a collection of services. These services each contain one or more characteristics. A BLE characteristic is made of attributes. These attributes constitute the data itself, and the handle to reference, access or modify said data. To have a characteristic that is able to be both written and read, it must be first created. This is done precisely in the GATT database file ( gatt_db.h 😞 /* gatt_db.h */ /* Custom service*/ PRIMARY_SERVICE_UUID128(service_custom, uuid_custom_service)     /* Custom characteristic with read and write properties */     CHARACTERISTIC_UUID128(char_custom, uuid_custom_char, (gGattCharPropRead_c | gGattCharPropWrite_c))         /* Custom length attribute with read and write permissions*/         VALUE_UUID128_VARLEN(value_custom, uuid_custom_char, (gPermissionFlagReadable_c | gPermissionFlagWritable_c), 50, 1, 0x00) The custom UUIDs are defined in the gatt_uuid128.h file: /* gatt_uuid128.h */ /* Custom 128 bit UUIDs*/ UUID128(uuid_custom_service, 0xE0, 0x1C, 0x4B, 0x5E, 0x1E, 0xEB, 0xA1, 0x5C, 0xEE, 0xF4, 0x5E, 0xBA, 0x00, 0x01, 0xFF, 0x01) UUID128(uuid_custom_char, 0xA1, 0xB2, 0xC3, 0xD4, 0xE5, 0xF6, 0x17, 0x28, 0x39, 0x4A, 0x5B, 0x6C, 0x7D, 0x8E, 0x9F, 0x00) With this custom characteristic, we can write and read a value of up to 50 bytes (as defined by the variable length value declared in the gatt_db.h file, see code above). Remember that you also need to implement the interface and functions for the service. For further information and guidance in how to make a custom profile, please refer to the BLE application developer's guide (BLEDAG.pdf, located in <KW40Z_connSw_install_dir>\ConnSw\doc\BLEADG.pdf. Once a connection has been made, and you've got two (or more) devices connected, read and write requests can be made. I'll first cover how to make a write and read request from the client side, then from the server side. Client To make a write request to a server, you'll need to have the handle for the characteristic you want to modify. This handle should be stored once the characteristic discovery is done. Obviously, you also need the data that is going to be written. The following function needs a pointer to the data and the size of the data. It also uses the handle to tell the server what characteristic is going to be written: static void SendWriteReq(uint8_t* data, uint8_t dataSize) {       gattCharacteristic_t characteristic;     characteristic.value.handle = charHandle;     // Previously stored characteristic handle     GattClient_WriteCharacteristicValue( mPeerInformation.deviceId, &characteristic,                                          dataSize, data, FALSE,                                          FALSE, FALSE, NULL); } uint8_t wdata[15] = {"Hello world!\r"}; uint8_t size = sizeof(wdata); SendWriteReq(wdata, size); The data is send with the GattClient_WriteCharacteristicValue() API. This function has various configurable parameters to establish how to send the data. The function's parameters are described with detail on the application developer's guide, but basically, you can determine whether you need or not a response for the server, whether the data is signed or not, etc. Whenever a client makes a read or write request to the server, there is a callback procedure triggered,  to which the program then goes. This callback function has to be registered though. You can register the client callback function using the App_RegisterGattClientProcedureCallback() API: App_RegisterGattClientProcedureCallback(gattClientProcedureCallback); void gattClientProcedureCallback ( deviceId_t deviceId,                                    gattProcedureType_t procedureType,                                    gattProcedureResult_t procedureResult,                                    bleResult_t error ) {   switch (procedureType)   {        /* ... */        case gGattProcWriteCharacteristicValue_c:             if (gGattProcSuccess_c == procedureResult)             {                  /* Continue */             }             else             {                  /* Handle error */             }             break;        /* ... */   } } Reading an attribute is somewhat similar to writing an attribute, you still need the handle for the characteristic, and a buffer in which to store the read value: #define size 17 static void SendReadReq(uint8_t* data, uint8_t dataSize) {     /* Memory has to be allocated for the characteristic because the        GattClient_ReadCharacteristicValue() API runs in a different task, so        it has a different stack. If memory were not allocated, the pointer to        the characteristic would point to junk. */     characteristic = MEM_BufferAlloc(sizeof(gattCharacteristic_t));     data = MEM_BufferAlloc(dataSize);         characteristic->value.handle = charHandle;     characteristic->value.paValue = data;     bleResult_t result = GattClient_ReadCharacteristicValue(mPeerInformation.deviceId, characteristic, dataSize); } uint8_t rdata[size];         SendReadReq(rdata, size); As mentioned before, a callback procedure is triggered whenever there is a write or read request. This is the same client callback procedure used for the write request, but the event generates a different procedure type: void gattClientProcedureCallback ( deviceId_t deviceId,                                    gattProcedureType_t procedureType,                                    gattProcedureResult_t procedureResult,                                    bleResult_t error ) {   switch (procedureType)   {        /* ... */        case gGattProcReadCharacteristicValue_c:             if (gGattProcSuccess_c == procedureResult)             {                  /* Read value length */                  PRINT(characteristic.value.valueLength);                  /* Read data */                  for (uint16_t j = 0; j < characteristic.value.valueLength; j++)                  {                       PRINT(characteristic.value.paValue[j]);                  }             }             else             {               /* Handle error */             }             break;       /* ... */   } } There are some other methods to read an attribute. For further information, refer to the application developer's guide chapter 5, section 5.1.4 Reading and Writing Characteristics. Server Naturally, every time there is a request to either read or write by a client, there must be a response from the server. Similar to the callback procedure from the client, with the server there is also a callback procedure triggered when the client makes a request. This callback function will handle both the write and read requests, but the procedure type changes. This function should also be registered using the  App_RegisterGattServerCallback() API. When there is a read request from a client, the server responds with the read status: App_RegisterGattServerCallback( gattServerProcedureCallback ); void gattServerProcedureCallback ( deviceId_t deviceId,                                    gattServerEvent_t* pServerEvent ) {     switch (pServerEvent->eventType)     {         /* ... */         case gEvtAttributeRead_c:             GattServer_SendAttributeReadStatus(deviceId, value_custom, gAttErrCodeNoError_c);                             break;         /* ... */     } } When there is a write request however, the server should write the received data in the corresponding attribute in the GATT database. To do this, the function GattDb_WriteAttribute() can be used: void gattServerProcedureCallback ( deviceId_t deviceId,                                    gattServerEvent_t* pServerEvent ) {     switch (pServerEvent->eventType)     {         /* ... */         case gEvtAttributeWritten_c:             if (pServerEvent->eventData.attributeWrittenEvent.handle == value_custom)             {                 GattDb_WriteAttribute( pServerEvent->eventData.attributeWrittenEvent.handle,                                        pServerEvent->eventData.attributeWrittenEvent.cValueLength,                                        pServerEvent->eventData.attributeWrittenEvent.aValue );                              GattServer_SendAttributeWrittenStatus(deviceId, value_custom, gAttErrCodeNoError_c);             }             break;         /* ... */     } } If you do not register the server callback function, the attribute can still be written in the GATT database (it is actually done automatically), however, if you want something else to happen when you receive a request (turning on a LED, for example), you will need the server callback procedure.
View full article