back to Chris' bitpool

the LCDML homepage

written by Christian Vogelgsang

Purpose

The LCDML (or Liquid Crystal Display Markup Language) is a description language based on XML and used to describe the text that should be displayed on a LCD. It supports both static and dynamic text messages and bar charts and allows to implement all kinds of (complex) status output on a display. A LCDML player (e.g. iowlcdml in the iowlcd package) uses a LCDML input file and then displays the pages defined there. Each page will be displayed for a specified duration, then the next page is displayed. This allows to use LCDML to describe lots of status messages on different pages and its output will be displayed with time multi-plexing.

Each page defined in LCDML contains regions. The regions are rectangular boxes defined on the text screen on the LCD where text output can be placed. A region is by default static, i. e. the contents displayed is generated if the page is constructed and keeps its contents until the page is no longer displayed. Other regions can be defined to be updated more often during the display of the page. These dynamic regions allow to update its contents more often during the lifetime of a page.

In a region text and bar charts can be defined. A text can be either defined directly, read from a file, or be the output of a shell command. The last method is very useful for creating status outputs defined in LCDML. The shell command is executed each time the region is updated. Similar to text output, the bar charts can be defined in regions, too. Both vertical and horizontal bars arranged in groups are supported. The values displayed in a bar can also be generated from a command output. LCDML allows to filter command output with regular expressions to extract the values from verbose output.

As a convenience and performance feature, the command output can be also stored as strings. The strings can then be used and filtered for different values and applied in different texts and bars in a regions without re-evaluating the command.

iowlcdml.pl - The LCDML player for Linux

In the iowlcd package the iowlcdml.pl, a LCDML player is available. The file can be run with:

iowlcdml.pl -d<debug level> -f<file.xml>

Without any parameters, the file lcdml.xml is searched in the current directory. Have a look at the provided sample file to find out more about LCDML syntax. The debug level allows to output processing information on the console if the pages are evaluated and displayed. Values ranging from 1 to 6 gradually increase the level of verboseness.

LCDML Specification

1. Display Block

Each LCDML is defined by a single display block embedded in the file. A display block defines which hardware output device controls the LCD and which size the display has. Furthermore a "loop" parameter allows to define how often all the pages are display. A value of -1 will loop the pages for ever (ideally for a server status display)

<display device="0" size="20,4" loop="10">
  ... pages defined here ...
</display>

This example defines a valid LCDML file and describes an LCD connected at device 0 (for the iowlcdml player the number denotes the IOWarrior device number). The display has a size of 20 chars in 4 lines. The pages defined in the display block are looped for 10 times.

2. Page Block

A display block can contain any number of pages. They are displayed in the given order. The duration parameter specifies for how long the page will be displayed. This value is given in seconds and fractional values are allowed. The timestep parameter defines the finest granularity of updates that will be possible for regions defined inside the page. Thus the timestep value must always be smaller or equal to the duration value. Finally the clear parameter allows to specify if the display should be cleared before the regions of the page are rendered.

<page duration="5" timestep="1" clear="1">
  ... regions defined here ...
</page>

This example defines a page that will be displayed for five seconds. Dynamic regions inside the page can update their contents every second and the display should be cleared before the page is displayed. If parameters are omitted then default values are used (duration=5, timestep=duration, clear=1).

3. Region Block

A region is a rectangular region on the display. With the pos and size parameters this rectangle is defined. If no values are given then the full size of the display is used. Finally the update parameter defines if the region is a static region (update="0") or a dynamic one (update>0). A static region is only rendered if the page is displayed initially. A dynamic region is updated in intervals defined by the timestep of the page. If update is one then at each timestep this region is updated. If the value is two then the update occurs only every two timesteps and so on...

<region pos="1,2" size="10,3" update="1">
  ... string, text or bars defined here ...
</region>

This example defines the region rectangle at position (x=1,y=2) with a width of 10 and height of 3 characters. The region will be updated in each timestep of the page.

4. Text Block

A text block will actually render some text inside a region window on the display. First a position defines the relative coordinate in the region window where the text output starts. The text block encloses the text content that will be used on the page. The parameters type, filter and keeplf allow to modify the contents specified in the LCDML file and to render dynamic content.

