123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810 |
- /**
- * The MIT License (MIT)
- *
- * Copyright (c) 2016 by Daniel Eichhorn
- * Copyright (c) 2016 by Fabrice Weinberg
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *
- * Credits for parts of this code go to Mike Rankin. Thank you so much for sharing!
- */
- #include "OLEDDisplay.h"
- bool OLEDDisplay::init() {
- if (!this->connect()) {
- DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Can't establish connection to display\n");
- return false;
- }
- this->buffer = (uint8_t*) malloc(sizeof(uint8_t) * DISPLAY_BUFFER_SIZE);
- if(!this->buffer) {
- DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Not enough memory to create display\n");
- return false;
- }
- #ifdef OLEDDISPLAY_DOUBLE_BUFFER
- this->buffer_back = (uint8_t*) malloc(sizeof(uint8_t) * DISPLAY_BUFFER_SIZE);
- if(!this->buffer_back) {
- DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Not enough memory to create back buffer\n");
- free(this->buffer);
- return false;
- }
- #endif
- sendInitCommands();
- resetDisplay();
- return true;
- }
- void OLEDDisplay::end() {
- if (this->buffer) free(this->buffer);
- #ifdef OLEDDISPLAY_DOUBLE_BUFFER
- if (this->buffer_back) free(this->buffer_back);
- #endif
- }
- void OLEDDisplay::resetDisplay(void) {
- clear();
- #ifdef OLEDDISPLAY_DOUBLE_BUFFER
- memset(buffer_back, 1, DISPLAY_BUFFER_SIZE);
- #endif
- display();
- }
- void OLEDDisplay::setColor(OLEDDISPLAY_COLOR color) {
- this->color = color;
- }
- void OLEDDisplay::setPixel(int16_t x, int16_t y) {
- if (x >= 0 && x < 128 && y >= 0 && y < 64) {
- switch (color) {
- case WHITE: buffer[x + (y / 8) * DISPLAY_WIDTH] |= (1 << (y & 7)); break;
- case BLACK: buffer[x + (y / 8) * DISPLAY_WIDTH] &= ~(1 << (y & 7)); break;
- case INVERSE: buffer[x + (y / 8) * DISPLAY_WIDTH] ^= (1 << (y & 7)); break;
- }
- }
- }
- // Bresenham's algorithm - thx wikipedia and Adafruit_GFX
- void OLEDDisplay::drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1) {
- int16_t steep = abs(y1 - y0) > abs(x1 - x0);
- if (steep) {
- _swap_int16_t(x0, y0);
- _swap_int16_t(x1, y1);
- }
- if (x0 > x1) {
- _swap_int16_t(x0, x1);
- _swap_int16_t(y0, y1);
- }
- int16_t dx, dy;
- dx = x1 - x0;
- dy = abs(y1 - y0);
- int16_t err = dx / 2;
- int16_t ystep;
- if (y0 < y1) {
- ystep = 1;
- } else {
- ystep = -1;
- }
- for (; x0<=x1; x0++) {
- if (steep) {
- setPixel(y0, x0);
- } else {
- setPixel(x0, y0);
- }
- err -= dy;
- if (err < 0) {
- y0 += ystep;
- err += dx;
- }
- }
- }
- void OLEDDisplay::drawRect(int16_t x, int16_t y, int16_t width, int16_t height) {
- drawHorizontalLine(x, y, width);
- drawVerticalLine(x, y, height);
- drawVerticalLine(x + width - 1, y, height);
- drawHorizontalLine(x, y + height - 1, width);
- }
- void OLEDDisplay::fillRect(int16_t xMove, int16_t yMove, int16_t width, int16_t height) {
- for (int16_t x = xMove; x < xMove + width; x++) {
- drawVerticalLine(x, yMove, height);
- }
- }
- void OLEDDisplay::drawCircle(int16_t x0, int16_t y0, int16_t radius) {
- int16_t x = 0, y = radius;
- int16_t dp = 1 - radius;
- do {
- if (dp < 0)
- dp = dp + 2 * (++x) + 3;
- else
- dp = dp + 2 * (++x) - 2 * (--y) + 5;
- setPixel(x0 + x, y0 + y); //For the 8 octants
- setPixel(x0 - x, y0 + y);
- setPixel(x0 + x, y0 - y);
- setPixel(x0 - x, y0 - y);
- setPixel(x0 + y, y0 + x);
- setPixel(x0 - y, y0 + x);
- setPixel(x0 + y, y0 - x);
- setPixel(x0 - y, y0 - x);
- } while (x < y);
- setPixel(x0 + radius, y0);
- setPixel(x0, y0 + radius);
- setPixel(x0 - radius, y0);
- setPixel(x0, y0 - radius);
- }
- void OLEDDisplay::drawCircleQuads(int16_t x0, int16_t y0, int16_t radius, uint8_t quads) {
- int16_t x = 0, y = radius;
- int16_t dp = 1 - radius;
- while (x < y) {
- if (dp < 0)
- dp = dp + 2 * (++x) + 3;
- else
- dp = dp + 2 * (++x) - 2 * (--y) + 5;
- if (quads & 0x1) {
- setPixel(x0 + x, y0 - y);
- setPixel(x0 + y, y0 - x);
- }
- if (quads & 0x2) {
- setPixel(x0 - y, y0 - x);
- setPixel(x0 - x, y0 - y);
- }
- if (quads & 0x4) {
- setPixel(x0 - y, y0 + x);
- setPixel(x0 - x, y0 + y);
- }
- if (quads & 0x8) {
- setPixel(x0 + x, y0 + y);
- setPixel(x0 + y, y0 + x);
- }
- }
- if (quads & 0x1 && quads & 0x8) {
- setPixel(x0 + radius, y0);
- }
- if (quads & 0x4 && quads & 0x8) {
- setPixel(x0, y0 + radius);
- }
- if (quads & 0x2 && quads & 0x4) {
- setPixel(x0 - radius, y0);
- }
- if (quads & 0x1 && quads & 0x2) {
- setPixel(x0, y0 - radius);
- }
- }
- void OLEDDisplay::fillCircle(int16_t x0, int16_t y0, int16_t radius) {
- int16_t x = 0, y = radius;
- int16_t dp = 1 - radius;
- do {
- if (dp < 0)
- dp = dp + 2 * (++x) + 3;
- else
- dp = dp + 2 * (++x) - 2 * (--y) + 5;
- drawHorizontalLine(x0 - x, y0 - y, 2*x);
- drawHorizontalLine(x0 - x, y0 + y, 2*x);
- drawHorizontalLine(x0 - y, y0 - x, 2*y);
- drawHorizontalLine(x0 - y, y0 + x, 2*y);
- } while (x < y);
- drawHorizontalLine(x0 - radius, y0, 2 * radius);
- }
- void OLEDDisplay::drawHorizontalLine(int16_t x, int16_t y, int16_t length) {
- if (y < 0 || y >= DISPLAY_HEIGHT) { return; }
- if (x < 0) {
- length += x;
- x = 0;
- }
- if ( (x + length) > DISPLAY_WIDTH) {
- length = (DISPLAY_WIDTH - x);
- }
- if (length <= 0) { return; }
- uint8_t * bufferPtr = buffer;
- bufferPtr += (y >> 3) * DISPLAY_WIDTH;
- bufferPtr += x;
- uint8_t drawBit = 1 << (y & 7);
- switch (color) {
- case WHITE: while (length--) {
- *bufferPtr++ |= drawBit;
- }; break;
- case BLACK: drawBit = ~drawBit; while (length--) {
- *bufferPtr++ &= drawBit;
- }; break;
- case INVERSE: while (length--) {
- *bufferPtr++ ^= drawBit;
- }; break;
- }
- }
- void OLEDDisplay::drawVerticalLine(int16_t x, int16_t y, int16_t length) {
- if (x < 0 || x >= DISPLAY_WIDTH) return;
- if (y < 0) {
- length += y;
- y = 0;
- }
- if ( (y + length) > DISPLAY_HEIGHT) {
- length = (DISPLAY_HEIGHT - y);
- }
- if (length <= 0) return;
- uint8_t yOffset = y & 7;
- uint8_t drawBit;
- uint8_t *bufferPtr = buffer;
- bufferPtr += (y >> 3) * DISPLAY_WIDTH;
- bufferPtr += x;
- if (yOffset) {
- yOffset = 8 - yOffset;
- drawBit = ~(0xFF >> (yOffset));
- if (length < yOffset) {
- drawBit &= (0xFF >> (yOffset - length));
- }
- switch (color) {
- case WHITE: *bufferPtr |= drawBit; break;
- case BLACK: *bufferPtr &= ~drawBit; break;
- case INVERSE: *bufferPtr ^= drawBit; break;
- }
- if (length < yOffset) return;
- length -= yOffset;
- bufferPtr += DISPLAY_WIDTH;
- }
- if (length >= 8) {
- switch (color) {
- case WHITE:
- case BLACK:
- drawBit = (color == WHITE) ? 0xFF : 0x00;
- do {
- *bufferPtr = drawBit;
- bufferPtr += DISPLAY_WIDTH;
- length -= 8;
- } while (length >= 8);
- break;
- case INVERSE:
- do {
- *bufferPtr = ~(*bufferPtr);
- bufferPtr += DISPLAY_WIDTH;
- length -= 8;
- } while (length >= 8);
- break;
- }
- }
- if (length > 0) {
- drawBit = (1 << (length & 7)) - 1;
- switch (color) {
- case WHITE: *bufferPtr |= drawBit; break;
- case BLACK: *bufferPtr &= ~drawBit; break;
- case INVERSE: *bufferPtr ^= drawBit; break;
- }
- }
- }
- void OLEDDisplay::drawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t progress) {
- uint16_t radius = height / 2;
- uint16_t xRadius = x + radius;
- uint16_t yRadius = y + radius;
- uint16_t doubleRadius = 2 * radius;
- uint16_t innerRadius = radius - 2;
- setColor(WHITE);
- drawCircleQuads(xRadius, yRadius, radius, 0b00000110);
- drawHorizontalLine(xRadius, y, width - doubleRadius + 1);
- drawHorizontalLine(xRadius, y + height, width - doubleRadius + 1);
- drawCircleQuads(x + width - radius, yRadius, radius, 0b00001001);
- uint16_t maxProgressWidth = (width - doubleRadius - 1) * progress / 100;
- fillCircle(xRadius, yRadius, innerRadius);
- fillRect(xRadius + 1, y + 2, maxProgressWidth, height - 3);
- fillCircle(xRadius + maxProgressWidth, yRadius, innerRadius);
- }
- void OLEDDisplay::drawFastImage(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const char *image) {
- drawInternal(xMove, yMove, width, height, image, 0, 0);
- }
- void OLEDDisplay::drawXbm(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const char *xbm) {
- int16_t widthInXbm = (width + 7) / 8;
- uint8_t data;
- for(int16_t y = 0; y < height; y++) {
- for(int16_t x = 0; x < width; x++ ) {
- if (x & 7) {
- data >>= 1; // Move a bit
- } else { // Read new data every 8 bit
- data = pgm_read_byte(xbm + (x / 8) + y * widthInXbm);
- }
- // if there is a bit draw it
- if (data & 0x01) {
- setPixel(xMove + x, yMove + y);
- }
- }
- }
- }
- void OLEDDisplay::drawStringInternal(int16_t xMove, int16_t yMove, char* text, uint16_t textLength, uint16_t textWidth) {
- uint8_t textHeight = pgm_read_byte(fontData + HEIGHT_POS);
- uint8_t firstChar = pgm_read_byte(fontData + FIRST_CHAR_POS);
- uint16_t sizeOfJumpTable = pgm_read_byte(fontData + CHAR_NUM_POS) * JUMPTABLE_BYTES;
- uint8_t cursorX = 0;
- uint8_t cursorY = 0;
- switch (textAlignment) {
- case TEXT_ALIGN_CENTER_BOTH:
- yMove -= textHeight >> 1;
- // Fallthrough
- case TEXT_ALIGN_CENTER:
- xMove -= textWidth >> 1; // divide by 2
- break;
- case TEXT_ALIGN_RIGHT:
- xMove -= textWidth;
- break;
- }
- // Don't draw anything if it is not on the screen.
- if (xMove + textWidth < 0 || xMove > DISPLAY_WIDTH ) {return;}
- if (yMove + textHeight < 0 || yMove > DISPLAY_HEIGHT) {return;}
- for (uint16_t j = 0; j < textLength; j++) {
- int16_t xPos = xMove + cursorX;
- int16_t yPos = yMove + cursorY;
- byte code = text[j];
- if (code >= firstChar) {
- byte charCode = code - firstChar;
- // 4 Bytes per char code
- byte msbJumpToChar = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES ); // MSB \ JumpAddress
- byte lsbJumpToChar = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_LSB); // LSB /
- byte charByteSize = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_SIZE); // Size
- byte currentCharWidth = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_WIDTH); // Width
- // Test if the char is drawable
- if (!(msbJumpToChar == 255 && lsbJumpToChar == 255)) {
- // Get the position of the char data
- uint16_t charDataPosition = JUMPTABLE_START + sizeOfJumpTable + ((msbJumpToChar << 8) + lsbJumpToChar);
- drawInternal(xPos, yPos, currentCharWidth, textHeight, fontData, charDataPosition, charByteSize);
- }
- cursorX += currentCharWidth;
- }
- }
- }
- void OLEDDisplay::drawString(int16_t xMove, int16_t yMove, String strUser) {
- uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS);
- // char* text must be freed!
- char* text = utf8ascii(strUser);
- uint16_t yOffset = 0;
- // If the string should be centered vertically too
- // we need to now how heigh the string is.
- if (textAlignment == TEXT_ALIGN_CENTER_BOTH) {
- uint16_t lb = 0;
- // Find number of linebreaks in text
- for (uint16_t i=0;text[i] != 0; i++) {
- lb += (text[i] == 10);
- }
- // Calculate center
- yOffset = (lb * lineHeight) / 2;
- }
- uint16_t line = 0;
- char* textPart = strtok(text,"\n");
- while (textPart != NULL) {
- uint16_t length = strlen(textPart);
- drawStringInternal(xMove, yMove - yOffset + (line++) * lineHeight, textPart, length, getStringWidth(textPart, length));
- textPart = strtok(NULL, "\n");
- }
- free(text);
- }
- void OLEDDisplay::drawStringMaxWidth(int16_t xMove, int16_t yMove, uint16_t maxLineWidth, String strUser) {
- uint16_t firstChar = pgm_read_byte(fontData + FIRST_CHAR_POS);
- uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS);
- char* text = utf8ascii(strUser);
- uint16_t length = strlen(text);
- uint16_t lastDrawnPos = 0;
- uint16_t lineNumber = 0;
- uint16_t strWidth = 0;
- uint16_t preferredBreakpoint = 0;
- uint16_t widthAtBreakpoint = 0;
- for (uint16_t i = 0; i < length; i++) {
- strWidth += pgm_read_byte(fontData + JUMPTABLE_START + (text[i] - firstChar) * JUMPTABLE_BYTES + JUMPTABLE_WIDTH);
- // Always try to break on a space or dash
- if (text[i] == ' ' || text[i]== '-') {
- preferredBreakpoint = i;
- widthAtBreakpoint = strWidth;
- }
- if (strWidth >= maxLineWidth) {
- if (preferredBreakpoint == 0) {
- preferredBreakpoint = i;
- widthAtBreakpoint = strWidth;
- }
- drawStringInternal(xMove, yMove + (lineNumber++) * lineHeight , &text[lastDrawnPos], preferredBreakpoint - lastDrawnPos, widthAtBreakpoint);
- lastDrawnPos = preferredBreakpoint + 1;
- // It is possible that we did not draw all letters to i so we need
- // to account for the width of the chars from `i - preferredBreakpoint`
- // by calculating the width we did not draw yet.
- strWidth = strWidth - widthAtBreakpoint;
- preferredBreakpoint = 0;
- }
- }
- // Draw last part if needed
- if (lastDrawnPos < length) {
- drawStringInternal(xMove, yMove + lineNumber * lineHeight , &text[lastDrawnPos], length - lastDrawnPos, getStringWidth(&text[lastDrawnPos], length - lastDrawnPos));
- }
- free(text);
- }
- uint16_t OLEDDisplay::getStringWidth(const char* text, uint16_t length) {
- uint16_t firstChar = pgm_read_byte(fontData + FIRST_CHAR_POS);
- uint16_t stringWidth = 0;
- uint16_t maxWidth = 0;
- while (length--) {
- stringWidth += pgm_read_byte(fontData + JUMPTABLE_START + (text[length] - firstChar) * JUMPTABLE_BYTES + JUMPTABLE_WIDTH);
- if (text[length] == 10) {
- maxWidth = max(maxWidth, stringWidth);
- stringWidth = 0;
- }
- }
- return max(maxWidth, stringWidth);
- }
- uint16_t OLEDDisplay::getStringWidth(String strUser) {
- char* text = utf8ascii(strUser);
- uint16_t length = strlen(text);
- uint16_t width = getStringWidth(text, length);
- free(text);
- return width;
- }
- void OLEDDisplay::setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment) {
- this->textAlignment = textAlignment;
- }
- void OLEDDisplay::setFont(const char *fontData) {
- this->fontData = fontData;
- }
- void OLEDDisplay::displayOn(void) {
- sendCommand(DISPLAYON);
- }
- void OLEDDisplay::displayOff(void) {
- sendCommand(DISPLAYOFF);
- }
- void OLEDDisplay::invertDisplay(void) {
- sendCommand(INVERTDISPLAY);
- }
- void OLEDDisplay::normalDisplay(void) {
- sendCommand(NORMALDISPLAY);
- }
- void OLEDDisplay::setContrast(char contrast) {
- sendCommand(SETCONTRAST);
- sendCommand(contrast);
- }
- void OLEDDisplay::flipScreenVertically() {
- sendCommand(SEGREMAP | 0x01);
- sendCommand(COMSCANDEC); //Rotate screen 180 Deg
- }
- void OLEDDisplay::clear(void) {
- memset(buffer, 0, DISPLAY_BUFFER_SIZE);
- }
- void OLEDDisplay::drawLogBuffer(uint16_t xMove, uint16_t yMove) {
- uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS);
- // Always align left
- setTextAlignment(TEXT_ALIGN_LEFT);
- // State values
- uint16_t length = 0;
- uint16_t line = 0;
- uint16_t lastPos = 0;
- for (uint16_t i=0;i<this->logBufferFilled;i++){
- // Everytime we have a \n print
- if (this->logBuffer[i] == 10) {
- length++;
- // Draw string on line `line` from lastPos to length
- // Passing 0 as the lenght because we are in TEXT_ALIGN_LEFT
- drawStringInternal(xMove, yMove + (line++) * lineHeight, &this->logBuffer[lastPos], length, 0);
- // Remember last pos
- lastPos = i;
- // Reset length
- length = 0;
- } else {
- // Count chars until next linebreak
- length++;
- }
- }
- // Draw the remaining string
- if (length > 0) {
- drawStringInternal(xMove, yMove + line * lineHeight, &this->logBuffer[lastPos], length, 0);
- }
- }
- bool OLEDDisplay::setLogBuffer(uint16_t lines, uint16_t chars){
- if (logBuffer != NULL) free(logBuffer);
- uint16_t size = lines * chars;
- if (size > 0) {
- this->logBufferLine = 0; // Lines printed
- this->logBufferMaxLines = lines; // Lines max printable
- this->logBufferSize = size; // Total number of characters the buffer can hold
- this->logBuffer = (char *) malloc(size * sizeof(uint8_t));
- if(!this->logBuffer) {
- DEBUG_OLEDDISPLAY("[OLEDDISPLAY][setLogBuffer] Not enough memory to create log buffer\n");
- return false;
- }
- }
- return true;
- }
- size_t OLEDDisplay::write(uint8_t c) {
- if (this->logBufferSize > 0) {
- // Don't waste space on \r\n line endings, dropping \r
- if (c == 13) return 1;
- bool maxLineNotReached = this->logBufferLine < this->logBufferMaxLines;
- bool bufferNotFull = this->logBufferFilled < this->logBufferSize;
- // Can we write to the buffer?
- if (bufferNotFull && maxLineNotReached) {
- this->logBuffer[logBufferFilled] = utf8ascii(c);
- this->logBufferFilled++;
- // Keep track of lines written
- if (c == 10) this->logBufferLine++;
- } else {
- // Max line number is reached
- if (!maxLineNotReached) this->logBufferLine--;
- // Find the end of the first line
- uint16_t firstLineEnd = 0;
- for (uint16_t i=0;i<this->logBufferFilled;i++) {
- if (this->logBuffer[i] == 10){
- // Include last char too
- firstLineEnd = i + 1;
- break;
- }
- }
- // If there was a line ending
- if (firstLineEnd > 0) {
- // Calculate the new logBufferFilled value
- this->logBufferFilled = logBufferFilled - firstLineEnd;
- // Now we move the lines infront of the buffer
- memcpy(this->logBuffer, &this->logBuffer[firstLineEnd], logBufferFilled);
- } else {
- // Let's reuse the buffer if it was full
- if (!bufferNotFull) {
- this->logBufferFilled = 0;
- }// else {
- // Nothing to do here
- //}
- }
- write(c);
- }
- }
- // We are always writing all uint8_t to the buffer
- return 1;
- }
- size_t OLEDDisplay::write(const char* str) {
- if (str == NULL) return 0;
- size_t length = strlen(str);
- for (size_t i = 0; i < length; i++) {
- write(str[i]);
- }
- return length;
- }
- // Private functions
- void OLEDDisplay::sendInitCommands(void) {
- sendCommand(DISPLAYOFF);
- sendCommand(SETDISPLAYCLOCKDIV);
- sendCommand(0xF0); // Increase speed of the display max ~96Hz
- sendCommand(SETMULTIPLEX);
- sendCommand(0x3F);
- sendCommand(SETDISPLAYOFFSET);
- sendCommand(0x00);
- sendCommand(SETSTARTLINE);
- sendCommand(CHARGEPUMP);
- sendCommand(0x14);
- sendCommand(MEMORYMODE);
- sendCommand(0x00);
- sendCommand(SEGREMAP);
- sendCommand(COMSCANINC);
- sendCommand(SETCOMPINS);
- sendCommand(0x12);
- sendCommand(SETCONTRAST);
- sendCommand(0xCF);
- sendCommand(SETPRECHARGE);
- sendCommand(0xF1);
- sendCommand(DISPLAYALLON_RESUME);
- sendCommand(NORMALDISPLAY);
- sendCommand(0x2e); // stop scroll
- sendCommand(DISPLAYON);
- }
- 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) {
- if (width < 0 || height < 0) return;
- if (yMove + height < 0 || yMove > DISPLAY_HEIGHT) return;
- if (xMove + width < 0 || xMove > DISPLAY_WIDTH) return;
- uint8_t rasterHeight = 1 + ((height - 1) >> 3); // fast ceil(height / 8.0)
- int8_t yOffset = yMove & 7;
- bytesInData = bytesInData == 0 ? width * rasterHeight : bytesInData;
- int16_t initYMove = yMove;
- int8_t initYOffset = yOffset;
- for (uint16_t i = 0; i < bytesInData; i++) {
- // Reset if next horizontal drawing phase is started.
- if ( i % rasterHeight == 0) {
- yMove = initYMove;
- yOffset = initYOffset;
- }
- byte currentByte = pgm_read_byte(data + offset + i);
- int16_t xPos = xMove + (i / rasterHeight);
- int16_t yPos = ((yMove >> 3) + (i % rasterHeight)) * DISPLAY_WIDTH;
- int16_t yScreenPos = yMove + yOffset;
- int16_t dataPos = xPos + yPos;
- if (dataPos >= 0 && dataPos < DISPLAY_BUFFER_SIZE &&
- xPos >= 0 && xPos < DISPLAY_WIDTH ) {
- if (yOffset >= 0) {
- switch (this->color) {
- case WHITE: buffer[dataPos] |= currentByte << yOffset; break;
- case BLACK: buffer[dataPos] &= ~(currentByte << yOffset); break;
- case INVERSE: buffer[dataPos] ^= currentByte << yOffset; break;
- }
- if (dataPos < (DISPLAY_BUFFER_SIZE - DISPLAY_WIDTH)) {
- switch (this->color) {
- case WHITE: buffer[dataPos + DISPLAY_WIDTH] |= currentByte >> (8 - yOffset); break;
- case BLACK: buffer[dataPos + DISPLAY_WIDTH] &= ~(currentByte >> (8 - yOffset)); break;
- case INVERSE: buffer[dataPos + DISPLAY_WIDTH] ^= currentByte >> (8 - yOffset); break;
- }
- }
- } else {
- // Make new offset position
- yOffset = -yOffset;
- switch (this->color) {
- case WHITE: buffer[dataPos] |= currentByte >> yOffset; break;
- case BLACK: buffer[dataPos] &= ~(currentByte >> yOffset); break;
- case INVERSE: buffer[dataPos] ^= currentByte >> yOffset; break;
- }
- // Prepare for next iteration by moving one block up
- yMove -= 8;
- // and setting the new yOffset
- yOffset = 8 - yOffset;
- }
- yield();
- }
- }
- }
- // Code form http://playground.arduino.cc/Main/Utf8ascii
- uint8_t OLEDDisplay::utf8ascii(byte ascii) {
- static uint8_t LASTCHAR;
- if ( ascii < 128 ) { // Standard ASCII-set 0..0x7F handling
- LASTCHAR = 0;
- return ascii;
- }
- uint8_t last = LASTCHAR; // get last char
- LASTCHAR = ascii;
- switch (last) { // conversion depnding on first UTF8-character
- case 0xC2: return (ascii); break;
- case 0xC3: return (ascii | 0xC0); break;
- case 0x82: if (ascii == 0xAC) return (0x80); // special case Euro-symbol
- }
- return 0; // otherwise: return zero, if character has to be ignored
- }
- // You need to free the char!
- char* OLEDDisplay::utf8ascii(String str) {
- uint16_t k = 0;
- uint16_t length = str.length() + 1;
- // Copy the string into a char array
- char* s = (char*) malloc(length * sizeof(char));
- if(!s) {
- DEBUG_OLEDDISPLAY("[OLEDDISPLAY][utf8ascii] Can't allocate another char array. Drop support for UTF-8.\n");
- return (char*) str.c_str();
- }
- str.toCharArray(s, length);
- length--;
- for (uint16_t i=0; i < length; i++) {
- char c = utf8ascii(s[i]);
- if (c!=0) {
- s[k++]=c;
- }
- }
- s[k]=0;
- // This will leak 's' be sure to free it in the calling function.
- return s;
- }
|