Нам понадобится Ардуино УНО, дисплей 1602(и переходник i2c), датчик давления БМП180. Прошу прощения за качество фото, но какое есть.



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

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

Остальное комментариями в коде.

#include <Wire.h>
#include <SPI.h>
#include <LiquidCrystal_I2C.h> //Подключаем библиотеку для работы с LCD через i2c
LiquidCrystal_I2C lcd(0x3F,16,2);  // иницилизируем дисплей по его i2c адресу
//это нужно для подключения дополнительных символов дисплея
          #if defined(ARDUINO) && ARDUINO >= 100
          #define printByte(args)  write(args);
          #else
          #define printByte(args)  print(args,BYTE);
          #endif
//Создаем массив значений на дисплее, от пустого к полному.          
          uint8_t graf0[8] = {0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F};
          uint8_t graf1[8] = {B00000, B11111, B11111, B11111, B11111, B11111, B11111};
          uint8_t graf2[8] = {B00000, B00000, B11111, B11111, B11111, B11111, B11111};
          uint8_t graf3[8] = {B00000, B00000, B00000, B11111, B11111, B11111, B11111};
          uint8_t graf4[8] = {B00000, B00000, B00000, B00000, B11111, B11111, B11111};
          uint8_t graf5[8] = {B00000, B00000, B00000, B00000, B00000, B11111, B11111};
          uint8_t graf6[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B11111};
          uint8_t graf7[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B00000};
//Добавили символы для графиков

int stat[2][25]; //Возьмем 2х мерный массив, где один массив время(контроль действительности значения) и второй массив наши данные
//int stat[0]; //Давление
//int stat[1]; //Время


#include <BMP085.h>
BMP085 dps = BMP085();      // Подключаем датчик давления 
long Pressure = 0;
void setup(void) {
  lcd.init();  
  lcd.backlight(); //включаем подсветку
  lcd.createChar(0, graf0);// Подключаем доп. символы
  lcd.createChar(1, graf1);
  lcd.createChar(2, graf2);
  lcd.createChar(3, graf3);
  lcd.createChar(4, graf4);
  lcd.createChar(5, graf5);
  lcd.createChar(6, graf6);
  lcd.createChar(7, graf7);

}

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

