Когда printf — мало, а ncurses — много


Когда данных становится слишком много, бывает не хватает стандартного вывода printf в консольной программе. Особенно если различных событий много и различные данные превращаются в безумный листинг. Эти данные могут поступать от контроллера через UART, и тут нечего и думать о какой-то gui-программе. Может так же быть и обычный bash-скрипт, к которому хочется прикрутить какой-никакой псевдографический интерфейс.

Скажу сразу, может поначалу показаться это изобретением велосипеда, ведь есть прекрасная библиотека ncurses. Ничуть не умаляю её достоинств, но часто её возможности бывают слишком избыточны. Плюс она слабо переносима в контроллеры и там уж точно избыточна. А вот пару макросов и функций для работы с псевдографикой можно набросать и самим.

Постановка задачи


Те кто занимался программированием контроллеров часто для вывода информации при отладке используют консоль аки UART или УАПП по-русски. Раньше я давал описание данного интерфейса в своих постах на хабре. При достаточно большом количестве разнообразной информации отлавливать её становится совершенно невозможно. Получается многостраничный лог, особенно если непрерывно идут данные с каких-то аналоговых датчиков, плюс надо контролировать состояние портов и т.п. В результате люди начинают писать свои обработчики на стороне компьютера и решение становится совсем не переносимым, а главное тратится впустую куча времени. Но, на самом деле можно всю разнообразную информацию с контроллера уместить в одном окне терминала и удобно её использовать, без написания стороннего софта. В результате наше решение становится переносимым (нужна любая терминалка), и экономится куча программисто-часов на отладку.

Методический пример


Данный пример будет скорее учебный, методический, но вы его легко сможете адаптировать к своему реальному устройство на любом контроллере. Так же по аналогии это всё будет работать на bash, pyton и прочем.

Пусть у нас есть контроллер, где к 8-ми разрядному порту подключены контактные датчики, которые могут принимать состояние ВКЛ и ВЫКЛ, есть аналоговый трёхосевой датчик, и мы должны выводить их состояние, плюс в контроллере есть время, дата, и мы хотим контролировать правильность отображения времени; и наконец там может случится событие (например сбой по питанию), где мы должны срочно информировать оператора. Подытожим:

  • Состояние 8-ми пинов порта (on/off)
  • Трёхосевой датчик (x, y, z) — отдельный вывод каждой оси
  • Дата и время
  • Сообщение оператору об ошибке

Можно сразу прикинуть, что если подобную инфу просто тупо слать по UART, то это будет нечитаемый мусор. Но её можно упорядочить, и выглядеть всё должно примерно таким образом (эскиз в текстовом редакторе):


Рамочки я скопировал тупо из mc. Те, кто хочет красоты, могут посмотреть какие есть рамочки в unicode. Сверху цифры написаны специально для координат.

Вроде как идея понятна, но как это реализовать в терминале? А вот тут начинается настоящая магия! "А теперь надо обязательно дунуть! Если не дунуть, никакого чуда не произойдет!" Амаяк Акопян

Управляющие последовательности


Изначально планировал в этой главе развернуть лекцию про управление терминалом с помощью ESC-последовательностей. Но тогда бы статья разрослась просто до неприличных размеров, поэтому я дам лишь некоторые понятия, а остальное можно найти в интернете или в мануалах.
Терминал — это устройство ввода-вывода, которое позволяет вводить и отображать данные. Раньше терминал представлял из себя монитор и клавиатуру, подключаемые по СОМ-порту к ЭВМ, а в более ранних версиях даже телетайп.


Телетайп Model 33 (картинка с википедии)

Сегодня терминал представляет собой программу, которая может быть виртуальным терминалом, как в linux или терминалом, которая работает с СОМ-портом. Все эти терминалы, даже классический HyperTerminal отвечают некоторым стандартам. Как не сложно догадаться, такими терминалами нужно управлять. Есть обычные ASCII символы, которые отображаются на экране, а есть специальные последовательности символов, которые позволяют задавать координаты курсора, очищать экран, задавать цвет и т.п. В своих статьях на хабре и гиктаймс я уже неоднократно касался тем ESC-последовательностей. Мы их применяли в управлении дисплеем при написание драйверов под Linux (описание в спойлере, который никто не читал), и в статье про Дисплей покупателя для wifi-радио, который так же управляется ESC-последовательностями.

