Graph a single line from analog read in a TFT

Hi all

I am trying to get the following code to work

It is a basic plotter for a value on a TFT screen

The code is modified from this post Graph a single line from analog read in a TFT

All I get on the screen is the text and vert line to the left



#include <Adafruit_GFX.h> // Hardware-specific library
#include <Adafruit_ST7796S.h>

#define display_CS 10
#define display_RST 8
#define display_DC 9
#define display_MOSI 11
#define display_CLK 13

Adafruit_ST7796S display(display_CS, display_DC, display_MOSI, display_CLK, display_RST);


#define BLACK   0x0000
#define BLUE    0x001F
#define RED     0xF800
#define GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF

int xPos = 0, xPosPrev = 0;
int n = 0;
int graphHeightPrev = 0;

int16_t ht = 16, top = 3, line, lines = 15, scroll;

void setup() {
  Serial.begin(9600);
  Serial.print("Oih !");
  display.init(320, 480, 0, 0, ST7796S_BGR);  //screen size 74 x 284
  display.invertDisplay(1);
  display.fillScreen(BLACK);
  display.setRotation(1);
  display.setTextColor(WHITE);
  display.setCursor(80, 100);
  display.setTextSize(3);
  display.println("T.I.S.M");  // mum, we are serious.   google it
  delay(10);
}

void loop() {

  int sensor = random(200); // for testing
    //int graphHeight = map(sensor, 410, 614, 0, display.height());
  int graphHeight = map(sensor, 205, 307, 0, display.height());
  display.drawLine(xPosPrev, display.height() - graphHeightPrev, xPos, display.height() - graphHeight, WHITE);
  graphHeightPrev = graphHeight;
  if (xPos >= 480) {
    xPos = 20;
    xPosPrev = 20;
    display.fillScreen(BLACK);
   
    display.drawLine(20, 60, 20, 260, WHITE);
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(30, 50);
    display.print("value");
    display.setCursor(30, 260);
    display.print("value");
  }
  else {
    xPosPrev = xPos;
    xPos++;
  }
  delay(16);
}

Sometimes, some Serial.print statements can be quite illuminating.

Here's the values your code is passing to display.drawline, based on 'display.height()' returning 320.

X1=0 Y1=320 X2=0 Y2=941
X1=0 Y1=941 X2=1 Y2=809
X1=1 Y1=809 X2=2 Y2=734
X1=2 Y1=734 X2=3 Y2=781
X1=3 Y1=781 X2=4 Y2=555
X1=4 Y1=555 X2=5 Y2=737
X1=5 Y1=737 X2=6 Y2=511
X1=6 Y1=511 X2=7 Y2=718
X1=7 Y1=718 X2=8 Y2=577
X1=8 Y1=577 X2=9 Y2=621
X1=9 Y1=621 X2=10 Y2=837
X1=10 Y1=837 X2=11 Y2=445

I think you'll agree that those Y values are not realistic for a display with a height of only 320 pixels.

the map is wrong..
try..

 int graphHeight = map(sensor, 1, 200, 60, display.height()-60);

good luck.. ~q

Indeed, you are correct. I didnt think of that….:woozy_face:

Yes, that works. Thankyou for pointing it out…

So…now my next question is….

Can I make it keep scrolling once it hits the R/H side so that old values move left, like a histogram ?

If your display controller supports hardware scrolling, it's easy enough. And since most of them only support hardware scrolling vertically in portrait mode, since you're in landscape mode you get horizontal hardware scrolling.

If you have to do software scrolling it's still possible, just a little more work. Keep track of your last n Y values in an array with the same number of pixels as your graph width, rolling around when you reach the end and keep track of which one you want displayed first.

Arrays are definitely not my strong point :woozy_face:

going to flicker without a sprite or canvas and hope you're using something with a bit of ram like esp32..

#include <Adafruit_GFX.h> // Hardware-specific library
#include <Adafruit_ST7796S.h>
#include <vector>

#define display_CS 10
#define display_RST 8
#define display_DC 9
#define display_MOSI 11
#define display_CLK 13

Adafruit_ST7796S display(display_CS, display_DC, display_MOSI, display_CLK, display_RST);


#define BLACK   0x0000
#define BLUE    0x001F
#define RED     0xF800
#define GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF

int xPos = 0, xPosPrev = 0;
int n = 0;
int graphHeightPrev = 0;
int border = 20;
int sampleCount = 480 - border;