void loop(void) {

if (((millis()/3600000)-stat[1][23]) >= 1.0) //заполняем текущее значение раз 1 час (3600000)
     {
       //сдвигаем массив влево, чтобы освободить предпоследнюю ячейку
         int i=0;
         for (i = 0; i < 24; i++) stat[0][i] = stat[0][i+1];
         for (i = 0; i < 24; i++) stat[1][i] = stat[1][i+1];
      
      dps.getPressure(&Pressure); //Записываем значения давления
      stat[0][23] = Pressure/13.33;
      stat[1][23] = millis()/3600000; 
      
      grafik(0, 2, 0); //строим график, вызов усложнен для построения разных графиков из массива, первое число номер массива, затем периодичность выборки данных, потом начальное значение в массиве. Мы берем первый массив(0), берем каждое второе значение(2) и начинаем с начала(0)
  }

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

int interval(int x) {
  int maxy = -5000;
  int inty = 0;
  int minx = minimum(x);

       for (int k = 0; k <= 24; k++) {
                                if (stat[1][k] != 0) {                     //если значение не ноль
                                if (stat[x][k] > maxy) maxy = stat[x][k];   //считаем максимальное значение
                                }}
                      if (maxy == -5000) maxy = 0;                                         
     //Строим интервал
     inty = maxy - minx;
     intx = inty/8;
     return intx;
  } 

При расчете интервала нам нужно минимальное значение в массиве:

int minimum(int d) {
      minx = 32767; 
      for (int i = 0; i <= 24; i++) {
                              if (stat[1][i] != 0)  {                 //если значение не ноль
                                if (stat[d][i] < minx) minx = stat[d][i];//считаем минимальное значение
                               }}
       if (minx == 50000) minx = 0;
       return minx;
  }

Строим сам график, если значений в массиве времени нет, выводим прочерк. Вызов функции усложнен для построения разных графиков из массива. Первое число номер массива, затем периодичность выборки данных, потом начальное значение в массиве. Мы берем первый массив(0), берем каждое второе значение(2) и начинаем с начала(0):

void grafik(int x, int y, int z) {
        lcd.setCursor(0, 1);
        lcd.print("Dav"); //Подпись на дисплее

           intx = interval(x); //вызовем функцию расчета интервала графика
           int minx = minimum(x); //Уточним минимальное значение
           for (int i=z; i <= 24; i= i + y){
              if (stat[x][i] == 0){                              //проверяем есть ли данные о времени
                lcd.print("-");                                  //если значений нет, отображаем прочерк
              } else if (stat[x][i] > (minx + intx*7)) {
                lcd.printByte(0);
              } else if (stat[x][i] > (minx + intx*6)) {
                lcd.printByte(1);
              }else if (stat[x][i] > (minx + intx*5)) {
                lcd.printByte(2);
              }else if (stat[x][i] > (minx + intx*4)) {
                lcd.printByte(3);
              }else if (stat[x][i] > (minx + intx*3)) {
                lcd.printByte(4);
              }else if (stat[x][i] > (minx + intx*2)) {
                lcd.printByte(5);
              }else if (stat[x][i] > (minx + intx)) {
                lcd.printByte(6);
              } else {
                lcd.printByte(7);
              }
           }
         
  }

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


  1. shadowpanther
    14.01.2016 03:23
    +2

    В последнем куске кода можно использовать функцию map() вместо этажерки if'ов:
    lcd.printByte(map(stat[x][i], minx, minx+intx*8, 7, 0))
    Ну и естественно вместо intx станет логичнее считать maxx

    «i = i + y» ==> «i += y»

    if (stat[1][i] != 0) { //если значение не ноль <== тут явно просится stat[d][i]
    if (stat[d][i] < minx)

    «minx = 32767» ==> «minx = stat[d][0]»


    1. tramp_m
      14.01.2016 14:18

      map() надо будет посмотреть.
      stat[1][i] это массив времени, stat[d][i] как раз может быть, и 0, и любым другим числом. Это нужно как раз чтобы не учитывать ячейки где еще нет значения и они равны 0. Изначально строилось три разных графика. По трем показателям(массивам), и только время не может быть равно 0, остальное варьируется.

      «minx = 32767» ==> «minx = stat[d][0]»
      а если следующее значение больше, а если меньше? в моей версии все значимое гарантированно меньше


      1. shadowpanther
        14.01.2016 14:38
        +1

        Да, не уследил логики со вторым массивом. С другой стороны, если нужна проверка на «реальность» значения, можно и само значение проверять, оно тоже ненулевое.

        Если следующее меньше — само станет минимумом, если больше — оно нам не нужно. Можно за один перебор выбрать и минимум, и максимум:

        dmin = data[0];
        dmax = data[0];
        for(i=1; i<size; i++) {
          dmin = min(dmin, data[i]);
          dmax = max(dmax, data[i]);
        }
        


        1. tramp_m
          14.01.2016 14:54

          Значение может быть 0, это нормальное поведение. Например температура бывает 0.
          а вот min() и max() тоже не видел, это упрощает да =-)


          1. shadowpanther
            14.01.2016 14:58

            Ну самый тривиальный вариант, если нас интересует только график (не статистика), при получении первого значения записать его в весь массив. Тогда весь график будет «рисовабелен» с первого такта. Если нужны прочерки — там да, вариант со временем или хотя бы массивом bool'ов.

            Про функции — рекомендую глянуть www.arduino.cc/en/Reference/HomePage — там всё основное есть.


            1. tramp_m
              14.01.2016 15:17

              функции уже посмотрел, удивило что вроде там же смотрел и не увидел =-((

              получается можно выкинуть interval() и minimum() и оставить только grafik()

              void grafik(int x, int y, int z) {
                      lcd.setCursor(0, 1);
                      lcd.print("Dav");
                      
                         int minx = stat[x][0];
                         int maxy = stat[x][0];
                          for(int i=z; i<=24; i++) {
                            minx = min(minx, stat[x][i]);
                            maxy = max(maxy, stat[x][i]);
                          }
                         for (int i=z; i <= 24; i= i + y){
                            if (stat[1][i] == 0){
                              lcd.print("-");                                   //если значений нет
                            } else {
                         lcd.printByte(map(stat[x][i], minx, maxy, 7, 0))
                      }
                       
                }
              


              1. shadowpanther
                14.01.2016 15:23

                Да, уже намного проще читать.
                Про i += y из моего первого комментария еще напомню.


                1. tramp_m
                  14.01.2016 15:25

                  угу, я там еще скобочку не закрыл. не хватает } в конце


  1. tramp_m
    14.01.2016 15:23

    не туда