Если кто-то из вас застал времена BBS, то вы помните какие «красивые» были эти доски: цветные, у которых были определённые поля ввода-вывода информации и т.п. Вся эта радость выводилась с помощью таких Управляющих символов. Символ считается управляющим, если (до преобразования согласно таблице перекодировки) он содержит один из 14-и кодов: 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). Нас интересует в первую очередь символ ESC=0x1b, '\033' или же '\e'. Подробнее о этих последовательностях можно прочитать в мануалах man console_codes, либо в интернете на русском.

Кроме управления курсором, можно ещё раскрашивать терминал в разные цвета (если он, конечно, цветной). Проверить цвета вашего терминала можно простейшим BASH-скриптом:

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

Результат выполнения будет примерно такой:


Более детально почитать о цветах и шрифтах в консоли (и любых терминалах) с примерами можно вот тут.

Вот, вы уже владеете необходимой информацией, чтобы сделать весёлые пасхалки в BASH-скриптах, как например картинка, вынесенная в заголовок поста.


Более детально, как я делал такую пасхалку на bash, можно почитать у меня в ЖЖ.

Оформим всё на си


Данный пример я даю на своих мастер-классах по программированию и приведу его здесь. Сделаем макросы установления атрибутов текста, очистки экрана, перемещения курсора в верхний угол и перемещения курсора в заданную позицию:

#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)

По возможности, особенно на медленных системах лучше не использовать printf, так как его работа весьма медленная. Лучше заменять более легковесными функциями вывода на дисплей (COM-порт).

Сами атрибуты цветов закинем в отдельный заголовочный файл в виде отдельных макросов.

Заголовочный файл
#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__*/


Обратите внимание, что Foreground — раскрашивает сам текст, а Background подложку текста.

Сделаем небольшую програмку, демонстрирующую работу всех макросов.

Демонстрационная програмка
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;
}


Результат работы:



Обращаю ваше внимание, что атрибуты текста следует сбрасывать, иначе они наследуются!

Симуляция данных выводимых контроллером



Дело стало за малым, набросать программу, которая будет отвечать нашему ТЗ: выводить состояние пинов порта, акселерометр, дата и время, сообщение об ошибке. Код написан таким образом, что легко может быть перенесён на любой контроллер. Не буду загромождать текст кусками кода, тем более что можно его легко посмотреть на github, остановлюсь только на некоторых моментах.

В функции main мы переводим курсор в левый верхний угол, затем очищаем экран и рисуем рамку с помощью функции frame_draw ();. Рамку данных мы рисуем только один раз, потом все данные выводим просто каждую в своей позиции. После передаём управление функции controller_emulator ();.

В функции controller_emulator (); мы каждые 30 секунд меняем сообщение пользователю с хорошо на ошибку, генерируем данные в порт (там реализован «бегущий» бит) и генерируем случайные числа на акселерометре. Вы можете легко переписать эту функцию под свои задачи. Выводим все данные с помощью трёх функций:

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

Рамка рисуется элементарно просто, одной функцией puts с предварительно заданными атрибутами цвета:

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
		"L----------L----------\n" //9
		"---------------------¬\n" //10
		"¦                    ¦\n" //11
		"L---------------------");  //12
	resetcolor();
}

И для примера разберу функцию print_port_bits.

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();
}

Она принимает без знаковый байт. Устанавливает атрибуты текста (жирный, подложка голубая) и по очереди выводит бит, каждый в свою позицию с помощью макроса gotoxy(2,2 + i); и в зависимости от того бит равен нулю или единице, окрашивает текст соответственно в красный или зелёный цвет.

Результат работы программы лучше посмотреть самостоятельно, как и код. Но тем кому лень git-ить программу и хочет просто посмотреть как это работает, я привожу гифку работы:



Что хочу отметить, что все позиции обозначайте #define и лучше всего, чтобы они рассчитывались от какой-то позиции. Таким образом, вы сможете перемещать вывод данных в нужное место, не перепахивая весь код, а просто изменив макросы в одном месте.

Итого


С помощью управляющих последовательностей можно даже получать размер окна терминала, события нажатий мыши и т.п. Специально не стал приводить эти примеры в данной статье, чтобы не загромождать её. Вы сможете сами найти данные примеры. В программе под линукс вообще доступно много всяких плюшек с управлением терминалом, отображением эха, скорости и прочим-прочим.

