TextDisplay.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. // Display Library example for SPI e-paper panels from Dalian Good Display and boards from Waveshare.
  2. // Requires HW SPI and Adafruit_GFX. Caution: the e-paper panels require 3.3V supply AND data lines!
  3. //
  4. // Display Library based on Demo Example from Good Display: https://www.good-display.com/companyfile/32/
  5. //
  6. // Author: Jean-Marc Zingg
  7. //
  8. // Version: see library.properties
  9. //
  10. // Library: https://github.com/ZinggJM/GxEPD2
  11. //
  12. // Purpose: show uses of GxEPD2_GFX base class for references to a display instance
  13. //
  14. // TextDisplay shows the use of the display instance reference as a function parameter
  15. // note for partial update window and setPartialWindow() method:
  16. // partial update window size and position is on byte boundary in physical x direction
  17. // the size is increased in setPartialWindow() if x or w are not multiple of 8 for even rotation, y or h for odd rotation
  18. // see also comment in GxEPD2_BW.h, GxEPD2_3C.h or GxEPD2_GFX.h for method setPartialWindow()
  19. #include "TextDisplay.h"
  20. #include <Fonts/FreeMonoBold9pt7b.h>
  21. const char HelloWorld[] = "Hello World!";
  22. const char HelloArduino[] = "Hello Arduino!";
  23. const char HelloEpaper[] = "Hello E-Paper!";
  24. void helloWorld(GxEPD2_GFX& display)
  25. {
  26. //Serial.println("helloWorld");
  27. display.setRotation(1);
  28. display.setFont(&FreeMonoBold9pt7b);
  29. display.setTextColor(GxEPD_BLACK);
  30. int16_t tbx, tby; uint16_t tbw, tbh;
  31. display.getTextBounds(HelloWorld, 0, 0, &tbx, &tby, &tbw, &tbh);
  32. // center bounding box by transposition of origin:
  33. uint16_t x = ((display.width() - tbw) / 2) - tbx;
  34. uint16_t y = ((display.height() - tbh) / 2) - tby;
  35. display.setFullWindow();
  36. display.firstPage();
  37. do
  38. {
  39. display.fillScreen(GxEPD_WHITE);
  40. display.setCursor(x, y);
  41. display.print(HelloWorld);
  42. }
  43. while (display.nextPage());
  44. //Serial.println("helloWorld done");
  45. }
  46. void helloWorldForDummies(GxEPD2_GFX& display)
  47. {
  48. //Serial.println("helloWorld");
  49. const char text[] = "Hello World!";
  50. // most e-papers have width < height (portrait) as native orientation, especially the small ones
  51. // in GxEPD2 rotation 0 is used for native orientation (most TFT libraries use 0 fix for portrait orientation)
  52. // set rotation to 1 (rotate right 90 degrees) to have enough space on small displays (landscape)
  53. display.setRotation(1);
  54. // select a suitable font in Adafruit_GFX
  55. display.setFont(&FreeMonoBold9pt7b);
  56. // on e-papers black on white is more pleasant to read
  57. display.setTextColor(GxEPD_BLACK);
  58. // Adafruit_GFX has a handy method getTextBounds() to determine the boundary box for a text for the actual font
  59. int16_t tbx, tby; uint16_t tbw, tbh; // boundary box window
  60. display.getTextBounds(text, 0, 0, &tbx, &tby, &tbw, &tbh); // it works for origin 0, 0, fortunately (negative tby!)
  61. // center bounding box by transposition of origin:
  62. uint16_t x = ((display.width() - tbw) / 2) - tbx;
  63. uint16_t y = ((display.height() - tbh) / 2) - tby;
  64. // full window mode is the initial mode, set it anyway
  65. display.setFullWindow();
  66. // here we use paged drawing, even if the processor has enough RAM for full buffer
  67. // so this can be used with any supported processor board.
  68. // the cost in code overhead and execution time penalty is marginal
  69. // tell the graphics class to use paged drawing mode
  70. display.firstPage();
  71. do
  72. {
  73. // this part of code is executed multiple times, as many as needed,
  74. // in case of full buffer it is executed once
  75. // IMPORTANT: each iteration needs to draw the same, to avoid strange effects
  76. // use a copy of values that might change, don't read e.g. from analog or pins in the loop!
  77. display.fillScreen(GxEPD_WHITE); // set the background to white (fill the buffer with value for white)
  78. display.setCursor(x, y); // set the postition to start printing text
  79. display.print(text); // print some text
  80. // end of part executed multiple times
  81. }
  82. // tell the graphics class to transfer the buffer content (page) to the controller buffer
  83. // the graphics class will command the controller to refresh to the screen when the last page has been transferred
  84. // returns true if more pages need be drawn and transferred
  85. // returns false if the last page has been transferred and the screen refreshed for panels without fast partial update
  86. // returns false for panels with fast partial update when the controller buffer has been written once more, to make the differential buffers equal
  87. // (for full buffered with fast partial update the (full) buffer is just transferred again, and false returned)
  88. while (display.nextPage());
  89. //Serial.println("helloWorld done");
  90. }
  91. void helloFullScreenPartialMode(GxEPD2_GFX& display)
  92. {
  93. //Serial.println("helloFullScreenPartialMode");
  94. const char fullscreen[] = "full screen update";
  95. const char fpm[] = "fast partial mode";
  96. const char spm[] = "slow partial mode";
  97. const char npm[] = "no partial mode";
  98. display.setPartialWindow(0, 0, display.width(), display.height());
  99. display.setRotation(1);
  100. display.setFont(&FreeMonoBold9pt7b);
  101. display.setTextColor(GxEPD_BLACK);
  102. const char* updatemode;
  103. if (display.epd2.hasFastPartialUpdate)
  104. {
  105. updatemode = fpm;
  106. }
  107. else if (display.epd2.hasPartialUpdate)
  108. {
  109. updatemode = spm;
  110. }
  111. else
  112. {
  113. updatemode = npm;
  114. }
  115. // do this outside of the loop
  116. int16_t tbx, tby; uint16_t tbw, tbh;
  117. // center update text
  118. display.getTextBounds(fullscreen, 0, 0, &tbx, &tby, &tbw, &tbh);
  119. uint16_t utx = ((display.width() - tbw) / 2) - tbx;
  120. uint16_t uty = ((display.height() / 4) - tbh / 2) - tby;
  121. // center update mode
  122. display.getTextBounds(updatemode, 0, 0, &tbx, &tby, &tbw, &tbh);
  123. uint16_t umx = ((display.width() - tbw) / 2) - tbx;
  124. uint16_t umy = ((display.height() * 3 / 4) - tbh / 2) - tby;
  125. // center HelloWorld
  126. display.getTextBounds(HelloWorld, 0, 0, &tbx, &tby, &tbw, &tbh);
  127. uint16_t hwx = ((display.width() - tbw) / 2) - tbx;
  128. uint16_t hwy = ((display.height() - tbh) / 2) - tby;
  129. display.firstPage();
  130. do
  131. {
  132. display.fillScreen(GxEPD_WHITE);
  133. display.setCursor(hwx, hwy);
  134. display.print(HelloWorld);
  135. display.setCursor(utx, uty);
  136. display.print(fullscreen);
  137. display.setCursor(umx, umy);
  138. display.print(updatemode);
  139. }
  140. while (display.nextPage());
  141. //Serial.println("helloFullScreenPartialMode done");
  142. }
  143. void helloArduino(GxEPD2_GFX& display)
  144. {
  145. //Serial.println("helloArduino");
  146. display.setRotation(1);
  147. display.setFont(&FreeMonoBold9pt7b);
  148. display.setTextColor(display.epd2.hasColor ? GxEPD_RED : GxEPD_BLACK);
  149. int16_t tbx, tby; uint16_t tbw, tbh;
  150. // align with centered HelloWorld
  151. display.getTextBounds(HelloWorld, 0, 0, &tbx, &tby, &tbw, &tbh);
  152. uint16_t x = ((display.width() - tbw) / 2) - tbx;
  153. // height might be different
  154. display.getTextBounds(HelloArduino, 0, 0, &tbx, &tby, &tbw, &tbh);
  155. uint16_t y = ((display.height() / 4) - tbh / 2) - tby; // y is base line!
  156. // make the window big enough to cover (overwrite) descenders of previous text
  157. uint16_t wh = FreeMonoBold9pt7b.yAdvance;
  158. uint16_t wy = (display.height() / 4) - wh / 2;
  159. display.setPartialWindow(0, wy, display.width(), wh);
  160. display.firstPage();
  161. do
  162. {
  163. display.fillScreen(GxEPD_WHITE);
  164. //display.drawRect(x, y - tbh, tbw, tbh, GxEPD_BLACK);
  165. display.setCursor(x, y);
  166. display.print(HelloArduino);
  167. }
  168. while (display.nextPage());
  169. delay(1000);
  170. //Serial.println("helloArduino done");
  171. }
  172. void helloEpaper(GxEPD2_GFX& display)
  173. {
  174. //Serial.println("helloEpaper");
  175. display.setRotation(1);
  176. display.setFont(&FreeMonoBold9pt7b);
  177. display.setTextColor(display.epd2.hasColor ? GxEPD_RED : GxEPD_BLACK);
  178. int16_t tbx, tby; uint16_t tbw, tbh;
  179. // align with centered HelloWorld
  180. display.getTextBounds(HelloWorld, 0, 0, &tbx, &tby, &tbw, &tbh);
  181. uint16_t x = ((display.width() - tbw) / 2) - tbx;
  182. // height might be different
  183. display.getTextBounds(HelloEpaper, 0, 0, &tbx, &tby, &tbw, &tbh);
  184. uint16_t y = (display.height() * 3 / 4) + tbh / 2; // y is base line!
  185. // make the window big enough to cover (overwrite) descenders of previous text
  186. uint16_t wh = FreeMonoBold9pt7b.yAdvance;
  187. uint16_t wy = (display.height() * 3 / 4) - wh / 2;
  188. display.setPartialWindow(0, wy, display.width(), wh);
  189. display.firstPage();
  190. do
  191. {
  192. display.fillScreen(GxEPD_WHITE);
  193. display.setCursor(x, y);
  194. display.print(HelloEpaper);
  195. }
  196. while (display.nextPage());
  197. //Serial.println("helloEpaper done");
  198. }
  199. #if defined(ESP8266) || defined(ESP32)
  200. #include <StreamString.h>
  201. #define PrintString StreamString
  202. #else
  203. class PrintString : public Print, public String
  204. {
  205. public:
  206. size_t write(uint8_t data) override
  207. {
  208. return concat(char(data));
  209. };
  210. };
  211. #endif
  212. void helloValue(GxEPD2_GFX& display, double v, int digits)
  213. {
  214. //Serial.println("helloValue");
  215. display.setRotation(1);
  216. display.setFont(&FreeMonoBold9pt7b);
  217. display.setTextColor(display.epd2.hasColor ? GxEPD_RED : GxEPD_BLACK);
  218. PrintString valueString;
  219. valueString.print(v, digits);
  220. int16_t tbx, tby; uint16_t tbw, tbh;
  221. display.getTextBounds(valueString, 0, 0, &tbx, &tby, &tbw, &tbh);
  222. uint16_t x = ((display.width() - tbw) / 2) - tbx;
  223. uint16_t y = (display.height() * 3 / 4) + tbh / 2; // y is base line!
  224. // show what happens, if we use the bounding box for partial window
  225. uint16_t wx = (display.width() - tbw) / 2;
  226. uint16_t wy = (display.height() * 3 / 4) - tbh / 2;
  227. display.setPartialWindow(wx, wy, tbw, tbh);
  228. display.firstPage();
  229. do
  230. {
  231. display.fillScreen(GxEPD_WHITE);
  232. display.setCursor(x, y);
  233. display.print(valueString);
  234. }
  235. while (display.nextPage());
  236. delay(2000);
  237. // make the partial window big enough to cover the previous text
  238. uint16_t ww = tbw; // remember window width
  239. display.getTextBounds(HelloEpaper, 0, 0, &tbx, &tby, &tbw, &tbh);
  240. // adjust, because HelloEpaper was aligned, not centered (could calculate this to be precise)
  241. ww = max(ww, uint16_t(tbw + 12)); // 12 seems ok
  242. wx = (display.width() - tbw) / 2;
  243. // make the window big enough to cover (overwrite) descenders of previous text
  244. uint16_t wh = FreeMonoBold9pt7b.yAdvance;
  245. wy = (display.height() * 3 / 4) - wh / 2;
  246. display.setPartialWindow(wx, wy, ww, wh);
  247. // alternately use the whole width for partial window
  248. //display.setPartialWindow(0, wy, display.width(), wh);
  249. display.firstPage();
  250. do
  251. {
  252. display.fillScreen(GxEPD_WHITE);
  253. display.setCursor(x, y);
  254. display.print(valueString);
  255. }
  256. while (display.nextPage());
  257. //Serial.println("helloValue done");
  258. }
  259. void showFont(GxEPD2_GFX& display, const char name[], const GFXfont* f)
  260. {
  261. display.setFullWindow();
  262. display.setRotation(0);
  263. display.setTextColor(GxEPD_BLACK);
  264. display.firstPage();
  265. do
  266. {
  267. drawFont(display, name, f);
  268. }
  269. while (display.nextPage());
  270. }
  271. void drawFont(GxEPD2_GFX& display, const char name[], const GFXfont* f)
  272. {
  273. //display.setRotation(0);
  274. display.fillScreen(GxEPD_WHITE);
  275. display.setTextColor(GxEPD_BLACK);
  276. display.setFont(f);
  277. display.setCursor(0, 0);
  278. display.println();
  279. display.println(name);
  280. display.println(" !\"#$%&'()*+,-./");
  281. display.println("0123456789:;<=>?");
  282. display.println("@ABCDEFGHIJKLMNO");
  283. display.println("PQRSTUVWXYZ[\\]^_");
  284. if (display.epd2.hasColor)
  285. {
  286. display.setTextColor(GxEPD_RED);
  287. }
  288. display.println("`abcdefghijklmno");
  289. display.println("pqrstuvwxyz{|}~ ");
  290. }