├── Documentation.pdf ├── images ├── NumberBox.jpg ├── HookupGuide.png ├── LCD_Display.jpg ├── SelectionBox.jpg ├── HookupGuide-770.png └── TeensyUserInterface.jpg ├── library.properties ├── README.md ├── LICENSE.txt ├── examples ├── Example3_MenusWithColumns │ └── Example3_MenusWithColumns.ino ├── Example4_SettingTheMenuColor │ └── Example4_SettingTheMenuColor.ino ├── Example2_SetDisplayOrientation │ └── Example2_SetDisplayOrientation.ino ├── Example1_SimpleMenu │ └── Example1_SimpleMenu.ino ├── Example7_MenuWithSubMenus │ └── Example7_MenuWithSubMenus.ino ├── Example8_StopWatch │ └── Example8_StopWatch.ino ├── Example5_ScreensWithInformation │ └── Example5_ScreensWithInformation.ino └── Example6_GetNumbersAndValues │ └── Example6_GetNumbersAndValues.ino ├── src └── TeensyUserInterface.h └── Documentation.md /Documentation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Stan-Reifel/TeensyUserInterface/HEAD/Documentation.pdf -------------------------------------------------------------------------------- /images/NumberBox.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Stan-Reifel/TeensyUserInterface/HEAD/images/NumberBox.jpg -------------------------------------------------------------------------------- /images/HookupGuide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Stan-Reifel/TeensyUserInterface/HEAD/images/HookupGuide.png -------------------------------------------------------------------------------- /images/LCD_Display.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Stan-Reifel/TeensyUserInterface/HEAD/images/LCD_Display.jpg -------------------------------------------------------------------------------- /images/SelectionBox.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Stan-Reifel/TeensyUserInterface/HEAD/images/SelectionBox.jpg -------------------------------------------------------------------------------- /images/HookupGuide-770.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Stan-Reifel/TeensyUserInterface/HEAD/images/HookupGuide-770.png -------------------------------------------------------------------------------- /images/TeensyUserInterface.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Stan-Reifel/TeensyUserInterface/HEAD/images/TeensyUserInterface.jpg -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=TeensyUserInterface 2 | version=1.3.0 3 | author=S. Reifel 4 | license=MIT 5 | maintainer=S. Reifel 6 | sentence=A simple touchscreen user interface for Teensy. 7 | paragraph=Easy to use library for building Teensy applications with a menu based user interface. 8 | category=Display 9 | url=https://github.com/Stan-Reifel/TeensyUserInterface 10 | architectures=* 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Teensy User Interface 2 | 3 | This library is a simple touchscreen user interface for building Teensy applications. Adding buttons and menus to your sketch is as-easy-as filling in a table. The library also includes many functions for displaying your own data. 4 | 5 | ![alt_text](images/TeensyUserInterface.jpg "Teensy User Interface") 6 | 7 | 8 | ## Documentation: 9 | Documentation for the *Teensy User Interface* library can be found at: 10 | 11 | ​ https://github.com/Stan-Reifel/TeensyUserInterface/blob/master/Documentation.md 12 | 13 | 14 | 15 | ## License: 16 | 17 | Copyright (c) 2022 S. Reifel & Co. - Licensed under the MIT license. 18 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Stanley Reifel & Co. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /examples/Example3_MenusWithColumns/Example3_MenusWithColumns.ino: -------------------------------------------------------------------------------- 1 | 2 | // ****************************************************************** 3 | // * * 4 | // * Example shows how to create a menu with buttons arranged * 5 | // * in columns * 6 | // * * 7 | // * S. Reifel & Co. 2/18/2022 * 8 | // * * 9 | // ****************************************************************** 10 | 11 | // 12 | // DOCUMENTATION: 13 | // 14 | // Documentation for the "Teensy User Interface" library can be found at: 15 | // https://github.com/Stan-Reifel/TeensyUserInterface 16 | 17 | 18 | 19 | // *********************************************************************** 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | 26 | // 27 | // create the user interface object 28 | // 29 | TeensyUserInterface ui; 30 | 31 | 32 | 33 | // --------------------------------------------------------------------------------- 34 | // Setup the hardware 35 | // --------------------------------------------------------------------------------- 36 | 37 | void setup() 38 | { 39 | // 40 | // pin numbers used in addition to the default SPI pins 41 | // 42 | const int LCD_CS_PIN = 10; 43 | const int LCD_DC_PIN = 9; 44 | const int TOUCH_CS_PIN = 8; 45 | 46 | // 47 | // setup the LCD orientation, the default font and initialize the user interface 48 | // 49 | ui.begin(LCD_CS_PIN, LCD_DC_PIN, TOUCH_CS_PIN, LCD_ORIENTATION_LANDSCAPE_4PIN_RIGHT, Arial_9_Bold); 50 | } 51 | 52 | 53 | // --------------------------------------------------------------------------------- 54 | // Define the menus and top level loop, place menus after setup() 55 | // --------------------------------------------------------------------------------- 56 | 57 | // 58 | // Notes on building the menu table: 59 | // 60 | // A menu can have as many buttons as you like, simply by adding more entries to the menu 61 | // table. Buttons are sized such that they always fill the screen, adding more causes 62 | // them to be shorter in height. In many cases it is advantageous to arrange a menu's 63 | // buttons in two or more columns. Columns of buttons often look better, are easier to 64 | // touch, and you can fit more on a single screen. 65 | // 66 | // The buttons on a menu can be arranged in 1, 2, 3 or 4 columns. The number of columns 67 | // is set in the third field of the menu table's first line by inserting one of these 68 | // values: 69 | // MENU_COLUMNS_1, MENU_COLUMNS_2, MENU_COLUMNS_3, or MENU_COLUMNS_4 70 | // 71 | // So far all of our examples have placed the menu buttons in a single column. In this 72 | // example we will use two columns. 73 | 74 | 75 | // 76 | // for each menu, create a forward declaration with "extern" 77 | // 78 | extern MENU_ITEM mainMenu[]; 79 | 80 | 81 | // 82 | // the main menu 83 | // 84 | MENU_ITEM mainMenu[] = { 85 | {MENU_ITEM_TYPE_MAIN_MENU_HEADER, "Example Three - Menus with Columns", MENU_COLUMNS_2, mainMenu}, 86 | {MENU_ITEM_TYPE_COMMAND, "Count to one", commandCountTo1, NULL}, 87 | {MENU_ITEM_TYPE_COMMAND, "Count to two", commandCountTo2, NULL}, 88 | {MENU_ITEM_TYPE_COMMAND, "Count to three", commandCountTo3, NULL}, 89 | {MENU_ITEM_TYPE_COMMAND, "Count to four", commandCountTo4, NULL}, 90 | {MENU_ITEM_TYPE_COMMAND, "Count to five", commandCountTo5, NULL}, 91 | {MENU_ITEM_TYPE_COMMAND, "Count to six", commandCountTo6, NULL}, 92 | {MENU_ITEM_TYPE_END_OF_MENU, "", NULL, NULL} 93 | }; 94 | 95 | 96 | 97 | // 98 | // display the menu, then execute commands selected by the user 99 | // 100 | void loop() 101 | { 102 | ui.displayAndExecuteMenu(mainMenu); 103 | } 104 | 105 | 106 | 107 | // --------------------------------------------------------------------------------- 108 | // Commands executed from the menu 109 | // --------------------------------------------------------------------------------- 110 | 111 | // 112 | // menu command to "Count To 1" 113 | // 114 | void commandCountTo1(void) 115 | { 116 | ui.drawTitleBar("Count To One"); 117 | showCount(1); 118 | } 119 | 120 | 121 | // 122 | // menu command to "Count To 2" 123 | // 124 | void commandCountTo2(void) 125 | { 126 | ui.drawTitleBar("Count To Two"); 127 | showCount(2); 128 | } 129 | 130 | 131 | // 132 | // menu command to "Count To 3" 133 | // 134 | void commandCountTo3(void) 135 | { 136 | ui.drawTitleBar("Count To Three"); 137 | showCount(3); 138 | } 139 | 140 | 141 | // 142 | // menu command to "Count To 4" 143 | // 144 | void commandCountTo4(void) 145 | { 146 | ui.drawTitleBar("Count To Four"); 147 | showCount(4); 148 | } 149 | 150 | 151 | // 152 | // menu command to "Count To 5" 153 | // 154 | void commandCountTo5(void) 155 | { 156 | ui.drawTitleBar("Count To Five"); 157 | showCount(5); 158 | } 159 | 160 | 161 | // 162 | // menu command to "Count To 6" 163 | // 164 | void commandCountTo6(void) 165 | { 166 | ui.drawTitleBar("Count To Six"); 167 | showCount(6); 168 | } 169 | 170 | 171 | 172 | // 173 | // display a screen that counts 174 | // 175 | void showCount(int countValue) 176 | { 177 | // 178 | // draw the digits using a large font 179 | // 180 | ui.lcdSetFont(Arial_60_Bold); 181 | 182 | 183 | // 184 | // count to ten, showing the digits on the screen, when done return to the menu 185 | // 186 | for(int i = 1; i <= countValue; i++) 187 | { 188 | // 189 | // blank the screen below the Title Bar 190 | // 191 | ui.clearDisplaySpace(); 192 | 193 | // 194 | // show the count number on the LCD screen 195 | // 196 | ui.lcdSetCursorXY(ui.displaySpaceCenterX, ui.displaySpaceCenterY - 30); 197 | ui.lcdPrintCentered(i); 198 | 199 | // 200 | // delay for a quarter second while showing the count value 201 | // 202 | delay(350); 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /examples/Example4_SettingTheMenuColor/Example4_SettingTheMenuColor.ino: -------------------------------------------------------------------------------- 1 | 2 | // ****************************************************************** 3 | // * * 4 | // * Example shows how to set the colors and font used * 5 | // * by the menus * 6 | // * * 7 | // * S. Reifel & Co. 2/18/2022 * 8 | // * * 9 | // ****************************************************************** 10 | 11 | // 12 | // DOCUMENTATION: 13 | // 14 | // Documentation for the "Teensy User Interface" library can be found at: 15 | // https://github.com/Stan-Reifel/TeensyUserInterface 16 | 17 | 18 | 19 | // *********************************************************************** 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | 26 | // 27 | // create the user interface object 28 | // 29 | TeensyUserInterface ui; 30 | 31 | 32 | 33 | // --------------------------------------------------------------------------------- 34 | // Setup the hardware 35 | // --------------------------------------------------------------------------------- 36 | 37 | void setup() 38 | { 39 | // 40 | // pin numbers used in addition to the default SPI pins 41 | // 42 | const int LCD_CS_PIN = 10; 43 | const int LCD_DC_PIN = 9; 44 | const int TOUCH_CS_PIN = 8; 45 | 46 | // 47 | // setup the LCD orientation, the default font and initialize the user interface 48 | // 49 | ui.begin(LCD_CS_PIN, LCD_DC_PIN, TOUCH_CS_PIN, LCD_ORIENTATION_LANDSCAPE_4PIN_RIGHT, Arial_9_Bold); 50 | 51 | 52 | // 53 | // By default the menus use a blue color palette, however you can set the colors 54 | // to match your preference. In this example we will build RGB color values for 55 | // each of the menu's attributes. 56 | // 57 | // The TeensyUserInterface has two built in color palettes, the default blue palette, 58 | // and a grayscale palette. The author prefers the gray palette and it will be used 59 | // in future examples. Selecting the gray palette is done with a single call to this 60 | // function: 61 | // ui.setColorPaletteGray(); 62 | // 63 | // Note about colors: This LCD display uses a 16 bit color format. The format is 64 | // referred to as RGB565, meaning 5 bits of red, 6 bits of green, and 5 bits of blue. 65 | // The function lcdMakeColor() can be used to build a color in the RGB565 format. 66 | // Call it with a red value between 0-31, green between 0-63, and a blue value 67 | // between 0-31. 68 | 69 | // 70 | // build a red color palette 71 | // 72 | int16_t red; 73 | uint16_t green; 74 | uint16_t blue; 75 | 76 | red = 20; green = 0; blue = 0; uint16_t titleBarColor = ui.lcdMakeColor(red, green, blue); 77 | red = 25; green = 4; blue = 2; uint16_t titleBarBackButtonColor = ui.lcdMakeColor(red, green, blue); 78 | red = 30; green = 6; blue = 3; uint16_t titleBarBackButtonSelectedColor = ui.lcdMakeColor(red, green, blue); 79 | uint16_t titleBarTextColor = LCD_WHITE; 80 | uint16_t menuBackgroundColor = LCD_BLACK; 81 | red = 20; green = 0; blue = 0; uint16_t menuButtonColor = ui.lcdMakeColor(red, green, blue); 82 | uint16_t menuButtonSelectedColor = titleBarBackButtonSelectedColor; 83 | red = 22; green = 0; blue = 0; uint16_t menuButtonFrameColor = ui.lcdMakeColor(red, green, blue); 84 | uint16_t menuButtonTextColor = LCD_WHITE; 85 | 86 | ui.setTitleBarColors(titleBarColor, titleBarTextColor, titleBarBackButtonColor, titleBarBackButtonSelectedColor); 87 | ui.setMenuColors(menuBackgroundColor, menuButtonColor, menuButtonSelectedColor, menuButtonFrameColor, menuButtonTextColor); 88 | 89 | 90 | // 91 | // set the menus to use a larger than the default font 92 | // 93 | ui.setTitleBarFont(Arial_13_Bold); 94 | ui.setMenuFont(Arial_13_Bold); 95 | } 96 | 97 | 98 | // --------------------------------------------------------------------------------- 99 | // Define the menus and top level loop, place menus after setup() 100 | // --------------------------------------------------------------------------------- 101 | 102 | 103 | // 104 | // for each menu, create a forward declaration with "extern" 105 | // 106 | extern MENU_ITEM mainMenu[]; 107 | 108 | 109 | // 110 | // the main menu 111 | // 112 | MENU_ITEM mainMenu[] = { 113 | {MENU_ITEM_TYPE_MAIN_MENU_HEADER, "Example Four - Setting Colors", MENU_COLUMNS_1, mainMenu}, 114 | {MENU_ITEM_TYPE_COMMAND, "Count to one", commandCountTo1, NULL}, 115 | {MENU_ITEM_TYPE_COMMAND, "Count to two", commandCountTo2, NULL}, 116 | {MENU_ITEM_TYPE_COMMAND, "Count to three", commandCountTo3, NULL}, 117 | {MENU_ITEM_TYPE_END_OF_MENU, "", NULL, NULL} 118 | }; 119 | 120 | 121 | 122 | // 123 | // display the menu, then execute commands selected by the user 124 | // 125 | void loop() 126 | { 127 | ui.displayAndExecuteMenu(mainMenu); 128 | } 129 | 130 | 131 | 132 | // --------------------------------------------------------------------------------- 133 | // Commands executed from the menu 134 | // --------------------------------------------------------------------------------- 135 | 136 | // 137 | // menu command to "Count To 1" 138 | // 139 | void commandCountTo1(void) 140 | { 141 | ui.drawTitleBar("Count To One"); 142 | showCount(1); 143 | } 144 | 145 | 146 | // 147 | // menu command to "Count To 2" 148 | // 149 | void commandCountTo2(void) 150 | { 151 | ui.drawTitleBar("Count To Two"); 152 | showCount(2); 153 | } 154 | 155 | 156 | // 157 | // menu command to "Count To 3" 158 | // 159 | void commandCountTo3(void) 160 | { 161 | ui.drawTitleBar("Count To Three"); 162 | showCount(3); 163 | } 164 | 165 | 166 | 167 | // 168 | // display a screen that counts 169 | // 170 | void showCount(int countValue) 171 | { 172 | // 173 | // draw the digits using a large font 174 | // 175 | ui.lcdSetFont(Arial_60_Bold); 176 | 177 | 178 | // 179 | // count to ten, showing the digits on the screen, when done return to the menu 180 | // 181 | for(int i = 1; i <= countValue; i++) 182 | { 183 | // 184 | // blank the screen below the Title Bar 185 | // 186 | ui.clearDisplaySpace(); 187 | 188 | // 189 | // show the count number on the LCD screen 190 | // 191 | ui.lcdSetCursorXY(ui.displaySpaceCenterX, ui.displaySpaceCenterY - 30); 192 | ui.lcdPrintCentered(i); 193 | 194 | // 195 | // delay for a quarter second while showing the count value 196 | // 197 | delay(500); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /examples/Example2_SetDisplayOrientation/Example2_SetDisplayOrientation.ino: -------------------------------------------------------------------------------- 1 | 2 | // ****************************************************************** 3 | // * * 4 | // * Example to shows how to set the display's orientation * 5 | // * choose between Portrait and Landscape * 6 | // * * 7 | // * S. Reifel & Co. 2/18/2022 * 8 | // * * 9 | // ****************************************************************** 10 | 11 | // These are the hardware and software requirements needed to make this example work: 12 | // 13 | // 14 | // HARDWARE: 15 | // 16 | // This user interface works with a Teensy microcontroller (www.pjrc.com) and a 2.8" 17 | // 320x240 LCD touch screen display (the LCD must include a ILI9341 controller). 18 | // These displays can be purchased from PJRC: 19 | // https://www.pjrc.com/store/display_ili9341_touch.html 20 | // or on eBay and Amazon. 21 | // 22 | // To hookup the LCD to the Teensy, see: 23 | // https://github.com/Stan-Reifel/TeensyUserInterface/blob/master/images/HookupGuide.png 24 | // 25 | // 26 | // LIBRARIES: 27 | // 28 | // The TeensyUserInterface requires three libraries, all of which must be installed into 29 | // the Arduino IDE. To do so: 1) From the "Sketch" menu, select "Include Library", 30 | // then "Manage libraries". 2) In the "Filter your search" field, enter the library 31 | // name as shown below. 3) Click "Install". 32 | // 33 | // TeensyUserInterface - The Teensy User Interface 34 | // ILI9341_t3 - Driver for the LCD screen 35 | // XPT2046_Touchscreen - Driver for the touch screen 36 | // 37 | // The author has only tested the TeensyUserInterface code with a Teensy 3.6 but it 38 | // will likely work with other Teensys that support these libraries. 39 | // 40 | // 41 | // DOCUMENTATION: 42 | // 43 | // Documentation for the "Teensy User Interface" library can be found at: 44 | // https://github.com/Stan-Reifel/TeensyUserInterface 45 | 46 | 47 | 48 | // *********************************************************************** 49 | 50 | #include 51 | #include 52 | #include 53 | 54 | 55 | // 56 | // create the user interface object 57 | // 58 | TeensyUserInterface ui; 59 | 60 | 61 | 62 | // --------------------------------------------------------------------------------- 63 | // Setup the hardware 64 | // --------------------------------------------------------------------------------- 65 | 66 | void setup() 67 | { 68 | // 69 | // pin numbers used in addition to the default SPI pins 70 | // 71 | const int LCD_CS_PIN = 10; 72 | const int LCD_DC_PIN = 9; 73 | const int TOUCH_CS_PIN = 8; 74 | 75 | 76 | // 77 | // this is where you set the display's orientation, choosing between Portrait 78 | // and Landscape modes by uncommenting the one you want 79 | // 80 | ui.begin(LCD_CS_PIN, LCD_DC_PIN, TOUCH_CS_PIN, LCD_ORIENTATION_PORTRAIT_4PIN_TOP, Arial_9_Bold); 81 | // ui.begin(LCD_ORIENTATION_LANDSCAPE_4PIN_LEFT, Arial_9_Bold); 82 | // ui.begin(LCD_ORIENTATION_PORTRAIT_4PIN_BOTTOM, Arial_9_Bold); 83 | // ui.begin(LCD_ORIENTATION_LANDSCAPE_4PIN_RIGHT, Arial_9_Bold); 84 | } 85 | 86 | 87 | // --------------------------------------------------------------------------------- 88 | // Define the menus and top level loop, place menus after setup() 89 | // --------------------------------------------------------------------------------- 90 | 91 | // 92 | // for each menu, create a forward declaration with "extern" 93 | // 94 | extern MENU_ITEM mainMenu[]; 95 | 96 | 97 | // 98 | // the main menu 99 | // 100 | MENU_ITEM mainMenu[] = { 101 | {MENU_ITEM_TYPE_MAIN_MENU_HEADER, "Example Two - Portrait Mode", MENU_COLUMNS_1, mainMenu}, 102 | {MENU_ITEM_TYPE_COMMAND, "Count to ten", commandCountToTen, NULL}, 103 | {MENU_ITEM_TYPE_COMMAND, "First law of robotics", commandFirstLawOfRobotics, NULL}, 104 | {MENU_ITEM_TYPE_COMMAND, "Draw one hundred dots", commandDraw100Dots, NULL}, 105 | {MENU_ITEM_TYPE_END_OF_MENU, "", NULL, NULL} 106 | }; 107 | 108 | 109 | 110 | // 111 | // display the menu, then execute commands selected by the user 112 | // 113 | void loop() 114 | { 115 | ui.displayAndExecuteMenu(mainMenu); 116 | } 117 | 118 | 119 | 120 | // --------------------------------------------------------------------------------- 121 | // Commands executed from the menu 122 | // --------------------------------------------------------------------------------- 123 | 124 | // 125 | // menu command to "Count To Ten" 126 | // 127 | void commandCountToTen(void) 128 | { 129 | // 130 | // draw title bar showing at the top of the screen 131 | // 132 | ui.drawTitleBar("Count To Ten"); 133 | 134 | // 135 | // draw the digits using a larger font 136 | // 137 | ui.lcdSetFont(Arial_60_Bold); 138 | 139 | 140 | // 141 | // count to ten, showing the digits on the screen, when done return to the menu 142 | // 143 | for(int i = 1; i <= 10; i++) 144 | { 145 | // 146 | // blank the screen below the Title Bar 147 | // 148 | ui.clearDisplaySpace(); 149 | 150 | // 151 | // show the count number on the LCD screen 152 | // 153 | ui.lcdSetCursorXY(ui.displaySpaceCenterX, ui.displaySpaceCenterY - 30); 154 | ui.lcdPrintCentered(i); 155 | 156 | // 157 | // delay for a quarter second while showing the count value 158 | // 159 | delay(250); 160 | } 161 | } 162 | 163 | 164 | 165 | // 166 | // menu command to "First Law Of Robotics" 167 | // 168 | void commandFirstLawOfRobotics(void) 169 | { 170 | // 171 | // draw title bar showing with the "Back" button 172 | // 173 | ui.drawTitleBarWithBackButton("First Law of Robotics"); 174 | 175 | // 176 | // blank the screen below the Title Bar 177 | // 178 | ui.clearDisplaySpace(); 179 | 180 | // 181 | // set the font for drawing the text 182 | // 183 | ui.lcdSetFont(Arial_12); 184 | 185 | // 186 | // print some text in the LCD's display space 187 | // 188 | int y = ui.displaySpaceCenterY - (2 * 18); 189 | ui.lcdSetCursorXY(ui.displaySpaceCenterX, y); 190 | ui.lcdPrintCentered("A robot may not injure a"); 191 | 192 | y += 18; 193 | ui.lcdSetCursorXY(ui.displaySpaceCenterX, y); 194 | ui.lcdPrintCentered("human being or, through"); 195 | 196 | y += 18; 197 | ui.lcdSetCursorXY(ui.displaySpaceCenterX, y); 198 | ui.lcdPrintCentered(" inaction, allow a human"); 199 | 200 | y += 18; 201 | ui.lcdSetCursorXY(ui.displaySpaceCenterX, y); 202 | ui.lcdPrintCentered("being to come to harm."); 203 | 204 | 205 | // 206 | // wait for the user to press the "Back" button, then return to the main menu 207 | // 208 | while(true) 209 | { 210 | ui.getTouchEvents(); 211 | 212 | if (ui.checkForBackButtonClicked()) 213 | return; 214 | } 215 | } 216 | 217 | 218 | 219 | // 220 | // menu command to "Draw dots" 221 | // 222 | void commandDraw100Dots(void) 223 | { 224 | // 225 | // draw title bar showing with the "Back" button 226 | // 227 | ui.drawTitleBarWithBackButton("Draw Dots"); 228 | 229 | // 230 | // blank the screen below the Title Bar 231 | // 232 | ui.clearDisplaySpace(); 233 | 234 | // 235 | // draw 100 random circles 236 | // 237 | for (int i = 0; i < 100; i++) 238 | { 239 | int radius = 20; 240 | int x = random(25, ui.displaySpaceWidth - 25); 241 | int y = random(25, ui.displaySpaceHeight - 25); 242 | uint16_t color = random(0, 0xffff); 243 | ui.lcdDrawFilledCircle(ui.displaySpaceLeftX + x, ui.displaySpaceTopY + y, radius, color); 244 | } 245 | 246 | // 247 | // wait for the user to press the "Back" button, then return to the main menu 248 | // 249 | while(true) 250 | { 251 | ui.getTouchEvents(); 252 | 253 | if (ui.checkForBackButtonClicked()) 254 | return; 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /examples/Example1_SimpleMenu/Example1_SimpleMenu.ino: -------------------------------------------------------------------------------- 1 | 2 | // ****************************************************************** 3 | // * * 4 | // * Example to shows how to create a simple menu * 5 | // * * 6 | // * S. Reifel & Co. 2/18/2022 * 7 | // * * 8 | // ****************************************************************** 9 | 10 | // This sketch shows how to use the "Teensy User Interface" to create a simple menu 11 | // with 3 commands. Below are the hardware and software requirements needed to make 12 | // this example work. 13 | // 14 | // 15 | // HARDWARE: 16 | // 17 | // This user interface works with a Teensy microcontroller (www.pjrc.com) and a 2.8" 18 | // 320x240 LCD touch screen display (the LCD must include a ILI9341 controller). 19 | // These displays can be purchased from PJRC: 20 | // https://www.pjrc.com/store/display_ili9341_touch.html 21 | // or on eBay and Amazon. 22 | // 23 | // To hookup the LCD to the Teensy, see: 24 | // https://github.com/Stan-Reifel/TeensyUserInterface/blob/master/images/HookupGuide.png 25 | // 26 | // 27 | // LIBRARIES: 28 | // 29 | // The TeensyUserInterface requires three libraries, all of which must be installed into 30 | // the Arduino IDE. To do so: 1) From the "Sketch" menu, select "Include Library", 31 | // then "Manage libraries". 2) In the "Filter your search" field, enter the library 32 | // name as shown below. 3) Click "Install". 33 | // 34 | // TeensyUserInterface - The Teensy User Interface 35 | // ILI9341_t3 - Driver for the LCD screen 36 | // XPT2046_Touchscreen - Driver for the touch screen 37 | // 38 | // The author has only tested the TeensyUserInterface code with a Teensy 3.6 but it 39 | // will likely work with other Teensys that support these libraries. 40 | // 41 | // 42 | // DOCUMENTATION: 43 | // 44 | // Documentation for the "Teensy User Interface" library can be found at: 45 | // https://github.com/Stan-Reifel/TeensyUserInterface 46 | 47 | 48 | 49 | // *********************************************************************** 50 | 51 | #include 52 | #include 53 | #include 54 | 55 | 56 | // 57 | // create the user interface object 58 | // 59 | TeensyUserInterface ui; 60 | 61 | 62 | 63 | // --------------------------------------------------------------------------------- 64 | // Setup the hardware 65 | // --------------------------------------------------------------------------------- 66 | 67 | void setup() 68 | { 69 | // 70 | // pin numbers used in addition to the default SPI pins 71 | // 72 | const int LCD_CS_PIN = 10; 73 | const int LCD_DC_PIN = 9; 74 | const int TOUCH_CS_PIN = 8; 75 | 76 | // 77 | // setup the LCD orientation, the default font and initialize the user interface 78 | // 79 | ui.begin(LCD_CS_PIN, LCD_DC_PIN, TOUCH_CS_PIN, LCD_ORIENTATION_LANDSCAPE_4PIN_RIGHT, Arial_9_Bold); 80 | } 81 | 82 | 83 | // --------------------------------------------------------------------------------- 84 | // Define the menus and top level loop, place menus after setup() 85 | // --------------------------------------------------------------------------------- 86 | 87 | // 88 | // Notes on building the menu table: 89 | // 90 | // The first line in the table always defines what type of menu it is, either a 91 | // Main Menu, or a Sub Menu. The table's last line marks the end of the table. 92 | // In between are menu items, each line represents one menu choice the user will 93 | // see displayed. 94 | // 95 | // There are three types of menu items: Commands, Toggles, and Sub Menus. In this 96 | // sketch we are only going to explore "Commands". 97 | // 98 | // A MENU_ITEM_TYPE_COMMAND line in the table adds a button to the menu. When 99 | // pressed, the specified function is executed. In the second field is the text 100 | // displayed on the button. A pointer to the function is entered in the third 101 | // field. The last field should always be NULL. 102 | 103 | 104 | // 105 | // for each menu, create a forward declaration with "extern" 106 | // 107 | extern MENU_ITEM mainMenu[]; 108 | 109 | 110 | // 111 | // the main menu 112 | // 113 | MENU_ITEM mainMenu[] = { 114 | {MENU_ITEM_TYPE_MAIN_MENU_HEADER, "Example One - Simple Menu", MENU_COLUMNS_1, mainMenu}, 115 | {MENU_ITEM_TYPE_COMMAND, "Count to ten", commandCountToTen, NULL}, 116 | {MENU_ITEM_TYPE_COMMAND, "Gettsburg address", commandGettsburgAddress, NULL}, 117 | {MENU_ITEM_TYPE_COMMAND, "Draw some shapes", commandDrawShapes, NULL}, 118 | {MENU_ITEM_TYPE_END_OF_MENU, "", NULL, NULL} 119 | }; 120 | 121 | 122 | 123 | // 124 | // display the menu, then execute commands selected by the user 125 | // 126 | void loop() 127 | { 128 | ui.displayAndExecuteMenu(mainMenu); 129 | } 130 | 131 | 132 | 133 | // --------------------------------------------------------------------------------- 134 | // Commands executed from the menu 135 | // --------------------------------------------------------------------------------- 136 | 137 | // 138 | // menu command to "Count To Ten" 139 | // 140 | void commandCountToTen(void) 141 | { 142 | // 143 | // draw title bar showing at the top of the screen 144 | // 145 | ui.drawTitleBar("Count To Ten"); 146 | 147 | // 148 | // count to ten, showing the digits on the screen, when done return to the menu 149 | // 150 | for(int i = 1; i <= 10; i++) 151 | { 152 | // 153 | // blank the screen below the Title Bar 154 | // 155 | ui.clearDisplaySpace(); 156 | 157 | // 158 | // show the count number on the LCD screen 159 | // 160 | ui.lcdSetCursorXY(160, 130); 161 | ui.lcdPrintCentered(i); 162 | 163 | // 164 | // delay for a half second while showing the count value 165 | // 166 | delay(500); 167 | } 168 | } 169 | 170 | 171 | 172 | // 173 | // menu command to "Show the Gettysburg Address" 174 | // 175 | void commandGettsburgAddress(void) 176 | { 177 | // 178 | // draw title bar showing with the "Back" button 179 | // 180 | ui.drawTitleBarWithBackButton("Gettsburg Address"); 181 | 182 | // 183 | // blank the screen below the Title Bar 184 | // 185 | ui.clearDisplaySpace(); 186 | 187 | // 188 | // print some text in the LCD's display space 189 | // 190 | ui.lcdSetCursorXY(160, 100); 191 | ui.lcdPrintCentered("Four score and seven years ago our fathers"); 192 | ui.lcdSetCursorXY(160, 120); 193 | ui.lcdPrintCentered("brought forth on this continent, a new nation,"); 194 | ui.lcdSetCursorXY(160, 140); 195 | ui.lcdPrintCentered("conceived in Liberty, and dedicated to the"); 196 | ui.lcdSetCursorXY(160, 160); 197 | ui.lcdPrintCentered("proposition that all men are created equal."); 198 | 199 | 200 | // 201 | // wait for the user presses the "Back" button, then return to the main menu 202 | // 203 | while(true) 204 | { 205 | ui.getTouchEvents(); 206 | 207 | if (ui.checkForBackButtonClicked()) 208 | return; 209 | } 210 | } 211 | 212 | 213 | 214 | // 215 | // menu command to "Draw shapes" 216 | // 217 | void commandDrawShapes(void) 218 | { 219 | // 220 | // draw title bar showing with the "Back" button 221 | // 222 | ui.drawTitleBarWithBackButton("Draw Shapes"); 223 | 224 | // 225 | // blank the screen below the Title Bar 226 | // 227 | ui.clearDisplaySpace(); 228 | 229 | // 230 | // print some random shapes in the LCD's display space 231 | // 232 | int radius = random(20, 50); 233 | int x = random(55, 260); 234 | int y = random(100, 140); 235 | uint16_t color = LCD_GREEN; 236 | ui.lcdDrawFilledCircle(x, y, radius, color); 237 | 238 | x = random(55, 260); 239 | y = random(100, 140); 240 | int w = random(20, 50); 241 | color = LCD_RED; 242 | ui.lcdDrawFilledTriangle(x, y, x-w/2, y+w, x+w/2, y+w, color); 243 | 244 | x = random(55, 260); 245 | y = random(100, 140); 246 | w = random(20, 50); 247 | int h = random(20, 50); 248 | color = LCD_YELLOW; 249 | ui.lcdDrawFilledRectangle(x, y, w, h, color); 250 | 251 | 252 | // 253 | // wait for the user presses the "Back" button, then return to the main menu 254 | // 255 | while(true) 256 | { 257 | ui.getTouchEvents(); 258 | 259 | if (ui.checkForBackButtonClicked()) 260 | return; 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /examples/Example7_MenuWithSubMenus/Example7_MenuWithSubMenus.ino: -------------------------------------------------------------------------------- 1 | 2 | // ****************************************************************** 3 | // * * 4 | // * Example shows how to create a menu one or more sub menus * 5 | // * * 6 | // * S. Reifel & Co. 2/18/2022 * 7 | // * * 8 | // ****************************************************************** 9 | 10 | // 11 | // DOCUMENTATION: 12 | // 13 | // Documentation for the "Teensy User Interface" library can be found at: 14 | // https://github.com/Stan-Reifel/TeensyUserInterface 15 | 16 | 17 | 18 | // *********************************************************************** 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | 25 | // 26 | // create the user interface object 27 | // 28 | TeensyUserInterface ui; 29 | 30 | 31 | 32 | // --------------------------------------------------------------------------------- 33 | // Setup the hardware 34 | // --------------------------------------------------------------------------------- 35 | 36 | void setup() 37 | { 38 | // 39 | // pin numbers used in addition to the default SPI pins 40 | // 41 | const int LCD_CS_PIN = 10; 42 | const int LCD_DC_PIN = 9; 43 | const int TOUCH_CS_PIN = 8; 44 | 45 | // 46 | // setup the LCD orientation, the default font and initialize the user interface 47 | // 48 | ui.begin(LCD_CS_PIN, LCD_DC_PIN, TOUCH_CS_PIN, LCD_ORIENTATION_LANDSCAPE_4PIN_RIGHT, Arial_9_Bold); 49 | } 50 | 51 | 52 | // --------------------------------------------------------------------------------- 53 | // Define the menus and top level loop, place menus after setup() 54 | // --------------------------------------------------------------------------------- 55 | 56 | // 57 | // Notes on building the menu tables: 58 | // 59 | // Every application should have one Main Menu. In some cases, you may have too many 60 | // buttons to fit on a single screen. The solution here is to use Sub Menus. Sub Menus 61 | // are menus that are called from a main menu, or another sub menu. 62 | // 63 | // Sub menus tables are built the same way the Main Menu, except for changes in the first 64 | // line. The first field, of the first line, in a sub menu table is always set to: 65 | // MENU_ITEM_TYPE_SUB_MENU_HEADER 66 | // and the last field is set to the name of its parent menu (typically "mainMenu"). 67 | // 68 | // The process for adding a Sub Menu button to the main menu is very similar to adding a 69 | // "Command" button, by inserting one line in the Main Menu table. In this case that line 70 | // begins with MENU_ITEM_TYPE_SUB_MENU (in the first field). The second field is the text 71 | // that you want displayed on the button that invokes the sub menu. The third field is 72 | // the number of columns (ie MENU_COLUMNS_1, MENU_COLUMNS_2...), and the the fourth field 73 | // points to the Sub Menu table. 74 | // 75 | // In this example, we will define a main menu having several commands, along with a 76 | // "Settings" sub menu. The Settings menu will also have some commands, plus it will have 77 | // a sub menu. 78 | 79 | 80 | 81 | // 82 | // for each menu, create a forward declaration with "extern" 83 | // 84 | extern MENU_ITEM mainMenu[]; 85 | extern MENU_ITEM settingsMenu[]; 86 | extern MENU_ITEM ridiculousMenu[]; 87 | 88 | 89 | 90 | // 91 | // the main menu 92 | // 93 | MENU_ITEM mainMenu[] = { 94 | {MENU_ITEM_TYPE_MAIN_MENU_HEADER, "Example Seven - Sub Menus", MENU_COLUMNS_1, mainMenu}, 95 | {MENU_ITEM_TYPE_COMMAND, "Count to two", commandCountTo2, NULL}, 96 | {MENU_ITEM_TYPE_COMMAND, "Count to four", commandCountTo4, NULL}, 97 | {MENU_ITEM_TYPE_COMMAND, "Count to six", commandCountTo6, NULL}, 98 | {MENU_ITEM_TYPE_SUB_MENU, "Settings", NULL, settingsMenu}, 99 | {MENU_ITEM_TYPE_END_OF_MENU, "", NULL, NULL} 100 | }; 101 | 102 | 103 | // 104 | // the Settings menu 105 | // 106 | MENU_ITEM settingsMenu[] = { 107 | {MENU_ITEM_TYPE_SUB_MENU_HEADER, "Settings...", MENU_COLUMNS_1, mainMenu}, 108 | {MENU_ITEM_TYPE_COMMAND, "Set screen colors", commandSetScreenColors, NULL}, 109 | {MENU_ITEM_TYPE_TOGGLE, "Self destruct", enableSelfDestructCallback, NULL}, 110 | {MENU_ITEM_TYPE_SUB_MENU, "Ridiculous stuff", NULL, ridiculousMenu}, 111 | {MENU_ITEM_TYPE_END_OF_MENU, "", NULL, NULL} 112 | }; 113 | 114 | 115 | // 116 | // the Ridiculous menu 117 | // 118 | MENU_ITEM ridiculousMenu[] = { 119 | {MENU_ITEM_TYPE_SUB_MENU_HEADER, "Ridiculous Stuff...", MENU_COLUMNS_2, settingsMenu}, 120 | {MENU_ITEM_TYPE_COMMAND, "Beer on the wall", commandBeerOnTheWall, NULL}, 121 | {MENU_ITEM_TYPE_COMMAND, "Funny buttons", commandFunnyButtons, NULL}, 122 | {MENU_ITEM_TYPE_END_OF_MENU, "", NULL, NULL} 123 | }; 124 | 125 | 126 | 127 | 128 | // 129 | // display the menu, then execute commands selected by the user 130 | // 131 | void loop() 132 | { 133 | ui.displayAndExecuteMenu(mainMenu); 134 | } 135 | 136 | 137 | 138 | // --------------------------------------------------------------------------------- 139 | // Commands executed from the Main Menu 140 | // --------------------------------------------------------------------------------- 141 | 142 | // 143 | // menu command to "Count To 2" 144 | // 145 | void commandCountTo2(void) 146 | { 147 | ui.drawTitleBar("Count To Two"); 148 | showCount(2); 149 | } 150 | 151 | 152 | // 153 | // menu command to "Count To 4" 154 | // 155 | void commandCountTo4(void) 156 | { 157 | ui.drawTitleBar("Count To Four"); 158 | showCount(4); 159 | } 160 | 161 | 162 | // 163 | // menu command to "Count To 6" 164 | // 165 | void commandCountTo6(void) 166 | { 167 | ui.drawTitleBar("Count To Six"); 168 | showCount(6); 169 | } 170 | 171 | 172 | 173 | // 174 | // display a screen that counts 175 | // 176 | void showCount(int countValue) 177 | { 178 | // 179 | // draw the digits using a large font 180 | // 181 | ui.lcdSetFont(Arial_60_Bold); 182 | 183 | 184 | // 185 | // count to ten, showing the digits on the screen, when done return to the menu 186 | // 187 | for(int i = 1; i <= countValue; i++) 188 | { 189 | // 190 | // blank the screen below the Title Bar 191 | // 192 | ui.clearDisplaySpace(); 193 | 194 | // 195 | // show the count number on the LCD screen 196 | // 197 | ui.lcdSetCursorXY(ui.displaySpaceCenterX, ui.displaySpaceCenterY - 30); 198 | ui.lcdPrintCentered(i); 199 | 200 | // 201 | // delay for a quarter second while showing the count value 202 | // 203 | delay(350); 204 | } 205 | } 206 | 207 | 208 | // --------------------------------------------------------------------------------- 209 | // Commands executed from the "Settings" Sub Menu 210 | // --------------------------------------------------------------------------------- 211 | 212 | // 213 | // settings menu command to set the screen colors 214 | // 215 | static int colorSelection = 1; 216 | 217 | void commandSetScreenColors(void) 218 | { 219 | ui.drawTitleBarWithBackButton("Display Settings"); 220 | ui.clearDisplaySpace(); 221 | 222 | // 223 | // use a Selection Boxes to select screen colors 224 | // 225 | SELECTION_BOX colorSelectionBox; 226 | colorSelectionBox.labelText = "Screen color palette"; 227 | colorSelectionBox.value = colorSelection; 228 | colorSelectionBox.choice0Text = "Gray"; 229 | colorSelectionBox.choice1Text = "Blue"; 230 | colorSelectionBox.choice2Text = ""; 231 | colorSelectionBox.choice3Text = ""; 232 | colorSelectionBox.centerX = ui.displaySpaceCenterX; 233 | colorSelectionBox.centerY = 146; 234 | colorSelectionBox.width = 250; 235 | colorSelectionBox.height = 33; 236 | ui.drawSelectionBox(colorSelectionBox); 237 | 238 | // 239 | // process touch events 240 | // 241 | while(true) 242 | { 243 | ui.getTouchEvents(); 244 | 245 | // 246 | // process touch events in the selection box 247 | // 248 | ui.checkForSelectionBoxTouched(colorSelectionBox); 249 | 250 | // 251 | // when the "Back" button is pressed, save all the values selected 252 | // by the user 253 | // 254 | if (ui.checkForBackButtonClicked()) 255 | { 256 | colorSelection = colorSelectionBox.value; 257 | 258 | if (colorSelection == 0) 259 | ui.setColorPaletteGray(); 260 | else 261 | ui.setColorPaletteBlue(); 262 | return; 263 | } 264 | } 265 | } 266 | 267 | 268 | 269 | // 270 | // toggle used to enable / disable "Self Destruct Mode" 271 | // 272 | // Sometimes it is easier to use a single button to configure something, rather than 273 | // having a separate screen with setting choices. This can be done using a "Toggle" 274 | // button. 275 | // 276 | static byte enableSelfDestruct = false; 277 | 278 | void enableSelfDestructCallback(void) 279 | { 280 | // 281 | // check if menu is requesting that the state be changed (can have more than 2 states) 282 | // 283 | if (ui.toggleSelectNextStateFlg) 284 | { 285 | // 286 | // select the next state 287 | // 288 | enableSelfDestruct = !enableSelfDestruct; 289 | } 290 | 291 | // 292 | // here is where you update stuff depending on the choice made by the user 293 | // This assumes that you have wired a LED to pin 31 294 | // 295 | pinMode(31, OUTPUT); 296 | 297 | if (enableSelfDestruct) 298 | digitalWrite(31, HIGH); 299 | else 300 | digitalWrite(31, LOW); 301 | 302 | // 303 | // send back an indication of the current state, this text is written on the button 304 | // 305 | if(enableSelfDestruct) 306 | ui.toggleText = "On"; 307 | else 308 | ui.toggleText = "Off"; 309 | } 310 | 311 | 312 | // --------------------------------------------------------------------------------- 313 | // Commands executed from the "Ridiculous" Sub Menu 314 | // --------------------------------------------------------------------------------- 315 | 316 | // 317 | // "Beer on the wall command" command 318 | // 319 | static int initialBottlesOfBeer = 99; 320 | 321 | 322 | void commandBeerOnTheWall(void) 323 | { 324 | int bottlesOfBeer = initialBottlesOfBeer; 325 | int beerCountY = ui.displaySpaceTopY + 44; 326 | 327 | ui.drawTitleBar("Beer Ccounter"); 328 | ui.clearDisplaySpace(); 329 | ui.lcdSetCursorXY(ui.displaySpaceCenterX, ui.displaySpaceTopY + 28); 330 | ui.lcdPrintCentered("Bottles of beer on the wall"); 331 | ui.lcdSetCursorXY(ui.displaySpaceCenterX, beerCountY); 332 | ui.lcdPrintCentered(bottlesOfBeer); 333 | 334 | BUTTON okButton = {"OK", ui.displaySpaceCenterX-60, ui.displaySpaceBottomY-37, 100, 35}; 335 | BUTTON cancelButton = {"Cancel", ui.displaySpaceCenterX+60, ui.displaySpaceBottomY-37, 100, 35}; 336 | BUTTON countButton = {"Take one down", ui.displaySpaceCenterX, ui.displaySpaceCenterY, 120, 45}; 337 | 338 | ui.drawButton(okButton); 339 | ui.drawButton(cancelButton); 340 | ui.drawButton(countButton); 341 | 342 | while(true) 343 | { 344 | ui.getTouchEvents(); 345 | 346 | if (ui.checkForMenuButtonClicked()) 347 | return; 348 | 349 | if (ui.checkForButtonClicked(okButton)) 350 | { 351 | initialBottlesOfBeer = bottlesOfBeer; 352 | return; 353 | } 354 | 355 | if (ui.checkForButtonClicked(cancelButton)) 356 | return; 357 | 358 | if (ui.checkForButtonAutoRepeat(countButton)) 359 | { 360 | bottlesOfBeer--; 361 | ui.lcdDrawFilledRectangle(ui.displaySpaceCenterX - 40, beerCountY, 80, 10, LCD_BLACK); 362 | ui.lcdSetCursorXY(ui.displaySpaceCenterX, beerCountY); 363 | ui.lcdPrintCentered(bottlesOfBeer); 364 | } 365 | } 366 | } 367 | 368 | 369 | 370 | // 371 | // "Funny buttons" command 372 | // 373 | void commandFunnyButtons(void) 374 | { 375 | ui.drawTitleBar("Funny buttons"); 376 | 377 | BUTTON_EXTENDED redButton = {"Red", 90, 80, 45, 25, LCD_RED, LCD_YELLOW, LCD_ORANGE, LCD_WHITE, Arial_8}; 378 | BUTTON_EXTENDED blueButton = {"Blue", 250, 100, 90, 90, LCD_BLUE, LCD_LIGHTBLUE, LCD_LIGHTBLUE, LCD_NAVY, Arial_32}; 379 | BUTTON okButton = {"OK", ui.displaySpaceCenterX, 200, 100, 35}; 380 | 381 | ui.clearDisplaySpace(LCD_CYAN); 382 | 383 | ui.drawButton(redButton); 384 | ui.drawButton(blueButton); 385 | ui.drawButton(okButton); 386 | 387 | while(true) 388 | { 389 | ui.getTouchEvents(); 390 | 391 | if (ui.checkForButtonClicked(redButton)) 392 | ui.lcdDrawFilledCircle(50, 170, 30, LCD_RED); 393 | 394 | if (ui.checkForButtonClicked(blueButton)) 395 | ui.lcdDrawFilledCircle(50, 170, 30, LCD_BLUE); 396 | 397 | if (ui.checkForButtonClicked(okButton)) 398 | return; 399 | } 400 | } 401 | -------------------------------------------------------------------------------- /src/TeensyUserInterface.h: -------------------------------------------------------------------------------- 1 | // ****************************************************************** 2 | // * * 3 | // * Header file for TeensyUserInterface.cpp * 4 | // * * 5 | // * Copyright (c) S. Reifel & Co, 2022 * 6 | // * * 7 | // ****************************************************************** 8 | 9 | 10 | // MIT License 11 | // 12 | // Copyright (c) 2022 Stanley Reifel & Co. 13 | // 14 | // Permission is hereby granted, free of charge, to any person obtaining a copy 15 | // of this software and associated documentation files (the "Software"), to deal 16 | // in the Software without restriction, including without limitation the rights 17 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | // copies of the Software, and to permit persons to whom the Software is furnished 19 | // to do so, subject to the following conditions: 20 | // 21 | // The above copyright notice and this permission notice shall be included in all 22 | // copies or substantial portions of the Software. 23 | // 24 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | // SOFTWARE. 31 | 32 | 33 | #ifndef TeensyUserInterface_h 34 | #define TeensyUserInterface_h 35 | 36 | #include 37 | 38 | 39 | // 40 | // lcd display screen orientations 41 | // 42 | const int LCD_ORIENTATION_PORTRAIT_4PIN_TOP = 0; 43 | const int LCD_ORIENTATION_LANDSCAPE_4PIN_LEFT = 1; 44 | const int LCD_ORIENTATION_PORTRAIT_4PIN_BOTTOM = 2; 45 | const int LCD_ORIENTATION_LANDSCAPE_4PIN_RIGHT = 3; 46 | 47 | 48 | // 49 | // 16 bit colors in rgb 565 format 50 | // 51 | const uint16_t LCD_BLACK = 0x0000; 52 | const uint16_t LCD_NAVY = 0x000F; 53 | const uint16_t LCD_DARKGREEN = 0x03E0; 54 | const uint16_t LCD_DARKCYAN = 0x03EF; 55 | const uint16_t LCD_MAROON = 0x7800; 56 | const uint16_t LCD_PURPLE = 0x780F; 57 | const uint16_t LCD_OLIVE = 0x7BE0; 58 | const uint16_t LCD_LIGHTGREY = 0xC618; 59 | const uint16_t LCD_DARKGREY = 0x7BEF; 60 | const uint16_t LCD_BLUE = 0x001F; 61 | const uint16_t LCD_LIGHTBLUE = 0x5ADF; 62 | const uint16_t LCD_DARKBLUE = 0x0016; 63 | const uint16_t LCD_GREEN = 0x07E0; 64 | const uint16_t LCD_CYAN = 0x07FF; 65 | const uint16_t LCD_RED = 0xF800; 66 | const uint16_t LCD_MAGENTA = 0xF81F; 67 | const uint16_t LCD_YELLOW = 0xFFE0; 68 | const uint16_t LCD_WHITE = 0xFFFF; 69 | const uint16_t LCD_ORANGE = 0xFD20; 70 | const uint16_t LCD_GREENYELLOW = 0xAFE5; 71 | 72 | 73 | // 74 | // datatype for fonts 75 | // 76 | typedef ILI9341_t3_font_t ui_font; 77 | 78 | 79 | // 80 | // definition of a Button, the menu's colors and font are used 81 | // 82 | typedef struct 83 | { 84 | const char *labelText; 85 | int centerX; 86 | int centerY; 87 | int width; 88 | int height; 89 | } BUTTON; 90 | 91 | 92 | // 93 | // definition of a Button with extended options 94 | // 95 | typedef struct 96 | { 97 | const char *labelText; 98 | int centerX; 99 | int centerY; 100 | int width; 101 | int height; 102 | uint16_t buttonColor; 103 | uint16_t buttonSelectedColor; 104 | uint16_t buttonFrameColor; 105 | uint16_t buttonTextColor; 106 | const ui_font &buttonFont; 107 | } BUTTON_EXTENDED; 108 | 109 | 110 | // 111 | // definition of a integer Number Box 112 | // 113 | typedef struct 114 | { 115 | const char *labelText; 116 | int value; 117 | int minimumValue; 118 | int maximumValue; 119 | int stepAmount; 120 | int centerX; 121 | int centerY; 122 | int width; 123 | int height; 124 | } NUMBER_BOX; 125 | 126 | 127 | // 128 | // definition of a float Number Box 129 | // 130 | typedef struct 131 | { 132 | const char *labelText; 133 | float value; 134 | float minimumValue; 135 | float maximumValue; 136 | float stepAmount; 137 | int digitsRightOfDecimal; 138 | int centerX; 139 | int centerY; 140 | int width; 141 | int height; 142 | } NUMBER_BOX_FLOAT; 143 | 144 | 145 | // 146 | // definition of a Selection Box 147 | // 148 | typedef struct 149 | { 150 | const char *labelText; 151 | int value; 152 | const char *choice0Text; 153 | const char *choice1Text; 154 | const char *choice2Text; 155 | const char *choice3Text; 156 | int centerX; 157 | int centerY; 158 | int width; 159 | int height; 160 | } SELECTION_BOX; 161 | 162 | 163 | // 164 | // definition of an entry in menu's table 165 | // 166 | typedef struct _MENU_ITEM 167 | { 168 | byte MenuItemType; 169 | const char *MenuItemText; 170 | void (*MenuItemFunction)(); 171 | _MENU_ITEM *MenuItemSubMenu; 172 | } MENU_ITEM; 173 | 174 | 175 | // 176 | // types of entries in a menu table 177 | // 178 | const byte MENU_ITEM_TYPE_MAIN_MENU_HEADER = 0; 179 | const byte MENU_ITEM_TYPE_SUB_MENU_HEADER = 1; 180 | const byte MENU_ITEM_TYPE_SUB_MENU = 2; 181 | const byte MENU_ITEM_TYPE_COMMAND = 3; 182 | const byte MENU_ITEM_TYPE_TOGGLE = 4; 183 | const byte MENU_ITEM_TYPE_END_OF_MENU = 5; 184 | 185 | 186 | // 187 | // number of columns of buttons in a menu 188 | // 189 | #define MENU_COLUMNS_1 ((void (*)()) 1) 190 | #define MENU_COLUMNS_2 ((void (*)()) 2) 191 | #define MENU_COLUMNS_3 ((void (*)()) 3) 192 | #define MENU_COLUMNS_4 ((void (*)()) 4) 193 | 194 | 195 | // 196 | // types of touch events 197 | // 198 | const int TOUCH_NO_EVENT = 0; // no event from the touch screen (nothing being touched) 199 | const int TOUCH_PUSHED_EVENT = 1; // touch screen just touched 200 | const int TOUCH_RELEASED_EVENT = 2; // touch screen just released 201 | const int TOUCH_REPEAT_EVENT = 3; // touch screen touched and event repeating 202 | 203 | 204 | // 205 | // the TeensyUserInterface class 206 | // 207 | class TeensyUserInterface 208 | { 209 | public: 210 | // 211 | // public member variables 212 | // 213 | int lcdWidth; 214 | int lcdHeight; 215 | int displaySpaceWidth; 216 | int displaySpaceHeight; 217 | int displaySpaceLeftX; 218 | int displaySpaceRightX; 219 | int displaySpaceTopY; 220 | int displaySpaceBottomY; 221 | int displaySpaceCenterX; 222 | int displaySpaceCenterY; 223 | int touchEventType; 224 | int touchEventX; 225 | int touchEventY; 226 | byte toggleSelectNextStateFlg; 227 | const char *toggleText; 228 | 229 | 230 | // 231 | // public functions 232 | // 233 | TeensyUserInterface(void); 234 | void begin(int lcdCSPin, int LcdDCPin, int TouchScreenCSPin, int lcdOrientation, const ui_font &font); 235 | void setOrientation(int lcdOrientation); 236 | void setColorPaletteBlue(void); 237 | void setColorPaletteGray(void); 238 | 239 | void setMenuColors(uint16_t _menuBackgroundColor, uint16_t _menuButtonColor, uint16_t _menuButtonSelectedColor, uint16_t _menuButtonFrameColor, uint16_t _menuButtonTextColor); 240 | void setMenuFont(const ui_font &font); 241 | void selectAndDrawMenu(MENU_ITEM *menu, boolean drawMenuFlg); 242 | void displayAndExecuteMenu(MENU_ITEM *menu); 243 | void setInMenuCallbackFunction(void (*callbackFunction)()); 244 | 245 | void setTitleBarColors(uint16_t _titleBarColor, uint16_t _titleBarTextColor, uint16_t _titleBarBackButtonColor, uint16_t _titleBarBackButtonSelectedColor); 246 | void setTitleBarFont(const ui_font &font); 247 | void drawTitleBar(const char *titleBarText); 248 | void drawTitleBarWithBackButton(const char *titleBarText); 249 | void drawTitleBarWithMenuButton(const char *titleBarText); 250 | boolean checkForBackButtonClicked(void); 251 | boolean checkForMenuButtonClicked(void); 252 | void clearDisplaySpace(void); 253 | void clearDisplaySpace(uint16_t backgroundColor); 254 | 255 | void drawButton(BUTTON &uiButton); 256 | void drawButton(BUTTON &uiButton, boolean buttonSelectedFlg); 257 | void drawButton(BUTTON_EXTENDED &uiButtonExt); 258 | void drawButton(BUTTON_EXTENDED &uiButtonExt, boolean buttonSelectedFlg); 259 | void drawButton(const char *buttonText, boolean buttonSelectedFlg, int buttonX, int buttonY, int buttonWidth, int buttonHeight); 260 | void drawButton(const char *buttonText, int buttonX, int buttonY, int buttonWidth, int buttonHeight, uint16_t buttonColor, uint16_t buttonFrameColor, uint16_t buttonTextColor, const ui_font &buttonFont); 261 | boolean breakStringAtWhiteSpace(const char *srcString, int *srcIndex, char *destString, int destBufferLength, int breakAtWhiteCount); 262 | boolean checkForButtonClicked(BUTTON &uiButton); 263 | boolean checkForButtonClicked(BUTTON_EXTENDED &uiButton); 264 | boolean checkForButtonAutoRepeat(BUTTON &uiButton); 265 | boolean checkForButtonAutoRepeat(BUTTON_EXTENDED &uiButton); 266 | boolean checkForButtonFirstTouched(BUTTON &uiButton); 267 | boolean checkForButtonFirstTouched(BUTTON_EXTENDED &uiButton); 268 | 269 | void drawNumberBox(NUMBER_BOX &numberBox); 270 | void drawNumberBox(NUMBER_BOX_FLOAT &numberBox); 271 | boolean checkForNumberBoxTouched(NUMBER_BOX &numberBox); 272 | boolean checkForNumberBoxTouched(NUMBER_BOX_FLOAT &numberBox); 273 | 274 | void drawSelectionBox(SELECTION_BOX &selectionBox); 275 | boolean checkForSelectionBoxTouched(SELECTION_BOX &selectionBox); 276 | 277 | boolean checkForTouchEventInRect(int eventType, int rectX1, int rectY1, int rectX2, int rectY2); 278 | void getTouchEvents(void); 279 | void setDefaultTouchScreenCalibrationConstants(int lcdOrientation); 280 | void setTouchScreenCalibrationConstants(int tsToLCDOffsetX, float tsToLCDScalerX, int tsToLCDOffsetY, float tsToLCDScalerY); 281 | boolean getTouchScreenCoords(int *xLCD, int *yLCD); 282 | 283 | void lcdClearScreen(uint16_t color); 284 | void lcdDrawPixel(int x, int y, uint16_t color); 285 | void lcdDrawLine(int x1, int y1, int x2, int y2, uint16_t color); 286 | void lcdDrawHorizontalLine(int x, int y, int length, uint16_t color); 287 | void lcdDrawVerticalLine(int x, int y, int length, uint16_t color); 288 | void lcdDrawRectangle(int x, int y, int width, int height, uint16_t color); 289 | void lcdDrawRoundedRectangle(int x, int y, int width, int height, int radius, uint16_t color); 290 | void lcdDrawTriangle(int x0, int y0, int x1, int y1, int x2, int y2, uint16_t color); 291 | void lcdDrawCircle(int x, int y, int radius, uint16_t color); 292 | void lcdDrawFilledRectangle(int x, int y, int width, int height, uint16_t color); 293 | void lcdDrawFilledRoundedRectangle(int x, int y, int width, int height, int radius, uint16_t color); 294 | void lcdDrawFilledTriangle(int x0, int y0, int x1, int y1, int x2, int y2, uint16_t color); 295 | void lcdDrawFilledCircle(int x, int y, int radius, uint16_t color); 296 | void lcdDrawImage(int x, int y, int width, int height, const uint16_t *image); 297 | void lcdSetFont(const ui_font &font); 298 | void lcdSetFontColor(uint16_t color); 299 | void lcdPrint(char *s); 300 | void lcdPrint(const char *s); 301 | void lcdPrint(int n); 302 | void lcdPrint(double n, int digitsRightOfDecimal = 5); 303 | void lcdPrintRightJustified(char *s); 304 | void lcdPrintRightJustified(const char *s); 305 | void lcdPrintRightJustified(int n); 306 | void lcdPrintRightJustified(double n, int digitsRightOfDecimal = 5); 307 | void lcdPrintCentered(char *s); 308 | void lcdPrintCentered(const char *s); 309 | void lcdPrintCentered(int n); 310 | void lcdPrintCentered(double n, int digitsRightOfDecimal = 5); 311 | void lcdPrintCharacter(byte character); 312 | int lcdStringWidthInPixels(char *s); 313 | int lcdStringWidthInPixels(const char *s); 314 | int lcdGetFontHeightWithoutDecenders(void); 315 | int lcdGetFontHeightWithDecentersAndLineSpacing(void); 316 | void lcdSetCursorXY(int x, int y); 317 | void lcdGetCursorXY(int *x, int *y); 318 | uint16_t lcdMakeColor(int red, int green, int blue); 319 | 320 | void writeConfigurationByte(int EEPromAddress, byte value); 321 | byte readConfigurationByte(int EEPromAddress, byte defaultValue); 322 | void writeConfigurationShort(int EEPromAddress, short value); 323 | short readConfigurationShort(int EEPromAddress, short defaultValue); 324 | void writeConfigurationInt(int EEPromAddress, int value); 325 | int readConfigurationInt(int EEPromAddress, int defaultValue); 326 | void writeConfigurationFloat(int EEPromAddress, float value); 327 | float readConfigurationFloat(int EEPromAddress, float defaultValue); 328 | 329 | 330 | 331 | private: 332 | // 333 | // private member variables 334 | // 335 | MENU_ITEM *currentMenuTable; 336 | const ui_font *currentFont; 337 | void (*inMenuCallbackFunction)(); 338 | 339 | uint16_t titleBarColor; 340 | uint16_t titleBarTextColor; 341 | uint16_t titleBarBackButtonColor; 342 | uint16_t titleBarBackButtonSelectedColor; 343 | const ui_font *titleBarFont; 344 | int buttonTypeOnTitleBar; 345 | 346 | uint16_t menuBackgroundColor; 347 | uint16_t menuButtonColor; 348 | uint16_t menuButtonSelectedColor; 349 | uint16_t menuButtonFrameColor; 350 | uint16_t menuButtonTextColor; 351 | const ui_font *menuButtonFont; 352 | 353 | int numberBoxRepeatCount; 354 | 355 | int touchScreenToLCDOffsetX; 356 | float touchScreenToLCDScalerX; 357 | int touchScreenToLCDOffsetY; 358 | float touchScreenToLCDScalerY; 359 | int touchState; 360 | 361 | 362 | // 363 | // private functions 364 | // 365 | void executeMenuItem(int menuIdx); 366 | void drawMenu(void); 367 | void drawMenuItem(int menuIdx, boolean buttonSelectedFlg); 368 | int findMenuButtonForTouchEvent(void); 369 | void getMenuButtonSizeAndLocation(int menuButtonNumber, int *buttonX, int *buttonY, int *buttonWidth, int *buttonHeight); 370 | 371 | void drawTitleBar(const char *titleBarText, int buttonType); 372 | void drawTitleBarBackButton(boolean buttonSelectedFlg); 373 | void drawTitleBarMenuButton(boolean buttonSelectedFlg); 374 | void getBackButtonSizeAndLocation(int *buttonX, int *buttonY, int *buttonWidth, int *buttonHeight); 375 | void getMenuButtonSizeAndLocation(int *buttonX, int *buttonY, int *buttonWidth, int *buttonHeight); 376 | 377 | void drawDownButtonInNumberBox(NUMBER_BOX &numberBox, boolean showButtonTouchedFlg); 378 | void drawUpButtonInNumberBox(NUMBER_BOX &numberBox, boolean showButtonTouchedFlg); 379 | boolean updateNumberBoxNumber(NUMBER_BOX &numberBox, int stepAmount); 380 | void drawNumberInNumberBox(NUMBER_BOX &numberBox); 381 | void getNumberBoxCoordinates(NUMBER_BOX &numberBox, int *downButtonX, int *numberX, int *upButtonX, int *topY, int *buttonWidth, int *numberWidth, int *height); 382 | void drawDownButtonInNumberBoxFloat(NUMBER_BOX_FLOAT &numberBox, boolean showButtonTouchedFlg); 383 | void drawUpButtonInNumberBoxFloat(NUMBER_BOX_FLOAT &numberBox, boolean showButtonTouchedFlg); 384 | boolean updateNumberBoxNumberFloat(NUMBER_BOX_FLOAT &numberBox, float stepAmount); 385 | void drawNumberInNumberBoxFloat(NUMBER_BOX_FLOAT &numberBox); 386 | void getNumberBoxCoordinatesFloat(NUMBER_BOX_FLOAT &numberBox, int *downButtonX, int *numberX, int *upButtonX, int *topY, int *buttonWidth, int *numberWidth, int *height); 387 | 388 | void drawSelectionBoxCell(SELECTION_BOX &selectionBox, int cellNumber, boolean showButtonTouchedFlg); 389 | void getCoordsOfSelectionBoxCell(SELECTION_BOX &selectionBox, int cellNumber, int *X, int *Y, int *width, int *height); 390 | int countSelectionBoxChoices(SELECTION_BOX &selectionBox); 391 | 392 | void touchScreenInitialize(int lcdOrientation); 393 | void touchScreenSetOrientation(int lcdOrientation); 394 | boolean getRAWTouchScreenCoords(int *xRaw, int *yRaw); 395 | 396 | void lcdInitialize(int lcdOrientation, const ui_font &font); 397 | void lcdSetOrientation(int lcdOrientation); 398 | }; 399 | 400 | // ------------------------------------ End --------------------------------- 401 | #endif 402 | -------------------------------------------------------------------------------- /examples/Example8_StopWatch/Example8_StopWatch.ino: -------------------------------------------------------------------------------- 1 | 2 | // ****************************************************************** 3 | // * * 4 | // * Example shows an application that starts with the app's * 5 | // * main screen, then pressing the Menu button sets options * 6 | // * * 7 | // * S. Reifel & Co. 2/18/2022 * 8 | // * * 9 | // ****************************************************************** 10 | 11 | // The previous examples show sketches that first run by displaying a menu. 12 | // In some cases you want to start your application with its display, then 13 | // press a button to pull up its menu. This sketch runs a Stopwatch. The 14 | // stopwatch is displayed immediately when powered up. At anytime the user 15 | // can press the "Menu" button to get a list of options. 16 | 17 | // 18 | // DOCUMENTATION: 19 | // 20 | // Documentation for the "Teensy User Interface" library can be found at: 21 | // https://github.com/Stan-Reifel/TeensyUserInterface 22 | 23 | 24 | 25 | // *********************************************************************** 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | 32 | // 33 | // create the user interface object 34 | // 35 | TeensyUserInterface ui; 36 | 37 | 38 | // 39 | // values for stopwatchMode 40 | // 41 | const byte STOPWATCH_READY = 0; 42 | const byte STOPWATCH_RUNNING = 1; 43 | const byte STOPWATCH_STOPPED = 2; 44 | 45 | 46 | // 47 | // local vars 48 | // 49 | static byte displayDigitsFlg = 1; 50 | static byte displayColorsFlg = 1; 51 | static byte stopwatchMode; 52 | static unsigned long stopwatchStartTime; 53 | static unsigned long stopwatchCurrentTime; 54 | 55 | 56 | // 57 | // build a button for the Stopwatch display 58 | // 59 | BUTTON stopwatchButton; 60 | 61 | 62 | // --------------------------------------------------------------------------------- 63 | // Setup the hardware 64 | // --------------------------------------------------------------------------------- 65 | 66 | void setup() 67 | { 68 | // 69 | // pin numbers used in addition to the default SPI pins 70 | // 71 | const int LCD_CS_PIN = 10; 72 | const int LCD_DC_PIN = 9; 73 | const int TOUCH_CS_PIN = 8; 74 | 75 | // 76 | // setup the LCD orientation, the default font and initialize the user interface 77 | // 78 | ui.begin(LCD_CS_PIN, LCD_DC_PIN, TOUCH_CS_PIN, LCD_ORIENTATION_LANDSCAPE_4PIN_RIGHT, Arial_9_Bold); 79 | } 80 | 81 | 82 | // --------------------------------------------------------------------------------- 83 | // Define the menus and top level loop, place menus after setup() 84 | // --------------------------------------------------------------------------------- 85 | 86 | // 87 | // Notes on building the Main-menu: 88 | // 89 | // In this app, the first screen the user sees is not a menu, but the stopwatch. 90 | // The user calls up the menu by pressing the "Menu" button on the Title Bar. 91 | // From the menu, they can return to the stopwatch by pressing the menu's "Back" 92 | // button. 93 | // 94 | // Normally the Main-menu does not show a "Back" button. To enable the "Back" 95 | // button, the fourth field in the MENU_ITEM_TYPE_MAIN_MENU_HEADER line is set 96 | // to NULL. 97 | // 98 | 99 | 100 | // 101 | // for each menu, create a forward declaration with "extern" 102 | // 103 | extern MENU_ITEM mainMenu[]; 104 | 105 | 106 | // 107 | // main menu, NOTE HOW 4TH COLUMN IN FIRST LINE IS SET TO "NULL", THIS DISPLAYS THE "MENU" BUTTON 108 | // 109 | MENU_ITEM mainMenu[] = { 110 | {MENU_ITEM_TYPE_MAIN_MENU_HEADER, "Stopwatch Settings", MENU_COLUMNS_1, NULL}, 111 | {MENU_ITEM_TYPE_COMMAND, "Set number of digits", commandSetDigits, NULL}, 112 | {MENU_ITEM_TYPE_COMMAND, "Set screen colors", commandSetScreenColors, NULL}, 113 | {MENU_ITEM_TYPE_COMMAND, "About", commandAbout, NULL}, 114 | {MENU_ITEM_TYPE_END_OF_MENU, "", NULL, NULL} 115 | }; 116 | 117 | 118 | 119 | // --------------------------------------------------------------------------------- 120 | // Stopwatch application 121 | // --------------------------------------------------------------------------------- 122 | 123 | // 124 | // example of an application that's displayed first upon power up, then 125 | // menus can be called from it 126 | // 127 | void loop() 128 | { 129 | stopWatch(); 130 | } 131 | 132 | 133 | 134 | // 135 | // stopwatch application 136 | // 137 | void stopWatch(void) 138 | { 139 | // 140 | // build the Stopwatch button 141 | // 142 | stopwatchButton = {"Start", ui.displaySpaceCenterX, ui.displaySpaceBottomY-40, 150 , 50}; 143 | 144 | // 145 | // fully redraw the stopwatch, including the title bar 146 | // 147 | drawStopwatch(true); 148 | 149 | // 150 | // check for touch events from the user, and update the stopwatch display if it's running 151 | // 152 | while(true) 153 | { 154 | // 155 | // get any new touch events on the LCD 156 | // 157 | ui.getTouchEvents(); 158 | 159 | 160 | // 161 | // check if the Menu button was press 162 | // 163 | if (ui.checkForMenuButtonClicked()) 164 | { 165 | // 166 | // the user pressed the Menu button, display the menu 167 | // 168 | ui.displayAndExecuteMenu(mainMenu); 169 | drawStopwatch(true); 170 | } 171 | 172 | 173 | // 174 | // check if the Stopwatch button was pressed 175 | // 176 | if (ui.checkForButtonClicked(stopwatchButton)) 177 | { 178 | // 179 | // Stopwatch button was pressed, select the next stopwatch mode 180 | // 181 | switch(stopwatchMode) 182 | { 183 | case STOPWATCH_READY: 184 | { 185 | // 186 | // start the stopwatch 187 | // 188 | stopwatchStartTime = millis(); 189 | stopwatchCurrentTime = millis(); 190 | stopwatchMode = STOPWATCH_RUNNING; 191 | drawStopwatch(false); 192 | stopwatchButton.labelText = "Stop"; // change the label on the button to "Stop" 193 | ui.drawButton(stopwatchButton); 194 | break; 195 | } 196 | 197 | case STOPWATCH_RUNNING: 198 | { 199 | // 200 | // stop the stopwatch 201 | // 202 | stopwatchCurrentTime = millis(); 203 | stopwatchMode = STOPWATCH_STOPPED; 204 | drawStopwatch(false); 205 | stopwatchButton.labelText = "Reset"; // change the label on the button to "Reset" 206 | ui.drawButton(stopwatchButton); 207 | break; 208 | } 209 | 210 | case STOPWATCH_STOPPED: 211 | { 212 | // 213 | // reset the stopwatch 214 | // 215 | stopwatchStartTime = 0; 216 | stopwatchCurrentTime = 0; 217 | stopwatchMode = STOPWATCH_READY; 218 | drawStopwatch(false); 219 | stopwatchButton.labelText = "Start"; // change the label on the button to "Start" 220 | ui.drawButton(stopwatchButton); 221 | break; 222 | } 223 | } 224 | } 225 | 226 | 227 | // 228 | // update the time shown on the stopwatch 229 | // 230 | if (stopwatchMode == STOPWATCH_RUNNING) 231 | { 232 | unsigned long timeNow = millis(); 233 | if (timeNow > stopwatchCurrentTime + 47) 234 | { 235 | stopwatchCurrentTime = timeNow; 236 | drawStopwatch(false); 237 | } 238 | } 239 | } 240 | } 241 | 242 | 243 | 244 | // 245 | // draw the stopwatch display 246 | // Enter: redrawAllFlg = true if the display space should be cleared and everything redrawn 247 | // 248 | void drawStopwatch(boolean redrawAllFlg) 249 | { 250 | byte hours; 251 | byte minutes; 252 | byte seconds; 253 | byte hundredths; 254 | byte tenths; 255 | long milliSeconds; 256 | char sBuf[15]; 257 | char *sbufPntr; 258 | 259 | // 260 | // set the background color for the stopwatch 261 | // 262 | uint16_t backgroundColor = ui.lcdMakeColor(25, 50, 31); 263 | 264 | // 265 | // check if the stopwatch should be fully initialized and everything redrawn 266 | // 267 | if (redrawAllFlg) 268 | { 269 | // 270 | // draw the title bar, include the "Hamburger Menu" button on it 271 | // 272 | ui.drawTitleBarWithMenuButton("Stopwatch!"); 273 | 274 | // 275 | // draw everything on the stopwatch display, including the button 276 | // 277 | ui.clearDisplaySpace(backgroundColor); 278 | stopwatchButton.labelText = "Start"; 279 | ui.drawButton(stopwatchButton); 280 | 281 | // 282 | // initialize the stopwatch 283 | // 284 | stopwatchStartTime = 0; 285 | stopwatchCurrentTime = 0; 286 | stopwatchMode = STOPWATCH_READY; 287 | } 288 | 289 | 290 | // 291 | // compute the stopwatch time in Hours, Minutes, Seconds and Hundredths 292 | // 293 | milliSeconds = stopwatchCurrentTime - stopwatchStartTime; 294 | 295 | hours = milliSeconds / (60L * 60L * 1000L); 296 | milliSeconds -= (hours * (60L * 60L * 1000L)); 297 | 298 | minutes = milliSeconds / (60L * 1000L); 299 | milliSeconds -= (minutes * (60L * 1000L)); 300 | 301 | seconds = milliSeconds / (1000L); 302 | milliSeconds -= (seconds * 1000L); 303 | 304 | hundredths = milliSeconds / 10L; 305 | tenths = milliSeconds / 100L; 306 | 307 | sbufPntr = sBuf; 308 | 309 | 310 | // 311 | // build a single ASCII string formatted for the stopwatch's display 312 | // 313 | sbufPntr = timeDigits(sbufPntr, hours); 314 | *sbufPntr++ = ':'; 315 | 316 | sbufPntr = timeDigits(sbufPntr, minutes); 317 | *sbufPntr++ = ':'; 318 | 319 | sbufPntr = timeDigits(sbufPntr, seconds); 320 | *sbufPntr++ = ':'; 321 | 322 | if (displayDigitsFlg == 1) 323 | sbufPntr = timeDigits(sbufPntr, hundredths); 324 | else 325 | { 326 | *sbufPntr++ = tenths + '0'; 327 | *sbufPntr = 0; 328 | } 329 | 330 | 331 | // 332 | // pick a color to display the stopwatch digits 333 | // 334 | uint16_t textColor; 335 | if (displayColorsFlg == 0) 336 | textColor = ui.lcdMakeColor(5, 10, 5); 337 | else 338 | textColor = ui.lcdMakeColor(0, 0, 25); 339 | ui.lcdSetFontColor(textColor); 340 | 341 | 342 | // 343 | // blank the screen where the stopwatch's number are displayed 344 | // 345 | ui.lcdSetFont(Arial_20_Bold); 346 | int textWidth = ui.lcdStringWidthInPixels(sBuf); 347 | int textHeight = ui.lcdGetFontHeightWithoutDecenders(); 348 | int textY = ui.displaySpaceCenterY-40; 349 | int textX = ui.displaySpaceCenterX - textWidth/2; 350 | ui.lcdSetCursorXY(textX, textY); 351 | ui.lcdDrawFilledRectangle(textX, textY, textWidth, textHeight, backgroundColor); 352 | 353 | 354 | // 355 | // display the string showing the stopwatch's time 356 | // 357 | ui.lcdPrint(sBuf); 358 | } 359 | 360 | 361 | 362 | // 363 | // convert a number into a two digit string, pad with leading zeros 364 | // 365 | char* timeDigits(char *sBufPntr, byte n) 366 | { 367 | *sBufPntr++ = (n / 10) + '0'; 368 | *sBufPntr++ = (n % 10) + '0'; 369 | *sBufPntr = 0; 370 | return(sBufPntr); 371 | } 372 | 373 | 374 | // --------------------------------------------------------------------------------- 375 | // Commands executed from the menu 376 | // --------------------------------------------------------------------------------- 377 | 378 | // 379 | // menu command to "Set display colors" 380 | // 381 | void commandSetDigits(void) 382 | { 383 | // 384 | // draw the title bar and clear the display space 385 | // 386 | ui.drawTitleBar("Set Stopwatch Precision"); 387 | ui.clearDisplaySpace(); 388 | 389 | // 390 | // build and display a selection box for setting the number of digits 391 | // 392 | SELECTION_BOX digitsSelectionBox; 393 | digitsSelectionBox.labelText = "Configure seconds"; 394 | digitsSelectionBox.value = displayDigitsFlg; 395 | digitsSelectionBox.choice0Text = "Show tenths"; 396 | digitsSelectionBox.choice1Text = "Show hundredths"; 397 | digitsSelectionBox.choice2Text = ""; 398 | digitsSelectionBox.choice3Text = ""; 399 | digitsSelectionBox.centerX = ui.displaySpaceCenterX; 400 | digitsSelectionBox.centerY = 115; 401 | digitsSelectionBox.width = 280; 402 | digitsSelectionBox.height = 40; 403 | ui.drawSelectionBox(digitsSelectionBox); 404 | 405 | // 406 | // define and display an "OK" button 407 | // 408 | BUTTON okButton = {"OK", ui.displaySpaceCenterX, ui.displaySpaceBottomY-35, 120 , 40}; 409 | ui.drawButton(okButton); 410 | 411 | 412 | // 413 | // process touch events 414 | // 415 | while(true) 416 | { 417 | ui.getTouchEvents(); 418 | 419 | // 420 | // process touch events on the selection Box 421 | // 422 | ui.checkForSelectionBoxTouched(digitsSelectionBox); 423 | 424 | // 425 | // check for touch events on the "OK" button 426 | // 427 | if (ui.checkForButtonClicked(okButton)) 428 | { 429 | // 430 | // save the values set by the user to EEPROM, then return to the menu 431 | // 432 | displayDigitsFlg = digitsSelectionBox.value; 433 | return; 434 | } 435 | } 436 | } 437 | 438 | 439 | 440 | // 441 | // settings menu command to "set the screen colors" 442 | // 443 | void commandSetScreenColors(void) 444 | { 445 | ui.drawTitleBar("Set Screen Colors"); 446 | ui.clearDisplaySpace(); 447 | 448 | // 449 | // use a Selection Boxes to select screen colors 450 | // 451 | SELECTION_BOX colorSelectionBox; 452 | colorSelectionBox.labelText = "Screen color palette"; 453 | colorSelectionBox.value = displayColorsFlg; 454 | colorSelectionBox.choice0Text = "Gray"; 455 | colorSelectionBox.choice1Text = "Blue"; 456 | colorSelectionBox.choice2Text = ""; 457 | colorSelectionBox.choice3Text = ""; 458 | colorSelectionBox.centerX = ui.displaySpaceCenterX; 459 | colorSelectionBox.centerY = 115; 460 | colorSelectionBox.width = 280; 461 | colorSelectionBox.height = 40; 462 | ui.drawSelectionBox(colorSelectionBox); 463 | 464 | // 465 | // define and display an "OK" button 466 | // 467 | BUTTON okButton = {"OK", ui.displaySpaceCenterX, ui.displaySpaceBottomY-35, 120 , 40}; 468 | ui.drawButton(okButton); 469 | 470 | // 471 | // process touch events 472 | // 473 | while(true) 474 | { 475 | ui.getTouchEvents(); 476 | 477 | // 478 | // process touch events in the selection box 479 | // 480 | ui.checkForSelectionBoxTouched(colorSelectionBox); 481 | 482 | // 483 | // check for touch events on the "OK" button 484 | // 485 | if (ui.checkForButtonClicked(okButton)) 486 | { 487 | displayColorsFlg = colorSelectionBox.value; 488 | 489 | if (displayColorsFlg == 0) 490 | ui.setColorPaletteGray(); 491 | else 492 | ui.setColorPaletteBlue(); 493 | return; 494 | } 495 | } 496 | } 497 | 498 | 499 | 500 | // 501 | // menu command to "Display the About Box" 502 | // 503 | void commandAbout(void) 504 | { 505 | // 506 | // clear the screen and draw title bar showing with the "Back" button 507 | // 508 | ui.drawTitleBarWithBackButton("About This Program"); 509 | ui.clearDisplaySpace(); 510 | 511 | // 512 | // show some info in the display space 513 | // 514 | int y = 70; 515 | int ySpacing = 17; 516 | ui.lcdSetCursorXY(ui.displaySpaceCenterX, y); 517 | ui.lcdPrintCentered("Stopwatch!"); 518 | 519 | y += ySpacing * 2; 520 | ui.lcdSetCursorXY(ui.displaySpaceCenterX, y); 521 | ui.lcdPrintCentered("This is an example program for the Teensy"); 522 | 523 | y += ySpacing; 524 | ui.lcdSetCursorXY(ui.displaySpaceCenterX, y); 525 | ui.lcdPrintCentered("User Interface. It shows how to add a"); 526 | 527 | y += ySpacing; 528 | ui.lcdSetCursorXY(ui.displaySpaceCenterX, y); 529 | ui.lcdPrintCentered("hamburger menu to an application."); 530 | 531 | y += ySpacing * 2; 532 | ui.lcdSetCursorXY(ui.displaySpaceCenterX, y); 533 | ui.lcdPrintCentered("Copyright (c) 2019, S. Reifel & Company."); 534 | 535 | 536 | // 537 | // wait for the user to press the "Back" button, then return to the main menu 538 | // 539 | while(true) 540 | { 541 | ui.getTouchEvents(); 542 | 543 | if (ui.checkForBackButtonClicked()) 544 | return; 545 | } 546 | } 547 | -------------------------------------------------------------------------------- /examples/Example5_ScreensWithInformation/Example5_ScreensWithInformation.ino: -------------------------------------------------------------------------------- 1 | 2 | // ****************************************************************** 3 | // * * 4 | // * Example shows build screens that display information * 5 | // * * 6 | // * S. Reifel & Co. 2/18/2022 * 7 | // * * 8 | // ****************************************************************** 9 | 10 | // 11 | // DOCUMENTATION: 12 | // 13 | // Documentation for the "Teensy User Interface" library can be found at: 14 | // https://github.com/Stan-Reifel/TeensyUserInterface 15 | 16 | 17 | 18 | // *********************************************************************** 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | 25 | // 26 | // create the user interface object 27 | // 28 | TeensyUserInterface ui; 29 | 30 | 31 | 32 | // --------------------------------------------------------------------------------- 33 | // Setup the hardware 34 | // --------------------------------------------------------------------------------- 35 | 36 | void setup() 37 | { 38 | // 39 | // pin numbers used in addition to the default SPI pins 40 | // 41 | const int LCD_CS_PIN = 10; 42 | const int LCD_DC_PIN = 9; 43 | const int TOUCH_CS_PIN = 8; 44 | 45 | // 46 | // setup the LCD orientation, the default font and initialize the user interface 47 | // 48 | ui.begin(LCD_CS_PIN, LCD_DC_PIN, TOUCH_CS_PIN, LCD_ORIENTATION_LANDSCAPE_4PIN_RIGHT, Arial_9_Bold); 49 | 50 | // 51 | // use a grayscale color palette 52 | // 53 | ui.setColorPaletteGray(); 54 | } 55 | 56 | 57 | // --------------------------------------------------------------------------------- 58 | // Define the menus and top level loop, place menus after setup() 59 | // --------------------------------------------------------------------------------- 60 | 61 | 62 | // 63 | // for each menu, create a forward declaration with "extern" 64 | // 65 | extern MENU_ITEM mainMenu[]; 66 | 67 | 68 | // 69 | // the main menu 70 | // 71 | MENU_ITEM mainMenu[] = { 72 | {MENU_ITEM_TYPE_MAIN_MENU_HEADER, "Example Five - Display Information", MENU_COLUMNS_2, mainMenu}, 73 | {MENU_ITEM_TYPE_COMMAND, "Back button", commandBackButtonExample, NULL}, 74 | {MENU_ITEM_TYPE_COMMAND, "OK button", commandOKButtonExample, NULL}, 75 | {MENU_ITEM_TYPE_COMMAND, "OK/Cancel buttons", commandOkCancelExample, NULL}, 76 | {MENU_ITEM_TYPE_COMMAND, "Printing in columns", commandPrintingColumns, NULL}, 77 | {MENU_ITEM_TYPE_COMMAND, "Fonts and colors", commandFontsAndColors, NULL}, 78 | {MENU_ITEM_TYPE_COMMAND, "Graphics", commandGraphics, NULL}, 79 | {MENU_ITEM_TYPE_COMMAND, "Live updates", commandLiveUpdates, NULL}, 80 | {MENU_ITEM_TYPE_COMMAND, "Hold to say hello", commandHoldToSayHello, NULL}, 81 | {MENU_ITEM_TYPE_END_OF_MENU, "", NULL, NULL} 82 | }; 83 | 84 | 85 | 86 | // 87 | // display the menu, then execute commands selected by the user 88 | // 89 | void loop() 90 | { 91 | ui.displayAndExecuteMenu(mainMenu); 92 | } 93 | 94 | 95 | 96 | // --------------------------------------------------------------------------------- 97 | // Commands executed from the menu 98 | // --------------------------------------------------------------------------------- 99 | 100 | // 101 | // menu command that demonstrates the use of the "Back" button 102 | // 103 | void commandBackButtonExample(void) 104 | { 105 | // 106 | // clear the screen and draw title bar showing with the "Back" button 107 | // 108 | ui.drawTitleBarWithBackButton("Back Button Example"); 109 | ui.clearDisplaySpace(); 110 | 111 | // 112 | // show some info in the display space 113 | // 114 | ui.lcdSetCursorXY(ui.displaySpaceCenterX, ui.displaySpaceCenterY - 10); 115 | ui.lcdPrintCentered("We exit this screen with the Back button."); 116 | 117 | 118 | // 119 | // wait for the user to press the "Back" button, then return to the main menu 120 | // 121 | while(true) 122 | { 123 | ui.getTouchEvents(); 124 | 125 | if (ui.checkForBackButtonClicked()) 126 | return; 127 | } 128 | } 129 | 130 | 131 | 132 | // 133 | // menu command that demonstrates the use of an "OK" button 134 | // 135 | void commandOKButtonExample(void) 136 | { 137 | // 138 | // clear the screen and draw title bar showing without the "Back" button 139 | // 140 | ui.drawTitleBar("OK Button Example"); 141 | ui.clearDisplaySpace(); 142 | 143 | // 144 | // show some info in the display space 145 | // 146 | ui.lcdSetCursorXY(ui.displaySpaceCenterX, ui.displaySpaceCenterY - 20); 147 | ui.lcdPrintCentered("We exit this screen with an OK button."); 148 | 149 | // 150 | // define and display an "OK" button 151 | // 152 | int buttonX = ui.displaySpaceCenterX; 153 | int buttonY = ui.displaySpaceBottomY-30; 154 | int buttonWidth = 120; 155 | int buttonHeight = 35; 156 | BUTTON okButton = {"OK", buttonX, buttonY, buttonWidth, buttonHeight}; 157 | ui.drawButton(okButton); 158 | 159 | 160 | // 161 | // wait for the user to press the "OK" button, then return to the main menu 162 | // 163 | while(true) 164 | { 165 | // 166 | // get touch events on the LCD 167 | // 168 | ui.getTouchEvents(); 169 | 170 | // 171 | // check for a touch event on the OK button, if so return to the main menu 172 | // 173 | if (ui.checkForButtonClicked(okButton)) 174 | return; 175 | } 176 | } 177 | 178 | 179 | 180 | // 181 | // menu command that demonstrates the use of "OK" & "Cancel" buttons 182 | // 183 | void commandOkCancelExample(void) 184 | { 185 | // 186 | // clear the screen and draw title bar showing without the "Back" button 187 | // 188 | ui.drawTitleBar("OK / Cancel Button Example"); 189 | ui.clearDisplaySpace(); 190 | 191 | // 192 | // show some info in the display space 193 | // 194 | ui.lcdSetCursorXY(ui.displaySpaceCenterX, ui.displaySpaceCenterY - 20); 195 | ui.lcdPrintCentered("The user can exit with OK or Cancel."); 196 | 197 | // 198 | // define and display "OK" and "Cancel" buttons 199 | // 200 | BUTTON okButton = {"OK", ui.displaySpaceCenterX-70, ui.displaySpaceBottomY-30, 120 , 35}; 201 | ui.drawButton(okButton); 202 | 203 | BUTTON cancelButton = {"Cancel", ui.displaySpaceCenterX+70, ui.displaySpaceBottomY-30, 120 , 35}; 204 | ui.drawButton(cancelButton); 205 | 206 | 207 | // 208 | // wait for the user to press the "OK" button, then return to the main menu 209 | // 210 | while(true) 211 | { 212 | // 213 | // get touch events on the LCD 214 | // 215 | ui.getTouchEvents(); 216 | 217 | 218 | // 219 | // check for a touch event on the OK button, if so do stuff then return to the main menu 220 | // 221 | if (ui.checkForButtonClicked(okButton)) 222 | { 223 | // 224 | // do some stuff, or save some stuff because the user pressed OK 225 | // 226 | ui.clearDisplaySpace(); 227 | ui.lcdSetCursorXY(ui.displaySpaceCenterX, ui.displaySpaceCenterY - 20); 228 | ui.lcdPrintCentered("Saving..."); 229 | delay(1500); 230 | 231 | // 232 | // return to the main menu 233 | // 234 | return; 235 | } 236 | 237 | 238 | // 239 | // check for a touch event on the Cancel button, if so return to the main menu 240 | // 241 | if (ui.checkForButtonClicked(cancelButton)) 242 | return; 243 | } 244 | } 245 | 246 | 247 | 248 | // 249 | // menu command that demonstrates printing in columns 250 | // 251 | void commandPrintingColumns(void) 252 | { 253 | // 254 | // clear the screen and draw title bar showing with the "Back" button 255 | // 256 | ui.drawTitleBarWithBackButton("Show Info in Columns"); 257 | ui.clearDisplaySpace(); 258 | 259 | 260 | // 261 | // display some data in columns 262 | // 263 | int titleX = 96; 264 | int valueX = 181; 265 | int y = 65; 266 | ui.lcdSetCursorXY(titleX, y); ui.lcdPrint("X Offset"); 267 | ui.lcdSetCursorXY(valueX, y); ui.lcdPrint(17); 268 | 269 | y += 30; 270 | ui.lcdSetCursorXY(titleX, y); ui.lcdPrint("X Scaler"); 271 | ui.lcdSetCursorXY(valueX, y); ui.lcdPrint(123.456); 272 | 273 | y += 30; 274 | ui.lcdSetCursorXY(titleX, y); ui.lcdPrint("Y Offset"); 275 | ui.lcdSetCursorXY(valueX, y); ui.lcdPrint(-350); 276 | 277 | y += 30; 278 | ui.lcdSetCursorXY(titleX, y); ui.lcdPrint("Y Scaler"); 279 | ui.lcdSetCursorXY(valueX, y); ui.lcdPrint(3.14159); 280 | 281 | 282 | // 283 | // print using left, right and center justifying 284 | // 285 | y = ui.displaySpaceBottomY - 30; 286 | ui.lcdSetCursorXY(ui.displaySpaceLeftX + 10, y); ui.lcdPrint("Left"); 287 | ui.lcdSetCursorXY(ui.displaySpaceRightX - 10, y); ui.lcdPrintRightJustified("Right"); 288 | ui.lcdSetCursorXY(ui.displaySpaceCenterX, y); ui.lcdPrintCentered("Center"); 289 | 290 | 291 | // 292 | // wait for the user to press the "Back" button, then return to the main menu 293 | // 294 | while(true) 295 | { 296 | ui.getTouchEvents(); 297 | 298 | if (ui.checkForBackButtonClicked()) 299 | return; 300 | } 301 | } 302 | 303 | 304 | 305 | // 306 | // menu command that demonstrates the use of fonts and color 307 | // 308 | void commandFontsAndColors(void) 309 | { 310 | // 311 | // draw title bar showing with the "Back" button 312 | // 313 | ui.drawTitleBarWithBackButton("Show Colors and Fonts"); 314 | 315 | // 316 | // clear the display space with a color instead of the default black 317 | // 318 | ui.clearDisplaySpace(LCD_NAVY); 319 | 320 | 321 | // 322 | // display titles in white bold 323 | // 324 | int titleX = 90; 325 | int y = 60; 326 | ui.lcdSetFont(Arial_11_Bold); 327 | ui.lcdSetFontColor(LCD_WHITE); 328 | ui.lcdSetCursorXY(titleX, y); ui.lcdPrint("Sensor 1:"); 329 | 330 | y += 30; 331 | ui.lcdSetCursorXY(titleX, y); ui.lcdPrint("Sensor 2:"); 332 | 333 | y += 30; 334 | ui.lcdSetCursorXY(titleX, y); ui.lcdPrint("Sensor 3:"); 335 | 336 | y += 30; 337 | ui.lcdSetCursorXY(titleX, y); ui.lcdPrint("Sensor 4:"); 338 | 339 | 340 | // 341 | // values without bold, colored green for positive, red for negative 342 | // 343 | int valueX = 175; 344 | y = 60; 345 | ui.lcdSetFont(Arial_12); 346 | ui.lcdSetFontColor(LCD_GREEN); 347 | ui.lcdSetCursorXY(valueX, y); ui.lcdPrint(17); 348 | 349 | y += 30; 350 | ui.lcdSetFontColor(LCD_GREEN); 351 | ui.lcdSetCursorXY(valueX, y); ui.lcdPrint(123.456); 352 | 353 | y += 30; 354 | ui.lcdSetFontColor(LCD_RED); 355 | ui.lcdSetCursorXY(valueX, y); ui.lcdPrint(-350); 356 | 357 | y += 30; 358 | ui.lcdSetFontColor(LCD_GREEN); 359 | ui.lcdSetCursorXY(valueX, y); ui.lcdPrint(3.14159); 360 | 361 | 362 | // 363 | // draw a color bar like thing 364 | // 365 | int red = 0; 366 | int green = 0; 367 | int blue = 0; 368 | int i = 0; 369 | 370 | for (red = 0; red <= 31; red++) 371 | { 372 | ui.lcdDrawVerticalLine(65 + i, 190, 30, ui.lcdMakeColor(red, green*2, blue)); 373 | i++; 374 | ui.lcdDrawVerticalLine(65 + i, 190, 30, ui.lcdMakeColor(red, green*2, blue)); 375 | i++; 376 | } 377 | 378 | for (green = 0; green <= 31; green++) 379 | { 380 | red--; 381 | ui.lcdDrawVerticalLine(65 + i, 190, 30, ui.lcdMakeColor(red, green*2, blue)); 382 | i++; 383 | ui.lcdDrawVerticalLine(65 + i, 190, 30, ui.lcdMakeColor(red, green*2, blue)); 384 | i++; 385 | } 386 | red = 0; 387 | 388 | for (blue = 0; blue <= 31; blue++) 389 | { 390 | green--; 391 | ui.lcdDrawVerticalLine(65 + i, 190, 30, ui.lcdMakeColor(red, green*2, blue)); 392 | i++; 393 | ui.lcdDrawVerticalLine(65 + i, 190, 30, ui.lcdMakeColor(red, green*2, blue)); 394 | i++; 395 | } 396 | 397 | 398 | // 399 | // wait for the user to press the "Back" button, then return to the main menu 400 | // 401 | while(true) 402 | { 403 | ui.getTouchEvents(); 404 | 405 | if (ui.checkForBackButtonClicked()) 406 | return; 407 | } 408 | } 409 | 410 | 411 | 412 | // 413 | // menu command that demonstrates drawing with graphics 414 | // 415 | void commandGraphics(void) 416 | { 417 | // 418 | // draw title bar showing with the "Back" button 419 | // 420 | ui.drawTitleBarWithBackButton("Draw a graph"); 421 | ui.clearDisplaySpace(); 422 | 423 | // 424 | // draw the axes 425 | // 426 | ui.lcdDrawHorizontalLine(10, 230, 300, LCD_WHITE); 427 | ui.lcdDrawVerticalLine(10, 50, 230-50, LCD_WHITE); 428 | for (int x = 10+30; x <= 300; x+=30) 429 | ui.lcdDrawVerticalLine(x, 230-4, 8, LCD_WHITE); 430 | for (int y = 230-30; y > 50; y-=30) 431 | ui.lcdDrawHorizontalLine(6, y, 8, LCD_WHITE); 432 | 433 | 434 | // 435 | // draw the graph with endpoints 436 | // 437 | int dataA[] = {30, 160, 60, 210, 90, 190, 120, 150, 150, 150, 180, 220, 210, 190, 240, 220, 270, 100, 300, 90}; 438 | int arrayLength = sizeof(dataA) / sizeof(dataA[0]); 439 | int i = 2; 440 | while(i < arrayLength) 441 | { 442 | ui.lcdDrawLine(dataA[i-2], dataA[i-1], dataA[i], dataA[i+1], LCD_YELLOW); 443 | ui.lcdDrawFilledCircle(dataA[i], dataA[i+1], 3, LCD_LIGHTBLUE); 444 | i += 2; 445 | } 446 | 447 | 448 | // 449 | // wait for the user to press the "Back" button, then return to the main menu 450 | // 451 | while(true) 452 | { 453 | ui.getTouchEvents(); 454 | 455 | if (ui.checkForBackButtonClicked()) 456 | return; 457 | } 458 | } 459 | 460 | 461 | 462 | // 463 | // menu command that demonstrates display live numeric values 464 | // 465 | void commandLiveUpdates(void) 466 | { 467 | // 468 | // clear the screen and draw title bar showing with the "Back" button 469 | // 470 | ui.drawTitleBar("Showing Live Data"); 471 | ui.clearDisplaySpace(); 472 | 473 | 474 | // 475 | // define and display an "OK" button 476 | // 477 | BUTTON okButton = {"OK", ui.displaySpaceCenterX, ui.displaySpaceBottomY-30, 120, 35}; 478 | ui.drawButton(okButton); 479 | 480 | 481 | // 482 | // set the location and update rate for the sensor text 483 | // 484 | unsigned long refreshTimer = millis(); 485 | const unsigned long updateRate = 100; 486 | int sensorLabelTextX = 78; 487 | int sensorValueTextX = sensorLabelTextX + 80; 488 | int sensorValueTextY = 100; 489 | ui.lcdSetFont(Arial_13); 490 | int sensorValueTextWidth = ui.lcdStringWidthInPixels("1234.56789"); 491 | int sensorValueTextHeight = ui.lcdGetFontHeightWithoutDecenders(); 492 | 493 | // 494 | // draw a label for the sensor value 495 | // 496 | ui.lcdSetCursorXY(sensorLabelTextX, sensorValueTextY); ui.lcdPrint("Azimuth:"); 497 | 498 | // 499 | // continuously update the display until the user presses "OK" 500 | // 501 | while(true) 502 | { 503 | // 504 | // update the display every 0.1 seconds 505 | // 506 | if (millis() - refreshTimer >= updateRate) 507 | { 508 | // 509 | // read the sensor value 510 | // 511 | float sensorValue = (float) analogRead(0) / 2.5; 512 | 513 | // 514 | // blank the display where the sensor value is shown 515 | // 516 | ui.lcdDrawFilledRectangle(sensorValueTextX, sensorValueTextY, sensorValueTextWidth, sensorValueTextHeight, LCD_BLACK); 517 | 518 | // 519 | // print the sensor value 520 | // 521 | ui.lcdSetFont(Arial_13); 522 | ui.lcdSetCursorXY(sensorValueTextX, sensorValueTextY); 523 | ui.lcdPrint(sensorValue); 524 | 525 | // 526 | // reset the update timer 527 | // 528 | refreshTimer = millis(); 529 | } 530 | 531 | 532 | // 533 | // get touch events, return to the main menu when the user presses OK 534 | // 535 | ui.getTouchEvents(); 536 | if (ui.checkForButtonClicked(okButton)) 537 | return; 538 | } 539 | } 540 | 541 | 542 | 543 | // 544 | // menu command that demonstrates doing something while pressing a button 545 | // 546 | void commandHoldToSayHello(void) 547 | { 548 | // 549 | // clear the screen and draw title bar showing with the "Back" button 550 | // 551 | ui.drawTitleBarWithBackButton("Touch The Button To Say Hello"); 552 | ui.clearDisplaySpace(); 553 | 554 | int helloX = 220; 555 | int helloY = ui.displaySpaceCenterY - 6; 556 | 557 | // 558 | // create and draw a button 559 | // 560 | BUTTON sayHelloButton = {"Hold to say hello", 90, ui.displaySpaceCenterY, 140, 120}; 561 | ui.drawButton(sayHelloButton); 562 | 563 | 564 | // 565 | // continuously check if any buttons are pressed by the user 566 | // 567 | while(true) 568 | { 569 | // 570 | // get events from the touch screen 571 | // 572 | ui.getTouchEvents(); 573 | 574 | // 575 | // check if the user pressed and released the Back button, if so return to the main menu 576 | // 577 | if (ui.checkForBackButtonClicked()) 578 | return; 579 | 580 | // 581 | // check if the user is currently pressing the Hello button 582 | // 583 | if (ui.checkForButtonFirstTouched(sayHelloButton)) 584 | { 585 | // 586 | // they are holding down the button, print "Hello" 587 | // 588 | ui.lcdSetCursorXY(helloX, helloY); 589 | ui.lcdPrint("Hello"); 590 | } 591 | 592 | // 593 | // check if the user has released the Hello button 594 | // 595 | if (ui.checkForButtonClicked(sayHelloButton)) 596 | { 597 | // 598 | // blank the area of the screen where "Hello" was printed 599 | // 600 | int widthOfMessage = ui.lcdStringWidthInPixels("Hello"); 601 | int heightOfMessage = ui.lcdGetFontHeightWithDecentersAndLineSpacing(); 602 | ui.lcdDrawFilledRectangle(helloX, helloY, widthOfMessage, heightOfMessage, LCD_BLACK); 603 | } 604 | } 605 | } 606 | -------------------------------------------------------------------------------- /examples/Example6_GetNumbersAndValues/Example6_GetNumbersAndValues.ino: -------------------------------------------------------------------------------- 1 | 2 | // ****************************************************************** 3 | // * * 4 | // * Example shows how to prompt the user for numbers and values * 5 | // * * 6 | // * S. Reifel & Co. 2/18/2022 * 7 | // * * 8 | // ****************************************************************** 9 | 10 | // 11 | // DOCUMENTATION: 12 | // 13 | // Documentation for the "Teensy User Interface" library can be found at: 14 | // https://github.com/Stan-Reifel/TeensyUserInterface 15 | 16 | 17 | 18 | // *********************************************************************** 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | 25 | // 26 | // create the user interface object 27 | // 28 | TeensyUserInterface ui; 29 | 30 | 31 | 32 | // --------------------------------------------------------------------------------- 33 | // Setup the hardware 34 | // --------------------------------------------------------------------------------- 35 | 36 | void setup() 37 | { 38 | // 39 | // pin numbers used in addition to the default SPI pins 40 | // 41 | const int LCD_CS_PIN = 10; 42 | const int LCD_DC_PIN = 9; 43 | const int TOUCH_CS_PIN = 8; 44 | 45 | // 46 | // setup the LCD orientation, the default font and initialize the user interface 47 | // 48 | ui.begin(LCD_CS_PIN, LCD_DC_PIN, TOUCH_CS_PIN, LCD_ORIENTATION_LANDSCAPE_4PIN_RIGHT, Arial_9_Bold); 49 | 50 | // 51 | // use a grayscale color palette 52 | // 53 | ui.setColorPaletteGray(); 54 | } 55 | 56 | 57 | // --------------------------------------------------------------------------------- 58 | // Define the menus and top level loop, place menus after setup() 59 | // --------------------------------------------------------------------------------- 60 | 61 | // 62 | // Notes on building the menu table: 63 | // 64 | // This example uses many "Command" button, exactly like we have used in the 65 | // earlier examples. Here we introduce a new menu item, the "Toggle" button. 66 | // 67 | // A MENU_ITEM_TYPE_TOGGLE is used somewhat like a Radio Button in a dialog 68 | // box. It allows the user to select one of a fixed number of choices (such as 69 | // On / Off, or Red / Green / Blue). Each time the user presses this button, 70 | // it alternates the selection (i.e. toggles between On and Off, or rotates between 71 | // Red, Green and Blue). The third field in this entry points to a callback 72 | // function that alternates the value. 73 | // 74 | // The "Self Destruct" button in this example uses the Toggle feature. 75 | 76 | 77 | // 78 | // for each menu, create a forward declaration with "extern" 79 | // 80 | extern MENU_ITEM mainMenu[]; 81 | 82 | 83 | // 84 | // the main menu 85 | // 86 | MENU_ITEM mainMenu[] = { 87 | {MENU_ITEM_TYPE_MAIN_MENU_HEADER, "Example Six - Prompt User for Infomation", MENU_COLUMNS_2, mainMenu}, 88 | {MENU_ITEM_TYPE_COMMAND, "Get an integer", commandGetAnInteger, NULL}, 89 | {MENU_ITEM_TYPE_COMMAND, "Get a float", commandGetAFloat, NULL}, 90 | {MENU_ITEM_TYPE_COMMAND, "Make a choice", commandMakeAChoice, NULL}, 91 | {MENU_ITEM_TYPE_COMMAND, "Save settings", commandGetValuesAndSave, NULL}, 92 | {MENU_ITEM_TYPE_TOGGLE, "Self destruct", enableSelfDestructCallback, NULL}, 93 | {MENU_ITEM_TYPE_END_OF_MENU, "", NULL, NULL} 94 | }; 95 | 96 | 97 | 98 | // 99 | // display the menu, then execute commands selected by the user 100 | // 101 | void loop() 102 | { 103 | ui.displayAndExecuteMenu(mainMenu); 104 | } 105 | 106 | 107 | 108 | // --------------------------------------------------------------------------------- 109 | // Commands executed from the menu 110 | // --------------------------------------------------------------------------------- 111 | 112 | // 113 | // menu command that demonstrates how to prompt the user for an integer 114 | // 115 | static int xOffsetValue = 50; 116 | 117 | void commandGetAnInteger(void) 118 | { 119 | char sBuffer[25]; 120 | 121 | // 122 | // draw the title bar and clear the screen 123 | // 124 | ui.drawTitleBar("Prompt User for an Integer"); 125 | ui.clearDisplaySpace(); 126 | 127 | // 128 | // set the size and initial value of the number box 129 | // 130 | const int numberBoxWidth = 200; 131 | const int numberBoxAndButtonsHeight = 35; 132 | 133 | // 134 | // define a Number Box so the user can select a numeric value, specify the initial value, 135 | // max and min values, and step up/down amount 136 | // 137 | NUMBER_BOX my_NumberBox; 138 | my_NumberBox.labelText = "Set X offset"; 139 | my_NumberBox.value = xOffsetValue; 140 | my_NumberBox.minimumValue = -200; 141 | my_NumberBox.maximumValue = 200; 142 | my_NumberBox.stepAmount = 2; 143 | my_NumberBox.centerX = ui.displaySpaceCenterX; 144 | my_NumberBox.centerY = ui.displaySpaceCenterY - 20; 145 | my_NumberBox.width = numberBoxWidth; 146 | my_NumberBox.height = numberBoxAndButtonsHeight; 147 | ui.drawNumberBox(my_NumberBox); 148 | 149 | 150 | // 151 | // define and display "OK" and "Cancel" buttons 152 | // 153 | BUTTON okButton = {"OK", ui.displaySpaceCenterX-70, ui.displaySpaceBottomY-35, 120 , numberBoxAndButtonsHeight}; 154 | ui.drawButton(okButton); 155 | 156 | BUTTON cancelButton = {"Cancel", ui.displaySpaceCenterX+70, ui.displaySpaceBottomY-35, 120 , numberBoxAndButtonsHeight}; 157 | ui.drawButton(cancelButton); 158 | 159 | 160 | // 161 | // process touch events 162 | // 163 | while(true) 164 | { 165 | ui.getTouchEvents(); 166 | 167 | // 168 | // process touch events on the Number Box 169 | // 170 | ui.checkForNumberBoxTouched(my_NumberBox); 171 | 172 | // 173 | // check for touch events on the "OK" button 174 | // 175 | if (ui.checkForButtonClicked(okButton)) 176 | { 177 | // 178 | // user OK pressed, get the value from the Number Box and display it 179 | // 180 | xOffsetValue = my_NumberBox.value; 181 | sprintf(sBuffer, "X Offset = %d", xOffsetValue); 182 | 183 | ui.clearDisplaySpace(); 184 | ui.lcdSetCursorXY(ui.displaySpaceCenterX, ui.displaySpaceCenterY-10); 185 | ui.lcdPrintCentered(sBuffer); 186 | delay(1500); 187 | return; 188 | } 189 | 190 | // 191 | // check for touch events on the "Cancel" button 192 | // 193 | if (ui.checkForButtonClicked(cancelButton)) 194 | return; 195 | } 196 | } 197 | 198 | 199 | 200 | // 201 | // menu command that demonstrates how to prompt the user for an float 202 | // 203 | static float xScalerValue = 0.57; 204 | 205 | void commandGetAFloat(void) 206 | { 207 | char sBuffer[30]; 208 | 209 | // 210 | // draw the title bar and clear the screen 211 | // 212 | ui.drawTitleBar("Prompt User for an float"); 213 | ui.clearDisplaySpace(); 214 | 215 | // 216 | // set the size and initial value of the number box 217 | // 218 | const int numberBoxWidth = 200; 219 | const int numberBoxAndButtonsHeight = 35; 220 | 221 | // 222 | // define a Number Box for Floats so the user can select a numeric value, specify 223 | // the initial value, max and min values, and step up/down amount 224 | // 225 | NUMBER_BOX_FLOAT my_NumberBox; 226 | my_NumberBox.labelText = "Set X scaler"; 227 | my_NumberBox.value = xScalerValue; 228 | my_NumberBox.minimumValue = 0.0; 229 | my_NumberBox.maximumValue = 1.0; 230 | my_NumberBox.stepAmount = 0.01; 231 | my_NumberBox.digitsRightOfDecimal = 2; 232 | my_NumberBox.centerX = ui.displaySpaceCenterX; 233 | my_NumberBox.centerY = ui.displaySpaceCenterY - 20;; 234 | my_NumberBox.width = numberBoxWidth; 235 | my_NumberBox.height = numberBoxAndButtonsHeight; 236 | ui.drawNumberBox(my_NumberBox); 237 | 238 | 239 | // 240 | // define and display "OK" and "Cancel" buttons 241 | // 242 | BUTTON okButton = {"OK", ui.displaySpaceCenterX-70, ui.displaySpaceBottomY-35, 120 , numberBoxAndButtonsHeight}; 243 | ui.drawButton(okButton); 244 | 245 | BUTTON cancelButton = {"Cancel", ui.displaySpaceCenterX+70, ui.displaySpaceBottomY-35, 120 , numberBoxAndButtonsHeight}; 246 | ui.drawButton(cancelButton); 247 | 248 | 249 | // 250 | // process touch events 251 | // 252 | while(true) 253 | { 254 | ui.getTouchEvents(); 255 | 256 | // 257 | // process touch events on the Number Box 258 | // 259 | ui.checkForNumberBoxTouched(my_NumberBox); 260 | 261 | // 262 | // check for touch events on the "OK" button 263 | // 264 | if (ui.checkForButtonClicked(okButton)) 265 | { 266 | // 267 | // user OK pressed, get the value from the Number Box and display it 268 | // 269 | xScalerValue = my_NumberBox.value; 270 | sprintf(sBuffer, "X Offset = %4.2f", xScalerValue); 271 | 272 | ui.clearDisplaySpace(); 273 | ui.lcdSetCursorXY(ui.displaySpaceCenterX, ui.displaySpaceCenterY-10); 274 | ui.lcdPrintCentered(sBuffer); 275 | delay(1500); 276 | return; 277 | } 278 | 279 | // 280 | // check for touch events on the "Cancel" button 281 | // 282 | if (ui.checkForButtonClicked(cancelButton)) 283 | return; 284 | } 285 | } 286 | 287 | 288 | 289 | // 290 | // menu command that demonstrates how to prompt the user to select one of n things 291 | // 292 | static int powerSelection = 2; 293 | static int colorSelection = 1; 294 | static int safetySelection = 0; 295 | 296 | void commandMakeAChoice(void) 297 | { 298 | ui.drawTitleBarWithBackButton("Using Selection Boxes"); 299 | ui.clearDisplaySpace(); 300 | 301 | // 302 | // define and display 3 selection boxes, one with 2 choice, one with 3 choice 303 | // and one with 4 304 | // 305 | SELECTION_BOX powerSelectionBox; 306 | powerSelectionBox.labelText = "Laser power level"; 307 | powerSelectionBox.value = powerSelection; 308 | powerSelectionBox.choice0Text = "Low"; 309 | powerSelectionBox.choice1Text = "Medium"; 310 | powerSelectionBox.choice2Text = "High"; 311 | powerSelectionBox.choice3Text = ""; 312 | powerSelectionBox.centerX = ui.displaySpaceCenterX; 313 | powerSelectionBox.centerY = 79; 314 | powerSelectionBox.width = 250; 315 | powerSelectionBox.height = 33; 316 | ui.drawSelectionBox(powerSelectionBox); 317 | 318 | SELECTION_BOX colorSelectionBox; 319 | colorSelectionBox.labelText = "Laser color"; 320 | colorSelectionBox.value = colorSelection; 321 | colorSelectionBox.choice0Text = "Red"; 322 | colorSelectionBox.choice1Text = "Green"; 323 | colorSelectionBox.choice2Text = "Blue"; 324 | colorSelectionBox.choice3Text = "White"; 325 | colorSelectionBox.centerX = ui.displaySpaceCenterX; 326 | colorSelectionBox.centerY = 146; 327 | colorSelectionBox.width = 250; 328 | colorSelectionBox.height = 33; 329 | ui.drawSelectionBox(colorSelectionBox); 330 | 331 | SELECTION_BOX safetySelectionBox; 332 | safetySelectionBox.labelText = "Laser safety switch"; 333 | safetySelectionBox.value = safetySelection; 334 | safetySelectionBox.choice0Text = "Off"; 335 | safetySelectionBox.choice1Text = "On"; 336 | safetySelectionBox.choice2Text = ""; 337 | safetySelectionBox.choice3Text = ""; 338 | safetySelectionBox.centerX = ui.displaySpaceCenterX; 339 | safetySelectionBox.centerY = 214; 340 | safetySelectionBox.width = 250; 341 | safetySelectionBox.height = 33; 342 | ui.drawSelectionBox(safetySelectionBox); 343 | 344 | // 345 | // process touch events 346 | // 347 | while(true) 348 | { 349 | ui.getTouchEvents(); 350 | 351 | // 352 | // process touch events in the selection boxes 353 | // 354 | ui.checkForSelectionBoxTouched(powerSelectionBox); 355 | ui.checkForSelectionBoxTouched(colorSelectionBox); 356 | ui.checkForSelectionBoxTouched(safetySelectionBox); 357 | 358 | // 359 | // when the "Back" button is pressed, save all the values selected 360 | // by the user 361 | // 362 | if (ui.checkForBackButtonClicked()) 363 | { 364 | powerSelection = powerSelectionBox.value; 365 | colorSelection = colorSelectionBox.value; 366 | safetySelection = safetySelectionBox.value; 367 | return; 368 | } 369 | } 370 | } 371 | 372 | 373 | 374 | // 375 | // toggle used to enable / disable "Self Destruct Mode" 376 | // 377 | // Sometimes it is easier to use a single button to configure something, rather than 378 | // having a separate screen with setting choices. This can be done using a "Toggle" 379 | // button. 380 | // 381 | static byte enableSelfDestruct = false; 382 | 383 | void enableSelfDestructCallback(void) 384 | { 385 | // 386 | // check if menu is requesting that the state be changed (can have more than 2 states) 387 | // 388 | if (ui.toggleSelectNextStateFlg) 389 | { 390 | // 391 | // select the next state 392 | // 393 | enableSelfDestruct = !enableSelfDestruct; 394 | } 395 | 396 | // 397 | // here is where you update stuff depending on the choice made by the user 398 | // This assumes that you have wired a LED to pin 31 399 | // 400 | pinMode(31, OUTPUT); 401 | 402 | if (enableSelfDestruct) 403 | digitalWrite(31, HIGH); 404 | else 405 | digitalWrite(31, LOW); 406 | 407 | // 408 | // send back an indication of the current state, this text is written on the button 409 | // 410 | if(enableSelfDestruct) 411 | ui.toggleText = "On"; 412 | else 413 | ui.toggleText = "Off"; 414 | } 415 | 416 | 417 | // 418 | // storage locations in EEPROM for configuration values settable below 419 | // 420 | const int EEPROM_X_OFFSET = 0; // int requires 5 bytes of EEPROM storage 421 | const int EEPROM_X_SCALER = EEPROM_X_OFFSET + 5; // float requires 5 bytes of EEPROM storage 422 | const int EEPROM_Y_OFFSET = EEPROM_X_SCALER + 5; // int requires 5 bytes of EEPROM storage 423 | const int EEPROM_Y_SCALER = EEPROM_Y_OFFSET + 5; // float requires 5 bytes of EEPROM storage 424 | 425 | 426 | // 427 | // defaults for configuration values, these values are used if they have never been set before 428 | // 429 | const int DEFAULT_X_OFFSET = 50; 430 | const float DEFAULT_X_SCALER = 0.57; 431 | const int DEFAULT_Y_OFFSET = -51; 432 | const float DEFAULT_Y_SCALER = 0.85; 433 | 434 | 435 | // 436 | // menu command that demonstrates how to prompt the user for info then save it 437 | // 438 | void commandGetValuesAndSave(void) 439 | { 440 | const int numberBoxWidth = 145; 441 | const int numberBoxHeight = 34; 442 | 443 | 444 | // 445 | // read initial values from the EEPROM, if the EEPROM has never been set, use default values 446 | // 447 | int initialXOffset = ui.readConfigurationInt(EEPROM_X_OFFSET, DEFAULT_X_OFFSET); 448 | float initialXScaler = ui.readConfigurationFloat(EEPROM_X_SCALER, DEFAULT_X_SCALER); 449 | int initialYOffset = ui.readConfigurationInt(EEPROM_Y_OFFSET, DEFAULT_Y_OFFSET); 450 | float initialYScaler = ui.readConfigurationFloat(EEPROM_Y_SCALER, DEFAULT_Y_SCALER); 451 | 452 | 453 | // 454 | // draw title bar without a "Back" button 455 | // 456 | ui.drawTitleBar("Calibrate X and Y Axes"); 457 | ui.clearDisplaySpace(); 458 | 459 | 460 | // 461 | // define and display number boxes for setting calibration constants 462 | // 463 | NUMBER_BOX XOffset_NumberBox; 464 | XOffset_NumberBox.labelText = "Set X offset"; 465 | XOffset_NumberBox.value = initialXOffset; 466 | XOffset_NumberBox.minimumValue = -200; 467 | XOffset_NumberBox.maximumValue = 200; 468 | XOffset_NumberBox.stepAmount = 2; 469 | XOffset_NumberBox.centerX = ui.displaySpaceCenterX - 80; 470 | XOffset_NumberBox.centerY = 83; 471 | XOffset_NumberBox.width = numberBoxWidth; 472 | XOffset_NumberBox.height = numberBoxHeight; 473 | ui.drawNumberBox(XOffset_NumberBox); 474 | 475 | NUMBER_BOX_FLOAT XScaler_NumberBox; 476 | XScaler_NumberBox.labelText = "Set X scaler"; 477 | XScaler_NumberBox.value = initialXScaler; 478 | XScaler_NumberBox.minimumValue = 0.0; 479 | XScaler_NumberBox.maximumValue = 1.0; 480 | XScaler_NumberBox.stepAmount = 0.01; 481 | XScaler_NumberBox.digitsRightOfDecimal = 2; 482 | XScaler_NumberBox.centerX = ui.displaySpaceCenterX - 80; 483 | XScaler_NumberBox.centerY = 157; 484 | XScaler_NumberBox.width = numberBoxWidth; 485 | XScaler_NumberBox.height = numberBoxHeight; 486 | ui.drawNumberBox(XScaler_NumberBox); 487 | 488 | 489 | NUMBER_BOX YOffset_NumberBox; 490 | YOffset_NumberBox.labelText = "Set Y offset"; 491 | YOffset_NumberBox.value = initialYOffset; 492 | YOffset_NumberBox.minimumValue = -200; 493 | YOffset_NumberBox.maximumValue = 200; 494 | YOffset_NumberBox.stepAmount = 2; 495 | YOffset_NumberBox.centerX = ui.displaySpaceCenterX + 80; 496 | YOffset_NumberBox.centerY = 83; 497 | YOffset_NumberBox.width = numberBoxWidth; 498 | YOffset_NumberBox.height = numberBoxHeight; 499 | ui.drawNumberBox(YOffset_NumberBox); 500 | 501 | NUMBER_BOX_FLOAT YScaler_NumberBox; 502 | YScaler_NumberBox.labelText = "Set Y scaler"; 503 | YScaler_NumberBox.value = initialYScaler; 504 | YScaler_NumberBox.minimumValue = 0.0; 505 | YScaler_NumberBox.maximumValue = 1.0; 506 | YScaler_NumberBox.stepAmount = 0.01; 507 | YScaler_NumberBox.digitsRightOfDecimal = 2; 508 | YScaler_NumberBox.centerX = ui.displaySpaceCenterX + 80; 509 | YScaler_NumberBox.centerY = 157; 510 | YScaler_NumberBox.width = numberBoxWidth; 511 | YScaler_NumberBox.height = numberBoxHeight; 512 | ui.drawNumberBox(YScaler_NumberBox); 513 | 514 | 515 | // 516 | // define and display "OK" and "Cancel" buttons 517 | // 518 | BUTTON okButton = {"OK", ui.displaySpaceCenterX-80, ui.displaySpaceBottomY-27, 100 , numberBoxHeight}; 519 | ui.drawButton(okButton); 520 | 521 | BUTTON cancelButton = {"Cancel", ui.displaySpaceCenterX+80, ui.displaySpaceBottomY-27, 100 , numberBoxHeight}; 522 | ui.drawButton(cancelButton); 523 | 524 | 525 | // 526 | // process touch events 527 | // 528 | while(true) 529 | { 530 | ui.getTouchEvents(); 531 | 532 | // 533 | // process touch events on the Number Boxes 534 | // 535 | ui.checkForNumberBoxTouched(XOffset_NumberBox); 536 | ui.checkForNumberBoxTouched(XScaler_NumberBox); 537 | ui.checkForNumberBoxTouched(YOffset_NumberBox); 538 | ui.checkForNumberBoxTouched(YScaler_NumberBox); 539 | 540 | // 541 | // check for touch events on the "OK" button 542 | // 543 | if (ui.checkForButtonClicked(okButton)) 544 | { 545 | // 546 | // save the values set by the user to EEPROM, then return to the menu 547 | // 548 | ui.writeConfigurationInt(EEPROM_X_OFFSET, XOffset_NumberBox.value); 549 | ui.writeConfigurationFloat(EEPROM_X_SCALER, XScaler_NumberBox.value); 550 | ui.writeConfigurationInt(EEPROM_Y_OFFSET, YOffset_NumberBox.value); 551 | ui.writeConfigurationFloat(EEPROM_Y_SCALER, YScaler_NumberBox.value); 552 | return; 553 | } 554 | 555 | // 556 | // check for touch events on the "Cancel" button 557 | // 558 | if (ui.checkForButtonClicked(cancelButton)) 559 | return; 560 | } 561 | } 562 | -------------------------------------------------------------------------------- /Documentation.md: -------------------------------------------------------------------------------- 1 | # Teensy User Interface: 2 | 3 | This library is a simple user interface for building Teensy applications. The user interface centers around building apps with one or more touchscreen menus. There are also many features for creating your own screens to present data or prompt the user for information. 4 | 5 | The *Teensy User Interface* library requires a touchscreen LCD display that includes a ILI9341 controller chip. The most common version of these uses a 2.8", 320x240 display. 6 | 7 | ![alt_text](images/TeensyUserInterface.jpg "Teensy User Interface") 8 | 9 | 10 | 11 | Documentation and code for the *Teensy User Interface* library can be found at: 12 | 13 | ​ https://github.com/Stan-Reifel/TeensyUserInterface 14 | 15 | 16 | 17 | ##### Overview: 18 | 19 | The display of the *Teensy User Interface* is divided into two sections: along the top is a *Title Bar* and below is the *Display Space*. The Display Space is where menus, message boxes, configuration screens, along with the application's main display are shown. 20 | 21 | The heart of this user interface are menus. Menus are displayed in rows and columns of touch sensitive buttons. There are three types of menu buttons: *Commands*, *Toggles* and *Sub Menus*. Menus are displayed by creating a *Menu Table* in your source code then calling: *ui.displayAndExecuteMenu()* 22 | 23 | 24 | 25 | ##### Hardware: 26 | 27 | This user interface works with Teensy microcontrollers along with a 2.8" 320x240 ILI9341 LCD touch screen display. These displays are very inexpensive and easy to hookup. This library has only been tested with a *Teensy 3.6* and *Teensy 4.1* but will likely work with much of the Teensy family. 28 | 29 | ![alt_text](images/LCD_Display.jpg "Touch screen display") 30 | 31 | 32 | 33 | The Touchscreen LCDs can be purchased online from many sources, including: 34 | 35 | - PJRC www.pjrc.com/store/display_ili9341_touch.html 36 | - Amazon: Search for "HiLetgo ILI9341 2.8" TFT LCD Display" 37 | - eBay: Search for "ILI9341 2.8" TFT LCD Display" 38 | 39 | 40 | 41 | Here is how to wire the LCD to the Teensy 3.6 and Teensy 4.1: 42 | 43 | ![alt_text](images/HookupGuide-770.png "Hookup Guide") 44 | 45 | | Teensy | Display | 46 | | :----: | :-------: | 47 | | VIN | LCD VCC | 48 | | GND | LCD GND | 49 | | D10 | LCD CS | 50 | | 3.3V | LCD RESET | 51 | | D9 | LCD DC | 52 | | D11 | LCD SDI | 53 | | D13 | LCD CLK | 54 | | 3.3V | LCD LED | 55 | | D12 | LCD SDO | 56 | | D13 | TOUCH CLK | 57 | | D8 | TOUCH CS | 58 | | D11 | TOUCH DIN | 59 | | D12 | TOUCH DO | 60 | 61 | 62 | 63 | # Software Notes: 64 | 65 | ### Getting started: 66 | 67 | It is assumed that you have already configured the Arduino IDE to work with your Teensy micro controller. If not, look here: https://www.pjrc.com/teensy/teensyduino.html 68 | 69 | 70 | 71 | To use the *TeensyUserInterface* library you will need to add it to the *Arduino IDE*, along with two other libraries. Do so by: 72 | 73 | 1. From the Arduino IDE select: *Sketch* / *Include Library* / *Manage Libraries...* 74 | 2. In the *Filter your search* field, type in *TeensyUserInterface* then click *Install*. 75 | 3. In the *Filter your search* field, type in *ILI9341_t3* then click *Install*. 76 | 4. In the *Filter your search* field, type in *XPT2046_Touchscreen* then click *Install*. 77 | 5. As with all Teensy applications, you must configure the Arduino IDE for the type of Teensy microcontroller that you are using. Do that from the menu: *Tools / Board.* 78 | 79 | 80 | 81 | In your sketch, near the top, add this code: 82 | 83 | ``` 84 | #include 85 | #include 86 | #include 87 | 88 | TeensyUserInterface ui; 89 | ``` 90 | 91 | 92 | 93 | Inside your *setup()* function, add this: 94 | 95 | ``` 96 | ui.begin(LCD_ORIENTATION_LANDSCAPE_4PIN_RIGHT, Arial_9_Bold); 97 | ``` 98 | 99 | or this, depending if you want your display wide or tall: 100 | 101 | ``` 102 | ui.begin(LCD_ORIENTATION_PORTRAIT_4PIN_TOP, Arial_9_Bold); 103 | ``` 104 | 105 | 106 | 107 | ### Creating the main menu: 108 | 109 | Just below the *setup()* function you will define your *Main Menu*. A typical main menu table might look like: 110 | 111 | ``` 112 | // 113 | // forward declarations for each menus 114 | // 115 | extern MENU_ITEM mainMenu[]; 116 | extern MENU_ITEM settingsMenu[]; // add this line if you have a Settings submenu 117 | 118 | // 119 | // the main menu table 120 | // 121 | MENU_ITEM mainMenu[] = { 122 | {MENU_ITEM_TYPE_MAIN_MENU_HEADER, "My App", MENU_COLUMNS_1, mainMenu}, 123 | {MENU_ITEM_TYPE_COMMAND, "Log data", menuCommandLogData, NULL}, 124 | {MENU_ITEM_TYPE_TOGGLE, "Sound", menuToggleSoundCallback, NULL}, 125 | {MENU_ITEM_TYPE_SUB_MENU, "Settings", NULL, settingsMenu}, 126 | {MENU_ITEM_TYPE_END_OF_MENU, "", NULL, NULL} 127 | }; 128 | ``` 129 | 130 | 131 | 132 | The first line in a menu table sets what type of menu it is, either a *Main Menu*, or a *Sub Menu*. Since we are building the main menu, the first column of this entry is set to: *MENU_ITEM_TYPE_MAIN_MENU_HEADER*. The three other columns in this line define more about the menu. The second field contains text printed on the LCD's Title Bar when the menu is displayed. The next column sets how the buttons are arranged on the screen and is usually set to MENU_COLUMNS_1 or MENU_COLUMNS_2. The fourth field is typically set to the name of the menu, i.e. *mainMenu*. 133 | 134 | 135 | 136 | Now you will add one line to the table for each button that you want displayed in the menu. There are three different types of buttons that can be added: 137 | 138 | Commands: A *MENU_ITEM_TYPE_COMMAND* entry indicates that a function (written by you) will be executed when this menu button is pushed by the user. In the second column you place the text that you want displayed on the button. The third column is the name of the function that is executed when the menu button is clicked. The last column should always be *NULL*. 139 | 140 | Toggles: A *MENU_ITEM_TYPE_TOGGLE* is used somewhat like a Radio Button in a dialog box. Toggles let the user choose one of a fixed number of choices (such as *On* / *Off*, or *Red* / *Green* / *Blue*). Each time the user clicks on a toggle button, it alternates the selection. In the second column is the button's text. The third column is the name of a callback function that you write to alternates the value. The last column should always be *NULL*. 141 | 142 | Sub menus: A *MENU_ITEM_TYPE_SUB_MENU* entry is used to select a different menu. Often it is useful to group related commands into their own menu, this is what *Sub menus* are for. For example, the main menu might reference a *Settings* sub menu which would be filled with commands for configuring your app. In the second column of this entry is the text displayed on the button describing the sub menu. The fourth field is the name of the sub menu table. 143 | 144 | 145 | 146 | A menu can have as many buttons as you like, simply by adding more entries to the menu table. Buttons are sized such that they always fill the screen, adding more causes them to be shorter in height. In many cases it is advantageous to arrange a menu's buttons in two or more columns. Columns of buttons often look better, are easier to touch, and you can fit more on a single screen. 147 | 148 | The buttons on a menu can be arranged in 1, 2, 3 or 4 columns. The number of columns is set in the third field of the menu table's first line by inserting one of these values: 149 | MENU_COLUMNS_1, MENU_COLUMNS_2, MENU_COLUMNS_3, or MENU_COLUMNS_4 150 | 151 | 152 | The table's last line marks the menu's end with a *MENU_ITEM_TYPE_END_OF_MENU* entry. The second column should always be "". The third and fourth columns are sent to *Null*. 153 | 154 | 155 | 156 | ### Creating a sub menu: 157 | 158 | Sub-menus are menus called from the main menu, or another sub menu. Sub menus allow you to group related commands into their own menu. For example, a *Settings* sub menu might be filled with commands for configuring your app. 159 | 160 | Sub menus are built just like the Main menu, except that the first entry must be *MENU_ITEM_TYPE_SUB_MENU_HEADER*. In the first line's fourth column is the name of the parent menu (typically the main menu). This is used to reselect the parent menu when the user presses the *Back* button, indicating they are done with the sub menu. 161 | 162 | Here is a table for a typical sub menu: 163 | 164 | ``` 165 | // 166 | // the Settings menu 167 | // 168 | MENU_ITEM settingsMenu[] = { 169 | {MENU_ITEM_TYPE_SUB_MENU_HEADER, "Menu Name", MENU_COLUMNS_2, mainMenu}, 170 | {MENU_ITEM_TYPE_COMMAND, "Set contrast", menuCommandSetContrast, NULL}, 171 | {MENU_ITEM_TYPE_TOGGLE, "Power mode", menuTogglePowerCallback, NULL}, 172 | {MENU_ITEM_TYPE_TOGGLE, "LED", menuToggleLEDCallback, NULL}, 173 | {MENU_ITEM_TYPE_END_OF_MENU, "", NULL, NULL} 174 | }; 175 | 176 | ``` 177 | 178 | 179 | 180 | ### The Toggle call back function: 181 | 182 | *Toggle* buttons in a menu let the user to select one of a fixed number of choices (such as *On* / *Off*, or *Red* / *Green* / *Blue*). Each time the user clicks on a Toggle button, it alternates the selection (i.e. toggles between *On* and *Off*, or rotates between *Red*, *Green* and *Blue*). To accomplish this, the Toggle's menu entry includes the name of a callback function that you must write. This function does several things: 1) Switches to the next state. 2) Optionally updates hardware to reflect the new state. 3) Sets the text displayed on the menu button for that state. 183 | 184 | Here's an example of how to write the callback function for a Toggle: 185 | 186 | ``` 187 | void menuToggleLEDCallback(void) 188 | { 189 | // 190 | // check if menu is requesting state be changed (can have more than 2 states) 191 | // 192 | if (ui.toggleMenuChangeStateFlag) 193 | { 194 | ledState = !ledState; // select the next state 195 | } 196 | 197 | // 198 | // turn the LED on or off as indicated by the state 199 | // 200 | if (ledState) 201 | digitalWrite(LED_PIN, HIGH); 202 | else 203 | digitalWrite(LED_PIN, LOW); 204 | 205 | // 206 | // send back text describing the current state 207 | // 208 | if(ledState) 209 | ui.toggleMenuStateText = "On"; 210 | else 211 | ui.toggleMenuStateText = "Off"; 212 | } 213 | ``` 214 | 215 | 216 | 217 | ### Making the first screen show your application, not a menu: 218 | 219 | Most of the examples sketches included with this library display a menu when the sketch first runs. In some cases you want to start with your application showing its own display, then let the user press a button to pull up the menu. An example sketch of this type is *Example8_StopWatch* found in the *examples* folder. 220 | 221 | In this situation you will display your application when the sketch first runs. The LCD's Title bar will show a Menu button. At any time, the user can exit your display and bring up the menu by clicking on this button. There are a few things that you must do to make this all work. 222 | 223 | 1) In your application's main screen, you must first draw the Title Bar to include the Menu button. Do so as follows: 224 | 225 | ​ ui.drawTitleBarWithMenuButton("The Name Of Your App"); 226 | 227 | 228 | 229 | 2) In your app's main loop, you must continuously check if the user has clicked the Menu button. 230 | 231 | ``` 232 | while(true) 233 | { 234 | ui.getTouchEvents(); // get new touch events on the LCD 235 | if (ui.checkForMenuButtonClicked()) // check if Menu button was press 236 | { 237 | ui.displayAndExecuteMenu(mainMenu); // user pressed "Menu", display it 238 | redrawYourApp(); // after exiting menu, redraw app 239 | } 240 | // do other things that your app needs: update the display, check IO... 241 | } 242 | ``` 243 | 244 | 245 | 246 | 3) When the menu is displayed, it needs to have a *Back* button. This provides a way for the user to exit the menu and go back to your application. Normally the Main Menu doesn't show the *Back* button, so it must be enabled. To do that, the fourth column in the first line of your menu table must be set to NULL (instead of *mainMenu*, which is typical). 247 | 248 | 249 | 250 | ### Building your own screens: 251 | 252 | Often commands executed from a menu button need to display some information, or prompt the user to enter some information. To do this you can build screens of your own design. On these screens you can draw text and display graphics. You can also place a few different widgets that are built into the *Teensy User Interface*. These widgets are: Buttons, Number Boxes, and Selection Boxes. 253 | 254 | When you create a custom screen, you need a way for the user to exit that screen and return to the menu. There are two methods for doing this. The first is with a *Back* button on the Title Bar. This code shows how to write a complete menu command that draws the Title bar to include the Back button, and checks if the user touches it: 255 | 256 | ``` 257 | void commandBackButtonExample(void) 258 | { 259 | ui.drawTitleBarWithBackButton("Back Button Example"); // include Back button 260 | ui.clearDisplaySpace(); // clear the display 261 | 262 | // display some text 263 | ui.lcdSetCursorXY(ui.displaySpaceCenterX, ui.displaySpaceCenterY - 10); 264 | ui.lcdPrintCentered("We exit this screen with the Back button."); 265 | 266 | while(true) // wait for user to press "Back" button 267 | { 268 | ui.getTouchEvents(); 269 | 270 | if (ui.checkForBackButtonClicked()) 271 | return; // "Back" pressed, return to menu 272 | } 273 | } 274 | ``` 275 | 276 | 277 | 278 | The second method for exiting a display is to place a custom button on the screen. The button can be any size, placed anywhere on the screen, and can be labeled with any text (i.e. "OK", "Cancel", "Done"). Buttons can also be used to execute custom code and might be labeled like: "Start Motor", "Enable Pump", "Collect Data". 279 | 280 | This sample code shows how to create a complete menu command that draws an "OK" button and checks if the user presses it: 281 | 282 | ``` 283 | void commandOKButtonExample(void) 284 | { 285 | ui.drawTitleBar("OK Button Example"); // draw title bar without Back button 286 | ui.clearDisplaySpace(); 287 | // display some text 288 | ui.lcdSetCursorXY(ui.displaySpaceCenterX, ui.displaySpaceCenterY - 20); 289 | ui.lcdPrintCentered("We exit this screen with an OK button."); 290 | 291 | int buttonX = ui.displaySpaceCenterX; // define and display an "OK" button 292 | int buttonY = ui.displaySpaceBottomY-30; 293 | int buttonWidth = 120; 294 | int buttonHeight = 35; 295 | BUTTON okButton = {"OK", buttonX, buttonY, buttonWidth, buttonHeight}; 296 | ui.drawButton(okButton); 297 | 298 | while(true) // wait for user to press "OK" button 299 | { 300 | ui.getTouchEvents(); 301 | 302 | if (ui.checkForButtonClicked(okButton)) 303 | { 304 | // optionally add more code to do the stuff you want done when 305 | // the button is pressed 306 | return; // OK pressed, return to menu 307 | } 308 | } 309 | } 310 | ``` 311 | 312 | 313 | 314 | ### Prompting the user to enter a number: 315 | 316 | Frequently applications need the user to input one or more numeric values. For this purpose, the library makes use of *Number Boxes*. A Number Box is a widget that can be added to a screen of your own design. You can place just one, or several as shown below. Touching the *Up* and *Down* buttons allow the user to set the number. When you place a Number Box, you specify the Min and Max values, along with a Step value. There are two types of Number Boxes, one for INTs and one for FLOATs. The designer of this screen placed two INT Number Boxes, two FLOAT Number Boxes, and two buttons. 317 | 318 | ![alt_text](images/NumberBox.jpg "Number boxes") 319 | 320 | 321 | 322 | The code below shows how to create a complete screen that includes one INT Number Box, along with OK and Cancel buttons: 323 | 324 | ``` 325 | void commandGetXOffsetInteger(void) 326 | { 327 | ui.drawTitleBar("Prompt User for an Integer"); 328 | ui.clearDisplaySpace(); 329 | 330 | // 331 | // define a Number Box, specify initial value, max and min values, 332 | // step up/down amount, and how big the Number Box is (in pixels), 333 | // along with where it's placed on screen 334 | // 335 | NUMBER_BOX my_NumberBox; 336 | my_NumberBox.labelText = "Set X offset"; 337 | my_NumberBox.value = xOffsetValue; // Number Box's default value 338 | my_NumberBox.minimumValue = -200; 339 | my_NumberBox.maximumValue = 200; 340 | my_NumberBox.stepAmount = 2; 341 | my_NumberBox.centerX = ui.displaySpaceCenterX; 342 | my_NumberBox.centerY = ui.displaySpaceCenterY - 20; 343 | my_NumberBox.width = 200; 344 | my_NumberBox.height = 35; 345 | ui.drawNumberBox(my_NumberBox); // display the Number Box 346 | 347 | // 348 | // define and display "OK" and "Cancel" buttons 349 | // 350 | BUTTON okButton = {"OK", ui.displaySpaceCenterX-70, ui.displaySpaceBottomY-35, 351 | 120, 35}; 352 | ui.drawButton(okButton); 353 | 354 | BUTTON cancelButton = {"Cancel", ui.displaySpaceCenterX+70, 355 | ui.displaySpaceBottomY-35, 120, 35}; 356 | ui.drawButton(cancelButton); 357 | 358 | // 359 | // process touch events 360 | // 361 | while(true) 362 | { 363 | ui.getTouchEvents(); // check for touch events 364 | ui.checkForNumberBoxTouched(my_NumberBox); // process NumberBox touch events 365 | 366 | if (ui.checkForButtonClicked(okButton)) // check for touches on OK button 367 | { 368 | xOffsetValue = my_NumberBox.value; // pressed OK, get NumberBox value 369 | return; // return to the menu 370 | } 371 | 372 | if (ui.checkForButtonClicked(cancelButton)) 373 | return; // user pressed Cancel, return to menu 374 | } 375 | } 376 | ``` 377 | 378 | 379 | 380 | Creating a FLOAT Number Box is very similar to the INT example above. The main difference is that it's declared with a *NUMBER_BOX_FLOAT* datatype like this: 381 | 382 | ``` 383 | NUMBER_BOX_FLOAT my_NumberBox; 384 | my_NumberBox.labelText = "Set X scaler"; 385 | my_NumberBox.value = xScalerValue; 386 | my_NumberBox.minimumValue = 0.0; 387 | my_NumberBox.maximumValue = 1.0; 388 | my_NumberBox.stepAmount = 0.01; 389 | my_NumberBox.digitsRightOfDecimal = 2; 390 | my_NumberBox.centerX = ui.displaySpaceCenterX; 391 | my_NumberBox.centerY = ui.displaySpaceCenterY - 20; 392 | my_NumberBox.width = numberBoxWidth; 393 | my_NumberBox.height = numberBoxAndButtonsHeight; 394 | ui.drawNumberBox(my_NumberBox); 395 | ``` 396 | 397 | 398 | 399 | ### Selection Boxes: 400 | 401 | Another widget that can be added to your screen is a *Selection Box*. Selection Boxes allow the user to pick one of 2, 3, or 4 choices. The image below shows a typical configuration screen that uses three Selection Boxes, along with the *Back* button. 402 | 403 | ![alt_text](images/SelectionBox.jpg "Selection boxes") 404 | 405 | 406 | 407 | Here is how to create a screen that includes a Selection Box: 408 | 409 | ``` 410 | void commandMakeAChoice(void) 411 | { 412 | ui.drawTitleBarWithBackButton("Using Selection Boxes"); 413 | ui.clearDisplaySpace(); 414 | 415 | // 416 | // define and display a selection box with 3 choices 417 | // 418 | SELECTION_BOX powerSelectionBox; 419 | powerSelectionBox.labelText = "Laser power level"; 420 | powerSelectionBox.value = powerSelection; // set default value, 0 is 1st choice 421 | powerSelectionBox.choice0Text = "Low"; 422 | powerSelectionBox.choice1Text = "Medium"; 423 | powerSelectionBox.choice2Text = "High"; 424 | powerSelectionBox.choice3Text = ""; // set unused choices to: "" 425 | powerSelectionBox.centerX = ui.displaySpaceCenterX; 426 | powerSelectionBox.centerY = ui.displaySpaceCenterY - 20;; 427 | powerSelectionBox.width = 250; 428 | powerSelectionBox.height = 33; 429 | ui.drawSelectionBox(powerSelectionBox); // display the Selection Box 430 | 431 | while(true) // process touch events 432 | { 433 | ui.getTouchEvents(); 434 | ui.checkForSelectionBoxTouched(powerSelectionBox); // process SelectionBox touches 435 | if (ui.checkForBackButtonClicked()) // check for Back button 436 | { 437 | powerSelection = powerSelectionBox.value; // read value from SelectionBox 438 | return; // return to the menu 439 | } 440 | } 441 | } 442 | 443 | ``` 444 | 445 | Note: The Selection Box's *.value* property is an integer between 0 and 3. A value of 0 indicates the left most choice was made. In the example above, *value* set to 0 corresponds to the "Low" choice, 1 goes with "Medium", and 2 for "High". 446 | 447 | 448 | 449 | ### Displaying text and graphics: 450 | 451 | The library includes many functions for drawing your own screens. The general approach is: 452 | 453 | ``` 454 | 1. Draw the Title bar: 455 | ui.drawTitleBar("My Screen Name") or 456 | ui.drawTitleBarWithBackButton("My Other Screen Name") 457 | 458 | 2. Clear the Display Space: 459 | ui.clearDisplaySpace() or 460 | ui.clearDisplaySpace(LCD_BLUE) 461 | 462 | 3. Prior to drawing text, first set the cursor position with 463 | ui.lcdSetCursorXY(X, Y). Note: (0, 0) is the upper left corner. 464 | X is a value from 0 to 319. Y is a value from 0 to 239. 465 | These constants can be helpful when setting coordinates: 466 | ui.displaySpaceWidth ui.displaySpaceHeight 467 | ui.displaySpaceLeftX ui.displaySpaceRightX 468 | ui.displaySpaceTopY ui.displaySpaceBottomY 469 | ui.displaySpaceCenterX ui.displaySpaceCenterY 470 | ui.lcdWidth ui.lcdHeight 471 | 472 | 4. Optionally set the text color with: ui.lcdSetFontColor(LCD_YELLOW) 473 | 474 | 5. Optionally set the text font with: 475 | ui.lcdSetFont(Arial_12) or 476 | ui.lcdSetFont(Arial_9_Bold) 477 | All fonts are Arial or Arial Bold and available in these sizes: 8, 9, 10, 478 | 11, 12, 13, 14, 16, 18, 20, 24, 28, 32, 40, 48, 60, 72, 96. 479 | 480 | 6. Print some text or numeric values. There are many functions for printing, 481 | including: 482 | ui.lcdPrint() 483 | ui.lcdPrint() 484 | ui.lcdPrintCentered() 485 | ui.lcdPrintRightJustified() 486 | ui.lcdPrintCharacter() 487 | 488 | You can determine the width and height of a printed string using these 489 | functions: 490 | ui.lcdStringWidthInPixels("Hello World") 491 | ui.lcdGetFontHeightWithoutDecenders() 492 | ui.lcdGetFontHeightWithDecentersAndLineSpacing() 493 | 494 | Note: The functions that print strings, expect a C String, meaning an 495 | array of char terminated by a 0 (as opposed to a string created using 496 | a String Object). 497 | 498 | 7. Print graphics using these functions: 499 | ui.lcdDrawPixel(x, y, color) 500 | ui.lcdDrawLine(x1, y1, x2, y2, color) 501 | ui.lcdDrawHorizontalLine(x, y, length, color) 502 | ui.lcdDrawVerticalLine(x, y, length, color) 503 | ui.lcdDrawRectangle(x, y, width, height, color) 504 | ui.lcdDrawRoundedRectangle(x, y, width, height, radius, color) 505 | ui.lcdDrawTriangle(x0, y0, x1, y1, x2, y2, color) 506 | ui.lcdDrawCircle(x, y, radius, color) 507 | ui.lcdDrawFilledRectangle(x, y, width, height, color) 508 | ui.lcdDrawFilledRoundedRectangle(x, y, width, height, radius, color) 509 | ui.lcdDrawFilledTriangle(x0, y0, x1, y1, x2, y2, color) 510 | ui.lcdDrawFilledCircle(x, y, radius, color) 511 | ``` 512 | 513 | 514 | 515 | ### Display colors: 516 | 517 | Many functions in the *Teensy User Interface* allow you to set display colors. This LCD display uses a 16 bit color format. The format is referred to as *RGB565*, meaning 5 bits of red, 6 bits of green, and 5 bits of blue. 518 | 519 | When you call a function that wants a color value, you have two choices: you can used a pre-defined color constant, or create your own value. The color constants built into the *Teensy User Interface* are: 520 | 521 | | | | | | | 522 | | :----------: | :-----------: | :----------: | :-----------: | :-------------: | 523 | | LCD_BLACK | LCD_WHITE | LCD_RED | LCD_BLUE | LCD_GREEN | 524 | | LCD_YELLOW | LCD_ORANGE | LCD_PURPLE | LCD_DARKBLUE | LCD_DARKGREEN | 525 | | LCD_MAROON | LCD_MAGENTA | LCD_CYAN | LCD_LIGHTBLUE | LCD_OLIVE | 526 | | LCD_DARKGREY | LCD_LIGHTGREY | LCD_DARKCYAN | LCD_NAVY | LCD_GREENYELLOW | 527 | 528 | 529 | ​ 530 | 531 | The easiest way to create your own color is with the *ui.lcdMakeColor*() function. It takes red, green and blue values then returns a color in the RGB565 format. Call it with a red value between 0-31, green between 0-63, and a blue value between 0-31. Here is an example: 532 | 533 | ``` 534 | uint16_t red = 10; 535 | uint16_t green = 20; 536 | uint16_t blue = 10; 537 | uint16_t grayColor = ui.lcdMakeColor(red, green, blue); 538 | ui.lcdDrawFilledCircle(160, 120, 30, grayColor); // draw a gray circle in middle of screen 539 | ``` 540 | 541 | 542 | 543 | ### Saving configuration settings: 544 | 545 | *Number Boxes* and *Selection Boxes* are often used to configure your project at runtime. Values set with these widgets can be saved in the Teensy's EEPROM so the project defaults to the configured values when powered up. 546 | 547 | The *Teensy User Interface* library has these functions for saving/reading configuration values: 548 | 549 | ``` 550 | writeConfigurationByte() and readConfigurationByte() for 8 bit values 551 | writeConfigurationShort() and readConfigurationShort() for 16 bit values 552 | writeConfigurationInt() and readConfigurationInt() for 32 bit values 553 | writeConfigurationFloat() and readConfigurationFloat() for 32 bit floats 554 | ``` 555 | 556 | 557 | 558 | Below is example code for writing and reading two different configuration settings, an *Int* and a *Float*. 559 | 560 | ``` 561 | // 562 | // storage locations in EEPROM for configuration values 563 | // 564 | const int EEPROM_X_OFFSET = 0; // int uses 5 bytes of EEPROM 565 | const int EEPROM_X_SCALER = EEPROM_X_OFFSET+5; // float uses 5 bytes 566 | 567 | // 568 | // defaults configuration values, these are used if they have never been set 569 | // 570 | const int DEFAULT_X_OFFSET = 50; 571 | const float DEFAULT_X_SCALER = 0.57; 572 | 573 | // 574 | // save configuration values in EEPROM 575 | // 576 | ui.writeConfigurationInt(EEPROM_X_OFFSET, valueXOffset); 577 | ui.writeConfigurationFloat(EEPROM_X_SCALER, valueXScaler); 578 | 579 | // 580 | // read configuration values from EEPROM, if EEPROM has never saved, use 581 | // default values 582 | // 583 | int valueXOffset = ui.readConfigurationInt(EEPROM_X_OFFSET, DEFAULT_X_OFFSET); 584 | float valueXScaler = ui.readConfigurationFloat(EEPROM_X_SCALER, DEFAULT_X_SCALER); 585 | ``` 586 | 587 | Note 1: The functions that read a configuration value take a *Default value*. This number is returned if no value has ever been saved for that setting (i.e. The first time the app is runs, the user will have never saved any configuration settings). 588 | 589 | Note 2: You need to manage where in EEPROM each of your values is stored. Your first value is saved at location 0. When determining the position for the following values it is important to note that they take one more byte than the data type requires (i.e. 2 EEPROM bytes are needed for a *byte*, 3 for a *short*, 5 for a *int*, 5 for a *float*). See how this is done with the *const* declarations above. 590 | 591 | 592 | 593 | # The Library of Functions: 594 | 595 | ### Setup functions: 596 | 597 | ``` 598 | // 599 | // initialize the UI, display hardware and touchscreen hardware 600 | // Enter: lcdCSPin = pin number for the LCD's CS pin 601 | // LcdDCPin = pin number for the LCD's DC pin 602 | // TouchScreenCSPin = pin number for the touchscreen's CS pin 603 | // lcdOrientation = 604 | // LCD_ORIENTATION_PORTRAIT_4PIN_TOP 605 | // LCD_ORIENTATION_LANDSCAPE_4PIN_LEFT 606 | // LCD_ORIENTATION_PORTRAIT_4PIN_BOTTOM 607 | // LCD_ORIENTATION_LANDSCAPE_4PIN_RIGHT 608 | // font -> the font typeface to load, ei: Arial_10 609 | // 610 | void TeensyUserInterface::begin(int lcdCSPin, int LcdDCPin, int TouchScreenCSPin, 611 | int lcdOrientation, const ui_font &font) 612 | 613 | // 614 | // set color palette to Blue 615 | // 616 | void TeensyUserInterface::setColorPaletteBlue(void) 617 | 618 | 619 | // 620 | // set color palette to Gray 621 | // 622 | void TeensyUserInterface::setColorPaletteGray(void) 623 | 624 | 625 | // 626 | // set the orientation of the lcd and touch screen, this can be called to change 627 | // the orientation after it is initially set 628 | // Enter: lcdOrientation = 629 | // LCD_ORIENTATION_PORTRAIT_4PIN_TOP 630 | // LCD_ORIENTATION_LANDSCAPE_4PIN_LEFT 631 | // LCD_ORIENTATION_PORTRAIT_4PIN_BOTTOM 632 | // LCD_ORIENTATION_LANDSCAPE_4PIN_RIGHT 633 | // 634 | void TeensyUserInterface::setOrientation(int lcdOrientation) 635 | 636 | ``` 637 | 638 | 639 | 640 | ### Menu functions: 641 | 642 | ``` 643 | // 644 | // set the menu colors 645 | // Enter: _menuBackgroundColor = RGB565 color for background of the menu 646 | // _menuButtonColor = RGB565 color for menu buttons 647 | // _menuButtonSelectedColor = RGB565 color for menu buttons that are selected 648 | // _menuButtonFrameColor = RGB565 color for highlight around the menu buttons 649 | // _menuButtonTextColor = RGB565 color for text of the buttons 650 | // 651 | void TeensyUserInterface::setMenuColors(uint16_t _menuBackgroundColor, 652 | uint16_t _menuButtonColor, uint16_t _menuButtonSelectedColor, 653 | uint16_t _menuButtonFrameColor, uint16_t _menuButtonTextColor) 654 | 655 | 656 | // 657 | // set the menu font 658 | // Enter: font -> the font to use when drawing the title bar 659 | // ie: Arial_8, Arial_8_Bold, Arial_9, Arial_9_Bold, Arial_10, 660 | // Arial_10_Bold, Arial_11, Arial_11_Bold, Arial_12, 13, 14, 661 | // 16, 18, 20, 24, 28, 32, 40, 48, 60, 72, 96 662 | // 663 | void TeensyUserInterface::setMenuFont(const ui_font &font) 664 | 665 | 666 | // 667 | // display the top level menu, then execute the commands selected by the user 668 | // Enter: menu -> the menu to display 669 | // 670 | void TeensyUserInterface::displayAndExecuteMenu(MENU_ITEM *menu) 671 | 672 | 673 | // 674 | // select and display a menu or submenu, for most applications this function is not used 675 | // Enter: menu -> the menu to display 676 | // drawMenuFlg = true if should draw the new menu 677 | // 678 | void TeensyUserInterface::selectAndDrawMenu(MENU_ITEM *menu, boolean drawMenuFlg) 679 | 680 | 681 | // 682 | // set a callback function that's periodically executed while the application 683 | // is showing a menu, for most applications setting a callback function is not needed 684 | // Enter: callbackFunction -> function to execute continuously while a menu is presented, 685 | // set to NULL to disable 686 | // 687 | void TeensyUserInterface::setInMenuCallbackFunction(void (*callbackFunction)()) 688 | ``` 689 | 690 | 691 | 692 | ### Title Bar functions: 693 | 694 | ``` 695 | // 696 | // set the title bar colors 697 | // Enter: _titleBarColor = RGB565 color for the title bar background 698 | // _titleBarTextColor = RGB565 color for the title bar's text 699 | // _titleBarBackButtonColor = RGB565 color for the back button on the title bar 700 | // 701 | void TeensyUserInterface::setTitleBarColors(uint16_t _titleBarColor, 702 | uint16_t _titleBarTextColor, uint16_t _titleBarBackButtonColor, 703 | uint16_t _titleBarBackButtonSelectedColor) 704 | 705 | 706 | // 707 | // set the title bar font 708 | // Enter: font -> the font to use when drawing the title bar 709 | // ie: Arial_8, Arial_8_Bold, Arial_9, Arial_9_Bold, Arial_10, 710 | // Arial_10_Bold, Arial_11, Arial_11_Bold, Arial_12, 13, 14, 711 | // 16, 18, 20, 24, 28, 32, 40, 48, 60, 72, 96 712 | // 713 | void TeensyUserInterface::setTitleBarFont(const ui_font &font) 714 | 715 | 716 | // 717 | // draw the title bar (without the back or hamburger button) 718 | // 719 | void TeensyUserInterface::drawTitleBar(const char *titleBarText) 720 | 721 | 722 | // 723 | // draw the title bar with the back button 724 | // 725 | void TeensyUserInterface::drawTitleBarWithBackButton(const char *titleBarText) 726 | 727 | 728 | // 729 | // draw the title bar with the Menu button 730 | // 731 | void TeensyUserInterface::drawTitleBarWithMenuButton(const char *titleBarText) 732 | 733 | 734 | // 735 | // check if user has touched and released the title bar's Back button, this also 736 | // highlights the button when the user first touches it 737 | // Note: getTouchEvents() must be called at the top of the loop that calls this 738 | // function 739 | // Exit: true returned if user has touched and released the Back button, 740 | // else false 741 | // 742 | boolean TeensyUserInterface::checkForBackButtonClicked(void) 743 | 744 | 745 | // 746 | // check if user has touched and released the title bar's Menu button, this also 747 | // highlights the button when the user first touches it 748 | // Note: getTouchEvents() must be called at the top of the loop that calls this 749 | // function 750 | // Exit: true returned if user has touched and released the Menu button, 751 | // else false 752 | // 753 | boolean TeensyUserInterface::checkForMenuButtonClicked(void) 754 | ``` 755 | 756 | 757 | 758 | ### Display Space functions: 759 | 760 | ``` 761 | // 762 | // clear the screen's "display space" using the menu's background color and draw 763 | // a frame around it 764 | // 765 | void TeensyUserInterface::clearDisplaySpace(void) 766 | 767 | 768 | // 769 | // clear the screen's "display space" using the given background color and draw 770 | // a frame around it 771 | // Enter: backgroundColor = color to fill the display space 772 | // 773 | void TeensyUserInterface::clearDisplaySpace(uint16_t backgroundColor) 774 | ``` 775 | 776 | 777 | 778 | ### Button functions: 779 | 780 | ``` 781 | // 782 | // draw a rectangular button using the colors and font defined for the menu 783 | // Enter: uiButton -> the specifications for the button to draw 784 | // 785 | void TeensyUserInterface::drawButton(BUTTON &uiButton) 786 | 787 | 788 | // 789 | // draw a rectangular button using the colors and font defined for the menu 790 | // Enter: uiButton -> the specifications for the button to draw 791 | // showButtonTouchedFlg = true to draw button showing it's being 792 | // touched, false to draw normal 793 | // 794 | void TeensyUserInterface::drawButton(BUTTON &uiButton, boolean showButtonTouchedFlg) 795 | 796 | 797 | // 798 | // draw a rectangular button with extended options for setting color and font 799 | // Enter: uiButton -> the specifications for the button to draw 800 | // 801 | void TeensyUserInterface::drawButton(BUTTON_EXTENDED &uiButtonExt) 802 | 803 | 804 | // 805 | // draw a rectangular button with extended options for setting color and font 806 | // Enter: uiButton -> the specifications for the button to draw 807 | // showButtonTouchedFlg = true to draw button showing it's being 808 | // touched, false to draw normal 809 | // 810 | void TeensyUserInterface::drawButton(BUTTON_EXTENDED &uiButtonExt, boolean showButtonTouchedFlg) 811 | 812 | 813 | // 814 | // check if user has touched and released the given button, this also highlights 815 | // the button when the user first touches it 816 | // Note: getTouchEvents() must be called at the top of the loop that calls this 817 | // function 818 | // Enter: uiButton -> the button to test 819 | // Exit: true returned if user has touched and released this button, 820 | // else false 821 | // 822 | boolean TeensyUserInterface::checkForButtonClicked(BUTTON &uiButton) 823 | 824 | 825 | // 826 | // check if user has touched and released the given button, this also highlights 827 | // the button when the user first touches it 828 | // Note: getTouchEvents() must be called at the top of the loop that calls this 829 | // function 830 | // Enter: uiButton -> the button to test 831 | // Exit: true returned if user has touched and released this button, 832 | // else false 833 | // 834 | boolean TeensyUserInterface::checkForButtonClicked(BUTTON_EXTENDED &uiButton) 835 | 836 | 837 | // 838 | // check if user is holding down the given button and it's now "Auto repeating" 839 | // Note: getTouchEvents() must be called at the top of the loop that calls this 840 | // function 841 | // Enter: uiButton -> the button to test 842 | // Exit: true returned if there's an Auto Repeat event for this button, 843 | // else false 844 | // 845 | boolean TeensyUserInterface::checkForButtonAutoRepeat(BUTTON &uiButton) 846 | 847 | 848 | // 849 | // check if user is holding down the given button and it's now "Auto repeating" 850 | // Note: getTouchEvents() must be called at the top of the loop that calls this 851 | // function 852 | // Enter: uiButton -> the button to test 853 | // Exit: true returned if there's an Auto Repeat event for this button, 854 | // else false 855 | // 856 | boolean TeensyUserInterface::checkForButtonAutoRepeat(BUTTON_EXTENDED &uiButton) 857 | 858 | 859 | // 860 | // check if user has just touched the given button, this happens once each time a 861 | // button is pressed, in most cases using checkForButtonClicked() is preferred 862 | // Note: getTouchEvents() must be called at the top of the loop that calls this 863 | // function 864 | // Enter: uiButton -> the button to test 865 | // Exit: true returned if user has touched and released this button, 866 | // else false 867 | // 868 | boolean TeensyUserInterface::checkForButtonFirstTouched(BUTTON &uiButton) 869 | 870 | 871 | // 872 | // check if user has just touched the given button, this happens once each time 873 | // a button is pressed, in most cases using checkForButtonClicked() is preferred 874 | // Note: getTouchEvents() must be called at the top of the loop that calls this 875 | // function 876 | // Enter: uiButton -> the button to test 877 | // Exit: true returned if user has touched and released this button, 878 | // else false 879 | // 880 | boolean TeensyUserInterface::checkForButtonFirstTouched(BUTTON_EXTENDED &uiButton) 881 | 882 | 883 | // 884 | // definition of a Button, the menu's colors and font are used 885 | // 886 | typedef struct 887 | { 888 | const char *labelText; 889 | int centerX; 890 | int centerY; 891 | int width; 892 | int height; 893 | } BUTTON; 894 | 895 | 896 | // 897 | // definition of a Button with extended options 898 | // 899 | typedef struct 900 | { 901 | const char *labelText; 902 | int centerX; 903 | int centerY; 904 | int width; 905 | int height; 906 | uint16_t buttonColor; 907 | uint16_t buttonSelectedColor; 908 | uint16_t buttonFrameColor; 909 | uint16_t buttonTextColor; 910 | const ui_font &buttonFont; 911 | } BUTTON_EXTENDED; 912 | ``` 913 | 914 | 915 | 916 | ### Number Box functions: 917 | 918 | ``` 919 | // 920 | // draw a Number Box 921 | // Enter: numberBox -> the specifications of the Number Box to draw 922 | // 923 | void TeensyUserInterface::drawNumberBox(NUMBER_BOX &numberBox) 924 | 925 | 926 | // 927 | // draw a Number Box (FLOAT) 928 | // Enter: numberBox -> the specifications of the Number Box to draw 929 | // 930 | void TeensyUserInterface::drawNumberBox(NUMBER_BOX_FLOAT &numberBox) 931 | 932 | 933 | // 934 | // check if user is touching the Number Box, pressing Up or Down 935 | // Note: getTouchEvents() must be called at the top of the loop that calls 936 | // this function 937 | // Enter: numberBox -> the Number Box to check 938 | // Exit: true returned if the Number Box's Value changed 939 | // 940 | boolean TeensyUserInterface::checkForNumberBoxTouched(NUMBER_BOX &numberBox) 941 | 942 | 943 | // 944 | // check if user is touching the Number Box, pressing Up or Down (FLOAT) 945 | // Note: getTouchEvents() must be called at the top of the loop that calls 946 | // this function 947 | // Enter: numberBox -> the Number Box to check 948 | // Exit: true returned if the Number Box's Value changed 949 | // 950 | boolean TeensyUserInterface::checkForNumberBoxTouched(NUMBER_BOX_FLOAT &numberBox) 951 | 952 | 953 | // 954 | // definition of a integer Number Box 955 | // 956 | typedef struct 957 | { 958 | const char *labelText; 959 | int value; 960 | int minimumValue; 961 | int maximumValue; 962 | int stepAmount; 963 | int centerX; 964 | int centerY; 965 | int width; 966 | int height; 967 | } NUMBER_BOX; 968 | 969 | 970 | // 971 | // definition of a float Number Box 972 | // 973 | typedef struct 974 | { 975 | const char *labelText; 976 | float value; 977 | float minimumValue; 978 | float maximumValue; 979 | float stepAmount; 980 | int digitsRightOfDecimal; 981 | int centerX; 982 | int centerY; 983 | int width; 984 | int height; 985 | } NUMBER_BOX_FLOAT; 986 | ``` 987 | 988 | 989 | 990 | ### Selection Box functions: 991 | 992 | ``` 993 | // 994 | // draw a Selection Box 995 | // Enter: selectionBox -> the specifications of the Selection Box to draw 996 | // 997 | void TeensyUserInterface::drawSelectionBox(SELECTION_BOX &selectionBox) 998 | 999 | 1000 | // 1001 | // check if user is touching the Selection Box, pressing one of the choices 1002 | // Note: getTouchEvents() must be called at the top of the loop that calls 1003 | // this function 1004 | // Enter: selectionBox -> the selection Box to check 1005 | // Exit: true returned if the Selection Box's Value changed 1006 | // 1007 | boolean TeensyUserInterface::checkForSelectionBoxTouched(SELECTION_BOX &selectionBox) 1008 | 1009 | 1010 | // 1011 | // definition of a Selection Box 1012 | // 1013 | typedef struct 1014 | { 1015 | const char *labelText; 1016 | int value; 1017 | const char *choice0Text; 1018 | const char *choice1Text; 1019 | const char *choice2Text; 1020 | const char *choice3Text; 1021 | int centerX; 1022 | int centerY; 1023 | int width; 1024 | int height; 1025 | } SELECTION_BOX; 1026 | ``` 1027 | 1028 | 1029 | 1030 | ### Touchscreen functions: 1031 | 1032 | ``` 1033 | // 1034 | // check if the most recent touch event happened inside the given rectangle 1035 | // Enter: eventType = the type of event to look for 1036 | // (ie TOUCH_PUSHED_EVENT, TOUCH_RELEASED_EVENT, TOUCH_REPEAT_EVENT) 1037 | // rectX1, rectY1 = upper left corner of the test area 1038 | // rectX2, rectY2 = lower right corner of the test area 1039 | // Exit: true returned if most recent touch event matches the given parameters 1040 | // 1041 | boolean TeensyUserInterface::checkForTouchEventInRect(int eventType, int rectX1, 1042 | int rectY1, int rectX2, int rectY2) 1043 | 1044 | 1045 | // 1046 | // check touch screen for new events 1047 | // Exit: touchEventType = touch event type, TOUCH_NO_EVENT if no event 1048 | // touchEventX, touchEventY = LCD coordinates of touch event 1049 | // 1050 | void TeensyUserInterface::getTouchEvents(void) 1051 | 1052 | 1053 | // 1054 | // set default calibration constants for converting to LCD coordinates 1055 | // Enter: lcdOrientation = LCD_ORIENTATION_PORTRAIT_4PIN_TOP, 1056 | // LCD_ORIENTATION_LANDSCAPE_4PIN_LEFT 1057 | // LCD_ORIENTATION_PORTRAIT_4PIN_BOTTOM, 1058 | // LCD_ORIENTATION_LANDSCAPE_4PIN_RIGHT 1059 | // 1060 | void TeensyUserInterface::setDefaultTouchScreenCalibrationConstants(int lcdOrientation) 1061 | 1062 | 1063 | // 1064 | // set the touch screen calibration constants used for converting from 1065 | // touch coordinates to LCD coordinates 1066 | // Enter: tsToLCDOffsetX = touch screen X offset calibration constant 1067 | // tsToLCDScalerX = touch screen X scaler calibration constant 1068 | // tsToLCDOffsetY = touch screen Y offset calibration constant 1069 | // tsToLCDScalerY = touch screen Y scaler calibration constant 1070 | // 1071 | void TeensyUserInterface::setTouchScreenCalibrationConstants(int tsToLCDOffsetX, 1072 | float tsToLCDScalerX, int tsToLCDOffsetY, float tsToLCDScalerY) 1073 | 1074 | 1075 | // 1076 | // get the XY values of where to touch screen is being touched (in LCD space) 1077 | // Enter: xLCD, yLCD -> storage to return X and Y coordinates 1078 | // Exit: true returned if touch screen is currently being touch, else false 1079 | // 1080 | boolean TeensyUserInterface::getTouchScreenCoords(int *xLCD, int *yLCD) 1081 | 1082 | 1083 | // 1084 | // types of touch events 1085 | // 1086 | const int TOUCH_NO_EVENT = 0; // no event from touch screen (nothing touched) 1087 | const int TOUCH_PUSHED_EVENT = 1; // touch screen just touched 1088 | const int TOUCH_RELEASED_EVENT = 2; // touch screen just released 1089 | const int TOUCH_REPEAT_EVENT = 3; // touch screen touched & event repeating 1090 | ``` 1091 | 1092 | 1093 | 1094 | ### LCD drawing functions: 1095 | 1096 | ``` 1097 | // 1098 | // fill the entire lcd screen with the given color 1099 | // Enter: color = 16 bit color, bit format: rrrrrggggggbbbbb 1100 | // 1101 | void TeensyUserInterface::lcdClearScreen(uint16_t color) 1102 | 1103 | 1104 | // 1105 | // draw one pixel with the given coords and color 1106 | // Enter: x, y = coords of the pixel to draw 1107 | // color = 16 bit color, bit format: rrrrrggggggbbbbb 1108 | // 1109 | void TeensyUserInterface::lcdDrawPixel(int x, int y, uint16_t color) 1110 | 1111 | 1112 | // 1113 | // draw a line with the given coords and color 1114 | // Enter: x1, y1 = first endpoint of line 1115 | // x2, y2 = second endpoint of line 1116 | // color = 16 bit color, bit format: rrrrrggggggbbbbb 1117 | // 1118 | void TeensyUserInterface::lcdDrawLine(int x1, int y1, int x2, int y2, 1119 | uint16_t color) 1120 | 1121 | 1122 | // 1123 | // draw a horizontal line with the given coords, length and color 1124 | // Enter: x, y = first endpoint of line 1125 | // length = length of line 1126 | // color = 16 bit color, bit format: rrrrrggggggbbbbb 1127 | // 1128 | void TeensyUserInterface::lcdDrawHorizontalLine(int x, int y, int length, 1129 | uint16_t color) 1130 | 1131 | 1132 | // 1133 | // draw a vertical line with the given coords, length and color 1134 | // Enter: x, y = first endpoint of line 1135 | // length = length of line 1136 | // color = 16 bit color, bit format: rrrrrggggggbbbbb 1137 | // 1138 | void TeensyUserInterface::lcdDrawVerticalLine(int x, int y, int length, 1139 | uint16_t color) 1140 | 1141 | 1142 | // 1143 | // draw rectangle at the given coords, length, width and color 1144 | // Enter: x, y = upper left corner of rect 1145 | // width = width of rectangle 1146 | // height = height of rectangle 1147 | // color = 16 bit color, bit format: rrrrrggggggbbbbb 1148 | // 1149 | void TeensyUserInterface::lcdDrawRectangle(int x, int y, int width, int height, 1150 | uint16_t color) 1151 | 1152 | 1153 | // 1154 | // draw rounded rectangle at the given coords, length, width and color 1155 | // Enter: x, y = upper left corner of rect 1156 | // width = width of rectangle 1157 | // height = height of rectangle 1158 | // radius = radius of the corners 1159 | // color = 16 bit color, bit format: rrrrrggggggbbbbb 1160 | // 1161 | void TeensyUserInterface::lcdDrawRoundedRectangle(int x, int y, int width, 1162 | int height, int radius, uint16_t color) 1163 | 1164 | 1165 | // 1166 | // draw a circle at the given coords, radius and color 1167 | // Enter: x0, y0 = endpoint 0 of the triangle 1168 | // x1, y1 = endpoint 1 of the triangle 1169 | // x2, y2 = endpoint 2 of the triangle 1170 | // color = 16 bit color, bit format: rrrrrggggggbbbbb 1171 | // 1172 | void TeensyUserInterface::lcdDrawTriangle(int x0, int y0, int x1, int y1, 1173 | int x2, int y2, uint16_t color) 1174 | 1175 | 1176 | // 1177 | // draw a filled circle at the given coords, radius and color 1178 | // Enter: x, y = upper left corner of rect 1179 | // radius = radius of the circle 1180 | // color = 16 bit color, bit format: rrrrrggggggbbbbb 1181 | // 1182 | void TeensyUserInterface::lcdDrawFilledCircle(int x, int y, int radius, 1183 | uint16_t color) 1184 | 1185 | 1186 | 1187 | // 1188 | // draw an image 1189 | // Enter: x, y = coords of upper left corner on LCD where the image will be displayed 1190 | // width, height = size of the image, this must be the same as the image data 1191 | // image -> image data, 2 bytes/pixel in the RGB565 format stored in PROGMEM 1192 | // Note: use this utility to convert an image to C sourse code: 1193 | // www.rinkydinkelectronics.com/_t_doimageconverter565.php 1194 | // 1195 | void TeensyUserInterface::lcdDrawImage(int x, int y, int width, int height, 1196 | const uint16_t *image) 1197 | 1198 | 1199 | // 1200 | // set the text font for the "print" functions 1201 | // Enter: font -> the font typeface to load 1202 | // ie: Arial_8, Arial_8_Bold, Arial_9, Arial_9_Bold, Arial_10, 1203 | // Arial_10_Bold, Arial_11, Arial_11_Bold, Arial_12, 13, 14, 1204 | // 16, 18, 20, 24, 28, 32, 40, 48, 60, 72, 96 1205 | 1206 | // 1207 | void TeensyUserInterface::lcdSetFont(const ui_font &font) 1208 | 1209 | 1210 | // 1211 | // set the foreground color for the "print" functions 1212 | // Enter: color = 16 bit color, bit format: rrrrrggggggbbbbb 1213 | // 1214 | void TeensyUserInterface::lcdSetFontColor(uint16_t color) 1215 | 1216 | 1217 | // 1218 | // print a string to the LCD display 1219 | // Enter: s -> a null terminated string 1220 | // 1221 | void TeensyUserInterface::lcdPrint(char *s) 1222 | 1223 | 1224 | // 1225 | // print a signed int at location of the cursor 1226 | // Enter: n = signed number to print 1227 | // 1228 | void TeensyUserInterface::lcdPrint(int n) 1229 | 1230 | 1231 | // 1232 | // print a float or double at location of the cursor 1233 | // Enter: n = signed number to print 1234 | // digitsRightOfDecimal = # digits to display right of decimal 1235 | // point (optional) 1236 | // 1237 | void TeensyUserInterface::lcdPrint(double n, int digitsRightOfDecimal) 1238 | 1239 | 1240 | // 1241 | // print a string to the LCD, right justified at the cursor 1242 | // Enter: s -> string to print 1243 | // 1244 | void TeensyUserInterface::lcdPrintRightJustified(char *s) 1245 | 1246 | 1247 | // 1248 | // print a signed int on the LCD, right justify at the cursor 1249 | // Enter: n = signed number to print 1250 | // 1251 | void TeensyUserInterface::lcdPrintRightJustified(int n) 1252 | 1253 | 1254 | // 1255 | // print a float on the LCD, right justify at the cursor 1256 | // Enter: n = signed number to print 1257 | // digitsRightOfDecimal = # digits to display right of decimal 1258 | // point (optional) 1259 | // 1260 | void TeensyUserInterface::lcdPrintRightJustified(double n, int digitsRightOfDecimal) 1261 | 1262 | 1263 | // 1264 | // print a string to the LCD, centered side-to-side at the cursor 1265 | // Enter: s -> string to print 1266 | // 1267 | void TeensyUserInterface::lcdPrintCentered(char *s) 1268 | 1269 | 1270 | // 1271 | // print a signed int to the LCD, centered side-to-side at the cursor 1272 | // Enter: n = signed number to print 1273 | // 1274 | void TeensyUserInterface::lcdPrintCentered(int n) 1275 | 1276 | 1277 | // 1278 | // print a float to the LCD, centered side-to-side at the cursor 1279 | // digitsRightOfDecimal = # digits to display right of decimal 1280 | // point (optional) 1281 | // Enter: n = signed number to print 1282 | // 1283 | void TeensyUserInterface::lcdPrintCentered(double n, int digitsRightOfDecimal) 1284 | 1285 | 1286 | // 1287 | // print one ASCII charater to the LCD, at location of the cursor 1288 | // Enter: c = character to display 1289 | // 1290 | void TeensyUserInterface::lcdPrintCharacter(byte character) 1291 | 1292 | 1293 | // 1294 | // get the width of a string in pixels 1295 | // 1296 | int TeensyUserInterface::lcdStringWidthInPixels(char *s) 1297 | 1298 | 1299 | // 1300 | // get the height of the selected font in pixels, excluding decenders 1301 | // 1302 | int TeensyUserInterface::lcdGetFontHeightWithoutDecenders(void) 1303 | 1304 | 1305 | // 1306 | // get the height of the selected font in pixels, including decenders and 1307 | // line spacing 1308 | // 1309 | int TeensyUserInterface::lcdGetFontHeightWithDecentersAndLineSpacing(void) 1310 | 1311 | 1312 | // 1313 | // set the cursor coords in pixels 1314 | // Enter: x 0 = left most pixel 1315 | // y 0 = left most pixel 1316 | // 1317 | void TeensyUserInterface::lcdSetCursorXY(int x, int y) 1318 | 1319 | 1320 | // 1321 | // get the cursor coords in pixels 1322 | // Enter: x -> storage to return X, 0 = left most pixel 1323 | // y -> storage to return Y, 0 = left most pixel 1324 | // 1325 | void TeensyUserInterface::lcdGetCursorXY(int *x, int *y) 1326 | 1327 | 1328 | // 1329 | // make a RGB565 color 1330 | // Enter: red (0 to 31) 1331 | // green (0 to 63) Note: 0x20 green has same intensity as 0x10 blue 1332 | // blue (0 to 31) 1333 | // Exit: 16 bit color returned, bit format: rrrrrggggggbbbbb 1334 | // 1335 | uint16_t TeensyUserInterface::lcdMakeColor(int red, int green, int blue) 1336 | 1337 | ``` 1338 | 1339 | 1340 | 1341 | ### Reading/writing configuration values: 1342 | 1343 | ``` 1344 | // 1345 | // write a configuration byte (8 bit) to the EEPROM 1346 | // Enter: EEPromAddress = address in EEPROM to write 1347 | // value = 8 bit value to write to EEPROM 1348 | // note: 2 bytes of EEPROM space are used 1349 | // 1350 | void TeensyUserInterface::writeConfigurationByte(int EEPromAddress, byte value) 1351 | 1352 | 1353 | // 1354 | // read a configuration byte (8 bit) from the EEPROM 1355 | // Enter: EEPromAddress = address in EEPROM to read from 1356 | // defaultValue = default value to return if value has never been 1357 | // written to the EEPROM 1358 | // note: 2 bytes of EEPROM space are used 1359 | // Exit: byte value from EEPROM (or default value) returned 1360 | // 1361 | byte TeensyUserInterface::readConfigurationByte(int EEPromAddress, 1362 | byte defaultValue) 1363 | 1364 | 1365 | // 1366 | // write a configuration short (16 bit) to the EEPROM 1367 | // Enter: EEPromAddress = address in EEPROM to write 1368 | // value = 16 bit value to write to EEPROM 1369 | // note: 3 bytes of EEPROM space are used 1370 | // 1371 | void TeensyUserInterface::writeConfigurationShort(int EEPromAddress, short value) 1372 | 1373 | 1374 | // 1375 | // read a configuration short (16 bit) from the EEPROM 1376 | // Enter: EEPromAddress = address in EEPROM to read from 1377 | // defaultValue = default value to return if value has never been 1378 | // written to the EEPROM 1379 | // note: 3 bytes of EEPROM space are used 1380 | // Exit: short value from EEPROM (or default value) returned 1381 | // 1382 | short TeensyUserInterface::readConfigurationShort(int EEPromAddress, 1383 | short defaultValue) 1384 | 1385 | 1386 | // 1387 | // write a configuration int (32 bit) to the EEPROM 1388 | // Enter: EEPromAddress = address in EEPROM to write 1389 | // value = 32 bit value to write to EEPROM 1390 | // note: 5 bytes of EEPROM space are used 1391 | // 1392 | void TeensyUserInterface::writeConfigurationInt(int EEPromAddress, int value) 1393 | 1394 | 1395 | // 1396 | // read a configuration int (32 bit) from the EEPROM 1397 | // Enter: EEPromAddress = address in EEPROM to read from 1398 | // defaultValue = default value to return if value has never been 1399 | // written to the EEPROM 1400 | // note: 5 bytes of EEPROM space are used 1401 | // Exit: long value from EEPROM (or default value) returned 1402 | // 1403 | int TeensyUserInterface::readConfigurationInt(int EEPromAddress, int defaultValue) 1404 | 1405 | 1406 | // 1407 | // write a configuration float (32 bit) to the EEPROM 1408 | // Enter: EEPromAddress = address in EEPROM to write 1409 | // value = 32 bit float to write to EEPROM 1410 | // note: 5 bytes of EEPROM space are used 1411 | // 1412 | void TeensyUserInterface::writeConfigurationFloat(int EEPromAddress, float value) 1413 | 1414 | 1415 | // 1416 | // read a configuration float (32 bit) from the EEPROM 1417 | // Enter: EEPromAddress = address in EEPROM to read from 1418 | // defaultValue = default value to return if value has never been 1419 | // written to the EEPROM 1420 | // note: 5 bytes of EEPROM space are used 1421 | // Exit: float value from EEPROM (or default value) returned 1422 | // 1423 | float TeensyUserInterface::readConfigurationFloat(int EEPromAddress, 1424 | float defaultValue) 1425 | ``` 1426 | 1427 | 1428 | 1429 | Copyright (c) 2022 S. Reifel & Co. - Licensed under the MIT license. 1430 | 1431 | --------------------------------------------------------------------------------