The type tells how the contents is interpreted. The default type is "string" and uses the text directly for display. Only variable substitution (see below) and replacement of "\\", "\n", and "\xXX" is performed. The "shell" type uses the text content and executes it as a command line in a shell. The output of the command is rendered on the display. The "file" type uses the text content as a path name, reads the file and renders the output on the display.

The filter parameter allows to replace the contents returned by the contents type with a regular expression filtered version. This helps to extract values from command output. The regular expression can be a Perl style regexp. It should contain a single brace section that will be replaced in the output.

The keeplf flag allows to modfiy the linefeed handling if external data is read in. On default all command output is concatenated into a single string and all linefeeds are replace with spaces. It the keeplf flag is set to one then the linefeeds are preserved for multiline output. Please note that for filtering linefeeds are often not suitable.

<text pos="0,0">a simple text</text>
<text pos="2,1">a multi\nline\ntext</text>
<text pos="10,1" type="shell" keeplf="1">ls | head -2</text>
<text pos="15,2" type="file" filter="^.*[daemon](\d+).*$">/var/log/mydaemon.log</text>

These examples show a wide range of usages of the text block. This first one is a single text block. The second one shows a multilline text that will be aligned to the given position (i.e. all new lines also start at position 2). The third generates a dynamic text field. The ls command is run on the shell and its output is printed on the display. The keeplf flag ensures that the line breaks are reproduced correctly. Due to the region size it is possible that the output is cropped in each line. The last example reads a log file and filters the output with a regular expression.

5. Bars Block

A bars block defines a set of either horizontal or vertical bars with different values. The dir parameter specifies with "h" a horizontal and with "v" a vertical bar. The size value gives the width or height of the bar. A pos parameter defines the relative position in the region. The bars itself are defined by bar blocks embedded in the bars block.

<bars pos="1,2" dir="h" size="10">
  ... bar blocks ...
</bars>

This example defines a horizontal bars group starting at position (1,2) with width 10.

Each bar is described by a bar block. The content in a bar block is evaluated similar to a text block. The type, filter, keeplf parameters are available here, too. Make sure that the textual result of the contents can be converted into a scalar value. This value will be used to draw the bar.

The value range that can be displayed in a bar is defined by the direction and the size. A horizontal bar can display 5 values in a char. A vertical bar can display 8 values per character. With the size the value range is defined.

The given value can now be converted in two ways to yield the bar value. The absolute scaling can be activated by passing the "offset" and/or "scale" parameter. With bar_value = offset + scale * value the output value for the bar will be calcualted directly. You have to find your self suitable values for offset and scale.

By specifying a "min" and "max" value, the bar_value will be calculated automatically to represent the full range of min to max. This is in most cases much more convenient.

<bar max="60" type="shell">date +%S</bar>
<bar scale="10" type="file">/my/number/file</bar>

This example shows the two typical uses of bars. The first one is automatically scaled to the range of 0..60 and displays the current second of the date command. The other bar directly reads the value of a file and scales it. This resulting value is then directly used for display.

6. Strings

Often dynamic values are extracted by calling external commands. Their output is filtered to extract different values for text and bar blocks in a region. In the current description of LCDML each text and bar block needs to use the "shell" type and execute the command itself. This may be very time consuming if the output of a single command is used for many text and bars blocks. That's the reason why string blocks were introduced to buffer command output in a string variable.

String blocks inside a region allow to define an output similar to a text or bar region and store the text output with a variable name. These string variables can then be used in a text and bar block by calling the variable name with a $ sign:

<region>
 <string name="secs" type="shell">date +%S</string>

 <text pos="0,0">secs: $secs</text>

 <bars dir="v" pos="0,1" size="8">
  <bar max="60">${secs}</bar>
 </bars>
</region>

This example defines a string called "secs" that is later on used in a text block (combined with other text) and also in a bar. As you can see in the second example, the variable name can be also enclosed into curly braces {}. Note: a $$ is replaced with a single $ sign.

Strings can be also used in defining new strings. This technique combined with filtering allows to easily extract numerous values from a single command output:

<string name="top" type="shell">top -b -n1 | head -5</string>
<string name="cpuusr" filter="^.*[^\d](\d+\.\d+)% us.*$">$top</string>
<string name="cpusys" filter="^.*[^\d](\d+\.\d+)% sy.*$">$top</string>
<string name="cpuidle" filter="^.*[^\d](\d+\.\d+)% id.*$">$top</string>

This example first calls the top command and stores the top five rows of output in the variable "top". (Note: since we do not use keeplf here, the whole output will be merged into a single line - suitable for matching). The other variables are now defined by filtering the top variable with different expressions. Finally we have values for CPU user, system and idle time.

Annotated Example LCDML File

This section shows you a full-featured LCDML file and explains some techniques. I use this file for my server status reporting. I have defined some pages, each one summarizing one aspect of the system.

Ok, let's start with the display block:

<display device="0" size="20,4" loop="-1">

I have a 20x4 LCD connected at IOWarrior device 0 and the display should run forever (if that matches the uptime of my server :)). Now let's define the first page that is displayed for 5 seconds and displays the hostname, the time and some info on ethernet interface eth0:

<page duration="5" timestep="1">
  <region>
    <string name="hostname" type="shell">hostname</string>
    <text pos="0,0">$hostname\neth0:\nRX byte:\nTX byte:</text>
  </region>
  <region update="1">
    <string name="time" type="shell">date +%H:%M:%S</string>
    <string name="eth0" type="shell">/sbin/ifconfig eth0</string>
    <string name="if" filter="^.*inet addr:([\d\.]+).*$">$eth0</string>
    <string name="rx" filter="^.*RX bytes:(\d+).*$">$eth0</string>
    <string name="tx" filter="^.*TX bytes:(\d+).*$">$eth0</string>
    <text pos="12,0">$time</text>
    <text pos="8,1">$if\n$rx\n$tx</text>
  </region>
</page>

This page uses two regions: a static one and a dynamic one that is updated every second. The static region displays some label text and the hostname (assuming that your host keeps its name). The dynamic region fetches dynamic data and displays it. Note the use of strings to avoid numerous external command calls.

The next page displays the CPU usage, both as numbers and with bars. Again the update rate is a second and the total display time is five seconds:

<page duration="5" timestep="1">
  <region>
    <text pos="0,0">CPU</text>
  </region>
  <region update="1">
    <string name="top" type="shell">top -b -n1 | head -5</string><!-- extract the cpu values with a filter from the above string -->
    <string name="cpuusr" filter="^.*[^\d](\d+\.\d+)% us.*$">$top</string>
    <string name="cpusys" filter="^.*[^\d](\d+\.\d+)% sy.*$">$top</string>
    <string name="cpuidle" filter="^.*[^\d](\d+\.\d+)% id.*$">$top</string><!-- write the cpu values in the top row -->
    <text pos="5,0">$cpuusr</text>
    <text pos="10,0">$cpusys</text>
    <text pos="15,0">$cpuidle</text>
    <bars pos="0,1" dir="h">
      <bar max="100">$cpuusr</bar>
      <bar max="100">$cpusys</bar>
      <bar max="100">$cpuidle</bar>
    </bars>
  </region>
</page>

Again, we split up the page into two regions: a static and a dynamic one. The dynamic one now first calls a top pipe to process the command output. Then various values are extracted with filters and stored in new string variables. These strings are then used for both a textual representation and graph drawing.

The final page in our little example displays the disk free of some important drives on my server. This page shows how the keeplf option can be used to directly use preformatted output of a comand call:

<page duration="5" timestep="1">
  <region update="1">
    <text pos="0,0" type="shell" keeplf="1">df -t ext2 -t reiserfs | tail +2 | gawk '{ print $1 " " $5 " " $6 }' | sed -e 's,/dev/,,'</string>
  </region>
</page>

Besides the rather long command line to extract and format the df output to fit on a small LCD, the integration into LCDML is rather simple. Finally don't forget to close your file with:

</display>

That's it for my explanations on LCDML... I hope you enjoyed it and find it also useful...

If there are any questions, remarks or annotations then don't hesitate to contact me... -> Back to Bitpool