ТРЕНД (график) на символьном дисплее 1602

yul-i-an
yul-i-an аватар
Offline
Зарегистрирован: 10.12.2012

Решил реализовать вывод тренда на символьный дисплей путем динамической генерации "символов" в соответствии с поступающими данными для отслеживания динамики входных данных (температуры, мощности и т.п.).

yul-i-an
yul-i-an аватар
Offline
Зарегистрирован: 10.12.2012

Выкладываю тестовый код на котором изучаю процесс

#include <LiquidCrystal.h>///yul-i-an 2016
LiquidCrystal lcd(10, 9, 5, 4, 3, 2);///
int data[30]={
  1,2,3,5,8,5,5,7,8,3,1,1,2,3,3,3,5,6,7,8,8,8,6,5,5,6,4,3,2,1};//данные для постройки графика
byte graph[8];

void setup(){
  lcd.begin(16, 2);
}
void loop(){
  //тут будем искать максимум для масштабирования графика
  //и вычислять цену деления одного пикселя
  for (int s=0; s<=5; s++){
    memset(graph, 0, sizeof(graph));//обнуляем генерируемый символ
    for (int y=6; y >=0; y--){//перебираем строки
      for (int x=0; x <5; x++){//перебираем столбец
        if (data[(s*5)+x]>y){//проверка на необходимость установки пикселя
          //в соответствии с масштабом (пока не реализовано)
          graph[6-y] |= (1 << 5-x-1);//устанавливаем пиксель в столбце
        }
      }
    }
    lcd.createChar(s, graph);
    //lcd.clear();
    lcd.setCursor(s, 0);//выводим сгенерированый символ
    lcd.write(byte(s));
    lcd.setCursor(0,1);//выводим номер выводимого пикселя для отладки
    lcd.print(s);
  }
  //прокручиваем график
  delay(100);
  byte v=data[0];
  for (byte i=0;i<29;i++){
    data[i]=data[i+1];
  }
  data[29]=v;
}

Код в железе не проверял, так что может лагать (тестовый варийант).

Вот что получилось на данный момент

Приведенный код пока работает с целыми числами от 0-8 (масштабирование на подходе)

Вроде всё работает как задумано, но я планировал использовать одно пользовательское знакоместо в памяти  LCD для генерации всего графика, но пока это не получается (работаю над этим), если между выводом каждого символа отчищать экран то всё получаеться, работаю над этим.

Кому показалось интересным подключайтесь.

Задачи следующие:

- организовать вывод путем использования одного пользовательского символа

  (для вывода графика более 8 символов длиной или сразу нескольких)

- организовать вывод в виде линии

- автомасштабирование (на стадии реализации)

Особой пользы кроме как слежение за динамикой в нем нет, больше академический интерес, ну и оживление устройств на символьных LCD (метеостанций, регуляторов и т.д.).

 

yul-i-an
yul-i-an аватар
Offline
Зарегистрирован: 10.12.2012

Код с автомасштабированием и случайными входными данными.

Код сырой, не обращайте внимания.

#include <LiquidCrystal.h>///
LiquidCrystal lcd(10, 9, 5, 4, 3, 2);///
int data[30]={
  1,2,3,5,8,5,5,10,15,24,1,1,2,3,3,3,5,6,7,8,8,8,6,5,4,6,4,3,2,1};
