Over the Air Programming (OTAP)

This chapter contains a detailed description of the Over The Air Programming capabilities of the Bluetooth Low Energy Host Stack enabled by dedicated GATT Service/Profile and of the support modules needed for OTA programming.

The image transfer is done using a dedicated protocol which is designed to run on both the Bluetooth Low Energy transport and serial transport.

The container for the upgrade image is an image file which has a predefined format which is described in detail. The image file format is independent of the protocol but must contain information specific to the image upgrade infrastructure on an OTAP Client device. Detailed information on how to build an image file starting from a generic format executable generated by an embedded cross-compiling toolchain is shown.

The demo applications implement a typical scenario where a new image is sent from a PC via serial interface to a Bluetooth Low Energy OTAP Server and then over the air to an OTAP Client which is the target of the upgrade image. There are 3 applications involved in the OTAP demo: 1 PC application which builds the image file and serves it to the embedded OTAP Server and 2 embedded applications (OTAP Server and OTAP Client). This chapter contains enough information for building Bluetooth Low Energy OTAP applications which implement different image upgrade scenarios specific to other use cases.

General functionality

A Bluetooth Low Energy OTAP system consists of an OTAP Server and an OTAP Client which exchange an image file over the air using the infrastructure provided by Bluetooth Low Energy (GAP, GATT, SM) via a custom GATT Service and GATT Profile. Additionally, a third application may be used to serve an image to the embedded OTAP Server.

The OTAP Server runs on the GATT Client via the Bluetooth Low Energy OTAP Profile and the OTAP Client runs on the GATT Server via the Bluetooth Low Energy OTAP Service. For the moment the OTAP Server runs on the GAP Central and the OTAP Client runs on the GAP Peripheral.

The Figure 1 shows a typical image upgrade scenario.

Parent topic:Over the Air Programming (OTAP)

Bluetooth Low Energy OTAP service-profile

The Bluetooth Low Energy OTAP Service is implemented using the GATT Server which runs on the OTAP Client (GAP Peripheral).

The Bluetooth LE OTAP Service does not require any other Bluetooth LE services because it is a custom service it has a 128-bit UUID. The service has 2 custom characteristics which also have 128-bit UUIDs.

The service must be included in the GATT database of the GATT Server as described in Creating GATT database of this document.

OTAP service and characteristics

The OTAP Service has a custom 128-bit UUID which is shown below. The UUID is based on a base 128-bit UUID used for Bluetooth LE custom services and characteristics. These are shown in the tables below.

Service

128-bit UUID

Base Bluetooth Low Energy

00000000 -ba5e-f4ee-5ca1-eb1e5e4b1ce0

The OTAP Service custom 128-bit UUID is built using the base UUID by replacing the most significant 4 bytes which are 0 with a value specific to the OTAP Service which is 01FF5550 in hexadecimal format.

Service

UUID (128-bit)

Bluetooth LE OTAP Service

01ff5550 -ba5e-f4ee-5ca1-eb1e5e4b1ce0

The Bluetooth LE OTAP Service Characteristics UUIDs are built the same as the Bluetooth LE OTAP Service UUID starting from the base 128-bit UUID but using other values for the most significant 4 bytes.

Characteristic

UUID (128-bit)

Properties

Descriptors

Bluetooth LE OTAP Control Point

01ff5551 -ba5e-f4ee-5ca1-eb1e5e4b1ce0

Write, Indicate

CCC

Bluetooth LE OTAP Data

01ff5552 -ba5e-f4ee-5ca1-eb1e5e4b1ce0

Write Without Response

-

Both characteristics are implemented as variable length characteristics.

The Bluetooth LE OTAP Control Point Characteristic is used for exchanging OTAP commands between the OTAP Server and the OTAP Client. The OTAP Client sends commands to the OTAP Server using ATT Notifications for this characteristic and the OTAP Server sends commands to the OTAP Client by making ATT Write Requests to this characteristic. Both ATT Writes and ATT Notifications are acknowledged operations via ATT Write Responses and ATT Confirmations.

The Bluetooth LE OTAP Data characteristic is used by the OTAP Server to send parts of the OTAP image file to the OTAP Client when the ATT transfer method is chosen by the application. The ATT Write Commands (GATT Write Without Response operation) is not an acknowledged operation.

The Bluetooth LE OTAP service and characteristics 128-bit UUIDs are defined in the gatt_uuid128.h just as shown below.

UUID128(uuid_service_otap, 0xE0, 0x1C, 0x4B, 0x5E, 0x1E, 0xEB, 0xA1, 0x5C, 0xEE, 0xF4, 0x5E, 0xBA, 0x50, 0x55, 0xFF, 0x01)
UUID128(uuid_char_otap_control_point, 0xE0, 0x1C, 0x4B, 0x5E, 0x1E, 0xEB, 0xA1, 0x5C, 0xEE, 0xF4, 0x5E, 0xBA, 0x51, 0x55, 0xFF, 0x01)
UUID128(uuid_char_otap_data, 0xE0, 0x1C, 0x4B, 0x5E, 0x1E, 0xEB, 0xA1, 0x5C, 0xEE, 0xF4, 0x5E, 0xBA, 0x52, 0x55, 0xFF, 0x01)

The service is included into the GATT database of the device. It is declared in the gatt_db.h file as shown below.

PRIMARY_SERVICE_UUID128(service_otap, uuid_service_otap)
CHARACTERISTIC_UUID128(char_otap_control_point, uuid_char_otap_control_point, (gGattCharPropWrite_c | gGattCharPropIndicate_c))
VALUE_UUID128_VARLEN(value_otap_control_point, uuid_char_otap_control_point, (gPermissionFlagWritable_c), 16, 16, 0x00)
CCCD(cccd_otap_control_point)
CHARACTERISTIC_UUID128(char_otap_data, uuid_char_otap_data, (gGattCharPropWriteWithoutRsp_c))
VALUE_UUID128_VARLEN(value_otap_data, uuid_char_otap_data, (gPermissionFlagWritable_c), gAttMaxMtu_c - 3, gAttMaxMtu_c - 3, 0x00)

The Bluetooth LE OTAP Control Point characteristic should be large enough for the longest command which can be exchanged between the OTAP Server and The OTAP Client.

The Bluetooth LE OTAP Data characteristic should be large enough for the longest data chunk command the OTAP Client expects from the OTAP Server to be sent via ATT. The maximum length of the OTAP Data Characteristic value is ATT_MTU- 3. 1 byte is used for the ATT OpCode and 2 bytes are used for the Attribute Handle when performing a Write Without Response, the only operation permitted for this characteristic value.

Parent topic:Bluetooth Low Energy OTAP service-profile

OTAP server and OTAP client interactions

The OTAP Server application scans for devices advertising the OTAP Service. When it finds one it connects to that device and notifies it of the available image files or waits for requests regarding available image files. The behavior is specific to each application which needs the OTAP functionality. The Bluetooth LE OTAP Protocol described below details how to do this.

After an OTAP Server (GAP Central, GATT Client) connects to an OTAP Client (GAP Peripheral, GATT Server) it scans the device database and identifies the handles of the OTAP Control Point and OTAP Data characteristics and their descriptors. Then it writes the CCC Descriptor of the OTAP Control point to allow the OTAP Client to send it commands via ATT Indications. It can send commands to the OTAP Client by using ATT Write Commands to the OTAP Control Point characteristic.

After the connection is established, if the OTAP Client wants to use the L2CAP CoC transfer method it must register a L2CAP PSM with the OTAP Server.

The OTAP Client only starts any image information request or image transfer request procedures only after the OTAP Server writes the OTAP Control Point CCCD to ensure there is bidirectional communication between the devices.

Parent topic:Bluetooth Low Energy OTAP service-profile

Parent topic:Over the Air Programming (OTAP)

Bluetooth LE OTAP protocol

The protocol consists of a set of commands (messages) which allow the OTAP Client to request or be notified about the available images on an OTAP Server and to request the transfer of parts of images from the OTAP Server.

All commands with the exception of the image data transfer commands are exchanged through the OTAP Control Point characteristic of the OTAP Service. The data transfer commands are sent only from the OTAP Server to the OTAP Client either via the OTAP Data characteristic of the OTAP Service or via a dedicated Credit Based Channel assigned to a L2CAP PSM.

Protocol design considerations

The OTAP Client is a GAP Peripheral device, and therefore has limited resources. This is why the OTAP Protocol was designed in such a way that it is at the discretion of the OTAP Client if, when, how fast and how much of an available upgrade image is transferred from the OTAP Server. The OTAP Client also decides which is the image transfer method based on its capabilities. Two image transfer methods are supported at this moment: the ATT Transfer Method and the L2CAP PSM CoC Transfer Method.

