Skip to content

Restore log buffer: revert upstream changes #14

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 1 addition & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[![PlatformIO Registry](https://badges.registry.platformio.org/packages/thingpulse/library/ESP8266%20and%20ESP32%20OLED%20driver%20for%20SSD1306%20displays.svg)](https://registry.platformio.org/libraries/thingpulse/ESP8266%20and%20ESP32%20OLED%20driver%20for%20SSD1306%20displays)
[![Build Status](https://github.com/ThingPulse/esp8266-oled-ssd1306/actions/workflows/main.yml/badge.svg)](https://github.com/ThingPulse/esp8266-oled-ssd1306/actions)

This is a driver for SSD1306 and SH1106 128x64, 128x32, 64x48 and 64x32 OLED displays running on the Arduino/ESP8266 & ESP32 and mbed-os platforms.
This is a driver for SSD1306 128x64, 128x32, 64x48 and 64x32 OLED displays running on the Arduino/ESP8266 & ESP32 and mbed-os platforms.
Can be used with either the I2C or SPI version of the display.

This library drives the OLED display included in the [ThingPulse IoT starter kit](https://thingpulse.com/product/esp8266-iot-electronics-starter-kit-weatherstation-planespotter-worldclock/) aka classic kit aka weather station kit.
Expand Down Expand Up @@ -262,27 +262,6 @@ void setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment);
void setFont(const uint8_t* fontData);
```

## Arduino `Print` functionality

Because this class has been "derived" from Arduino's `Print` class, you can use the functions it provides. In plain language, this means that you can use `print`, `println` and `printf` to the display. Internally, a buffer holds the text that was printed to the display previously (that would still fit on the display) and every time you print something, this buffer is put on the screen, using the functions from the previous section.

What that means is that printing using `print` and "manually" putting things on the display are somewhat mutually exclusive: as soon as you print, everything that was on the display already is gone and only what you put there before with `print`, `println` or `printf` remains. Still, using `print` is a very simple way to put something on the display quickly.

One extra function is provided: `cls()`
```cpp
// cls() will clear the display immediately and empty the logBuffer, meaning
// the next print statement will print at the top of the display again.
// cls() should not be confused with clear(), which only clears the internal
// graphics buffer, which can then be shown on the display with display().
void cls();

> _Note that printing to the display, contrary to what you might expect, does not wrap your lines, so everything on a line that doesn't fit on the screen is cut off._
```

 

<hr>

## Ui Library (OLEDDisplayUi)

The Ui Library is used to provide a basic set of user interface elements called `Frames` and `Overlays`. A `Frame` is used to provide
Expand Down
11 changes: 10 additions & 1 deletion examples/SSD1306DrawingDemo/SSD1306DrawingDemo.ino
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,10 @@ void drawCircle(void) {
}

void printBuffer(void) {
// Initialize the log buffer
// allocate memory to store 8 lines of text and 30 chars per line.
display.setLogBuffer(5, 30);

// Some test data
const char* test[] = {
"Hello",
Expand All @@ -185,10 +189,15 @@ void printBuffer(void) {
"scrolling is",
"working"
};
display.clear();

for (uint8_t i = 0; i < 11; i++) {
display.clear();
// Print to the screen
display.println(test[i]);
// Draw it to the internal screen buffer
display.drawLogBuffer(0, 0);
// Display it on the screen
display.display();
delay(500);
}
}
Expand Down
2 changes: 1 addition & 1 deletion library.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ESP8266 and ESP32 OLED driver for SSD1306 displays",
"version": "4.5.0",
"version": "4.4.1",
"keywords": "ssd1306, oled, display, i2c",
"description": "I2C display driver for SSD1306 OLED displays connected to ESP8266, ESP32, Mbed-OS",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=ESP8266 and ESP32 OLED driver for SSD1306 displays
version=4.5.0
version=4.4.1
author=ThingPulse, Fabrice Weinberg
maintainer=ThingPulse <[email protected]>
sentence=I2C display driver for SSD1306 OLED displays connected to ESP8266, ESP32, Mbed-OS
Expand Down
212 changes: 78 additions & 134 deletions src/OLEDDisplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
/*
* TODO Helmut
* - test/finish dislplay.printf() on mbed-os
* - Finish _putc with drawLogBuffer when running display
*/

#include "OLEDDisplay.h"
Expand All @@ -41,7 +42,6 @@ OLEDDisplay::OLEDDisplay() {
displayWidth = 128;
displayHeight = 64;
displayBufferSize = displayWidth * displayHeight / 8;
inhibitDrawLogBuffer = false;
color = WHITE;
geometry = GEOMETRY_128_64;
textAlignment = TEXT_ALIGN_LEFT;
Expand Down Expand Up @@ -764,12 +764,6 @@ void OLEDDisplay::setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment) {

void OLEDDisplay::setFont(const uint8_t *fontData) {
this->fontData = fontData;
// New font, so must recalculate. Whatever was there is gone at next print.
setLogBuffer();
}

void OLEDDisplay::setFont(const char *fontData) {
setFont(static_cast<const uint8_t*>(reinterpret_cast<const void*>(fontData)));
}

void OLEDDisplay::displayOn(void) {
Expand Down Expand Up @@ -838,10 +832,6 @@ void OLEDDisplay::clear(void) {
}

void OLEDDisplay::drawLogBuffer(uint16_t xMove, uint16_t yMove) {
Serial.println("[deprecated] Print functionality now handles buffer management automatically. This is a no-op.");
}

void OLEDDisplay::drawLogBuffer() {
uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS);
// Always align left
setTextAlignment(TEXT_ALIGN_LEFT);
Expand All @@ -851,27 +841,25 @@ void OLEDDisplay::drawLogBuffer() {
uint16_t line = 0;
uint16_t lastPos = 0;

// If the lineHeight and the display height are not cleanly divisible, we need
// to start off the screen when the buffer has logBufferMaxLines so that the
// first line, and not the last line, drops off.
uint16_t shiftUp = (this->logBufferLine == this->logBufferMaxLines) ? (lineHeight - (displayHeight % lineHeight)) % lineHeight : 0;

for (uint16_t i=0;i<this->logBufferFilled;i++){
length++;
// 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(0, 0 - shiftUp + (line++) * lineHeight, &this->logBuffer[lastPos], length, 0, false);
drawStringInternal(xMove, yMove + (line++) * lineHeight, &this->logBuffer[lastPos], length, 0, false);
// Remember last pos
lastPos = i;
// Reset length
length = 0;
} else {
// Count chars until next linebreak
length++;
}
}
// Draw the remaining string
if (length > 0) {
drawStringInternal(0, 0 - shiftUp + line * lineHeight, &this->logBuffer[lastPos], length, 0, false);
drawStringInternal(xMove, yMove + line * lineHeight, &this->logBuffer[lastPos], length, 0, false);
}
}

Expand All @@ -883,146 +871,102 @@ uint16_t OLEDDisplay::getHeight(void) {
return displayHeight;
}

void OLEDDisplay::cls() {
clear();
this->logBufferFilled = 0;
this->logBufferLine = 0;
display();
}

bool OLEDDisplay::setLogBuffer(uint16_t lines, uint16_t chars) {
Serial.println("[deprecated] Print functionality now handles buffer management automatically. This is a no-op.");
return true;
}

bool OLEDDisplay::setLogBuffer(){
// don't know how big we need it without a font set.
if (!fontData)
return false;

// we're always starting over
if (logBuffer != NULL)
free(logBuffer);

// figure out how big it needs to be
uint8_t textHeight = pgm_read_byte(fontData + HEIGHT_POS);
if (!textHeight)
return false; // Prevent division by zero crashes
uint16_t lines = this->displayHeight / textHeight + (this->displayHeight % textHeight ? 1 : 0);
uint16_t chars = 5 * (this->displayWidth / textHeight);
uint16_t size = lines * (chars + 1); // +1 is for \n

// Something weird must have happened
if (size == 0)
return false;

// All good, initialize logBuffer
this->logBufferLine = 0; // Lines printed
this->logBufferFilled = 0; // Nothing stored yet
this->logBufferMaxLines = lines; // Lines max printable
this->logBufferLineLen = chars; // Chars per line
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;
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->logBufferFilled = 0; // Nothing stored yet
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 (!fontData)
return 1;

// Create a logBuffer if there isn't one
if (!logBufferSize) {
// Give up if we can't create a logBuffer somehow
if (!setLogBuffer())
return 1;
}

// Don't waste space on \r\n line endings, dropping \r
if (c == 13) return 1;

// convert UTF-8 character to font table index
c = (this->fontTableLookupFunction)(c);
// drop unknown character
if (c == 0) return 1;

bool maxLineReached = this->logBufferLine >= this->logBufferMaxLines;
bool bufferFull = this->logBufferFilled >= this->logBufferSize;

// Can we write to the buffer? If not, make space.
if (bufferFull || maxLineReached) {
// See if we can chop off 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;
if (this->logBufferSize > 0) {
// Don't waste space on \r\n line endings, dropping \r
if (c == 13) return 1;

// convert UTF-8 character to font table index
c = (this->fontTableLookupFunction)(c);
// drop unknown character
if (c == 0) 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] = 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 move other lines to front of the buffer
// Now we move the lines infront of the buffer
memcpy(this->logBuffer, &this->logBuffer[firstLineEnd], logBufferFilled);
// And voila, buffer one line shorter
this->logBufferLine--;
break;
} else {
// Let's reuse the buffer if it was full
if (!bufferNotFull) {
this->logBufferFilled = 0;
}// else {
// Nothing to do here
//}
}
write(c);
}
// In we can't take off first line, we just empty the buffer
if (!firstLineEnd) {
this->logBufferFilled = 0;
this->logBufferLine = 0;
}
}

// So now we know for sure we have space in the buffer

// Find the length of the last line
uint16_t lastLineLen= 0;
for (uint16_t i = 0; i < this->logBufferFilled; i++) {
lastLineLen++;
if (this->logBuffer[i] == 10) lastLineLen = 0;
}
// if last line is max length, ignore anything but linebreaks
if (lastLineLen >= this->logBufferLineLen) {
if (c != 10) return 1;
}

// Write to buffer
this->logBuffer[this->logBufferFilled++] = c;
// Keep track of lines written
if (c == 10) this->logBufferLine++;

// Draw to screen unless we're writing a whole string at a time
if (!this->inhibitDrawLogBuffer) {
clear();
drawLogBuffer();
display();
}

// We always claim we printed it all
// 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);
// If we write a string, only do the drawLogBuffer at the end, not every time we write a char
this->inhibitDrawLogBuffer = true;
for (size_t i = 0; i < length; i++) {
write(str[i]);
}
this->inhibitDrawLogBuffer = false;
clear();
drawLogBuffer();
display();
return length;
}

#ifdef __MBED__
int OLEDDisplay::_putc(int c) {

if (!fontData)
return 1;
if (!logBufferSize) {
uint8_t textHeight = pgm_read_byte(fontData + HEIGHT_POS);
uint16_t lines = this->displayHeight / textHeight;
uint16_t chars = 2 * (this->displayWidth / textHeight);

if (this->displayHeight % textHeight)
lines++;
if (this->displayWidth % textHeight)
chars++;
setLogBuffer(lines, chars);
}

return this->write((uint8_t)c);
}
#endif
Expand Down
Loading
Loading