Mita

  1. General Information
    1. About XDK Live
    2. About Eclipse MITA
  2. Programming With Mita
    1. Creating a Project
    2. General Syntax
      1. Declaring Variables
      2. Functions
      3. Types
        1. Basic Data Types
        2. Strings
        3. Structs
        4. Enums
        5. Sum Types
    3. System Resources
      1. Setting up Resources
    4. Events
    5. Hello World Implementation
  3. Generated Code
    1. General Structure
    2. base
    3. main.c
    4. application.c
    5. applicationTypes.h

General Information

About XDK Live

Please note at the XDK-Workbench version 3.4.0 and higher XDK Live was renamed to Mita in the XDK-Workbench context.

As it is the case for many other embedded devices in the Internet of Things, the XDK’s Platform code is completely written in the programming language C. This demands the user himself to also use the language C to develop IoT applications. For experienced C programmers, this may not be an issue, but for users unfamiliar with C, it poses a restriction.

Compare that with the applications the XDK is usually interacting with. They are often cloud-based and they are usually written in more modern languages such as Python, JavaScript, Kotlin, and Go. These languages feel natural and pose less of a burden for users who are new to the language.

This leaves developers who are responsible for both, the server application and the XDK application, in a situation where they absolutely have to learn and understand C coding to write a few hundred lines of C code just to send a single message containing sensor data.

The purpose of XDK Live, which is the XDK branch of the open source project Eclipse Mita, is to bridge the gap between C and modern languages. It takes a lot of influence from languages such as Typescript, Kotlin and Go and abstracts the Platform’s C code into higher level constructs. On build, the code is automatically transpiled to C code, and then compiled into a binary that the XDK’s MCU can run. This means that, for the rough implementation of a use case, XDK Live is more than plenty. And as soon as XDK Live is not enough for a specific implementation, the developer can dive into the C code and continue work from there.

XDK-Live is available in the XDK-Workbench since version 3.0.0.

About Eclipse MITA

As mentioned in the previous chapter, XDK Live is a branch of Eclipse Mita. This is the Open Source Project behind the language and the transpiler.

While the XDK (and XDK Live) is the pilot platform of this project, the long-term goal of Eclipse Mita is to provide platform implementations for other devices, to make them more accessible for developers with backgrounds in modern languages.

A positive side-effect from using Eclipse Mita as a higher level language that transpiles into C code is the fact that the resulting C code is tested and optimized. This means that the code automatically adheres to certain standards that make scaling the application to production simpler. The developer does not have to concern him- or herself with whether the code will provide the expected result. Instead, the expected result is clearly indicated by the high level functions of Eclipse Mita / XDK Live. Of course, exceptions can still occur on runtime, but Eclipse Mita / XDK Live also provide language constructs to handle these cases.

Additionally, the structure of the resulting C code is unified. This way, other developers working on the same project know which functionality is located in which parts of the code.

Programming With Mita

This chapter provides a short introduction to the Mita programming language and a short tutorial how to create an Mita project in the XDK-Workbench, along with a code example featuring some key features.

The information provided here offers a quick overview of essential features of the language. For any further information and topics that are not covered here, please refer to the official Mita Documentation.

Creating a Project

Before development can start, a project has to be created first. There are two methods for doing this.

The first method is by opening the Welcome Screen of the XDK-Workbench and pressing the Use Eclipse Mita button in the Hands-On & Documentation section of the Welcome Screen.

Image

After pressing the button, a new project named EclipseMitaApplication is created.

The other method to create a new Mita Project is by using the respective Project Wizard. For this, first select File in the top menu bar, and then choose New > Project (or press Ctrl + N). A new window should have opened, that allows for selecting the Project Wizard.

Image

In that window, select XDK > Eclipse Mita Project, then press Next. This will open the Project Wizard for Mita.

Image

There, choose a name for the new project, and then press Finish.

After both methods, the project is created and opened automatically. The main source file application.mita and the Help tab, which contains the documentation of the language, are opened as well. The following screenshot shows the XDK-Workbench after project creation.

