πŸ“œ ⬆️ ⬇️

Terminal graphics

When printf is small and ncurses are many


When data gets too much, there is a lack of standard printf output in the console program. Especially if a lot of different events and different data turn into a crazy listing. This data can come from the controller through the UART, and there is nothing to think about any gui-program. It can also be a regular bash script to which you want to screw some kind of pseudo-graphic interface.

I will say right away that at first this may seem to be the invention of the bicycle, because there is an excellent library of ncurses. I do not detract from its merits, but often its possibilities are too excessive. Plus, it is poorly portable to controllers and is certainly redundant there. But a couple of macros and functions for working with pseudographics can be sketched by yourself.
')

Formulation of the problem


Those who have been programming controllers often use the UART or UART console in Russian to display information when debugging. I used to give a description of this interface in my posts on HabrΓ© . With a sufficiently large amount of various information, it becomes absolutely impossible to catch it. It turns out a multipage log, especially if the data from some analog sensors is continuously flowing, plus you need to monitor the status of the ports, etc. As a result, people start writing their handlers on the computer side and the solution becomes completely non-portable, and most importantly, a lot of time is wasted. But, in fact, you can fit all the various information from the controller in one terminal window and it is convenient to use it, without writing third-party software. As a result, our solution becomes portable (any terminalka is needed), and a lot of programming hours for debugging is saved.

Methodical example


This example will be more educational, methodical, but you can easily adapt it to your real device on any controller. Also, by analogy, this will all work on bash, pyton, and so on.

Suppose we have a controller, where contact sensors are connected to the 8-bit port, which can take ON and OFF states, there is an analog three-axis sensor, and we have to display their status, plus there is a time and date in the controller, and we want to monitor the correctness time display; and finally there may be an event (for example, a power failure), where we must immediately inform the operator. Summing up:


You can immediately estimate that if such an info is just stupid to send via UART, then it will be unreadable garbage. But it can be streamlined, and everything should look something like this (sketch in a text editor):


Frames I copied stupidly from mc. Those who want beauty, can see what are the frames in unicode. Top numbers written specifically for the coordinates.

It seems like the idea is clear, but how to implement it in the terminal? And here begins the real magic! β€œ And now you have to blow it! If you don’t blow, no miracle will happen! ” Hmayak Hakobyan

Control sequences


Originally planned in this chapter to deploy a lecture on terminal management using ESC-sequences. But then the article would have grown simply to indecent size, so I will give only some concepts, and the rest can be found on the Internet or in manuals.
A terminal is an I / O device that allows you to enter and display data. Previously, the terminal was a monitor and keyboard, connected via a COM port to a computer, and in earlier versions even a teletype.


Teletype Model 33 ( Wikipedia image)

Today, the terminal is a program that can be a virtual terminal, as in linux or a terminal that works with a COM port. All of these terminals, even the classic HyperTerminal, meet certain standards. As it is not difficult to guess, such terminals need to be managed. There are ordinary ASCII characters that are displayed on the screen, and there are special sequences of characters that allow you to set the coordinates of the cursor, clear the screen, set the color, etc. In my articles on HabrΓ© and Hyktimes, I have repeatedly touched upon those ESC sequences. We used them to control the display when writing drivers for Linux (the description in the spoiler that no one read), and in the article about the Customer Display for a wifi radio , which is also controlled by ESC sequences.

If one of you caught the BBS times, then you remember how β€œbeautiful” these boards were: colored, which had certain input / output fields, etc. All this joy was derived from such Control Characters. A symbol is considered to be a control one if (before conversion according to the conversion table) it contains one of 14 codes: 00 (NUL), 0x07 (BEL), 0x08 (BS), 0x09 (HT), 0x0a (LF), 0x0b (VT) , 0x0c (FF), 0x0d (CR), 0x0e (SO), 0x0f (SI), 0x18 (CAN), 0x1a (SUB), 0x1b (ESC), 0x7f (DEL). We are primarily interested in the symbol ESC = 0x1b, '\ 033' or '\ e'. You can read more about these sequences in man console_codes manuals, or on the Internet in Russian .

In addition to controlling the cursor, you can also paint the terminal in different colors (if it is, of course, color). You can check the colors of your terminal with the simplest BASH script:

for fgbg in 38 48 ; do #Foreground/Background for color in {0..256} ; do #Colors #Display the color echo -en "\e[${fgbg};5;${color}m ${color}\t\e[0m" #Display 10 colors per lines if [ $((($color + 1) % 10)) == 0 ] ; then echo #New line fi done echo #New line done exit 0 

The output will be something like this:


You can read more about colors and fonts in the console (and any terminals) with examples here .

Here, you already have the necessary information to make funny Easter eggs in BASH scripts, such as a picture in the post title.


In more detail, as I did such easter eggs on bash, you can read in my LJ .

Let's arrange everything on si


I give this example in my programming workshops and give it here. Let's create macros for setting text attributes, clearing the screen, moving the cursor to the top corner and moving the cursor to the specified position:

 #define home() printf(ESC "[H") //Move cursor to the indicated row, column (origin at 1,1) #define clrscr() printf(ESC "[2J") //lear the screen, move to (1,1) #define gotoxy(x,y) printf(ESC "[%d;%dH", y, x); #define visible_cursor() printf(ESC "[?251"); //Set Display Attribute Mode <ESC>[{attr1};...;{attrn}m #define resetcolor() printf(ESC "[0m") #define set_display_atrib(color) printf(ESC "[%dm",color) 

Whenever possible, especially on slow systems, it is better not to use printf, since its work is very slow. It is better to replace with more lightweight display functions (COM-port).

We will throw the attributes of colors into a separate header file as separate macros.