The ATT Transfer Method is supported by all devices which support Bluetooth LE but it has the disadvantage of a small data payload size and a larger Bluetooth LE stack protocols overhead leading to a lower throughput. This disadvantage has been somewhat reduced by the introduction of the Long Frames feature in the Bluetooth LE specification 4.2 Link Layer which allows for a larger ATT_MTU value. The L2CAP PSM CoC Transfer Method is an optional feature available for devices running a Bluetooth stack version 4.1 and later. The protocol overhead is smaller and the data payload is higher leading to a high throughput. The L2CAP PSM Transfer Method is the preferred transfer method and it is available on all Bluetooth LE Devices if the application requires it.

Based on application requirements and device resources and capabilities the OTAP Clients can request blocks of OTAP images divided into chunks. To minimize the protocol overhead and maximize throughput an OTAP Client makes a data block request specifying the block size and the chunk size and the OTAP Server sends the requested data chunks (which have a sequence number) without waiting for confirmation. The block size, chunk size and number of chunks per block are limited and suitable values must be used based on application needs.

The OTAP Client can stop or restart an image block transfer at any time if the application requires it or a transfer error occurs. The OTAP Server implementation can be almost completely stateless. The OTAP Server does not need to remember what parts of an image have been transferred, this is the job of the OTAP Client which can request any part of an image at any time. This allows it to download parts of the image whenever and how fast its resources allow it. The OTAP Server simply sends image information and image parts on request.

The Bluetooth LE OTAP Protocol is designed to be used not only on Bluetooth LE transport medium but on any transport medium, for example a serial communication interface or another type of wireless interface. This may be useful when transferring an upgrade image from a PC or a mobile device to the OTAP Server to be sent via Bluetooth LE to the OTAP Clients which require it. In the OTAP Demo Applications the embedded OTAP Server relays OTAP commands between an OTAP Client and a PC via a serial interface and using a FSCI type protocol. Effectively the OTAP Client downloads the upgrade image from the PC and not from the OTAP Server. Other transfer methods may be used based on application needs.

Parent topic:Bluetooth LE OTAP protocol

Bluetooth Low Energy OTAP commands

The Bluetooth LE OTAP Commands general format is shown below. A command consists of two parts, a Command ID, and a Command Payload as shown in the table below.

Field Name

CmdId

CmdPayload

Size (Bytes)

1

variable

Commands are sent over the transport medium starting with the Command ID and continuing with the Command Payload.

All multibyte command parameters in the Command Payload are sent in a least significant octet first order (little endian).

A summary of the commands supported by the Bluetooth LE OTAP Protocol is shown in the table below. Each of the commands is then detailed in its own section.

CmdId

Command Name

0x01

New Image Notification

0x02

New Image Info Request

0x03

New Image Info Response

0x04

Image Block Request

0x05

Image Chunk

0x06

Image Transfer Complete

0x07

Error Notification

0x08

Stop Image Transfer

New image notification command

This command can be sent by an OTAP Server to an OTAP Client, usually immediately after the first connection, to notify the OTAP Client of the available images on the OTAP Server.

|CmdId|Name|Dir|Parameters|Param Size

(Bytes)

|Description|Total Size (CmdId+Payload)| |—–|—-|—|———-|————————|———–|—————————-| |0x01|New Image Notification|S->C|ImageId|2|Short image identifier used for transactions between the OTAP Server and OTAP Client. Should be unique for all images on a server.|15| |ImageVersion|8|Image file version. Contains sufficient information to identify the target hardware, stack version and build version.| |ImageFileSize|4|Image file size in bytes.|

The ImageId parameter should not be ‘0x0000’, which is the reserved value for the current running image or 0xFFFF, which is the reserved value for “no image available”.

Parent topic:Bluetooth Low Energy OTAP commands

New image info request command

This command can be sent by an OTAP Client to an OTAP Server to inquire about available upgrade images on the OTAP Server.

|CmdId|Name|Dir|Parameters|Param Size

(Bytes)

|Description|Total Size (CmdId+Payload)| |—–|—-|—|———-|————————|———–|—————————-| |0x02|New Image Info Request|C->S|CurrImageId|2|Id of the currently running image. Should be 0x0000.|11| |CurrImageVer|8|Version of the currently running image. A value of all zeroes signals that the client is looking for all images available on an OTAP Server. A value of all zeroes requests information about all images on the server.|

The CurrImageId parameter should be set to 0x0000 to signify the current running image.

The CurrImageVer parameter should contain sufficient information about the target device for the OTAP Server to determine if it has an upgrade image available for the requesting OTAP Client.

A value of all zeroes for the CurrImageVer means that an OTAP Client is requesting information about all available images on an OTAP Server and the OTAP Server should send a New Image Info Response for each image.

Parent topic:Bluetooth Low Energy OTAP commands

New image info response command

This command is sent by the OTAP Server to the OTAP Client as a response to a New Image Information Request Command.

|CmdId|Name|Dir|Parameters|Param Size

(Bytes)

|Description|Total Size (CmdId+Payload)| |—–|—-|—|———-|————————|———–|—————————-| |0x03|New Image Info Response|S->C|ImageId|2|Image Id. Value 0xFFFF is reserved as “no image available”|15| |ImageVersion|8|Image file version.| |ImageFileSize|4|Image file size.|

The ImageId parameter with a value of 0xFFFF is reserved for the situation where no upgrade image is available for the requesting device.

Parent topic:Bluetooth Low Energy OTAP commands

Image block request command

This command is sent by the OTAP Client to the OTAP Server to request a part of the upgrade image after it has determined the OTAP Server has an upgrade image available.

When an OTAP Server Receives this command it should stop any image file chunk transfer sequences in progress.

|CmdId|Name|Dir|Parameters|Param Size

(Bytes)

|Description|Total Size (CmdId+Payload)| |—–|—-|—|———-|————————|———–|—————————-| |0x04|Image Block Request|C->S|ImageId|2|Image Id|16| |StartPosition|4|Start position of the image block to be transferred.| |BlockSize|4|Requested total block size in bytes.| |ChunkSize|2|Should be optimized to the TransferChannel type. The maximum number of chunks per block is 256. Value is in bytes.| |TransferMethod|1|0x00 - ATT

0x01 – L2CAP PSM Credit based channel

| |L2capChannelOrPsm|2|0x0004 - ATT

Other values – PSM for credit based channels

|

The ImageId parameter contains the ID of the upgrade image.

The StartPosition parameter specifies the location in the image upgrade file at which the requested block starts.

The BlockSize and ChunkSize parameters specify the size in bytes of the block to be transferred and the size of the chunks into which a block is separated. The ChunkSize value must be chosen in such a way that the total number of chunks can be represented by the SeqNumber parameter of the Image Chunk Command. At the moment this parameter is 1 byte in size so there are a maximum of 256 chunks per block. The chunk sequence number goes from 0 to 255 (0x00 to 0xFF). If this condition is not met or the requested block is not entirely into the image file bounds an error is sent to the OTAP Client when the OTAP Server receives this misconfigured Image Block Request Command.

The maximum value of the ChunkSize parameter depends on the maximum ATT_MTU and L2CAP_MTU supported by the Bluetooth LE stack version and implementation.

The TransferMethod parameter is used to select the transfer method which can be ATT or L2CAP PSM CoC. The L2capChannelOrPsm parameter must contain the value 0x0004 for the ATT transfer method and another value representing the chosen PSM for the L2CAP PSM transfer method. The default PSM for the Bluetooth LE OTAP demo applications is 0x004F for both the OTAP Server and the OTAP Client although the specification allows different values at the 2 ends of the L2CAP PSM connection. The PSM must be in the range reserved by the Bluetooth specification which is 0x0040 to 0x007F.

The optimal value of the ChunkSize parameter depends on the chosen transfer method and the Link Layer payload size. Ideally it must be chosen in such a way that full packets are sent for every chunk in the block.

The default Link Layer payload is 27 bytes form which we subtract 4 for the L2CAP layer and 3 for the ATT layer (1 for the ATT Cmd Opcode and 2 for the Handle) leaving us with a 20 byte OTAP protocol payload. From these 20 bytes we subtract 1 for the OTAP CmdId and 1 for the chunk sequence number leaving us with an optimum chunk size of 18 for the ATT transfer method – which is the default in the demo applications. For the L2CAP PSM transfer method the chosen default chunk size is 111. This was chosen so as a chunk fits exactly 5 link layer packets. The default L2CAP payload of 23 (27 - 4) multiplied by 5 gives us 115 from which we subtract 2 bytes for the SDU Length (which is only sent in the first packet), 1 byte for the OTAP CmdId and 1 byte for the chunk sequence number which leaves exactly 111 bytes for the actual payload.

