From 32e117479ceaf29f2c8fd144bca616c78e870759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Araujo=20Galav=C3=ADz?= Date: Mon, 29 May 2023 02:21:00 +0000 Subject: [PATCH] Documentation updated --- Readme.md | 264 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 181 insertions(+), 83 deletions(-) diff --git a/Readme.md b/Readme.md index 080bc45..762453c 100644 --- a/Readme.md +++ b/Readme.md @@ -1,5 +1,3 @@ -# Readme - # Basic Instructions of Repository DisplayESP32 **This repository aims to provide some functions for creating a user interface for the display @@ -26,6 +24,7 @@ Macros are defined to help use the library easily, making possible to modify the #define MAX_OPTIONS 10 //Maximum number of options for each menu #define MAX_MENUS 3 #define MAX_GRAPHS 3 + #define MAX_MODIFIERS 3 #define DISP_WIDTH 128 // OLED display width #define DISP_HEIGHT 64 // OLED display height #define REFRESH 10 //Refresh time in ms @@ -34,6 +33,7 @@ Macros are defined to help use the library easily, making possible to modify the * **MAX_OPTIONS** defines the maximum number of options a menu can hold, in this case, there can only be three menus in total, as there is only enough memory reserved for them. * **MAX_MENUS** declares the maximum number of menu screens in the interface, it's not possible to create more menus than this number. * **MAX_GRAPHS** is the maximum number of graphs to create. +* **MAX_MODIFIERS** is the maximum number of modifiers to create. * **DISP_WIDTH** and **DISP_HEIGHT** are hardware specific (SSD1306), it's possible to modify this values in the case that it's required to employ a different kind of display. * **REFRESH** is the time in milliseconds the interface will take in refreshing (this time affects the loop, keep that in mind) * **ADDRESS** address of the display @@ -140,7 +140,7 @@ Where: * **pos**: This variable stores the position of the menu in which the user is at a certain time. * **page**: According to the number of options per page (optPPage), this variable holds the page to be printed in larger menus. * **Option opt[MAX_OPTIONS]**: This is an array that holds every option created in the menu (with a maximum of MAX_OPTIONS). -* **previousScreen** and **previousContentType**: These integers keep the values of the screen that led to this menu, to make it possible to return to that screen. This values are assigned from a method in Menu, called from another one in Screen, which is used to control the interaction with the interface. This will be discussed later in this document, see Screen class. +* **previousScreen** and **previousContentType**: These integers keep the values of the screen that led to this menu, to make it possible to return to that screen. This values are assigned from a method in Menu.h, called from another one in Screen.h, which is used to control the interaction with the interface. This will be discussed later in this document, see Screen class. #### Methods @@ -232,6 +232,7 @@ To achieve the above stated the following attributes are used: double yStepSize; //For: Vertical Bar Cartesian double xStepSize; //For: Horizontal Bar Cartesian int digit; //For: Vertical Bar Horizontal Bar Cartesian + int * maximum; //For: Cartesian double x; double yrange; double xrange; @@ -258,6 +259,7 @@ To achieve the above stated the following attributes are used: * **yStepSize**: Size of the step in the y axis. This is the interval in which the axis is going to be split. Required only for vertical graph and cartesian chart. * **xStepSize**: Size of the step in the x axis. This is the interval in which the axis is going to be split. Required only for horizontal graph and cartesian chart. * **digit**: Number of decimal digits to display in the axis labels. +* ***maximum**: Maximum number to display in the x axis of a cartesian graph (it can be the number of samples to take). * **x**: This is used to know the ending point of a line in the cartesian chart. * **yrange**: Range in the y axis, depends on the maximum and minimum of this axis. * **xrange**: Range in the x axis, depends on the maximum and minimum of this axis. @@ -266,7 +268,7 @@ To achieve the above stated the following attributes are used: * **count**: Last iteration of the cartesian chart, in the x axis. * **graphScale**: Scale of the graph (vertical or horizontal), according to its minimum and maximum. * **redraw**: This boolean should only be true then the whole screen was cleared. It redraws the axes or the bars respectively. -* **previousScreen** and **previousContentType**: These integers keep the values of the screen that led to this graph, to make it possible to return to that screen. This values are assigned from a method in Graph, called from another one in Screen, which is used to control the interaction with the interface. This will be discussed later in this document, see Screen class. +* **previousScreen** and **previousContentType**: These integers keep the values of the screen that led to this graph, to make it possible to return to that screen. This values are assigned from a method in Graph.h, called from another one in Screen.h, which is used to control the interaction with the interface. This will be discussed later in this document, see Screen class. #### Methods The following methods are applied to manage data from Graph. @@ -294,7 +296,6 @@ According to the type of graph configured, the next calculations are made: case 'c': this->yrange = ymaximum - yminimum; - this->xrange = xmaximum - xminimum; break; ##### drawGraph() @@ -338,51 +339,22 @@ To graph the value received, two rectangles are display. A white one fills the p For horizontal graphs, the following code block is implemented to diplay the labels of the graph: - if (this->redraw) { - display.clearDisplay(); - this->redraw = false; - display.fillRect(0, 0, 127 , 16, SSD1306_WHITE); - display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); - display.setTextSize(1); - display.setCursor(2, 4); - display.println(this->title); - // draw the text - - for (i = 0; i <= this->width; i += this->graphScale) { - display.drawFastVLine(i + this->xpos , this->ypos , 5, SSD1306_WHITE); - // draw lables - display.setTextSize(1); - display.setTextColor(SSD1306_WHITE, SSD1306_BLACK); - display.setCursor(i + this->xpos , this->ypos + 10); - // addling a small value to eliminate round off errors - // this val may need to be adjusted - data = ( i * (this->xStepSize / this->graphScale)) + this->xminimum + 0.00001; - display.print(data, this->digit); - } - } - -Like with the vertical bar, in this case, two rectangles are drawn to avoid flickering. The procedure is the same, only that it happens along the x axis. - -###### Cartesian chart - -In the following code, the axes are drawn as are their labels. - if (this->redraw == true) { - this->redraw = false; - display.clearDisplay(); - display.fillRect(0, 0, 127 , 16, SSD1306_WHITE); - display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); - display.setTextSize(1); - display.setCursor(2, 4); - display.println(title); - this->ox = (this->count - this->xminimum) * (this->width) / (this->xrange) + this->xpos; - this->oy = (this->value - this->yminimum) * (- this->height) / (this->yrange) + this->ypos; - // draw y scale - display.setTextSize(1); - display.setTextColor(SSD1306_WHITE, SSD1306_BLACK); - for ( i = this->yminimum; i <= this->ymaximum; i += this->yStepSize) { + this->redraw = false; + this->xrange = *this->maximum - xminimum; + display.clearDisplay(); + display.fillRect(0, 0, 127 , 16, SSD1306_WHITE); + display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); + display.setTextSize(1); + display.setCursor(2, 4); + display.println(title); + this->ox = (this->count - this->xminimum) * (this->width) / (this->xrange) + this->xpos; + this->oy = (this->value - this->yminimum) * (- this->height) / (this->yrange) + this->ypos; + // draw y scale + display.setTextSize(1); + display.setTextColor(SSD1306_WHITE, SSD1306_BLACK); + for ( i = this->yminimum; i <= this->ymaximum; i += this->yStepSize) { // compute the transform - // note my transform funcition is the same as the map function, except the map uses long and we need doubles temp = (i - this->yminimum) * (- this->height) / (this->ymaximum - this->yminimum) + this->ypos; if (i == 0) { display.drawFastHLine(this->xpos - 3, temp, this->width + 3, SSD1306_WHITE); @@ -392,9 +364,9 @@ In the following code, the axes are drawn as are their labels. } display.setCursor(this->xpos - 27, temp - 3); display.println(i, this->digit); - } - // draw x scale - for (i = this->xminimum; i <= this->xmaximum; i += this->xStepSize) { + } + // draw x scale + for (i = this->xminimum; i <= *this->maximum; i += this->xStepSize) { // compute the transform display.setTextSize(1); display.setTextColor(SSD1306_WHITE, SSD1306_BLACK); @@ -407,8 +379,27 @@ In the following code, the axes are drawn as are their labels. } display.setCursor(temp, this->ypos + 6); display.println(i, this->digit); - } } + } + + // graph drawn now plot the data + // the entire plotting code are these few lines... + + this->x = (this->count - this->xminimum) * (this->width) / (this->xrange) + this->xpos; + this->value = (this->value - this->yminimum) * (- this->height) / (this->yrange) + this->ypos; + display.drawLine(this->ox, this->oy, this->x, this->value, SSD1306_WHITE); + display.drawLine(this->ox, this->oy - 1, this->x, this->value - 1, SSD1306_WHITE); + this->ox = this->x; + this->oy = this->value; + + // up until now print sends data to a video buffer NOT the screen + // this call sends the data to the screen + display.display(); + this->count += 1; + if(this->ox >= (this->xpos + this->width)){ + this->redraw = true; + this->count = xminimum; + } Two plot the data, the previous ending point of the graph is stored in **ox** and **oy**, and the new values are calculated depending on the value received and the scale of the graph. When the next point in the graph is greater than its width, the graph is redrawn and count is reset, in order to start graphing anew. @@ -428,6 +419,35 @@ This method passes a value as a new attribute of the graph so it can be plotted. Resets the position of x to redraw the cartesian chart when exiting and entering into it from the menus. +### Modifier class + +This class is used to create screens for runtime variable manipulation, without need to rewrite the code and load it into the microcontroller. It has the following attributes. + +#### Attributes + +* **title**: String containing the title of the screen. +* ***value**: Integer pointer to the value to modify. +* **max**: Highest possible value to set. +* **min**: Lowest possible value to set. +* **step**: Size of the step. +* **previousScreen** and **previousContentType**: These integers keep the values of the screen that led to this modifier, to make it possible to return to that screen. This values are assigned from a method in Modifier.h, called from another one in Screen.h, which is used to control the interaction with the interface. This will be discussed later in this document, see Screen class. + +#### Methods + +##### configure() + +This function configures the modifier screen after being created. Its prototype is: + + void configure(String title, int *value, int max, int min, int step) + +##### drawModifier() + +This function allows the drawing of the modifier screen on the OLED display. This function is used by the Screen object controlling the user interface. + +##### increaseValue() and decreaseValue() + +Both functions are designed to change the values of the variable pointed by the pointer configured within the modifier, dependig on the value of *step*. + ### Screen class This class is the main class of this code, it's responsible for the control of the whole interface, allowing to switch between different menus and graphs, also for receiving data and interacting with the keyboard. @@ -442,6 +462,7 @@ Screen attributes are listed in the following code and explained afterwards. Graph graph[MAX_GRAPHS]; int counterM = 0; int counterG = 0; + int counterMod = 0; bool redraw = true; int currentScreen = 0; int contentType = 0; @@ -449,7 +470,8 @@ Screen attributes are listed in the following code and explained afterwards. * **menu**: This array holds all the menus for the interface, limited by **MAX_MENUS**, see Macros. * **graph**: This array holds all the graphs for the interface, limited by **MAX_GRAPHS**, see Macros. * **counterM**: This is the number of menus created, it increases when a new menu is configured using the methods available from Screen. -* **counterG**: This is the number of graphs created, it increases when a new menu is configured using the methods available from Screen. +* **counterG**: This is the number of graphs created, it increases when a new graph is configured using the methods available from Screen. +* **counterMod**: This is the number of modifiers created, it increases when a new modifier is configured using the methods available from Screen. * **redraw**: Variable to control the redrawing of the interface. * **currentScreen** and **contentType**: Variables where the current screen direction is stored, so that it's the only screen displayed. @@ -461,7 +483,9 @@ To control the behaviour of the interface the following methods are used. This method is used to set up the display and also print a welcome message, it is necessary to call this method once the object is created, in order to configure the direction of the display and allow communication. This configuration has place only if the parameter in configure is *true*, otherwise the display is only cleared and the welcome message is printed. Its prototype is shown in the following code block. - void configure(bool fullsetting) + void configure(bool fullsetting, char address) + +*address* corresponds to the I2C address of the display. ##### createMenu() @@ -483,7 +507,7 @@ To create graphs, it is advisable to do it from Screen, using the methods **crea void createHGraph(String title, double xpos, double ypos, double width, double height, double xminimum, double xmaximum, double xStepSize, int digit) - void createCGraph(String title, double xpos, double ypos, double width, double height, double yminimum, double ymaximum, double xminimum, double xmaximum, double yStepSize, double xStepSize, int digit) + void createCGraph(String title, double xpos, double ypos, double width, double height, double yminimum, double ymaximum, double xminimum, double yStepSize, double xStepSize, int digit, void * maximum) ##### graphAssignValue() @@ -491,6 +515,12 @@ Using this method, it's possible to assing a value to a graph, specifying its in void graphAssignValue(int graphIndex, double value) +##### Modifier creation + +To create a new modifier screen, the following method must be used: + + void createModifier(String title, int *value, int max, int min, int step) + ##### control() This method is the main method of Screen. It redraws the content and prints the actual screen when needed, all the interface depends on it. This method should be used in a loop in order to keep it running and refreshing the contents. It will be shown later in the Implementation section. @@ -505,24 +535,25 @@ This method changes the screen and allows to enter a new screen if an option in void goTo(){ if(this->contentType == 0){ - int newScreen = this->menu[this->currentScreen].extractDestinationIndex(); - int newContentType = this->menu[this->currentScreen].extractDestinationType(); - if (newContentType == 0){ - this->menu[newScreen].setPreviousScreen(this->currentScreen); - this->menu[newScreen].setPreviousContentType(this->contentType); - } - else if(newContentType == 1){ - this->graph[newScreen].setPreviousScreen(this->currentScreen); - this->graph[newScreen].setPreviousContentType(this->contentType); - this->graph[newScreen].reset(); - this->graph[newScreen].redrawFlag(); - } - else if(newContentType == 2){ - - } - this->contentType = newContentType; - this->currentScreen = newScreen; - this->redraw = true; + int newScreen = this->menu[this->currentScreen].extractDestinationIndex(); + int newContentType = this->menu[this->currentScreen].extractDestinationType(); + if (newContentType == 0){ + this->menu[newScreen].setPreviousScreen(this->currentScreen); + this->menu[newScreen].setPreviousContentType(this->contentType); + } + else if(newContentType == 1){ + this->graph[newScreen].setPreviousScreen(this->currentScreen); + this->graph[newScreen].setPreviousContentType(this->contentType); + this->graph[newScreen].reset(); + this->graph[newScreen].redrawFlag(); + } + else if(newContentType == 2){ + this->modifier[newScreen].setPreviousScreen(this->currentScreen); + this->modifier[newScreen].setPreviousContentType(this->contentType); + } + this->contentType = newContentType; + this->currentScreen = newScreen; + this->redraw = true; } } @@ -591,13 +622,18 @@ Finally, the debouncing methods are called in the main method of Keyboard, which ### Implementation +#### Simple_Implementation + +There are two example implementation codes, the first one (Simple_Implementation), allows for the creation of a simple user interface, with two example menus, three graphs and two modifiers. The library already includes the creation of the Adafruit_SSD1306 object as display. It can be modified to match other display controllers. Adafruit_SSD1306 display(DISP_WIDTH, DISP_HEIGHT, &Wire, -1); -In the example provided, an integer variable *i* is declared to test the graphs. +In the example provided, an integer variable *i* is declared to test the graphs. Furthermore, two integer variables are created to test the modifiers. int i = 0; + int multiplier = 1; + int samples = 1000; To use the interface, it is important to create a Screen and a Keyboard objects, in order to have full control of the interface, that is done in the lines below: @@ -606,13 +642,17 @@ To use the interface, it is important to create a Screen and a Keyboard objects, Then, in the setup() function of the program, it's necessary call the configure() method from screen to establish communication, afterwards, the menus and graphs are created. Finally the options in the menus are configured. + screen.configure(true, 0x3C); + screen.createMenu(128, 13); //Menu 0 screen.createMenu(128, 13); //Menu 1 - screen.createVGraph("Grafica 1", 25, 60, 40, 40, 0, 100, 10, 0); //Graph 0 - screen.createHGraph("Grafica 2", 10, 40, 100, 20, 0, 100, 10, 0); //Graph 1 - screen.createCGraph("Grafica 3", 30, 50, 75, 30, 0, 100, 0, 1000, 25, 250, 0); //Graph 2 + screen.createVGraph("Grafica 1", 25, 60, 40, 40, 0, 100, 10, 0); //Graph 0 + screen.createHGraph("Grafica 2", 10, 40, 100, 20, 0, 100, 20, 0); //Graph 1 + screen.createCGraph("Grafica 3", 30, 50, 75, 30, 0, 100, 0, 25, 250, 0, &samples); //Graph 2 + screen.createModifier("Modify variable", &multiplier, 5, 1, 1); + screen.createOption(0, "Vertical graph", 1, 0); //Creates the first option in Menu 0, directing to a graph (contentType = 1 (Graph)), 0 (Graph 0) screen.createOption(0, "Horizontal graph", 1, 1); @@ -620,9 +660,10 @@ Then, in the setup() function of the program, it's necessary call the configure( screen.createOption(0, "Extra option", 0, 1); screen.createOption(1, "Test", 1, 3); - screen.createOption(1, "Working?", 1, 4); + screen.createOption(1, "Working?", 2, 2); + screen.createOption(1, "Modify variable", 2, 0); -There are some lines commented below which can be used to test the methods from Screen, to manipulate the interface. These can be tested with a keyboard if configured too. +There are some lines commented below which can be used to test the methods from Screen, to manipulate the interface without a keyboard. These can methods can be tested with a keyboard, if configured. // screen.increasePos(); // screen.increasePos(); @@ -651,10 +692,67 @@ In the loop() function, we find the main methods of Screen and Keyboard, which n The last delay is the time at which the display will refresh. -### Modifier class +#### Singleshot_Graph + +This example code allows for the creation of a graph that runs once. It contains a menu that enables the user to modify some parameters of the system before running the graph. The keyboard gets blocked until completing the graph. + +In the example provided, an integer variable *i* is declared to test the graphs. Furthermore, two integer variables are created to test the modifiers. + + int i = 0; + int multiplier = 1; + int samples = 1000; + +To use the interface, it is important to create a Screen and a Keyboard objects, in order to have full control of the interface, that is done in the lines below: + + Screen screen; + Keyboard keyboard(13, 12, 14, 27, 30, &screen); + +In the setup() function, the screen is configured, as is a menu with three options too, and two modifiers. + + screen.configure(true, 0x3C); + + screen.createMenu(128, 13); //Menu 0 + + screen.createCGraph("Test", 30, 50, 75, 30, 0, 100, 0, 25, 250, 0, &samples); //Graph 0 + + screen.createModifier("Multiplier", &multiplier, 5, 1, 1); //Modifier 0 + screen.createModifier("Samples number", &samples, 1000, 500, 10); //Modifier 1 + + screen.createOption(0, "Adjust multiplier", 2, 0); + screen.createOption(0, "Adjust samples #", 2, 1); + + screen.createOption(0, "Run test", 1, 0); + +In the loop() section, it is possible to see that at first, the user interface is completely responsive, being in a while loop, until the current screen is that of the graph, which means the test has started and is running. This permits the user to change the values of the variables of the system before starting the test. + + while(screen.getCurrentScreen() != 0 || screen.getContentType() != 1){ + screen.control(); //Controls the screen and redraws if needed while not in the test's graph + keyboard.control(); + delay(REFRESH); + } + +Once the user enter the graph, the system enters another loop, in which it the keyboard gets blocked, waiting for the test to finish. + + for(int j = 0; j <= samples; j++){ + if(i <= 100){ + screen.graphAssignValue(0, i); //Assigning a demo value to Graph 0 + i += multiplier; + } + else + i = 0; + screen.control(); + Serial.println(samples); + delay(REFRESH); + } + +Finally, when this is done, the user interface shows the graph completely plotted and expects for the user to exit this screen by enabling the keyboard. + + while(screen.getCurrentScreen() == 0 && screen.getContentType() == 1){ + screen.control(); //Controls the screen and redraws if needed while in the test's graph + keyboard.control(); + delay(REFRESH); + } -This class is used to create screens for runtime variable manipulation. -#### Attributes -[1]: "Kris Kasprzak, OLED_Graphing.ino" \ No newline at end of file +[1]: "Kris Kasprzak, OLED_Graphing.ino". \ No newline at end of file