Кто сказал, что символьные дисплеи для символов? Какие-то скучные чуваки. Если бы это было так, эти дисплеи не были бы настолько любимы и популярны в Arduino среде (купить можно тут https://alexgyver.ru/arduino_shop/). Подробного гайда лично от меня пока что нет, но суть такая: в этих дисплеях есть 8 ячеек для хранения “кастомных” символов, то есть символов, которые можно нарисовать самому, например при помощи различных онлайн сервисов типа такого http://maxpromer.github.io/LCD-Character-Creator/. Полученный массив байтов вставляется в скетч, передаётся в дисплей, и затем можно им пользоваться при помощи команды write(). Простой пример есть на офф сайте Ардуино https://www.arduino.cc/en/Reference/LiquidCrystalCreateChar. К слову эти ячейки в памяти занимают места с 0 по 7
Был у меня оч крутой проект, PCdisplay https://alexgyver.ru/pcdisplay/, в котором на дисплее 2004 отображалась в реальном времени информация о железе ПК: температуры и проценты загрузки, для красоты я выводил показатели не только числами, но и графически, в виде “полос загрузки” и графиков.
Проект получился очень классный, но вот я подумал, а почему бы не вынести построение графиков и полос загрузки удобными отдельными функциями? Ведь по-любому пригодятся кому-нибудь из вас, а достать код из PCdisplay новичку не под силу (к тому же он там чутка кривоват…). Так что представляю вашему вниманию GyverLCDbars – набор удобных инструментов для украшения ваших проектов графическими элементами: полосками загрузки и графиками. Актуальная версия всегда лежит у меня на GitHub https://github.com/AlexGyver/GyverLCDbars. Ссылка на прямую загрузку архива.
В библиотеке вас ждёт 6 примеров: 4 типа полос загрузки и два примера с графиком.
Как этим пользоваться? Да очень просто. Одна функция для инициализации “кастомных” символов, вторая – для вывода нужного элемента с настройками его размера и позиции на дисплее!
Полоса загрузки: fillBar(столбец, строка, ширина, значение)
- Столбец: отвечает за положение левой точки полосы, нумерация идёт слева направо с нуля
- Строка: отвечает за положение левой точки полосы, нумерация идёт сверху вниз с нуля
- Ширина: полная ширина полосы по горизонтали. Очевидно, что ширина + стартовая позиция по горизонтали (столбец) не должны превышать ширину дисплея в символах по горизонтали
- Значение: число от 0 до 100 – процент заполнения полосы. Любая ваша величина приводится к диапазону 0-100 при помощи ардуиновской функции map
- Особенность: если вы используете свои кастомные символы, то перед выводом полосок нужно обязательно вызвать initBar() для загрузки в память дисплея символов полосы! Полоски занимают разное количество мест в зависимости от типа, подробнее смотрите в самих примерах
График из массива: drawPlotArray(столбец, строка, ширина, высота, мин. значение, макс. значение, массив) – смотри пример!
График в реальном времени: drawPlot(столбец, строка, ширина, высота, мин. значение, макс. значение, величина)
- Столбец: нумерация идёт слева направо с нуля. Начало координат графика – нижняя левая точка!
- Строка: нумерация идёт сверху вниз с нуля. Начало координат графика – нижняя левая точка!
- Ширина: ширина графика по горизонтали. Очевидно, что ширина + стартовая позиция по горизонтали (столбец) не должны превышать ширину дисплея в символах по горизонтали
- Высота: высота графика по вертикали. Очевидно, что высота+ стартовая позиция по вертикали (строка) не должны превышать высоту дисплея в символах по вертикали. То есть для 2004 максимум высота 4, для 1602 максимум 2.
- Мин. значение: минимальное значение для графика, ниже него строиться не будет (тип данных int -32,768 to 32,767)
- Макс. значение: максимальное значение для графика, выше него строиться не будет (тип данных int -32,768 to 32,767)
- Величина: значение, которое будет построено на графике с краю, предыдущие столбики автоматически сдвинутся в сторону при вызове функции drawPlot. Тип данных int -32,768 to 32,767
- Также в примере есть готовый кусок кода для расчёта и вывода максимального и минимального значения на текущем графике!
- Особенность: если вы используете свои кастомные символы, то перед выводом графика нужно обязательно вызвать initPlot() для загрузки в память дисплея символов графика! Они занимают все места, с 0 по 7!
/* Скетч примера графика на символьном LCD дисплее 1602/2004 итд. */ // библиотеки дисплея #include#include // создаём дисплей // дисплей 1602. Если не работает, используйте другой адрес //LiquidCrystal_I2C lcd(0x3f, 16, 2); //LiquidCrystal_I2C lcd(0x27, 16, 2); // дисплей 2004. Если не работает, используйте другой адрес! //LiquidCrystal_I2C lcd(0x3f, 20, 4); LiquidCrystal_I2C lcd(0x27, 20, 4); int plot_array[20]; // массив данных для графика void setup() { Serial.begin(9600); lcd.init(); lcd.backlight(); lcd.clear(); // инициализация символов для отрисовки. При использовании каких то своих символов, // нужно вызывать эту функцию перед отрисовкой графика/полосы! initPlot(); } void loop() { // для примера потенциометр подключен к A0 int value = map(analogRead(0), 0, 1023, 10, 80); // drawPlot принимает аргументы (столбец, строка, ширина, высота, мин. значение, макс. значение, величина) // строка, столбец: начало графика - нижняя левая точка! // значения целочисленные (int) и могут быть отрицательными! // для примера отрисовка графика 12х4 занимает 140 миллисекунд // график сдвигается на 1 шаг АВТОМАТИЧЕСКИ при вызове функции! drawPlot(0, 3, 12, 4, 10, 80, value); // также можно вывести минимум и максимум по отображаемому участку графика /* int max_value = -32000; int min_value = 32000; for (byte i = 0; i < 12; i++) { if (plot_array[i] > max_value) max_value = plot_array[i]; if (plot_array[i] < min_value) min_value = plot_array[i]; } lcd.setCursor(17, 1); lcd.print(value); lcd.print(" "); lcd.setCursor(13, 0); lcd.print("max "); lcd.print(max_value); lcd.print(" "); lcd.setCursor(13, 3); lcd.print("min "); lcd.print(min_value); lcd.print(" "); */ delay(300); } void initPlot() { // необходимые символы для работы // создано в http://maxpromer.github.io/LCD-Character-Creator/ byte row8[8] = {0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111}; byte row7[8] = {0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111}; byte row6[8] = {0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111}; byte row5[8] = {0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111}; byte row4[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111}; byte row3[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111}; byte row2[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111}; byte row1[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111}; lcd.createChar(0, row8); lcd.createChar(1, row1); lcd.createChar(2, row2); lcd.createChar(3, row3); lcd.createChar(4, row4); lcd.createChar(5, row5); lcd.createChar(6, row6); lcd.createChar(7, row7); } void drawPlot(byte pos, byte row, byte width, byte height, int min_val, int max_val, int fill_val) { for (byte i = 0; i < width; i++) { plot_array[i] = plot_array[i + 1]; } fill_val = constrain(fill_val, min_val, max_val); plot_array[width] = fill_val; for (byte i = 0; i < width; i++) { // каждый столбец параметров byte infill, fract; // найти количество целых блоков с учётом минимума и максимума для отображения на графике infill = floor((float)(plot_array[i] - min_val) / (max_val - min_val) * height * 10); fract = (infill % 10) * 8 / 10; // найти количество оставшихся полосок infill = infill / 10; for (byte n = 0; n < height; n++) { // для всех строк графика if (n < infill && infill > 0) { // пока мы ниже уровня lcd.setCursor(i, (row - n)); // заполняем полными ячейками lcd.write(0); } if (n >= infill) { // если достигли уровня lcd.setCursor(i, (row - n)); if (fract > 0) lcd.write(fract); // заполняем дробные ячейки else lcd.write(16); // если дробные == 0, заливаем пустой for (byte k = n + 1; k < height; k++) { // всё что сверху заливаем пустыми lcd.setCursor(i, (row - k)); lcd.write(16); } break; } } } }
/* Скетч примера графика на символьном LCD дисплее 1602/2004 итд. */ // библиотеки дисплея #include#include // создаём дисплей // дисплей 1602. Если не работает, используйте другой адрес //LiquidCrystal_I2C lcd(0x3f, 16, 2); LiquidCrystal_I2C lcd(0x27, 16, 2); // дисплей 2004. Если не работает, используйте другой адрес! //LiquidCrystal_I2C lcd(0x3f, 20, 4); //LiquidCrystal_I2C lcd(0x27, 20, 4); int plot_array[20]; // массив данных для графика void setup() { Serial.begin(9600); lcd.init(); lcd.backlight(); lcd.clear(); // инициализация символов для отрисовки. При использовании каких то своих символов, // нужно вызывать эту функцию перед отрисовкой графика/полосы! initPlot(); } void loop() { // для примера потенциометр подключен к A0 int value = map(analogRead(0), 0, 1023, 10, 80); // drawPlot принимает аргументы (столбец, строка, ширина, высота, мин. значение, макс. значение, величина) // строка, столбец: начало графика - нижняя левая точка! // значения целочисленные (int) и могут быть отрицательными! // для примера отрисовка графика 12х4 занимает 140 миллисекунд // график сдвигается на 1 шаг АВТОМАТИЧЕСКИ при вызове функции! drawPlot(0, 1, 16, 2, 10, 80, value); // также можно вывести минимум и максимум по отображаемому участку графика /* int max_value = -32000; int min_value = 32000; for (byte i = 0; i < 12; i++) { if (plot_array[i] > max_value) max_value = plot_array[i]; if (plot_array[i] < min_value) min_value = plot_array[i]; } lcd.setCursor(17, 1); lcd.print(value); lcd.print(" "); lcd.setCursor(13, 0); lcd.print("max "); lcd.print(max_value); lcd.print(" "); lcd.setCursor(13, 3); lcd.print("min "); lcd.print(min_value); lcd.print(" "); */ delay(300); } void initPlot() { // необходимые символы для работы // создано в http://maxpromer.github.io/LCD-Character-Creator/ byte row8[8] = {0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111}; byte row7[8] = {0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111}; byte row6[8] = {0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111}; byte row5[8] = {0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111}; byte row4[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111}; byte row3[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111}; byte row2[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111}; byte row1[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111}; lcd.createChar(0, row8); lcd.createChar(1, row1); lcd.createChar(2, row2); lcd.createChar(3, row3); lcd.createChar(4, row4); lcd.createChar(5, row5); lcd.createChar(6, row6); lcd.createChar(7, row7); } void drawPlot(byte pos, byte row, byte width, byte height, int min_val, int max_val, int fill_val) { for (byte i = 0; i < width; i++) { plot_array[i] = plot_array[i + 1]; } fill_val = constrain(fill_val, min_val, max_val); plot_array[width] = fill_val; for (byte i = 0; i < width; i++) { // каждый столбец параметров byte infill, fract; // найти количество целых блоков с учётом минимума и максимума для отображения на графике infill = floor((float)(plot_array[i] - min_val) / (max_val - min_val) * height * 10); fract = (infill % 10) * 8 / 10; // найти количество оставшихся полосок infill = infill / 10; for (byte n = 0; n < height; n++) { // для всех строк графика if (n < infill && infill > 0) { // пока мы ниже уровня lcd.setCursor(i, (row - n)); // заполняем полными ячейками lcd.write(0); } if (n >= infill) { // если достигли уровня lcd.setCursor(i, (row - n)); if (fract > 0) lcd.write(fract); // заполняем дробные ячейки else lcd.write(16); // если дробные == 0, заливаем пустой for (byte k = n + 1; k < height; k++) { // всё что сверху заливаем пустыми lcd.setCursor(i, (row - k)); lcd.write(16); } break; } } } }
/* Строим график из массива! */ // библиотеки дисплея #include#include // создаём дисплей // дисплей 1602. Если не работает, используйте другой адрес //LiquidCrystal_I2C lcd(0x3f, 16, 2); //LiquidCrystal_I2C lcd(0x27, 16, 2); // дисплей 2004. Если не работает, используйте другой адрес! //LiquidCrystal_I2C lcd(0x3f, 20, 4); LiquidCrystal_I2C lcd(0x27, 20, 4); int plot_array1[20]; // массив данных для графика int plot_array2[20]; // массив данных для графика int plot_array3[20]; // массив данных для графика void setup() { Serial.begin(9600); lcd.init(); lcd.backlight(); lcd.clear(); // инициализация символов для отрисовки. При использовании каких то своих символов, // нужно вызывать эту функцию перед отрисовкой графика/полосы! initPlot(); // забьём массивы разными данными for (byte i = 0; i < 20; i++) { plot_array1[i] = random(0, 25); plot_array2[i] = random(25, 50); plot_array3[i] = random(50, 75); } } void loop() { // показываем по очереди все три графика drawPlot(0, 3, 20, 4, 0, 100, (int*)plot_array1); delay(1000); drawPlot(0, 3, 20, 4, 0, 100, (int*)plot_array2); delay(1000); drawPlot(0, 3, 20, 4, 0, 100, (int*)plot_array3); delay(1000); } void initPlot() { // необходимые символы для работы // создано в http://maxpromer.github.io/LCD-Character-Creator/ byte row8[8] = {0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111}; byte row7[8] = {0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111}; byte row6[8] = {0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111}; byte row5[8] = {0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111}; byte row4[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111}; byte row3[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111}; byte row2[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111}; byte row1[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111}; lcd.createChar(0, row8); lcd.createChar(1, row1); lcd.createChar(2, row2); lcd.createChar(3, row3); lcd.createChar(4, row4); lcd.createChar(5, row5); lcd.createChar(6, row6); lcd.createChar(7, row7); } void drawPlot(byte pos, byte row, byte width, byte height, int min_val, int max_val, int *plot_array) { for (byte i = 0; i < width; i++) { // каждый столбец параметров int fill_val = plot_array[i]; fill_val = constrain(fill_val, min_val, max_val); byte infill, fract; // найти количество целых блоков с учётом минимума и максимума для отображения на графике infill = floor((float)(plot_array[i] - min_val) / (max_val - min_val) * height * 10); fract = (infill % 10) * 8 / 10; // найти количество оставшихся полосок infill = infill / 10; for (byte n = 0; n < height; n++) { // для всех строк графика if (n < infill && infill > 0) { // пока мы ниже уровня lcd.setCursor(i, (row - n)); // заполняем полными ячейками lcd.write(0); } if (n >= infill) { // если достигли уровня lcd.setCursor(i, (row - n)); if (fract > 0) lcd.write(fract); // заполняем дробные ячейки else lcd.write(16); // если дробные == 0, заливаем пустой for (byte k = n + 1; k < height; k++) { // всё что сверху заливаем пустыми lcd.setCursor(i, (row - k)); lcd.write(16); } break; } } } }
/* Скетч примера полосы загрузки на символьном LCD дисплее 1602/2004 итд. */ // библиотеки дисплея #include#include // создаём дисплей // дисплей 1602. Если не работает, используйте другой адрес LiquidCrystal_I2C lcd(0x3f, 16, 2); //LiquidCrystal_I2C lcd(0x27, 16, 2); // дисплей 2004. Если не работает, используйте другой адрес! //LiquidCrystal_I2C lcd(0x3f, 20, 4); //LiquidCrystal_I2C lcd(0x27, 20, 4); void setup() { Serial.begin(9600); lcd.init(); lcd.backlight(); lcd.clear(); } void loop() { // для примера потенциометр подключен к A0 int perc = map(analogRead(0), 0, 1023, 0, 100); //fillBar0 принимает аргументы (столбец, строка, длина полосы, значение в % (0 - 100) ) fillBar0(0, 0, 10, perc); fillBar0(0, 1, 16, perc); delay(50); } void fillBar0(byte start_pos, byte row, byte bar_length, byte fill_percent) { byte infill = round((float)bar_length * fill_percent / 100); lcd.setCursor(start_pos, row); if (infill == 0) lcd.write(16); else lcd.write(255); for (int n = 1; n < bar_length - 1; n++) { if (n < infill) lcd.write(255); if (n >= infill) lcd.write(16); } if (infill == bar_length) lcd.write(255); else lcd.write(16); }
/* Скетч примера полосы загрузки на символьном LCD дисплее 1602/2004 итд. */ // библиотеки дисплея #include#include // создаём дисплей // дисплей 1602. Если не работает, используйте другой адрес LiquidCrystal_I2C lcd(0x3f, 16, 2); //LiquidCrystal_I2C lcd(0x27, 16, 2); // дисплей 2004. Если не работает, используйте другой адрес! //LiquidCrystal_I2C lcd(0x3f, 20, 4); //LiquidCrystal_I2C lcd(0x27, 20, 4); void setup() { Serial.begin(9600); lcd.init(); lcd.backlight(); lcd.clear(); // инициализация символов для отрисовки. При использовании каких то своих символов, // нужно вызывать эту функцию перед отрисовкой графика/полосы! initBar1(); } void loop() { // для примера потенциометр подключен к A0 int perc = map(analogRead(0), 0, 1023, 0, 100); //fillBar1 принимает аргументы (столбец, строка, длина полосы, значение в % (0 - 100) ) fillBar1(0, 0, 10, perc); fillBar1(0, 1, 16, perc); delay(50); } void initBar1() { // необходимые символы для работы // создано в http://maxpromer.github.io/LCD-Character-Creator/ byte right_empty[8] = {0b11111, 0b00001, 0b00001, 0b00001, 0b00001, 0b00001, 0b00001, 0b11111}; byte left_empty[8] = {0b11111, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b11111}; byte center_empty[8] = {0b11111, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111}; lcd.createChar(0, left_empty); lcd.createChar(1, center_empty); lcd.createChar(2, right_empty); } void fillBar1(byte start_pos, byte row, byte bar_length, byte fill_percent) { byte infill = round((float)bar_length * fill_percent / 100); lcd.setCursor(start_pos, row); if (infill == 0) lcd.write(0); else lcd.write(255); for (int n = 1; n < bar_length - 1; n++) { if (n < infill) lcd.write(255); if (n >= infill) lcd.write(1); } if (infill == bar_length) lcd.write(255); else lcd.write(2); }
/* Скетч примера полосы загрузки на символьном LCD дисплее 1602/2004 итд. */ // библиотеки дисплея #include#include // создаём дисплей // дисплей 1602. Если не работает, используйте другой адрес LiquidCrystal_I2C lcd(0x3f, 16, 2); //LiquidCrystal_I2C lcd(0x27, 16, 2); // дисплей 2004. Если не работает, используйте другой адрес! //LiquidCrystal_I2C lcd(0x3f, 20, 4); //LiquidCrystal_I2C lcd(0x27, 20, 4); void setup() { Serial.begin(9600); lcd.init(); lcd.backlight(); lcd.clear(); // инициализация символов для отрисовки. При использовании каких то своих символов, // нужно вызывать эту функцию перед отрисовкой графика/полосы! initBar2(); } void loop() { // для примера потенциометр подключен к A0 int perc = map(analogRead(0), 0, 1023, 0, 100); //fillBar2 принимает аргументы (столбец, строка, длина полосы, значение в % (0 - 100) ) fillBar2(0, 0, 10, perc); fillBar2(0, 1, 16, perc); delay(50); } void initBar2() { // необходимые символы для работы // создано в http://maxpromer.github.io/LCD-Character-Creator/ byte right_empty[8] = {0b11111, 0b00001, 0b00001, 0b00001, 0b00001, 0b00001, 0b00001, 0b11111}; byte left_empty[8] = {0b11111, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b11111}; byte center_empty[8] = {0b11111, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111}; byte left_full[8] = {0b11111, 0b10000, 0b10111, 0b10111, 0b10111, 0b10111, 0b10000, 0b11111}; byte right_full[8] = {0b11111, 0b00001, 0b11101, 0b11101, 0b11101, 0b11101, 0b00001, 0b11111}; byte center_full[8] = {0b11111, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b00000, 0b11111}; lcd.createChar(0, left_empty); lcd.createChar(1, center_empty); lcd.createChar(2, right_empty); lcd.createChar(3, left_full); lcd.createChar(4, center_full); lcd.createChar(5, right_full); } void fillBar2(byte start_pos, byte row, byte bar_length, byte fill_percent) { byte infill = round((float)bar_length * fill_percent / 100); lcd.setCursor(start_pos, row); if (infill == 0) lcd.write(0); else lcd.write(3); for (int n = 1; n < bar_length - 1; n++) { if (n < infill) lcd.write(4); if (n >= infill) lcd.write(1); } if (infill == bar_length) lcd.write(5); else lcd.write(2); }
/* Скетч примера полосы загрузки на символьном LCD дисплее 1602/2004 итд. */ // библиотеки дисплея #include#include // создаём дисплей // дисплей 1602. Если не работает, используйте другой адрес LiquidCrystal_I2C lcd(0x3f, 16, 2); //LiquidCrystal_I2C lcd(0x27, 16, 2); // дисплей 2004. Если не работает, используйте другой адрес! //LiquidCrystal_I2C lcd(0x3f, 20, 4); //LiquidCrystal_I2C lcd(0x27, 20, 4); boolean change_flag = true; // флаг для 3 типа полосы void setup() { Serial.begin(9600); lcd.init(); lcd.backlight(); lcd.clear(); // инициализация символов для отрисовки. При использовании каких то своих символов, // нужно вызывать эту функцию перед отрисовкой графика/полосы! initBar3(); } void loop() { // для примера потенциометр подключен к A0 int perc = map(analogRead(0), 0, 1023, 0, 100); //fillBar3 принимает аргументы (столбец, строка, длина полосы, значение в % (0 - 100) ) fillBar3(0, 0, 10, perc); fillBar3(0, 1, 16, perc); delay(50); } void initBar3() { // необходимые символы для работы // создано в http://maxpromer.github.io/LCD-Character-Creator/ byte right_empty[8] = {0b11111, 0b00001, 0b00001, 0b00001, 0b00001, 0b00001, 0b00001, 0b11111}; byte left_empty[8] = {0b11111, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b11111}; byte center_empty[8] = {0b11111, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111}; byte bar2[] = {0b11111, 0b11000, 0b11000, 0b11000, 0b11000, 0b11000, 0b11000, 0b11111}; byte bar3[] = { B11111, B11100, B11100, B11100, B11100, B11100, B11100, B11111}; byte bar4[] = { B11111, B11110, B11110, B11110, B11110, B11110, B11110, B11111}; lcd.createChar(0, left_empty); lcd.createChar(1, center_empty); lcd.createChar(2, right_empty); lcd.createChar(3, bar2); lcd.createChar(4, bar3); lcd.createChar(5, bar4); } void fillBar3(byte start_pos, byte row, byte bar_length, byte fill_percent) { byte infill = bar_length * fill_percent / 10; byte fract = infill % 10; infill = infill / 10; // change_flag - true слева, false справа if (infill < bar_length - 1) { if (!change_flag) { change_flag = true; byte bar2[] = {0b11111, 0b11000, 0b11000, 0b11000, 0b11000, 0b11000, 0b11000, 0b11111}; byte bar3[] = { B11111, B11100, B11100, B11100, B11100, B11100, B11100, B11111}; byte bar4[] = { B11111, B11110, B11110, B11110, B11110, B11110, B11110, B11111}; lcd.createChar(3, bar2); lcd.createChar(4, bar3); lcd.createChar(5, bar4); } } else { if (change_flag) { change_flag = false; byte leftbar1[] = { B11111, B10001, B10001, B10001, B10001, B10001, B10001, B11111}; byte leftbar2[] = { B11111, B11001, B11001, B11001, B11001, B11001, B11001, B11111}; byte leftbar3[] = { B11111, B11101, B11101, B11101, B11101, B11101, B11101, B11111}; lcd.createChar(3, leftbar1); lcd.createChar(4, leftbar2); lcd.createChar(5, leftbar3); } } lcd.setCursor(start_pos, row); if (infill == 0) { if (fract >= 0 && fract < 2) lcd.write(0); else if (fract >= 2 && fract < 4) lcd.write(0); else if (fract >= 4 && fract < 6) lcd.write(3); else if (fract >= 6 && fract < 8) lcd.write(4); else if (fract >= 8) lcd.write(5); } else lcd.write(255); for (int n = 1; n < bar_length - 1; n++) { if (n < infill) lcd.write(255); if (n == infill) { if (fract >= 0 && fract < 2) lcd.write(1); else if (fract >= 2 && fract < 4) lcd.write(0); else if (fract >= 4 && fract < 6) lcd.write(3); else if (fract >= 6 && fract < 8) lcd.write(4); else if (fract >= 8) lcd.write(5); } if (n > infill) lcd.write(1); } if (infill == bar_length - 1) { if (fract >= 0 && fract < 2) lcd.write(2); else if (fract >= 2 && fract < 4) lcd.write(3); else if (fract >= 4 && fract < 6) lcd.write(4); else if (fract >= 6 && fract < 8) lcd.write(5); else if (fract >= 8) lcd.write(255); } else if (infill == bar_length) lcd.write(255); else lcd.write(2); }
Все примеры также есть на GitHub проекта