If the Link layer supports Long Frames feature then the chunk size should be set according to the negotiated ATT MTU for the ATT transfer method. From the negotiated ATT MTU (att_mtu) subtract 3 bytes for the ATT layer (1 for the ATT Cmd Opcode and 2 for the Handle) then subtract 2 bytes for the OTAP protocol (1 for the CmdId and 1 for the chunk sequence number) to determine the optimum chunk size (optimum_att_chunk_size = att_mtu – 3 – 2). For the L2CAP PSM transfer method the chunk size can be set based on the maximum L2CAP SDU size (max_l2cap_sdu_size) from which 4 bytes should be subtracted, 2 for the SDU Length and 2 for the OTAP protocol (optimum_l2cap_chunk_size = max_l2cap_sdu_size – 3 – 2). In some particular cases reducing the L2CAP chunk size could lead to better performance. If the L2CAP chunk size needs to be reduced it should be reduced so it fits exactly a number of link layer packets. An example of how to compute an optimal reduced L2CAP chunk size is given in the previous paragraph.

Parent topic:Bluetooth Low Energy OTAP commands

Image chunk command

One or more Image Chunk Commands are sent from the OTAP Server to the OTAP Client after an Image Block Request is received by the former. The image chunks are sent via the ATT Write Without Response mechanism if the ATT transfer method is chosen and directly via L2CAP if the L2CAP PSM CoC transfer method is chosen.

|CmdId|Name|Dir|Parameters|Param Size

(Bytes)

|Description|Total Size (CmdId+Payload)| |—–|—-|—|———-|————————|———–|—————————-| |0x05|Image Chunk|S->C|SeqNumber|1|In the range 0 -> BlockSize/ChunkSize - calculated by Server, checked by Client.

The command code is present even when ATT is used.

|3 or more

| |Data|variable|Actual data|

The SeqNumber parameter is the chunk sequence number and it has incremental values from 0 to 255 (0x00 to 0x FF) for a maximum of 256 chunks per block.

The Data parameter is an array containing the actual image part being transferred starting from the BlockStartPosition + SeqNumber ***ChunkSizeposition in the image file and containing ChunkSize or less bytes depending on the position in the block. Only the last chunk in a block can have less than ChunkSizebytes in the Image Chunk Command data payload.

Parent topic:Bluetooth Low Energy OTAP commands

Image transfer complete command

This command is sent by the OTAP Client to the OTAP Server when an image file has been completely transferred and its integrity has been checked.

|CmdId|Name|Dir|Parameters|Param Size

(Bytes)

|Description|Total Size (CmdId+Payload)| |—–|—-|—|———-|————————|———–|—————————-| |0x06|Image Transfer Complete|C->S|ImageId|2|Image Id|4| |Status|1|Status of the image transfer. 0x00 - Success|

The ImageId parameter contains the ID of the image file that was transferred.

The Status parameter is 0x00 (Success) if image integrity and possibly other checks have been successfully made after the image is transferred and another value if integrity or other kind of errors have occurred.

If the status is 0x00 the OTAP Client can trigger the Bootloader to start flashing the new image. The image flashing should take about 15 seconds for a 160 KB flash memory.

Parent topic:Bluetooth Low Energy OTAP commands

Error notification command

This command can be sent by both the OTAP Server and the OTAP Client when an error of any kind occurs. When an OTAP Server Receives this command it should stop any image file chunk transfer sequences in progress.

CmdId

Name

Dir

Parameters

Param Size (Bytes)

Description

Total Size (CmdId+Payload)

0x07

Error Notification

Bidir

CmdId

1

Id of the command which generated the error.

3

ErrorStatus

1

Error Status: Examples: out of image bounds, chunk too small, chunk too large, image verification failure, bad command format, image not available, unknown command

|

The CmdId parameter contains the ID of the command which caused the error (if applicable).

The ErrorStatus parameter contains the source of the error. All error statuses are defined in the otapStatus_t enumerated type in the otap_interface.h file.

Parent topic:Bluetooth Low Energy OTAP commands

Stop image transfer command

This command is sent from the OTAP Client to the OTAP Server whenever the former wants to stop the transfer of an image block which is currently in progress, or from OTAP Server to the OTAP Client when the image transfer is stopped from application (Test Tool).

|CmdId|Name|Dir|Parameters|Param Size

(Bytes)

|Description|Total Size (CmdId+Payload)| |—–|—-|—|———-|————————|———–|—————————-| |0x08|Stop Image Transfer|C->S|ImageId|2|Image Id|3|

The ImageId parameter contains the ID of the image being transferred.

Parent topic:Bluetooth Low Energy OTAP commands

Parent topic:Bluetooth LE OTAP protocol

OTAP client–server interactions

The interactions between the OTAP Server and OTAP Client start immediately after the connection, discovery of the OTAP Service characteristics and writing of the OTAP Control Point CCC Descriptor by the OTAP Server.

The first command sent could be a New Image Notification sent by the OTAP Server to the OTAP Client or a New Image Info Request sent by the OTAP Client. The OTAP Server can respond with a New Image Info response if it has a new image for the device which sent the request (this can be determined from the ImageVerison parameter). The best strategy depends on application requirements.

After the OTAP Client has determined that the OTAP Sever has a newer image it can start downloading the image. This is done by Sending Image Block Request commands to retrieve parts of the image file. The OATP Server answers to these requests with one or more Image Chunk Commands via the requested transfer method or with an Error Notification if there are improper parameters in the Image Block Request. The OTAP Client makes as many Image Block Requests as it is necessary to transfer the entire image file.

The OTAP Client decides how often Image Block Request Commands are sent and can even stop a block transfer which is in progress via the Stop Image Transfer Command. The OTAP Client is in complete control of the image download process and can stop it and restart it at any time based on its resources and application requirements.

A typical Bluetooth LE OTAP Image Transfer scenario is shown in the message sequence chart Figure 1.

Parent topic:Bluetooth LE OTAP protocol

Parent topic:Over the Air Programming (OTAP)

Bluetooth Low Energy OTAP image file format

The Bluetooth LE OTAP Image file has a binary file format. It is composed of a header followed by a number of sub-elements. The header describes general information about the file. There are some predefined sub-elements of a file but an end manufacturer could add manufacturer-specific sub-elements. The header does not have details of the sub-elements. Each element is described by its type.

The general format of an image file is shown in the table below.

Image File Element

Value Field Length (bytes)

Description

Header

Variable

The header contains general information about the image file.

Upgrade Image Sub-element

Variable

This sub-element contains the actual binary executable image, which is copied into the flash memory of the target device. The maximum size of this sub-element depends on the target hardware.

Image File CRC Sub-element

2

This is a 16-bit CCITT type CRC which is calculated over all elements of the image file with the exception of the Image File CRC sub-element itself. This must be the last sub-element in an image file.

Each sub-element in a Bluetooth LE OTAP Image File has a Type-Length-Value (TLV) format. The type identifier provides forward and backward compatibility as new sub-elements are introduced. Existing devices that do not understand newer sub-elements may ignore the data.

The following table shows the general format of a Bluetooth LE Image File sub-element.

Subfield

Size (Bytes)

Format

Description

Type

2

uint16

Type Identifier – determines the format of the data contained in the value field

Length

4

uint32

Length of the Value field of the sub-element.

Value

variable

uint8[]

Data payload

Some sub-element type identifiers are reserved while others are left for manufacturer-specific use. The table below shows the reserved type identifiers and the manufacturer-specific ranges.

Type Identifiers

Description

0x0000

Upgrade Image

0x0001 – 0xefff

Reserved

0xf000 – 0xffff

Manufacturer-Specific Use

The OTAP Demo applications use two of the manufacturer-specific sub-element type identifiers while the rest remain free to use. The two are shown in the table below along with a short description.

Manufacturer-specific Type Identifiers

Sub-element Name

Notes

0xf000

Sector Bitmap

Bitmap that signals the bootloader the sectors of the internal flash, which should be overwritten and which should remain as is.

0xf100

Image File CRC

16-bit CRC that is computed over the image file with the exception of the CRC sub-element itself.

Bluetooth Low Energy OTAP header

The format and fields of the Bluetooth Low Energy OTAP Header are summarized in the table below.

|Octets|Data Types|Field Name|Mandatory/Optional| |4|Unsigned 32-bit integer|Upgrade File Identifier|M| |2|Unsigned 16-bit integer|Header Version|M| |2|Unsigned 16-bit integer|Header Length|M| |2|Unsigned 16-bit integer|Header Field Control|M| |2|Unsigned 16-bit integer|Company Identifier|M| |2|Unsigned 16-bit integer|Image ID|M| |8|8 byte array|Image Version|M| |32|Character string|Header String|M| |4|Unsigned 32-bit integer|Total Image File Size

(including header)

|M|

The fields are shown in the order they are placed in memory from the first location to the last.

