diff --git a/README.md b/README.md index 2263b24..6f58056 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ [![Build Status](https://travis-ci.org/ThingPulse/esp8266-oled-ssd1306.svg?branch=master)](https://travis-ci.org/ThingPulse/esp8266-oled-ssd1306) -# ThingPulse ESP8266 OLED SSD1306 +# ThingPulse OLED SSD1306 (ESP8266/ESP32/Mbed-OS) > We just released version 4.0.0. Please have a look at our [upgrade guide](UPGRADE-4.0.md) -This is a driver for SSD1306 128x64 and 128x32 OLED displays running on the Arduino/ESP8266 platform. -Can be used with either the I2C or SPI version of the display. +This is a driver for the SSD1306 based 128x64 pixel OLED display running on the Arduino (ESP8266 and ESP32) and mbed-os platform. +Can be used with either the I2C or SPI version of the display -You can either download this library as a zip file and unpack it to your Arduino/libraries folder or find it in the Arduino library manager under "ESP8266 and ESP32 Oled Driver for SSD1306 display". +You can either download this library as a zip file and unpack it to your Arduino/libraries folder or (once it has been added) choose it from the Arduino library manager. For mbed-os a copy of the files are available as an mbed-os library It is also available as a platformio library. Just execute the following command: ``` @@ -24,6 +24,9 @@ platformio lib install 562 This library has initially been written by Daniel Eichhorn (@squix78). Many thanks go to Fabrice Weinberg (@FWeinb) for optimizing and refactoring many aspects of the library. Also many thanks to the many committers who helped to add new features and who fixed many bugs. The init sequence for the SSD1306 was inspired by Adafruit's library for the same display. +## mbed-os +This library has been adopted to support the ARM mbed-os environment. A copy of this library is available in mbed-os under the name OLED_SSD1306 by Helmut Tschemernjak. An alternate installation option is to copy the following files into your mbed-os project: OLEDDisplay.cpp OLEDDisplay.h OLEDDisplayFonts.h OLEDDisplayUi.cpp OLEDDisplayUi.h SSD1306I2C.h + ## Usage Check out the examples folder for a few comprehensive demonstrations how to use the library. Also check out the [ESP8266 Weather Station](https://github.com/ThingPulse/esp8266-weather-station) library which uses the OLED library to display beautiful weather information. @@ -119,8 +122,9 @@ SH1106Spi display(D0, D2); // RES, DC ### Display Control ```C++ -// Initialize the display -void init(); +// Initialize the display via init(); +// Optionally the default fonts can be overwritten to save flash memory, e.g. init(ArialMT_Plain_10) +void init(const uint8_t *defaultFontData); // Free the memory used by the display void end(); @@ -175,6 +179,9 @@ void setColor(OLEDDISPLAY_COLOR color); // Draw a pixel at given position void setPixel(int16_t x, int16_t y); +// Clear a pixel at given position FIXME: INVERSE is untested with this function +void clearPixel(int16_t x, int16_t y); + // Draw a line from position 0 to position 1 void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1); diff --git a/src/OLEDDisplay.cpp b/src/OLEDDisplay.cpp index 8b68dd0..e2c469e 100644 --- a/src/OLEDDisplay.cpp +++ b/src/OLEDDisplay.cpp @@ -3,6 +3,7 @@ * * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn * Copyright (c) 2018 by Fabrice Weinberg + * Copyright (c) 2019 by Helmut Tschemernjak - www.radioshuttle.de * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,20 +29,51 @@ * */ + /* + * TODO Helmut + * - test/finish dislplay.printf() on mbed-os + * - Finish _putc with drawLogBuffer when running display + */ + #include "OLEDDisplay.h" +OLEDDisplay::OLEDDisplay() { + + displayWidth = 128; + displayHeight = 64; + displayBufferSize = displayWidth * displayHeight / 8; + color = WHITE; + geometry = GEOMETRY_128_64; + textAlignment = TEXT_ALIGN_LEFT; + fontData = NULL; + fontTableLookupFunction = DefaultFontTableLookup; + buffer = NULL; +#ifdef OLEDDISPLAY_DOUBLE_BUFFER + buffer_back = NULL; +#endif +} + OLEDDisplay::~OLEDDisplay() { end(); } -bool OLEDDisplay::init() { +bool OLEDDisplay::init(const uint8_t *defaultFontData) { + + fontData = defaultFontData; + logBufferSize = 0; + logBufferFilled = 0; + logBufferLine = 0; + logBufferMaxLines = 0; + logBuffer = NULL; + if (!this->connect()) { DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Can't establish connection to display\n"); return false; } if(this->buffer==NULL) { - this->buffer = (uint8_t*) malloc(sizeof(uint8_t) * displayBufferSize); + this->buffer = (uint8_t*) malloc((sizeof(uint8_t) * displayBufferSize) + getBufferOffset()); + this->buffer += getBufferOffset(); if(!this->buffer) { DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Not enough memory to create display\n"); @@ -51,11 +83,12 @@ bool OLEDDisplay::init() { #ifdef OLEDDISPLAY_DOUBLE_BUFFER if(this->buffer_back==NULL) { - this->buffer_back = (uint8_t*) malloc(sizeof(uint8_t) * displayBufferSize); + this->buffer_back = (uint8_t*) malloc((sizeof(uint8_t) * displayBufferSize) + getBufferOffset()); + this->buffer_back += getBufferOffset(); if(!this->buffer_back) { DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Not enough memory to create back buffer\n"); - free(this->buffer); + free(this->buffer - getBufferOffset()); return false; } } @@ -68,9 +101,9 @@ bool OLEDDisplay::init() { } void OLEDDisplay::end() { - if (this->buffer) { free(this->buffer); this->buffer = NULL; } + if (this->buffer) { free(this->buffer - getBufferOffset()); this->buffer = NULL; } #ifdef OLEDDISPLAY_DOUBLE_BUFFER - if (this->buffer_back) { free(this->buffer_back); this->buffer_back = NULL; } + if (this->buffer_back) { free(this->buffer_back - getBufferOffset()); this->buffer_back = NULL; } #endif if (this->logBuffer != NULL) { free(this->logBuffer); this->logBuffer = NULL; } } @@ -405,8 +438,8 @@ void OLEDDisplay::drawStringInternal(int16_t xMove, int16_t yMove, char* text, u 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; + uint16_t cursorX = 0; + uint16_t cursorY = 0; switch (textAlignment) { case TEXT_ALIGN_CENTER_BOTH: @@ -430,15 +463,15 @@ void OLEDDisplay::drawStringInternal(int16_t xMove, int16_t yMove, char* text, u int16_t xPos = xMove + cursorX; int16_t yPos = yMove + cursorY; - byte code = text[j]; + uint8_t code = text[j]; if (code >= firstChar) { - byte charCode = code - firstChar; + uint8_t 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 + uint8_t msbJumpToChar = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES ); // MSB \ JumpAddress + uint8_t lsbJumpToChar = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_LSB); // LSB / + uint8_t charByteSize = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_SIZE); // Size + uint8_t currentCharWidth = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_WIDTH); // Width // Test if the char is drawable if (!(msbJumpToChar == 255 && lsbJumpToChar == 255)) { @@ -745,20 +778,50 @@ size_t OLEDDisplay::write(const char* str) { 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 + // Private functions -void OLEDDisplay::setGeometry(OLEDDISPLAY_GEOMETRY g) { +void OLEDDisplay::setGeometry(OLEDDISPLAY_GEOMETRY g, uint16_t width, uint16_t height) { this->geometry = g; - if (g == GEOMETRY_128_64) { - this->displayWidth = 128; - this->displayHeight = 64; - } else if (g == GEOMETRY_128_32) { - this->displayWidth = 128; - this->displayHeight = 32; - } - this->displayBufferSize = displayWidth*displayHeight/8; + switch (g) { + case GEOMETRY_128_64: + this->displayWidth = 128; + this->displayHeight = 64; + break; + case GEOMETRY_128_32: + this->displayWidth = 128; + this->displayHeight = 32; + break; + case GEOMETRY_RAWMODE: + this->displayWidth = width > 0 ? width : 128; + this->displayHeight = height > 0 ? height : 64; + break; + } + this->displayBufferSize = displayWidth * displayHeight /8; } void OLEDDisplay::sendInitCommands(void) { + if (geometry == GEOMETRY_RAWMODE) + return; sendCommand(DISPLAYOFF); sendCommand(SETDISPLAYCLOCKDIV); sendCommand(0xF0); // Increase speed of the display max ~96Hz @@ -821,7 +884,7 @@ void inline OLEDDisplay::drawInternal(int16_t xMove, int16_t yMove, int16_t widt yOffset = initYOffset; } - byte currentByte = pgm_read_byte(data + offset + i); + uint8_t currentByte = pgm_read_byte(data + offset + i); int16_t xPos = xMove + (i / rasterHeight); int16_t yPos = ((yMove >> 3) + (i % rasterHeight)) * this->width(); @@ -862,8 +925,9 @@ void inline OLEDDisplay::drawInternal(int16_t xMove, int16_t yMove, int16_t widt // and setting the new yOffset yOffset = 8 - yOffset; } - +#ifndef __MBED__ yield(); +#endif } } } @@ -899,3 +963,26 @@ char* OLEDDisplay::utf8ascii(String str) { void OLEDDisplay::setFontTableLookupFunction(FontTableLookupFunction function) { this->fontTableLookupFunction = function; } + + +char DefaultFontTableLookup(const uint8_t ch) { + // UTF-8 to font table index converter + // Code form http://playground.arduino.cc/Main/Utf8ascii + static uint8_t LASTCHAR; + + if (ch < 128) { // Standard ASCII-set 0..0x7F handling + LASTCHAR = 0; + return ch; + } + + uint8_t last = LASTCHAR; // get last char + LASTCHAR = ch; + + switch (last) { // conversion depnding on first UTF8-character + case 0xC2: return (uint8_t) ch; + case 0xC3: return (uint8_t) (ch | 0xC0); + case 0x82: if (ch == 0xAC) return (uint8_t) 0x80; // special case Euro-symbol + } + + return (uint8_t) 0; // otherwise: return zero, if character has to be ignored +} diff --git a/src/OLEDDisplay.h b/src/OLEDDisplay.h index c02c1a6..4447152 100644 --- a/src/OLEDDisplay.h +++ b/src/OLEDDisplay.h @@ -3,6 +3,7 @@ * * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn * Copyright (c) 2018 by Fabrice Weinberg + * Copyright (c) 2019 by Helmut Tschemernjak - www.radioshuttle.de * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -31,10 +32,39 @@ #ifndef OLEDDISPLAY_h #define OLEDDISPLAY_h +#ifdef ARDUINO #include +#elif __MBED__ +#define pgm_read_byte(addr) (*(const unsigned char *)(addr)) + +#include +#define delay(x) wait_ms(x) +#define yield() void() + +/* + * This is a little Arduino String emulation to keep the OLEDDisplay + * library code in common between Arduino and mbed-os + */ +class String { +public: + String(const char *s) { _str = s; }; + int length() { return strlen(_str); }; + const char *c_str() { return _str; }; + void toCharArray(char *buf, unsigned int bufsize, unsigned int index = 0) const { + memcpy(buf, _str + index, std::min(bufsize, strlen(_str))); + }; +private: + const char *_str; +}; + +#else +#error "Unkown operating system" +#endif + #include "OLEDDisplayFonts.h" //#define DEBUG_OLEDDISPLAY(...) Serial.printf( __VA_ARGS__ ) +//#define DEBUG_OLEDDISPLAY(...) dprintf("%s", __VA_ARGS__ ) #ifndef DEBUG_OLEDDISPLAY #define DEBUG_OLEDDISPLAY(...) @@ -107,22 +137,31 @@ enum OLEDDISPLAY_TEXT_ALIGNMENT { enum OLEDDISPLAY_GEOMETRY { GEOMETRY_128_64 = 0, - GEOMETRY_128_32 = 1 + GEOMETRY_128_32, + GEOMETRY_RAWMODE, }; -typedef byte (*FontTableLookupFunction)(const byte ch); +typedef char (*FontTableLookupFunction)(const uint8_t ch); +char DefaultFontTableLookup(const uint8_t ch); -class OLEDDisplay : public Print { +#ifdef ARDUINO +class OLEDDisplay : public Print { +#elif __MBED__ +class OLEDDisplay : public Stream { +#else +#error "Unkown operating system" +#endif public: + OLEDDisplay(); virtual ~OLEDDisplay(); - const uint16_t width(void) const { return displayWidth; }; - const uint16_t height(void) const { return displayHeight; }; + uint16_t width(void) const { return displayWidth; }; + uint16_t height(void) const { return displayHeight; }; // Initialize the display - bool init(); + bool init(const uint8_t *defaultFontData = ArialMT_Plain_10); // Free the memory used by the display void end(); @@ -139,9 +178,9 @@ class OLEDDisplay : public Print { // Draw a pixel at given position void setPixel(int16_t x, int16_t y); - - // Clear a pixel at given position FIXME: INVERSE is untested with this function - void clearPixel(int16_t x, int16_t y); + + // Clear a pixel at given position FIXME: INVERSE is untested with this function + void clearPixel(int16_t x, int16_t y); // Draw a line from position 0 to position 1 void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1); @@ -260,36 +299,47 @@ class OLEDDisplay : public Print { // Implement needed function to be compatible with Print class size_t write(uint8_t c); size_t write(const char* s); + + // Implement needed function to be compatible with Stream class +#ifdef __MBED__ + int _putc(int c); + int _getc() { return -1; }; +#endif + - uint8_t *buffer = NULL; + uint8_t *buffer; #ifdef OLEDDISPLAY_DOUBLE_BUFFER - uint8_t *buffer_back = NULL; + uint8_t *buffer_back; #endif protected: - OLEDDISPLAY_GEOMETRY geometry = GEOMETRY_128_64; + OLEDDISPLAY_GEOMETRY geometry; - uint16_t displayWidth = 128; - uint16_t displayHeight = 64; - uint16_t displayBufferSize = 1024; + uint16_t displayWidth; + uint16_t displayHeight; + uint16_t displayBufferSize; // Set the correct height, width and buffer for the geometry - void setGeometry(OLEDDISPLAY_GEOMETRY g); + void setGeometry(OLEDDISPLAY_GEOMETRY g, uint16_t width = 0, uint16_t height = 0); - OLEDDISPLAY_TEXT_ALIGNMENT textAlignment = TEXT_ALIGN_LEFT; - OLEDDISPLAY_COLOR color = WHITE; + OLEDDISPLAY_TEXT_ALIGNMENT textAlignment; + OLEDDISPLAY_COLOR color; - const uint8_t *fontData = ArialMT_Plain_10; + const uint8_t *fontData; // State values for logBuffer - uint16_t logBufferSize = 0; - uint16_t logBufferFilled = 0; - uint16_t logBufferLine = 0; - uint16_t logBufferMaxLines = 0; - char *logBuffer = NULL; + uint16_t logBufferSize; + uint16_t logBufferFilled; + uint16_t logBufferLine; + uint16_t logBufferMaxLines; + char *logBuffer; + + // the header size of the buffer used, e.g. for the SPI command header + virtual int getBufferOffset(void) = 0; + // Send a command to the display (low level function) virtual void sendCommand(uint8_t com) {(void)com;}; @@ -305,28 +355,8 @@ class OLEDDisplay : public Print { void inline drawInternal(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const uint8_t *data, uint16_t offset, uint16_t bytesInData) __attribute__((always_inline)); void drawStringInternal(int16_t xMove, int16_t yMove, char* text, uint16_t textLength, uint16_t textWidth); - - // UTF-8 to font table index converter - // Code form http://playground.arduino.cc/Main/Utf8ascii - FontTableLookupFunction fontTableLookupFunction = [](const byte ch) { - static uint8_t LASTCHAR; - - if (ch < 128) { // Standard ASCII-set 0..0x7F handling - LASTCHAR = 0; - return ch; - } - - uint8_t last = LASTCHAR; // get last char - LASTCHAR = ch; - - switch (last) { // conversion depnding on first UTF8-character - case 0xC2: return (uint8_t) ch; - case 0xC3: return (uint8_t) (ch | 0xC0); - case 0x82: if (ch == 0xAC) return (uint8_t) 0x80; // special case Euro-symbol - } - - return (uint8_t) 0; // otherwise: return zero, if character has to be ignored - }; + + FontTableLookupFunction fontTableLookupFunction; }; #endif diff --git a/src/OLEDDisplayFonts.h b/src/OLEDDisplayFonts.h index 3544edb..abc61ba 100644 --- a/src/OLEDDisplayFonts.h +++ b/src/OLEDDisplayFonts.h @@ -1,6 +1,10 @@ #ifndef OLEDDISPLAYFONTS_h #define OLEDDISPLAYFONTS_h +#ifdef __MBED__ +#define PROGMEM +#endif + const uint8_t ArialMT_Plain_10[] PROGMEM = { 0x0A, // Width: 10 0x0D, // Height: 13 diff --git a/src/OLEDDisplayUi.cpp b/src/OLEDDisplayUi.cpp index cd371a0..e1da0fd 100644 --- a/src/OLEDDisplayUi.cpp +++ b/src/OLEDDisplayUi.cpp @@ -3,6 +3,7 @@ * * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn * Copyright (c) 2018 by Fabrice Weinberg + * Copyright (c) 2019 by Helmut Tschemernjak - www.radioshuttle.de * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,8 +31,41 @@ #include "OLEDDisplayUi.h" +void LoadingDrawDefault(OLEDDisplay *display, LoadingStage* stage, uint8_t progress) { + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->setFont(ArialMT_Plain_10); + display->drawString(64, 18, stage->process); + display->drawProgressBar(4, 32, 120, 8, progress); +}; + + OLEDDisplayUi::OLEDDisplayUi(OLEDDisplay *display) { this->display = display; + + indicatorPosition = BOTTOM; + indicatorDirection = LEFT_RIGHT; + activeSymbol = ANIMATION_activeSymbol; + inactiveSymbol = ANIMATION_inactiveSymbol; + frameAnimationDirection = SLIDE_RIGHT; + lastTransitionDirection = 1; + ticksPerFrame = 151; // ~ 5000ms at 30 FPS + ticksPerTransition = 15; // ~ 500ms at 30 FPS + frameCount = 0; + nextFrameNumber = -1; + overlayCount = 0; + indicatorDrawState = 1; + loadingDrawFunction = LoadingDrawDefault; + updateInterval = 33; + state.lastUpdate = 0; + state.ticksSinceLastStateSwitch = 0; + state.frameState = FIXED; + state.currentFrame = 0; + state.frameTransitionDirection = 1; + state.isIndicatorDrawen = true; + state.manuelControll = false; + state.userData = NULL; + shouldDrawIndicators = true; + autoTransition = true; } void OLEDDisplayUi::init() { @@ -194,16 +228,30 @@ OLEDDisplayUiState* OLEDDisplayUi::getUiState(){ int8_t OLEDDisplayUi::update(){ +#ifdef ARDUINO unsigned long frameStart = millis(); +#elif __MBED__ + Timer t; + t.start(); + unsigned long frameStart = t.read_ms(); +#else +#error "Unkown operating system" +#endif int8_t timeBudget = this->updateInterval - (frameStart - this->state.lastUpdate); if ( timeBudget <= 0) { // Implement frame skipping to ensure time budget is keept - if (this->autoTransition && this->state.lastUpdate != 0) this->state.ticksSinceLastStateSwitch += ceil(-timeBudget / this->updateInterval); + if (this->autoTransition && this->state.lastUpdate != 0) this->state.ticksSinceLastStateSwitch += ceil((double)-timeBudget / (double)this->updateInterval); this->state.lastUpdate = frameStart; this->tick(); } +#ifdef ARDUINO return this->updateInterval - (millis() - frameStart); +#elif __MBED__ + return this->updateInterval - (t.read_ms() - frameStart); +#else +#error "Unkown operating system" +#endif } @@ -378,7 +426,7 @@ void OLEDDisplayUi::drawIndicator() { uint16_t x = 0,y = 0; - for (byte i = 0; i < this->frameCount; i++) { + for (uint8_t i = 0; i < this->frameCount; i++) { switch (this->indicatorPosition){ case TOP: diff --git a/src/OLEDDisplayUi.h b/src/OLEDDisplayUi.h index 2cabf30..0769599 100644 --- a/src/OLEDDisplayUi.h +++ b/src/OLEDDisplayUi.h @@ -3,6 +3,7 @@ * * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn * Copyright (c) 2018 by Fabrice Weinberg + * Copyright (c) 2019 by Helmut Tschemernjak - www.radioshuttle.de * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -31,7 +32,14 @@ #ifndef OLEDDISPLAYUI_h #define OLEDDISPLAYUI_h +#ifdef ARDUINO #include +#elif __MBED__ +#include +#else +#error "Unkown operating system" +#endif + #include "OLEDDisplay.h" //#define DEBUG_OLEDDISPLAYUI(...) Serial.printf( __VA_ARGS__ ) @@ -76,21 +84,21 @@ const uint8_t ANIMATION_inactiveSymbol[] PROGMEM = { // Structure of the UiState struct OLEDDisplayUiState { - uint64_t lastUpdate = 0; - uint16_t ticksSinceLastStateSwitch = 0; + uint64_t lastUpdate; + uint16_t ticksSinceLastStateSwitch; - FrameState frameState = FIXED; - uint8_t currentFrame = 0; + FrameState frameState; + uint8_t currentFrame; - bool isIndicatorDrawen = true; + bool isIndicatorDrawen; // Normal = 1, Inverse = -1; - int8_t frameTransitionDirection = 1; + int8_t frameTransitionDirection; - bool manuelControll = false; + bool manuelControll; // Custom data that can be used by the user - void* userData = NULL; + void* userData; }; struct LoadingStage { @@ -107,54 +115,49 @@ class OLEDDisplayUi { OLEDDisplay *display; // Symbols for the Indicator - IndicatorPosition indicatorPosition = BOTTOM; - IndicatorDirection indicatorDirection = LEFT_RIGHT; + IndicatorPosition indicatorPosition; + IndicatorDirection indicatorDirection; - const uint8_t* activeSymbol = ANIMATION_activeSymbol; - const uint8_t* inactiveSymbol = ANIMATION_inactiveSymbol; + const uint8_t* activeSymbol; + const uint8_t* inactiveSymbol; - bool shouldDrawIndicators = true; + bool shouldDrawIndicators; // Values for the Frames - AnimationDirection frameAnimationDirection = SLIDE_RIGHT; + AnimationDirection frameAnimationDirection; - int8_t lastTransitionDirection = 1; + int8_t lastTransitionDirection; - uint16_t ticksPerFrame = 151; // ~ 5000ms at 30 FPS - uint16_t ticksPerTransition = 15; // ~ 500ms at 30 FPS + uint16_t ticksPerFrame; // ~ 5000ms at 30 FPS + uint16_t ticksPerTransition; // ~ 500ms at 30 FPS - bool autoTransition = true; + bool autoTransition; FrameCallback* frameFunctions; - uint8_t frameCount = 0; + uint8_t frameCount; // Internally used to transition to a specific frame - int8_t nextFrameNumber = -1; + int8_t nextFrameNumber; // Values for Overlays OverlayCallback* overlayFunctions; - uint8_t overlayCount = 0; + uint8_t overlayCount; // Will the Indicator be drawen // 3 Not drawn in both frames // 2 Drawn this frame but not next // 1 Not drown this frame but next // 0 Not known yet - uint8_t indicatorDrawState = 1; + uint8_t indicatorDrawState; // Loading screen - LoadingDrawFunction loadingDrawFunction = [](OLEDDisplay *display, LoadingStage* stage, uint8_t progress) { - display->setTextAlignment(TEXT_ALIGN_CENTER); - display->setFont(ArialMT_Plain_10); - display->drawString(64, 18, stage->process); - display->drawProgressBar(4, 32, 120, 8, progress); - }; - + LoadingDrawFunction loadingDrawFunction; + // UI State OLEDDisplayUiState state; // Bookeeping for update - uint8_t updateInterval = 33; + uint8_t updateInterval; uint8_t getNextFrameNumber(); void drawIndicator(); diff --git a/src/SH1106Brzo.h b/src/SH1106Brzo.h index 8ef3d5b..be9c0c7 100644 --- a/src/SH1106Brzo.h +++ b/src/SH1106Brzo.h @@ -127,6 +127,9 @@ class SH1106Brzo : public OLEDDisplay { } private: + int getBufferOffset(void) { + return 0; + } inline void sendCommand(uint8_t com) __attribute__((always_inline)){ uint8_t command[2] = {0x80 /* command mode */, com}; brzo_i2c_start_transaction(_address, BRZO_I2C_SPEED); diff --git a/src/SH1106Spi.h b/src/SH1106Spi.h index cf8f088..23693bc 100644 --- a/src/SH1106Spi.h +++ b/src/SH1106Spi.h @@ -123,6 +123,9 @@ class SH1106Spi : public OLEDDisplay { } private: + int getBufferOffset(void) { + return 0; + } inline void sendCommand(uint8_t com) __attribute__((always_inline)){ digitalWrite(_dc, LOW); SPI.transfer(com); diff --git a/src/SH1106Wire.h b/src/SH1106Wire.h index 486ba1a..b088807 100644 --- a/src/SH1106Wire.h +++ b/src/SH1106Wire.h @@ -144,6 +144,9 @@ class SH1106Wire : public OLEDDisplay { } private: + int getBufferOffset(void) { + return 0; + } inline void sendCommand(uint8_t command) __attribute__((always_inline)){ Wire.beginTransmission(_address); Wire.write(0x80); diff --git a/src/SSD1306Brzo.h b/src/SSD1306Brzo.h index e90b7b3..987dd5b 100644 --- a/src/SSD1306Brzo.h +++ b/src/SSD1306Brzo.h @@ -148,6 +148,9 @@ class SSD1306Brzo : public OLEDDisplay { } private: + int getBufferOffset(void) { + return 0; + } inline void sendCommand(uint8_t com) __attribute__((always_inline)){ uint8_t command[2] = {0x80 /* command mode */, com}; brzo_i2c_start_transaction(_address, BRZO_I2C_SPEED); diff --git a/src/SSD1306I2C.h b/src/SSD1306I2C.h new file mode 100644 index 0000000..ab94840 --- /dev/null +++ b/src/SSD1306I2C.h @@ -0,0 +1,152 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2019 by Helmut Tschemernjak - www.radioshuttle.de + * + * 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. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef SSD1306I2C_h +#define SSD1306I2C_h + + +#ifdef __MBED__ + +#include "OLEDDisplay.h" +#include + +#ifndef UINT8_MAX + #define UINT8_MAX 0xff +#endif + +class SSD1306I2C : public OLEDDisplay { +public: + SSD1306I2C(uint8_t _address, PinName _sda, PinName _scl, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64) { + setGeometry(g); + + this->_address = _address << 1; // convert from 7 to 8 bit for mbed. + this->_sda = _sda; + this->_scl = _scl; + _i2c = new I2C(_sda, _scl); + } + + bool connect() { + // mbed supports 100k and 400k some device maybe 1000k +#ifdef TARGET_STM32L4 + _i2c->frequency(1000000); +#else + _i2c->frequency(400000); +#endif + return true; + } + + void display(void) { + const int x_offset = (128 - this->width()) / 2; +#ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = UINT8_MAX; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = UINT8_MAX; + uint8_t maxBoundX = 0; + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (this->height() / 8); y++) { + for (x = 0; x < this->width(); x++) { + uint16_t pos = x + y * this->width(); + if (buffer[pos] != buffer_back[pos]) { + minBoundY = std::min(minBoundY, y); + maxBoundY = std::max(maxBoundY, y); + minBoundX = std::min(minBoundX, x); + maxBoundX = std::max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + yield(); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + + if (minBoundY == UINT8_MAX) return; + + sendCommand(COLUMNADDR); + sendCommand(x_offset + minBoundX); // column start address (0 = reset) + sendCommand(x_offset + maxBoundX); // column end address (127 = reset) + + sendCommand(PAGEADDR); + sendCommand(minBoundY); // page start address + sendCommand(maxBoundY); // page end address + + for (y = minBoundY; y <= maxBoundY; y++) { + uint8_t *start = &buffer[(minBoundX + y * this->width())-1]; + uint8_t save = *start; + + *start = 0x40; // control + _i2c->write(_address, (char *)start, (maxBoundX-minBoundX) + 1 + 1); + *start = save; + } +#else + + sendCommand(COLUMNADDR); + sendCommand(x_offset); // column start address (0 = reset) + sendCommand(x_offset + (this->width() - 1));// column end address (127 = reset) + + sendCommand(PAGEADDR); + sendCommand(0x0); // page start address (0 = reset) + + if (geometry == GEOMETRY_128_64) { + sendCommand(0x7); + } else if (geometry == GEOMETRY_128_32) { + sendCommand(0x3); + } + + buffer[-1] = 0x40; // control + _i2c->write(_address, (char *)&buffer[-1], displayBufferSize + 1); +#endif + } + +private: + int getBufferOffset(void) { + return 0; + } + + inline void sendCommand(uint8_t command) __attribute__((always_inline)) { + char _data[2]; + _data[0] = 0x80; // control + _data[1] = command; + _i2c->write(_address, _data, sizeof(_data)); + } + + uint8_t _address; + PinName _sda; + PinName _scl; + I2C *_i2c; +}; + +#endif + +#endif diff --git a/src/SSD1306Spi.h b/src/SSD1306Spi.h index 988dc2c..e1b6458 100644 --- a/src/SSD1306Spi.h +++ b/src/SSD1306Spi.h @@ -148,6 +148,9 @@ class SSD1306Spi : public OLEDDisplay { } private: + int getBufferOffset(void) { + return 0; + } inline void sendCommand(uint8_t com) __attribute__((always_inline)){ digitalWrite(_cs, HIGH); digitalWrite(_dc, LOW); diff --git a/src/SSD1306Wire.h b/src/SSD1306Wire.h index a3d0a05..3a0ab10 100644 --- a/src/SSD1306Wire.h +++ b/src/SSD1306Wire.h @@ -34,6 +34,11 @@ #include "OLEDDisplay.h" #include +#ifdef ARDUINO_ARCH_AVR +#define _min min +#define _max max +#endif + class SSD1306Wire : public OLEDDisplay { private: uint8_t _address; @@ -51,7 +56,11 @@ class SSD1306Wire : public OLEDDisplay { } bool connect() { +#ifdef ARDUINO_ARCH_AVR + Wire.begin(); +#else Wire.begin(this->_sda, this->_scl); +#endif // Let's use ~700khz if ESP8266 is in 160Mhz mode // this will be limited to ~400khz if the ESP8266 in 80Mhz mode. Wire.setClock(700000); @@ -128,7 +137,6 @@ class SSD1306Wire : public OLEDDisplay { sendCommand(PAGEADDR); sendCommand(0x0); - sendCommand((this->height() / 8) - 1); if (geometry == GEOMETRY_128_64) { sendCommand(0x7); @@ -154,6 +162,9 @@ class SSD1306Wire : public OLEDDisplay { } private: + int getBufferOffset(void) { + return 0; + } inline void sendCommand(uint8_t command) __attribute__((always_inline)){ initI2cIfNeccesary(); Wire.beginTransmission(_address); @@ -164,7 +175,11 @@ class SSD1306Wire : public OLEDDisplay { void initI2cIfNeccesary() { if (_doI2cAutoInit) { - Wire.begin(this->_sda, this->_scl); +#ifdef ARDUINO_ARCH_AVR + Wire.begin(); +#else + Wire.begin(this->_sda, this->_scl); +#endif } }