Image

General Syntax

This chapter provides some examples for typical constructs of the Mita language, that are used within the Hello World Implementation in a later chapter.

Declaring Variables

Variables are declared using the keyword var, followed by the variable name, and the type separated by a colon from the name.

var myBool : bool;

In this case, the variable receives a default value. For booleans, the default value is true. The variable can also be initialized with a specific value.

var myInitiallyFalseBool : bool = false;

If the type is omitted, but an initial value is provided, then the type will be inferred from the initial value. It is required to either provide a type or initial value.

Finally, a variable can also be declared constant using the keyword let instead of var.

let myConstantNumber : uint32 = 42;

A variable declared with the keyword let cannot be changed after its declaration.

Functions

Functions are defined using either the keyword fn or the keyword function.

function foo() {

}

fn bar() {

}

Functions can of course have input parameters as well. All input parameters must have a type.

fn foo(x : int32, myStr : string) {

}

Functions can also return a value of a certain type.

fn bar() : uint32 {
        var x : uint32 = 2;
        return x;
}

The only restriction to the return type is that the size of the returned variable size must be known at compile time. As such, basic data types can be used as return types, along with other types defined by the user. On the other hand, strings and arrays do not have sizes that are known at compile time, thus they are not allowed.

Types

Basic Data Types

The following basic types are currently available

  • Typical C types for integers, such as uint8, int16, uint32 (currently only up to 32 bit integers).
  • Floating Point Numbers with type-name float (32 bit) or double (64) bit. Floating Point Number literals (such as 1.0) are Float by default.
  • Booleans with type-name bool. Booleans are either true or false. Numbers cannot be used for Booleans.
Strings

Strings in Mita behave a bit differently than they would in C. The main reason for this is the fact that Mita abstracts all the pointer logic. Hence, strings behave like stripped-down versions of strings from languages like Java.

A string is declared as follows:

var myString : string = 'Hello World';

Keep in mind that the variable’s type does not need to be declared here, since the type is inferred by the fact that a string literal is assigned to it. Also, note that myString does not need to receive a size. The size is instead inferred, and appropriate (minimal) sizes are used in the resulting C code.

Two strings can also be easily concatenated.

var myFirstName = 'James';
var myLastName = 'Smith';
var myCombinedName = myFirstName;
myCombinedName += ' ';
myCombinedName += myLastname;

Mita also features string interpolation. This means that, instead of composing printf() statements as they are available in C, the variables can be directly included in the string itself. The following shows an example, where a variable’s value is printed to the console.

var x = 2;
println(`x = ${x}`);