std::vector<int> v;

int16_t ht = 16, top = 3, line, lines = 15, scroll;

void setup() {
  Serial.begin(9600);
  Serial.print("Oih !");
  display.init(320, 480, 0, 0, ST7796S_BGR);  //screen size 74 x 284
  display.invertDisplay(1);
  display.fillScreen(BLACK);
  display.setRotation(1);
  display.setTextColor(WHITE);
  display.drawLine(12, 60, 12, 260, WHITE);
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(20, 40);
  display.print("value");
  display.setCursor(20, 270);
  display.print("value");
  display.setCursor(display.width() / 2 - 10, display.height() / 2);
  display.print("Buffering..");

}

void loop() {
  int sensor = random(200); // for testing
  //map the test values into screen cords..
  int graphHeight = map(sensor, 1, 200, 60, 260);
  //add to the end of the vector..
  v.push_back(graphHeight);
  //have too many, remove first..
  if (v.size() > sampleCount) v.erase(v.begin());
  //is it time to draw..
  if (v.size() == sampleCount)
  {
    xPos = 20;
    xPosPrev = 20;
    graphHeightPrev = 100;
    //clear out old graph..
    display.fillRect(20, 58, 460, 204, BLACK);
    //draw all line in the vector..
    for ( int n : v)
    {
      graphHeight = n;
      display.drawLine(xPosPrev, display.height() - graphHeightPrev, xPos, display.height() - graphHeight, WHITE);
      graphHeightPrev = graphHeight;
      xPosPrev = xPos;
      xPos++;
    }
  }
  delay(100);
}

untested sorry..~q

Will a Teensy4 be enough :grin:

an alternative approach I use when I need to plot data over a long period of time where the number of samples is greater than the width of the TFT is to plot every second, third, fourth, etc sample automatically adjusting as samples are acquired.
e.g. if I have 1000 samples and the TFT width is 320 plot sample 1 4 7 10 etc
this shows 1943 temperature samples taken from a heat pump plotted on a 2.8" 240*320 TFT CYD display


the samples are also saved on to SD card and transmitted over WiFi to a remote server for later analysis, e.g.

Yes, that worked well.

i re worked it to show he graph in a smaller rectangle so I can integrate it into a few other projects

#include <Adafruit_GFX.h> // Hardware-specific library

#include <Adafruit_ST7796S.h>

#include

#define display_CS 10

//#define display_RST 8

#define display_DC 9

#define display_MOSI 11

#define display_CLK 13

Adafruit_ST7796S display(display_CS, display_DC, display_MOSI, display_CLK);

Adafruit_ST7796S display2(7, display_DC, display_MOSI, display_CLK );

#define BLACK 0x0000

#define BLUE 0x001F

#define RED 0xF800

#define GREEN 0x07E0

#define CYAN 0x07FF

#define MAGENTA 0xF81F

#define YELLOW 0xFFE0

#define WHITE 0xFFFF

int xPos = 0, xPosPrev = 0;

int n = 0;

int graphHeightPrev = 0;

int border = 20; // how far from the left

int sampleCount = 200 - border; // width of plot

std::vector v;

void setup() {

Serial.begin(9600);

Serial.print("Oih !");

display.init(320, 480, 0, 0, ST7796S_BGR); //screen size 74 x 284

display.invertDisplay(1);

display.fillScreen(BLACK);

display.setRotation(1);

display.setTextColor(BLUE);

display.setCursor(50,75);

display.print("Buffering..");

}

void loop(){

graph();

}

void graph() {

float dispheight = 200; // was display.height

int sensor = random( 25,75); // for testing

//map the test values into screen cords..

int graphHeight = map(sensor, 1, 100, 100, 150);

//add to the end of the vector..

v.push_back(graphHeight);

//have too many, remove first..

if (v.size() > sampleCount) v.erase(v.begin());

//is it time to draw..

if (v.size() == sampleCount)

{

xPos = 20;

xPosPrev = 20;

graphHeightPrev = 100;

//clear out old graph..

display.fillRect(20, 50, 180, 50,YELLOW);

//draw all line in the vector..

for ( int n : v)

{

  graphHeight = n;

display.drawLine(xPosPrev, dispheight - graphHeightPrev, xPos, dispheight - graphHeight, RED);

  graphHeightPrev = graphHeight;

  xPosPrev = xPos;

  xPos++;

}

}

delay(100);

}

