
[tocstart]: # (toc start)

  * [Introduction](#introduction)
    * [MUI](#mui)
    * [Graphical User Interface](#graphical-user-interface)
  * [Minimal Example](#minimal-example)
    * [Content (Minimal Example)](#content-minimal-example)
    * [MUIMinimal.ino](#muiminimalino)
    * [Includes and Constructors](#includes-and-constructors)
    * [MUIF](#muif)
    * [ Form Definition String (FDS)](#-form-definition-string-fds)
    * [Arduino Setup](#arduino-setup)
    * [Arduino Loop: Draw User Interface](#arduino-loop-draw-user-interface)
    * [Arduino Loop: Map and Execute Events](#arduino-loop-map-and-execute-events)
    * [Arduino Loop: Inactive User Interface](#arduino-loop-inactive-user-interface)
    * [Minimal Example Summary](#minimal-example-summary)
  * [MUICountDown Example](#muicountdown-example)
    * [Content (Count Down)](#content-count-down)
    * [MUICountDown.ino](#muicountdownino)
    * [Event Mapping and Action Execution](#event-mapping-and-action-execution)
    * [Field Processing Functions (MUIF)](#field-processing-functions-muif)
    * [Form Definition String (FDS)](#form-definition-string-fds)
    * [Leave and Enter the Menu System](#leave-and-enter-the-menu-system)
  * [Stopwatch Example](#stopwatch-example)
    * [Content (Stopwatch)](#content-stopwatch)
    * [MUIStopwatch.ino](#muistopwatchino)
    * [FDS](#fds)
  * [Waveform Generator Example](#waveform-generator-example)
    * [Content](#content)
    * [Code ](#code-)
    * [Array Editor for the Waveform](#array-editor-for-the-waveform)
    * [Conclusion](#conclusion)

[tocend]: # (toc end)

# Introduction

## MUI

The monochrome minimal graphical user interface (MUI) is a small addon for u8g2 library.
The purpose is to create interactive user interfaces with u8g2. 

![ref/mui_animation.png](ref/mui_animation.gif)


Key features:

 * Event Processing
 * User interface drawing
 * Many predefined user elements
 * User interface definition in PROGMEM area for AVR systems
 * Static menu definitions
 * Small code size (4 to 8 KB)
 * Low RAM (<0.5 KB)
 * Optimized for monochrome displays
 * MUI Reference: https://github.com/olikraus/u8g2/wiki/muiref
 
 
## Graphical User Interface

### Generic Approach

Steps to implement a graphical user interface::

 1. Draw the user interface on a display. 
 2. Detect user input events (like a button press)
 3. Map the user input event to a user interface action (like moving the focus or changing a value)
 4. Execute the user interface action (and continue with 1.)
 
### User Interface vs. other Information

The user interface and your own display procedures
need to coexist. Both parts may share the same display:

  1. Check if the user interface is active or inactive
  2. If user interface is active: Draw UI, handle events, execute actions (see above)
  3. If user interface is inactive: Draw custom code, check whether user wants to return to the menu system

It is also possible to place custom graphics into the user interface. 
The [Waveform Generator](#waveform-generator-example) will show more details how user interface and custom graphics can be used together.

### Definitions

Definitions for some terms, used in this documentation (see also the more detailed definitions [here](muiref#definitions)).

  * **Form**: A form contains multiple fields and is described in FDS.  
  * **Field**: A field is placed at a certain position on the form in FDS. Each field must have a corresponding MUIF entry in the MUIF list.        
  * **MUIF** The MUI Function declares properties of a field. A field on the form referes to a MUIF. All MUIF must be part of a MUIF list.
  * **FDS** The "Form Definition String" defines all the forms with their fields.

# Minimal Example

## Content (Minimal Example)
  * MUI setup for U8g2
  * Preconditions for the user interface: MUIF list and FDS
  * How to draw the menu
  * How to process events
  * How to deactivate the user interface
 
## MUIMinimal.ino

This is a minimal example for MUI together with U8g2. 
The code will assume the dogs102 shield (http://shieldlist.org/controlconnection/dogs102).

[Full Arduino code of for this example](https://github.com/olikraus/u8g2/blob/master/sys/arduino/u8g2_page_buffer/MUIMinimal/MUIMinimal.ino)

![ref/mui_select_me.png](ref/mui_select_me.png)


``` C++
#include <Arduino.h>
#include <U8g2lib.h>
#include <MUIU8g2.h>

U8G2_UC1701_EA_DOGS102_1_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);
MUIU8G2 mui;

muif_t muif_list[] = {  
  MUIF_VARIABLE("BN", NULL, mui_u8g2_btn_exit_wm_fi)
};

fds_t fds_data[] = 
MUI_FORM(1)
MUI_XYT("BN", 64, 30, " Select Me ")
;

void setup(void) {
  u8g2.begin(/* menu_select_pin= */ 5, /* menu_next_pin= */ 4, /* menu_prev_pin= */ 2, /* menu_up_pin= */ U8X8_PIN_NONE, /* menu_down_pin= */ U8X8_PIN_NONE, /* menu_home_pin= */ 3);  
  mui.begin(u8g2, fds_data, muif_list, sizeof(muif_list)/sizeof(muif_t));
  mui.gotoForm(/* form_id= */ 1, /* initial_cursor_position= */ 0);
}

uint8_t is_redraw = 1;
void loop(void) {
  u8g2.setFont(u8g2_font_helvR08_tr);
  if ( mui.isFormActive() )
  {
    /* menu is active: draw the menu */
    if ( is_redraw ) {
      u8g2.firstPage();
      do {
          mui.draw();
      } while( u8g2.nextPage() );
      is_redraw = 0;
    }
    /* handle events */
    switch(u8g2.getMenuEvent()) {
      case U8X8_MSG_GPIO_MENU_SELECT:
        mui.sendSelect();
        is_redraw = 1;
        break;
    }
  } else {  
    /* menu not active: show something else */
    u8g2.firstPage();
    do {
        u8g2.setCursor(0,20);
        u8g2.print(millis());
    } while( u8g2.nextPage() );
  } /* mui.isFormActive() */
} /* loop */

```

The following sections will discuss the code step by step.

## Includes and Constructors

``` C++
#include <Arduino.h>
#include <U8g2lib.h>
#include <MUIU8g2.h>

U8G2_UC1701_EA_DOGS102_1_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);
MUIU8G2 mui;
```

After the usual includes create the u8g2 object and the new `mui` object.
The `mui` object will not receive any arguments. This is done later with the `.begin` function.

## MUIF


``` C++
muif_t muif_list[] = {  
  MUIF_VARIABLE("BN", NULL, mui_u8g2_btn_exit_wm_fi)
};
```

This is a list of all the MUIFs (field processing functions), which are required for the menu. 
In this case there is only one MUIF,
because there will be only one element in the menu system. 

Available MUIFs are listed in the MUIF [reference](muiref).
From left to right,
the arguments for the MUIF are:

 * `"BN"`: A unique identifier. This MUST be a string with exactly two chars. This identifier 
   is later used in the field definitions (FDS).
 * `NULL`: This is a pointer to a data value. `NULL` means, that there is no variable assigned 
  to this field.
 * `mui_u8g2_btn_exit_wm_fi`: This is the callback function 
  for the field. It will render the button on the display and handle the select event 
  for the corresponding field. The name usually reflects the behaviour ("exit" = leave menu system) 
  and the visual representation ("fi" = "with frame, inverted text"). 
  The [MUIF reference](muiref) will include all predefined callback functions.

Details for this MUIF can be found [here](https://github.com/olikraus/u8g2/wiki/muiref#exit-button).

It is possible to define custom MUIFs. Usually a new custom MUIF extends the functionality of an existing MUIFs. 
The [Stopwatch Example](#stopwatch-example) and the [Waveform Generator Example](#waveform-generator-example) will discuss this in more detail.

  
##  Form Definition String (FDS)

``` C++
fds_t fds_data[] = 
MUI_FORM(1)
MUI_XYT("BN", 64, 30, " Select Me ")
;
```

The form definition string (FDS) defines all forms along with their fields. Each form must have a number between 1 and 255.
In this
case there is only one form with the number `1`. A form always starts with the 
macro `MUI_FORM`.

The field is defined with the macro `MUI_XYT`, which requires the following 
arguments:
 * `"BN"`: The MUIF identifier for the MUIF callback which should render and handle this field. A MUIF with the "BN" keyword must exist in the MUIF list.
 * `64`: The X reference position
 * `30`: The Y reference position
 * `" Select Me "`: A UTF8 string, which is rendered as button text on the display.
 
FDS and MUIF are connected with each other:
Each identifier in FDS (like `"BN"`) must also appear in the MUIF list. If the identifier is
used in FDS, but not found  in the MUIF list, then the field will not appear on the form.

Note: The FDS internally is a string, so there must be exactly only one ';' at the end.  

## Arduino Setup

``` C++
void setup(void) {
  u8g2.begin(/* menu_select_pin= */ 5, /* menu_next_pin= */ 4, /* menu_prev_pin= */ 2, /* menu_up_pin= */ U8X8_PIN_NONE, /* menu_down_pin= */ U8X8_PIN_NONE, /* menu_home_pin= */ 3);  
  mui.begin(u8g2, fds_data, muif_list, sizeof(muif_list)/sizeof(muif_t));
  mui.gotoForm(/* form_id= */ 1, /* initial_cursor_position= */ 0);
}
```

In this case U8g2 is configured to check button events from the DOGS102 shield. 
However any other button library can also be used.

In the second line, the mui object is connected to u8g2 object, FDS list and MUIF list. The last 
argument of the mui begin statement is the number of entries in the MUIF list.
This is calculated by the compiler with help of the `sizeof` operator: `sizeof(muif_list)/sizeof(muif_t))`.

The last statement (`mui.gotoForm`) will setup the menu system and
jump to form `1` as initial menu form.  Obviously the form with the number `1` should exist in FDS (`MUI_FORM(1)`)
otherwise `mui.gotoForm` will fail and the menu is not activated.

The focus cursor will be placed on the first interactive field (`initial_cursor_position`).

## Arduino Loop: Draw User Interface

``` C++
uint8_t is_redraw = 1;          // indicates whether the menu should be drawn
void loop(void) {
  u8g2.setFont(u8g2_font_helvR08_tr);
  if ( mui.isFormActive() )
  {
    /* menu is active: draw the menu */
    if ( is_redraw ) {                  // is any redraw of the menu required?
      u8g2.firstPage();
      do {
          mui.draw();
      } while( u8g2.nextPage() );
      is_redraw = 0;                    // menu is now up to date, no redraw required at the moment
    }
```

The `is_redraw` variable will be used to avoid unrequired redraws. Drawing the menu
will happen only if `is_redraw` is not 0 (which is the initally the case, because `1` is 
assigned as default value)

The menu system is also active because of the previous `mui.gotoForm` statement: 
`mui.isFormActive()` will return true.

`mui.draw()` is called to draw the complete current state of the user interface and finally
the `is_redraw` is cleared to ensure that the menu is not rendered again without change 
in the user interface.

## Arduino Loop: Map and Execute Events

``` C++
    /* handle events */
    switch(u8g2.getMenuEvent()) {
      case U8X8_MSG_GPIO_MENU_SELECT:
        mui.sendSelect();
        is_redraw = 1;                          // The select statement will change something: Enforce a redraw
        break;
    }
```

After drawing the user interface the code will check for any user button press events.
If the select button is pressed, then the select action (`mui.sendSelect()`) will be executed.

Usually a user interface has to check and act on multiple events, but for this 
minimal example only one event is sufficient.

Once the event has been detected, the `is_redraw` flag is set to enforce a redraw of the 
user interface.

## Arduino Loop: Inactive User Interface

``` C++
  } else {  
    /* menu not active: show something else */
    u8g2.firstPage();
    do {
        u8g2.setCursor(0,20);
        u8g2.print(millis());
    } while( u8g2.nextPage() );
  } /* mui.isFormActive() */
```

The `Select Me` button will call the `mui_u8g2_btn_exit_wm_fi`
function. If `mui_u8g2_btn_exit_wm_fi` receives a select
action, then the user interface menu will be deactivated. `isFormActive` will return
false and the program will execute the above code.

This code will just display the current `millis()` forever (until next reset).

## Minimal Example Summary

This completes the minimal MUI example. The following topics had been covered:
 * How to define a MUIF (field function) list
 * How to define a form with a single field
 * How to connect MUI to U8g2
 * How to display an initial form
 * How to draw the form (user interface) and how to handle user input events
 
The following topics are not covered or may require more details:
 * How to handle sufficient user input events for a usefull user interface
 * How to build a more complex menu system
 * How to connect and exchange data between the user interface and the rest of your code
 * How to define a custom callback function for a MUIF entry
 
 
# MUICountDown Example

## Content (Count Down)
  * More details on user interface events
  * Using the style feature
  * More MUIF examples
  * How to re-enter the menu system

## MUICountDown.ino

The code examples in the following sections are taken from the "MUICountDown" example.

[Full Arduino code for this example](https://github.com/olikraus/u8g2/blob/master/sys/arduino/u8g2_page_buffer/MUICountDown/MUICountDown.ino)

![ref/mui_count_down.png](ref/mui_count_down.png)


## Event Mapping and Action Execution

``` C++
    /* handle events */
    switch(u8g2.getMenuEvent()) {
      case U8X8_MSG_GPIO_MENU_SELECT:
        mui.sendSelect();
        is_redraw = 1;
        break;
      case U8X8_MSG_GPIO_MENU_NEXT:
        mui.nextField();
        is_redraw = 1;
        break;
      case U8X8_MSG_GPIO_MENU_PREV:
        mui.prevField();
        is_redraw = 1;
        break;
    }
```

The user can jump from one interactive field to the next/previous interactive field with 
the `mui.nextField()`/`mui.prevField()` function. An interactive field is activated by
sending the select action (`mui.sendSelect()`).

Additional buttons could be used to jump to a specific form 
by using `mui.gotoForm()`.

## Field Processing Functions (MUIF)

``` C++
muif_t muif_list[]  MUI_PROGMEM = {
  /* normal text style */
  MUIF_U8G2_FONT_STYLE(0, u8g2_font_helvR08_tr),
  
  /* Leave the menu system */
  MUIF_VARIABLE("LV",&exit_code,mui_u8g2_btn_exit_wm_fi),
  
  /* input for a number between 0 to 9 */
  MUIF_U8G2_U8_MIN_MAX("IN", &number_input, 0, 9, mui_u8g2_u8_min_max_wm_mse_pi),
  
  /* MUI_LABEL is used to place fixed text on the screeen */
  MUIF_LABEL(mui_u8g2_draw_text)
};
```

The MUICountDown example contains several MUIF entries.
MUIF entries are connected with a field macro inside FDS if both entries share the same identifier (two-char or number, see table).

| MUIF |  FDS Macro | Identifier |
|---|---|---|
| MUIF_U8G2_FONT_STYLE | MUI_STYLE | single number, no double quotes |
| MUIF_VARIABLE | MUI_XYT | two chars in double quotes |
| MUIF_U8G2_U8_MIN_MAX | MUI_XY | two chars in double quotes |
| MUIF_LABEL | MUI_LABEL | no identifier required |


## Form Definition String (FDS)

``` C++
fds_t fds_data[] MUI_PROGMEM = 
MUI_FORM(1)
MUI_STYLE(0)    // select "u8g2_font_helvR08_tr" (see MUIF above)
MUI_LABEL(5,12, "Countdown Time")
MUI_LABEL(5,30, "Seconds:")
MUI_XY("IN",60, 30)
MUI_XYT("LV",64, 59, " OK ")
;
```
The form definition string defines all the forms. One form describes the position of each field on the display:

![ref/mui_count_down.png](ref/mui_count_down.png)

 * A form always starts with the `MUI_FORM()` command. Each form must have a unique number (here: 1).
 * Each form should be followed by `MUI_STYLE()` command, which defines a suitable font for the following commands.
 * The `MUI_LABEL` will place a read only text at the specified postion.
 
## Leave and Enter the Menu System

The function `mui.isFormActive()` checks whether there is an active form (menu). The arduino loop should look like this:

``` C++
void loop(void) {
  if ( mui.isFormActive() ) {
    // draw the menu and handle user events
  } else {
    // display your own content, do something else
  }
```

The function `mui.leaveForm` will close the current form. 
The "LV" button (`mui_u8g2_btn_exit_wm_fi` MUIF) will call `mui.leaveForm` to close the current form and the complete menu system.
As a result, `mui.isFormActive()` will return false.
However: The content on the display is not modified and probably still displays the last form. It is the responsibility of the user code to clear the display.
This could be done by checking whether the form is inactive after handling all the user events:

``` C++
void loop(void) {
  if ( mui.isFormActive() ) {
    // draw the menu and handle user events
    if ( mui.isFormActive() == false ) {
      // clear display
    }
  } else {
    // display your own content, do something else
  }
```

Jumping back to the menu system is simple: Just call `mui.gotoForm()`. `mui.isFormActive()` will return true after successful call to `mui.gotoForm()`.

``` C++
void loop(void) {
  if ( mui.isFormActive() ) {
    // draw the menu and handle user events
    if ( mui.isFormActive() == false ) {
      // clear display
    }
  } else {
    // display your own content, do something else
    if ( all_is_done )
      mui.gotoForm(/* form_id= */ 1, /* initial_cursor_position= */ 0);      
  }
```
 
# Stopwatch Example

## Content (Stopwatch)
  * How to display custom data on a form
  * Form change events
  * Custom MUI functions
  
## MUIStopwatch.ino

The code examples in the following sections are taken from the "MUIStopwatch" example.

[Full Arduino code for this example](https://github.com/olikraus/u8g2/blob/master/sys/arduino/u8g2_page_buffer/MUIStopwatch/MUIStopwatch.ino)

Implementation of the Stopwatch:
 * Two forms: Form 1 for the stopped state of the timer and form 2 for the running timer
 * Transition from form 1 to 2: Start the timer
 * Transition from form 2 to 1: Stop the timer
 
## FDS

```
fds_t fds_data[] = 
MUI_FORM(1)
MUI_AUX("SO")                      // this will stop the stop watch time once this form is entered
MUI_STYLE(0)
MUI_LABEL(5,12, "Stopwatch")
MUI_XY("CT", 5, 24)
MUI_XYAT("GO",20, 36, 2, " Start ")     // jump to the second form to start the timer

MUI_FORM(2)
MUI_AUX("ST")                      // this will start the stop watch time once this form is entered
MUI_STYLE(0)
MUI_LABEL(5,12, "Stopwatch")
MUI_XY("CT", 5, 24)
MUI_XYAT("GO",20, 36, 1, " Stop ")      // jump to the first form to stop the timer
;
```

There are three custom fields to implement the stopwatch:
  * `MUI_XY("CT", 5, 24)`: A field which will draw the current stopwatch timer.
  * `MUI_AUX("SO")`: Invisible field which will receive a form change event to stop the timer.
  * `MUI_AUX("ST")`: Invisible field which will receive a form change event to start the timer.

### MUIF

The MUIF callback will receive several messages. A custom code can react on each message.

The most important messages are:
| Message | Description | Return Value |
|---|---|---|
| `MUIF_MSG_DRAW` | The field content should be drawn on the screen | 0 |
| `MUIF_MSG_FORM_START` | A new form is entered. | 0 |
| `MUIF_MSG_FORM_END` | A current form is closed. | 0 |
| `MUIF_MSG_CURSOR_SELECT` | Active fields only: "Select" button was pressed by the user. | 0 |

Here is the code, which does the timer start, stop and visualization:

``` C++
long stop_watch_timer = 0;                      // stop watch timer 1/100 seconds 
long stop_watch_millis = 0;                      // millis() value, when the stop watch was started
uint8_t is_stop_watch_running = 1;          // defines the current state of the stop watch: running or not running

/* draw the current stop watch value */
uint8_t mui_draw_current_timer(mui_t *ui, uint8_t msg) {
  if ( msg == MUIF_MSG_DRAW ) {
      u8g2.setCursor(mui_get_x(ui), mui_get_y(ui));
      u8g2.print(stop_watch_timer/1000);
      u8g2.print(".");
      u8g2.print((stop_watch_timer/10)%100);
  }
  return 0;
}

/* start the stop watch */
uint8_t mui_start_current_timer(mui_t *ui, uint8_t msg) {
  if ( msg == MUIF_MSG_FORM_START ) {
      is_stop_watch_running = 1;
      stop_watch_millis = millis();
      stop_watch_timer = 0;
  }
  return 0;
}

/* stop the stop watch timer */
uint8_t mui_stop_current_timer(mui_t *ui, uint8_t msg) {
  if ( msg == MUIF_MSG_FORM_START )
      is_stop_watch_running = 0;
  return 0;
}
```

The above custom callbacks are then registered in the MUIF table:

```
muif_t muif_list[] = {
  /* normal text style */
  MUIF_U8G2_FONT_STYLE(0, u8g2_font_helvR08_tr),

  /* custom MUIF callback to draw the timer value */
  MUIF_RO("CT", mui_draw_current_timer),
  
  /* custom MUIF callback to start the stop watch timer */
  MUIF_RO("ST", mui_start_current_timer),

  /* custom MUIF callback to end the stop watch timer */
  MUIF_RO("SO", mui_stop_current_timer),

  /* a button for the menu... */
  MUIF_BUTTON("GO", mui_u8g2_btn_goto_wm_fi),
  
  /* MUI_LABEL is used to place fixed text on the screeen */
  MUIF_LABEL(mui_u8g2_draw_text)
};
```

`MUIF_RO` (RO=read only) registers a simple function, which does not get any further data, but receives all the messages.
The above registered functions `mui_start_current_timer` and `mui_stop_current_timer` are placed as hidden fields on the form with `MUI_AUX()`.
`MUI_XY` is used for `mui_draw_current_timer` to provide the `x` and `y` values for `mui_draw_current_timer`. 

The main loop for this example is a little bit unusual: It will force a permanent screen update once the stopwatch timer runs. 

``` C++
void loop(void) {

  /* check whether the menu is active */
  if ( mui.isFormActive() ) {
    /* if so, then draw the menu */
    if ( is_redraw ) {
      u8g2.firstPage();
      do {
          mui.draw();
      } while( u8g2.nextPage() );
      is_redraw = 0;
    }
    
    /* handle events */
    switch(u8g2.getMenuEvent()) {
      ...
    }
    
    /* update the stop watch timer */
    if ( is_stop_watch_running != 0 ) {
      stop_watch_timer = millis() - stop_watch_millis;
      is_redraw = 1;            // always redraw the screen so that the timer gets updated
    }
      
  } else {
      /* the menu should never become inactive, but if so, then restart the menu system */
      mui.gotoForm(/* form_id= */ 1, /* initial_cursor_position= */ 0);
  }
}
```

# Waveform Generator Example

## Content
  * How to update a data array
  * Custom MUIF for drawing custom data
  * Custom MUIF to control the current array element
 
## Code 

![ref/mui_wg.gif](ref/mui_wg.gif)

 * [Full Arduino code MUIWaveformSimpleRotary.ino](https://github.com/olikraus/u8g2/blob/master/sys/arduino/u8g2_page_buffer/MUIWaveformSimpleRotary/MUIWaveformSimpleRotary.ino)
 * [Full Arduino code MUIWaveformBounce2.ino](https://github.com/olikraus/u8g2/blob/master/sys/arduino/u8g2_page_buffer/MUIWaveformBounce2/MUIWaveformBounce2.ino)

### Waveform Array

The signal waveform has four parts. Each part has a duration and a signal height:
``` C++
struct array_element_struct
{
  uint8_t value;
  uint8_t time;  
};

/* array list  */

#define WAVEFORM_ELEMENT_CNT 4
struct array_element_struct waveform_array[WAVEFORM_ELEMENT_CNT];
```

MUI does not offer a scrollable table which would allow a 2x4 matrix like editing. 
Instead the idea is to introduce a "current-element-position" (`array_edit_pos`) and allow the user to modify the current element (see the animation above).
For the user update, the current element is copied from the array into a local editable element (`array_edit_element`). After the edit operation, the content of the 
edit element (`array_edit_element`) is copied back into the array.
``` C++
/* array editable local copy */

volatile uint8_t array_edit_pos = 0;                            // "volatile" might not be required, but still; array_edit_pos is modified by MUI callbacks and used in the extended MUIF
struct array_element_struct array_edit_element;
```

### Waveform Output

This example can output the waveform to a LED. If the LED is connected to a PWM output, then the brightness of the LED will change according to the defined waveform:
``` C++
    if ( is_redraw == 0 && mui.getCurrentFormId() == 2 )
    {
      ...
      analogWrite(LED_PIN, current_value*15);       // This will only work, if the output pin for the LED supports PWM 
      delay(10);
      is_redraw = 1;                    /* keep doing a redraw, because we want some animation */
    }
```

There is also a custom MUIF, which will show the waveform during the waveform output to the LED:
``` C++
uint8_t muif_waveform_show(mui_t *ui, uint8_t msg)
{
  switch(msg)
  {
    case MUIF_MSG_DRAW:
      current_value = waveform_array_draw(mui_get_x(ui), mui_get_y(ui), WAVEFORM_ELEMENT_CNT, 255, current_time);
      break;
  }
  return 0;
}
```
The above code will place the waveform at the position defined with the FDS (`mui_get_x(ui)` and `mui_get_y(ui)`).

This MUIF is very simple and will only process the `MUIF_MSG_DRAW` event. It is connected to the keyword "WS" with a simple "read-only" macro in the MUIF list:
``` C++
MUIF_RO("WS", muif_waveform_show)
```

The waveform is now available with key "WS" and is placed at position 54/62 in form 2:

``` C++
MUI_FORM(2)
MUI_STYLE(0)

MUI_XYAT("GO", 63, 28, 1, "Stop")

MUI_XY("HL", 0, 45)
MUI_LABEL(3,60, "Waveform")
MUI_XY("WS", 54, 62)
```

## Array Editor for the Waveform

### Update a single Array Element

As mentioned above, there is an extra object to hold the data for the user update.
This data is copied from the array and later stored back. The connection of that extra object to MUI is simple:

``` C++
/* array editable local copy */

struct array_element_struct array_edit_element;
...

muif_t muif_list[]  MUI_PROGMEM = {  
  ...
  MUIF_U8G2_U8_MIN_MAX("AV", &array_edit_element.value, 0, ARRAY_VALUE_MAX, mui_u8g2_u8_min_max_wm_mud_pi),
  MUIF_U8G2_U8_MIN_MAX("AT", &array_edit_element.time, 0, ARRAY_TIME_MAX, mui_u8g2_u8_min_max_wm_mud_pi),
  ...
}
```

In this case the standard MUI macros for U8 are good enough for the update of the element member values.
The editbale fields can now be placed at any position inside the FDS by using the keywords "AV" and "AT".

### Update the current Array Position

The current array position is just a U8 (from 0 to 3). It can be put into the MUIF list with the `MUIF_U8G2_U8_MIN_MAX` macro.
However the desired field function (`mui_u8g2_u8_min_max_wm_mud_pi` in this example) has to be extended to support the copying of the data between array and local editable extra object: 
  * When the form is entered: Copy the data from the current array element to the extra object.
  * When the array position is about to change: Copy the data from the extra object back to the array.
  * When the array position has been changed: Copy the data from the new array position to the extra object.
  * When the form is closed: Copy the data from the extra object back to the array.
  
The following messages will be checked:
| Message | Description | Return Value |
|---|---|---|
| `MUIF_MSG_FORM_START` | A new form is entered. | 0 |
| `MUIF_MSG_FORM_END` | A current form is closed. | 0 |
| `MUIF_MSG_CURSOR_SELECT` | Active fields only: "Select" button was pressed by the user. | 0 |
| `MUIF_MSG_EVENT_NEXT`   | Rotary Encoder right rotation | depends on mud state |
| `MUIF_MSG_EVENT_PREV`   | Rotary Encoder left rotation | depends on mud state|

Checking for `MUIF_MSG_EVENT_NEXT` and `MUIF_MSG_EVENT_PREV` is required only for MUD mode (which is usually used together   with a rotary encoder).
The below example code for the rotary encoder will indeed use the MUD version of the U8 field, so we need to check for the EVENT messages:
``` C++
uint8_t muif_array_edit_pos(mui_t *ui, uint8_t msg)
{
  uint8_t return_value = 0; 
  switch(msg)
  {
    case MUIF_MSG_FORM_START:
      array_edit_element = waveform_array[array_edit_pos];          // copy  array element to the local editable copy
      return_value = mui_u8g2_u8_min_max_wm_mud_pi(ui, msg);        // call the original MUIF
      break;
    case MUIF_MSG_FORM_END:
      return_value = mui_u8g2_u8_min_max_wm_mud_pi(ui, msg);        // finalise the form
      waveform_array[array_edit_pos] = array_edit_element ;         // store the current elements in the array before leaving the form
      break;
    case MUIF_MSG_CURSOR_SELECT:                        // for mse mode
    case MUIF_MSG_EVENT_NEXT:                           // for mud mode
    case MUIF_MSG_EVENT_PREV:                           // for mud mode
      waveform_array[array_edit_pos] = array_edit_element;          // store the modified local copy back to the array
      return_value = mui_u8g2_u8_min_max_wm_mud_pi(ui, msg);        // let MUI modify the current array position
      array_edit_element = waveform_array[array_edit_pos] ;         // load the new element from the array to the local editable copy
      break;
    default:
      return_value = mui_u8g2_u8_min_max_wm_mud_pi(ui, msg);        // for any other messages, just call the original MUIF
  }
  return return_value;
}
```

The return value for this custom MUIF is just taken from the calls to `mui_u8g2_u8_min_max_wm_mud_pi()`.
The code of the MUIF should be almost clear:
When entering the new form, the element is taken from the array:
``` C++
      array_edit_element = waveform_array[array_edit_pos];   // copy local array element to the local editable copy
```
When leaving the form, the data is stored back into the array:
``` C++
      waveform_array[array_edit_pos] = array_edit_element;   // store the local editable copy into the array
```
Whenever the user changes the current array position: Store and load the data:
``` C++
      waveform_array[array_edit_pos] = array_edit_element;   // store the modified local copy back to the array
      return_value = mui_u8g2_u8_min_max_wm_mud_pi(ui, msg);    // let MUI modify the current array position
      array_edit_element = waveform_array[array_edit_pos] ;   // load the new element from the array to the local editable copy
```

This custom function can be now placed in the MUIF list ("AP" keyword):
``` C++
muif_t muif_list[]  MUI_PROGMEM = {  
  MUIF_U8G2_LABEL(),
  MUIF_U8G2_FONT_STYLE(0, u8g2_font_helvR08_tr),

  MUIF_BUTTON("GO", mui_u8g2_btn_goto_wm_fi),
  MUIF_RO("WS", muif_waveform_show),
  MUIF_RO("HL", mui_hline),

  /* waveform editor */
  
  MUIF_RO("DE", muif_waveform_editor_decoration),
  MUIF_RO("AD", muif_waveform_draw),
  MUIF_U8G2_U8_MIN_MAX("AP", (uint8_t *)&array_edit_pos, 0, WAVEFORM_ELEMENT_CNT-1, muif_array_edit_pos),               // the pointer cast avoids warning because of the volatile keyword
  MUIF_U8G2_U8_MIN_MAX("AV", &array_edit_element.value, 0, ARRAY_VALUE_MAX, mui_u8g2_u8_min_max_wm_mud_pi),
  MUIF_U8G2_U8_MIN_MAX("AT", &array_edit_element.time, 0, ARRAY_TIME_MAX, mui_u8g2_u8_min_max_wm_mud_pi),
  MUIF_BUTTON("AG", mui_u8g2_btn_back_wm_fi)
};
```

The final form will include some additional decoration and also has a "Done" button:

``` C++
MUI_FORM(20)
MUI_STYLE(0)
MUI_AUX("DE")
MUI_LABEL(3,15, "Pos:")
MUI_XY("AP", 30, 15)

MUI_LABEL(54,10, "Value:")
MUI_XY("AV", 100, 10)

MUI_LABEL(54,20, "Time:")
MUI_XY("AT", 100, 20)

MUI_LABEL(3,39, "Waveform")
MUI_XY("AD", 54, 41)

MUI_XYAT("AG", 63, 59, 1, "Done")
```

## Conclusion

 * There is no scrollable list in MUI, instead use a "current position" and let the user modify each element of the list.
 * MUIFs can be extended with extra functionality to copy data from and to a list.

