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.

694 lines
22 KiB

  1. #include <SPI.h>
  2. #include <Wire.h>
  3. #include <Adafruit_GFX.h>
  4. #include <Adafruit_SSD1306.h>
  5. #define __DEBUG__
  6. #define MAX_OPTIONS 10 //Maximum number of options for each menu
  7. #define MAX_MENUS 3
  8. #define MAX_GRAPH 3
  9. #define DISP_WIDTH 128 // OLED display width
  10. #define DISP_HEIGHT 64 // OLED display height
  11. #define REFRESH 10 //Refresh time in ms
  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. int destinationType;
  24. int destinationIndex;
  25. public:
  26. //Option(){}
  27. void configure(String content, int sizex, int sizey, int pos, int destinationType, int destinationIndex){
  28. this->sizex = sizex;
  29. this->sizey = sizey;
  30. this->content = content;
  31. this->pos = pos;
  32. this->destinationType = destinationType;
  33. this->destinationIndex = destinationIndex;
  34. this->disp = true;
  35. this->textSpacing = ((sizey - 7)/2) + 7;
  36. }
  37. int getDestinationType(){
  38. int destinationType = this->destinationType;
  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 optPPage;
  71. int previousScreen = 0;
  72. int previousContentType = 0;
  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. int extractDestinationType(){
  83. int destinationType = this->opt[this->pos].getDestinationType();
  84. return destinationType;
  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. int extractPos(){
  99. return(this->pos);
  100. }
  101. int extractOptNumber(){
  102. return(this->options);
  103. }
  104. void increasePos(){
  105. this->pos++;
  106. }
  107. void decreasePos(){
  108. this->pos--;
  109. }
  110. void setPreviousScreen(int prev){
  111. this->previousScreen = prev;
  112. }
  113. void setPreviousContentType(int prev){
  114. this->previousContentType = prev;
  115. }
  116. int getPreviousScreen(){
  117. int prev = this->previousScreen;
  118. return prev;
  119. }
  120. int getPreviousContentType(){
  121. int prev = this->previousContentType;
  122. return prev;
  123. }
  124. };
  125. class Graph{ //ContentTypeMenu false, it is not a menu
  126. private:
  127. String title;
  128. char graphType; //'a' Vertical Bar, 'b' Horizontal Bar, 'c' Cartesian Graph
  129. //Assign whatever value in "configure(..." if a parameter is not required for the specified graphType
  130. double value; //For: Vertical Bar Horizontal Bar Cartesian
  131. double xpos; //For: Vertical Bar Horizontal Bar Cartesian
  132. double ypos; //For: Vertical Bar Horizontal Bar Cartesian
  133. double height; //For: Vertical Bar Horizontal Bar Cartesian
  134. double width; //For: Vertical Bar Horizontal Bar Cartesian
  135. double yminimum; //For: Vertical Bar Cartesian
  136. double ymaximum; //For: Vertical Bar Cartesian
  137. double xminimum; //For: Horizontal Bar Cartesian
  138. double xmaximum; //For: Horizontal Bar Cartesian
  139. double yStepSize; //For: Vertical Bar Cartesian
  140. double xStepSize; //For: Horizontal Bar Cartesian
  141. double digit; //For: Vertical Bar Horizontal Bar Cartesian
  142. double x;
  143. double yrange;
  144. double xrange;
  145. double ox;
  146. double oy;
  147. double count;
  148. double graphScale;
  149. bool redraw = true;
  150. int previousScreen = 0;
  151. int previousContentType = 0;
  152. public:
  153. void configure(String title, char graphType, double xpos, double ypos, double width, double height,
  154. double yminimum, double ymaximum, double xminimum, double xmaximum, double yStepSize, double xStepSize, double digit){
  155. this->title = title;
  156. this->graphType = graphType;
  157. this->yminimum = yminimum;
  158. this->ymaximum = ymaximum;
  159. this->xminimum = xminimum;
  160. this->count = xminimum;
  161. this->xmaximum = xmaximum;
  162. this->height = height;
  163. this->width = width;
  164. this->yStepSize = yStepSize;
  165. this->xStepSize = xStepSize;
  166. this->digit = digit;
  167. this->xpos = xpos;
  168. this->ypos = ypos;
  169. switch(graphType){
  170. case 'a':
  171. this->yrange = ymaximum - yminimum;
  172. this->graphScale = (yStepSize) * (height / this->yrange) - .001; //Adjusts the scale of the graph, according to the range and the size of the step
  173. break;
  174. case 'b':
  175. this->xrange = xmaximum - xminimum;
  176. this->graphScale = (xStepSize) * (width / this->xrange) - .001; //Adjusts the scale of the graph, according to the range and the size of the step
  177. break;
  178. case 'c':
  179. this->yrange = ymaximum - yminimum;
  180. this->xrange = xmaximum - xminimum;
  181. break;
  182. }
  183. }
  184. void drawGraph(){
  185. double level, data, i;
  186. switch(graphType){
  187. case 'a':
  188. double my;
  189. if (this->redraw) {
  190. display.clearDisplay();
  191. this->redraw = false;
  192. display.fillRect(0, 0, 127 , 14, SSD1306_WHITE);
  193. display.setTextColor(SSD1306_BLACK, SSD1306_WHITE);
  194. display.setTextSize(1);
  195. display.setCursor(2, 4);
  196. display.println(this->title);
  197. for (i = 0; i <= this->height; i += this->graphScale) {
  198. my = this->ypos - this->height + i;
  199. display.drawFastHLine(this->xpos + this->width + 1, my, 5, SSD1306_WHITE);
  200. // draw lables
  201. display.setTextSize(1);
  202. display.setTextColor(SSD1306_WHITE, SSD1306_BLACK);
  203. display.setCursor(this->xpos + this->width + 12, my - 3 );
  204. data = this->ymaximum - ( i * (this->yStepSize / this->graphScale));
  205. display.print(data, this->digit);
  206. }
  207. }
  208. // compute level of bar graph that is scaled to the height and the hi and low vals
  209. // this is needed to accompdate for +/- range
  210. level = (this->height * (((this->value - this->yminimum) / (this->yrange))));
  211. // draw the bar graph
  212. // write a upper and lower bar to minimize flicker cause by blanking out bar and redraw on update
  213. display.drawRect(this->xpos, this->ypos - this->height, this->width, this->height, SSD1306_WHITE);
  214. display.fillRect(this->xpos, this->ypos - this->height, this->width, this->height - level, SSD1306_BLACK);
  215. display.drawRect(this->xpos, this->ypos - this->height, this->width, this->height, SSD1306_WHITE);
  216. display.fillRect(this->xpos, this->ypos - level, this->width, level, SSD1306_WHITE);
  217. // up until now print sends data to a video buffer NOT the screen
  218. // this call sends the data to the screen
  219. display.display();
  220. break;
  221. case 'b':
  222. if (this->redraw) {
  223. display.clearDisplay();
  224. this->redraw = false;
  225. display.fillRect(0, 0, 127 , 16, SSD1306_WHITE);
  226. display.setTextColor(SSD1306_BLACK, SSD1306_WHITE);
  227. display.setTextSize(1);
  228. display.setCursor(2, 4);
  229. display.println(this->title);
  230. // draw the text
  231. for (i = 0; i <= this->width; i += this->graphScale) {
  232. display.drawFastVLine(i + this->xpos , this->ypos , 5, SSD1306_WHITE);
  233. // draw lables
  234. display.setTextSize(1);
  235. display.setTextColor(SSD1306_WHITE, SSD1306_BLACK);
  236. display.setCursor(i + this->xpos , this->ypos + 10);
  237. // addling a small value to eliminate round off errors
  238. // this val may need to be adjusted
  239. data = ( i * (this->xStepSize / this->graphScale)) + this->xminimum + 0.00001;
  240. display.print(data, this->digit);
  241. }
  242. }
  243. // compute level of bar graph that is scaled to the width and the hi and low vals
  244. // this is needed to accompdate for +/- range capability
  245. // draw the bar graph
  246. // write a upper and lower bar to minimize flicker cause by blanking out bar and redraw on update
  247. level = (this->width * (((this->value - this->xminimum) / (this->xmaximum - this->xminimum))));
  248. display.fillRect(this->xpos + level, this->ypos - this->height, this->width - level, this->height, SSD1306_BLACK);
  249. display.drawRect(this->xpos, this->ypos - this->height, this->width, this->height, SSD1306_WHITE);
  250. display.fillRect(this->xpos, this->ypos - this->height, level, this->height, SSD1306_WHITE);
  251. // up until now print sends data to a video buffer NOT the screen
  252. // this call sends the data to the screen
  253. display.display();
  254. break;
  255. case 'c':
  256. double temp;
  257. if (this->redraw == true) {
  258. this->redraw = false;
  259. display.clearDisplay();
  260. display.fillRect(0, 0, 127 , 16, SSD1306_WHITE);
  261. display.setTextColor(SSD1306_BLACK, SSD1306_WHITE);
  262. display.setTextSize(1);
  263. display.setCursor(2, 4);
  264. display.println(title);
  265. this->ox = (this->count - this->xminimum) * (this->width) / (this->xrange) + this->xpos;
  266. this->oy = (this->value - this->yminimum) * (- this->height) / (this->yrange) + this->ypos;
  267. // draw y scale
  268. display.setTextSize(1);
  269. display.setTextColor(SSD1306_WHITE, SSD1306_BLACK);
  270. for ( i = this->yminimum; i <= this->ymaximum; i += this->yStepSize) {
  271. // compute the transform
  272. // note my transform funcition is the same as the map function, except the map uses long and we need doubles
  273. temp = (i - this->yminimum) * (- this->height) / (this->ymaximum - this->yminimum) + this->ypos;
  274. if (i == 0) {
  275. display.drawFastHLine(this->xpos - 3, temp, this->width + 3, SSD1306_WHITE);
  276. }
  277. else {
  278. display.drawFastHLine(this->xpos - 3, temp, 3, SSD1306_WHITE);
  279. }
  280. display.setCursor(this->xpos - 27, temp - 3);
  281. display.println(i, this->digit);
  282. }
  283. // draw x scale
  284. for (i = this->xminimum; i <= this->xmaximum; i += this->xStepSize) {
  285. // compute the transform
  286. display.setTextSize(1);
  287. display.setTextColor(SSD1306_WHITE, SSD1306_BLACK);
  288. temp = (i - this->xminimum) * (this->width) / (this->xrange) + this->xpos;
  289. if (i == 0) {
  290. display.drawFastVLine(temp, this->ypos - this->height, this->height + 3, SSD1306_WHITE);
  291. }
  292. else {
  293. display.drawFastVLine(temp, this->ypos, 3, SSD1306_WHITE);
  294. }
  295. display.setCursor(temp, this->ypos + 6);
  296. display.println(i, this->digit);
  297. }
  298. }
  299. // graph drawn now plot the data
  300. // the entire plotting code are these few lines...
  301. this->x = (this->count - this->xminimum) * (this->width) / (this->xrange) + this->xpos;
  302. this->value = (this->value - this->yminimum) * (- this->height) / (this->yrange) + this->ypos;
  303. display.drawLine(this->ox, this->oy, this->x, this->value, SSD1306_WHITE);
  304. display.drawLine(this->ox, this->oy - 1, this->x, this->value - 1, SSD1306_WHITE);
  305. this->ox = this->x;
  306. this->oy = this->value;
  307. // up until now print sends data to a video buffer NOT the screen
  308. // this call sends the data to the screen
  309. display.display();
  310. this->count += 1;
  311. if(this->ox >= (this->xpos + this->width)){
  312. this->redraw = true;
  313. this->count = xminimum;
  314. }
  315. }
  316. }
  317. void redrawFlag(){
  318. this->redraw = true;
  319. }
  320. void setPreviousScreen(int prev){
  321. this->previousScreen = prev;
  322. }
  323. void setPreviousContentType(int prev){
  324. this->previousContentType = prev;
  325. }
  326. int getPreviousScreen(){
  327. int prev = this->previousScreen;
  328. return prev;
  329. }
  330. int getPreviousContentType(){
  331. int prev = this->previousContentType;
  332. return prev;
  333. }
  334. void assignValue(double value){
  335. this->value = value;
  336. }
  337. void reset(){
  338. this->x = 0;
  339. }
  340. };
  341. class Screen{
  342. private:
  343. Menu menu[MAX_MENUS];
  344. Graph graph[MAX_GRAPH];
  345. int counterM = 0;
  346. int counterG = 0;
  347. bool redraw = true;
  348. int currentScreen = 0;
  349. int contentType = 0;
  350. public:
  351. void configure(bool fullsetting){
  352. if(fullsetting){
  353. //Adafruit_SSD1306 display(DISP_WIDTH, DISP_HEIGHT, &Wire, -1);
  354. Serial.begin(115200);
  355. if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
  356. #ifdef __DEBUG__
  357. Serial.println("Display not found!");
  358. #endif
  359. while (true);
  360. }
  361. }
  362. display.clearDisplay();
  363. // Text size
  364. display.setTextSize(2);
  365. // Text color
  366. display.setTextColor(SSD1306_WHITE);
  367. // Text position
  368. display.setCursor(0, 20);
  369. display.println("Bienvenido");
  370. display.setTextSize(1);
  371. display.display();
  372. delay(5000);
  373. }
  374. void createMenu(int sizex, int sizey){
  375. this->menu[counterM].configure(sizex, sizey);
  376. this->counterM++;
  377. }
  378. void createOption(int menuIndex, String content, bool destinationTypeMenu, int destinationIndex){
  379. this->menu[menuIndex].createOption(content, destinationTypeMenu, destinationIndex);
  380. }
  381. void createVGraph(String title, double xpos, double ypos, double width, double height,
  382. double yminimum, double ymaximum, double yStepSize, double digit){
  383. this->graph[counterG].configure(title, 'a', xpos, ypos, width, height, yminimum, ymaximum, 0, 0, yStepSize, 0, digit);
  384. this->counterG++;
  385. }
  386. void createHGraph(String title, double xpos, double ypos, double width, double height,
  387. double xminimum, double xmaximum, double xStepSize, double digit){
  388. this->graph[counterG].configure(title, 'b', xpos, ypos, width, height, 0, 0, xminimum, xmaximum, 0, xStepSize, digit);
  389. counterG++;
  390. }
  391. void createCGraph(String title, double xpos, double ypos, double width, double height,
  392. double yminimum, double ymaximum, double xminimum, double xmaximum, double yStepSize, double xStepSize, double digit){
  393. this->graph[counterG].configure(title, 'c', xpos, ypos, width, height, yminimum, ymaximum, xminimum, xmaximum, yStepSize, xStepSize, digit);
  394. counterG++;
  395. }
  396. /*
  397. void redrawFlag(){
  398. this->redraw = true;
  399. }
  400. */
  401. void graphAssignValue(int graphIndex, double value){
  402. this->graph[graphIndex].assignValue(value);
  403. this->redraw = true;
  404. }
  405. void control(){
  406. if (redraw){
  407. if (contentType == 0){
  408. menu[currentScreen].drawMenu();
  409. }
  410. else if (contentType == 1){
  411. graph[currentScreen].drawGraph();
  412. }
  413. this->redraw = false;
  414. }
  415. }
  416. void increasePos(){
  417. if(this->menu[this->currentScreen].extractPos() < this->menu[this->currentScreen].extractOptNumber() - 1)
  418. this->menu[this->currentScreen].increasePos();
  419. }
  420. void decreasePos(){
  421. if(this->menu[this->currentScreen].extractPos() > 0)
  422. this->menu[this->currentScreen].decreasePos();
  423. }
  424. void goTo(){
  425. if(this->contentType == 0){
  426. int newScreen = this->menu[this->currentScreen].extractDestinationIndex();
  427. int newContentType = this->menu[this->currentScreen].extractDestinationType();
  428. if (newContentType == 0){
  429. this->menu[newScreen].setPreviousScreen(this->currentScreen);
  430. this->menu[newScreen].setPreviousContentType(this->contentType);
  431. }
  432. else if(newContentType == 1){
  433. this->graph[newScreen].setPreviousScreen(this->currentScreen);
  434. this->graph[newScreen].setPreviousContentType(this->contentType);
  435. this->graph[newScreen].reset();
  436. this->graph[newScreen].redrawFlag();
  437. }
  438. else if(newContentType == 2){
  439. }
  440. this->contentType = newContentType;
  441. this->currentScreen = newScreen;
  442. this->redraw = true;
  443. }
  444. }
  445. void goBack(){
  446. if(contentType == 0){
  447. //Gets indexes from previous screen saved in actual screen if it is a menu, and sets them as the current indexes
  448. this->currentScreen = this->menu[this->currentScreen].getPreviousScreen();
  449. this->contentType = this->menu[this->currentScreen].getPreviousContentType();
  450. }
  451. else if(contentType == 1){
  452. //Gets indexes from previous screen saved in actual screen if it is a graph, and sets them as the current indexes
  453. this->currentScreen = this->graph[this->currentScreen].getPreviousScreen();
  454. this->contentType = this->graph[this->currentScreen].getPreviousContentType();
  455. }
  456. }
  457. void plusAction(){
  458. if(contentType == 0){
  459. increasePos();
  460. }
  461. }
  462. void minusAction(){
  463. if(contentType == 0){
  464. decreasePos();
  465. }
  466. }
  467. };
  468. class Keyboard{
  469. private:
  470. byte goTo;
  471. byte goBack;
  472. byte plus;
  473. byte minus;
  474. byte debounceTime;
  475. Screen * screen;
  476. public:
  477. Keyboard(byte goTo, byte goBack, byte plus, byte minus, byte debounceTime, Screen * screen){
  478. this->goTo = goTo;
  479. this->goBack = goBack;
  480. this->plus = plus;
  481. this->minus = minus;
  482. this->debounceTime = debounceTime;
  483. this->screen = screen;
  484. pinMode(goTo, INPUT_PULLUP);
  485. pinMode(goBack, INPUT_PULLUP);
  486. pinMode(plus, INPUT_PULLUP);
  487. pinMode(minus, INPUT_PULLUP);
  488. }
  489. void checkGoTo(){
  490. static char cont;
  491. if(digitalRead(this->goTo) == LOW)
  492. cont++;
  493. else
  494. cont = 0;
  495. if(cont == debounceTime/REFRESH){
  496. cont = 0;
  497. this->screen->goTo();
  498. while(digitalRead(this->goTo) == LOW){
  499. }
  500. }
  501. }
  502. void checkGoBack(){
  503. static char cont;
  504. if(digitalRead(this->goBack) == LOW){
  505. cont++;
  506. Serial.println("Ayudaaaaa");
  507. }
  508. else
  509. cont = 0;
  510. if(cont == debounceTime/REFRESH){
  511. cont = 0;
  512. this->screen->goBack();
  513. Serial.println("Ayudaaaaant");
  514. while(digitalRead(this->goBack) == LOW){
  515. }
  516. }
  517. }
  518. void checkPlus(){
  519. static char cont;
  520. if(digitalRead(this->plus) == LOW)
  521. cont++;
  522. else
  523. cont = 0;
  524. if(cont == debounceTime/REFRESH){
  525. cont = 0;
  526. this->screen->plusAction();
  527. while(digitalRead(this->plus) == LOW){
  528. }
  529. }
  530. }
  531. void checkMinus(){
  532. static char cont;
  533. if(digitalRead(this->minus) == LOW)
  534. cont++;
  535. else
  536. cont = 0;
  537. if(cont == debounceTime/REFRESH){
  538. cont = 0;
  539. this->screen->minusAction();
  540. while(digitalRead(this->minus) == LOW){
  541. }
  542. }
  543. }
  544. void control(){
  545. this->checkGoTo();
  546. this->checkGoBack();
  547. this->checkPlus();
  548. this->checkMinus();
  549. }
  550. };
  551. Screen screen, * disp = &screen;
  552. Keyboard keyboard(14, 4, 12, 13, 40, disp);
  553. void setup(){
  554. screen.configure(true);
  555. Serial.println("Screen created");
  556. screen.createMenu(128, 13); //Menu 0
  557. screen.createMenu(128, 13); //Menu 1
  558. /*String title, char graphType, double xpos, double ypos, double width, double height,
  559. double yminimum, double ymaximum, double xminimum, double xmaximum, double yStepSize, double xStepSize, double digit*/
  560. screen.createVGraph("Grafica 1", 25, 60, 40, 40, 0, 100, 10, 0); //Graph 0
  561. screen.createHGraph("Grafica 2", 10, 40, 100, 20, 0, 100, 10, 0); //Graph 1
  562. screen.createCGraph("Grafica 3", 30, 50, 75, 30, 0, 100, 0, 100, 25, 25, 0); //Graph 2
  563. screen.createOption(0, "Vertical graph", 1, 0);
  564. //Creates the first option in Menu 0, directing to a graph (contentType = 1 (Graph)), 0 (Graph 0)
  565. screen.createOption(0, "Horizontal graph", 1, 1);
  566. screen.createOption(0, "Cartesian graph", 1, 2);
  567. screen.createOption(0, "Extra option", 0, 1);
  568. screen.createOption(1, "Test", 1, 3);
  569. screen.createOption(1, "Working?", 1, 4);
  570. screen.increasePos();
  571. screen.increasePos();
  572. screen.goTo();
  573. // screen.graphAssignValue(2, 50);
  574. screen.goBack();
  575. screen.increasePos();
  576. screen.goTo();
  577. // screen.goBack();
  578. // screen.decreasePos();
  579. }
  580. void loop(){
  581. screen.control(); //Controls the screen and redraws if needed
  582. keyboard.control();
  583. if(i <= 100){
  584. screen.graphAssignValue(1, i); //Assigning a demo value to Graph 1
  585. screen.graphAssignValue(2, i); //Assigning a demo value to Graph 2
  586. i++;
  587. }
  588. else
  589. i = 0;
  590. delay(REFRESH); //Refresh time (approx)
  591. }