Header file
 #ifndef __TERM_EXAMPLE__ #define __TERM_EXAMPLE__ #define ESC "\033" //Format text #define RESET 0 #define BRIGHT 1 #define DIM 2 #define UNDERSCORE 3 #define BLINK 4 #define REVERSE 5 #define HIDDEN 6 //Foreground Colours (text) #define F_BLACK 30 #define F_RED 31 #define F_GREEN 32 #define F_YELLOW 33 #define F_BLUE 34 #define F_MAGENTA 35 #define F_CYAN 36 #define F_WHITE 37 //Background Colours #define B_BLACK 40 #define B_RED 41 #define B_GREEN 42 #define B_YELLOW 44 #define B_BLUE 44 #define B_MAGENTA 45 #define B_CYAN 46 #define B_WHITE 47 #endif /*__TERM_EXAMPLE__*/ 


Please note that Foreground - colors the text itself, and Background background text.

Let's make a small program demonstrating the work of all macros.

Demo program
 int main (void) { home(); clrscr(); printf("Home + clrscr\n"); gotoxy(20,7); printf("gotoxy(20,7)"); gotoxy(1,10); printf("gotoxy(1,10) \n\n"); set_display_atrib(BRIGHT); printf("Formatting text:\n"); resetcolor(); set_display_atrib(BRIGHT); printf("Bold\n"); resetcolor(); set_display_atrib(DIM); printf("Dim\n"); resetcolor(); set_display_atrib(BLINK); printf("Blink\n"); resetcolor(); set_display_atrib(REVERSE); printf("Reverse\n"); printf("\n"); set_display_atrib(BRIGHT); printf("Text color example:\n"); resetcolor(); set_display_atrib(F_RED); printf("Red\n"); resetcolor(); set_display_atrib(F_GREEN); printf("Green\n"); resetcolor(); set_display_atrib(F_BLUE); printf("Blue\n"); resetcolor(); set_display_atrib(F_CYAN); printf("Cyan\n"); resetcolor(); set_display_atrib(BRIGHT); printf("\nBottom color example:\n"); resetcolor(); set_display_atrib(B_RED); printf("Red\n"); resetcolor(); set_display_atrib(B_GREEN); printf("Green\n"); resetcolor(); set_display_atrib(B_BLUE); printf("Blue\n"); resetcolor(); set_display_atrib(B_CYAN); printf("Cyan\n"); printf("\n"); resetcolor(); return 0; } 


Result of work:



I draw your attention that the text attributes should be reset, otherwise they are inherited!

Simulation of data output by the controller



The matter became small, to sketch a program that will respond to our TK: display the status of the port pins, accelerometer, date and time, error message. The code is written in such a way that it can easily be transferred to any controller. I will not clutter the text with pieces of code, especially since you can easily see it on github , I’ll dwell only on some points.

In the main function, we move the cursor to the upper left corner, then clear the screen and draw a frame using the frame_draw (); function. We only draw the data frame once, then we just output all the data in its own position. Then we transfer the control to the controller_emulator () ;.

In the function controller_emulator (); every 30 seconds we change the message to the user with a good error, generate data to the port (the running bit is implemented there) and generate random numbers on the accelerometer. You can easily rewrite this function for your tasks. We display all data using three functions:

  print_accelerometer(a); print_port_bits(port); print_time_date(tm_info); 

The frame is drawn elementarily simply by one puts function with predefined color attributes:

 void frame_draw () { home(); set_display_atrib(B_BLUE); // 123456789012345678901 puts( "β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”\n" //0 "β”‚ β”‚β”‚ β”‚\n" //1 "β”‚ β”‚β”‚ β”‚\n" //2 "β”‚ β”‚β”‚ β”‚\n" //3 "β”‚ β”‚β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€\n" //4 "β”‚ β”‚β”‚ β”‚\n" //5 "β”‚ β”‚β”‚ β”‚\n" //6 "β”‚ β”‚β”‚ β”‚\n" //7 "β”‚ β”‚β”‚ β”‚\n" //8 "β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜\n" //9 "β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”\n" //10 "β”‚ β”‚\n" //11 "β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜"); //12 resetcolor(); } 

And for an example I will sort the print_port_bits function.

 void print_port_bits (unsigned char port) { int i; unsigned char maxPow = 1<<(8-1); set_display_atrib(B_BLUE); set_display_atrib(BRIGHT); for(i=0;i<8;++i){ // print last bit and shift left. gotoxy(2,2 + i); if (port & maxPow) { set_display_atrib(F_GREEN); printf("pin%d= on ",i); } else { set_display_atrib(F_RED); printf("pin%d= off",i); } port = port<<1; } resetcolor(); } 

It takes no sign byte. Sets text attributes (bold, blue background) and in turn outputs a bit, each in its own position using the gotoxy macro (2.2 + i); and depending on whether the bit is zero or one, colors the text in red or green, respectively.

The result of the program is better to look at yourself, like the code. But for those who are too lazy to git-it program and just want to see how it works, I cite the work gif:



What I want to note is that all positions are #define and, best of all, they are calculated from a position. Thus, you can move the output to the right place, without plowing the entire code, but simply by changing the macros in one place.

Total


With the help of control sequences, you can even get the size of the terminal window, mouse click events, etc. Specifically did not give these examples in this article, so as not to clutter it. You can find these examples yourself. In the program under Linux, there are many different kinds of buns available with terminal management, echo display, speed, and so on.

In my opinion, knowing the mechanisms of interaction with terminal programs is necessary and useful. Especially developers of controllers and embedded solutions, where program debugging is most often done using the COM port and its derivatives.

Useful links:

1. Linux console control and esc sequences
2. How to color the console and everything about color attributes with examples
3. Sample program from this article
4. Example of creating Easter eggs in a bash script

Source: https://habr.com/ru/post/325082/


All Articles