Lesson 13.1. FACES. UI Inputbox & textbox

  • The purpose of this lesson

    Hi! Today we will learn how to create and connect our own graphical user interface elements for M5 FACES. (rice. 1).

    Figure 1

    You must implement two graphical user interface (UI) elements - a text input field (inputbox) and a text output field (textbox). Elements must work with a string data type. The connection process should be as simple as possible for the user.

    As a good example of using UI, we will ask the user for Wi-Fi connection data, host address and perform a GET request in the inputbox, and the result will be displayed in the textbox.

    Short help

    A graphical interface is a set of functional elements that are required for interaction with the user. As these elements appear in various fields of the input/output of text, buttons, checkboxes, sliders, radio buttons and many others. As an example of a graphical interface, let's look at figure 2.

    Figure 2. Macintosh System 6

    Inputbox represents the input area of the text information on the screen with a fixed height of 50 px. The width can be set by the user, but cannot be less than 32 px. At the top is the inscription (for example: Enter user name), note that at the end will automatically be added the symbol ':'. At the bottom is a rectangular area in which the user can enter data from the keyboard. When you focus on this element-the bottom of the backlight.

    Textbox is an area where text information is displayed on the screen. Dimensions can be user-defined, but cannot be smaller than the size of a single character. This element consists of one part. The text fits over the entire area and does not go beyond. There is no focus on this element.

    List of components for the lesson

    • M5STACK;
    • FACES;
    • FACES Keyboard
    • USB-C cable from standard set;

    Let's start!

    Step 1. Introduction to the structure

    All graphic elements will be described by one structure, but each element has its own character. Let's look at the structure itself:

    struct UIElement 
      bool focus;
      int x;
      int y;
      int width;
      int height;
      int color;
      String type;
      String layer;
      String label;
      String *rootVar;

    Focus field-determines whether the user can access this element or not;

    Type field-determines the type of the element, and as a result the behavior;

    Label field-text title (not used in every element);

    The *rootVar field is a pointer to the memory area where the key data of the element is located;

    Special attention should be paid to the field layer. This field defines the layer on which the element is located.

    Figure 3. Layer's

    If you call up a new layer, the elements of the open layer will be erased from the screen.

    Step 2. Who's in charge?

    If you pay attention - FACES Keyboard is connected to M5 via I2C interface. When new data arrives, namely the codes of the pressed keys, we will perform certain actions: add characters, change focus, etc.

    We will write a function that will be started from the hotel stream, so as not to interfere with us in void loop():

    void UIController(void *pvParameters) {
      while (true)
        if (digitalRead(KEYBOARD_INT) == LOW) {
          Wire.requestFrom(KEYBOARD_I2C_ADDR, 1);  
          while (Wire.available())
            uint8_t key_val = Wire.read();                  
            if (key_val != 0)
              if (key_val > 20 && key_val < 0x7F) UIAddChar(key_val);
              if (key_val == 0x08) UIBackspace(); // Backspace
              if (key_val == 0xba) UITab(); // TAB
              if (key_val == 0x0d) (*UIEnter)(); // Enter
              //M5.Lcd.printf("0x%02X ",key_val);

    Start UIController(void*) a separate thread will carry out the function UIBegin():

    void UIBegin() {
      for (int i = 0; i < UIElementsSize; i++)
        UIElements[i].layer = "";
      xTaskCreatePinnedToCore(UIController, "UIController", 2048, NULL, 1, NULL, 0);  

    Step 3. How does Tab work, for example?

    The Tab key allows you to change the focus between elements in the layer.
    In order to make a Tab call, you need to use the function key on the FACES Keyboard. Press the Fn key, then you will see the right led light on the Keyboard, then press the TAB key (red sticker) (Fig. 4).

    Figure 4. The use of function keys

    If you need to lock the Fn key, press it quickly twice, then the led on the right should flash quickly.

    void UITab() {
      int i = 0;
      int j = -2;
      while (true)
        if (j >= 0)
          i = j + 1;
          j = -1;
        for ( ; i < UIElementsSize; i++)
          if ((UIElements[i].layer == activeLayer) && (UIElements[i].focus > -1))
            if (j == -2)
              if (UIElements[i].focus == 1)
                UIElements[i].focus = 0;
                j = i;
            else if (j == -1)
              UIElements[i].focus = 1;
        if (j == -2)
          i = 0;
          j = -1;
        else if (j == -1)
          i = 0;

    Step 4. Enter-what? O_o

    The Enter key is needed by the user more than the GUI. Therefore, there is a question of its lease to the user. How to do this? ... Through a pointer to a function, exactly.

    You can see the source code, but I assure you - you will not find the implementation of the function to Enter.

    Make a function stub:

    void UIEmpty(){}

    Now let's plug this function a pointer to a function to Enter:

    UIEnter pfunc = UIEmpty;

    If the user presses Enter, nothing much will happen. For something to happen, the user needs to rent a key with a simple line of code:

    UIEnter = WifiConnectPage;

    Don't be afraid of the word WifiConnectPage is just the name of the custom function. You can change it to any of your own, the main thing is that the signature is void myFunc();.

    Step 5. For dessert, consider the WifiConnectPage:

    Here I recommend just looking at the custom function. Try to understand and find something familiar:

    void WifiConnectPage() {
      UIValue(&result, "WiFi starting...");
      WiFi.begin(strToChar(ssid), strToChar(pswd));
      for (int i = 0; i < 20; i++) // timeout
        UIValue(&result, (result + "."));
        if (WiFi.status() == WL_CONNECTED)
          UIValue(&result, "WiFi connected");
          UIValue(&result, "");
          UIAddInputbox(10, 10, 300, "web", "Enter HOST", &host);
          UIValue(&host, "http://m5stack.com/?");
          UIAddTextbox(10, 70, 300, 160, "web", &result, 0x7bef);
          UIEnter = WebPage;
      UIValue(&result, "WiFi error");

    Step 6. It's time for void setup()

    Start our library UI UIBegin();. Build a layer where the user will be able to connect to wifi network - WiFiAuthPage();:

    void setup() {

    Is it? - yeah.

    Step 7. Create a layer. Hello, layer!

    Look at the code:

    void WiFiAuthPage() {
      UIAddInputbox(10, 10, 300, "wifi", "Please, enter SSID here", &ssid);
      UIAddInputbox(10, 80, 300, "wifi", "Please, enter PSWD here", &pswd);
      UIAddTextbox(35, 150, 250, 80, "wifi", &result, 0x7bef);
      UIEnter = WifiConnectPage;

    And now let's talk in more detail. To add an input field, use UIAddInputbox (x, y, width, "layer name", "label", &name_space);. A memory space is a regular string variable created by a user. All text entered in this element will be immediately written to this memory area.

    In the same way, let's do it with UIAddTextbox(x, y, width, height, "layer name", & name_space, text color);

    UIEnter = WifiConnectPage; - we are already familiar with this. Miss.

    UIOpenLayer ("wifi"); not difficult to guess, to cause it is necessary to see the layer.

    Step 8. Modification

    If you need to change the values inside the graphic to a new value, use UIValue (&name_space, "new value");

    If you want to add, then: UIValue(&oblastyami, oblastyami + "new value");

    Step 9. Launch!

    In the section "Download" attached video with a demonstration of the work. This completes the lesson.



与 M5Stack Community 的连接断开,我们正在尝试重连,请耐心等待