By dave | May 14, 2018

tcMenu - Java API to Arduino for remote menu control

Introduction - Java Remote control for Arduino Menus

One of the design goals of TcMenu is that it must be very easy to remotely control the menu. I’m a big Audiophile and like building my own equipment, most if not all Audio equipment needs a method of remote control. Nowadays, this extends beyond audio into almost all devices; where control by phone or a computer is common place.

Using the Java API we can see exactly the same menu structure as it appears on the device, and the mappings are soclose that I built the Arduino embedded menu designer using this API.

We also plan to directly support other languages soon, but the protocol is fully documented for use in any language. If you’d like to help with porting please get in touch through github or my contact form, I’d be glad to help you get started with the protocol layer.


How the Java API maps to the type system

Below is a block diagram showing the high level components of a menu with remote control capabilities. It looks complex, but don’t worry as the designer UI does much of the work on the Arduino side for you. Further more, simple builders make it really to setup in Java.

Top level block diagram of the menu library

Block diagram of a menu with remote capability (click for larger image)

Getting the Java API

I’m assuming you’ve already followed at least the Quick Start guide, and got a menu deployed to an Arduino or other board. If not you’ll have nothing to connect to. You should also at least skim read about the menu type system as we’ll not cover it again.

If you are using Maven or Gradle then add the following dependency to your project:

   <dependency>
        <groupId>com.thecoderscorner.tcmenu</groupId>
        <artifactId>tcMenuJavaAPI</artifactId>
        <version>1.4.2</version>
    </dependency>

Otherwise you can copy the library from github manually. Full javadoc and source are available with the above dependency. The above dependency will also automatically include the Java serial (rs232) library onto your path.

This library uses JDK 11 system logging as the logging framework. The default for this backs onto java.util.logging.

Getting started with the API

In the Java API menu items are managed by a MenuTree instance, each connection to an Arduino has its own tree, as otherwise the ID’s would overlap. Menu’s are represented as a ROOT menu and submenu’s, the MenuTree controls where in this tree of menus each MenuItem sits. MenuItem is the base class of all menu item instances. We draw the hierarchy below:

MenuItem
    SubMenuItem         -- holds more menu items (like a directory)
    AnalogMenuItem      -- holds numeric values
    EnumMenuItem        -- holds choices
    BooleanMenuItem     -- true / false,  yes / no, on / off
    TextMenuItem        -- textual values and IP addresses
    FloatMenuItem       -- a floating point value (not editable)
    ActionMenuItem      -- an executable menu item with no status.
    RuntimeListMenuItem -- a list of items sent from the server

All of the items are immutable, this means they can be used safely across threads. If a change in the menu item does occur (infrequently) you’ll be notified of the new menu item. However, the current value associated with a menu item changes frequently, this is accessed via the state. To get the current value and status of a menu item, use the MenuState which is obtained from the MenuTree.

When you want to make a change to a remote value, you do not need to update the menu item or it’s state, send a command to the Arduino, and it will be reflected back in the menu item state once applied.

There are two examples that you can look at to get started:

Connecting remotely

If authentication is enabled on the embedded side, before any connection can be initiated we must ensure that the application has been paired with the Arduino / embedded device. No connection will be possible otherwise. See the notes below on how to arrange for pairing through the API.

In order to connect with An Arduino we need a RemoteConnector and a MenuTree. Typically, the menu tree will be populated with the menu items by the remote connector, and then we register for changes or make changes to the items.

Creating an Ethernet Socket controller

When generating the Arduino side of the software, ensure that the ethernet socket is chosen for remote access. Now enter the port on which you wish to listen for connections, default is 3333. Here is the example code to create a connection on the Java side:

var menuTree = new MenuTree();
var controller = new SocketControllerBuilder()
        .withAddress(ipAddress)
        .withPort(port)
        .withLocalName("myApp")
        .withUUID(myUUID)
        .withMenuTree(menuTree)
        .build(); // see note below

If you need to create a connection for pairing, then instead of build call:

// you can initiate pairing with an optional pairing listener, if you don't wish to be
// informed of progress just set to Optional's EMPTY.
boolean paired = attemptPairing(Optional<Consumer<PairingHelper.PairingState>> maybePairingListener);

Creating an rs232 backed controller

