CUI API  1.00.00.00
CUI API Documentation
Date
2018-12-18 12:13:13 -0800 (Tue, 18 Dec 2018)
Revision
42528

Overview

This file contains the interface of the Common User Interface or CUI.

The CUI controls the access to User Interface resources. On a launchpad
device, these resources are made up of Buttons, LEDs and UART i/o.

The CUI provides an API that can be used to request for access to resources
and then read/write to those resources. In order to request and acquire a
resource the notion of a 'client' is used. The Client handle is used to
successfully communicate with the CUI API. Once you open a client with the
CUI you may then request access to a resource. If the resource is available
the CUI will give the client access to the resource.

Resource Types

As described earlier, there are three different resources the CUI controls
access to. (Buttons, LEDs and UART i/o).

For Buttons and LEDs the process for requesting and using the resource is
quite simple and is nearly identical to how you would use the Button or LED
drivers to do the same.

For UART i/o there are two resources to consider. Firstly there is the
notion of a 'menu'. A menu specific to your application can be registered with
the CUI. The CUI will handle menu navigation, reading and writing. A menu
can be as simple or as complex as desired. The second UART i/o is the notion
of a 'status line'. Where the menu is designed to be a display
driven by the input of the user, the status lines are capable of displaying
the latest value of any particular set of information the developer chooses.
A great example of a menu item would be any sort of toggle led event your
device can perform or maybe a commissioning action and an example of a
status line could be current network status or any application state
information. More details on menus and status lines can be found below.

Usage

This documentation provides a basic usage summary and a set of examples in
the form of commented code fragments. Detailed descriptions of the APIs are
provided in subsequent sections.

Initilization is the first step.
// Import the CUI definitions
#include "cui.h"
CUI_params_t cuiParams;
CUI_paramsInit(&cuiParams);
// One-time initialization of the CUI
if (CUI_SUCCESS != CUI_init(&cuiParams)) {
// handle failure
}

By calling CUI_paramsInit() you are setting the cuiParams to their default values.

cuiParams.manageBtns = true
cuiParams.manageLeds = true
cuiParams.manageUart = true

If your application requires special management of buttons, leds, or uart the developer can set the parameters appropriately before calling CUI_init()

After the CUI has been initialized you can then open a client.

CUI_clientParams_t clientParams;
CUI_clientParamsInit(&clientParams);
strncpy(clientParams.clientName, "My first CUI application", MAX_CLIENT_NAME_LEN);
clientParams.maxStatusLines = 5;
CUI_clientHandle_t clientHandle = CUI_clientOpen(&clientParams);
if (clientHandle == NULL) {
// handle failure
}

By Calling CUI_clientParamsInit() you are setting the clientParams to their default values.

strcpy(clientParams.clientName, "");
clientParams.maxStatusLines = 0;

It is currently required that your clientName not be empty. It can be anything you would like as it is only used to create a simple hash value.

If you would like to add status lines to your application, you will need to update clientParams.maxStatusLines as the code snippet above shows.

Now that you have a valid client you may start requesting access to resources. When requesting a button resource you must specify which button you are requesting as well as a callback that the CUI can use to notify you of a button change event that matches the CUI_btnPressCB_t prototype.