The total size of the header without the optional fields (if defined by the Header Field Control) is 58 bytes.

All the fields in the header have a little endian format with the exception of the Header String field which is an ASCII character string.

A packed structure type definition for the contents of the Bluetooth LE OTAP Header can be found in the otap_interface.hfile.

Upgrade file identifier

Fixed value 4 byte field used to identify the file as being a Bluetooth LE OTAP Image File. The predefined value is “0x0B1EF11E”.

Parent topic:Bluetooth Low Energy OTAP header

Header version

This 2 byte field contains the major and minor version number. The high byte contains the major version and the low byte contains the minor version. The current value is “0x0100” with the major version “01” and the minor version “00”. A change to the minor version means the OTA upgrade file format is still backward compatible, while a change to the major version suggests incompatibility.

Parent topic:Bluetooth Low Energy OTAP header

Header length

Length of all the fields in the header including the Upgrade File Identifier field, Header Length field and all the optional fields. The value insulates existing software against new fields that may be added to the header. If new header fields added are not compatible with current running software, the implementations should process all fields they understand and then skip over any remaining bytes in the header to process the image or CRC sub-element. The value of the Header Length field depends on the value of the Header Field Control field, which dictates which optional header fields are included.

Parent topic:Bluetooth Low Energy OTAP header

Header field control

This is a 2-byte bit mask that specifies the optional fields present in the OTAP Header.

In case no optional fields are defined, this whole field is reserved and should be set to “0x0000”.

Parent topic:Bluetooth Low Energy OTAP header

Company identifier

This is the company identifier assigned by the Bluetooth SIG. The Company Identifier used for the OTAP demo applications is “0x01FF”.

Parent topic:Bluetooth Low Energy OTAP header

Image ID

This is a unique short identifier for the image file. It is used to request parts of an image file. This number should be unique for all images available on a Bluetooth LE OTAP Server.

  • The value 0x0000 is reserved for the current running image.

  • The value 0xFFFF is reserved as a “no image available” code for New Image Info Response commands.

This field value must be used in the ImageID field in the New Image Notification and New Image Info Response commands.

Parent topic:Bluetooth Low Energy OTAP header

Image version

This is the full identifier of the image file. It should allow a Bluetooth LE OTAP Client to identify the target hardware, stack version, image file build version, and other parameters if necessary. The recommended format of this field (which is used by the OTAP Demo applications) is shown below but an end device manufacturer could choose different format. The subfields are shown in the order they are placed in memory from the first location to the last. Each subfield has a little-endian format, if applicable. Refer Table 1

Subfield

Size (bytes)

Format

Description

Build Version

3

uint8[]

Image build version.

Stack Version

1

uint8

0x41 for example for Bluetooth Low Energy Stack version 4.1.

Hardware ID

3

uint8[]

Unique hardware identifier.

End Manufacturer Id

1

uint8

ID of the hardware–specific to the end manufacturer

This field value must be used in the ImageVersion field in the New Image Notification and New Image Info Response commands.

Parent topic:Bluetooth Low Energy OTAP header

Header string

This is a manufacturer-specific string that may be used to store other necessary information as seen fit by each manufacturer. The idea is to have a human readable string that can prove helpful during the development cycle. The string is defined to occupy 32 bytes of space in the OTAP Header. The default string used for the Bluetooth LE OTAP demo application is “BLE OTAP Demo Image File”.

Parent topic:Bluetooth Low Energy OTAP header

Total image file size

The value represents the total image size in bytes. This is the total of data in bytes that is transferred over-the-air from the server to the client. In most cases, the total image size of an OTAP upgrade image file is the sum of the sizes of the OTAP Header and all the other sub-elements on the file. If the image contains any integrity and/or source identity verification fields then the Total Image File Size also includes the sizes of these fields.

Parent topic:Bluetooth Low Energy OTAP header

Parent topic:Bluetooth Low Energy OTAP image file format

Parent topic:Over the Air Programming (OTAP)

Building Bluetooth Low Energy OTAP image file from SREC file

A SREC (Motorola S-record) file is an ASCII format file which contains binary information. Common file extensions are: .srec, .s19, .s28, .s37 and others. Most modern compiler toolchains can generate an SREC format executable.

The steps described in this section enable the creation of a SREC file for your embedded application in IAR Embedded Workbench.

For this, open the target properties and go to the Output Converter tab. Activate the Generate additional output checkbox and choose the Motorola option from the Output format drop down menu. From the same pane you can also override the name of the output file. A screenshot of the described configuration is shown in Figure 1.

![](../images/figure_19_new_srec.png “Enabling Options for Node “otap_client_att_freertos” in IAR Embedded Workbench”)

In MCUXpresso IDE, go to Project properties -> Settings -> Build steps window and press the “Edit” button for the Post-build steps. A Post-build steps window shows up in which the following command must be added:

arm-none-eabi-objcopy -v -O srec --only-section=.text --only-section=.data --only-section=.ARM.exidx 
"${BuildArtifactFileName}" 
"${BuildArtifactFileBaseName}.srec"

A snapshot of this window is shown in the Figure 2.

The format of the SREC file is shown in Table 1. It contains lines of text called records which have a specific format. An example of the contents of a SREC file is shown below.


                S02000006F7461705F636C69656E745F6174745F4672656552544F532E73726563A1
                S1130000F83F0020EB0500007506000075060000AF
                S113001075060000750600007506000075060000F0
                S113002075060000750600007506000075060000E0
                S113003075060000750600007506000075060000D0
                S113004000000000000000000000000000000000AC
                S1130050000000000000000000000000000000009C
                .............
                S2140117900121380004F05FF8002866D12A003100E4
                S2140117A06846008804F022F8A689002E16D0002884
                S2140117B014D12569278801A868A11022F7F782FCB1
                S2140117C06B4601AA0121380004F045F800284CD1E7
                S2140117D02A0031006846008804F008F8A68A002E20

All records start with the ASCII letter ‘S’ followed by an ASCII digit from ‘0’ to ‘9’. These two characters from the record type identify the format of the data field of the record.

The next 2 ASCII characters are 2 hex digits that indicate the number of bytes (hex digit pairs) which follow the rest of the record (address, data, and checksum).

The address that follows next can have 4, 6, or 8 ASCII hex digits, depending on the record type.

The data field is placed after the address and it contains 2 * n ASCII hex digits for ‘n’ bytes of actual data.

The last element of the S record is the checksum, which comprises 2 ASCII hex digits. The checksum is computed by adding all the bytes of the byte count, address, and data fields. Then the ones complement of the least significant octet of the sum is computed to determine the checksum.

Field

Record Type

Count

Address

Data

Checksum

Line Terminator

Format

“Sn”, n=0..9

|ASCII

hex digits

|ASCII

hex digits

|ASCII

hex digits

|ASCII

hex digits

|“\r\n”| |Length (characters)|2|2|4,6,8|*Count –len(Address) –*len(Checksum)|2|2|

More details about the SREC file format can be found at this location: en.wikipedia.org/wiki/SREC_(file_format).

We are only interested in records that contain actual data. These are S1, S2, and S3 records. The other types of records can be ignored.

The S1, S2, and S3 records are used to build the Upgrade Image Sub-element of the image file simply by placing the record data at the location specified by the record address in the Value field of the Sub-element. It is recommended to fill all gaps in S record addresses with 0xFF.

To build an OTAP Image File from a SREC file, follow the procedure described below:

  • Generate the SREC file by correctly configuring your toolchain to do so.

  • Create the image file header.

    • Set the Image ID field of the header to be unique on the OTAP Server.

    • Leave the Total Image File Size Field blank for the moment.

  • Create the Upgrade Image Sub-element

    • Read the S1, S2, and S3 records from the SREC file and place the binary record data to the record addresses in the Value filed of the sub-element. Fill all address gaps in the S records with 0xFF.

    • Fill in the Length field of the sub-element with the length of the written Value field.

  • Create the Sector Bitmap Sub-element

    • A default working setting would be all byes 0xFF for the Value field of this sub-element.

  • Create the Image File CRC Sub-element

    • Compute the total image file size as the length of the header + the length of all 3 sub-elements and fill in the appropriate filed in the header with this value.

    • Compute and write the Valuefield of this sub-element using the header and all sub-elements except this one.

    • The OTA_CrcCompute() function in the OtaSupport.c file can be used to incrementally compute the CRC.

If the Image ID is not available when the image file is created, then the CRC cannot be computed. It can be computed later after the Image ID is established and written in the appropriate field in the header.

Parent topic:Over the Air Programming (OTAP)

Building Bluetooth Low Energy OTAP image file from BIN file

A BIN file is an binary file which contains an executable image. The most common extension for this type of file is .bin. Most modern compiler toolchains can output a BIN format executable.