Yeah, yeah,..I know..

But there was a glitch in the matrix.. I even tried the bbcode ….

So……after more tweeking, I have a plot that starts at the left, progresses to the right, then continues to scroll from the right, and the slower you make it, the less flicker you get. ( it really only flickers when running v fast)

Code has plot as red on yellow with black background

#include <Adafruit_GFX.h>  // Hardware-specific library
#include <Adafruit_ST7796S.h>
#include <vector>
#include <RotaryEncoder.h>

#define display_CS 10
#define display_DC 9
#define display_MOSI 11
#define display_CLK 13

Adafruit_ST7796S display(display_CS, display_DC, display_MOSI, display_CLK);
RotaryEncoder Rotary(&RotaryChanged, 22, 23, 28);  // Pins 2 (DT), 3 (CLK), 28 (SW) - 28 is just a null number as the (SW) is not used

#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define WHITE 0xFFFF

int xPos = 0, xPosPrev = 0;
int n = 0;
int graphHeightPrev = 0;
int border = 20;                 // how far from the left
int sampleCount = 200 - border;  // width of plot
int encCounter ;

std::vector<int> v;

void RotaryChanged() {
  const unsigned int state = Rotary.GetState();
  if (state & DIR_CW)
    encCounter++;
  if (state & DIR_CCW)
    encCounter--;
}
void setup() {
  Serial.begin(9600);
  Serial.print("Oih !");
  display.init(320, 480, 0, 0, ST7796S_BGR);  //screen size 74 x 284
  display.invertDisplay(1);
  display.fillScreen(BLACK);
  display.setRotation(1);
  display.fillScreen(BLACK);
  display.fillRect(20, 49, 180, 52, YELLOW);
}
void loop() {
  graph1();
  if (sampleCount < 181) {graph2();}
  }

void graph1() {
  Serial.println(encCounter);
 
  int dispheight = 200;
  //int sensor = random(1, 100);  // for testing
  int sensor = encCounter;  // for testing
  int graphHeight = map(sensor, 1, 100, 100, 150);

  display.drawLine(xPosPrev, dispheight - graphHeightPrev, xPos, dispheight - graphHeight, RED);
  graphHeightPrev = graphHeight;
  if (xPos >= 200) {
    xPos = 20;
    xPosPrev = 20;

  } else {
    xPosPrev = xPos;
    xPos++;
  }
  display.fillRoundRect(0, 50, 20, 200, 0, BLACK);
  delay(100);
}
void graph2() {
  float dispheight = 200;       // was display.height
  //int sensor = random(1,100);  // for testing
  int sensor = encCounter;  // for testing
  
  //map the test values into screen cords..
  int graphHeight = map(sensor, 1, 100, 100, 150);

  //display.drawLine(xPosPrev, dispheight - graphHeightPrev, xPos, dispheight - graphHeight, WHITE);
  //add to the end of the vector..
  v.push_back(graphHeight);
  //have too many, remove first..
  if (v.size() > sampleCount) v.erase(v.begin());
  //is it time to draw..
  if (v.size() == sampleCount) {
    xPos = 20;
    xPosPrev = 20;
    graphHeightPrev = 100;
    //clear out old graph..
    display.fillRect(20, 49, 180, 52, YELLOW);
    //draw all line in the vector..
    for (int n : v) {
      graphHeight = n;
      display.drawLine(xPosPrev, dispheight - graphHeightPrev, xPos, dispheight - graphHeight, RED);
      graphHeightPrev = graphHeight;
      xPosPrev = xPos;
      xPos++;
    }
  }

  delay(100);
}

sweet, i was using a sprite, eliminates all flicker even at high speeds..
with the AdafruitGFX would be like canvas and maybe you could use just a 1 bit canvas, 1 bit per pixel if it's only 2 colors, might be worth a play..
I'm using LovyanGFX simulating TFT screens using SDL2 on an UnoQ in SBC mode or in other words i'm using linux..
fun stuff.. ~q

I guess I could use sprites, but as I probably will update only every 30s-1m its not really an issue.

Its part of a larger engine monitor project using 4-6” tft displays, where one tft has typical dial gauges to display values ( oil press, temp,egt,rpm,boost,whatever) and the other will have a plot graph to show trend history, so simples is niceness and easy

Running on a Teensy 4.0