8. Applets

Over time it became clear that some use cases may benefit from using a small helper programs to perform simple housekeeping tasks, usually run by the user. Plumcore was challenged to run them occasionally, on demand, without introducing much burden to the running system. The obvious place to run them was directly in the CLI service. Which means:

  • it is hard to build those helpers selectively, eg. depending on the service they are helping

  • the CLI service started to have unmaintainable amount of dependencies

  • it is simply a very flawed concept

An Applet interface was defined describing what is the helper program doing (now called an applet), how to use it, how to run it and how to communicate with it when it is running.

8.1. What are applets

Applet is a small helper program, generally performing a simple task to help the user of the system. They directly interact with the user by means of a standard input and output. Note that there is no standard error output. Instead, an applet gets a reference to a Logger instance which can be used to log events and errors.

Applets are run in the context of the parent service. May it be the CLI service (running the applet with a command) or any other service running in the system and providing means of user interaction (eg. a network server).

They are usually compiled together with the main firmware and saved in the device flash memory. In the interpreted form, they are included in the firmware either as a bytecode or as a source code and run in runtime using an interpreter service (ecmascript, Wren, etc.)

The Applet interface contains:

  • name of the applet as a string

  • human readable help formatted as restructured text

  • entry point of the applet (a compiled function in case of compiled applets, a function name for interpreted applets)

  • reference to the stdin/stdout Stream, which the parent service is properly handling

  • reference to the Logger interface

  • and a flag whether the applet wants to be run in a dedicated thread (together with the required stack size)

It should be noted that, depending on the applet code requirements, it may not be possible to run the particular applet because of lack of resources. Applets are not considered essential parts of the system and they are treated as such. No memory is preallocated during the initialisation phase of the system as is mandated for essential services.

8.2. Tutorial: create your own applet

Applets are residing in their own subdirectories in the applets/ top level directory. The applet directory contains:

  • SConscript file defining source code of the applet which should be compiled

  • KConfig file with a configuration directive allowing the user to select if the particular applet should be included in the final firmware

  • a main applet header

  • applet source code

Decide on the applet name. We will call it TEMPCO_CALIBRATION. First create the applet directory tempco-calibration with a SConscript file inside. Scons will check if a configuration variable APPLET_TEMPCO_CALIBRATION is set and compile all C source files in the current directory if yes. It is a recommended practice to name applet configuration variables starting with APPLET_.

1Import("env")
2Import("objs")
3Import("conf")
4
5if conf["APPLET_TEMPCO_CALIBRATION"] == "y":
6    objs.append(env.Object(File(Glob("*.c"))))

Continue with creating a KConfig file with a configuration tree fragment. It should contain a single bool configuration option enabling the applet compilation. You may add any other config options you want too, it is a good practice to hide them in a dedicated menu visible only if the applet is enabled.

1config APPLET_TEMPCO_CALIBRATION
2    bool "Output adc-composite temperature coeff calibration data as a CSV"
3
4menu "tempco-calibration configuration"
5    depends on APPLET_TEMPCO_CALIBRATION
6
7endmenu

Add a basic main header file tempco-cal.h. There is nothing specific inside:

1#pragma once
2
3#include <interfaces/applet.h>
4
5extern const Applet tempco_calibration;

Add some C source to a tempco-cal.c file:

 1#include <stdint.h>
 2#include <main.h>
 3#include <interfaces/applet.h>
 4
 5#include "tempco-cal.h"
 6
 7static applet_ret_t tempco_calibration_main(Applet *self, struct applet_args *args) {
 8
 9    /* Applet code goes here */
10
11    return APPLET_RET_OK;
12}
13
14const Applet tempco_calibration = {
15    .executable.compiled = {
16        .main = tempco_calibration_main
17    },
18    .name = "tempco-calibration",
19    .help = "Output temperature and processed channel data as a CSV to ease temperature coefficient computation & calibration"
20};