I'm busy graphing temperature on a KS0108 GLCD. I've plotted a set point line for the PID controller to follow (Solder reflow oven controller) then I want to draw lines vertically as the time progresses and the temperature changes.
My question is that I want to test when the vertical line indicating the temperature intersect with the set point line so that when it happens the set point line gets cleared so that it doesn't disappear within the vertical lines but that you can see it. I hope I'm making sense here. When the set point line is horizontal it's no problem, but when I've plotted it at an angle it's a bit of a different story. I thought about using the coordinates of the endpoint of the vertical stages of the line to calculate the angle of the line and then use the angle with a tan function to calculate the the height as the horizontal line extends according to time and temperature and then to plot it by using the "GLCD.SetDot" command to set on pixels as I move along. I can make this work and I am halfway there but I was hoping that there would be a better way since the method I plan on using might not yield the exact same line as the set point line.
I am hoping that someone would be able to shed some light on the issue. I've attached a image that might explain it a bit better. If there's any questions please don't hesitate to let me know.
It's conceptually simple enough to do, but requires you to calculate the height of the 'set point' line at a given X coordinate. How you would do that would depend on how the 'set point' line is defined in your sketch.
What you want to do is create a slope-intercept function for each section of the profile (what you are calling the set point line). It looks like you have 9 sections of the profile and so you will have 9 functions that will end up looking like:
y = mx + b
where
m = slope = (Y2-Y1)/(X2-X1)
x = time axis (in seconds?)
b = y-axis intercept
y = the set-point temperature you want to compare to
You should already have the starting (X1,Y1) and ending (X2,Y2) points for each line section. I didn't look at the GLCD library so if you are using a different method to define each line let me know and we can change the functions.
To convert the points into a function that will let you find the set-point on the fly you should be able to do the following:
unsigned int t_seconds; // You should NOT need more than 65,000 seconds!
float set_point;
float X1, X2, Y1, Y2;
// Do this for each of the 9 line sections.
set_point = (Y2-Y1)/(X2-X1) * (float(t_seconds) - X1) + Y1
It's late and I know this is a little terse so if you have any questions then don't hesitate to ask.
Thank you for the positive feedback from everyone. I've come up with the following last night and it seems to be working 99.9 % correct, 1 pixel not activated compared to the pre-plotted "static" line. I will compare the other suggestions from you guys with my code that I've developed and I will see which solution will work the best and then I will give you guys some feedback. Code I've developed last night (Remember that this is just preliminary code and I will still make adjustments):
// include the library header
#include <glcd.h>
#include <math.h>
// include the Fonts
#include <fonts/allFonts.h>
int X_Starting_Point;
int Y_Starting_Point;
int X_End_Point;
int Y_End_Point;
int X_Transit;
int Y_Transit;
int X_Plotting;
int Y_Plotting;
int X_Positioning;
int Y_Positioning;
int Degrees;
void setup() {
// Initialize the GLCD
GLCD.Init();
// Select the font for the default text area
GLCD.SelectFont(System5x7);
X_Starting_Point = 4;
Y_Starting_Point = 48;
X_End_Point = 23;
Y_End_Point = 38;
X_Transit = X_End_Point - X_Starting_Point;
if ( X_Transit < 0 )
{
X_Transit = (-1) * X_Transit;
}
Y_Transit = Y_End_Point - Y_Starting_Point;
if ( Y_Transit < 0 )
{
Y_Transit = (-1) * Y_Transit;
}
Degrees_Calculator();
}
void loop()
{
GLCD.DrawLine( 4, 48, 23, 48); // Graph X line
GLCD.DrawLine( 4, 48, 4, 38); // Graph Y line
GLCD.DrawLine( 4, 48, 23, 38); // PREH Line
//GLCD.DrawLine( 23, 38, 37, 38); // PREH KEEP Time Line
GLCD.CursorToXY(10, 3);
GLCD.print("DEGREES:");
GLCD.CursorToXY(65, 3);
GLCD.print(Degrees);
GLCD.CursorToXY(10, 13);
GLCD.print("X:");
GLCD.CursorToXY(65, 13);
GLCD.print(X_Plotting);
GLCD.CursorToXY(10, 23);
GLCD.print("Y:");
GLCD.CursorToXY(65, 23);
GLCD.print(Y_Plotting);
if ( X_Plotting < X_Transit )
{
X_Plotting++;
}
Moving_Graph();
delay(1000);
}
void Degrees_Calculator()
{
Degrees = round( atan2 (Y_Transit, X_Transit) * 180/3.14159265 );
}
void Moving_Graph()
{
Y_Plotting = tan ( ( Degrees * 3.14159265 ) / 180 ) * X_Plotting;
X_Positioning = 5 + X_Starting_Point + X_Plotting;
Y_Positioning = Y_Starting_Point - Y_Plotting;
GLCD.SetDot(X_Positioning,Y_Positioning, BLACK);
}
I've tested the code that you've mentioned Mark but it seems that the trigonometry maths works better (I've got 6 steps so I will implement the maths for each step), or maybe I'm just not doing your suggested method right. I will try and implement the maths over the entire "set point" line and post the feedback for you guys over here.
I'm still struggling with what I've mentioned here earlier and i was hoping that someone would be able to assist. Just a quick recap:
I'm trying to determine whether one line is intersecting the other if so I want to invert the reference line by drawing another line over it in an inverted color. The way I set out to determine this is by applying trigonometric functions to determine the angles of the lines and then the draw pixel for pixel in an inverted color should the one line intersect the other. I've attached my approach to it for you guys in an txt file. The code is working but the line that I plot over my "reference" line is not following it accurately.
I think that the reason for this is that where I'm testing between my set points I only move forward and then upwards one pixel and not upwards more then one pixel in one position sometimes i.e. when plotting a line with the command " GLCD.DrawLine( 0, 12, 126, 12); " the line will not always move forward one pixel and upwards one pixel, sometimes it will move up 2 pixels while moving forward 1 pixel.
So could you guys please have a look at where I'm testing my set points and give me some advise how I can change this so that my redrawn graph will follow my "reference graph" exactly please.
PLEASE, I know that I should be returning variables from functions and that I shouldn't rely so heavily on global variables but I'm just trying to get the code working and then I will possibly refine it otherwise in my next project I will start implementing passing of variables between functions so don't comment on that aspect.
If you don't need to know when the setpoint has been set,
then you could potentially modify the glcd_Device.cpp code to know about
a new "color" that could XOR the pixel into memory vs set/clear it.
That would then turn off pixels that were already on.
If you modified SetDot() to know about this XOR color (it is only 2-3 lines of code),
then you could plot the vertical lines point by point and then if a pixel was already on
it would turned off.
If you do need to know when the setpoint is reached and react, or don't want to
modify the library code, then again I'd suggest a different approach.
There are only 64 horizontal pixels.
Simply pre-calculate them and keep them around. That way
you can replot the graph or know when a given vertical line passes the set point.
You could then determine if the most recent vertical line "erased" the graph point and
then redraw that graph point using "WHITE".
It depends on your specific application needs.
More description about your needs would be helpful.
Thanks for the feedback and apologies for the late response, I've been running around a bit. I don't think that I've explained myself very good. So let me try again, I've also uploaded a video to youtube so that you and others might better understand the question:
The project is a solder reflow oven. I insert my setpoints for the different stages and from there the program calculates how to plot the "reference graph" which you will see as the plotted line which is displayed when "run program" is selected. The temperature is then plotted as the process goes along. So when the plotted temperature overshoots the "reference graph" I want to invert it so that you don't loose retrospect of the "reference graph". So I need to keep track of it to react when the intersection occurs.
Using the trigonometric functions is yielding the correct plotting angles meaning that the lines that I do plot is following the reference line more or less correct. I think where my mistake is, is that sometimes I need to plot a rising / lowering temperature by going two pixels up / down and one pixel to the right or two to the right and one up / down etc etc. Unfortunately I'm not sure how I will be able to accomplish this so if you or anyone else can give me any suggestions or point me in the right direction or a different direction I will really appreciate it. Thank you in advance.
My plotting code is listed below to give you a better idea of what I'm referring too:
X_Transit = ( X_End_Point - X_Starting_Point ); // X_Transit is the distance that X travels or alternatively your adjacent value in your tan formula.
Y_Transit = ( Y_Starting_Point - Y_End_Point ); // Y_Transit is the distance that Y travels or alternatively your opposite value in your tan formula.
if ( Y_Transit >= 0 ) // Tests to see if Y_Transit is a negative value and if so, it changes it to a positive value.
{
Degrees = round( atan2 (Y_Transit, X_Transit) * 180/3.14159265 ); // Calculates the angle at which the line must be.
}
if ( Y_Transit < 0 ) // Tests to see if Y_Transit is a negative value and if so, it changes it to a positive value.
{
Y_Transit = (-1) * Y_Transit;
Degrees = ( round( atan2 (X_Transit, Y_Transit) * 180/3.14159265 ) + 90 ); // Calculates the angle at which the line must be.
}
Y_Plotting = tan ( ( Degrees * 3.14159265 ) / 180 ) * X_Plotting; // Calculates the momentary value for Y_Transit. Y_Plotting will coninue to chainge until it reaches the Y_Transit value.
switch (Stage_Selector)
{
case 0: // Default value - PREH Stage
X_Positioning = 5 + X_Plotting; // 5 X_Positioning is the value that is calculated to determine where exactly the dot must be placed to draw the line - X co-ordinates.
break;
// There are more case switches here but too shorten it I'm only listing one.
}
if( Y_Starting_Point > Y_End_Point )
{
Y_Positioning = Y_Starting_Point - Y_Plotting - 1; // Y_Positioning is the value that is calculated to determine where exactly the dot must be placed to draw the line - Y co-ordinates.
}
if ( Y < Y_Positioning ) // Tests the line when it is at an angle to see whether the temperature have overshot the reference graph to invert it.
{
GLCD.SetDot(X_Positioning,Y_Positioning, WHITE); // Places dot according to X_Positioning and Y_Positioning
}
if ( X_Plotting < X_Transit ) // X_Plotting is the momentary value for X_Transit, and continues to "grow" until it reaches the X_Transit value at which stage it stops.
{ // Testing whether X_Plotting have reached the same length as that of X_Transit, if not, continue to increment.
X_Plotting++;
}
if (X_Plotting == X_Transit && Stage_Selector != 5)
{
switch (Stage_Selector)
{
case 0:
if(X_PREH_Transit < X_PREH_END ) // This part of the code is testing a section where the line is not at an angle and then inverts the reference graph if need be.
{
X_PREH_Transit++;
if ( Y < Y_PREH ) // Y is a global variable used to plot the actual temperature so here I'm testing whether the actual temperature have overshot the reference graph
{
GLCD.SetDot(X_PREH_Transit,Y_PREH, WHITE); // If so invert the color
}
}
break;
}
From a visual point of view, it looks about like what I was expecting.
From a function point of view, there are two items:
inverting the over written pixels
detecting the overshoot to be able to react to it.
(I'm assuming you have to do this to control your heater element?)
These can be done several different ways.
The first can be cone using the XOR color but that won't give you the ability
to detect the crossover point.
As far as detecting the crossover point, i'd look at using the map() function
rather than floating point trig calculations.
Part of what make things a bit funky is the coordinate system used in GLCD.
It isn't a normal Cartesian coordinate system.
(ignore that for a moment, since any solution has that issue)
It looks like you can use the map() function to return the crossover value.
From your graph, I'm assuming that the the graph line is a linear approximation between
the two Y coordinates (temperatures).
So to find the cross over point for any X value, all you have to do is feed the map() function
your two number ranges and give it your current X value and let it figure out the crossover point.
i.e.
map( currentX, MinX, MaxX, MinY, MaxY);
That should map your x values into your y values and return the crossover point
for any x value.
The GLCDdiags sketch uses this technique to draw the triangle that you see
at the beginning of the diags.
It does this to ensure a perfect triangle corner to corner regardless of the geometry of the glcd being used.
Your data is a bit more complex, but I think you should be able to use this technique.
Thank you for the well presented and thought through reply. I really appreciate it. I don't know the map() function as yet but I will go read up about it and then play around with it to get familiar with it and then I will implement it in my main program. If I don't get it I'll ask about it again. Thanks again and I hope that you will enjoy a wonderful day.