Introduction

This workshop offers a first experience in the data_stream example project. The purpose is to have a better understanding of the device's functionalities. Here it will dive into echoing a value entered by the user and plenty of features around this principle. This project is the perfect entrance for UART over Bluetooth® LE. It can also easily handle data transfer and can be used for data streaming.

Prerequisites

Hardware

  • SimpleLink™ CC2340RX LaunchPad™ Target
  • SimpleLink™ Modular LaunchPad™ Emulator
  • USB cable
  • SmartPhone or Tablet with a GATT Table viewer application installed (such as TI SimpleLink Connect app)

Note

Here are links to the TI SimpleLink Connect app

Software

Readings

  • SDK User's Guide: PATH = C:/ti/simplelink_lowpower_f3_sdk_x_xx_xx_xx_eng/docs/ble5stack/ble_user_guide/ html/ble-stack-5.x-guide/ble-stack-5-index-cc23xx.html.

    You should particularly have a look on the Generic Access Profile (GAP) and Generic Attribute Profile (GATT) sections, it will help you to understand a bit more about the theory behind Bluetooth™ Low Energy protocol.

Agenda

In this training, we will discuss how to prepare the data_stream example for custom development.

1. Discuss how to set up the environment and how it works

2. A brief explanation of the GATT concepts

3. Working principle of the project

4. How to change basic device configurations

5. Some help to customize your project

Preparation

Initial Setup

If your system is already setup skip this part and go directly to the first connection using GATT table.

All the collaterals are available on TI website.

  1. Download and Install the SDK found in the software section above.

  2. Follow the Quick Start Guide found inside the BLE5 Stack User Guide

PATH = C:/ti/simplelink_lowpower_f3_sdk_x_xx_xx_xx_eng/docs/ble5stack/ble_user_guide/html/quickstart-guide/quickstart-intro-cc23xx.html.

Setting up the Serial terminal

  1. Identify the COM port used by the SimpleLink™ CC2340RX LaunchPad™.

    • Disconnect all the LaunchPads™ you may have connected to your computer leaving only the SimpleLink™ CC2340RX LaunchPad™.
    • Open the Device Manager. You can do so by typing Device Manager in the Windows start menu.
    • In the Device manager, find the menu Ports (COM & LPT) and unroll it.
    • Observe the line called XDS110 Class Application/User UART. Note down the COM port this interface is using. In my case (see below), the COM port used is called COM27.

  2. Open a serial terminal to observe the COM port identified previously. For this example, I'll show how to use the serial port provided by CCS, but you can use your favorite serial terminal provider.

    • On CSS, click on ViewTerminal, or use the shortcut Ctrl+Alt+Shift+T.
    • A pop-up allowing you to configure the terminal will appear. Select the following settings:
      • Choose Terminal: Serial Terminal
      • Serial Port: The serial port identified before (COM27 for me)
      • Baud rate: 115200
      • Data size: 8
      • Parity: None
      • Stop bits: 1
      • Encoding: Default (ISO-8859-1)
    • Once done, click OK.
    • Display the Command Input Field by clicking on Toggle Command Input Field (see label 1), then clear the terminal content by clicking on Clear Terminal (see label 2)
    • To finish, reset the SimpleLink™ CC2340RX LaunchPad™ by pressing the reset button on the emulator.

Flash the CC2340 with the unmodified code