To enable the creation of a BIN file for your embedded application in IAR Embedded Workbench open the target properties and go to the Output Converter tab. Activate the “Generate additional output” checkbox and choose the binary option from the “Output format” drop down menu. From the same pane you can also override the name of the output file. The Figure 1 shows a screenshot of the described configuration.

In MCUXpresso IDE, go to Project properties -> Settings -> Build steps window and press the “Edit” button for the Post-build steps. A Post-build steps window shows up in which the following command must be added:

arm-none-eabi-objcopy -v -O binary --only-section=.text --only-section=.data --only-section=.ARM.exidx 
   "${BuildArtifactFileName}"
   "${BuildArtifactFileBaseName}.bin"

The Figure 2 shows the Build steps and Post-build steps in Settings window.

The format of the BIN file is very simple. It contains the executable image in binary format as is, starting from address 0 and up to the highest address. This type of file does not have any explicit address information.

To build an OTAP Image File from a BIN file, follow the procedure below:

  • Generate the BIN file by correctly configuring your toolchain to do so.

  • Create the image file header

    • Set the Image ID field of the header to be unique on the OTAP Server.

    • Leave the Total Image File Size field blank for the moment.

  • Create the Upgrade Image Sub-element

    • Copy the entire contents of the BIN file as is into the Value filed of the sub-element.

    • Fill in the Length field of the sub-element with the length of the written Value filed.

  • Create the Sector Bitmap Sub-element

    • A default working setting would be all bytes 0xFF for the Value field of this sub-element.

  • Create the Image File CRC Sub-element

    • Compute the total image file size as the length of the header + the length of all 3 sub-elements and fill in the appropriate filed in the header with this value.

    • Compute and write the Valuefield of this sub-element using the header and all sub-elements except this one.

    • The OTA_CrcCompute() function in the OtaSupport.c file can be used to incrementally compute the CRC.

If the Image ID is not available when the image file is created, then the CRC cannot be computed. It can be computed later after the Image ID is established and written in the appropriate field in the header.

Parent topic:Over the Air Programming (OTAP)

Bluetooth Low Energy OTAP application integration

The Bluetooth Low Energy OTAP demo applications are standalone applications that only run the OTAP Server and the OTAP Client. In practice, however the OTAP Server and OTAP Client are used alongside with other functions. The OTAP functionality is used as a tool along with the main application on a device.

This section contains some guidelines on how to integrate OTAP functionality into other Bluetooth Low Energy applications.

OTAP server

Before any OTAP transactions can be done the application which acts as an OTAP Server must connect to a peer device and perform ATT service and characteristic discovery. Once the handles of the OTAP Service, OTAP Control Point and OTAP Data characteristics and their descriptors are found then OTAP communication can begin.

A good starting point for OTAP transactions for both the OTAP Server and The OTAP client is the moment the Server writes the OTAP Control Point CCCD to receive ATT Indications from the OTAP Client. At that point the Server can send a New Image Notification to the Client if it finds out what kind of device the client is through other means than the OTAP server. How this can be done is entirely application-specific. If the OTAP Server does not know exactly what kind of device is the OTAP Client it can wait for the Client to send a New Image Info Request. Again, the best behavior depends on application requirements.

Once OTAP communication begins then the OTAP Server just has to wait for commands from the OTAP Client and answer them. This behavior is almost completely stateless. An example state diagram for the OTAP Server application is shown in Figure 1.

The OTAP Server waits in an idle state until a valid Image Block Request command is received and then moves to a pseudo-state and starts sending the requested block. The transfer can be interrupted by some commands (Error Notification, Stop Image Transfer, and so on) or other events (disconnection, user interruption, and so on).

The otap_interface.h file contains infrastructure for sending and receiving OTAP Commands and parsing OTAP image files. Packed structure types are defined for all OTAP commands and type enumerations are defined for command parameter values and some configuration values like the data payloads for the different transfer methods.

To receive ATT Indications and ATT Write Confirmations from the OTAP Client the OTAP Server application registers a set of callbacks in the stack. This is done in the BluetoothLEHost_Initialized function.

App_RegisterGattClientProcedureCallback (BleApp_GattClientCallback);
App_RegisterGattClientIndicationCallback (BleApp_GattIndicationCallback);

This BleApp_GattIndicationCallback() function is called when any attribute is indicated so the handle of the indicated attribute must be checked against a list of expected handles. In our case, we are looking for the OTAP Control Point handle that was obtained during the discovery procedure.

The BleApp_GattIndicationCallback() function from the demo calls an application-specific function called BleApp_AttributeIndicated() in which the OTAP Commands are handled.

**static void BleApp\_AttributeIndicated**
(
    deviceId_t     deviceId,
    uint16_t       handle,
    uint8_t*       pValue,
    uint16_t       length
)
{
    **if** (handle == mPeerInformation.customInfo.otapServerConfig.hControlPoint)
    {
       otapCommandVars.pValueTemp = pValue;
        otapCommand_t*  pOtaCmd = otapCommandVars.otapCommandTemp;
        /* ... Missing code here ... */
        /* If the OTAP Server does not have internal storage then all commands must be forwarded
          *  via the serial interface. */
            FsciBleOtap_SendPkt (&(pOtaCmd->cmdId),
                        (uint8_t*)(&(pOtaCmd->cmd)),
                        length - gOtap_CmdIdFieldSize_c);
    }
    **elseif** (handle == otherHandle)
    {
        /* Handle other attribute indications here */
        /* ... Missing code here ... */
    }
    **else**
    {
        /*! A GATT Client is trying to GATT Indicate an unknown attribute value.
         * This should not happen. Disconnect the link. */
        Gap_Disconnect (deviceId);
    }
}

OTAP Server demo does not have internal storage, so all commands are forwarded via the serial interface.

To send OTAP Commands to the OTAP Client the application running the OTAP Server calls the OtapServer_SendCommandToOtapClient() function, which performs an ATT Write operation on the OTAP Control Point attribute.

**static void OtapServer\_SendCommandToOtapClient** 
        (deviceId_t  otapClientDevId,
         void*       pCommand,
         uint16_t    cmdLength)
{
    /* GATT Characteristic to be written - OTAP Client Control Point */
    gattCharacteristic_t    otapCtrlPointChar;
    bleResult_t             bleResult;

    /* Only the value handle element of this structure is relevant for this operation. */
    otapCtrlPointChar.value.handle = mPeerInformation.customInfo.otapServerConfig.hControlPoint;
    otapCtrlPointChar.value.valueLength = 0;
    otapCtrlPointChar.cNumDescriptors = 0;
    otapCtrlPointChar.aDescriptors = NULL;

    bleResult = GattClient_SimpleCharacteristicWrite (mPeerInformation.deviceId,
                                                      &otapCtrlPointChar,
                                                      cmdLength,
                                                      pCommand);
**
    if** (gBleSuccess_c == bleResult)
    {
        otapServerData.lastCmdSentToOtapClient = (otapCmdIdt_t)(((otapCommand_t*)pCommand)->cmdId);
    }
    **else**
    {
        /*! A Bluetooth Low Energy error has occurred - Disconnect */
        (void)Gap_Disconnect (otapClientDevId);
    }
}
   

The ATT Confirmation for the ATT Write is received in the BleApp_GattClientCallback() set up earlier which receives a GATT procedure success message for a gGattProcWriteCharacteristicValue_c procedure type.

**static void BleApp\_GattClientCallback**(
    deviceId_t              serverDeviceId,
    gattProcedureType_t     procedureType,
    gattProcedureResult_t   procedureResult,
    bleResult_t             error
)
{
   ** union**
    {
        uint8_t                     errorTemp;
        attErrorCode_t              attErrorCodeTemp;
    }attErrorCodeVars;

    **if** (procedureResult == gGattProcError_c)
    {
        attErrorCodeVars.errorTemp = (uint8_t)error & 0xFFU;
        attErrorCode_t attError = attErrorCodeVars.attErrorCodeTemp;
        if (attError == gAttErrCodeInsufficientEncryption_c     ||
            attError == gAttErrCodeInsufficientAuthorization_c  ||
            attError == gAttErrCodeInsufficientAuthentication_c)
        {
    **\#if **gAppUsePairing_d
            /* Start Pairing Procedure */
            (void)Gap_Pair (serverDeviceId, &gPairingParameters);
    **\#endif**
        }

        BleApp_StateMachineHandler (serverDeviceId, mAppEvt_GattProcError_c);
    }
    **else if** (procedureResult == gGattProcSuccess_c)
    {
        **switch**(procedureType)
        {
            /* ... Missing code here... */
           ** case** gGattProcWriteCharacteristicValue_c:
            {
                BleApp_HandleValueWriteConfirmations (serverDeviceId);
            }
            **break;**

           ** default:**
                ; /* For MISRA compliance */
           ** break;**
        }

        BleApp_StateMachineHandler(serverDeviceId, mAppEvt_GattProcComplete_c);
    }
    **else**
    {
        ; /* For MISRA compliance */
    }
}

