Introduction

In this workshop we will construct a simple application that continually reads an ADC channel and toggles an LED on or off depending on whether or not our ADC reading exceeds a custom threshold. We will create a simple PC-side graphical user interface (GUI) that complements our application code.

To do this, we will use a nifty tool called GUI Composer. GUI Composer is a browser-based utility for creating PC-side graphical interfaces for interacting with your hardware. GUI Composer supports several interfaces today, including Serial/UART, JTAG/XDS, or MQTT (for IoT applications).

For this demo, we will use the JTAG/XDS interface. We will visualize our latest ADC readings in an analog gauge & will add a horizontal slider for dynamically setting the threshold that triggers the LED to turn on/off.

Here's what we'll learn:

  • Get introduced to TI's GUI Composer
  • Create a simple analog gauge for visualizing ADC values
  • Create a horizontal slider to dynamically set a threshold
  • Add an "LED" indicator to the GUI that mirrors the physical LED on our LaunchPad kit

Preview of the GUI we're making:

What is GUI Composer

GUI Composer is a browser-based "What you see is what you get" (WYSIWYG) tool for developing PC-side HTML-based graphical user interfaces (GUIs) that can complement your embedded project/application. With this tool users will be able to drag & drop various GUI elements into a sandbox to build their interface. These tools include gauges, dials, sliders, line charts & more.

Features:

  • Allows you to build HTML-based GUIs graphically (simple drag & drop interface)
  • Supports communication with the target device via USB (serial I/O), XDS Debug Port or the Internet (MQTT / IoT)
  • Wide variety of components to choose from, including graphs, gauges, dials, buttons, menus, meters, and many more
  • Components configured via easy-to-use properties
  • Full JavaScript editor provided for advanced users

What interfaces does GUI Composer support?

GUI Composer supports various data transport interfaces. The following are supported:

  • Serial/UART
  • JTAG/XDS Debug interface (This is the interface we will use for this training)
  • MQTT (Internet of Things)

Prerequisites

