Display SSD1306 for ESP32
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

570 lines
19 KiB

  1. #define MAX_OPTIONS 10 //Maximum number of options for each menu
  2. #define MAX_MENUS 3
  3. #define MAX_GRAPH 3
  4. #define __DEBUG__
  5. #include <Arduino.h>
  6. #include <SPI.h>
  7. #include <Wire.h>
  8. #include <Adafruit_GFX.h>
  9. #include <Adafruit_SSD1306.h>
  10. #define DISP_WIDTH 128 // OLED display width
  11. #define DISP_HEIGHT 64 // OLED display height
  12. Adafruit_SSD1306 display(DISP_WIDTH, DISP_HEIGHT, &Wire, -1);
  13. int i = 0;
  14. class Option{
  15. private:
  16. int sizex;
  17. int sizey;
  18. String content;
  19. int pos;
  20. int textSpacing;
  21. bool fill = false;
  22. bool disp = false;
  23. bool destinationTypeMenu;
  24. int destinationIndex;
  25. public:
  26. //Option(){}
  27. void configure(String content, int sizex, int sizey, int pos, bool destinationTypeMenu, int destinationIndex){
  28. this->sizex = sizex;
  29. this->sizey = sizey;
  30. this->content = content;
  31. this->pos = pos;
  32. this->destinationTypeMenu = destinationTypeMenu;
  33. this->destinationIndex = destinationIndex;
  34. this->disp = true;
  35. this->textSpacing = ((sizey - 7)/2) + 7;
  36. }
  37. bool getDestinationTypeMenu(){
  38. bool destinationType = this->destinationTypeMenu;
  39. return destinationType;
  40. }
  41. int getDestinationIndex(){
  42. int destinationIndex = this->destinationIndex;
  43. return destinationIndex;
  44. }
  45. void drawopt(int page, int pos, int optPPage){
  46. if(this->disp){
  47. if(this->pos == pos){
  48. display.fillRect(0, (this->sizey)*(this->pos) + 1 - (page*optPPage*this->sizey), this->sizex, this->sizey, WHITE);
  49. display.setTextColor(SSD1306_BLACK);
  50. display.setCursor(5, (this->sizey)*(this->pos + 1) - (page*optPPage*this->sizey) - this->textSpacing);
  51. display.print(this->content);
  52. display.setTextColor(SSD1306_WHITE);
  53. }
  54. else{
  55. display.drawRect(0, (this->sizey)*(this->pos) + 1 - (page*optPPage*this->sizey), this->sizex, this->sizey, WHITE);
  56. display.setCursor(5, (this->sizey)*(this->pos + 1) - (page*optPPage*this->sizey) - this->textSpacing);
  57. display.print(this->content);
  58. }
  59. }
  60. }
  61. };
  62. class Menu{ //ContentTypeMenu true, it is a menu
  63. private:
  64. int sizex;
  65. int sizey; //Y size of each option in the menu
  66. int options = 0; //This indicates the number of options created
  67. int pos = 0; //This indicates the position of the cursor
  68. int page = 0; //If the menu is too long, this indicates the page that is being displayed
  69. Option opt[MAX_OPTIONS];
  70. int previousScreen = 0;
  71. int optPPage;
  72. bool previousContentTypeMenu = true;
  73. public:
  74. void configure(int sizex, int sizey){
  75. this->sizex = sizex;
  76. this->sizey = sizey;
  77. this->optPPage = DISP_HEIGHT / this->sizey;
  78. }
  79. void createOption(String content, bool destinationTypeMenu, int destinationIndex){
  80. this->opt[this->options].configure(content, this->sizex, this->sizey, this->options++, destinationTypeMenu, destinationIndex);
  81. }
  82. bool extractDestinationTypeMenu(){
  83. bool destinationTypeMenu = this->opt[this->pos].getDestinationTypeMenu();
  84. return destinationTypeMenu;
  85. }
  86. int extractDestinationIndex(){
  87. int destinationIndex = this->opt[this->pos].getDestinationIndex();
  88. return destinationIndex;
  89. }
  90. void drawMenu(){
  91. display.clearDisplay();
  92. this->page = pos/this->optPPage;
  93. for(int i = 0; i < options; i++){
  94. this->opt[i].drawopt(this->page, this->pos, this->optPPage);
  95. }
  96. display.display();
  97. }
  98. void increasePos(){
  99. this->pos++;
  100. }
  101. void decreasePos(){
  102. this->pos--;
  103. }
  104. void setPreviousScreen(int prev){
  105. this->previousScreen = prev;
  106. }
  107. void setPreviousContentTypeMenu(bool prev){
  108. this->previousContentTypeMenu = prev;
  109. }
  110. int getPreviousScreen(){
  111. int prev = this->previousScreen;
  112. return prev;
  113. }
  114. bool getPreviousContentTypeMenu(){
  115. bool prev = this->previousContentTypeMenu;
  116. return prev;
  117. }
  118. };
  119. class Graph{ //ContentTypeMenu false, it is not a menu
  120. private:
  121. String title;
  122. char graphType; //'a' Vertical Bar, 'b' Horizontal Bar, 'c' Cartesian Graph
  123. //Assign whatever value in "configure(..." if a parameter is not required for the specified graphType
  124. double value; //For: Vertical Bar Horizontal Bar Cartesian
  125. double xpos; //For: Vertical Bar Horizontal Bar Cartesian
  126. double ypos; //For: Vertical Bar Horizontal Bar Cartesian
  127. double height; //For: Vertical Bar Horizontal Bar Cartesian
  128. double width; //For: Vertical Bar Horizontal Bar Cartesian
  129. double yminimum; //For: Vertical Bar Cartesian
  130. double ymaximum; //For: Vertical Bar Cartesian
  131. double xminimum; //For: Horizontal Bar Cartesian
  132. double xmaximum; //For: Horizontal Bar Cartesian
  133. double yStepSize; //For: Vertical Bar Cartesian
  134. double xStepSize; //For: Horizontal Bar Cartesian
  135. double digit; //For: Vertical Bar Horizontal Bar Cartesian
  136. double x;
  137. double yrange;
  138. double xrange;
  139. double ox;
  140. double oy;
  141. double count;
  142. double graphScale;
  143. bool redraw = true;
  144. int previousScreen = 0;
  145. bool previousContentTypeMenu = true;
  146. public:
  147. void configure(String title, char graphType, double xpos, double ypos, double width, double height,
  148. double yminimum, double ymaximum, double xminimum, double xmaximum, double yStepSize, double xStepSize, double digit){
  149. this->title = title;
  150. this->graphType = graphType;
  151. this->yminimum = yminimum;
  152. this->ymaximum = ymaximum;
  153. this->xminimum = xminimum;
  154. this->count = xminimum;
  155. this->xmaximum = xmaximum;
  156. this->height = height;
  157. this->width = width;
  158. this->yStepSize = yStepSize;
  159. this->xStepSize = xStepSize;
  160. this->digit = digit;
  161. this->xpos = xpos;
  162. this->ypos = ypos;
  163. switch(graphType){
  164. case 'a':
  165. this->yrange = ymaximum - yminimum;
  166. this->graphScale = (yStepSize) * (height / this->yrange) - .001; //Adjusts the scale of the graph, according to the range and the size of the step
  167. break;
  168. case 'b':
  169. this->xrange = xmaximum - xminimum;
  170. this->graphScale = (xStepSize) * (width / this->xrange) - .001; //Adjusts the scale of the graph, according to the range and the size of the step
  171. break;
  172. case 'c':
  173. this->yrange = ymaximum - yminimum;
  174. this->xrange = xmaximum - xminimum;
  175. break;
  176. }
  177. }
  178. void drawGraph(){
  179. double level, data, i;
  180. switch(graphType){
  181. case 'a':
  182. double my;
  183. if (this->redraw) {
  184. display.clearDisplay();
  185. this->redraw = false;
  186. display.fillRect(0, 0, 127 , 14, SSD1306_WHITE);
  187. display.setTextColor(SSD1306_BLACK, SSD1306_WHITE);
  188. display.setTextSize(1);
  189. display.setCursor(2, 4);
  190. display.println(this->title);
  191. for (i = 0; i <= this->height; i += this->graphScale) {
  192. my = this->ypos - this->height + i;
  193. display.drawFastHLine(this->xpos + this->width + 1, my, 5, SSD1306_WHITE);
  194. // draw lables
  195. display.setTextSize(1);
  196. display.setTextColor(SSD1306_WHITE, SSD1306_BLACK);
  197. display.setCursor(this->xpos + this->width + 12, my - 3 );
  198. data = this->ymaximum - ( i * (this->yStepSize / this->graphScale));
  199. display.print(data, this->digit);
  200. }
  201. }
  202. // compute level of bar graph that is scaled to the height and the hi and low vals
  203. // this is needed to accompdate for +/- range
  204. level = (this->height * (((this->value - this->yminimum) / (this->yrange))));
  205. // draw the bar graph
  206. // write a upper and lower bar to minimize flicker cause by blanking out bar and redraw on update
  207. display.drawRect(this->xpos, this->ypos - this->height, this->width, this->height, SSD1306_WHITE);
  208. display.fillRect(this->xpos, this->ypos - this->height, this->width, this->height - level, SSD1306_BLACK);
  209. display.drawRect(this->xpos, this->ypos - this->height, this->width, this->height, SSD1306_WHITE);
  210. display.fillRect(this->xpos, this->ypos - level, this->width, level, SSD1306_WHITE);
  211. // up until now print sends data to a video buffer NOT the screen
  212. // this call sends the data to the screen
  213. display.display();
  214. break;
  215. case 'b':
  216. if (this->redraw) {
  217. display.clearDisplay();
  218. this->redraw = false;
  219. display.fillRect(0, 0, 127 , 16, SSD1306_WHITE);
  220. display.setTextColor(SSD1306_BLACK, SSD1306_WHITE);
  221. display.setTextSize(1);
  222. display.setCursor(2, 4);
  223. display.println(this->title);
  224. // draw the text
  225. for (i = 0; i <= this->width; i += this->graphScale) {
  226. display.drawFastVLine(i + this->xpos , this->ypos , 5, SSD1306_WHITE);
  227. // draw lables
  228. display.setTextSize(1);
  229. display.setTextColor(SSD1306_WHITE, SSD1306_BLACK);
  230. display.setCursor(i + this->xpos , this->ypos + 10);
  231. // addling a small value to eliminate round off errors
  232. // this val may need to be adjusted
  233. data = ( i * (this->xStepSize / this->graphScale)) + this->xminimum + 0.00001;
  234. display.print(data, this->digit);
  235. }
  236. }
  237. // compute level of bar graph that is scaled to the width and the hi and low vals
  238. // this is needed to accompdate for +/- range capability
  239. // draw the bar graph
  240. // write a upper and lower bar to minimize flicker cause by blanking out bar and redraw on update
  241. level = (this->width * (((this->value - this->xminimum) / (this->xmaximum - this->xminimum))));
  242. display.fillRect(this->xpos + level, this->ypos - this->height, this->width - level, this->height, SSD1306_BLACK);
  243. display.drawRect(this->xpos, this->ypos - this->height, this->width, this->height, SSD1306_WHITE);
  244. display.fillRect(this->xpos, this->ypos - this->height, level, this->height, SSD1306_WHITE);
  245. // up until now print sends data to a video buffer NOT the screen
  246. // this call sends the data to the screen
  247. display.display();
  248. break;
  249. case 'c':
  250. double temp;
  251. if (this->redraw == true) {
  252. this->redraw = false;
  253. display.clearDisplay();
  254. display.fillRect(0, 0, 127 , 16, SSD1306_WHITE);
  255. display.setTextColor(SSD1306_BLACK, SSD1306_WHITE);
  256. display.setTextSize(1);
  257. display.setCursor(2, 4);
  258. display.println(title);
  259. this->ox = (this->count - this->xminimum) * (this->width) / (this->xrange) + this->xpos;
  260. this->oy = (this->value - this->yminimum) * (- this->height) / (this->yrange) + this->ypos;
  261. // draw y scale
  262. display.setTextSize(1);
  263. display.setTextColor(SSD1306_WHITE, SSD1306_BLACK);
  264. for ( i = this->yminimum; i <= this->ymaximum; i += this->yStepSize) {
  265. // compute the transform
  266. // note my transform funcition is the same as the map function, except the map uses long and we need doubles
  267. temp = (i - this->yminimum) * (- this->height) / (this->ymaximum - this->yminimum) + this->ypos;
  268. if (i == 0) {
  269. display.drawFastHLine(this->xpos - 3, temp, this->width + 3, SSD1306_WHITE);
  270. }
  271. else {
  272. display.drawFastHLine(this->xpos - 3, temp, 3, SSD1306_WHITE);
  273. }
  274. display.setCursor(this->xpos - 27, temp - 3);
  275. display.println(i, this->digit);
  276. }
  277. // draw x scale
  278. for (i = this->xminimum; i <= this->xmaximum; i += this->xStepSize) {
  279. // compute the transform
  280. display.setTextSize(1);
  281. display.setTextColor(SSD1306_WHITE, SSD1306_BLACK);
  282. temp = (i - this->xminimum) * (this->width) / (this->xrange) + this->xpos;
  283. if (i == 0) {
  284. display.drawFastVLine(temp, this->ypos - this->height, this->height + 3, SSD1306_WHITE);
  285. }
  286. else {
  287. display.drawFastVLine(temp, this->ypos, 3, SSD1306_WHITE);
  288. }
  289. display.setCursor(temp, this->ypos + 6);
  290. display.println(i, this->digit);
  291. }
  292. }
  293. // graph drawn now plot the data
  294. // the entire plotting code are these few lines...
  295. this->x = (this->count - this->xminimum) * (this->width) / (this->xrange) + this->xpos;
  296. this->value = (this->value - this->yminimum) * (- this->height) / (this->yrange) + this->ypos;
  297. display.drawLine(this->ox, this->oy, this->x, this->value, SSD1306_WHITE);
  298. display.drawLine(this->ox, this->oy - 1, this->x, this->value - 1, SSD1306_WHITE);
  299. this->ox = this->x;
  300. this->oy = this->value;
  301. // up until now print sends data to a video buffer NOT the screen
  302. // this call sends the data to the screen
  303. display.display();
  304. this->count += 1;
  305. if(this->ox >= (this->xpos + this->width)){
  306. this->redraw = true;
  307. this->count = xminimum;
  308. }
  309. }
  310. }
  311. void redrawFlag(){
  312. this->redraw = true;
  313. }
  314. void setPreviousScreen(int prev){
  315. this->previousScreen = prev;
  316. }
  317. void setPreviousContentTypeMenu(bool prev){
  318. this->previousContentTypeMenu = prev;
  319. }
  320. int getPreviousScreen(){
  321. int prev = this->previousScreen;
  322. return prev;
  323. }
  324. bool getPreviousContentTypeMenu(){
  325. bool prev = this->previousContentTypeMenu;
  326. return prev;
  327. }
  328. void assignValue(double value){
  329. this->value = value;
  330. }
  331. void reset(){
  332. this->x = 0;
  333. }
  334. };
  335. class Screen{
  336. private:
  337. Menu menu[MAX_MENUS];
  338. Graph graph[MAX_GRAPH];
  339. int counterM = 0;
  340. int counterG = 0;
  341. int currentScreen = 0;
  342. bool redraw = true;
  343. bool contentTypeMenu = true;
  344. public:
  345. void configure(bool fullsetting){
  346. if(fullsetting){
  347. //Adafruit_SSD1306 display(DISP_WIDTH, DISP_HEIGHT, &Wire, -1);
  348. Serial.begin(115200);
  349. if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
  350. #ifdef __DEBUG__
  351. Serial.println("Display not found!");
  352. #endif
  353. while (true);
  354. }
  355. }
  356. display.clearDisplay();
  357. // Text size
  358. display.setTextSize(2);
  359. // Text color
  360. display.setTextColor(SSD1306_WHITE);
  361. // Text position
  362. display.setCursor(0, 20);
  363. display.println("Bienvenido");
  364. display.setTextSize(1);
  365. display.display();
  366. delay(5000);
  367. }
  368. void createMenu(int sizex, int sizey){
  369. this->menu[counterM].configure(sizex, sizey);
  370. this->counterM++;
  371. }
  372. void createOption(int menuIndex, String content, bool destinationTypeMenu, int destinationIndex){
  373. this->menu[menuIndex].createOption(content, destinationTypeMenu, destinationIndex);
  374. }
  375. void createVGraph(String title, double xpos, double ypos, double width, double height,
  376. double yminimum, double ymaximum, double yStepSize, double digit){
  377. this->graph[counterG].configure(title, 'a', xpos, ypos, width, height, yminimum, ymaximum, 0, 0, yStepSize, 0, digit);
  378. this->counterG++;
  379. }
  380. void createHGraph(String title, double xpos, double ypos, double width, double height,
  381. double xminimum, double xmaximum, double xStepSize, double digit){
  382. this->graph[counterG].configure(title, 'b', xpos, ypos, width, height, 0, 0, xminimum, xmaximum, 0, xStepSize, digit);
  383. counterG++;
  384. }
  385. void createCGraph(String title, double xpos, double ypos, double width, double height,
  386. double yminimum, double ymaximum, double xminimum, double xmaximum, double yStepSize, double xStepSize, double digit){
  387. this->graph[counterG].configure(title, 'c', xpos, ypos, width, height, yminimum, ymaximum, xminimum, xmaximum, yStepSize, xStepSize, digit);
  388. counterG++;
  389. }
  390. /*
  391. void redrawFlag(){
  392. this->redraw = true;
  393. }
  394. */
  395. void graphAssignValue(int graphIndex, double value){
  396. this->graph[graphIndex].assignValue(value);
  397. this->redraw = true;
  398. }
  399. void control(){
  400. if (redraw){
  401. if (contentTypeMenu){
  402. menu[currentScreen].drawMenu();
  403. }
  404. else{
  405. graph[currentScreen].drawGraph();
  406. }
  407. this->redraw = false;
  408. }
  409. }
  410. void increasePos(){
  411. this->menu[this->currentScreen].increasePos();
  412. }
  413. void decreasePos(){
  414. this->menu[this->currentScreen].decreasePos();
  415. }
  416. void goTo(){
  417. if(this->contentTypeMenu){
  418. int newScreen = this->menu[this->currentScreen].extractDestinationIndex();
  419. bool newContentTypeMenu = this->menu[this->currentScreen].extractDestinationTypeMenu();
  420. if (newContentTypeMenu){
  421. this->menu[newScreen].setPreviousScreen(this->currentScreen);
  422. this->menu[newScreen].setPreviousContentTypeMenu(this->contentTypeMenu);
  423. }
  424. else{
  425. this->graph[newScreen].setPreviousScreen(this->currentScreen);
  426. this->graph[newScreen].setPreviousContentTypeMenu(this->contentTypeMenu);
  427. this->graph[newScreen].reset();
  428. this->graph[newScreen].redrawFlag();
  429. }
  430. this->contentTypeMenu = newContentTypeMenu;
  431. this->currentScreen = newScreen;
  432. this->redraw = true;
  433. }
  434. }
  435. void goBack(){
  436. if (contentTypeMenu){
  437. //Gets indexes from previous screen saved in actual screen if it is a menu, and sets them as the current indexes
  438. this->currentScreen = this->menu[this->currentScreen].getPreviousScreen();
  439. this->contentTypeMenu = this->menu[this->currentScreen].getPreviousContentTypeMenu();
  440. }
  441. else{
  442. //Gets indexes from previous screen saved in actual screen if it is a graph, and sets them as the current indexes
  443. this->currentScreen = this->graph[this->currentScreen].getPreviousScreen();
  444. this->contentTypeMenu = this->graph[this->currentScreen].getPreviousContentTypeMenu();
  445. }
  446. }
  447. };
  448. Screen screen;
  449. void setup(){
  450. screen.configure(true);
  451. screen.createMenu(128, 13); //Menu 0
  452. screen.createMenu(128, 13); //Menu 1
  453. /*String title, char graphType, double xpos, double ypos, double width, double height,
  454. double yminimum, double ymaximum, double xminimum, double xmaximum, double yStepSize, double xStepSize, double digit*/
  455. screen.createVGraph("Grafica 1", 25, 60, 40, 40, 0, 100, 10, 0); //Graph 0
  456. screen.createHGraph("Grafica 2", 10, 40, 100, 20, 0, 100, 10, 0); //Graph 1
  457. screen.createCGraph("Grafica 3", 30, 50, 75, 30, 0, 100, 0, 100, 25, 25, 0); //Graph 2
  458. screen.createOption(0, "Vertical graph", false, 0);
  459. //Creates the first option in Menu 0, directing to a graph (Menu Content = false), 0 (Graph 0)
  460. screen.createOption(0, "Horizontal graph", false, 1);
  461. screen.createOption(0, "Cartesian graph", false, 2);
  462. screen.createOption(0, "Extra option", true, 1);
  463. screen.createOption(1, "Test", false, 3);
  464. screen.createOption(1, "Working?", false, 4);
  465. // screen.increasePos();
  466. // screen.increasePos();
  467. // screen.goTo();
  468. // screen.graphAssignValue(2, 50);
  469. // screen.goBack();
  470. // screen.increasePos();
  471. // screen.goTo();
  472. // screen.goBack();
  473. // screen.decreasePos();
  474. }
  475. void loop(){
  476. screen.control(); //Controls the screen and redraws if needed
  477. delay(10);
  478. if(i <= 100){
  479. screen.graphAssignValue(1, i); //Assigning a demo value to Graph 1
  480. screen.graphAssignValue(2, i); //Assigning a demo value to Graph 2
  481. i++;
  482. }
  483. else
  484. i = 0;
  485. }