OLEDDisplay.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810
  1. /**
  2. * The MIT License (MIT)
  3. *
  4. * Copyright (c) 2016 by Daniel Eichhorn
  5. * Copyright (c) 2016 by Fabrice Weinberg
  6. *
  7. * Permission is hereby granted, free of charge, to any person obtaining a copy
  8. * of this software and associated documentation files (the "Software"), to deal
  9. * in the Software without restriction, including without limitation the rights
  10. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. * copies of the Software, and to permit persons to whom the Software is
  12. * furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be included in all
  15. * copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  23. * SOFTWARE.
  24. *
  25. * Credits for parts of this code go to Mike Rankin. Thank you so much for sharing!
  26. */
  27. #include "OLEDDisplay.h"
  28. bool OLEDDisplay::init() {
  29. if (!this->connect()) {
  30. DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Can't establish connection to display\n");
  31. return false;
  32. }
  33. this->buffer = (uint8_t*) malloc(sizeof(uint8_t) * DISPLAY_BUFFER_SIZE);
  34. if(!this->buffer) {
  35. DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Not enough memory to create display\n");
  36. return false;
  37. }
  38. #ifdef OLEDDISPLAY_DOUBLE_BUFFER
  39. this->buffer_back = (uint8_t*) malloc(sizeof(uint8_t) * DISPLAY_BUFFER_SIZE);
  40. if(!this->buffer_back) {
  41. DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Not enough memory to create back buffer\n");
  42. free(this->buffer);
  43. return false;
  44. }
  45. #endif
  46. sendInitCommands();
  47. resetDisplay();
  48. return true;
  49. }
  50. void OLEDDisplay::end() {
  51. if (this->buffer) free(this->buffer);
  52. #ifdef OLEDDISPLAY_DOUBLE_BUFFER
  53. if (this->buffer_back) free(this->buffer_back);
  54. #endif
  55. }
  56. void OLEDDisplay::resetDisplay(void) {
  57. clear();
  58. #ifdef OLEDDISPLAY_DOUBLE_BUFFER
  59. memset(buffer_back, 1, DISPLAY_BUFFER_SIZE);
  60. #endif
  61. display();
  62. }
  63. void OLEDDisplay::setColor(OLEDDISPLAY_COLOR color) {
  64. this->color = color;
  65. }
  66. void OLEDDisplay::setPixel(int16_t x, int16_t y) {
  67. if (x >= 0 && x < 128 && y >= 0 && y < 64) {
  68. switch (color) {
  69. case WHITE: buffer[x + (y / 8) * DISPLAY_WIDTH] |= (1 << (y & 7)); break;
  70. case BLACK: buffer[x + (y / 8) * DISPLAY_WIDTH] &= ~(1 << (y & 7)); break;
  71. case INVERSE: buffer[x + (y / 8) * DISPLAY_WIDTH] ^= (1 << (y & 7)); break;
  72. }
  73. }
  74. }
  75. // Bresenham's algorithm - thx wikipedia and Adafruit_GFX
  76. void OLEDDisplay::drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1) {
  77. int16_t steep = abs(y1 - y0) > abs(x1 - x0);
  78. if (steep) {
  79. _swap_int16_t(x0, y0);
  80. _swap_int16_t(x1, y1);
  81. }
  82. if (x0 > x1) {
  83. _swap_int16_t(x0, x1);
  84. _swap_int16_t(y0, y1);
  85. }
  86. int16_t dx, dy;
  87. dx = x1 - x0;
  88. dy = abs(y1 - y0);
  89. int16_t err = dx / 2;
  90. int16_t ystep;
  91. if (y0 < y1) {
  92. ystep = 1;
  93. } else {
  94. ystep = -1;
  95. }
  96. for (; x0<=x1; x0++) {
  97. if (steep) {
  98. setPixel(y0, x0);
  99. } else {
  100. setPixel(x0, y0);
  101. }
  102. err -= dy;
  103. if (err < 0) {
  104. y0 += ystep;
  105. err += dx;
  106. }
  107. }
  108. }
  109. void OLEDDisplay::drawRect(int16_t x, int16_t y, int16_t width, int16_t height) {
  110. drawHorizontalLine(x, y, width);
  111. drawVerticalLine(x, y, height);
  112. drawVerticalLine(x + width - 1, y, height);
  113. drawHorizontalLine(x, y + height - 1, width);
  114. }
  115. void OLEDDisplay::fillRect(int16_t xMove, int16_t yMove, int16_t width, int16_t height) {
  116. for (int16_t x = xMove; x < xMove + width; x++) {
  117. drawVerticalLine(x, yMove, height);
  118. }
  119. }
  120. void OLEDDisplay::drawCircle(int16_t x0, int16_t y0, int16_t radius) {
  121. int16_t x = 0, y = radius;
  122. int16_t dp = 1 - radius;
  123. do {
  124. if (dp < 0)
  125. dp = dp + 2 * (++x) + 3;
  126. else
  127. dp = dp + 2 * (++x) - 2 * (--y) + 5;
  128. setPixel(x0 + x, y0 + y); //For the 8 octants
  129. setPixel(x0 - x, y0 + y);
  130. setPixel(x0 + x, y0 - y);
  131. setPixel(x0 - x, y0 - y);
  132. setPixel(x0 + y, y0 + x);
  133. setPixel(x0 - y, y0 + x);
  134. setPixel(x0 + y, y0 - x);
  135. setPixel(x0 - y, y0 - x);
  136. } while (x < y);
  137. setPixel(x0 + radius, y0);
  138. setPixel(x0, y0 + radius);
  139. setPixel(x0 - radius, y0);
  140. setPixel(x0, y0 - radius);
  141. }
  142. void OLEDDisplay::drawCircleQuads(int16_t x0, int16_t y0, int16_t radius, uint8_t quads) {
  143. int16_t x = 0, y = radius;
  144. int16_t dp = 1 - radius;
  145. while (x < y) {
  146. if (dp < 0)
  147. dp = dp + 2 * (++x) + 3;
  148. else
  149. dp = dp + 2 * (++x) - 2 * (--y) + 5;
  150. if (quads & 0x1) {
  151. setPixel(x0 + x, y0 - y);
  152. setPixel(x0 + y, y0 - x);
  153. }
  154. if (quads & 0x2) {
  155. setPixel(x0 - y, y0 - x);
  156. setPixel(x0 - x, y0 - y);
  157. }
  158. if (quads & 0x4) {
  159. setPixel(x0 - y, y0 + x);
  160. setPixel(x0 - x, y0 + y);
  161. }
  162. if (quads & 0x8) {
  163. setPixel(x0 + x, y0 + y);
  164. setPixel(x0 + y, y0 + x);
  165. }
  166. }
  167. if (quads & 0x1 && quads & 0x8) {
  168. setPixel(x0 + radius, y0);
  169. }
  170. if (quads & 0x4 && quads & 0x8) {
  171. setPixel(x0, y0 + radius);
  172. }
  173. if (quads & 0x2 && quads & 0x4) {
  174. setPixel(x0 - radius, y0);
  175. }
  176. if (quads & 0x1 && quads & 0x2) {
  177. setPixel(x0, y0 - radius);
  178. }
  179. }
  180. void OLEDDisplay::fillCircle(int16_t x0, int16_t y0, int16_t radius) {
  181. int16_t x = 0, y = radius;
  182. int16_t dp = 1 - radius;
  183. do {
  184. if (dp < 0)
  185. dp = dp + 2 * (++x) + 3;
  186. else
  187. dp = dp + 2 * (++x) - 2 * (--y) + 5;
  188. drawHorizontalLine(x0 - x, y0 - y, 2*x);
  189. drawHorizontalLine(x0 - x, y0 + y, 2*x);
  190. drawHorizontalLine(x0 - y, y0 - x, 2*y);
  191. drawHorizontalLine(x0 - y, y0 + x, 2*y);
  192. } while (x < y);
  193. drawHorizontalLine(x0 - radius, y0, 2 * radius);
  194. }
  195. void OLEDDisplay::drawHorizontalLine(int16_t x, int16_t y, int16_t length) {
  196. if (y < 0 || y >= DISPLAY_HEIGHT) { return; }
  197. if (x < 0) {
  198. length += x;
  199. x = 0;
  200. }
  201. if ( (x + length) > DISPLAY_WIDTH) {
  202. length = (DISPLAY_WIDTH - x);
  203. }
  204. if (length <= 0) { return; }
  205. uint8_t * bufferPtr = buffer;
  206. bufferPtr += (y >> 3) * DISPLAY_WIDTH;
  207. bufferPtr += x;
  208. uint8_t drawBit = 1 << (y & 7);
  209. switch (color) {
  210. case WHITE: while (length--) {
  211. *bufferPtr++ |= drawBit;
  212. }; break;
  213. case BLACK: drawBit = ~drawBit; while (length--) {
  214. *bufferPtr++ &= drawBit;
  215. }; break;
  216. case INVERSE: while (length--) {
  217. *bufferPtr++ ^= drawBit;
  218. }; break;
  219. }
  220. }
  221. void OLEDDisplay::drawVerticalLine(int16_t x, int16_t y, int16_t length) {
  222. if (x < 0 || x >= DISPLAY_WIDTH) return;
  223. if (y < 0) {
  224. length += y;
  225. y = 0;
  226. }
  227. if ( (y + length) > DISPLAY_HEIGHT) {
  228. length = (DISPLAY_HEIGHT - y);
  229. }
  230. if (length <= 0) return;
  231. uint8_t yOffset = y & 7;
  232. uint8_t drawBit;
  233. uint8_t *bufferPtr = buffer;
  234. bufferPtr += (y >> 3) * DISPLAY_WIDTH;
  235. bufferPtr += x;
  236. if (yOffset) {
  237. yOffset = 8 - yOffset;
  238. drawBit = ~(0xFF >> (yOffset));
  239. if (length < yOffset) {
  240. drawBit &= (0xFF >> (yOffset - length));
  241. }
  242. switch (color) {
  243. case WHITE: *bufferPtr |= drawBit; break;
  244. case BLACK: *bufferPtr &= ~drawBit; break;
  245. case INVERSE: *bufferPtr ^= drawBit; break;
  246. }
  247. if (length < yOffset) return;
  248. length -= yOffset;
  249. bufferPtr += DISPLAY_WIDTH;
  250. }
  251. if (length >= 8) {
  252. switch (color) {
  253. case WHITE:
  254. case BLACK:
  255. drawBit = (color == WHITE) ? 0xFF : 0x00;
  256. do {
  257. *bufferPtr = drawBit;
  258. bufferPtr += DISPLAY_WIDTH;
  259. length -= 8;
  260. } while (length >= 8);
  261. break;
  262. case INVERSE:
  263. do {
  264. *bufferPtr = ~(*bufferPtr);
  265. bufferPtr += DISPLAY_WIDTH;
  266. length -= 8;
  267. } while (length >= 8);
  268. break;
  269. }
  270. }
  271. if (length > 0) {
  272. drawBit = (1 << (length & 7)) - 1;
  273. switch (color) {
  274. case WHITE: *bufferPtr |= drawBit; break;
  275. case BLACK: *bufferPtr &= ~drawBit; break;
  276. case INVERSE: *bufferPtr ^= drawBit; break;
  277. }
  278. }
  279. }
  280. void OLEDDisplay::drawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t progress) {
  281. uint16_t radius = height / 2;
  282. uint16_t xRadius = x + radius;
  283. uint16_t yRadius = y + radius;
  284. uint16_t doubleRadius = 2 * radius;
  285. uint16_t innerRadius = radius - 2;
  286. setColor(WHITE);
  287. drawCircleQuads(xRadius, yRadius, radius, 0b00000110);
  288. drawHorizontalLine(xRadius, y, width - doubleRadius + 1);
  289. drawHorizontalLine(xRadius, y + height, width - doubleRadius + 1);
  290. drawCircleQuads(x + width - radius, yRadius, radius, 0b00001001);
  291. uint16_t maxProgressWidth = (width - doubleRadius - 1) * progress / 100;
  292. fillCircle(xRadius, yRadius, innerRadius);
  293. fillRect(xRadius + 1, y + 2, maxProgressWidth, height - 3);
  294. fillCircle(xRadius + maxProgressWidth, yRadius, innerRadius);
  295. }
  296. void OLEDDisplay::drawFastImage(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const char *image) {
  297. drawInternal(xMove, yMove, width, height, image, 0, 0);
  298. }
  299. void OLEDDisplay::drawXbm(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const char *xbm) {
  300. int16_t widthInXbm = (width + 7) / 8;
  301. uint8_t data;
  302. for(int16_t y = 0; y < height; y++) {
  303. for(int16_t x = 0; x < width; x++ ) {
  304. if (x & 7) {
  305. data >>= 1; // Move a bit
  306. } else { // Read new data every 8 bit
  307. data = pgm_read_byte(xbm + (x / 8) + y * widthInXbm);
  308. }
  309. // if there is a bit draw it
  310. if (data & 0x01) {
  311. setPixel(xMove + x, yMove + y);
  312. }
  313. }
  314. }
  315. }
  316. void OLEDDisplay::drawStringInternal(int16_t xMove, int16_t yMove, char* text, uint16_t textLength, uint16_t textWidth) {
  317. uint8_t textHeight = pgm_read_byte(fontData + HEIGHT_POS);
  318. uint8_t firstChar = pgm_read_byte(fontData + FIRST_CHAR_POS);
  319. uint16_t sizeOfJumpTable = pgm_read_byte(fontData + CHAR_NUM_POS) * JUMPTABLE_BYTES;
  320. uint8_t cursorX = 0;
  321. uint8_t cursorY = 0;
  322. switch (textAlignment) {
  323. case TEXT_ALIGN_CENTER_BOTH:
  324. yMove -= textHeight >> 1;
  325. // Fallthrough
  326. case TEXT_ALIGN_CENTER:
  327. xMove -= textWidth >> 1; // divide by 2
  328. break;
  329. case TEXT_ALIGN_RIGHT:
  330. xMove -= textWidth;
  331. break;
  332. }
  333. // Don't draw anything if it is not on the screen.
  334. if (xMove + textWidth < 0 || xMove > DISPLAY_WIDTH ) {return;}
  335. if (yMove + textHeight < 0 || yMove > DISPLAY_HEIGHT) {return;}
  336. for (uint16_t j = 0; j < textLength; j++) {
  337. int16_t xPos = xMove + cursorX;
  338. int16_t yPos = yMove + cursorY;
  339. byte code = text[j];
  340. if (code >= firstChar) {
  341. byte charCode = code - firstChar;
  342. // 4 Bytes per char code
  343. byte msbJumpToChar = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES ); // MSB \ JumpAddress
  344. byte lsbJumpToChar = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_LSB); // LSB /
  345. byte charByteSize = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_SIZE); // Size
  346. byte currentCharWidth = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_WIDTH); // Width
  347. // Test if the char is drawable
  348. if (!(msbJumpToChar == 255 && lsbJumpToChar == 255)) {
  349. // Get the position of the char data
  350. uint16_t charDataPosition = JUMPTABLE_START + sizeOfJumpTable + ((msbJumpToChar << 8) + lsbJumpToChar);
  351. drawInternal(xPos, yPos, currentCharWidth, textHeight, fontData, charDataPosition, charByteSize);
  352. }
  353. cursorX += currentCharWidth;
  354. }
  355. }
  356. }
  357. void OLEDDisplay::drawString(int16_t xMove, int16_t yMove, String strUser) {
  358. uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS);
  359. // char* text must be freed!
  360. char* text = utf8ascii(strUser);
  361. uint16_t yOffset = 0;
  362. // If the string should be centered vertically too
  363. // we need to now how heigh the string is.
  364. if (textAlignment == TEXT_ALIGN_CENTER_BOTH) {
  365. uint16_t lb = 0;
  366. // Find number of linebreaks in text
  367. for (uint16_t i=0;text[i] != 0; i++) {
  368. lb += (text[i] == 10);
  369. }
  370. // Calculate center
  371. yOffset = (lb * lineHeight) / 2;
  372. }
  373. uint16_t line = 0;
  374. char* textPart = strtok(text,"\n");
  375. while (textPart != NULL) {
  376. uint16_t length = strlen(textPart);
  377. drawStringInternal(xMove, yMove - yOffset + (line++) * lineHeight, textPart, length, getStringWidth(textPart, length));
  378. textPart = strtok(NULL, "\n");
  379. }
  380. free(text);
  381. }
  382. void OLEDDisplay::drawStringMaxWidth(int16_t xMove, int16_t yMove, uint16_t maxLineWidth, String strUser) {
  383. uint16_t firstChar = pgm_read_byte(fontData + FIRST_CHAR_POS);
  384. uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS);
  385. char* text = utf8ascii(strUser);
  386. uint16_t length = strlen(text);
  387. uint16_t lastDrawnPos = 0;
  388. uint16_t lineNumber = 0;
  389. uint16_t strWidth = 0;
  390. uint16_t preferredBreakpoint = 0;
  391. uint16_t widthAtBreakpoint = 0;
  392. for (uint16_t i = 0; i < length; i++) {
  393. strWidth += pgm_read_byte(fontData + JUMPTABLE_START + (text[i] - firstChar) * JUMPTABLE_BYTES + JUMPTABLE_WIDTH);
  394. // Always try to break on a space or dash
  395. if (text[i] == ' ' || text[i]== '-') {
  396. preferredBreakpoint = i;
  397. widthAtBreakpoint = strWidth;
  398. }
  399. if (strWidth >= maxLineWidth) {
  400. if (preferredBreakpoint == 0) {
  401. preferredBreakpoint = i;
  402. widthAtBreakpoint = strWidth;
  403. }
  404. drawStringInternal(xMove, yMove + (lineNumber++) * lineHeight , &text[lastDrawnPos], preferredBreakpoint - lastDrawnPos, widthAtBreakpoint);
  405. lastDrawnPos = preferredBreakpoint + 1;
  406. // It is possible that we did not draw all letters to i so we need
  407. // to account for the width of the chars from `i - preferredBreakpoint`
  408. // by calculating the width we did not draw yet.
  409. strWidth = strWidth - widthAtBreakpoint;
  410. preferredBreakpoint = 0;
  411. }
  412. }
  413. // Draw last part if needed
  414. if (lastDrawnPos < length) {
  415. drawStringInternal(xMove, yMove + lineNumber * lineHeight , &text[lastDrawnPos], length - lastDrawnPos, getStringWidth(&text[lastDrawnPos], length - lastDrawnPos));
  416. }
  417. free(text);
  418. }
  419. uint16_t OLEDDisplay::getStringWidth(const char* text, uint16_t length) {
  420. uint16_t firstChar = pgm_read_byte(fontData + FIRST_CHAR_POS);
  421. uint16_t stringWidth = 0;
  422. uint16_t maxWidth = 0;
  423. while (length--) {
  424. stringWidth += pgm_read_byte(fontData + JUMPTABLE_START + (text[length] - firstChar) * JUMPTABLE_BYTES + JUMPTABLE_WIDTH);
  425. if (text[length] == 10) {
  426. maxWidth = max(maxWidth, stringWidth);
  427. stringWidth = 0;
  428. }
  429. }
  430. return max(maxWidth, stringWidth);
  431. }
  432. uint16_t OLEDDisplay::getStringWidth(String strUser) {
  433. char* text = utf8ascii(strUser);
  434. uint16_t length = strlen(text);
  435. uint16_t width = getStringWidth(text, length);
  436. free(text);
  437. return width;
  438. }
  439. void OLEDDisplay::setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment) {
  440. this->textAlignment = textAlignment;
  441. }
  442. void OLEDDisplay::setFont(const char *fontData) {
  443. this->fontData = fontData;
  444. }
  445. void OLEDDisplay::displayOn(void) {
  446. sendCommand(DISPLAYON);
  447. }
  448. void OLEDDisplay::displayOff(void) {
  449. sendCommand(DISPLAYOFF);
  450. }
  451. void OLEDDisplay::invertDisplay(void) {
  452. sendCommand(INVERTDISPLAY);
  453. }
  454. void OLEDDisplay::normalDisplay(void) {
  455. sendCommand(NORMALDISPLAY);
  456. }
  457. void OLEDDisplay::setContrast(char contrast) {
  458. sendCommand(SETCONTRAST);
  459. sendCommand(contrast);
  460. }
  461. void OLEDDisplay::flipScreenVertically() {
  462. sendCommand(SEGREMAP | 0x01);
  463. sendCommand(COMSCANDEC); //Rotate screen 180 Deg
  464. }
  465. void OLEDDisplay::clear(void) {
  466. memset(buffer, 0, DISPLAY_BUFFER_SIZE);
  467. }
  468. void OLEDDisplay::drawLogBuffer(uint16_t xMove, uint16_t yMove) {
  469. uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS);
  470. // Always align left
  471. setTextAlignment(TEXT_ALIGN_LEFT);
  472. // State values
  473. uint16_t length = 0;
  474. uint16_t line = 0;
  475. uint16_t lastPos = 0;
  476. for (uint16_t i=0;i<this->logBufferFilled;i++){
  477. // Everytime we have a \n print
  478. if (this->logBuffer[i] == 10) {
  479. length++;
  480. // Draw string on line `line` from lastPos to length
  481. // Passing 0 as the lenght because we are in TEXT_ALIGN_LEFT
  482. drawStringInternal(xMove, yMove + (line++) * lineHeight, &this->logBuffer[lastPos], length, 0);
  483. // Remember last pos
  484. lastPos = i;
  485. // Reset length
  486. length = 0;
  487. } else {
  488. // Count chars until next linebreak
  489. length++;
  490. }
  491. }
  492. // Draw the remaining string
  493. if (length > 0) {
  494. drawStringInternal(xMove, yMove + line * lineHeight, &this->logBuffer[lastPos], length, 0);
  495. }
  496. }
  497. bool OLEDDisplay::setLogBuffer(uint16_t lines, uint16_t chars){
  498. if (logBuffer != NULL) free(logBuffer);
  499. uint16_t size = lines * chars;
  500. if (size > 0) {
  501. this->logBufferLine = 0; // Lines printed
  502. this->logBufferMaxLines = lines; // Lines max printable
  503. this->logBufferSize = size; // Total number of characters the buffer can hold
  504. this->logBuffer = (char *) malloc(size * sizeof(uint8_t));
  505. if(!this->logBuffer) {
  506. DEBUG_OLEDDISPLAY("[OLEDDISPLAY][setLogBuffer] Not enough memory to create log buffer\n");
  507. return false;
  508. }
  509. }
  510. return true;
  511. }
  512. size_t OLEDDisplay::write(uint8_t c) {
  513. if (this->logBufferSize > 0) {
  514. // Don't waste space on \r\n line endings, dropping \r
  515. if (c == 13) return 1;
  516. bool maxLineNotReached = this->logBufferLine < this->logBufferMaxLines;
  517. bool bufferNotFull = this->logBufferFilled < this->logBufferSize;
  518. // Can we write to the buffer?
  519. if (bufferNotFull && maxLineNotReached) {
  520. this->logBuffer[logBufferFilled] = utf8ascii(c);
  521. this->logBufferFilled++;
  522. // Keep track of lines written
  523. if (c == 10) this->logBufferLine++;
  524. } else {
  525. // Max line number is reached
  526. if (!maxLineNotReached) this->logBufferLine--;
  527. // Find the end of the first line
  528. uint16_t firstLineEnd = 0;
  529. for (uint16_t i=0;i<this->logBufferFilled;i++) {
  530. if (this->logBuffer[i] == 10){
  531. // Include last char too
  532. firstLineEnd = i + 1;
  533. break;
  534. }
  535. }
  536. // If there was a line ending
  537. if (firstLineEnd > 0) {
  538. // Calculate the new logBufferFilled value
  539. this->logBufferFilled = logBufferFilled - firstLineEnd;
  540. // Now we move the lines infront of the buffer
  541. memcpy(this->logBuffer, &this->logBuffer[firstLineEnd], logBufferFilled);
  542. } else {
  543. // Let's reuse the buffer if it was full
  544. if (!bufferNotFull) {
  545. this->logBufferFilled = 0;
  546. }// else {
  547. // Nothing to do here
  548. //}
  549. }
  550. write(c);
  551. }
  552. }
  553. // We are always writing all uint8_t to the buffer
  554. return 1;
  555. }
  556. size_t OLEDDisplay::write(const char* str) {
  557. if (str == NULL) return 0;
  558. size_t length = strlen(str);
  559. for (size_t i = 0; i < length; i++) {
  560. write(str[i]);
  561. }
  562. return length;
  563. }
  564. // Private functions
  565. void OLEDDisplay::sendInitCommands(void) {
  566. sendCommand(DISPLAYOFF);
  567. sendCommand(SETDISPLAYCLOCKDIV);
  568. sendCommand(0xF0); // Increase speed of the display max ~96Hz
  569. sendCommand(SETMULTIPLEX);
  570. sendCommand(0x3F);
  571. sendCommand(SETDISPLAYOFFSET);
  572. sendCommand(0x00);
  573. sendCommand(SETSTARTLINE);
  574. sendCommand(CHARGEPUMP);
  575. sendCommand(0x14);
  576. sendCommand(MEMORYMODE);
  577. sendCommand(0x00);
  578. sendCommand(SEGREMAP);
  579. sendCommand(COMSCANINC);
  580. sendCommand(SETCOMPINS);
  581. sendCommand(0x12);
  582. sendCommand(SETCONTRAST);
  583. sendCommand(0xCF);
  584. sendCommand(SETPRECHARGE);
  585. sendCommand(0xF1);
  586. sendCommand(DISPLAYALLON_RESUME);
  587. sendCommand(NORMALDISPLAY);
  588. sendCommand(0x2e); // stop scroll
  589. sendCommand(DISPLAYON);
  590. }
  591. void inline OLEDDisplay::drawInternal(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const char *data, uint16_t offset, uint16_t bytesInData) {
  592. if (width < 0 || height < 0) return;
  593. if (yMove + height < 0 || yMove > DISPLAY_HEIGHT) return;
  594. if (xMove + width < 0 || xMove > DISPLAY_WIDTH) return;
  595. uint8_t rasterHeight = 1 + ((height - 1) >> 3); // fast ceil(height / 8.0)
  596. int8_t yOffset = yMove & 7;
  597. bytesInData = bytesInData == 0 ? width * rasterHeight : bytesInData;
  598. int16_t initYMove = yMove;
  599. int8_t initYOffset = yOffset;
  600. for (uint16_t i = 0; i < bytesInData; i++) {
  601. // Reset if next horizontal drawing phase is started.
  602. if ( i % rasterHeight == 0) {
  603. yMove = initYMove;
  604. yOffset = initYOffset;
  605. }
  606. byte currentByte = pgm_read_byte(data + offset + i);
  607. int16_t xPos = xMove + (i / rasterHeight);
  608. int16_t yPos = ((yMove >> 3) + (i % rasterHeight)) * DISPLAY_WIDTH;
  609. int16_t yScreenPos = yMove + yOffset;
  610. int16_t dataPos = xPos + yPos;
  611. if (dataPos >= 0 && dataPos < DISPLAY_BUFFER_SIZE &&
  612. xPos >= 0 && xPos < DISPLAY_WIDTH ) {
  613. if (yOffset >= 0) {
  614. switch (this->color) {
  615. case WHITE: buffer[dataPos] |= currentByte << yOffset; break;
  616. case BLACK: buffer[dataPos] &= ~(currentByte << yOffset); break;
  617. case INVERSE: buffer[dataPos] ^= currentByte << yOffset; break;
  618. }
  619. if (dataPos < (DISPLAY_BUFFER_SIZE - DISPLAY_WIDTH)) {
  620. switch (this->color) {
  621. case WHITE: buffer[dataPos + DISPLAY_WIDTH] |= currentByte >> (8 - yOffset); break;
  622. case BLACK: buffer[dataPos + DISPLAY_WIDTH] &= ~(currentByte >> (8 - yOffset)); break;
  623. case INVERSE: buffer[dataPos + DISPLAY_WIDTH] ^= currentByte >> (8 - yOffset); break;
  624. }
  625. }
  626. } else {
  627. // Make new offset position
  628. yOffset = -yOffset;
  629. switch (this->color) {
  630. case WHITE: buffer[dataPos] |= currentByte >> yOffset; break;
  631. case BLACK: buffer[dataPos] &= ~(currentByte >> yOffset); break;
  632. case INVERSE: buffer[dataPos] ^= currentByte >> yOffset; break;
  633. }
  634. // Prepare for next iteration by moving one block up
  635. yMove -= 8;
  636. // and setting the new yOffset
  637. yOffset = 8 - yOffset;
  638. }
  639. yield();
  640. }
  641. }
  642. }
  643. // Code form http://playground.arduino.cc/Main/Utf8ascii
  644. uint8_t OLEDDisplay::utf8ascii(byte ascii) {
  645. static uint8_t LASTCHAR;
  646. if ( ascii < 128 ) { // Standard ASCII-set 0..0x7F handling
  647. LASTCHAR = 0;
  648. return ascii;
  649. }
  650. uint8_t last = LASTCHAR; // get last char
  651. LASTCHAR = ascii;
  652. switch (last) { // conversion depnding on first UTF8-character
  653. case 0xC2: return (ascii); break;
  654. case 0xC3: return (ascii | 0xC0); break;
  655. case 0x82: if (ascii == 0xAC) return (0x80); // special case Euro-symbol
  656. }
  657. return 0; // otherwise: return zero, if character has to be ignored
  658. }
  659. // You need to free the char!
  660. char* OLEDDisplay::utf8ascii(String str) {
  661. uint16_t k = 0;
  662. uint16_t length = str.length() + 1;
  663. // Copy the string into a char array
  664. char* s = (char*) malloc(length * sizeof(char));
  665. if(!s) {
  666. DEBUG_OLEDDISPLAY("[OLEDDISPLAY][utf8ascii] Can't allocate another char array. Drop support for UTF-8.\n");
  667. return (char*) str.c_str();
  668. }
  669. str.toCharArray(s, length);
  670. length--;
  671. for (uint16_t i=0; i < length; i++) {
  672. char c = utf8ascii(s[i]);
  673. if (c!=0) {
  674. s[k++]=c;
  675. }
  676. }
  677. s[k]=0;
  678. // This will leak 's' be sure to free it in the calling function.
  679. return s;
  680. }