The BleApp_HandleValueWriteConfirmations() function deals with ATT Write Confirmations based on the requirements of the application.

There are two possible transfer methods for Image Chunks, the ATT transfer method and the L2CAP transfer method. The OTAP server is prepared to handle both, as requested by the OTAP Client.

To be able to use the L2CAP transfer method, the OTAP Server application must register a L2CAP LE PSM and 2 callbacks: a data callback and a control callback. This is done by using the BluetoothLEHost_Initialized() function.

/* Register OTAP L2CAP PSM */
  L2ca_RegisterLePsm (gOtap_L2capLePsm_c,
                      gOtapCmdImageChunkCocLength_c); /*!< The negotiated MTU must be higher than the biggest data chunk that is sent fragmented */
...
  App_RegisterLeCbCallbacks(BleApp_L2capPsmDataCallback, BleApp_L2capPsmControlCallback);

The data callback BleApp_L2capPsmDataCallback() is not used by the OTAP Server.

The control callback is used to handle L2CAP LE PSM connection requests from the OTAP Client and other events: PSM disconnections, No peer credits, and so on. The OTAP Client must initiate the L2CAP PSM connection if it wants to use the L2CAP transfer method.

**static** **void** **BleApp\_L2capPsmControlCallback**(l2capControlMessageType_t messageType,
                                                                          **void***              pMessage)
{
    **switch** (messageType)
    {
        **case** *gL2ca\_LePsmConnectRequest\_c*:
        {
            l2caLeCbConnectionRequest_t *pConnReq = ( l2caLeCbConnectionRequest_t *)pMessage;
            /* Respond to the peer L2CAP CB Connection request - send a connection response. */
            L2ca_ConnectLePsm (gOtap_L2capLePsm_c,
                               pConnReq-> deviceId,
                               mAppLeCbInitialCredits_c);
            **break**;
        }
        **case** *gL2ca\_LePsmConnectionComplete\_c*:
        {
            l2caLeCbConnectionComplete_t *pConnComplete = ( l2caLeCbConnectionComplete_t *)pMessage;
            **if** (pConnComplete->result == *gSuccessful\_c*)
            {
                /* Set the application L2CAP PSM Connection flag to TRUE because there is no gL2ca_LePsmConnectionComplete_c
                 * event on the responder of the PSM connection. */
                otapServerData. l2capPsmConnected = TRUE;
                otapServerData. l2capPsmChannelId = pConnComplete->cId;
            }
            **break**;
        }
        **case** *gL2ca\_LePsmDisconnectNotification\_c*:
        {
            l2caLeCbDisconnection_t *pCbDisconnect = ( l2caLeCbDisconnection_t *)pMessage;
            /* Call App State Machine */
            BleApp_StateMachineHandler (pCbDisconnect-> deviceId, *mAppEvt\_CbDisconnected\_c*);
            otapServerData. l2capPsmConnected = FALSE;
            **break**;
        }
        **case** *gL2ca\_NoPeerCredits\_c*:
        {
            l2caLeCbNoPeerCredits_t *pCbNoPeerCredits = ( l2caLeCbNoPeerCredits_t *)pMessage;
            L2ca_SendLeCredit (pCbNoPeerCredits-> deviceId,
                               otapServerData. l2capPsmChannelId,
                               mAppLeCbInitialCredits_c);
            **break**;
        }
        **case** *gL2ca\_LocalCreditsNotification\_c*:
        {
            l2caLeCbLocalCreditsNotification_t *pMsg = ( l2caLeCbLocalCreditsNotification_t *)pMessage;
            **break**;
        }
        **default**:
            **break**;
    }
}

The ATT transfer method is supported by default but the L2CAP transfer method only works if the OTAP Client opens an L2CAP PSM credit-oriented channel.

To send data chunks to the OTAP Client the OTAP Server application calls the OtapServer_SendCImgChunkToOtapClient() function which delivers the chunk via the selected transfer method. For the ATT transfer method the chunk is sent via the GattClient_CharacteristicWriteWithoutResponse() function and for the L2CAP transfer method the chunk is sent via the L2ca_SendLeCbData() function.

**static void OtapServer\_SendCImgChunkToOtapClient** (deviceId_t otapClientDevId,
                                                 **void***      pChunk,
                                                 uint16_t   chunkCmdLength)
{
    bleResult_t bleResult = gBleSuccess_c;
    **if** (otapServerData.transferMethod == gOtapTransferMethodAtt_c)
    {
        /* GATT Characteristic to be written without response - OTAP Client Data */
        gattCharacteristic_t otapDataChar;
        /* Only the value handle element of this structure is relevant for this operation. */
        otapDataChar.value.handle = mPeerInformation.customInfo.otapServerConfig.hData;
        bleResult = GattClient_CharacteristicWriteWithoutResponse
                                                (mPeerInformation.deviceId,
                                                 &otapDataChar,
                                                 chunkCmdLength,
                                                 pChunk);
    }
    **else if** (otapServerData.transferMethod == gOtapTransferMethodL2capCoC_c)
    {
        bleResult = L2ca_SendLeCbData (mPeerInformation.deviceId,
                                       otapServerData.l2capPsmChannelId,
                                       pChunk,
                                       chunkCmdLength);
    }
    **if** (gBleSuccess_c != bleResult)
    {
        /*! A Bluetooth Low Energy error has occurred - Disconnect */
        Gap_Disconnect (otapClientDevId);
    }
}

The OTAP Server demo application relays all commands received from the OTAP Client to a PC through the FSCI type protocol running over a serial interface. It also directly relays all responses from the PC back to the OTAP Client.

Other implementations can bring the image to an external memory through other means of communication and directly respond to the OTAP Client requests.

Parent topic:Bluetooth Low Energy OTAP application integration

OTAP client

An application running an OTAP Client must wait for an OTAP Server to connect and perform service and characteristic discovery before performing any OTAP-related operations. OTAP transactions can begin only after the OTAP Server writes the OTAP Control point CCC Descriptor to receive ATT Notifications. After this is done, bidirectional communication is established between the OTAP Server and Client and OTAP transactions can begin.

The OTAP Client can advertise the OTAP Service via the demo application. Optionally, the OTAP Server may already know the advertising device has an OTAP Service based on application-specific means. In both situations, the OTAP Server must discover the handles of the OTAP Service and its characteristics.

In addition to the OTAP Service instantiated in the GATT Database, the OTAP Client needs to have some storage capabilities for the downloaded image file.

How to put the OTAP Service in the GATT Database is described in The OTAP Service and Characteristics.

The upgrade image storage capabilities in the demo OTAP Client applications are handled by the OtaSupport module from the Framework, which contains support modules and drivers. The OtaSupport module has support for both internal storage (a part of the internal flash memory is reserved for storing the upgrade image) and external storage (a SPI flash memory chip).

The demo applications use internal storage by default. The internal storage is viable only if there is enough space in the internal flash for the upgrade image – the flash in this case should be at least twice the size of the largest application. The OtaSupport module also needs the Eeprom module from the Framework to work correctly.

The OtaSupport module also includes functionality for configuring the OTACFG IFR sections after the image is received in order to enable the ROM bootloader to perform the actual image update.

To use the OtaSupport module several configuration options must be set up in both the source files and the linker options of the toolchain.

To use internal storage, set up the gUseInternalStorageLink_d=1 symbol in the linker configuration window (Linker->Config tab in the IAR project properties) and set the gAppOtaExternalStorage_c value to (0) in the app_preinclude.hfile:

/*! Define as 1 to place OTA storage in external flash */
  #define gAppOtaExternalStorage_c (0)

The OTAP demo applications for the IAR EW IDE have some settings in the Linker options tab which must be configured to use OtaSupport and the OTAP Bootloader. In the Project Target Options->Linker->Config tab, 3 symbols must be correctly defined. To use NVM storage, thegUseNVMLink_d symbol must be set to 1. The gUseInternalStorageLink_d symbol must be set to 0 when OTAP external storage is used and to 1when the internal storage is used. The gEraseNVMLink_d must be set to 0.

An example linker configuration window for IAR is shown in Figure 1.

Note: The gEraseNVMLink_d=1 IAR linker flag places some dummy bytes into the NVM region to invalidate the data and force the application to erase the entire NVM region. When generating an image for the OTA upgrade, this flag must be set to 0. This results in a smaller image size being transferred and lower power consumption. If the NVM region must be erased after the upgrade process, the “Preserve NVM” checkbox (from the Over The Air programming tool) should be unchecked.