Software for desktop development

  • This tutorial can be done 100% with a web browser in CCS Cloud
  • The exercises can also be completed using desktop/offline tools as well. If you want to run the exercises offline, you will need to download & install the following:
    • CCS (refer to the SimpleLink SDK's Release Notes for recommended version)
    • SimpleLink SDK for your given LaunchPad
  • We will make small modifications to an existing example & will use GUI Composer to build a simple graphical interface for use with our application

Hardware requirements

  • A SimpleLink MCU LaunchPad Development Kit

Task 1 - Setting up code example for GUI Composer

1. Using a JTAG/XDS Debug interface to exchange data with your GUI

When using the JTAG/XDS Debug interface, GUI Composer can use your firmware's symbolic information to bind widgets. This essentially allows you to bind your application's global variables to GUI Composer widgets. In this training, we'll learn how to upload your project's .out file into GUI Composer, where it will parse the symbols within your application. We will then be able to bind our application's variables to various GUI elements. This mode requires the device to support non-intrusive memory access. There is no overhead on application processing and there is no code that needs to be added to your application. This option is available on all SimpleLink MCUs.

GUI Composer uses bindings to the ti-program-model component to allow widgets to be automatically updated to reflect the value of target-side global variables when those variables change, and to update the target-side global variable when the widget is configured by the user. For example, you can bind ADC readings to an analog gauge widget, or bind a variable within your application that is altered by a GUI Composer slider.

2. Add ADC conversion to the "empty" project

  1. Import the empty example for your launchpad into CCS or into CCS Cloud. The TI Drivers Project Zero Lab provides instructions on how to do this.

  2. We need to include the ADC driver's header at the top of our example's empty.c file:

    #include <ti/drivers/ADC.h>
    

    empty.c

  3. Add an ADC driver to our configuration. This is done through SysConfig:

    1. Double-click the empty.syscfg file within your CCS project in order to open the SysConfig editor.
    2. Add an ADC module to the configuration by clicking the plus button next to "ADC" in the left panel under "TI DRIVERS"

    3. This causes changes to the generated SysConfig files "ti_drivers_config.c" and "ti_drivers_config.h". These files can be viewed immediately in the SysConfig editor, or within your project directory under Generated Source → SysConfig after the project has been built. It is in these files that we will find the macros naming our added peripherals i.e. CONFIG_ADC_0. See the SysConfig Basics lab for more details.
    4. Be sure to save the empty.syscfg file
  4. Next we need a call to ADC_init() which will initialize the ADC driver. In empty.c there are comments that recommend where to place such driver initializations.

  5. Now let's create an ADC Handle. We must also initialize driver parameters and open the driver for usage. We can add this code inside mainThread, right before the while(1) loop (around line 75 in the code).

    /* Open ADC Driver */
    ADC_Handle adc;
    ADC_Params params;
    ADC_Params_init(&params);
    adc = ADC_open(CONFIG_ADC_0, &params);
    if (adc == NULL) {
       /* Error initializing ADC channel 0 */
       while (1);
    }
    

    empty.c :: mainThread() – Before while loop

  6. Finally, let's add the ADC conversion code inside the already present while(1) loop to periodically sample the ADC every second. Let's also use a simple if statement to set the LED high when the ADC result is >= an arbitrary threshold of 100, and to set the LED low when < then the threshold. We'll use the GPIO_write() API to accomplish this. We should also remove the GPIO_toggle() from the original empty example.

    while (1) {
      uint16_t     adcValue;
      int_fast16_t res;
    
      res = ADC_convert(adc, &adcValue);
      if (res == ADC_STATUS_SUCCESS) {
        if (adcValue >= 100) {
          GPIO_write(CONFIG_GPIO_LED_0, CONFIG_GPIO_LED_ON);
        } else {
          GPIO_write(CONFIG_GPIO_LED_0, CONFIG_GPIO_LED_OFF);
        }
      }
    
      sleep(time);
    }
    

    empty.c :: mainThread() – ADC_convert() with LED toggling statement.

3. Let's turn the variables we want to visualize & modify into global variables

In this exercise, we want to build a simple GUI that does the following things:

  • Visualize our latest ADC readings using an analog gauge
  • Get an "LED" notification when we are above/below a threshold
  • Feature a slider that allows us to modify the threshold for the LED alert

This means we need a global variable for our ADC reading & a global variable for our threshold. We will also need a global variable that tells us if we're above or below the threshold. Again, these variables need to be globals in order to bind them to GUI Composer.

Make adcValue a global variable

First, let's make adcValue a global variable. We need to get rid of the local declaration we have in our while(1) loop, then add a global declaration to the top of our code. We will eventually bind this global variable to an analog gauge in the GUI to visualize our latest readings.

Create a global variable for threshold

Second, we need to make our threshold value variable-dependent. Currently, our threshold is hard-coded at 100. Let's define a new global variable called "threshold" and replace our hardcoded limit of 100. We will eventually bind this global variable with a slider to enable the threshold to be altered via the GUI.

Create a global variable for alert

Lastly, we need to create a new global variable for our alert. This will be '1' if our ADC reading is above the threshold, or '0' if it is below the threshold. We can use this global variable to feed the LED indicator in the GUI we are about to build.

Let's also make our variables update more frequently

To do this, we will use the usleep() API instead of the sleep() API. This allows us to pass in the number of microseconds we want to sleep for. Set the value of the time variable to 100000.

We should end up with the following

  /*
   *  ======== empty.c ========
   */

  /* For usleep() */
  #include <unistd.h>
  #include <stdint.h>
  #include <stddef.h>

  /* Driver Header files */
  #include <ti/drivers/GPIO.h>
  #include <ti/drivers/ADC.h>
  // #include <ti/drivers/I2C.h>
  // #include <ti/drivers/SPI.h>
  // #include <ti/drivers/UART.h>
  // #include <ti/drivers/Watchdog.h>

  /* Driver configuration */
  #include "ti_drivers_config.h"

  /* global variableS FOR GUI COMPOSER */
  uint16_t adcValue = 0;
  uint16_t threshold = 100;
  uint16_t alert = 0;

  /*
   *  ======== mainThread ========
   */

  void *mainThread(void *arg0)
  {
      /* ~10 loops/second */
      uint32_t time = 100000;  // update ~10/second

      /* Call driver init functions */
      GPIO_init();
      ADC_init();
      // I2C_init();
      // SPI_init();
      // UART_init();
      // Watchdog_init();

      /* Open ADC Driver */
      ADC_Handle adc;
      ADC_Params params;
      ADC_Params_init(&params);
      adc = ADC_open(CONFIG_ADC_0, &params);
      if (adc == NULL) {
          // Error initializing ADC channel 0
          while (1);
      }

      while (1) {
          int_fast16_t res;

          res = ADC_convert(adc, &adcValue);
          if (res == ADC_STATUS_SUCCESS) {
              if(adcValue >= threshold) {
                  GPIO_write(CONFIG_GPIO_LED_0, CONFIG_GPIO_LED_ON);
                  alert = 1;
              } else{
                  GPIO_write(CONFIG_GPIO_LED_0, CONFIG_GPIO_LED_OFF);
                  alert = 0;
              }
          }

          usleep(time);
      }
  }

4. Let's compile our code to generate a .out file

Once we've made the small changes to our project, we can go ahead and compile our project. This will generate a .out file, which we can ultimately pass on to GUI Composer. With the .out file, GUI Composer will be able to use the symbolic information to extract the global variables within your application. Clicking the "Debug" button in CCS (or the "Run" button in CCS Cloud) will compile our code to generate a .out file & will also flash our LaunchPad.

Once the project is built, we can find the .out file by going to the Debug folder within the Project Explorer/Workspace Files window within your IDE. In CCS Cloud you will find it in the Debug folder within your project. If using CCS Cloud, right-click the file to download it locally.

Task 2 - Creating our first GUI!

1. Launch GUI Composer & Create new GUI project!

Now that we have our .out file, we can launch GUI Composer by navigating to http://dev.ti.com/gc

Select "CREATE A NEW PROJECT" to launch the "New Project Wizard."

Let's use the following parameters:

  • Project Template: Application
  • Project Name: GUI_Composer_Demo
  • Application Name: GUI_Composer_Demo
  • Enable TI-Branding: [Check]
  • Then, press NEXT >>

2. The Target programming window

We need to tell GUI Composer which device we are using as well as upload the .out file of our firmware. This will allow GUI Composer to unintrusively read/modify global variables without any additional code required in our firmware.

Start by clicking "ADD".

Select the device you are using

In the development of this tutorial, we used the MSP-EXP432P401R LaunchPad, so we will select MSP432P401R. Be sure to select the device you are developing with. After selecting your device the Connection field should automatically be populated. You can also find the connection mechanism your launchpad uses on its product page or within its provided documentation.

Upload your .out file

Click the upload button & navigate to the .out file inside your local CCS project or to the .out file that you downloaded from CCS Cloud.

Leave Auto Program checked and press NEXT.

3. The Target communication window

In this window we specify how we will extract data from our target.

Select a transport mechanism

Create a new transport mechanism by pressing the "Add" button. The XDS transport option is available on Cortex-based MCU's and C2000 devices. As we have been using the MSP-EXP432P401R in this workshop, we will select XDS.

Select a data representation scheme

The target data representation scheme (a.k.a. Available Model) we will use is "Program". Ensure this is selected, then click the "Add" button next to the selection. On the resulting screen under Program Model there should only be one option for Program Loader ID - "ti_core_programloader". Finally, click "OK" to exit the wizard and create the project.

Please see the "Interacting with a Target Device" section of the GUI Composer User's Guide for an explanation of the different target communication options.

4. Adding elements to your GUI

At this point, we should have a blank window to start developing our GUI. We can search the palette of available GUI elements by opening the component palette on the left-hand side of the editor.

For this demo, we need 3 different elements:

  • Dials & Gauges > Analog Meter (for our ADC readings)
  • Common Widgets > Horizontal Slider (for modifying our threshold)
  • Status Indicators > LED (to indicate if ADC reading is above/below threshold)

Search for these items in the GUI palette & drag them into the GUI editor window. The editor may look a bit different from what is presented below. You should end up with the following:

5. Editing GUI elements with Properties & Styles panel

Each GUI element can be modified & edited using the Properties & Styles panel. Click on the GUI element you want to modify, then simply edit the parameters in the side configuration pane.

For this tutorial, we need to make a few small adjustments

Analog meter

  • Change the max-value to the maximum digital number your device's ADC resolution can support. The MSP432P401R device we are using has a 14-bit Analog to Digital Converter, which can give us 2^14 unique readings. So we'll put 16,383 as our max-value.

Horizontal slider

  • We want to change the labels here to reflect percentage, so let's change the "labels" parameter to: 0%, 25%, 50%, 75%, 100%
  • We also need to change the max-value of the slider to match that of the analog meter. In the case of a 14-bit ADC, we will change it to 16,383.

LED

  • If we wanted to, we can change the LED color, but we'll go ahead and leave ours red.

6. Binding global variables with GUI widgets

Now that our GUI elements are configured, we can go ahead & bind the global variables to them. To do this, we will again modify the Properties pane for each widget.

Binding Analog meter

Click the analog meter widget to make it "active." Click the "bind" icon next to the "value" field. This will introduce a drop down menu that is pre-set for "ti_model_program." Leave that as-is. In the empty text field to the right, we will type the name of the global variable that we want to bind. In this case, we want to bind the global variable adcValue

Binding Horizontal slider

Click the horizontal slider widget to make it "active." Click the "bind" icon next to the "value" field. Again, leave the "ti_model_program" drop down as-is & type in the global variable we want to bind to the horizontal slider. In this case, we want to bind the global variable threshold

Binding LED indicator

Click the LED indicator widget to make it "active." Click the "bind" icon next to the "on" field. Again, leave the "ti_model_program" drop down as-is & type in the global variable we want to bind to the LED indicator. In this case, we want to bind the global variable alert

7. Let's run our GUI!

And that's it! Now that we've successfully built our GUI, we can go ahead and run our GUI by clicking the "play" button.

This will open up your GUI in a new tab. At this point, your GUI will start to connect to your LaunchPad, which is running the same firmware that we uploaded into GUI Composer.

At this time, you should see the analog meter updating appropriately. You can also slide the horizontal slider to change the target threshold. Lastly, the LED indicator in the GUI should match the status of the LED found on your LaunchPad.

8. Exporting your GUI/app

Once you're happy with your GUI, you GUI Composer can export your GUI as a standalone application. You can export your GUI by clicking on File > Export > as Stand-Alone App

This will generate a zip file of your GUI, which can now run standalone.

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