CUI_btnRequest_t rightBtnReq;
rightBtnReq.index = CONFIG_BTN_RIGHT;
rightBtnReq.appCB = myBtnChangeHandler;
if (CUI_SUCCESS != CUI_btnResourceRequest(clientHandle, &rightBtnReq) {
//handle failure
}

If the resource was available to acquire then you will now have access to the right button.

Alternatively, if you would like to read the button state without requiring an application callback, you can request the button in a sort of polling mode by using a NULL appCB. Then simply request the button's value whenever you require it.

CUI_btnRequest_t rightBtnReq;
rightBtnReq.index = CONFIG_BTN_RIGHT;
rightBtnReq.appCB = NULL;
if (CUI_SUCCESS != CUI_btnResourceRequest(clientHandle, &rightBtnReq) {
//handle failure
}
bool btnState = false;
retVal = CUI_btnGetValue(clientHandle, CONFIG_BTN_RIGHT, &btnState);
if (!btnState) {
//handle button logic
}

If you would like to change the btnMode of a button you already have access to then you can use the CUI_btnSetCb() API. By using a non-NULL value for the third parameter you will be using a sort of call back mode. If you use a NULL value for the third parameter, you will be using a polling mode.

CUI_retVal retVal;
retVal = CUI_btnSetCb(clientHandle, CONFIG_BTN_RIGHT, appChangeKeyCB);
if (retVal != CUI_SUCCESS) {
//handle failure
}

Buttons are great but only part of the picture. Let's request a LED as well. This time the only information you need to provide is the index of the LED you wish to acquire.

CUI_ledRequest_t greenLedReq;
greenLedReq.index = CONFIG_LED_GREEN;
if (CUI_SUCCESS != CUI_ledResourceRequest(clientHandle, &greenLedReq) {
// Handle failure
}

If the led was available you will have gained access to it and when you wish to change the state of the led you need only to call the CUI LED APIs.

CUI_ledToggle(clientHandle, CONFIG_LED_GREEN);
CUI_ledBlink(clientHandle, CONFIG_LED_GREEN, 5);
CUI_ledBlink(clientHandle, CONFIG_LED_GREEN, CUI_BLINK_CONTINUOUS);
CUI_ledOn(clientHandle, CONFIG_LED_GREEN);
CUI_ledOff(clientHandle, CONFIG_LED_GREEN);

One note on CUI_ledBlink is that you can choose to blink continuously or you can choose to blink a specific number of times. If you call CUI_ledBlink while the led is already blinking. The only thing that will happen is that the number of blinks will be reset to the new value.

Now you have full control over buttons and LEDs. But you may want to let the user know when you have joined a network for instance. Well this is the perfect use for a status line. First we must request access to an available status line. Status lines are handed out upon request in numeric order and the number of status lines available to you are determined by clientParams.maxStatusLines when you opened your client.

uint32_t connStatusLine;
if (CUI_SUCCESS != CUI_statusLineResourceRequest(clientHandle, "Conn Status", &connStatusLine) {
// handle failure
}

If clientParams.maxStatusLines haven't already been requested from the CUI you will have been given access to that line. When a status line is acquired, a default line value will be printed in the form of "LABEL: --". So in the example above we will see "Conn Status: --" printed on the screen.

If you wish to tell the user a different value for that status line you need to tell the CUI what that new value is. Using a variatic function you can pass in a format string and any number of optional items to be formated according to your format string. Similar to printf() if you are familiar.

char lineFormat[MAX_STATUS_LINE_VALUE_LEN];
strncpy(lineFormat, "Successfully Connected", MAX_STATUS_LINE_VALUE_LEN);
if (CUI_SUCCESS != CUI_statusLinePrintf(clientHandle, connStatusLine, lineFormat) {
//handle failure
}

Now you will see "Conn Status: Successfully Connected".

If you wish to spice your status lines up a bit more, there are a few color codes available to you by default. To make use of these, simply surround the text you wish to be given a color between a color macro and the reset macro.

For instance, you could use red for something bad, and green for something good. These can be used for status lines or menu lines. Any text that is being displayed can be colored with these macros

CUI_statusLinePrintf(clientHandle, connStatusLine, CUI_COLOR_RED "Connection Failure :(" CUI_COLOR_RESET);
CUI_statusLinePrintf(clinetHandle, connStatusLine, CUI_COLOR_GREEN "Connection Successful! :)" CUI_COLOR_RESET);

Lastly in order to create a menu you must first declare your menu layout. A menu can be as simple or as complicated as you want. At the root of every application's menu though are three things. Main Menu Sub Menus Menu Actions

A Main menu as it sounds is the top level menu of your application, this is what you will use to register your entire menu with the CUI. Your main menu can consist of any number of sub menus and menu actions. In turn, those sub menus can consist of any number of sub menus and menu actions as well. This creates a tree topology that will go as far as you desire it.

A menu action can be of two variants. Either a normal action or an interceptable action. A normal action is used best for triggering sections of code via UART input. Say for instance you want to toggle the state of your led or trigger your device to connect to an open network. Then a normal action is exactly what you want. You may have several actions you want to perform that are very similar. A normal action can help with this by providing you with the itemEntry that is calling your action. This can be used in order to have a single action function in your code that can handle multiple different situations based upon the itemEntry that is received. Though if you want to do something more complex like modify your device's channel mask or panId, then an interceptable action is required.

Interceptable actions are created such that instead of triggering a one off section of code, they actually start to intercept the input from the UART. Say for instance you want to modify the channel mask. The user selects the action and then all UART input can be processed by your action code. You can handle this input however you want. Then when the user de-selects the action the UART input will be again handled by the CUI and menu navigation will continue as normal.

As menus can become rather complex this file provides some helpful macros in order to simplify menu definition. Here's an example of declaring a main menu that has a normal action as well as a sub menu. Within the sub menu there is an interceptable action.

CUI_SUB_MENU(mySubMenu, " My Sub Menu ", 1, myMainMenu)
CUI_MENU_ITEM_INT_ACTION("< Edit Channel Mask >", editChannelMaskFn)
CUI_MAIN_MENU(myMainMenu, " My Main Menu ", 2, processMenuUpdateFn)
CUI_MENU_ITEM_ACTION("< Toggle LED >", toggleLedFn)

If you are wondering how the macros work you can look at the implementation. Here is the prototype for the CUI_MAIN_MENU and CUI_SUB_MENU macros though.

#define CUI_MAIN_MENU(_menuSymbol, _pMenuTitle, _numItems, _pMenuUpdateFn)
#define CUI_SUB_MENU(_menuSymbol, _pMenuTitle, _numItems, _pUpperMenu)

Now you can see that the sub menu was defined to be the 'mySubMenu' symbol with a menu title of " My Sub Menu ", '1' menu item and it's parent menu is the 'myMainMenu' symbol.

Similarly the main menu was declared to be the 'myMainMenu' symbol with a menu title of " My Main Menu " and 2 menu items. The last parameter is different between them though because of two reasons. Firstly, a main menu has no notion of a parent menu because it is already the top level menu. Secondly, '_pMenuUpdateFn' is a parameter so that the CUI can gain processing context whenever menu processing needs to occur.

Since the CUI does not create an rtos task, it technically does not have it's own processing context. This means that if any CUI menu processing is to take place, such as the user navigating left and right through the different menus, some other rtos task must own the processing time for this to occur. This function simply needs to end up calling CUI_processMenuUpdate. Whether it does that directly or it posts an event to a semaphore so that the task can call CUI_processMenuUpdate() when there is time to do so is up to the developer. To be clear, the '_pMenuUpdateFn' is required for your menu to operate correctly. Without it the menu will fail to register.

Now that you have a main menu defined it still needs to be registered with the CUI.

if (CUI_SUCCESS != CUI_registerMenu(clientHandle, &myMainMenu) {
//handle failure
}

As long as no more than MAX_REGISTERED_MENUS have already been registered and your menu was defined correctly the registration will be successful and your main menu will appear over UART.

Notice how there is the capability to register MAX_REGISTERED_MENUS? This is a feature so that when two examples are merged together to create a DMM (Dynamic Multi protocol Manager) example, the menus of each standalone application's menu can exist safely without impacting the other menu. When multiple menus are registered the CUI will introduce a new top most main menu. This main menu will then have a sub menu for each menu that was registered with the CUI. The title of this new multi-menu will default to " TI DMM Application ", but can be changed by providing the address to a different valid c-string to the CUI_updateMultiMenuTitle() function.

const char newTitle[] = " My First Multi Menu Project ";

When developing an interceptable action, it is important to know what to expect to receive as input. Your interceptable action should match the CUI_pFnIntercept_t typdef. typedef void (CUI_pFnIntercept_t)(const char _input, char _lines[3], CUI_cursorInfo_t * _curInfo); The first parameter, _input, will be a single byte indicating what type of input the user has pressed. There are a few special input types for your use.

CUI_ITEM_PREVIEW allows your action to display some information on the menu without the user executing your action. This is useful for configurable application variables such as panId. When the user navigates over top of the theoretical "< PAN ID >" screen, it could also display what the current panId is without the need for the user to execute on the action. In the case of a CUI_ITEM_PREVIEW event, your interceptable action will only be able to provide a preview on the first two lines. The third line will be maintained by the cui to be the item's description regardless of what you put in the third line.

CUI_ITEM_INTERCEPT_START is an indication to your action that the user has decided to execute on your action. This is where you could perform some sort of setup steps for the lifetime of the action if necessary.

CUI_ITEM_INTERCEPT_STOP is an indication to your action that the user has completed their time in your action. This is where you could perform some clean up code or finalization code for the end of the action.

CUI_ITEM_INTERCEPT_CANCEL is an indication to your action that the user decided to cancel any current progress within your action. What should/can be done in this case depends on what your action is doing. (i.e) If your action is modifying the Pan ID. CUI_ITEM_INTERCEPT_START could clear the local copy of the pan ID, the user can modify the local copy. Then if you receive a CUI_ITEM_INTERCEPT_CANCEL input, you should clear out the local copy and make sure not to save the changes the user had made.

CUI_ITEM_INTERCEPT_START and CUI_ITEM_INTERCEPT_STOP are useful for configurable variables such as panID because when CUI_ITEM_INTERCEPT_START occurs you can obtain the latest panId from the stack and save a static local copy of it during the lifetime of the action. Then while the user is modifying the panId you are only updating the static local version of the variable. Finally, when the user has completed the modifications to the panId, CUI_ITEM_INTERCEPT_STOP will come through and you can set the new panId as the official panId in the stack. This process allows you to limit the number of stack APIs you need to call.

The UP, DOWN, RIGHT, LEFT inputs come directly from the arrow keys on the user's keyboard. Similarly, the BACK input is from the backspace key on the keyboard. As with all of these inputs. You as the developer of the interceptable action get to choose whether to act on them. They are only there if you want to use them.

Besides those special inputs, any ascii characters the user types will be provided to you via the same _input variable. It is again up to you as the developer to handle this input as you deem necessary. A few more macros are provided to you in order to simplify your logic. You can use these to determine if the input is what you expect or not. They will return TRUE or FALSE depending on the _input provided. CUI_IS_INPUT_NUM(_input) CUI_IS_INPUT_ALPHA(_input) CUI_IS_INPUT_ALPHA_NUM(_input) CUI_IS_INPUT_HEX(_input) CUI_IS_INPUT_BINARY(_input)

The second param to CUI_pFnIntercept_t are the _pLines[3] that the menu will print out. By putting c strings in these buffers you can share any information with the user while they are either previewing your action or during the interceptable period of your action. When writing data to these buffers take care not to write more than MAX_MENU_LINE_LEN per line. This will cause undefined behavior and possibly cause your application to assert. These buffers are provided to you out of kindness, so treat them with the due respect and the CUI will behave accordingly :)

The last param to CUI_pFnIntercept_t is a pointer to a CUI_cursorInfo_t struct. This is here only if you want to use it. If you do not modify this struct then no behavior will change. Though if you would like a cursor to appear on the menu to indicate to the user where some information is, then you can set the cursor.col and cursor.row values. By doing this the CUI will maintain the cursor on the screen as you have requested. There is no need to turn the cursor off as after receiving an input of CUI_ITEM_INTERCEPT_STOP the CUI will automatically remove the cursor. In the case of CUI_ITEM_PREVIEW, the cursor will be ignored. This means that the cursor can only be on the screen as long as the user is currently using your interceptable item action.


© Copyright 1995-2021, Texas Instruments Incorporated. All rights reserved.
Trademarks | Privacy policy | Terms of use | Terms of sale