String interpolation requires the string to be surrounded by backticks ` and interpolated variables are always surrounded by ${}. Because every interpolated variable is preceded by the dollar-sign, other curly braces do not have to be escaped. As such, a JSON holding data can be created and printed follows:

var x = 2;
var json = `{ data : ${x}}`;
println(json);

Of course, other strings can also be used within interpolation.

Structs

As an example, the following code defines a struct and declares variables as instances of that struct.

struct xy {
    var x: uint32;
    var y: uint32;
}

var vector1 = xy(x=2, y=3);
var vector2 = xy(2, 3); // 2d respects the order of members
var vector3 = xy(y=10, x=2); // if members are explicitly assigned, the order does not matter.

Note that the struct keyword is not needed for initialization and declaration, when assigning a struct.

Enums

The following code shows an example for enums.

enum state {
  one, two, three, four, five
}

var x = state.one;

Of course, enum values can be compared with one another. Thus, given the previous initialization of the variable x, the following code checks if x is currently a value of state.one:

var b : bool = state.one == x;
if(b) {
    println('x is in state one');
}
Sum Types

As an additional language construct, Mita offers the so called Sum Types. They transpile to a combination of enums, structs and unions in C and can be roughly described as enums with associated data.

The following code declares a Sum Type:

alt state {
    zero
    | one : uint32
    | two : uint32
    | three : uint32
}

Notice that the members of a Sum type can be declared with or without an associated type. The associated type can of course be a struct or an enum as well. A variable of a sum type can be declared as follows:

var myState = state.zero();

Whenever a certain state such as zero is assigned to a variable, it may also receive parameters. Since the member zero does not have a type itself, no parameter is given. But, if the state were to change to member one, then a parameter of type uint32 must be given.

var myState = state.one(42);

Now, given that there exists a variable of the Sum Type state, such as myState, we can use a switch-case like construct on myState as follows:

where(myState) {
    is(state.zero) {
        println('I am in State One');
    }
    is(state.one -> x) {
        println('I am in State Two with the value ${x}');
    }
}

System Resources

Setting up Resources

In Mita, setting up specific resources, such as a Wi-Fi configuration or an MQTT connection is very simple. There are two types of resources in Mita. They are either singletons, i.e. they can only exist once, or they can exist multiple times. An Both types can be set up using the keyword setup. The following code creates a Wi-Fi resource:

setup wifi : WLAN {
    connection = Personal;
    ssid = "MyWifiSSID";
    psk = "MyWifiPassword";
}

In this code, Wi-Fi is a resource of type WLAN. Thus, WLAN is not a singleton, and there can exist multiple Wi-Fi configurations within the same code.

On the other hand, the following code sets up the accelerometer:

setup accelerometer {
    bandwidth=BW_500Hz;
    range=Range_4G;
}

Since accelerometer is a singleton, an instance name is not required. Keep in mind that, even if accelerometer is configured using setup, there is no C code generated in regards to the accelerometer, until the accelerometer is used. This is because the Mita transpiler tries to minimize used code by not generating code for modules that are not used (even if they occur in the code).

Finally, some resources have additional variables, which are called signals. For HTTP, resources are a signal. For MQTT on the other hand, topics are a signal, and for LED, the individual colors can be signals. Signals can be written to and read. The exact type of input for signals differs from resource to resource.

For further information on you can refer to the individual articles as this is just a broad overview.

The following is an example for setting up the LEDs:

setup led : LED {
    var red = light_up(Red);
    var yellow = light_up(Yellow);
    var orange = light_up(Orange);
}

Individual LEDs can now be turned on or off using the following line outside of the setup:

led.red.write(true); // turn the red LED on
led.red.write(false); // turn the red LED off

Signals have two basic functions. They can either be written or they can be read. For MQTT, reading a topic-signal means subscribing on that topic, whereas writing on a topic signal means sending a message with that topic.

hivemq.helloWorld.write('{"hello": "world"}');

For LEDs, writing sets the state on a LED, and reading returns the state of that LED.

Events

In its core, the Mita language is a completely event-based language. That means, nothing happens until a certain event triggers a certain user-defined action.

One such event type is time. For example, the following code will print Hello World in the console every 10 seconds:

every 10 seconds {
    println('Hello World');
}

Notice that this is all there is to printing something specific every 10 seconds. Compare this to C, where the first step is to write a function for writing to console, then a timer has to be created and started. In total, that may be a total of around 15 lines of code, including error handling, as compared to the 3 lines of Mita code.

Some platform modules also offer certain events. For example, the accelerometer offers events that are based on interrupts, such as the new_data event, which can be used as follows:

every accelerometer.new_data {
    println('New Data!');
}

Hello World Implementation

Finally, all the tools neccessary for writing a first, simple application are assembled and can be combined into a typical IoT application. This Hello World application may not be as typical as expected, though. Leveraging the fact that Mita abstracts so much of the complicated C code into a few lines of code, a lot can be done with only little setup time.

For example, a more advanced application would check the light intensity every 60 seconds, and if the light intensity exceeds a certain threshold, the current temperature data is sent to a server via HTTP every 5 second.

First, we setup the Wi-Fi and the HTTP resource.

setup wifi : WLAN {
    authentication = Personal(psk='myPassword');
    ssid = 'mySSID';
}

setup postmanEcho : HttpRestClient {
    transport = wifi;
    endpointBase = "http://www.postman-echo.com:80";
    var post = resource('/post');
}

With this, we have setup a Wi-Fi that will connect using WPA to the WiFi network specified with ssid and psk. The resource http is now basically a variable bound to the webserver postman-echo.com, where the signal postEndpoint is used to send requests to the post URI, i.e. postman-echo.com/post

Next, a global variable is set to define a certain threshold.

// use keyword 'let' for constant variables
let threshold = 50000;

Additionally, a variable is required to define the state the application is in. Either the XDK should be sending new data, or it should rest idle. Of course, the simplest solution would be to define a variable of type bool that is true in the first case, and false in the latter.

But instead of using a boolean variable, it is also possible to define an LED that more or less acts like a boolean variable. For this, an LED resource with the name state is defined. Its only signal will be aptly named thresholdExceeded, and this signal is bound to the yellow LED.

setup state : LED {
    var thresholdExceeded = light_up(Yellow);
}

Now, instead of setting a variable to true or false and checking it in if-statements, it is possible to write to and read the signal thresholdExceeded of the resource state. This has the benefit of combining a boolean state with a visual representation of the state in the form of an LED.

Next, the current light intensity will be checked every 60 seconds. For this, an event is setup that is triggered every minute and the application reacts to that event by checking the current light intensity and setting the LED state appropriately. This is implemented in the following code:

every 1 second {
    var data = light.intensity.read();
    state.thresholdExceeded.write(data > threshold ? true : false);
}

And finally, depending on the state, the current temperature data should be sent via HTTP every 5 seconds. For this, we use a JSON payload consisting of the field data that holds the temperature. The previously setup resource postmanEcho is used for this, with the resource-signal post.

every 5 seconds {
    if (state.thresholdExceeded.read()) {
        postmanEcho.post.write(`{ data : ${environment.temperature.read()}}`);
    }
}

The complete required code for the application summed up results in

package main;
import platforms.xdk110;

setup wifi : WLAN {
    authentication = Personal(psk='myPassword');
    ssid = 'mySSID';
}

setup postmanEcho : HttpRestClient {
    transport = wifi;
    endpointBase = "http://www.postman-echo.com:80";
    var post = resource('/post');
}

// use keyword 'let' for constant variables
let threshold = 50000;

setup state : LED {
    var thresholdExceeded = light_up(Yellow);
}

every 1 second {
    var data = light.intensity.read();
    state.thresholdExceeded.write(data > threshold ? true : false);
}

every 5 seconds {
    if (state.thresholdExceeded.read()) {
        postmanEcho.post.write(`{ data : ${environment.temperature.read()}}`);
    }
}

Generated Code

As it was mentioned multiple times in this guide, the code written in Mita is automatically transpiled into C code. In fact, code is generated every time the Mita file is saved and the code is syntactically correct. If the code is invalid, no code is generated.

Generated code can be used to make modifications to the application that can not be handled within the Mita. If some platform module is missing in Mita, but still needed for the use case, it can also be added in the generated C code. In all cases, make sure to export the generated code first, otherwise it will be overwritten when changes are made to the Mita code.

This chapter will explain the structure and some details of the generated code.

General Structure

All the generated code is located in the directory src-gen in the Mita Project. It has the following files and sub-directories

  • base - This directory holds implementations of resources and modules that are used in the application
  • application.c - This file contains the implementation of application-level logic. Since Mita is event driven, this mainly holds the implementation of event-reactions and functions defined in the application, as well as global variables.
  • applicationTypes.h - This file contains any types defined in the Mita applications.
  • main.c - This file bootstraps the entire application

The next chapters will explain the details of each of the listed items, since understanding what happens in these files is essential for further development.

base

This directory holds the implementations of resources and modules that are used in the Mita application logic. First of all, if nothing is implemented in the Mita implementation file application.mita, then the only files in base are generic header-files. These generic header-files are MitaEvents, MitaExceptions and MitaGeneratedTypes and they are common to all applications. Note that only Exceptions is filled with code, since the application is essentially empty and nothing has to be made available in these header-files.

If the user implements a time event Mita will generate the required resources. With following Mita code

every 1 second {
    println("Hello World");
}

the files MitaTime.h and MitaTime.c are created setting up the ressource in a C format. These two files implement the timers that represent the time events and the header file offers an interface for the rest of application.

Time is a singleton resource, hence it is only created once. <!— On the other hand, LED resources are not singletons and can be created multiple times. The resulting file names are thus different.

For example, when the following Mita code is implemented:

setup myLedInstance : LED {
    var control = light_up(Red);
}

then the file ConnectivityLEDMyLedInstance.c and the corresponding header file are created. –>

All resource files in the base directory all implement the two functions Setup and Enable, prefixed with the respective filename.

The distinction between Setup and Enable is an important feature of the Mita architecture. Every resource is first set up, then it is enabled. This allows for certain resources, that may depend on one another, to be enabled in an orderly fashion.

For example, the Wi-Fi resource needs to be enabled before anything can be sent over Wi-Fi. That means, anything that depends on Wi-Fi will be enabled after the Wi-Fi resource. As such, the enable function for a HTTP Client is called after the enable function for a Wi-Fi resource.

Some resources have optional variables. For the above example, the optional variable is control. These variables are referred to as signals. Every such signal has write and read functions, which are also implemented in the resource’s file.

The naming structure for these functions is _ResourceName_SignalName_Write|Read_. Thus, the above example produces the functions ConnectivityLEDMyLedInstance_Control_Write() and ConnectivityLEDMyLedInstance_Control_Read().

main.c

This file starts the application in a bootstrap fashion. It consists of three functions. The primary function is main(), which is also the entry function for the entire C application. The other two functions are Mita_initialize() and Mita_goLive().

During the main-function, the first function call is for systemStartup() as provided by the XDK’s SDK. Then, Mita_initialize() is called. This function calls the setup functions, which were already mentioned in the previous chapter, for the individual resources used in the Mita code. Then, Mita_goLive() is called to enable the resources which were previously set up.

Do note that these two functions are already called in the context of a Command Processor. Everything that happens on the XDK will happen in the context of that Command Processor, which is created during the main-function. Timers are running as well, but they are just used to enqueue other functions to the application’s Command Processor.

application.c

This file contains all the logic that is defined on Mita application level. That means, whenever a reaction to an event is defined, such as follows:

every 1 second {
    println("Hello World");
}

then a function implementing the procedural part of the event is defined in application.c. For the event reaction, the following function is defined:

Retcode_T HandleEvery1Second1(void* userParameter1, uint32_t userParameter2)
{

    BCDS_UNUSED(userParameter1);
    BCDS_UNUSED(userParameter2);

    Retcode_T exception = NO_EXCEPTION;

    printf("Hello World");
    printf("\n");

    return NO_EXCEPTION;
}

Of course, functions are also implemented in application.c. The following function definition in Mita:

function helloWorld() {
    println("Hello World");
}

transpiles to the function definition in application.c:

Retcode_T helloWorld(void* _result)
{

    Retcode_T exception = NO_EXCEPTION;

    printf("Hello World");
    printf("\n");

    return exception;
}

This way, all code that defines the actual logic of the application, i.e. defines what the XDK is doing, is located in one place, for users to modify, if the use case requires advanced implementation that can not be expressed in Mita.

applicationTypes.h

This file contains all compound types that are defined in the Mita Application. Compound types are structs, enums and [Sum Types](#Sum Types). The following Mita code defines three such types:

struct one {
    var x : int32;
}

enum two {
    a, b
}

alt three {
    alpha | beta | gamma : int32
}

which transpiles to the following C definitions in applicationTypes.h:

typedef struct {
    int32_t x;
} one;

typedef enum {
    a,
    b
} two;

typedef enum {
    alpha_e,
    beta_e,
    gamma_e
} three_enum;

typedef struct {
    three_enum tag;
    union Data {
        int32_t gamma;
    } data;
} three;