Wireless Connectivity Knowledge Base

cancel
Showing results for 
Search instead for 
Did you mean: 

Wireless Connectivity Knowledge Base

Discussions

This guide describes the hardware for the KW38 minimum BoM development board . The KW38 Minimum BoM development board is configurable, low-power, and cost-effective evaluation and development board for application prototyping and demonstration of the KW39A/38A/37A/39Z/38Z family of devices. The KW38 is an ultra-low-power, highly integrated single-chip device that enables Bluetooth Low Energy (Bluetooth LE) or Generic FSK (at 250, 500 and 1000 kbps) for portable, extremely low-power embedded systems. The KW38 integrates a radio transceiver operating in the 2.36 GHz to 2.48 GHz range supporting a range of GFSK, an ARM Cortex-M0+ CPU, up to 512 KB Flash and up to 64 KB SRAM, Bluetooth LE Link Layer hardware and peripherals optimized to meet the requirements of the target applications . MKW38 device is also available on the FRDM-KW38 Freedom Development Board. For more information about the FRDM-KW38 Freedom Development Board, see the FRDM-KW38 Freedom Development Board User's Guide (document FRDMKW38ZUG available in the NXP Connectivity website also).
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
Introduction   This post explains how to create a BLE GATT database using FSCI commands sent to the BLE Server device. Additionally, this document explains how to set up the fields of each FSCI command used to create the BLE GATT database for the BLE Server.   Main FSCI commands to create the BLE GATT DB in the BLE Server device   The following, are the main commands to create, write and read the GATT DB from the BLE Server perspective. The purpose of this post is to serve as a reference and summary of the most important commands. The full list of commands FSCI commands can be found in the Framework Serial Connectivity Interface (FSCI) for Bluetooth Low Energy Host Stack documentation within your SDK package. GATT-InitRequest This command is used to initialize the GATT database at runtime, and it must be sent before any other command to declare a database in your BLE Server device. GATTServer-RegisterCallback.Request This command installs an application callback for the GATT Server module, enabling the device to respond to the FSCI request from the CPU application through an FSCI indication. GATTDBDynamic-AddPrimaryServiceDeclaration.Request It adds a primary service to the database. It has 3 parameters that should be configured, the desired handle, the UUID type (16 bits, 32 bits, 128 bits), and the UUID value. Usually, the desired handle should be set to zero and the stack will assign the handle of the primary service automatically.   If the GATT application callback was installed through the GATTServer-RegisterCallback.Request command, the GATT Server responds to the GATTDBDynamic-AddPrimaryServiceDeclaration.Request command with a GATTDBDynamic-AddPrimaryServiceDeclaration.Indication that contains the handle assigned to the primary service. The following example shows how to prepare this command to define the battery service in the database. GATTDBDynamic-AddCharacteristicDeclarationAndValue.Request It adds a characteristic and its value to the database. It has 7 parameters that should be configured, the UUID type (16 bits, 32 bits, 128 bits), the UUID value, characteristic properties, the maximum length of the value (only for variable-length values), the initial length of the value, the initial value of the characteristic and value access permissions. The characteristic declared using this command, belongs to the last primary service declared in the database. For values with a fixed length, the maximum length parameter should be set to 0, and the length is obtained from the initial length of the value parameter.   If the GATT application callback was installed, the response of this command is indicated by the GATTDBDynamic-AddCharacteristicDeclarationAndValue.Indication command. The following example shows how to prepare this command to define the battery level characteristic in the database with a fixed length of 1 byte and an initial value of 90%. GATTDBDynamic-AddCharacteristicDescriptor.Request It adds a characteristic descriptor to the database. It has 5 parameters that should be configured, the UUID type (16 bits, 32 bits, 128 bits), UUID value, length of the descriptor value, descriptor’s value, and descriptor access permissions. The descriptor declared using this command, belongs to the last characteristic declared in the database.   If the GATT application callback was installed, the response of this command is indicated by the GATTDBDynamic-AddCharacteristicDescriptor.Indication command. The following example shows how to prepare this command to add the characteristic presentation format descriptor of the battery level characteristic in the database.   GATTDBDynamic-AddCccd.Request It adds a CCDD into the database. This command does not have parameters. The CCCD declared using this command, belongs to the last characteristic declared in the database. The response of this command is indicated by GATTDBDynamic-AddCccd.Indication.   GATTDB-FindServiceHandle.Request This command is used to find the handle of a service previously declared in the database. It has 3 parameters that should be configured, the handle to start the search (should be 1 on the first call), the UUID type of the service to find (16 bits, 32 bits, 128 bits), and the UUID value of the service that you are searching.   If the GATT application callback was installed, the response of this command is indicated by the GATTDB-FindServiceHandle.Indication command, which contains the handle of the found service. The following example shows how to prepare this command to find the handle of the battery service declared in the previous examples. Notice that the result of the search corresponds to the handle returned by the GATTDBDynamic-AddPrimaryServiceDeclaration.Indication as expected.   GATTDB-FindCharValueHandleInService It finds the characteristic´s handle of a given service previously declared in the database. It has 3 parameters that should be configured, the handle of the service that contains the characteristic, the UUID type of the characteristic to find (16 bits, 32 bits, 128 bits), and the UUID value of the characteristic that you are searching for.   If the GATT application callback was installed, the response of this command is indicated by the GATTDB-FindCharValueHandleInService.Indication command, which contains the handle of the found characteristic’s value. The following example shows how to prepare this command to find the handle of the battery level value. Notice that the result of the search corresponds to the handle returned by the GATTDBDynamic-AddCharacteristicDeclarationAndValue.Indication plus one, because the AddCharacteristicDeclarationAndValueIndication command returns the handle of the characteristic and, on the other hand, FindCharValueHandleInService returns the handle of the characteristic’s value. GATTDB-FindDescriptorHandleForcharValueHandle.Request It finds the descriptor´s handle of a given characteristic previously declared in the database. It has 3 parameters that should be configured, the handle of the characteristic’s value that contains the descriptor, the UUID type of the descriptor to find (16 bits, 32 bits, 128 bits), and the UUID value of the descriptor that you are searching.   If the GATT application callback was installed, the response of this command is indicated by the GATTDB-FindDescriptorHandleForCharValueHandle.Indication command, which contains the handle of the found descriptor. The following example shows how to prepare this command to find the handle of the characteristic presentation format descriptor. The result corresponds to the handle returned by the GATTDBDynamic-AddCharacteristicDescriptor.Indication   GATTDB-FindCccdHandleForCharValueHandle.Request It finds the CCCD’s handle of a given characteristic previously declared in the database. It has only one parameter, the handle of the characteristic’s value that contains the CCCD.   If the GATT application callback was installed, the response of this command is indicated by the GATTDB-FindCccdHandleForCharValueHandle.Indication command, which contains the handle of the found CCCD. The following example shows how to prepare this command to find the handle of CCCD. The result corresponds to the handle returned by the GATTDBDynamic-AddCccd.Indication.   GATTDB-WriteAttribute.Request It writes the value of a given attribute from the application level. It has 3 parameters that should be configured, the handle of the attribute that you want to write, the length of the value in bytes, and the new value.   In the following example, we will modify the battery level characteristic’s value from 90% to 80%.   GATTDB-ReadAttribute.Request   It reads the value of a given attribute from the application level. It has 2 parameters that should be configured, the handle of the attribute that you want to read, and the maximum bytes that you want to read. The GATT application callback must be installed, since the response of this command indicated by the GATTDB-ReadAttribute.Indication command contains the value read from the database. In the following example, we will read the battery level characteristic’s value, the result is 80%.      
View full article
Introduction This document is a quick start guide to load a new software image in a KW36 device through FSCI (Freescale Serial Communication Interface) bootloader software. Also, it contains all the steps needed to install the software required in a Windows host to handle the FSCI communication protocol. Software Requirements IAR Embedded Workbench IDE or MCUXpresso IDE. FRDM-KW36 SDK. Hardware Requirements FRDM-KW36 board. Downloading the SDK When downloading the SDK, select your specific IDE or simply choose all toolchains as shown below. In the option "Add software component", ensure to select all middleware components as depicted below. Installing FSCI Host in Windows OS The host software for the Windows OS was designed to work in a Python environment. The following steps are to download and install the software needed to use FSCI in a Windows OS. Visit the Python web site and download the latest Python 2.7.x MSI installer package for Windows OS. Open the MSI installer package. When customizing the installation options, check "Add python.exe to Path" as shown below Complete the rest of the steps for the Python installation process. Unzip the FRDM-KW36 SDK. Depending on your Python environment architecture, copy the HSDK.dll from  <SDK_root>\tools\wireless\host_sdk\sdk-python\lib\<x86_or_x64> to <Python_directory>\DLLs (default in C:\Python27\DLLs). Download and install Visual C++ Redistributable Packages for Microsoft Visual Studio 2013 depending on the Windows architecture (vcredist_x86.exe or vcredist_x64.exe) from the Microsoft web site. Download  and install the Microsoft Visual C++ Compiler for Python 2.7 from the following web site. To run  Python scripts from the Command Prompt of Windows, we must create a system variable named PYTHONPATH. Search “System” in the Windows browser. Go to Advanced system settings -> Environment Variables… -> System variables. Click on the “New…” button and create the PYTHONPATH variable with the following value: <SDK_root>\tools\wireless\host_sdk\hsdk-python\src. Programming the FSCI bootloader on FRDM-KW36 board Attach  the FRDM-KW36 board to your PC. Drag and drop the “bootloader_fsci_frdmkw36.bin” from the previously unzipped SDK file, you can find this file in: <SDK_root>\tools\wireless\binaries to your board. Like a common USB device. Creating a binary image to reprogram the device   IAR Embedded Workbench Open  the connectivity project that you want to program through the FSCI bootloader from your SDK. This example will make use of the heart rate sensor project, located at the following path: <SDK_root>\boards\frdmkw36\wireless_examples\bluetooth\hrs\freertos\iar\hrs_freertos.eww. Open  the project options window (Alt+F7). In Linker -> Config window, edit the “Configuration file symbol definitions” add the “gUseBootloaderLink_d=1” linker flag as shown below. Go to the “Output Converter” window and ensure that the output file is in binary format (.bin), otherwise, deselect the “Override default” checkbox, expand the “Output format” combo box and select “Raw binary. Click the OK button. Rebuild the project. The binary will be saved at: <SDK_root>\boards\frdmkw36\wireless_examples\bluetooth\hrs\freertos\iar\debug   MCUXpresso IDE Import your FRDM-KW36 SDK to MCUXpresso. Drag and drop your SDK on the "installed SDK's" toolbar. (In this step, it is not necessary to unzip the package). Open any connectivity project  that you want to program through the FSCI bootloader from your SDK. This example will make use of the heart rate sensor project. Go to Project -> Properties, a new window will appear. Then, open the C/C++ Build -> Settings -> Linker -> Miscellaneous. Press the icon below, a new window will be deployed. Add “--defsym=gUseBootloaderLink_d=1”. Click on “Apply and Close”. Build the project. Deploy the “Binaries” icon in the workspace. Click the right mouse button on the “.axf” file. Select “Binary Utilities -> Create binary” option. The binary file will be saved at “Debug” folder in the workspace with “.bin” extension. Reprogramming an FRDM-KW36 board using the FSCI bootloader The following steps are to test the FSCI bootloader in a Windows OS. Search "Command Prompt" in the Windows browser. Run the "fsci_bootloader.py" Python script. Type the “python.exe” path in the console (default C:\Python27\python.exe). Drag and drop the “fsci_bootloader.py” from:  <SDK_root>\tools\wireless\host_sdk\hsdk-python\src\com\nxp\wireless_connectivity\test\bootloader  on the command prompt screen. Search the  COM Port of your FRDM-KW36 board and type in the console. You can find it typing ‘Device manager’ from windows home and then search it in Ports (COM & LPT) toolbar. As you can see in this example the port may change depending on each case. Search the binary image file (created in the last section). Drag and drop on the screen. Press “Enter” to start the firmware update trough FSCI bootloader. Automatically the KW36 device will trigger to run the new software. To see all your process running, you can download the ‘IoT Toolbox’ from the app store to your smartphone and connect your device with the board to verify the random data that the heart rate sensor example generates.
View full article
Introduction Over The Air Programming (OTAP) is a Bluetooth LE custom NXP's service that provides a solution to upgrade the software running in the microcontroller. This document guides to load a new software image in a KW38 device through (Over The Air Programming) OTAP Bluetooth LE service . Software Requirements MCUXpresso IDE or IAR Embedded Workbench IDE. FRDM-KW38 SDK. IoT Toolbox App, available for Android and iOS. You can also download the APK of the IoT Toolbox App from this post: IoT Toolbox for Android  Hardware Requirements FRDM-KW38 board. A smartphone with IoT Toolbox App. KW38 Flash Memory Used by the OTAP Client Software During the Update Process By default, the 512KB KW38 flash memory is partitioned into: One 256KB Program Flash array (P-Flash) divided into 2KB sectors with a flash address range from 0x0000_0000 to 0x0003_FFFF. One 256KB FlexNVM array divided into 2KB sectors with address range from 0x1000_0000 to 0x1003_FFFF. Alias memory with address range from 0x0004_0000 to 0x0007_FFFF. Writes or reads at the Alias memory modifies or returns the FlexNVM content, respectively. In other words, Alias memory is another way to refer to FlexNVM memory using different addresses. The following statements simplify how does the OTAP service work:   The OTAP application consists of two independent parts, OTAP bootloader, and OTAP client. The OTAP bootloader verifies if there is a new image available in the OTAP client to reprogram the device. The OTAP client software, on the other hand, provides the Bluetooth LE custom service needed to communicate the OTAP client device (device to be reprogrammed) with the OTAP server device (device that contains the image to reprogram the OTAP client device). Therefore, to prepare the software for the first time, the OTAP client device needs to be programmed twice, first with the OTAP bootloader, and then with the OTAP client software. The mechanism created to have two different software coexisting in the same device is storing each one in different memory regions. This is achieved by indicating to the linker file different memory regions on each individual software. For the KW38 device, the OTAP bootloader has reserved an 8KB slot from 0x0000_0000 to 0x0000_1FFF, thus the rest of the memory is reserved, among other things, by the OTAP client software.     When generating the new image file for the OTAP client device, we need to specify to the linker file that the code will be placed with an offset of 8KB (as the OTAP client software does), since these address range must be preserved to do not overwrite the OTAP bootloader. The new application should also contain the bootloader flags at the corresponding address to work properly (later we will return to this point).     While OTAP client and OTAP server devices are connected, and the download is in progress, the OTAP server device sends the image packets (known as chunks) to the OTAP client device via Bluetooth LE. The OTAP client device can store these chunks, in the external SPI flash (which is already populated on the FRDM-KW38) or in the on-chip FlexNVM region. The destination for these chunks is selectable in the OTAP client software (This post will give the instructions to modify the destination).     When the transfer of the image has finished, and all chunks were sent from the OTAP server device to the OTAP client device, the OTAP client software writes information such as the source of the software update (either external flash or FlexNVM) in a portion of memory known as bootloader flags. Then the OTAP client performs a software reset on the MCU to execute the OTAP bootloader code. Then, the OTAP bootloader code reads the bootloader flags to get the information needed to reprogram the device with the new application. See the following flow diagram which explains the flow of both applications.   Because the new application was built with an offset of 8KB, the OTAP bootloader programs the device starting from the 0x0000_2000 address, so, in consequence, the OTAP client application is overwritten by the new image. Then, the OTAP bootloader moves the flow of the application to start the execution of the new code.     In practice, the boundary between the OTAP client software and the software update when FlexNVM storage is enabled described in statement 3 is not placed exactly in the boundary of the P-Flash and FlexNVM memory regions, moreover, these values might change depending on your linker settings. To know where is located the boundary, you should inspect the effective memory addressing in your project.        Configuring and Programming OTAP Client Software in IAR Embedded Workbench IDE As mentioned in the last section, to complete the software for OTAP implementation, there are required two software programmed in your FRDM-KW38, OTAP bootloader and OTAP client. This section guides you to program and configure the settings to choose between external or internal storage using the IAR Embedded Workbench IDE. 1- The first step is to program the OTAP bootloader in your KW38. Unzip your SDK and then locate the OTAP bootloader software in the following path: <KW38_SDK>\boards\frdmkw38\wireless_examples\framework\bootloader_otap\bm\iar\bootloader_otap.eww 2- Program the OTAP bootloader project on your board by clicking on the "Download and Debug" icon (Ctrl + D) . Once the KW38 was programmed and the debug session begun, abort the session (Ctrl + Caps Lock + D)  to stop the MCU safely. 3- At this point, you have programmed the OTAP bootloader in your KW38. The next is to program and configure the OTAP client software. Locate the OTAP client software at the following path: Freertos project version: <KW38_SDK>\boards\frdmkw38\wireless_examples\bluetooth\otac_att\freertos\iar\otap_client_att_freertos.eww Baremetal project version: <KW38_SDK>\boards\frdmkw38\wireless_examples\bluetooth\otac_att\bm\iar\otap_client_att_bm.eww 4- Then, configure the OTAP client to select external or internal storage. To select the external storage, follow the next steps (this is the default configuration in the SDK project): 4.1- Locate the "app_preinclude.h" header file in the source folder of your workspace. Search the "gEepromType_d" define and set its value to "gEepromDevice_AT45DB041E_c". /* Specifies the type of EEPROM available on the target board */ #define gEepromType_d gEepromDevice_AT45DB041E_c 4.2- Open the project options window (Alt + F7). Go to Linker->Config window and set "gUseInternalStorageLink=0". To select the internal storage, follow the next steps: 4.1- Locate the "app_preinclude.h" header file in the source folder of your workspace. Search the "gEepromType_d" define and set its value to "gEepromDevice_InternalFlash_c". /* Specifies the type of EEPROM available on the target board */ #define gEepromType_d gEepromDevice_InternalFlash_c 4.2- Open the project options window (Alt + F7). Go to Linker->Config window and set "gUseInternalStorageLink=1". 5- Once you have configured the storage settings, save the changes in the project. Then program the software on your board by clicking on the "Download and Debug" icon (Ctrl + D)  . Once the KW38 was programmed and the debug session begun, abort the session (Ctrl + Caps Lock + D)  to stop the MCU safely. Creating an SREC Image to Update the Software in OTAP Client in IAR Embedded Workbench IDE This section shows how to create an image compatible with OTAP to reprogram the KW38 OTAP Client using as a starting point, our wireless examples with IAR Embedded Workbench IDE. 1- Select any example from your SDK package in the Bluetooth folder and open it using the IAR IDE. Bluetooth examples are located in the following path: <KW38_SDK>\boards\frdmkw38\wireless_examples\bluetooth  In this example, we will use the glucose sensor project: <KW38_SDK>\boards\frdmkw38\wireless_examples\bluetooth\glucose_s\freertos\iar\glucose_sensor_freertos.eww 2- Open the project options window in IAR (Alt + F7). In Linker->Config window, edit the options to include the "gUseBootloaderLink_d=1" flag and update the "gEraseNVMLink_d=0" flag. When the gUseBootlaoderLink_d flag is true, it indicates to the linker file that the image must be addressed after the first flash sector, to do not overwrite the OTAP Bootloader software (as we stated previously). On the other hand, the gEraseNVMLink_d symbol is used to fill with a 0xFF pattern the unused NVM flash memory region. Disabling this flag, our software image will not contain this pattern, in consequence, the image reduces its total size and it improves the speed of the OTAP download and memory usage. 3- Go to "Output Converter" window. Deselect the "Override default" checkbox, then expand the "Output format" combo box and select "Motorola S-records" format. Click the "OK" button to finish. 4- Build the project. 5- Locate the S-Record file (.srec) in the following path, and save it to a known location on your smartphone. <KW38_SDK>\boards\frdmkw38\wireless_examples\bluetooth\glucose_s\freertos\iar\debug\glucose_sensor_freertos.srec Configuring and Programming OTAP Client Software in MCUXpresso IDE As mentioned in a previous section, to complete the software for OTAP implementation, there are required two software programmed in your FRDM-KW38, OTAP bootloader and OTAP client. This section guides you to program and configure the settings to choose between external or internal storage using the MCUXpresso IDE. 1- Open MCUXpresso IDE. Click on "Import SDK example(s)" in the "Quickstart Panel". 2- Select the FRDM-KW38 icon and click "Next >". 3- Import the OTAP bootloader project. It is located in "wireless_examples -> framework -> bootloader_otap -> bm -> bootloader_otap". Click on the "Finish" button. 4- Program the OTAP bootloader project on your board by clicking on the "Debug" icon  . Once the KW38 was programmed and the debug session begun, abort the session  (Ctrl + F2) to stop the MCU safely. 5- Repeat steps 1 to 3 to import the OTAP client software on MCUXpresso IDE. It is located at "wireless_examples -> bluetooth -> otac_att -> freertos -> otap_client_att_freertos" for freertos version, or "wireless_examples -> bluetooth -> otac_att -> bm -> otap_client_bm_freertos" if you prefer baremetal instead. 6- Then, configure the OTAP client to select external or internal storage. To select the external storage, follow the next steps (this is the default configuration in the SDK project): 6.1- Locate the "app_preinclude.h" file under the source folder in your workspace. Search the "gEepromType_d" define and set its value to "gEepromDevice_AT45DB041E_c". /* Specifies the type of EEPROM available on the target board */ #define gEepromType_d gEepromDevice_AT45DB041E_c 6.2- Navigate to "Project -> Properties -> C/C++ Build -> MCU settings -> Memory details". Edit the Flash fields as shown in the figure below, and leave intact the RAM. To select the internal storage, follow the next steps: 6.1- Locate the "app_preinclude.h" file under the source folder in your workspace. Search the "gEepromType_d" define and set its value to "gEepromDevice_InternalFlash_c". /* Specifies the type of EEPROM available on the target board */ #define gEepromType_d gEepromDevice_InternalFlash_c 6.2- Navigate to "Project -> Properties -> C/C++ Build -> MCU settings -> Memory details". Edit the Flash fields as shown in the figure below, and leave intact the RAM. 7- Once you have configured the storage settings, save the changes in the project. Then program the software on your board by clicking on the "Debug" icon  . Once the KW38 was programmed and the debug session begun, abort the session  (Ctrl + F2) to stop the MCU safely. Creating an SREC Image to Update the Software in OTAP Client in MCUXpresso IDE This section shows how to create an image compatible with OTAP to reprogram the KW38 OTAP Client using as a starting point, our wireless examples with MCUXpresso IDE. 1- Import any example from your SDK package in the Bluetooth folder as explained previously. Bluetooth examples are located in "wireless_examples -> bluetooth" folder in the SDK Import Wizard. This example will make use of the glucose sensor project in "wireless_examples -> bluetooth -> glucose_s -> freertos -> glucose_sensor_freertos". See the picture below. 2- Navigate to "Project -> Properties -> C/C++ Build -> MCU settings -> Memory details". Edit the Flash fields as shown in the figure below, and leave intact the RAM. The last fields indicate to the linker file that the image must be addressed after the first flash sector, to do not overwrite the OTAP bootloader software, as we stated in the introduction of this post. 3- Unzip your KW38 SDK package. Drag and drop the "main_text_section.ldt" linker script from the following path to the "linkscripts" folder on your workspace. The result must be similar as shown in the following figure. <KW38_SDK>\middleware\wireless\framework\Common\devices\MKW38A4\mcux\linkscript_bootloader\main_text_section.ldt 4- Open the "end_text.ldt" linker script file located in the linkscripts folder in MCUXpresso IDE. Locate the section shown in the following figure and remove "FILL" and "BYTE" statements. BYTE and FILL lines are used to fill with a 0xFF pattern the unused NVM flash memory region. Removing this code, our software image will not contain this pattern, in consequence, the image reduces its total size and it improves the speed of the OTAP download and memory usage. 5- Open the "app_preinclude.h" file, and define "gEepromType_d" as internal storage. This is a dummy definition needed to place the bootloader flags in the proper address, so this will not affect the storage method chosen before when you programmed the OTAP client and the OTAP bootloader software in your MCU. /* Specifies the type of EEPROM available on the target board */ #define gEepromType_d gEepromDevice_InternalFlash_c 6-  Include in your project, the "OtaSupport" folder and its files in the "framework" folder of your project. Include as well the "External" folder and its files in the "framework -> Flash" folder of your project. "OtaSupport" and "External" folders can be found in your SDK. You can easily drag those folders from your SDK download path and drop it into your workspace in MCUXpresso to include them. "OtaSupport" and "External" folders are located at: OtaSupport <KW38_SDK>middleware\wireless\framework\OtaSupport External <KW38_SDK>middleware\wireless\framework\Flash\External The result must look like the following picture:   7- Go to "Project -> Properties -> C/C++ Build -> Settings -> Tool Settings -> MCU C Compiler -> Includes". Click on the icon next to "Include paths" (See the picture below). A new window will be displayed, then click on the "Workspace" button. 8- Deploy the directory of the project in the "Folder selection" window, and select "framework -> Flash -> External -> interface" and "framework -> OtaSupport -> interface" folders. Click the "OK" button to save the changes. 9- Ensure that "OtaSupport" and "External" folders were imported in the "Include paths" window. Then save the changes by clicking on the "Apply and Close" button. 10- Save and build the project by clicking this icon  . Then, deploy the "Binaries" icon in your project. Click the right mouse button on the ".axf" file and select the "Binary Utilities -> Create S-Record" option. The S-Record file generated will be saved in the Debug folder in your workspace with ".s19" extension. Save the S-Record file in a known location on your smartphone.    Testing the OTAP Client with IoT Toolbox App This section explains how to test the OTAP client software using the IoT Toolbox App. 1- Open the IoT Toolbox App on your smartphone. Select OTAP and click "SCAN" to start scanning for a suitable OTAP Client device.  2- Press the ADV button (SW2) on your FRDM-KW38 board to start advertising. 3- Once your smartphone has found the FRDM-KW38 board, it will be identified as "NXP_OTAA". Connect your smartphone with this device. Then a new window will be displayed on your smartphone.  4- Click the "Open" button and search for the SREC software update. 5- Click "Upload" to start the transfer. Wait while the download is completed. A confirmation message will be displayed after a successful update.  6- Wait a few seconds until the software update was programmed on your MCU. The new code will start automatically.   Please let me know any questions about this topic.
View full article
This document provides the calculation of the Bluetooth Low Power consumption linked to the setting of the Kinetis.   The Power Profile Calculator is build to provide the power consumption of your application. It's a mix between real measurements in voltage and temperature. The process is not taken into account which may create some variation.   DISCLAIMER: This excel workbook is provided as an estimation tool for NXP customers and is based on power profile measurements done on a set of randomly selected parts. A specific part may exhibit deviation from the nominal measurements used on this tool.   This document is the summary of all the information available in the AN12180 Power Consumption Analysis - FRDM-KW36 available in the NXP web page.   Several parameters could be fill-in: Buck or bypass mode (DCDC) Supply Voltage (2.4V to 3.6V) Temperature (-40°C to +105°C) Processor configuration (20MHz, 32MHz or 48MHz) 2 different deep sleep modes (LLS3 or VLLS2) Different Tx output power (0dBm, +3.5dBm or +5dBm) Possibility to set the Advertising interval, connection interval, scan interval and active scan windows duration Fix the Bluetooth Packet sizes in Advertising and Connection  Tx/Rx payload.   One optional information is to provide an idea of the duration life time on typical batteries.
View full article
This document provides the calculation of the Bluetooth Low Power consumption linked to the setting of the Kinetis.   The Power Profile Calculator is build to provide the power consumption of your application. It's a mix between real measurements in voltage and temperature. The process is not taken into account which may create some variation.   DISCLAIMER: This excel workbook is provided as an estimation tool for NXP customers and is based on power profile measurements done on a set of randomly selected parts. A specific part may exhibit deviation from the nominal measurements used on this tool.   This document is the summary of all the information available in the AN12459 Power Consumption Analysis - FRDM-KW38 available in the NXP web page.   Several parameters could be fill-in: Buck or bypass mode (DCDC) Supply Voltage (2.4V to 3.6V) Temperature (-40°C to +105°C) Processor configuration (20MHz, 32MHz or 48MHz) 10 different deep sleep modes Different Tx output power (0dBm, +3.5dBm or +5dBm) Data rate (1Mbps, 2Mbps, 500kbps, 125kbps) Possibility to set the Advertising interval, connection interval, scan interval and active scan windows duration Fix the Bluetooth Packet sizes in Advertising and Connection  Tx/Rx payload.   One optional information is to provide an idea of the duration life time on 9 typical batteries.
View full article
Introduction When a software update is requested by an OTAP Client (a device that receives a software update, commonly Bluetooth LE Peripheral) from the OTAP Server (a device that sends a software update, commonly Bluetooth LE Central), you may want to preserve some data previously acquired, such as bonding information, trimming values for the system oscillators, or probably NVM data for your application. This document guides you in performing OTAP updates preserving the flash data content of your interest. This document is intended for developers familiarized with OTAP custom Bluetooth LE service, for more information, you can take a look at the following post: Reprogramming a KW36 device using the OTAP Client Software.   OTAP Header and Sub-elements OTAP Protocol implements a format for the software update that is composed of a header and a defined number of sub-elements. The OTAP Header describes general information about the software update and it has a defined format shown in the following figure. For more information about the header fields, you can go to 11.4.1 Bluetooth Low Energy OTAP header chapter of the Bluetooth Low Energy Application Developer's Guide document included in the SDK at <SDK_2.2.X_FRDM-KW36_Download_Path>\docs\wireless\Bluetooth                              Each Sub-element contains information for a specific purpose. You could implement your proprietary fields for your application (For more information about sub-element fields, you can go to 11.4.1 Bluetooth Low Energy OTAP header chapter of the Bluetooth Low Energy Application Developer's Guide document included in the SDK at <SDK_2.2.X_FRDM-KW36_Download_Path>\docs\wireless\Bluetooth). OTAP includes the following sub-elements: Image File Sub-element Value Field Lenght (bytes) Description Upgrade Image  Variable This sub-element contains the actual binary executable image which is copied into the flash memory of the OTAP Client device. The maximum size of this sub-element depends on the target hardware. Sector Bitmap 32 This sub-element contains a sector bitmap of the flash memory of the target device which tells the bootloader which sectors should be overwritten and which leave intact. The format of this field is the least-significant bit first for each byte with the least significant bytes and bits standing for the lowest memory sections of the flash.  Image File CRC 2 This is a 16-bit CRC calculated over all elements of the image file except this field itself. This element must be the last sub-element in an image file sent over the air.   OTAP Sector Bitmap Sub-element Field The KW36 Flash is partitioned into: One 256 KB Program Flash (P-Flash) array divided into 2 KB sectors with a flash address range from 0x0000_0000 to 0x0003_FFFF. One 256 KB FlexNVM array divided in 2 KB sectors, flash address ranges from 0x1000_0000 to 0x1003_FFFF with an Alias memory with address range 0x0004_0000 to 0x0007_FFFF. The Bitmap sub-element is 256 bits of length, in terms of the KW36 flash, each bit represents a 2KB sector covering the address range from 0x0 - 0x0007_FFFF (P-Flash to FlexNVM Alias address range), where 1 means that such sector should be erased and 0 means that such sector should be preserved. The Bitmap field is used by the OTAP Bootloader to obtain the address range which should be erased before programming the KW36 with the software update, so it must be configured before sending a software update to leave intact the address range of memory that contain data of your interest and erase only the address range that will be overwritten by the software update.        For example: Suppose that a developer wants to preserve the address range between 0x7D800 - 0x7FFFF and the address range between 0x0 - 0x1FFF, and the left memory must be erased. The address range between 0x7D800 - 0x7FFFF corresponds to the 5 top flash sectors and the address range between 0x0 - 0x1FFF is the lowest 4 sectors. So, it means that bits between 256 and 252 (256, 255, 254, 253 and 252) and bits between 4 and 1 (4,3,2 and 1) should be set to 0, that way OTAP Bitmap for this example is: 0x07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0   Configuring OTAP Bitmap to Protect an Address Range with NXP Test Tool Download and install Test Tool for Connectivity products in NXP's web site Open NXP Test Tool 12 software on your PC. Go to "OTA Updates -> OTAP Bluetooth LE" Then load your image file for the software update clicking on the "Browse..." button (NXP Test Tool only accepts .bin and .srec files). You can configure the OTAP Bitmap selecting the "Override sector bitmap" checkbox and changing the default value by your new bitmap value. Once you have configured the bitmap, select "Save...".   Then, a window will be displayed to select the destination to save the .bleota file. Provide a name to identify this file. You can use this file with IoT Toolbox App for Android and iOS to update the software using OTAP. This new .bleota file contains the bitmap that tells to the OTAP Bootloader which sectors will be erased and which sectors will be preserved.          
View full article
Introduction This document describes the hardware considerations for the schematic and layout of the MKW36A512VFT4 device. This MCU is packaged into a 48-pin HVQFN - 7x7 mm, dissimilar to MKW36Z512VHT4 which comes packaged into a 48-pin LQFN - 7x7 mm (the last one takes part of FRDM-KW36).   Pin Layout  The MKW36A512VFT4 MCU is pin to pin compatible with the MKW36Z512VHT4 (FRDM-KW36) MCU, except for the DCDC pins. The following figure shows the distribution of the pins in the MKW36A512VFT4 MCU (left), compared with the MKW36Z512VHT4 (FRDM-KW36 MCU, right). Surely, this is the most important consideration for MKW36A512VFT4, since you can not simply move the FRDM-KW36 layout on your design. Minimum BOM The following figures show the minimum BOM necessary for each DCDC mode in KW36. For more information about DCDC modes and hardware guidelines, please visit:  MKW4xZ/3xZ/3xA/2xZ DC-DC Power Management Bypass Mode   Buck Auto-Start Mode   Buck Manual-Start Mode     Layout Recommendations The footprint and layout are critical for RF performance, hence if the recommended design is followed exactly in the RF region of the PCB, sensitivity, output power, harmonic and spurious radiation, and range, you will succeed. For more information of layout recommendations, please visit Hardware Design Considerations for MKW35A/36A/35Z/36Z Bluetooth Low Energy Devices.  The footprint recommended for the MKW36A512VFT4 is shown in the figure below. NXP prefers to use a top layer thickness of no less than 8-10 mils. The use of a correct substrate like the FR4 with a dielectric constant of 4.3 will assist you in achieving a good RF design. Other recommendations during EMC certification stages are: - Specific attention must be taken on 4 pins PTC1, 2, 3 & 4 if they are used on the application. - 4 decoupling capacitors of 3pF are mandatory on those pins and be positioned as close as possible. - Wires from those 4 pins must be underlayer. - NXP recommends putting the vias under the package in case the customer HW design rules allow it. Some recommendations for a good Vdd_RF supply layout are: - Vdd_RF1 and Vdd_RF2 lines must have the same length as possible, linked to pointA (‘Y’ connection). - 12pF decoupling capacitor from Vdd_RF wire must be connected to the Ground Antenna. The purpose is to get the path as short as possible from Vdd_RF1/Vdd_RF2 to the ground antenna. - 12pF decoupling capacitor from the Vdd_RF3 pin must be as close as possible. Return to ground must be as short as possible. So vias (2 in this below picture) must be placed near to the decoupling capacitor to get close connection to the ground layer. The recommended RF stage is shown in the following figure. The MKW36A512VFT4 has a single-ended RF output with a 2 components matching network composed of a shunt capacitor and a series inductor. Both elements transform the device impedance to 50 ohms. The value of these components may vary depending on your board layout. Avoid routing traces near or parallel to RF transmission lines or crystal signals. Maintain a continuous ground under an RF trace is critical to keep unaltered the characteristic impedance of the transmission line. Avoid routing on the ground layer that will result in disrupting the ground under RF traces. For more information about RF considerations please visit: Freescale IEEE 802.15.4 / ZigBee Package and Hardware Layout Considerations.
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
Introduction This post guides you on migrating from MKW36Z512VHT4 to MKW36A512VFT4 MCUs. This example will make use of the "beacon" SDK example. SDK Download and Install 1- Go to MCUXpresso web page:  MCUXpresso Web Page 2- Log in with your registered account. 3- Search for the "KW36A" device. Then click on the suggested processor and click on "Build MCUXpresso SDK"       4- The next page will be displayed. Select “All toolchains” in the “Toolchain / IDE” box and provide a name to identify the package. Then click on "Download SDK".     5- Accept the license agreement. Wait a few minutes until the system gets the package into your profile. Download the SDK clicking on "Download SDK Archive" as depicted in the following figure.     6- If MCUXpresso IDE is used, drag and drop the KW36A SDK zip folder in “Installed SDK’s” perspective to install the package.     At this point, you have downloaded and installed the SDK package for the KW36A device.   Software Migration in MCUXpresso IDE 1- Import the "beacon" example on the MCUXpresso workspace. Click on “Import SDK examples(s)…” option, a new window will appear. Then select "MKW36Z512xxx4" and click on the FRDM-KW36 image. Click on the "Next >" button.     2- Search beacon and select your project version (bm or freertos).     3- Go to Project/Properties. Expand C/C++ Build/MCU settings and select MKW36A512xxx4 MCU. Click Apply and Close button to save the configuration.     4- Rename MKW36Z folders as MKW36A, clicking the right mouse button and selecting "Rename". These are the following:   framework/DCDC/Interface -> MKW36Z framework/DCDC/Source -> MKW36Z framework/LowPower/Interface -> MKW36Z framework/LowPower/Source -> MKW36Z framework/XCVR -> MKW36Z4     5- Open the Project/Properties window in MCUXpresso IDE. Go to C/C++ Build/Settings and select MCU C Compiler/Includes folder in the Tool Settings window. Edit all paths related to MKW36 MCU, in according to MKW35 folders before created. The results must look similar as shown below:   ../framework/LowPower/Interface/MKW3 6A ../framework/LowPower/Source/MKW3 6A ../framework/DCDC/Interface/MKW3 6A ../framework/XCVR/MKW3 6A4     6- Select MCU Assembler/General folder in Tool Settings. Edit the paths related to MKW36 MCU. The results must look similar as shown below:   ../framework/LowPower/Interface/MKW3 6A ../framework/LowPower/Source/MKW3 6A ../framework/DCDC/Interface/MKW3 6A ../framework/XCVR/MKW3 6A4     7- Go to Project/Properties. Expand MCU C Compiler/Preprocessor window. Edit "CPU_MKW36Z512VHT4" and "CPU_MKW36Z512VHT4_cm0plus" symbols, rename it as "CPU_MKW36A512VFT4"  and   "CPU_MKW36A512VFT4_cm0plus"   respectively. Save the changes.     8- Go to the workspace. Delete “fsl_device_registers, MKW36Z4, MKW36Z4_features, system_MKW36Z4.h and system_MKW36Z4.c” files located at CMSIS folder. Then, unzip the MKW35Z SDK package and search for “fsl_device_registers, MKW36A4, MKW36A4_features, system_MKW36A4.h and system_MKW36A4.c” files into this folder at the following paths:   <SDK_folder_root>/devices/MKW36A4/fsl_device_registers.h <SDK_folder_root>/devices/MKW36A4/MKW36A4.h <SDK_folder_root>/devices/MKW36A4/MKW36A4_features.h <SDK_folder_root>/devices/MKW36A4/system_MKW36A4.h <SDK_folder_root>/devices/MKW36A4/system_MKW36A4.c     9- Overwirte the “startup_mkw36z4.c” (located inthe startup folder) by the "startup_mkw36a4.c" located in the following path <SDK_folder_root>/devices/MKW36A4/mcuxpresso/startup_mkw36a4.c. You can simply drag and drop on the startup folder, and remove the older one.     10- Open "fsl_device_registers.h" file in CMSIS folder. Add"defined(CPU_MKW36A512VFT4)" in the following code (line 18 of the file):   /* * Include the cpu specific register header files. * * The CPU macro should be declared in the project or makefile. */ #if (defined(CPU_MKW36A512VFP4) || defined(CPU_MKW36A512VFT4) || defined(CPU_MKW36A512VHT4) || defined(CPU_MKW36A512VFT4))‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍   11- Open "ble_config.h" file in bluetooth->host->config folder. Add "defined(CPU_MKW36A512VFT4)" in the following code (line 146 of the file):   /* The maximum number of BLE connection supported by platform */ #if defined(CPU_QN9080C) #define MAX_PLATFORM_SUPPORTED_CONNECTIONS 16 #elif (defined(CPU_MKW36Z512VFP4) || defined(CPU_MKW36Z512VHT4) || defined(CPU_MKW36A512VFP4) || defined(CPU_MKW36A512VHT4) || defined(CPU_MKW36A512VFT4) || \ defined(CPU_MKW35Z512VHT4) || defined(CPU_MKW35A512VFP4) || \ defined(CPU_K32W032S1M2CAx_cm0plus) || defined(CPU_K32W032S1M2VPJ_cm0plus) || \ defined(CPU_K32W032S1M2CAx_cm4) || defined(CPU_K32W032S1M2VPJ_cm4) || \ defined(CPU_MKW38A512VFT4) || defined (CPU_MKW38Z512VFT4) || defined(CPU_MKW39A512VFT4) || \ defined(CPU_MKW37A512VFT4) || defined(CPU_MKW37Z512VFT4))‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍   12- Open "ble_controller_task.c" file in source->common folder. Add "defined(CPU_MKW36A512VFT4)" in the following code (line 272 of the file):    #elif (defined(CPU_MKW35A512VFP4) || defined(CPU_MKW35Z512VHT4) || defined(CPU_MKW36A512VFP4) || defined(CPU_MKW36A512VFT4) ||\ defined(CPU_MKW36A512VHT4) || defined(CPU_MKW36Z512VFP4) || defined(CPU_MKW36Z512VHT4)) /* Select BLE protocol on RADIO0_IRQ */ XCVR_MISC->XCVR_CTRL = (uint32_t)((XCVR_MISC->XCVR_CTRL & (uint32_t)~(uint32_t)( XCVR_CTRL_XCVR_CTRL_RADIO0_IRQ_SEL_MASK )) | (uint32_t)( (0UL << XCVR_CTRL_XCVR_CTRL_RADIO0_IRQ_SEL_SHIFT) ));‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍   13-Build the project.   At this point, the project is already migrated.   Software Migration in IAR Embedded Workbench IDE 1- Open the beacon project located in the following path: 2- Select the project in the workspace and press Alt + F7 to open project options.   3- In the General Options/Target window click the icon next to the device name and select the appropriate device NXP/KinetisKW/KW3x/NXP MKW36A512xxx4, then click the OK button.   4- Create a new folder with the name MKW36A at following paths: <SDK_root>/middleware/wireless/framework_5.4.6/DCDC/Interface <SDK_root>/middleware/wireless/framework_5.4.6/DCDC/Source <SDK_root>/middleware/wireless/framework_5.4.6/LowPower/Interface <SDK_root>/middleware/wireless/framework_5.4.6/LowPower/Source <SDK_root>/middleware/wireless/framework_5.4.6/XCVR     5- Copy all files inside MKW36Z folders located at the above paths and paste in MKW36A folders.     6- Select the beacon project in the workspace and press Alt+F7 to open project options window. In C/C++ Compiler/Preprocessor window, rename the paths related to MKW36Z folders to MKW36A folders. Rename the CPU_MKW36Z512VHT4 macro as CPU_MKW3 6A512VFT4  i n the defined symbols text box.  The results must look similar as shown below: Click the OK button. $PROJ_DIR$/middleware/wireless/framework_5.4.2/LowPower/Interface/MKW3 6A $PROJ_DIR$/../../../../../../../devices/MKW3 6A 4/drivers $PROJ_DIR$/../../../../../../../middleware/wireless/framework_5.4.2/DCDC/Interface/MKW3 6A $PROJ_DIR$/../../../../../../../middleware/wireless/framework_5.4.2/XCVR/MKW3 6A 4 $PROJ_DIR$/../../../../../../../devices/MKW3 6A 4 $PROJ_DIR$/../../../../../../../devices/MKW3 6A 4/utilities     7- Expand the startup folder, select all files, click the right mouse button and select the “Remove” option. Click the right mouse button on the folder and select “Add/Add files”. Add the startup_MKW36A4.s located at this path: <SDK_root>/devices/MKW36A4/iar/startup_MKW36A4.s Also, add system_MKW36A4.c and system_MKW36A4.h into the startup folder. Both files are located at the next path: <SDK_root>/devices/MKW36A4   8- Open "ble_config.h" file in bluetooth->host->config folder. Add "defined(CPU_MKW36A512VFT4)" in the following code: /* The maximum number of BLE connection supported by platform */ #if defined(CPU_QN9080C) #define MAX_PLATFORM_SUPPORTED_CONNECTIONS 16 #elif (defined(CPU_MKW36Z512VFP4) || defined(CPU_MKW36Z512VHT4) || defined(CPU_MKW36A512VFP4) || defined(CPU_MKW36A512VHT4) || defined(CPU_MKW36A512VFT4) || \ defined(CPU_MKW35Z512VHT4) || defined(CPU_MKW35A512VFP4) || \ defined(CPU_K32W032S1M2CAx_cm0plus) || defined(CPU_K32W032S1M2VPJ_cm0plus) || \ defined(CPU_K32W032S1M2CAx_cm4) || defined(CPU_K32W032S1M2VPJ_cm4) || \ defined(CPU_MKW38A512VFT4) || defined (CPU_MKW38Z512VFT4) || defined(CPU_MKW39A512VFT4) || \ defined(CPU_MKW37A512VFT4) || defined(CPU_MKW37Z512VFT4))‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍   9- Open "ble_controller_task.c" file in source->common folder. Add "defined(CPU_MKW36A512VFT4)" in the following code: #elif (defined(CPU_MKW35A512VFP4) || defined(CPU_MKW35Z512VHT4) || defined(CPU_MKW36A512VFP4) || defined(CPU_MKW36A512VFT4) ||\ defined(CPU_MKW36A512VHT4) || defined(CPU_MKW36Z512VFP4) || defined(CPU_MKW36Z512VHT4)) /* Select BLE protocol on RADIO0_IRQ */ XCVR_MISC->XCVR_CTRL = (uint32_t)((XCVR_MISC->XCVR_CTRL & (uint32_t)~(uint32_t)( XCVR_CTRL_XCVR_CTRL_RADIO0_IRQ_SEL_MASK )) | (uint32_t)( (0UL << XCVR_CTRL_XCVR_CTRL_RADIO0_IRQ_SEL_SHIFT) ));‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍   10-Build the project.   At this point, the project is already migrated.
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
Introduction This document is to guide how to modify the OTAP Client software to the Low Power module. The starting point of this document is the OTAP Client example in the FRDM-KW36 SDK v2.2.2.   Deep Sleep Modes This section provides a base to understand how the developer should change between DSM1 (Deep Sleep Mode 1) and DSM3 (Deep Sleep Mode 3). The DSM6 does not need to be started by the developer, instead, the controller configures this mode automatically and returns to the latest mode after finished the radio activity.   DSM1 This low-power mode was designed to be used when the BLE stack is active, in other words when the LL is in advertising, scanning, or connection states. In this mode, the MCU enters LLS3 and BLE Link Layer enters deep sleep. The SoC wakes up from this mode by the on-board switches, by LPTMR timeout, or by BLE Link Layer wake-up interrupt (BLE_LL reference clock reaches wake up instance register) using LLWU module. The LPTMR timer is used to measure the time that the MCU spends in deep sleep to synchronize low-power timers at wakeup.   DSM3 This low-power mode was designed to be used when all stacks enabled for this platform are idle, in other words, when the LL stop advertising, scanning, or connection. In this mode, the MCU enters LLS3 and all enabled link layers remain idle. All RAM is retained. The SoC wakes up from this mode by the on-board switches, by DCDC power switch (when DCDC is in buck mode), or by LPTMR timeout using LLWU module. The LPTMR timer is also used to measure the time that MCU spends in deep sleep to synchronize low-power timers at wakeup.   DSM6 This low-power mode was developed to save some power while the radio is on. Its most common use case is with the radio in Rx waiting for a packet. Upon receiving the packet the radio wakes up the MCU. In this mode, the MCU enters STOP mode and the radio maintains its state. Any module capable of producing an interrupt can wake up the MCU, such as on-board switches, DCDC power switch (when DCDC is in buck mode), LPTMR timeout, Radio Interrupt, UART, and so on. The LPTMR timer is also used to measure the time that the MCU spends in deep sleep to synchronize low-power timers at wakeup.   For more information about DSM modes, you can inspect the “Connectivity Framework Reference Manual” chapter 3.15 Low-power library, it provides full information of Low Power modes and the usage on the NXP stack. It is available in your SDK at <FRDM-KW36 SDK root>\docs\wireless\Common.   Modifications on the Software In order to add low power on the OTAP Client (switching between DSM1, DSM3, and DSM6) two files must be modified: - app_preinclude.h - otap_client_att.c The following sections explain these changes.   app_preinclude.h This file is intended to contain the definitions that manage the behavior of the application. To include and enable the Low Power module you must add (or modify if the macro is already defined in this file) the following preprocessor directives.   1. Modify the AppPoolsDetails as following. /* Defines pools by block size and number of blocks. Must be aligned to 4 bytes.*/ #define AppPoolsDetails_c \ _block_size_ 32 _number_of_blocks_ 6 _eol_ \ _block_size_ 64 _number_of_blocks_ 4 _eol_ \ _block_size_ 88 _number_of_blocks_ 3 _eol_ \ _block_size_ 248 _number_of_blocks_ 2 _eol_ \ _block_size_ 312 _number_of_blocks_ 1 _eol_ \ _block_size_ 392 _number_of_blocks_ 1 _eol_‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 2. Set “cPWR_UsePowerDownMode” to 1 and keep the following directives in the “Framework Configuration” section as shown below. /* Check Low Power Timer */ #define cPWR_CheckLowPowerTimers 1 /* Enable/Disable Low Power Timer */ #define gTMR_EnableLowPowerTimers 1 /* Enable/Disable PowerDown functionality in PwrLib */ #define cPWR_UsePowerDownMode 1 /* Enable/Disable BLE Link Layer DSM */ #define cPWR_BLE_LL_Enable 1 /* Default Deep Sleep Mode*/ #define cPWR_DeepSleepMode 3 /* Enable/Disable MCU Sleep During BLE Events */ #define cMCU_SleepDuringBleEvents 1 /* Default deep sleep duration in ms */ #define cPWR_DeepSleepDurationMs 30000 /* Number of slots(625us) before the wake up instant before which the hardware needs to exit from deep sleep mode. */ #define cPWR_BLE_LL_OffsetToWakeupInstant 3 /* Enables / Disables the DCDC platform component */ #define gDCDC_Enabled_d 1 /* Default DCDC Mode used by the application */ #define APP_DCDC_MODE gDCDC_Mode_Buck_c /* Default DCDC Battery Level Monitor interval */ #define APP_DCDC_VBAT_MONITOR_INTERVAL 600000‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 3. Add the following directives in the “BLE Stack Configuration” section. Create the “Auto Configuration” section to disable LED support whenever Low Power is enabled. /*! ********************************************************************************* * BLE Stack Configuration ********************************************************************************** */ /* Time between the beginning of two consecutive advertising PDU's */ #define mcAdvertisingPacketInterval_c 0x02 /* 1.25 msec */ /* Offset to the first instant register. */ #define mcOffsetToFirstInstant_c 0x00 /* 625usec */ /*! ********************************************************************************* * Auto Configuration ********************************************************************************** */ /* Disable LEDs when enabling low power */ #if cPWR_UsePowerDownMode || gMWS_UseCoexistence_d #define gLEDSupported_d 0 #endif #if gMWS_UseCoexistence_d #undef gKBD_KeysCount_c #define gKBD_KeysCount_c 1 #endif‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 4. Modify the “Memory Pools Configuration” section as follows. /* Enable RNG seed storage in Flash */ #define gRngSeedStorageAddr_d ((uint32_t)FREESCALE_PROD_DATA_BASE_ADDR + 1024) /* Enable XCVR calibration storage in Flash */ #define gPreserveXcvrDacTrimValue_d 1 #define gXcvrDacTrimValueSorageAddr_d ((uint32_t)FREESCALE_PROD_DATA_BASE_ADDR + 1040) /* Application Connection sleep mode */ #define gAppDeepSleepMode_c 1 /* Application RAM usage configuration */ #define cPWR_RamRetentionInVLLS 2 /* 32K */ /* Disable unused LowPower modes */ #define cPWR_EnableDeepSleepMode_1 1 #define cPWR_EnableDeepSleepMode_2 0 #define cPWR_EnableDeepSleepMode_3 1 #define cPWR_EnableDeepSleepMode_4 0 #define cPWR_EnableDeepSleepMode_5 0 #define cPWR_EnableDeepSleepMode_7 0 #define cPWR_EnableDeepSleepMode_8 0 /* Warm-boot sequence will use the default stack which is used by ISRs on FreeRTOS */ #define USE_WARMBOOT_SP 0‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍   otap_client_att.c This is the main source file at the application level. Here are managed all the procedures that the device performs, before, during, and after to create a connection. This allows you to get the state of the device any instant and, hence, the dedicated low power APIs that rule the application must be implemented here, in the callbacks contained in this file, for an easier switching among the low power states.   1. Include “PWR_Configuration.h” header in “Include” section: #if (cPWR_UsePowerDownMode) #include "PWR_Interface.h" #include "PWR_Configuration.h" #endif‍‍‍‍‍‍‍‍‍‍‍‍ 2. Locate the “BleApp_Config” function. This function is executed once, after POR (Power on reset) during the device setup. Here you can change the deep sleep mode to DSM3 and allow the device to sleep using “PWR_ChangeDeepSleepMode” and “PWR_AllowDeviceToSleep” APIs. When the device has finished the initialization, it goes to sleep since all stacks are idle. See the following example. static void BleApp_Config(void) { #if defined(MULTICORE_APPLICATION_CORE) && (MULTICORE_APPLICATION_CORE == 1) if (GattDbDynamic_CreateDatabase() != gBleSuccess_c) { panic(0,0,0,0); return; } #endif /* MULTICORE_APPLICATION_CORE */ /* Common GAP configuration */ BleConnManager_GapCommonConfig(); /* Register stack callbacks */ (void)App_RegisterGattServerCallback (BleApp_GattServerCallback);‍‍‍‍‍‍‍‍‍‍‍‍‍ mAdvState.advOn = FALSE; /* Start services */ basServiceConfig.batteryLevel = BOARD_GetBatteryLevel(); (void)Bas_Start(&basServiceConfig); (void)Dis_Start(&disServiceConfig); if (OtapClient_Config() == FALSE) { /* An error occurred in configuring the OTAP Client */ panic(0,0,0,0); } /* Allocate application timer */ appTimerId = TMR_AllocateTimer(); mBatteryMeasurementTimerId = TMR_AllocateTimer(); #if (cPWR_UsePowerDownMode) #if MULTICORE_APPLICATION_CORE #if gErpcLowPowerApiServiceIncluded_c PWR_ChangeBlackBoxDeepSleepMode(cPWR_DeepSleepMode); PWR_AllowBlackBoxToSleep(); #endif PWR_ChangeDeepSleepMode(cPWR_DeepSleepMode); PWR_AllowDeviceToSleep(); #else PWR_ChangeDeepSleepMode(cPWR_DeepSleepMode); PWR_AllowDeviceToSleep(); #endif #endif }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 3. Locate the “BleApp_Start” function. This function is executed just after wake up by pressing the LLWU SW3 button. This action will trigger the advertising, so, you must change the deep sleep mode to DSM1 using “PWR_ChangeDeepSleepMode” API since the BLE stack is active. See the following example. void BleApp_Start(void) { Led1On(); if (mPeerDeviceId == gInvalidDeviceId_c) { /* Device is not connected and not advertising*/ if (!mAdvState.advOn) { #if gAppUseBonding_d if (gcBondedDevices > 0) { mAdvState.advType = whiteListAdvState_c; } else { #endif mAdvState.advType = advState_c; #if gAppUseBonding_d } #endif #if (cPWR_UsePowerDownMode) #if MULTICORE_APPLICATION_CORE #if gErpcLowPowerApiServiceIncluded_c PWR_ChangeBlackBoxDeepSleepMode(gAppDeepSleepMode_c); #endif #else PWR_ChangeDeepSleepMode(gAppDeepSleepMode_c); #endif #endif BleApp_Advertise(); } } }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 4. Locate the “BleApp_AdvertisingCallback” function. This function is executed every time the advertising state changes. Change the deep sleep mode to DSM3 when “mAdvState.advOn” is false, in other words, when the device stops advertising. If you stop the advertising either using an application timer or a user button, KW36 will go to sleep until you start advertising again (pressing LLWU SW3 button), saving power when all stacks are idle. See the following example. static void BleApp_AdvertisingCallback (gapAdvertisingEvent_t* pAdvertisingEvent) { switch (pAdvertisingEvent->eventType) { case gAdvertisingStateChanged_c: { mAdvState.advOn = !mAdvState.advOn; if(mAdvState.advOn) { LED_StopFlashingAllLeds(); Led1Flashing(); } #if (cPWR_UsePowerDownMode) else { #if MULTICORE_APPLICATION_CORE #if gErpcLowPowerApiServiceIncluded_c PWR_ChangeBlackBoxDeepSleepMode(cPWR_DeepSleepMode); #endif #else PWR_ChangeDeepSleepMode(cPWR_DeepSleepMode); #endif } #endif } break; case gAdvertisingCommandFailed_c: { Led2On(); panic(0,0,0,0); } break; default: ; /* For MISRA compliance */ break; } }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 5. Locate “BleApp_ConnectionCallback” function. It is executed every time the connection state changes. In “gConnEvtConnected_c” add the following code to change to DSM1, since the BLE stack is active. case gConnEvtConnected_c: { /* Advertising stops when connected */ mAdvState.advOn = FALSE; (void)TMR_StopTimer(appTimerId); /* Subscribe client*/ mPeerDeviceId = peerDeviceId; (void)Bas_Subscribe(&basServiceConfig, peerDeviceId); (void)OtapCS_Subscribe(peerDeviceId); OtapClient_HandleConnectionEvent (peerDeviceId); /* Start battery measurements */ (void)TMR_StartLowPowerTimer(mBatteryMeasurementTimerId, gTmrLowPowerIntervalMillisTimer_c, TmrSeconds(mBatteryLevelReportInterval_c), BatteryMeasurementTimerCallback, NULL); #if (cPWR_UsePowerDownMode) #if MULTICORE_APPLICATION_CORE #if gErpcLowPowerApiServiceIncluded_c PWR_ChangeBlackBoxDeepSleepMode(gAppDeepSleepMode_c); PWR_AllowBlackBoxToSleep(); #endif #else PWR_ChangeDeepSleepMode(gAppDeepSleepMode_c); PWR_AllowDeviceToSleep(); #endif #else /* UI */ LED_StopFlashingAllLeds(); Led1On(); #endif } break;‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ In “gConnEvtDisconnected_c” add the following code to change to DSM3, since all stacks are idle. case gConnEvtDisconnected_c: { /* Unsubscribe client */ mPeerDeviceId = gInvalidDeviceId_c; (void)Bas_Unsubscribe(&basServiceConfig, peerDeviceId); (void)OtapCS_Unsubscribe(); /* UI */ LED_StopFlashingAllLeds(); Led1Flashing(); Led2Flashing(); Led3Flashing(); Led4Flashing();‍‍‍‍‍‍‍‍‍‍‍‍ OtapClient_HandleDisconnectionEvent (peerDeviceId); #if (cPWR_UsePowerDownMode) /* Go to sleep */ #if MULTICORE_APPLICATION_CORE #if gErpcLowPowerApiServiceIncluded_c PWR_ChangeBlackBoxDeepSleepMode(cPWR_DeepSleepMode); #endif #else PWR_ChangeDeepSleepMode(cPWR_DeepSleepMode); #endif #else /* Restart advertising*/ BleApp_Start(); #endif } break;‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍   Power Consumption Profile of OTAP Client This section explains the behavior of the power consumption profile along the time. We can differ when DSM1 or DSM3 are used depending on the device state. If the device needs to advertise or is in connection state, it will use DSM1 because this sleep mode can predict when the device needs to handle the communication with others and wake up automatically through the BLE Link Layer wakeup interrupt. On the other hand, when no actions are in progress, it will use DSM3 and the wake up depends entirely on the LLWU SW3 button in this example. On the other hand, the DSM6 puts the MCU in STOP mode during the transmission and reception in BLE events, it does not need to be started manually, instead, the controller configures this mode automatically and returns to DSM1 mode after finished the radio activity.   The APIs that change the deep sleep mode to DSM1 are: • BleApp_Start: It starts advertising. • BleApp_ConnectionCallback – gConnEvtConnected_d: It notifies when the MCU has been connected to a peer device.   The APIs that change the deep sleep mode to DSM3 are: • BleApp_Config: It takes part of the initialization procedure after POR. All tasks are idle, the device is waiting for the LLWU SW3 button to wake up and start advertising. • BleApp_AdvertisingCallback – mAdvState is off: The device has to stopped advertising, so the MCU is idle. • BleApp_ConnectionCallback – gConnEvtDisconnected_d: It notifies when the device has been disconnected, so the MCU is idle.   Please let us know any questions or comments regarding this topic.
View full article
Introduction This document provides guidance to load a new software image in a KW35 device through OTAP (Over The Air Programming) bootloader for KW35. This article also provides the steps needed to download and install the SDK used in the tutorial. Software Requirements IAR Embedded Workbench IDE or MCUXpresso IDE . SDK MKW36A512xxx4 RC4 or further. Hardware Requirements MKW35A512xxx4  device. KW35 Flash Memory Used for the OTAP Software Deployment The KW35 Flash is partitioned into: 2x256 KB Program Flash (P-Flash) array divided into 2 KB sectors with a flash address range from 0x0000_0000 to 0x0007_FFFF.     The statements to comprehend how the OTAP Client software and his features works are: The OTAP Client software is split into two parts, the OTAP bootloader and the OTAP client service. The OTAP bootloader verifies if there is a new image already available to reprogram the device. The OTAP client service software provides the Bluetooth LE custom services needed to communicate with the server that contains the new image file. Therefore, before to start the test, the device has been programmed twice, first with the OTAP bootloader then with the OTAP client service project. The mechanism used to have two different software in the same device is to store each one in different memory regions and this is implemented by the linker file. In the KW35 device, the bootloader application has reserved a 16KB slot of memory starting from the 0x0 address (0x0 to 0x3FFF) thus, the left memory of the first P-Flash memory bank is reserved, among other things, by the OTAP client service application.   To create a new image file for the client device, the developer needs to specify to the linker file that the code will be stored with an offset of 16KB since the first addresses are reserved for the bootloader. At connection event, the server sends all the chunks of code to the client via Bluetooth LE. The client stores the code at the second P-Flash memory bank but is not able to run yet.   When the broadcast has finished, and all chunks were sent, the OTAP bootloader detects this situation and triggers a command to reprogram the device with the new application. Due the new application was built with an offset of 16KB, the OTAP bootloader program the device starting from the 0x3FFF address and the OTAP client service application is overwritten by the new image. Then the OTAP bootloader triggers the new application, starting the execution of the code.   Software Development Kit download and install   Go to MCUXpresso web page. Log in with your registered account. Search for “MKW36A” device. Then click on the suggested processor and click on “Build MCUXpresso SDK” The next page is displayed. Select “All toolchains” in the “Toolchain / IDE” combo box and provide the name to identify the package. Click on “Add software component”, then deploy the combo box and click on “Select All” option. Save the changes. Click on “Download SDK” button and accept the license agreement. If MCUXpresso IDE is used, drag and drop the SDK zip folder in “Installed SDK’s” perspective to install the package.     Preparing the software to test the OTAP for KW35 device using IAR Embedded Workbench   This section provides the steps needed to test the OTAP software on the KW35. Program the OTAP bootloader on the KW35. 1.1 Open the OTAP_bootloader project located at the following path: <SDK_download_root>\boards\virtual-board-kw35\wireless_examples\framework\bootloader_otap\bm\iar\bootloader_otap_bm.eww     1.2 Flash the project (Ctrl + D). Stop the debug session (Ctrl + Shift + D). Program the OTAP client application on the KW35.         2.1 Open the OTAP client project located in the path below.           <SDK_download_root>\boards\frdmkw36\wireless_examples\bluetooth\otac_att\freertos\iar\otac_att_freertos.eww          2.2  Follow the steps 2 to 12 described in the “4.1. Changes Required in Project Options and Settings” section of the AN12252 “Migration Guide from               MKW36Z512xxx4 to MKW35Z512xxx4” application note.            2.3 Open the app_preinclude.h file under the source directory in the workspace. Find the “gEepromType_d” definition and update the value to                                 “gEepromDevice_InternalFlash_c” as shown below.   #define gEepromType_d gEepromDevice_InternalFlash_c‍‍‍‍‍            2.4  Save the MKW35Z512xxx4_connectivity.icf file located at:                 <SDK_download_root>\middleware\wireless\framework_5.4.4\Common\devices\MKW35Z4\iar                                Into the folder of the OTAP Client ATT project:                 <SDK_download_root>\boards\frdmkw36\wireless_examples\bluetooth\otac_att\freertos\iar            2.5  Open the project options window (Alt+F7). In Linker/Config window click the icon next to linker path and select the linker configuration file “MKW35Z512xxx4_connectivity.icf”. Set the "gUseInternalStorageLink_d” flag to 1.              2.6  Click the OK button in the project options window to save the new configuration.          2.7  Flash the project (Ctrl + D). Stop the debug session (Ctrl + Shift + D).    Preparing the software to test the OTAP for KW35 device using MCUXpresso IDE   This section provides the steps needed to test the OTAP software on the KW35. Program the OTAP bootloader on the KW35.          1.1 Open MCUXpresso IDE. Click on “Import SDK example(s)” option in the “Quickstart Panel” view.                        1.2 Click on virtual-board-kw35 SDK icon.          1.3 Deploy the wireless_examples\framework\bootloader_otap folders and select bm project. Click Finish button.                                                                           1.4 Select “Debug” option in the Quickstart Panel. Once the project is already loaded on the device, stop the debug session.      2. Program the OTAP client application on the KW35.          2.1 Open MCUXpresso IDE. Click on “Import SDK example(s)” option in the “Quickstart Panel” view.                          2.2 Click twice on the frdmkw36 icon.                                                                            2.3 Type “otac_att” in the examples textbox and select the freertos project at wireless_examples\bluetooth\otac_att\freertos. Finally, click on Finish button.              2.4  Follow the steps 5 to 17 described in the “5.1. Changes Required in Project Options and Settings” section of the AN12252 “Migration Guide from MKW36Z512xxx4 to MKW35Z512xxx4” application note .            2.5.  Open the app_preinclude.h file under the source directory in the workspace. Find the “gEepromType_d” definition and update the value to                “gEepromDevice_InternalFlash_c” as shown below. #define gEepromType_d gEepromDevice_InternalFlash_c‍‍‍‍‍            2.6  Save the MKW35Z512xxx4_connectivity.ld file located at:                 <SDK_download_root>\middleware\wireless\framework_5.4.4\Common\devices\MKW35Z4\gcc                Into the source folder in the workspace.              2.7  Open the Project/Properties window. Next, go to the MCU Linker/Managed Linker Script perspective and edit the Linker Script name to “MKW35Z512xxx4_connectivity.ld”.              2.8  Go to MCU Linker/Miscellaneous view. Press the icon below, a new window will be deployed. Add the following definition in the “Other options” box: --defsym=gUseInternalStorageLink_d=1.              2.9  Click the “Apply and Close” button in the project options window to save the new configuration.          2.10  Select “Debug” option in the Quickstart Panel. Once the project is already loaded on the device, stop the debug session.   Running OTAP demo with the IoT Toolbox App Save the S-Record file created with the steps in Appendix A or Appendix B 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. Perform a falling edge on the PTB18 in the KW35 to start advertising. Create a connection with the founded 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.    Appendix A. Creating an S-Record image file for KW35 client using IAR Embedded Workbench Open the connectivity project that you want to program using the OTAP bootloader from your SDK. This example will make use of the glucose sensor project. <SDK_download_root>\boards\frdmkw36\wireless_examples\bluetooth\glucose_s\freertos\iar\glucose_s_freertos.eww Follow the steps 2 to 12 described in the “ 4.1. Changes Required in Project Options and Settings ” section of the AN12252 “Migration Guide from              MKW36Z512xxx4 to MKW35Z512xxx4”   application note . Save the MKW35Z512xxx4_connectivity.icf file located at: <SDK_download_root>\middleware\wireless\framework_5.4.4\Common\devices\MKW35Z4\iar                 In the containing folder of your project. <SDK_download_root>\boards\frdmkw36\wireless_examples\bluetooth\glucose_s\freertos\iar Open the project options window (Alt+F7). In Linker/Config window click the icon next to linker path and select the linker configuration file MKW35Z512xxx4_connectivity.icf. Then, enable “gUseBootloaderLink_d” macro in the “Configuration file symbol definitions” textbox. Go to the “Output Converter” window. Deselect the “Override default" checkbox, expand the “Output format” combo box and select Motorola S-records format. Click OK button.                                                                                                                                           Rebuild the project. Search the S-Record file in the following path: <SDK_download_root>\boards\frdmkw36\wireless_examples\bluetooth\glucose_s\freertos\iar\debug   Appendix B. Creating an S-Record image file for KW35 client using 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 Follow the steps 5 to 17 described in the “ 5.1. Changes Required in Project Options and Settings ” section of the AN12252 “Migration Guide from MKW36Z512xxx4 to MKW35Z512xxx4”  application note . Save the MKW35Z512xxx4_connectivity.ld file located at: <SDK_download_root>\middleware\wireless\framework_5.4.4\Common\devices\MKW35Z4\gcc Into the source folder in the workspace.                                                                                                                  Open the Project/Properties window. Next, go to the MCU Linker/Managed Linker Script perspective and edit the Linker Script name to “MKW35Z512xxx4_connectivity.ld”.                                                                                  Go to MCU Linker/Miscellaneous view. Press the icon below, a new window will be deployed. Add the following definition in the “Other options” box: --defsym=gUseBootloaderLink_d=1. Click the “Apply and Close” button.                              Build the project. Deploy the “Binaries” icon in the workspace. Click the right mouse button on the “.axf” file. Select “Binary Utilities/Create S-Record” option. The S-Record file will be saved at “Debug” folder in the workspace with “.s19” extension.  
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. I n 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. I n 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 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_exa mples\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_exa mples\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
Hello everyone, Over The Air Programming (OTAP) NXP's custom Bluetooth LE service provides the developer a solution to upgrade the software that the MCU contains. It removes the need for cables and a physical link between the OTAP client (the device that is reprogrammed) and the OTAP server (the device that contains the software update). This post explains how to run the OTAP Client Software that comes within the FRDM-KW36 package: Reprogramming a KW36 device using the OTAP Client Software. As it is mentioned in the last post, the OTAP Client can reprogram the KW36 while it is running, with new software using Bluetooth LE. However, this implementation for most of the applications is not enough since once you have reprogrammed the new image, the KW36 can not be reprogramed a second time using this method. For these applications that require to be updated many times using Bluetooth LE during run-time, we have created the following application note, that comes with a functional example of how to implement the OTAP Client software, taking advantage of this service. You can download the software clicking on the link in blue and the documentation is in the link in green. Please visit the following link: DOCUMENTS and Application Notes for KW36 In the "DOCUMENTS" section, you can found more information of the KW36. In the "Application Note" section, you can found more software and documentation of interesting topics like this.        Best Regards.
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
Introduction In some applications, is it necessary to keep updated the software running in many MCU's that take part in the system, fortunately, Over The Air Programming, it's a custom Bluetooth LE service developed to send "over the air" software updates for the KW MCU series. FRDM-KW36 SDK already provides the "otap_client" software, that can be used together with the "otap_bootloader" such as it is described in the following community post:  Reprogramming a KW36 device using the OTAP Client Software  to reprogram the KW36. This example can be modified to store code for another MCU and later send the software update to this device as depicted in the figure below. This post guides you on modifying the OTAP client software to support software updates for other MCU's. Preparing the OTAP client software The starting point of the following modifications is supposing that there is no need to perform over the air updates for the KW36 MCU, so the use of the "otap_bootloader" is obsolete and will be removed in this example. In other words, KW36 will be programmed only with the "otap_client" code. Open the MCUXpresso settings window (Project->Properties->"C/C++ Build->MCU settings") and configure the following fields. Save the changes. For external storage: For internal storage: Locate the "app_preinclude.h" file, and set the storage method, as follows: For external storage: #define gEepromType_d       gEepromDevice_AT45DB041E_c For internal storage: #define gEepromType_d        gEepromDevice_InternalFlash_c Locate the "main_text_section.ldt" linker script into the "linkscripts" folder, and delete it from the project.  Search in the project for   "OTA_SetNewImageFlag();" and "ResetMCU();"  functions in the "otap_client.c" file (source->common->otap_client->otap_client.c) and delete or comment. (For reference, there are 4 in total). Locate the following code in "OtaSupport.h" (framework->OtaSupport->Interface) and delete or comment. extern uint16_t gBootFlagsSectorBitNo;‍‍‍‍‍‍ void OTA_SetNewImageFlag(void);‍‍‍‍‍‍‍ Locate the following code in "OtaSupport.c" (framework->OtaSupport->Source) and delete or comment. extern uint32_t __BootFlags_Start__[]; #define gBootImageFlagsAddress_c ((uint32_t)__BootFlags_Start__)‍‍‍‍‍‍‍‍‍‍‍‍ #if !gEnableOTAServer_d || (gEnableOTAServer_d && gUpgradeImageOnCurrentDevice_d) /*! Variables used by the Bootloader */ #if defined(__IAR_SYSTEMS_ICC__) #pragma location = "BootloaderFlags" const bootInfo_t gBootFlags = #elif defined(__GNUC__) const bootInfo_t gBootFlags __attribute__ ((section(".BootloaderFlags"))) = #elif defined(__CC_ARM) volatile const bootInfo_t gBootFlags __attribute__ ((section(".BootloaderFlags"))) = #else #error "Compiler unknown!" #endif { {gBootFlagUnprogrammed_c}, {gBootValueForTRUE_c}, {0x00, 0x02}, {gBootFlagUnprogrammed_c}, #if defined(CPU_K32W032S1M2VPJ_cm4) && (CPU_K32W032S1M2VPJ_cm4 == 1) {PLACEHOLDER_SBKEK}, {BOOT_MAGIC_WORD} #endif }; #endif /* !gEnableOTAServer_d || (gEnableOTAServer_d && gUpgradeImageOnCurrentDevice_d) */‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ uint16_t gBootFlagsSectorBitNo; gBootFlagsSectorBitNo = gBootImageFlagsAddress_c/(uint32_t)((uint8_t*)FSL_FEATURE_FLASH_PFLASH_BLOCK_SECTOR_SIZE);‍‍‍‍ gBootFlagsSectorBitNo = gBootImageFlagsAddress_c/(uint32_t)((uint8_t*)FSL_FEATURE_FLASH_PAGE_SIZE_BYTES);‍‍‍‍ void OTA_SetNewImageFlag(void) { #if (gEepromType_d != gEepromDevice_None_c) && (!gEnableOTAServer_d || (gEnableOTAServer_d && gUpgradeImageOnCurrentDevice_d)) /* OTA image successfully written into the non-volatile storage. Set the boot flag to trigger the Bootloader at the next CPU Reset. */ union{ uint32_t value; uint8_t aValue[FSL_FEATURE_FLASH_PFLASH_BLOCK_WRITE_UNIT_SIZE]; }bootFlag; #if defined(CPU_K32W032S1M2VPJ_cm4) && (CPU_K32W032S1M2VPJ_cm4 == 1) uint8_t defaultSBKEK[SBKEK_SIZE] = {DEFAULT_DEMO_SBKEK}; #endif uint32_t status; if( mNewImageReady ) { NV_Init(); bootFlag.value = gBootValueForTRUE_c; status = NV_FlashProgramUnaligned((uint32_t)&gBootFlags.newBootImageAvailable, sizeof(bootFlag), bootFlag.aValue); if( (status == kStatus_FLASH_Success) && FLib_MemCmpToVal(gBootFlags.internalStorageAddr, 0xFF, sizeof(gBootFlags.internalStorageAddr)) ) { bootFlag.value = gEepromParams_StartOffset_c + gBootData_ImageLength_Offset_c; status = NV_FlashProgramUnaligned((uint32_t)&gBootFlags.internalStorageAddr, sizeof(bootFlag), bootFlag.aValue); } #if defined(CPU_K32W032S1M2VPJ_cm4) && (CPU_K32W032S1M2VPJ_cm4 == 1) if( status == kStatus_FLASH_Success ) { /* Write the default SBKEK for secured OTA */ status = NV_FlashProgramUnaligned((uint32_t)&gBootFlags.sbkek, SBKEK_SIZE, defaultSBKEK); } #endif if( status == kStatus_FLASH_Success ) { mNewImageReady = FALSE; } } #endif }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍   At this point, the FRDM-KW36 can receive and store any image for any MCU and can request a further software update from the OTAP server device.    Adding API's to reprogram the "MCU X" on OTAP client software Once the software update has been downloaded from the OTAP Server into the OTAP Client, the developer should request the software update from the OTAP Client to the "MCU X" through a serial protocol such as UART, SPI, CAN, etc. You should develop the API's and the protocol according to the requirements for your system to send the software update to the "MCU X" (as well as the bootloader for the MCU X). The handling your protocol can be integrated into the OTAP client code replacing "ResetMCU()" (The same code removed in step 4) in the code by "APISendSoftwareUpdateToMCUX()" for instance, since at this point the image was successfully sent over the air and stored in the memory of the KW36. 
View full article
Introduction The FRDM-KW36 includes an RTC module with a 32 kHz crystal oscillator. This module generates a 32 kHz clock source for the MCU whilst running on very low power mode. This oscillator includes a set of programmable capacitors used as the C LOAD . Changing the value of these capacitors can modify the frequency the oscillator provides. This configurable capacitance ranges from 0 pF (capacitor bank disabled) to 30  pF in steps of 2 pF . These values are obtained by combining the enabled capacitors. The values available are 2  pF , 4  pF , 8  pF , and 16  pF .   Any combination of these four can be done.  It is recommended that these internal capacitors are disabled if the external capacitors are available (clearing SC2P, SC4P, SCS8, and SC16 bits in RTC Control Register SFR). To adjust the frequency provided by the oscillator, you must first be able to measure the frequency. Using a frequency counter would be ideal, as it provides a more precise measurement than an oscilloscope. You will also need to output the oscillator frequency.  To output the oscillator frequency, u sing any of the Bluetooth demo applications as an example, you should do the following: Adjusting Frequency Example This example will make use of the Heart Rate Sensor demo (freertos version) from the Connectivity Software Stack and assumes that the developer has the knowledge of import or open projects from the SDK to IDE. Open or clone the Heart Rate Sensor project from your SDK. Find the board.c and board.h files in the board folder at the workspace.                                                                                Declare a void function on the board.h file as shown below. This function will be in order to mux the RTC clock out to the PTB3 and be able to measure the frequency.  /* Function to mux PTB3 to RTC_CLKOUT */ void BOARD_EnableRtcClkOut (void);‍‍ Develop the BOARD_EnableRtcClkOut function inside the board.c file as below. void BOARD_EnableRtcClkOut(void) { /* Enable PORTB clock gating */ CLOCK_EnableClock(kCLOCK_PortB); /* Mux the RTC_CLKOUT to PTB3 */ PORT_SetPinMux(PORTB, 3u, kPORT_MuxAlt7); /* Select the 32kHz reference for RTC_CLKOUT signal */ SIM->SOPT1 |= SIM_SOPT1_OSC32KOUT(1); } Call the BOARD_EnableRtcClkOut function in hardware_init function just after BOARD_BootClockRUN (board.c file). Find clock_config.c file in the board folder at the workspace. Add the following defines at the top of the file. #define RTC_OSC_CAP_LOAD_0 0x0U /*!< RTC oscillator, capacitance 0pF */ #define RTC_OSC_CAP_LOAD_2 0x2000U /*!< RTC oscillator, capacitance 2pF */ #define RTC_OSC_CAP_LOAD_4 0x1000U /*!< RTC oscillator, capacitance 4pF */ #define RTC_OSC_CAP_LOAD_6 0x3000U /*!< RTC oscillator, capacitance 6pF */ #define RTC_OSC_CAP_LOAD_8 0x800U /*!< RTC oscillator, capacitance 8pF */ #define RTC_OSC_CAP_LOAD_10 0x2800U /*!< RTC oscillator, capacitance 10pF */ #define RTC_OSC_CAP_LOAD_12 0x1800U /*!< RTC oscillator, capacitance 12pF */ #define RTC_OSC_CAP_LOAD_14 0x3800U /*!< RTC oscillator, capacitance 14pF */ #define RTC_OSC_CAP_LOAD_16 0x400U /*!< RTC oscillator, capacitance 16pF */ #define RTC_OSC_CAP_LOAD_18 0x2400U /*!< RTC oscillator, capacitance 18pF */ #define RTC_OSC_CAP_LOAD_20 0x1400U /*!< RTC oscillator, capacitance 20pF */ #define RTC_OSC_CAP_LOAD_22 0x3400U /*!< RTC oscillator, capacitance 22pF */ #define RTC_OSC_CAP_LOAD_24 0xC00U /*!< RTC oscillator, capacitance 24pF */ #define RTC_OSC_CAP_LOAD_26 0x2C00U /*!< RTC oscillator, capacitance 26pF */ #define RTC_OSC_CAP_LOAD_28 0x1C00U /*!< RTC oscillator, capacitance 28pF */ #define RTC_OSC_CAP_LOAD_30 0x3C00U /*!< RTC oscillator, capacitance 30pF */ Search the CLOCK_CONFIG_EnableRtcOsc call to a function inside the BOARD_BootClockRUN function (also in the clock_config.c file), and edit the argument by any of the defines above. Finally, disable the low power options and led support in the "preinclude.h" file located in the source folder of the project: #define cPWR_UsePowerDownMode 0 #define gLEDSupported_d 0 At this point, you can measure in PTB3 and play with the frequency adjust using your frequency counter. Each time that the board is programmed, you need to perform a POR to get the correct measure. The following table was obtained from an FRDM-KW36 board rev B and it can be used as a reference to adjust the frequency.  Please note that the capacitance is not only composed of the enabled internal capacitance, but also the parasitic capacitances found in the package, bond wires, bond pad, and the PCB traces. So, while the reference measurements given below should be close to the actual value, you should also make measurements with your board, to ensure that the frequency is trimmed specifically to your board and layout.   Enabled Capacitors C LOAD Capacitance Definition Frequency - 0pF RTC_OSC_CAP_LOAD_0 (bank disabled) 32772.980Hz SC2P 2pF RTC_OSC_CAP_LOAD_2 32771.330Hz SC4P 4pF RTC_OSC_CAP_LOAD_4 32770.050Hz SC2P, SC4P 6pF RTC_OSC_CAP_LOAD_6 32769.122Hz SC8P 8pF RTC_OSC_CAP_LOAD_8 32768.289Hz SC2P, SC8P 10pF RTC_OSC_CAP_LOAD_10 32767.701Hz SC4P, SC8P 12pF RTC_OSC_CAP_LOAD_12 32767.182Hz SC2P, SC4P, SC8P 14pF RTC_OSC_CAP_LOAD_14 32766.766Hz SC16P 16pF RTC_OSC_CAP_LOAD_16 32766.338Hz SC2P, SC16P 18pF RTC_OSC_CAP_LOAD_18 32766.038Hz SC4P, SC16P 20pF RTC_OSC_CAP_LOAD_20 32765.762Hz SC2P, SC4P, SC16P 22pF RTC_OSC_CAP_LOAD_22 32765.532Hz SC8P, SC16P 24pF RTC_OSC_CAP_LOAD_24 32765.297Hz SC2P, SC8P, SC16P 26pF RTC_OSC_CAP_LOAD_26 32765.117Hz SC4P, SC8P, SC16P 28pF RTC_OSC_CAP_LOAD_28 32764.940Hz SC2P, SC4P, SC8P, SC16P 30pF RTC_OSC_CAP_LOAD_30 32764.764Hz
View full article