На мой взгляд, знать механизмы взаимодействия с терминальными программами нужно и полезно. Особенно разработчикам контроллеров и встраиваемых решений, где отладка программ чаще всего осуществляется с помощью СОМ-порта и его производных.

Полезные ссылки:

1. Управляющие и ESC-последовательности консоли Linux
2. Как раскрасить консоль и всё об атрибутах цвета с примерами
3. Пример программы из данной статьи
4. Пример создания пасхалки в bash-скрипте
Поделиться с друзьями
-->

Комментарии (69)


  1. Meklon
    28.03.2017 19:16
    +2

    Надо для Python подумать. Спасибо) как раз надо подключать датчик.


  1. jamakasi666
    28.03.2017 20:16

    Никогда в плотную не работал с девайсами и особо не знаю всех проблем. Есть пара вопросов:
    1) Почему бы не сделать микросервис который бы тупо буферизировал весь хлам из uart\com, или сохранял его в бд и т.д. Дальше отдавал бы все это по tcp\udp по некоему общему интерфейсу. Дальше уже эти данные переваривать в кому что удобно, хочешь выводи в консоль, хочешь нарисуй GUI и т.д.
    2) В чем именно проблема переносимости?
    PS не кидайтесь помидорами, я правда не писал и не делал ничего с такими самопальными игрушками. Хотя с удовольствием бы что то такое состряпал со стороны софта.


    1. dlinyj
      28.03.2017 20:25
      +3

      Вопрос где хранить БД? У контроллера может быть 256 БАЙТ ОЗУ. И всё остальное жёстко зашито. В 256 байтах (даже в килобайте) сложно развернутся.

      UPD Не совсем понял вопроса сразу. Тем не менее — это всё лишние сложности, которые во время отладки не нужны.


    1. t3hk0d3
      28.03.2017 20:30
      +2

      1) уже. тысячи их. Даже девайсы специальные есть.
      2) Памяти не очень много. Хотя я думаю что базовые библиотеки уже есть.


    1. khim
      28.03.2017 21:56
      +1

      PS не кидайтесь помидорами, я правда не писал и не делал ничего с такими самопальными игрушками. Хотя с удовольствием бы что то такое состряпал со стороны софта.
      Да легко! Ваше творение будет только с МЕГА версией на 8KiB памяти работать, или в минимальной версии с 1KiB памяти вы тоже микросервисы разведёте? Учтите что этой же памятью и все остальные подпрограммы должны, как бы, пользоваться.

      Почему бы не сделать микросервис который бы тупо буферизировал весь хлам из uart\com, или сохранял его в бд и т.д.
      Потому что если вы это хотите разводить не на самой железяке, то вы автоматически теряете возможность к ней подконнектится любым терминалом и, соответственно, делаете работу с железкой резко менее удобной, а если на ней, то… ну попробуйте…


      1. jamakasi666
        28.03.2017 23:11

        Говоря про микросервис я имел ввиду что некая железка подключается к тому же обычному ПК по обычному UART или RS232. На этом ПК стоит микросервис который тупо дампит себе абсолютно все что приходит, хранит\буферизирует и просто ждет кто все это запросит уже на обычном tcp\udp. Дальше с этими данными уже делай что хочешь, хочешь напиши на java красивую вебморду которая будет запускаться на любой OS, хочешь напиши на питоне, да хоть на паскале.
        Т.е. в понятие микросервиса выше я вложил смысл этакого драйвера который просто пишет себе все что выдала железка по uart на ПК складывая все в немного обработанном виде избавленном от совершенно точно лишней каши. Я не имел ввиду на самом МК колхозить такое.


        1. dlinyj
          28.03.2017 23:13
          +1

          Дело хорошее, но бывает избыточно, если нужно быстренько отловить проблемку. Взял мою програмку и отловил.


          1. jamakasi666
            28.03.2017 23:51

            Ок, я на работе сталкиваюсь с УСПД к которому подключены кучи датчиков. УСПД в нашем случае выполняет роль того самого микросервиса, он просто тупо собирает и пишет в бд все что ему пришло а после отдает по tcp или udp на выбор куда угодно. Сами датчики все работают на rs232 и rs485. Датчики совершенно разные(естественно проприетарные прошивки внутри) но производитель сделал умную вещь. Любой датчик шлет данные в бинарном виде(есть документация), грубо говоря абсолютно без разницы что это за датчик т.к. поток бинарных данных имеет вид:

            typedef struct {
                int16_t data_len;
                int16_t packet_type;
                int16_t data;
                int16_t crc;
            }
            

            УСПД принимает весь этот поток безобразия и просто складывает в базу, при приеме естественно откидывает битые пакеты отдельно для дебага и истории. Отдает их дальше в таком же виде уже клиентской стороне которая ковыряется и десериализует все это.
            К чему я все это, все гениально и просто, датчики и МК в них шлют просто данные не занимаясь больше ничем что вполне правильно на мой взгляд и побережет ресурсы которых и так не особо много. На клиентской стороне эти пакеты элементарно по документации разбираются абсолютно любым удобным ЯП и показываются клиенту.
            Когда нам надо проверить какойто датчик, берем обычный узбшный rs232\485, подключаем это безобразие, на ноутбуке есть самописная утилита(писал не я а кто то очень давно), в ней элементарно принимается поток, бьется на эти пакеты, в програмке есть «шаблонизатор» в котором просто прописываются шаблоны пакетов по типу packet_type=5{байт такойто это гироскоп, за ним байт показывающий темпереатуру, тут 6 байт подряд которые чары и т.д.}.
            Интерфейс, как по мне, максимально удобно выглядит, удобен, не требует горожения колхозов и т.д., хочу работаю по TCP через УСПД, хочу напрямую цепляю датчик и вижу тоже самое.
            Разве это не более универсальное решение, темболее я сильно сомневаюсь что никто до такого не догадался, производитель железок дорогих почему то именно такой способ выбрал и я думаю совсем не спроста а из соображений универсальности.
            Поправьте если ход мыслей не правильный.


            1. khim
              29.03.2017 00:35

              Поправьте если ход мыслей не правильный.
              Он неправильный в самом начале: вы предполагаете, что ваш микроконтроллер постоянно к чему-то подключён. Однако огромное число микроконтроллеров (возможно даже подавляющее число) — работают автономно и ни к чему «на постоянной основе» не подключены.

              Куда-то подключаются они когда нужно систему перенастроить или, просто, разобраться с тем, что происходит. То есть выделенного УСПД, подключённого к микроконтроллеру на постоянной основе — просто нет.

              производитель железок дорогих почему то именно такой способ выбрал и я думаю совсем не спроста а из соображений универсальности.
              Он выбрал такой способ потому, что мог его себе позволить. Стоимость STM32 — порядка полудоллара. Стоимость конечной хрени, на нём собранной, может быть 3-5 долларов. Ну хорошо — 10 долларов, от силы (это уже дорого).

              Вы уверены что при таких ценах вам захочется докупать к этому устройству УСПД?


              1. jamakasi666
                29.03.2017 06:20

                Я понял. Но почему даже просто без УСПД не унифицировать сам интерфейс данных и иметь 1 программу с возможностью писать шаблоны данных скриптами?


                1. khim
                  29.03.2017 20:46
                  +2

                  Одну программу для чего, я извиняюсь? Эмуляция VT52 есть в десятках вариантах под десятки операционных систем. Уже даже Windows этому научилась! putty скоро останется не у дел!

                  А вашу программу кто будет ставить себе? И зачем? Чтобы отлаживать ваш микроконтроллер? Ну так мы снова получаем проблему: вам нужен выделенный компьютер, с установленными «микросервисами», воткнуть в произвольный комп провод и использовать штатные средства, встроенные в OS — вы не можете!

                  В тех случаях когда у вас есть возможность в дополнение к железяке разрабатывать докупать ещё (неважно — УСПД или компьютер) — да, это решение.

                  Но очень часто — это просто неудобно.


          1. iig
            29.03.2017 07:35

            Пробовали ли вы запускать её под arduino ide? Что-то мне кажется, тамошний терминал не умеет escape-последовательностей.


        1. ser-mk
          29.03.2017 02:04

          Вообще странное решение. Для нахождения проблемы в коде, добавлять еще кода, который возможно принесет еще других проблем.


          1. dlinyj
            29.03.2017 10:59

            К сожалению так все и делают…


  1. iig
    28.03.2017 21:54

    Для отладки это избыточно. Да и не всегда сработает: printf в UART достаточно медленная операция.
    А если требуется постоянный вывод в uart — лучше парсить его в клиентском приложении, тогда с теми же затратами можно сделать и полноценную графику.
    Имхо.


    1. dlinyj
      28.03.2017 22:22
      +1

      В тексте статьи я сказал, что printf использовать нельзя. А все координаты можно забить дефайнами и выводить готовые строки.

      Парсить в приложении конечно хорошо, а если приложения не предвидится? Просто на этапе отладки.


      1. iig
        28.03.2017 22:38

        http://we.easyelectronics.ru/Soft/formatnyy-vyvod-na-si-dlya-mikrokontrollerov.html
        Есть, оказывается, разные реализации printf. Да и что значит нельзя, если вы его все равно используете? ;)


        1. dlinyj
          28.03.2017 23:12
          +2

          Ну я использовал такие штуки. Но всё же лучше обойтись без printf.

          Да и что значит нельзя, если вы его все равно используете? ;)


          Не рекомендую :). А мне было так проще.


          1. iig
            29.03.2017 07:32

            В вашей программе, которая пример форматированного debug'а, я вижу printf, который вы же не рекомендуете. ;)


            1. dlinyj
              29.03.2017 10:55

              Его легко можно заменить ;)


              1. iig
                29.03.2017 11:29

                Заменить, наверное можно. Хотя вы не заменили. Хотя другим не рекомендуете почему-то ;)
                Легко — сомневаюсь.


                1. dlinyj
                  29.03.2017 11:37

                  Функция printf нужна для задания атрибутов цвета и gotoxy. Можно жёстко забить все атрибуты сразу функцией puts. С gotoxy немного сложнеее, но можно жёстко задать все координаты в дефайне и просто дёргать их.


    1. eugenebabichenko
      29.03.2017 17:41

      Для того чтоб это обойти, на микроконтроллерах с DMA возможна отдельная порнография: сформировать буфер и выводить его на UART в обход ядра процессора. Занимался таким, вполне себе рабочий вариант, если DMA не забит другими задачами под завязку.


  1. MacIn
    29.03.2017 02:37
    +1

    Все эти терминалы, даже классический HyperTerminal отвечают некоторым стандартам.

    Стандартам VT* — VT52? VT100 и так далее.
    https://en.wikipedia.org/wiki/VT100


    1. dlinyj
      29.03.2017 10:56

      Всё именно так. Просто не стал писать.


  1. mbait
    29.03.2017 04:44
    +1

    Преимущества готовых решений в адаптивности к терминалам с разными способностями (см. Termcap и Теrminfo). Если записать raw-escape последовательность в терминал, который её не поддерживает, то последствия неопределены — пропадаёт курсор, не печатается текст, слетает кодировка и т.д. Это хорошо видно на примерах скриптов-на-коленке, после запуска которых приходится выполнять reset в терминале.


    Не умаляя ценности статьи и вспоминая принцип меньше значит больше, хочется спросить: если что-то не влазит по памяти, может оно там не нужно? На крайный случай всегда есть альтернатива.


    1. dlinyj
      29.03.2017 10:58

      Лично я столкнулся с проблемой отладкой множества коммутационных датчиков на разных пинах. Если делать листинг, то получается просто фарш. А вот в таком виде удобно.
      Без сомнения, я очень благодарен за такие правки.


      1. iig
        29.03.2017 11:40

        Ну, я могу предложить 2 варианта отладки в такой ситуации:
        1. писать в лог не все подряд, а только то что нужно
        2. писать все подряд, лог сбрасывать в файл, анализировать результаты отдельно.


        1. dlinyj
          29.03.2017 11:41

          32 датчика — все их писать. Нужно отслеживать в реальном времени. Решения дешевле и быстрее, чем сделать вывод на ESC-последовательностях не вижу.


          1. iig
            29.03.2017 11:53

            Это еще отладка или уже рабочее использование? Если отладка — в терминал на 22" мониторе влазит 238 символов в ширину. Быстрее и дешевле построить велосипед, чем вывести все одним printf в одну строку? Если напрягает много одинаковых строк — выводить только те, что отличаются.


            1. dlinyj
              29.03.2017 16:22

              Как быть с аналоговыми датчиками? Удобно, когда значение меняется в одной ячейке.


              1. iig
                29.03.2017 16:31

                А в чем проблема с аналоговыми датчиками? Вывод в таблицу с фиксированной шириной колонок придуман еще до C и егойного printf ;).


                1. dlinyj
                  29.03.2017 17:13

                  Смотрите, есть изменяемое значение. Я хочу его видеть в реальном времени в конткретном положении на экране, не изобретая велосипед с написанием софта на стороне ПК.


                  1. iig
                    29.03.2017 17:36

                    Если это задача одноразовая, убедиться что датчики работают — не вижу смысла строить велосипед ни со стороны ПК, ни со стороны контроллера.
                    Если же планируется режим отображения показаний на ПК — лучше делать велосипед визуализацию на ПК. Там больше ресурсов. Больше возможностей, например, графики сразу можно рисовать.


                    1. dlinyj
                      29.03.2017 17:37

                      Каждый из нас в праве решать проблемы удобным ему способом :)

                      Я просто предложил один из вариантов, который нахожу удобным. Да и потом, я порой просто оставляю пасхалки в разных консольных программа, чисто джаст фо лулз.


                      1. georgevp
                        30.03.2017 00:59
                        -2

                        лОл (англ. LOL — Lot Of Laugh). Извините, пожалуйста, за небольшое отступление от темы.


                        1. dlinyj
                          30.03.2017 16:24

                          Таки офтоп


        1. MerQcio
          05.04.2017 23:28

          Обычно у девайса есть сохраняемые настройки. Их можно задавать с той же консоли. Обычно в этих настройках есть уровень выводимой отладочной информации (масками можно задавать даже их комбинации — выводить с датчика 1 и поступающие по Ethernet команды, к примеру).
          А проблема переносимости, имхо, несколько надумана. Как часто у разработчика меняются терминалы? Что именно мешает использовать один и тот же ноутбук с одним и тем же Putty(по-моему лучший терминал, к тому же универсальный в плане протоколов, поддерживает цвета)?


  1. ToSHiC
    29.03.2017 08:58

    Если вдруг никогда не видели — запустите ffplay на машине без иксов (наверное, достаточно будет их убить на время). И вы офигеете!


  1. borisxm
    29.03.2017 16:22

    Вообще, у вас получился просто интерфейс отображения данных средствами терминала. Который, в отличие от журналов (логов) отладки, не содержит информации об истории событий. А именно история является самой важной при разборе полетов.


    1. dlinyj
      29.03.2017 16:22

      Для каждой задачи свой интерфейс. Я ещё раз говорю, что в реальном времени работать с логами не очень удобно. Логи и так по умолчанию идут.


      1. iig
        29.03.2017 21:03
        -1

        Логи и так по умолчанию идут.

        Куда они идут, если com порт занят рисованием формы?


  1. amarao
    29.03.2017 18:08

    Очень, очень, очень плохой текст.

    Почему? Потому что автор утверждает, что есть ESC-Последовательность, которая что-то делает. Одна.

    На самом деле нет. И term в первом приближении может быть xterm, linux или screen. Или vt100, или dump. И его надо поддерживать.

    Для этого и именно для этого нужна ncurses. Чтобы не париться о том, «каким esc-кодом рисовать».

    Если вы такой умный, покажите мне вывод вашей программы на терминале с помощью python-vt100. С переменной среды окружения TERM=vt100. Ncurses и остальные программы, которые писались в реальном мире, это обработают правильно и адаптируются к плохому терминалу. Хардкоженные последовательности — нет.


    1. dlinyj
      29.03.2017 18:20
      +2

      Очень хороший комментарий, не смотря на то что написан в такой хамской форме. И я полностью с ним согласен!

      Есть некоторые последовательности, которые корректно работают практически везде. Вот вы можете сказать, какие конкретно используемые в моём коде последовательности будут отличаться во всех приведённых вами случаях?

      Но тем не менее, я говорил в тексте и скажу здесь, что ncurses — это лучшее решение! Но если хочется быстро, качественно, не дорого и под МК — вот вариант.

      Прежде чем писать, погуглил, ncurses для 8бит avr. Сходу не нашлось. Так что остаётся либо писать консольное приложение, обрабатывающие полученные данные с порта, либо реализовывать вариант, что предложил я. Питоном там не пахнет.


      1. amarao
        29.03.2017 18:31

        Все, управляющие цветом, не будут поддерживаться на vt100 (кроме bright). Просто потому, что vt100 — черно-белый. При этом там есть свои esc-последовательности для выделения текста (bright/underline, вроде бы, ещё и bold, но я не помню).


        1. dlinyj
          29.03.2017 19:33

          Они просто будут чёрно-белыми и будут работать.


          1. amarao
            29.03.2017 20:44

            Это с чего вы взяли? Я тут ничего такого близко не вижу: http://www.csie.ntu.edu.tw/~r92094/c++/VT100.html


            1. dlinyj
              29.03.2017 21:02

              Мне стало интересно. Назовите эмулятор vt100 терминала, я попробую.


              1. amarao
                30.03.2017 00:09

                https://www.vandyke.com/products/securecrt/terminal_emulation_vt100.html

                Я не пробовал, но, вроде, оно.


                1. dlinyj
                  30.03.2017 16:25

                  Под linux бы.


            1. dlinyj
              29.03.2017 21:11

              TERM=vt100
              export TERM



              Время и дата в подтверждение.


              1. iig
                29.03.2017 21:31

                В arduino ide жду увидеть ;).


                1. dlinyj
                  29.03.2017 21:58

                  Выдайте ардиуно, покажу.


                  1. iig
                    29.03.2017 22:18

                    Подключите любой другой контроллер и запустите монитор COM порта. UART он везде UART.


                    1. dlinyj
                      29.03.2017 22:24

                      Считайте я проверил — работает.


                      1. iig
                        29.03.2017 22:29

                        Скриншот не получилось сделать?


                        1. dlinyj
                          30.03.2017 16:28

                          Вы программу переделайте для ардуино, и откройте её в minicom. Всё будет работать.


                      1. iig
                        30.03.2017 10:43
                        -1

                        Не верю.
                        Я проверил — в dumb терминале, встроенном в Arduino IDE, escape последовательности отрисовываются квадратиками.
                        arduino

                        Ожидалось такое вот:
                        xterm


                        1. dlinyj
                          30.03.2017 16:26

                          Ну дык, это не терминал, а просто компортовая плевалка. Явно не vt52


              1. amarao
                30.03.2017 00:11

                Простите, а как изменение переменной среды окружения что-то меняет? Она говорит какой у вас терминал софту, но сам терминал при этом не меняется же.


                1. dlinyj
                  30.03.2017 16:29

                  Не прощу!

                  Ну это я бегло погуглил, как можно сделать.


  1. lzb_j77
    30.03.2017 10:44

    Я тут внезапно подумал — хорошая задумка. Через ком-порт всегда было удобно отлаживать контроллеры, а тут прям мысль — сделать самопальную терминалку.
    Попробую реализовать под вин32 подобное.


    1. iig
      30.03.2017 11:05

      Лучше чем putty?


      1. lzb_j77
        30.03.2017 11:22

        Наверное, я неправильно выразился — подключаться к ком-порту через путти, а на стороне контроллера примерно такое ПО, которое описано в статье — будет выдавать рамку и нужную для отладки инфу.


        1. iig
          30.03.2017 11:50
          -1

          КМК, это достаточно неудобно — весь отладочный вывод заворачивать в escape-последовательности. А если заворачивать не все — форма будет искажаться. Отлаживать код, который должен помогать в отладке, это лишнее.


        1. dlinyj
          30.03.2017 16:27

          Собственно говоря я об этом и говорил в статье. Не слушайте тех кто говорит, что это не удобно Удобно, проверенно.


          1. iig
            30.03.2017 16:52

            Удобно, ну и ладно ;) У всех свои понятия про удобство, какие действия выполнять во время отладки, и зачем она воще нужна. Слава труду, короче ;)


          1. lzb_j77
            30.03.2017 16:59
            +1

            Да, мы уже так делали — в процессе отладки контроллер выводил разные данные в ком-порт. Но я не знал, что можно вот так симпатично и удобно оформить.
            Поработаю над этим :)


  1. wigneddoom
    05.04.2017 00:27

    Когда-то тоже занимался подобным "украшательством" вывода в UART на самом MCU, чтобы проще было отлаживать. Затем просто стандартиризировал отладочный вывод и отказался от использования стандартных терминалов в пользу скриптов на Python + модуль Serial — гибкости нет придела.