byte graph[8];
#define maximum 100
//#define stp maximum/8
byte mx=data[0];
void setup(){
  lcd.begin(16, 2);
}
void loop(){
  //тут будем искать максимум для масштабирования графика
  //и вычислять цену деления одного пикселя
  for (int s=0; s<=5; s++){
    memset(graph, 0, sizeof(graph));//обнуляем генерируемый символ
    for (int y=6; y >=0; y--){//перебираем строки
      for (int x=0; x <5; x++){//перебираем столбец
        if (data[(s*5)+x]>y*((mx*1.3)/8)){//проверка на необходимость установки пикселя
          //if (random(0, maximum)>y*stp){
          //в соответствии с масштабом (пока не реализовано)
          graph[6-y] |= (1 << 5-x-1);//устанавливаем пиксель в столбце
        }
      }
    }
    lcd.createChar(s, graph);
    //lcd.clear();
    lcd.setCursor(s, 0);//выводим сгенерированый символ
    lcd.write(byte(s));
    lcd.setCursor(0,1);//выводим максимального
    lcd.print("max");
    lcd.print(mx);
    lcd.print(" ");
        lcd.setCursor(8,1);//выводим максимального
    lcd.print("stp");
    lcd.print((mx*1.3)/8);
    lcd.print(" ");
  }
  //прокручиваем график
  delay(200);
  mx=data[0];
  for (byte i=0;i<29;i++){
    data[i]=data[i+1];
  }
  data[29]=((data[28]*4)+random(0, maximum))/5;
  for (byte i=29;i>0;i--){
    if (data[i]>mx){
      mx=data[i];
    }
  }
  
}

max - максимум шкалы

stp - цена деления одного пикселя

Клапауций 234
Offline
Зарегистрирован: 24.10.2016

попробуй рисовать бары через один пиксель по горизонтали(три бара на одно знакоместо индикатора) - не будут визуально бить по глазам разрывы между пять через пять баров.

yul-i-an
yul-i-an аватар
Offline
Зарегистрирован: 10.12.2012

Вообщем код преобразовался в 2 функции

void trend(short int *in_data, byte len, byte x, byte y){//массив с данными, длина графика в символах, координаты вывода на экран
  short int mx=0;
  short int mn;
  short int sc=0;//цена деления
  byte graph[8];//для генерации символа
  memset(graph, 0, sizeof(graph));//обнуляем генерируемый символ
  for (byte i=(len*5)-1;i>0;i--){
    if (in_data[i]>mx){
      mx=in_data[i];
    }
    mn=mx;
    if (in_data[i]<mn){
      mn=in_data[i];
    }
  }
  sc=((mx-mn)/8);//расчет цены деления
  for (int a=0; a<=(len-1); a++){//////
    memset(graph, 0, sizeof(graph));//обнуляем генерируемый символ
    for (int y=8; y > 0; y--){//перебираем строки
      for (int x=0; x <5; x++){//перебираем столбец
        if (((in_data[(a*5)+x])-mn+sc)>=(sc*y)){
          graph[8-y] |= (1 << 5-x-1);//устанавливаем пиксель в столбце
        }
      }
    }
    lcd.createChar(a, graph);
    lcd.setCursor((a+x), y);//выводим сгенерированый символ
    lcd.write(byte(a));
  }
}

void addVal(float val, short int *in_data, byte len){   //значение, массив в который добовляем, длина графика в символах <8
  for (byte i=0;i<(len*5)-1;i++){
    in_data[i]=in_data[i+1];//прокручиваем
  }
  data[(len*5)-1]=(val*100);//вносим новое значение
}

функция trend выводид тренд на дисплей, а addVal добовляет в массив новое значение.

Вот так это выглядит в готовом девайсе (ПИ терморегулятор)

На снимке график за последние 12 часов. (использовано 4 символа, данные добавляются каждые 1ч 20м).

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

yul-i-an
yul-i-an аватар
Offline
Зарегистрирован: 10.12.2012

К теме.

Заготовка для вывода на символьный дисплей псевдо механического счетчика как на старых магнитофонах. Сырой эксперимент с динамичиской генерацией символа.