When generating the Arduino side of the software, make sure that rs232 communication is chosen. It will default to the default port and 115200 baud. You can change to any other serial port as long as it extends Stream, which nearly all do. This would include most Bluetooth serial facilities. Here is the example code to create a connection on the Java side:

var menuTree = new MenuTree();
var controller = new Rs232ControllerBuilder()
        .withRs232(portName, baud)
        .withMenuTree(menuTree)
        .withUUID(myUUID)
        .withLocalName("myApp") 
        .build();

If you need to create a connection for pairing, then instead of build call:

// you can initiate pairing with an optional pairing listener, if you don't wish to be
// informed of progress just set to Optional's EMPTY.
boolean paired = attemptPairing(Optional<Consumer<PairingHelper.PairingState>> maybePairingListener);

Starting the controller and receiving events

Once everything is ready, we add a remote listener and start the controller for it to connect:

        controller.addListener(new RemoteControllerListener() {...});
        controller.start();

The remote listener interface is:

public interface RemoteControllerListener {
    // called when a menu item has changed, value only indicates if the MenuItem has changed
    // or only the value (state) has changed
    void menuItemChanged(MenuItem item, boolean valueOnly);
    
    // called when the tree is fully populated
    void treeFullyPopulated();
    
    // called when we have either connected or disconnected.
    void connectionState(RemoteInformation remoteInformation, boolean connected);
   
    // called when a dialog is shown or hidden 
    void dialogUpdate(DialogMode mode, String header, String buffer, MenuButtonType btn1, MenuButtonType btn2);
    
    //called when an acknowledgement has been recevied
    void ackReceived(CorrelationId key, MenuItem item, AckStatus status);
}

This is the minimum required code to connect!

Finding and querying MenuItems

The root level has a special MenuItem that never appears in the tree defined as MenuTree.ROOT. Whenever you want to get all the top level items, request using that key.

To find a given item by it’s ID:

Optional<MenuItem> item = tree.getMenuById(idToFind);

// or if you prefer functionally
tree.getMenuByItem(idToFind).ifPresent(item -> {
    // deal with item here..
});

For example to get all root elements as a list:

List<MenuItem> menuItems = menuTree.getMenuItems(MenuTree.ROOT); 

Another example to go through every single menu item regardless of level:

    // here we first traverse through all the submenus (even ROOT is a submenu)!
    menuTree.getAllSubMenus().forEach(subMenu -> {
        logger.info("SubMenu {} has the following child elements", subMenu);
        // and then we go through all the items within that submenu.
        menuTree.getMenuItems(subMenu).forEach(item -> logger.info("----->>> " + item));
    });

To get the value associated with a menu item we get it’s current state from the menuTree. It’s state stores the current value and also if it has changed or is active. Here are examples:

    MenuState<Integer> menuState = menuTree.getMenuState(anAnalogMenuItem);  OR
    MenuState<Boolean> menuState = menuTree.getMenuState(aBooleanMenuItem);  OR
    MenuState<String> menuState = menuTree.getMenuState(aTextMenuItem);
    
    menuState.getValue();
    menuState.isChanged();
    menuState.isActive();

Updating menu item values

To update a menu item, you don’t need to save the state locally, instead just send the command to the server. The server will respond sending the change. To send a delta change (IE add 5 to the items value):

    public CorrelationId sendDeltaUpdate(MenuItem item, int deltaChange);

To send an absolute change to the server:

    controller.sendAbsoluteUpdate(MenuItem item, Object newValue);

Once the Arduino receives the update, it will apply it to the menu item and publish out the changed value. However you can change the state yourself if you want to (but be aware this will not cause it to update on the remote Arduino device):

    menuTree.changeItem(theItem, theItem.newMenuState(value, changedBool, activeBool) );       

Sending a dialog button press

To send a dialog button press to the server, use the following method:

    controller.sendDialogAction(MenuButtonType buttonType)

The API documentation

This page is intended to just get you started, you can look at the full API documentation and examples that are packaged with the API tests in the source for more details.

Image of ethernet card running with display and encoder

Ethernet board with display and encoder

Back to tcMenu main page

Other pages within this category

comments powered by Disqus

We use cookies to analyse traffic and to personalise content. We also embed Twitter, Youtube and Disqus content on some pages, these companies have their own privacy policies.

Please see our privacy policy should you need more information or wish to adjust your settings.

Send a message
X

This message will be securely transmitted to Nutricherry LTD servers.