For MCUXpresso IDE, the linker settings required for OTAP applications can be set up from the “SDK Import Wizard” or from the “Project Properties ->MCU settings”. Refer to Figure 2.

The demo applications use internal storage by default. To enable external storage support for MCUX, set the gAppOtaExternalStorage_c value to (1) in the app_preinclude.hfile. Also remove the INT_STORAGE section (from Project Properties-> MCU settings) and extend the PROGRAM_FLASHsection as shown in the Figure 3.

Once the application starts and bidirectional OTAP communication is established via the OTAP Service, then the OTAP Client must determine if the connected OTAP Server has a newer image than the one currently present on the device. This can be done in two ways:

  • The OTAP Server knows by some application-specific means that it has a newer image and sends a New Image Notification to the OTAP Client or

  • The OTAP Client sends a New Image Info Request to the OTAP Server and waits for a response. This example application uses the second method.

The New Image Info Request contains enough information about the currently running image to allow the OTAP Server to determine if it has a newer image for the requesting device. The New Image Info Response contains enough information for the OTAP Client to determine if the “deadvertised” image is newer and it wants to download it. The best method is entirely dependent on application requirements.

An example function that checks if an ImageVerison field from a New Image Notification or a New Image Info Response corresponds to a newer image (based on the suggested format of this field) is provided in the OTAP Client demo applications. The function is called OtapClient_IsRemoteImageNewer().

The OTAP Client application is a little more complicated than the OTAP Server application because more state information needs to be handled (current image position, current chunk sequence number, image file parsing information, and so on). An example state diagram for the OTAP Client is shown below. The Figure 4 briefly lists the steps of the image download process. Note that some of the states may not be explicitly present in the demo applications.

After the OTAP Client determines that the peer OTAP Server has a suitable upgrade image available, it can start the download process. This is done by sending multiple Image Block Request messages and waiting for the Image Chunks via the selected transfer method.

While receiving the image file blocks, the OTAP Client application parses the image file. In case any parameter of an image file sub-element is invalid or the image file format is invalid, it sends an Error Notification to the OTAP Server and tries to restart the download process from the beginning or a known good position.

When an Image Chunk is received, its sequence number is checked and its content is parsed in the context of the image file format. If the sequence number is not as expected, then the block transfer is restarted from the last known good position. When all chunks of an Image Block are received, the next block is requested, if there are more blocks to download. When the last Image Block in an image file is received, then the image integrity is checked (the received CRC from the Image File CRC sub-element is compared to the computed CRC).

The computed image integrity initialization and intermediary value must be reset to ‘0’ before starting or restarting an image download. If the image integrity check fails then the image download process is restarted from the beginning. If the image integrity check is successful, then the Image Download Complete message is sent to the OTAP Server, the OTACFG IFR is updated and the MCU is restarted. After the restart, the ROM bootloader kicks in and writes the new image to the flash memory, afterwards giving CPU control to the newly installed application.

If at any time during the download process, a Link Layer disconnection occurs, then the image download process is restarted from the last known good position when the link is re-established.

As noted earlier, the OTAP Client application needs to handle a lot of state information. In the demo application, all this information is held in the otapClientData structure of the otapClientAppData_t type. The type is defined and the structure is initialized in the otap_client.c file of the application. This structure is defined and initialized differently for the OTAP Client ATT and L2CAP example applications. Mainly, the transferMethod member of the structure is constant and has different values for the two example applications and the L2CAP application structure has an extra member.

To receive write notifications when the OTAP Server writes the OTAP Control Point attribute and ATT Confirmations when it indicates the OTAP Control Point attribute, the OTAP Client application must register a GATT Server callback and enable write notifications for the OTAP Control Point attribute. This is done in the BluetoothLEHost_Initialized() function in the otap_client_att.c/otap_client_l2cap_credit.c file.

**static void BluetoothLEHost\_Initialized**(void)
{
    /* ... Missing code here ... */

    /* Register stack callbacks */
    (void)App_RegisterGattServerCallback (BleApp_GattServerCallback);

    /* ... Missing code here ... */
}

The BleApp_GattServerCallback() function handles all incoming communication from the OTAP Server.

**static void BleApp\_GattServerCallback **(deviceId_t deviceId, gattServerEvent_t* pServerEvent)
{
    **switch** (pServerEvent->eventType)
    {
        /* ... Missing code here ... */
        
        **case** gEvtCharacteristicCccdWritten_c:
        {
            OtapClient_CccdWritten (deviceId,
                                pServerEvent->eventData.charCccdWrittenEvent.handle,
                                pServerEvent->eventData.charCccdWrittenEvent.newCccd);
        }
        **break;**

       ** case** gEvtAttributeWritten_c:
        {
            OtapClient_AttributeWritten (deviceId,
                                     pServerEvent->eventData.attributeWrittenEvent.handle,
                                     pServerEvent->eventData.attributeWrittenEvent.cValueLength,
                                     pServerEvent->eventData.attributeWrittenEvent.aValue);
        }
       ** break;**

        **case** gEvtAttributeWrittenWithoutResponse_c:
        {
            OtapClient_AttributeWrittenWithoutResponse (deviceId,
                                                    pServerEvent->eventData.attributeWrittenEvent.handle,
                                                    pServerEvent->eventData.attributeWrittenEvent.cValueLength,
                                                    pServerEvent->eventData.attributeWrittenEvent.aValue);
        }
        **break;**

        **case **gEvtHandleValueConfirmation_c:
        {
            OtapClient_HandleValueConfirmation (deviceId);
        }
       ** break;**

        /* ... Missing code here ... */
        
        **default:**
            ; /* For MISRA compliance */
        **break;**
    }
}

When the OTAP Server Writes a CCCD the BleApp_GattServerCallback() function calls the OtapClient_CccdWritten() function which sends a New Image Info Request when the OTAP Control Point CCCD is written it – this is the starting point of OTAP transactions in the demo applications.

When an ATT Write Request is made by the OTAP Server the the BleApp_GattServerCallback() function calls the OtapClient_AttributeWritten() function which handles the data as an OTAP command. Only writes to the OTAP Control Point are handled as OTAP commands. For each command received from the OTAP Server there is a separate handler function which performs required OTAP operations. These are:

  • OtapClient_HandleNewImageNotification()

  • OtapClient_HandleNewImageInfoResponse()

  • OtapClient_HandleErrorNotification()

When an ATT Write Command (GATT Write Without Response) is sent by the OTAP Server the BleApp_GattServerCallback() function calls the OtapClient_AttributeWrittenWithoutResponse() function which handles Data Chunks if the selected transfer method is ATT and returns an error if any problems are encountered. Data chunks are handled by the OtapClient_HandleDataChunk() function.

**static void BleApp\_AttributeWrittenWithoutResponse** (deviceId_t deviceId,
                                                                     uint16_t handle,
                                                                     uint16_t length,
                                                                     uint8_t* pValue)
{
    /* ... Missing code here ... */
    **if** (handle == value_otap_data)
    {
        /* ... Missing code here ... */
        **if** (otapClientData.transferMethod == gOtapTransferMethodAtt_c)
        {
            **if** (((otapCommand_t*)pValue)->cmdId == gOtapCmdIdImageChunk_c)
            {
                OtapClient_HandleDataChunk (deviceId,
                                            length,
                                            pValue);
            }
        }
        /* ... Missing code here ... */
    }
    /* ... Missing code here ... */
}

Finally, when an ATT Confirmation is received for a previously sent ATT Indication the BleApp_GattServerCallback() function calls the OtapClient_HandleValueConfirmation() function, which performs the necessary OTAP operations based on the last sent command to the OTAP Server. This is done using separate confirmation handling functions for each command that is sent to the OTAP Server. These functions are:

  • OtapClient_HandleNewImageInfoRequestConfirmation()

  • OtapClient_HandleImageBlockRequestConfirmation()

  • OtapClient_HandleImageTransferCompleteConfirmation()

  • OtapClient_HandleErrorNotificationConfirmation()

  • OtapClient_HandleStopImageTransferConfirmation()

Outgoing communication from the OTAP Client to the OTAP Server is done using the OtapCS_SendCommandToOtapServer() function. This function writes the value to be indicated to the OTAP Control Point attribute in the GATT database and then calls the *OtapCS_SendControlPointIndication()*which checks if indications are enabled for the target device and sends the actual ATT Indication. Both functions are implemented in the otap_service.c file.