#include <LiquidCrystal.h>///
LiquidCrystal lcd(10, 9, 5, 4, 3, 2);///
byte numLent[80] = {
  B01110,//0
  B10001,
  B10001,
  B10001,
  B10001,
  B01110,
  B00000,
  B01100,//1
  B00100,
  B00100,
  B00100,
  B00100,
  B11111,
  B00000,
  B01110,//2
  B10001,
  B00010,
  B00100,
  B01000,
  B11110,
  B00000,
  B01110,//3
  B10001,
  B00110,
  B00001,
  B10001,
  B01110,
  B00000,
  B00011,//4
  B00101,
  B01001,
  B11111,
  B00001,
  B00001,
  B00000,
  B11111,//5
  B10000,
  B11110,
  B00001,
  B10001,
  B01110,
  B00000,
  B01110,//6
  B10000,
  B11110,
  B10001,
  B10001,
  B01110,
  B00000,
  B11111,//7
  B10001,
  B00010,
  B00100,
  B01000,
  B01000,
  B00000,
  B01110,//8
  B10001,
  B01110,
  B10001,
  B10001,
  B01110,
  B00000,
  B01110,//9
  B10001,
  B10001,
  B01111,
  B00001,
  B01110,
  B00000,
};
byte graph[8];//для генерации символа
byte n=0;
void setup(){
  lcd.begin(16, 2);
  lcd.setCursor(0, 0);//выводим сгенерированый символ
  lcd.print("0");
}

void loop(){
  memset(graph, 0, sizeof(graph));//обнуляем генерируемый символ
  for (byte ln=0;ln<6;ln++){
    graph[ln]=numLent[ln+n];
  }
  lcd.createChar(0, graph);
  lcd.setCursor(0, 0);//выводим сгенерированый символ
  lcd.write(byte(0));
  delay (62);//62
  n=n+1;
  if(n>64){
    n=0;
  }
}

 

yul-i-an
yul-i-an аватар
Offline
Зарегистрирован: 10.12.2012

Код тренда немного подправил

void trend(short int *in_data, byte len, byte x, byte y, byte sts){//массив с данными, длина графика в символах, координаты вывода на экран
  short int mx=0;
  short int mn=10000;
  short int sc=0;//цена деления
  byte graph[8];//для генерации символа
  memset(graph, 0, sizeof(graph));//обнуляем генерируемый символ
  for (byte i=(len*5)-1;i>0;i--){
    if (in_data[i]>mx){
      mx=in_data[i];
    }
    if (in_data[i]<mn){
      mn=in_data[i];
    }
  }
  lcd.setCursor(10, 1);//вывод максимума и минимума
  lcd.print(byte(mn/100));//делим потому что сохранял float в byte
  lcd.print("-");
  //   lcd.setCursor(4, 1);
  lcd.print((mx/100));
  lcd.print("S");
  sc=((mx-mn)/8);//расчет цены деления
  for (int a=0; a<=(len-1); a++){//////
    memset(graph, 0, sizeof(graph));//обнуляем генерируемый символ
    for (int y=8; y > 0; y--){//перебираем строки
      for (int x=0; x <5; x++){//перебираем столбец
        if (((in_data[(a*5)+x])-mn+(sc*2))>=(sc*y)){
          graph[8-y] |= (1 << 5-x-1);//устанавливаем пиксель в столбце
        }
      }
    }
    lcd.createChar(a+sts, graph);//генерируем символ
    lcd.setCursor((a+x), y);//устанавливаем курсор
    lcd.write(byte(a));//выводим сгенерированый символ
  }
}

void addDate(float val, short int *in_data, byte len){   //значение, массив в который добовляем, длина графика в символах <8
  for (byte i=0;i<(len*5)-1;i++){
    in_data[i]=in_data[i+1];//прокручиваем
  }
  data[(len*5)-1]=(val*100);//вносим новое значение
}

 

alexhaf
Offline
Зарегистрирован: 01.02.2019

yul-i-an пишет:

Код тренда немного подправил


Ну и как? Продолжения не будет?

vvadim
Offline
Зарегистрирован: 23.05.2012

alexhaf пишет:

yul-i-an пишет:

Код тренда немного подправил


Ну и как? Продолжения не будет?

 

уже три года прошло, вряд ли))))

yul-i-an
yul-i-an аватар
Offline
Зарегистрирован: 10.12.2012

Пока другим занят. По теме и так все понятно, кому интересно разбирутся.