At this point open CCS, create a new Workspace (don't forget to add the workspace variable FREERTOS_INSTALL_DIR) and import the data_stream project. Then build and flash the code on the device. If you need help for this, make sure to review Quick Start Guide!

Once the LaunchPad is ready, the green LED (labeled Green DIO15) turns on.

To test the Bluetooth LE communication, a connection should be formed between a central device and the CC2340RX running the data_stream example. An easy way to do this is by using a Smartphone or Tablet device with a GATT Table viewer application. With one of these applications, one should be able to scan and connect to the device as well as interact with the characteristics present in the project.

More details on the way of testing the Bluetooth communication between the CC2340 and a smartphone are provided in the next sections.

Introduction of the GATT concepts

This section will provide a quick introduction of the GATT the concepts you should know to handle properly this example. Feel free to skip this section if you are already familiar with these concepts.

Attributes and Characteristics

An Attribute is the smallest addressable unit of data used by ATT and GATT. It is addressed via a 16-bit handle, it has a type, which is an UUID that can be 16, 32 or 128 bits long, and it has a data field which can be up to 512 bytes long. Note that all 32-bit UUIDs should be converted to 128-bit before sending over the ATT protocol.

Summarized, the Attribute Protocol defines an attribute to consist of

  • Handle – The 'address' of the Attribute when accessed via the Attribute Protocol
  • UUID – The 'type' of the Attribute
  • Value – Array of bytes interpreted differently depending on the UUID (type).
Handle UUID Value
16 bits 16 or 128 bits 1 to 512 bytes.

Several of these attributes are needed to define a Characteristic. A Characteristic always consists of at least a Value attribute and a Declaration attribute. The meaning of the Value attribute is defined in the Profile. The Declaration always comes before the value attribute and describes whether the value attribute can be read or written, contains the UUID of the Characteristic and the handle of the Characteristic Value's attribute.

Other attributes of a characteristic can give a description of the characteristic in string format, or describe how the Value should be interpreted, or configure whether the GATT Server may send Notifications about value changes. These are called Descriptors.

Handle

In the TI BLE5-Stack, unique handles are assigned starting from 1 when the attribute is registered with the GATT Server. This usually happens during initialization. If the firmware doesn't change, the handle is always the same.

UUID

The UUID tells a peer device how the value of the Attribute should be interpreted. For example, 0x2803 is defined in the specification to mean Characteristic Declaration. Multiple attributes may share the same UUID so handles are needed to identify a particular attribute instance.

Warning

Proprietary (non-SIG-adopted) services must use 128-bit UUIDs to avoid collision, unless a 16- or 32-bit UUID is acquired from the SIG.

First connection using GATT table

Data_stream Operational Principles

The following steps will showcase how to connect, write and receive a value by using the TI SimpleLink Connect application on Android OS. Note that these steps are almost the same for iOS smartphone.

  1. Scan and locate the Data Stream device

    ScanningProject

    Find the right device

    Add a filter on the proximity of the device by pressing the settings menu (three dots on the top right-hand corner) or swapping to the right. Then activate the RSSI filter and update its value.

    This feature will help you to filter out any faraway devices and make it easier to find the data_stream device in a crowded BLE environment.

    FilterProject

  2. Connect to the Data Stream device by tapping on the arrow and expand the TI Terminal tab which has the UUID 0xF000C0C0-0451-4000-B000-000000000000

    Remark

    A common practice is to choose a base UUID for the custom service and then increment the 3rd and 4th Most Significant Bytes (MSB) within the UUID of each included characteristic. Here the 3rd and 4th MSB is set to 0xC0C0 for the base UUID. The characteristic UUIDs are then formed by incrementing 0xC0C0. To improve the readability of this lab, we may only mention the 3rd and 4th MSB of the UUIDs.

    TITerminal

  3. First, on the characteristic Server Data which has the UUID 0xC0C2, turn the switch on to enable Bluetooth Low Energy notification. This button will turn green after the notifications have been enabled.

    NotifyinGATT

  4. Then, on Characteristic Write Data which has the UUID 0xC0C1, press the Write button to write the string value to echo. Note to change the format in the combobox. To write an ASCII string you have to change it to UTF-8. A string should be written and the OK button should be pressed. This will write the data to the characteristic.

    UTF8Writting

    At this point the green led should be turn off and the red one turn on, to indicate that the data has been received.

  5. The value pop in the Server Data characteristic section as an echo of the Write Data characteristic. The response is sensitive case, a message in capital letters will be in small letters in response and vice versa. Echo

Remarks

If the string value exceed 20 characters, a second request is sent and two packets are visible in the notifications field. Thus, the led toggle twice.

To summarize, when we write to the characteristic, the SmartPhone device sends the data via Bluetooth LE to the SimpleLink™ CC2340RX LaunchPad™ which receives it, processes it, packages it, and sends it back to the SmartPhone to the other characteristic. Thus, data are transmitted both ways, from the SmartPhone to LaunchPad and vice-versa.

Changing device configurations

This section describes how to customize your device configurations:

  • Change the Data Stream Service and Characteristics UUID - This is useful when the central expects specific UUIDs or you want to deploy the system with your own UUIDs.

Change UUIDs

This section will touch upon UUIDs and how to change them for your project.

Currently our Characteristic UUIDs for Data_stream Service, In Characteristic and Out Characteristic are as the following picture shows:

PreviousUUID

Remarks

In this guide, we'll consider this point of view for the inputs/outputs for the TI_BASE_UUID_128:

Data Stream Server Service UUID: 0xC0C0

Data In characteristic UUID: 0xC0C1

Data Out characteristic UUID: 0xC0C2

To change that we need first to modify the advertising UUID:

  1. Open the .syscfg file inside the app folder of the data_stream project. Expand RF Stacks SectionBLEBroadcaster ConfigurationAdvertisement Set 1Advertisement Data 1UUID 0 (128-bit More) and change by an other UUID

    for this example

     0x9ECADC240EE5A9E093F3A3B50100406E
    

    newAdvUUID

  2. Then we need to do a copy of the data_stream_server.c file, as for the moment this file is a Linked File. Thus, to avoid to change all your future projects, as it will directly impact the roots of all your projects, we need to create a local copy of this file. To do so:

    • First, write down the location of the file by memorize its path:
      • Open data_stream_server.c located in commonServicesdata_stream in your file project.
      • Right click to the file → PropertiesResolved location, here is the path of your file.
    • Delete the file
    • Drag and drop the data_stream_server.c file from your File Explorer to the exact same location you deleted it once step before, but tick the copy option.

    At this point you should see in the properties of your file that the Type of your file is no longer a Linked File type.

  3. Then we will change the UUID base, open data_stream_server.c, at the beginning of the file add the following code in the CONSTANTS section and modify it with the UUID base we want for this example.

     /// @brief Customer Base 128-bit UUID: 6E40XXXX-B5A3-F393-E0A9-E50E24DCCA9E
     #define CUSTOMER_BASE_UUID_128( uuid )  0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, \
                                       0x93, 0xF3, 0xA3, 0xB5, LO_UINT16( uuid ), HI_UINT16( uuid ), \
                                       0x40, 0x6E 
     /// @brief GATT Custom UUID
     #define GATT_UUID_C(name, UUID) CONST uint8 name[ATT_UUID_SIZE] =\
     {\
       CUSTOMER_BASE_UUID_128(UUID)\
     }
    

    Content to add to the CONSTANTS section of the data_stream_server.c file

  4. Modify the LOCAL VARIABLES section with your GATT Custom new value by adding the _C characters at the end of the data name: GATT_UUID

     // Data Stream Server Service UUID: 0x0001
     GATT_UUID_C( dss_serv_UUID, DSS_SERV_UUID );
    
     // Data In Characteristic UUID: 0x0002
     GATT_UUID_C( dss_dataIn_UUID, DSS_DATAIN_UUID );
    
     // Data Out Characteristic UUID: 0x0003
     GATT_UUID_C( dss_dataOut_UUID, DSS_DATAOUT_UUID );
    

    Content to modify to the LOCAL VARIABLES section of the data_stream_server.c file

  5. To change the different UUID, create a copy of the data_stream_server.h file as the same way as we did on step 2 with the data_stream_server.c file.

    The data_stream_server.h file is located in commonServicesdata_stream in the project explorer.

    At the beggining of the file change the #defines: DSS_SERV_UUID DSS_DATAIN_UUID DSS_DATAOUT_UUID

     /*********************************************************************
     * CONSTANTS
     */
     // Service UUID
     #define DSS_SERV_UUID 0x0001
    
     // Characteristic defines
     #define DSS_DATAIN_ID   0
     #define DSS_DATAIN_UUID 0x0002
    
     // Characteristic defines
     #define DSS_DATAOUT_ID   1
     #define DSS_DATAOUT_UUID 0x0003
    

    Make sure to change the three lines with the right UUID part of the data_stream_server.h file

    Note

    Don't forget to change the include of the data_stream_server.h, it needs to point on your local copy.

     //#include <ti/bleapp/services/data_stream/data_stream_server.h>
     #include "data_stream_server.h" // change with this line
    

    change this code in the INCLUDES section of the data_strea_server.c file

Test your changes

At this point you can build and flash the code on the device.

newAdvUUID

You should have a GATT table similar to the one above.

Remarks

If the GATT table still unchanged, you have to update the GATT Table by disabling-enabling the Bluetooth LE connection. If it still doesn't work properly, reinitialize the GATT information cached by erasing the pairing connection, go to your device settings to dissociate it from the SimpleLink™ CC2340RX LaunchPad™

Customize the project

After this section you will be able to use few features of the device such as using hardware to send data to your phone by different ways.

Handle LEDs

First we need to know how to set up LED. Open data_stream.syscfgTI DRIVERSGPIO. GLedConfig

For this example, you should see 6 different GPIOs set up, you can choose the exact software name for each led, here for the green led we have choose CONFIG_GPIO_LED_GREEN. Then you have to select bunch of parameters, pay attention to attach the right hardware device. You could find below several functions to interact with this led: CONFIG_GPIO_LED_GREEN.

  GPIO_write(CONFIG_GPIO_LED_GREEN, CONFIG_LED_ON); // turn on the LED
  GPIO_write(CONFIG_GPIO_LED_GREEN, CONFIG_LED_OFF); // turn off the LED
  GPIO_toggle(CONFIG_GPIO_LED_GREEN); // toggle the LED

Handle an LED

Turn on both LED when receiving the expected keyword

Here the goal is to turn on both led when the peripheral received a specific value from the writing characteristic. To do so, we need to analyze the content of the buffer and compare it to an expected keyword. Open the app_data_stream.c file, located in appProfiles in the data_stream project. Find the DataStream_incomingDataCB( DataStream_dataIn_t *dataIn ) function at the middle of the file.

First, turn off the led at start and disable the toggle when receiving data. To do so, comment the GPIO_write() functions in the DataStream_start() function in the app_data_stream.c file.

  bStatus_t DataStream_start( void )
  {
    bStatus_t status = SUCCESS;

    status = DSP_start( &ds_profileCB );
    if( status != SUCCESS )
    {
      // Return status value
      return status;
    }
    // Set LEDs
    //GPIO_write( CONFIG_GPIO_LED_RED, CONFIG_LED_OFF );
    //GPIO_write( CONFIG_GPIO_LED_GREEN, CONFIG_LED_ON );

Comment the last two lines

To disable the toggle, comment the GPIO_toggle() functions in the DS_incomingDataCB() function in the app_data_stream.c file.

  // Clear lines
    MenuModule_clearLines(APP_MENU_PROFILE_STATUS_LINE1, APP_MENU_PROFILE_STATUS_LINE3);

    // Toggle LEDs to indicate that data was received
    //GPIO_toggle( CONFIG_GPIO_LED_RED );
    //GPIO_toggle( CONFIG_GPIO_LED_GREEN );

Comment the last two lines

Add this code just before the change upper case to lower case and lower case to upper case section. This code will turn on both LEDs when the expected word is entered and turn off when is not.

  if(len == (sizeof(keyWord) - 1))
  {
    if(!strncmp((const char*)pValue, (const char*)keyWord, len))
    {
      GPIO_write(CONFIG_GPIO_LED_RED, CONFIG_LED_ON);
      GPIO_write(CONFIG_GPIO_LED_GREEN, CONFIG_LED_ON);
    }
  }else
  {
    GPIO_write(CONFIG_GPIO_LED_RED, CONFIG_LED_OFF);
    GPIO_write(CONFIG_GPIO_LED_GREEN, CONFIG_LED_OFF);
  }

Code to add before the changing case section in the DS_incomingDataCB() function of the app_data_stream.c file

Don't forget to add your expected word at the beginning of the function, here we are waiting for the word "red".

  static void DS_incomingDataCB( uint16 connHandle, char *pValue, uint16 len )
  {
  bStatus_t status = SUCCESS;
  char dataOut[] = "Data size is too long";
  uint16 i = 0;
  uint8_t keyWord[] = "red";//add this line with your word

Initialization of the expected word in the app_data_stream.c file

Test your changes

At this point you can build and flash the code on the device.

Warning

The keyword is case sensitive !

Send Data to the Phone when pressing a button

This section suggests new ways to send data to the central device. For this lab, the left button of the SimpleLink™ CC2340RX LaunchPad™ is used but you could consider using any other peripheral (GPIO, UART, I2C, SPI, etc.). This section goes deeper in the code than the previous ones and takes about 30 minutes.

Enable a task to handle the button


A new FreeRTOS task will be created and the button configured to trigger an interruption.

Merge the empty example into data_stream

First you need to merge the empty example into the data_stream one.

  1. Prepare the environment

    Import the empty project example, making sure to select the same RTOS and Toolchain as for the data_stream project example. The empty project located at: C:\ti\simplelink_cc23xx_sdk_x_xx_xx_xx\examples\rtos\LP_EM_CC2340R5\drivers\empty

    Note

    All the code modifications will be done in the data_stream project example.

  2. Copy the empty.c file

    • empty.cis available at the root of the empty project example

    EmptyFile

    • Paste the file into the data_stream project → appempty.c

    CopyEmpty

  3. Copy the code used for task creation

    • In order to keep the main() function in main_freertos.c clear, all task creations are deferred to other files. The same will be done for our new task.
    • In the newly created empty.c (in the data_stream project), add the function emptyMain. This function can be placed at the end of the file.
    • Copy-paste into this function the code used in the empty project example to create the task. This code is located in the file main_freertos.c. Don't forget to add all the includes, variable declarations and other global symbols to your file.
    • Make to ensure the BLEStack is always running with the highest priority. To do so, lower the priority assigned to the newly created function.

         /* POSIX Header files */
         #include <pthread.h>
      
         /* RTOS header files */
         #include <FreeRTOS.h>
         #include <task.h>
      
         /* Stack size in bytes */
         #define THREADSTACKSIZE 1024
      

      Header Files and Defines

         void emptyMain(void)
         {
           pthread_t thread;
           pthread_attr_t attrs;
           struct sched_param priParam;
           int retc;
      
           /* Initialize the attributes structure with default values */
           pthread_attr_init(&attrs);
      
           /* Set priority, detach state, and stack size attributes */
           priParam.sched_priority = 5; // Lower the priority of this task
           retc = pthread_attr_setschedparam(&attrs, &priParam);
           retc |= pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);
           retc |= pthread_attr_setstacksize(&attrs, THREADSTACKSIZE);
           if (retc != 0)
           {
             /* failed to set attributes */
             while (1) {}
           }
      
           retc = pthread_create(&thread, &attrs, mainThread, NULL);
           if (retc != 0)
           {
             /* pthread_create() failed */
             while (1) {}
           }
         }
      

      Code to add in empty.cfile inside the data_stream project. The emptyMain function handles task creation.

  4. Call the newly added code from the main() function in main_freertos.c

    • main_freertos.cis available at the root of the data_stream project example

    • Declare the emptyMain function as an extern function in main_freertos.c

         /*******************************************************************************
           * EXTERNS
           */
         extern void appMain(void);
         extern void AssertHandler(uint8 assertCause, uint8 assertSubcause);
         extern void emptyMain(void); // Add this!
      

      The emptyMain function is declared as an extern function

    • Call the emptyMain function right before turning on the RTOS

       /* Initialize all applications tasks */
       appMain();
       emptyMain(); // Add this!
      
       /* Start the FreeRTOS scheduler */
         vTaskStartScheduler();
      

      emptyMain() is called right before turning on the RTOS

Merging done

At this point we have an empty task, now we can use it.

Configure the button

First, disable the hardware attached to CONFIG_BUTTON_0. Open the .syscfg file of the data_stream project, in TI DRIVER APPSButton click on CONFIG_BUTTON_0 and change the value in the Use Hardware field by None. See the next image for reference.

LButtonDisable

Note

These previous steps disable the hardware left button for the data_stream menu.

Now, configure a new GPIO for our task. In the same file go to TI DriversGPIO click on the + icon to add a GPIO. Then configure the new GPIO as shown on the image below. LButtonConfig

Add the trigger function LeftButtonPressed() at the end of the empty.c file.

  /*********************************************************************
  * @fn      LeftButtonPressed
  *
  * @brief   Send to notify the value only if the left button has been pressed
  *
  * @param   index
  *
  * @return  None.
  */
  void LeftButtonPressed(uint_least8_t index)
  {
    sem_post(&sem);
  }

Add this code at the end of the empty.c file

In the previous code section we have developped the function triggered when the left button is pressed. In this function you could see the function sem_post() which activate the semaphore when the button has been pressed.

Open the app_peripheral.c file located in the app folder. In the Peripheral_GAPConnEventHandler() function enables the button when the BLEAPPUTIL_LINK_ESTABLISHED_EVENT event is caught, see the code below.

  void Peripheral_GAPConnEventHandler(uint32 event, BLEAppUtil_msgHdr_t *pMsgData)
  {
    switch(event)
    {
      case BLEAPPUTIL_LINK_ESTABLISHED_EVENT:
      {
        //Enable the Left Button
        GPIO_enableInt(CONFIG_GPIO_BUTTON_LEFT);//add this line !

code to add in the app_peripheral.c file

Disable it in the same Peripheral_GAPConnEventHandler() function, but when the BLEAPPUTIL_LINK_TERMINATED_EVENT event is called.

  case BLEAPPUTIL_LINK_TERMINATED_EVENT:
  {
    GPIO_disableInt(CONFIG_GPIO_BUTTON_LEFT); // add this line
    BLEAppUtil_advStart(peripheralAdvHandle_1, &advSetStartParamsSet_1);
    break;
  }

add this code in the app_peripheral.c file, don't forget to add the header #include < ti/drivers/GPIO.h> at the beginning

Implement the thread

Now the most important part is to implement all these elements in the mainThread() function. Pay attention to place the BLEAppUtil_stackRegister() function at the very beginning of the mainThread, this function register callback in the ICALL for application events. Also, we create the semaphore, used in the trigger button function, with sem_init(). Then we call the function sem_wait() in order to wait if the button is triggered. Once it is thanks to the sem_post() function, the code can proceed and finally send the data with the DSP_sendData() function.

Find the mainThread code below:

    /*======== mainThread ========*/
    void *mainThread(void *arg0)
    {
      BLEAppUtil_stackRegister();
      int32_t semStatus;
      /* Create semaphore */
      semStatus = sem_init(&sem, 0, 0);
      if (semStatus != 0)
      {
        /* Error creating semaphore */
        while (1) {}
      }
      while (1)
      {
        /* Do not write until read callback executes */
        sem_wait(&sem);
        //Reset the toggle mode when both are ON
        if (GPIO_read(CONFIG_GPIO_LED_RED) == 1 && GPIO_read(CONFIG_GPIO_LED_GREEN) == 1)
        {
          GPIO_toggle( CONFIG_GPIO_LED_GREEN );
        }
        GPIO_toggle( CONFIG_GPIO_LED_RED );
        DSP_sendData(buttonWord,sizeof(buttonWord));
        GPIO_toggle( CONFIG_GPIO_LED_GREEN );
      }
    }

Code of the mainThread() function in the empty.cfile

Make sure to declare all the right headers and differents using functions and variables before the mainTread function in the empty.cfile.

    /*Bcomdef and semaphore include*/
    #include <semaphore.h>
    #include "bcomdef.h"

    /* External Function */
    extern bStatus_t DSP_sendData( uint8 *pValue, uint16 len );
    extern void BLEAppUtil_stackRegister();

    /* Local Function */
    void LeftButtonPressed(uint_least8_t index);

    /*Local Variable*/
    static sem_t sem;
    uint8 buttonWord[] = "LEFT";

To summarize, this section allows you to send data using the notify characteristic when you are pressing a button on the device. We're using semaphores when the function LeftButtonPressed() is triggered, it will send a string value to the characteristic with the DSP_sendData() function. With this example, once the button is pressed the content of buttonWord which contains the value LEFT is sending and both LEDs, green and red toggle to indicate that the data is received.

Test your changes

At this point you can build and flash the code on the device.

You should have a result similar to the one below, make sure to enable the notify by switching on the notification button. The value in the notify characteristic should be LEFT without entering anything in the writing characteristic.

Note

Pay attention to select UTF-8 in the combo box, otherwhise the value will be in hexadecimal unit.

LButtonSmartphoneResult

Congratulations!

You have completed this lab 🏆

References

SimpleLink™ CC2340RX LaunchPad™

SIMPLELINK-LOWPOWER-F3-SDK

CCS

TI website

TI SimpleLink Connect on Apple App Store

TI SimpleLink Connect on Google App Store

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.