bleResult_t **OtapCS\_SendCommandToOtapServer** (uint16_t serviceHandle,
                                            void* pCommand,
                                            uint16_t cmdLength)
{
    **union**
    {
        uint8_t*                uuid_char_otap_control_pointTemp;
        bleUuid_t*              bleUuidTemp;
    }bleUuidVars;

    uint16_t  handle;
    bleResult_t result;
    bleUuidVars.uuid_char_otap_control_pointTemp = uuid_char_otap_control_point;
    bleUuid_t* pUuid = bleUuidVars.bleUuidTemp;

    /* Get handle of OTAP Control Point characteristic */
    result = GattDb_FindCharValueHandleInService(serviceHandle,
                                                 gBleUuidType128_c, pUuid, &handle);

    **if** (result == gBleSuccess_c)
    {
        /* Write characteristic value */
        result = GattDb_WriteAttribute(handle,
                                       cmdLength,
                                       (uint8_t*)pCommand);

       ** if** (result == gBleSuccess_c)
        {
            /* Send Command to the OTAP Server via ATT Indication */
            result = OtapCS_SendControlPointIndication (handle);
        }
    }

   ** return** result;
}

**static** bleResult_t **OtapCS\_SendControlPointIndication** (uint16_t handle)
{
    uint16_t     hCccd;
    bool_t       isIndicationActive;
    /* Get handle of CCCD */
    GattDb_FindCccdHandleForCharValueHandle (handle, &hCccd);
    Gap_CheckIndicationStatus (...);
    **return** GattServer_SendIndication (...);
}

The otap_interface.h file contains all the necessary information for parsing and building OTAP commands (packed command structures type definitions, command parameters enumerations, and so on).

For the two possible image transfer methods (ATT and L2CAP) there are two separate demo applications. To be able to use the L2CAP transfer method the OATP Client application must register a L2CAP LE PSM and 2 callbacks: a data callback and a control callback. This is done in the OtapClient_Config() function.

/* Register OTAP L2CAP PSM */
L2ca_RegisterLePsm (gOtap_L2capLePsm_c,
gOtapCmdImageChunkCocLength_c); /*!< The negotiated MTU must be higher than the biggest data chunk that is sent fragmented */
...
App_RegisterLeCbCallbacks(BleApp_L2capPsmDataCallback, BleApp_L2capPsmControlCallback);

The control callback is used to handle L2CAP LE PSM-related events: PSM disconnections, PSM Connection Complete, No peer credits, and so on.

**static void BleApp\_L2capPsmControlCallback**
             (l2capControlMessageType_t   messageType,
              **void***   pMessage)
{
    **switch** (messageType)
    {
        **case** **gL2ca\_LePsmConnectRequest\_c:**
        {
            l2caLeCbConnectionRequest_t *pConnReq =
                            (l2caLeCbConnectionRequest_t *)pMessage;
            /* This message is unexpected on the OTAP Client, the OTAP Client sends L2CAP
             * PSM connection requests and expects L2CAP PSM connection responses.
             * Disconnect the peer. */
            Gap_Disconnect (pConnReq->deviceId);
            **break**;
        }
        **case** **gL2ca\_LePsmConnectionComplete\_c:**
        {
            l2caLeCbConnectionComplete_t *pConnComplete =
                             (l2caLeCbConnectionComplete_t *)pMessage;
            /* Call the application PSM connection complete handler. */
            OtapClient_HandlePsmConnectionComplete (pConnComplete);
        **break**;
    }
    **case** **gL2ca\_LePsmDisconnectNotification\_c:**
    {
        l2caLeCbDisconnection_t *pCbDisconnect = (l2caLeCbDisconnection_t *)pMessage;
        /* Call the application PSM disconnection handler. */
        OtapClient_HandlePsmDisconnection (pCbDisconnect);
        **break**;
    }
    **case** **gL2ca\_NoPeerCredits\_c:**
    {
        l2caLeCbNoPeerCredits_t *pCbNoPeerCredits =
                        (l2caLeCbNoPeerCredits_t *)pMessage;
        L2ca_SendLeCredit (pCbNoPeerCredits->deviceId,
                           otapClientData.l2capPsmChannelId,
                           mAppLeCbInitialCredits_c);
        **break**;
    }
   /* ... Missing code here ... */
    **case** **gL2ca\_Error\_c:**
        {
            /* Handle error */
            break;
        }
        **default:**
            ; /* For MISRA compliance */
       ** break;**
}

The OTAP Client must initiate the L2CAP PSM connection if it wants to use the L2CAP transfer method; this can be done using the L2ca_ConnectLePsm() function. The L2ca_ConnectLePsm() function is called by the OtapClient_ContinueImageDownload() if the transfer method is L2CAP and the PSM is found to be disconnected.

**static void OtapClient\_ContinueImageDownload** (deviceId_t deviceId)
{
    /* ... Missing code here ... */
    /* Check if the L2CAP OTAP PSM is connected and if not try to connect and exit immediately. */
    **if** ((otapClientData.l2capPsmConnected == FALSE) &&
                (otapClientData.state != mOtapClientStateImageDownloadComplete_c))
    {
        L2ca_ConnectLePsm (gOtap_L2capLePsm_c,
                           deviceId,
                           mAppLeCbInitialCredits_c);
        **bValidState = FALSE;**;
    }
    /* ... Missing code here ... */
}

The PSM data callback BleApp_L2capPsmDataCallback() is used by the OTAP Client to handle incoming image file parts from the OTAP Server.

**static void BleApp\_L2capPsmDataCallback** (deviceId_t  deviceId,
   uint8_t*    pPacket,
   uint16_t    uint16_t lePsm,
   uint16_t    packetLengt
{
   OtapClient_HandleDataChunk (deviceId,
                               packetLength,
                               pPacket);
}

All data chunks regardless of their source (ATT or L2CAP) are handled by the OtapClient_HandleDataChunk() function. This function checks the validity of Image Chunk messages, parses the image file, requests the continuation or restart of the image download and triggers the bootloader when the image download is complete.

**static void OtapClient\_HandleDataChunk** (deviceId_t deviceId, uint16_t length, uint8_t* pData);

The Image File CRC Value is computed on the fly as the image chunks are received using the OTA_CrcCompute() function from the OtaSupport module which is called by the OtapClient_HandleDataChunk() function. The OTA_CrcCompute() function has a parameter for the intermediary CRC value which must be initialized to 0 every time a new image download is started.

The actual write of the received image parts to the storage medium is also done in the OtapClient_HandleDataChunk() function using the OtaSupport module. This is achieved using the following functions:

  • OTA_StartImage() – called before the start of writing a new image to the storage medium.

  • OTA_CancelImage() – called whenever an error occurs and the image download process needs to be stopped/restarted from the beginning.

  • OTA_PushImageChunk() – called to write a received image chunk to the storage medium. Note that only the Upgrade Image Sub-element of the image file is actually written to the storage medium.

  • OTA_CommitImage() - called to set up what parts of the downloaded image are written to flash and other information for the bootloader. The Value field of the Sector Bitmap Sub-element of the Image File is given as a parameter to this function.

  • OTA_SetNewImageFlag() - called to configure the OTACFG IFR when a new image has been successfully received. When the MCU is reset, the ROM bootloader transfers the new image from the storage medium to the program flash.

To continue the image download process after a block is transferred or to restart it after an error has occurred the OtapClient_ContinueImageDownload() function is called. This function is used in multiple situations during the image download process.

To summarize, an outline of the steps required to perform the image download process is shown below:

  • Wait for a connection from an OTAP Server

  • Wait for the OTAP Server to write the OTAP Control Point CCCD

  • Ask or wait for image information from the server

  • If a new image is available on the server, start the download process using the OtapClient_ContinueImageDownload() function.

    • If the transfer method is L2CAP CoC, then initiate a PSM connection to the OTAP Server

  • Repeat while image download is not complete.

    • Wait for image chunks.

    • Call the OtapClient_HandleDataChunk() function for all received image chunks regardless of the selected transfer method.

      • Check image file header integrity using the OtapClient_IsImageFileHeaderValid() function.

      • Write the Upgrade Image Sub-element to the storage medium using OtaSupport module functions.

      • When the download is complete, check image integrity.

        • If the integrity check is successful, commit the image using the Sector Bitmap Sub-element and trigger the bootloader

        • If integrity check fails, restart the image download from the beginning

      • If the download is not complete, ask for a new image chunk.

    • If any error occurs during the processing of the image chunk, restart the download from the last known good position.

  • If an image was successfully downloaded and transferred to the storage medium and the bootloader triggered, then reset the MCU to start the flashing process of the new image.

Parent topic:Bluetooth Low Energy OTAP application integration

Parent topic:Over the Air Programming (OTAP)

Secured OTAP

The security features of the KW45/K32W1 devices enable them to use secured OTAP, meaning the new images can be authenticated and encrypted. The decryption/authentication keys are programmed into hardware fuses. For information on how to prepare the board, refer to the accompanying document related to board provisioning. For information on how to obtain the secured image, refer to the Bluetooth Low Energy Demo Applications User’s Guide (BLEDAUG).

Parent topic:Over the Air Programming (OTAP)