diff --git a/SSD1306Ui.cpp b/SSD1306Ui.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bcdb751355cef4eb88932f8f8538bb0695ae5b0f --- /dev/null +++ b/SSD1306Ui.cpp @@ -0,0 +1,249 @@ +#include "SSD1306Ui.h" + + +SSD1306Ui::SSD1306Ui(SSD1306 *display) { + this->display = display; +} + +void SSD1306Ui::init() { + this->display->init(); +} + +void SSD1306Ui::setTargetFPS(byte fps){ + this->updateInterval = ((float) 1.0 / (float) fps) * 1000; + Serial.println(this->updateInterval); +} + +// -/------ Automatic controll ------\- + +void SSD1306Ui::enableAutoTransition(){ + autoTransition = true; +} +void SSD1306Ui::disableAutoTransition(){ + autoTransition = false; +} +void SSD1306Ui::setAutoTransitionForwards(){ + this->frameTransitionDirection = 1; +} +void SSD1306Ui::setAutoTransitionBackwards(){ + this->frameTransitionDirection = 1; +} +void SSD1306Ui::setTimePerFrame(int time){ + this->ticksPerFrame = (int) ( (float) time / (float) updateInterval); +} +void SSD1306Ui::setTimePerTransition(int time){ + this->ticksPerTransition = (int) ( (float) time / (float) updateInterval); +} + + +// -/------ Customize indicator position and style -------\- +void SSD1306Ui::setIndicatorPosition(IndicatorPosition pos) { + this->indicatorPosition = pos; + this->dirty = true; +} +void SSD1306Ui::setIndicatorDirection(IndicatorDirection dir) { + this->indicatorDirection = dir; +} +void SSD1306Ui::setActiveSymbole(const char* symbole) { + this->activeSymbole = symbole; + this->dirty = true; +} +void SSD1306Ui::setInactiveSymbole(const char* symbole) { + this->inactiveSymbole = symbole; + this->dirty = true; +} + + +// -/----- Frame settings -----\- +void SSD1306Ui::setFrameAnimation(AnimationDirection dir) { + this->frameAnimationDirection = dir; +} +void SSD1306Ui::setFrames(bool (*frameFunctions[])(SSD1306 *display, int x, int y), int frameCount) { + this->frameCount = frameCount; + this->frameFunctions = frameFunctions; +} + +// -/----- Overlays ------\- +void SSD1306Ui::setOverlays(bool (*overlayFunctions[])(SSD1306 *display), int overlayCount){ + this->overlayCount = overlayCount; + this->overlayFunctions = overlayFunctions; +} + + +// -/----- Manuel control -----\- +void SSD1306Ui::nextFrame() { + this->frameState = IN_TRANSITION; + this->ticksSinceLastStateSwitch = 0; + this->frameTransitionDirection = 1; +} +void SSD1306Ui::previousFrame() { + this->frameState = IN_TRANSITION; + this->ticksSinceLastStateSwitch = 0; + this->frameTransitionDirection = -1; +} + + +// -/----- State information -----\- +FrameState SSD1306Ui::getFrameState(){ + return this->frameState; +} +int SSD1306Ui::getCurrentFrame(){ + return this->currentFrame; +} + + +int SSD1306Ui::update(){ + int timeBudget = this->updateInterval - (millis() - this->lastUpdate); + if ( timeBudget <= 0) { + + // Implement frame skipping to ensure time budget is keept + if (this->autoTransition && this->lastUpdate != 0) this->ticksSinceLastStateSwitch += abs(timeBudget) / this->updateInterval; + + this->lastUpdate = millis(); + this->tick(); + } + return timeBudget; +} + + +void SSD1306Ui::tick() { + this->ticksSinceLastStateSwitch++; + + switch (this->frameState) { + case IN_TRANSITION: + this->dirty = true; + if (this->ticksSinceLastStateSwitch >= this->ticksPerTransition){ + this->frameState = FIXED; + this->currentFrame = getNextFrameNumber(); + this->ticksSinceLastStateSwitch = 0; + } + break; + case FIXED: + if (this->ticksSinceLastStateSwitch >= this->ticksPerFrame){ + if (this->autoTransition){ + this->frameState = IN_TRANSITION; + this->dirty = true; + } + this->ticksSinceLastStateSwitch = 0; + } + break; + } + + if (this->dirty) { + this->dirty = false; + this->display->clear(); + this->drawIndicator(); + this->drawFrame(); + this->drawOverlays(); + this->display->display(); + } +} + +void SSD1306Ui::drawFrame(){ + switch (this->frameState){ + case IN_TRANSITION: { + float progress = (float) this->ticksSinceLastStateSwitch / (float) this->ticksPerTransition; + int x, y, x1, y1; + switch(this->frameAnimationDirection){ + case SLIDE_LEFT: + x = -128 * progress; + y = 0; + x1 = x + 128; + y1 = 0; + break; + case SLIDE_RIGHT: + x = 128 * progress; + y = 0; + x1 = x - 128; + y1 = 0; + break; + case SLIDE_UP: + x = 0; + y = -64 * progress; + x1 = 0; + y1 = y + 64; + break; + case SLIDE_DOWN: + x = 0; + y = 64 * progress; + x1 = 0; + y1 = y - 64; + break; + } + + // Invert animation if direction is reversed. + int dir = frameTransitionDirection >= 0 ? 1 : -1; + x *= dir; y *= dir; x1 *= dir; y1 *= dir; + + this->dirty |= (*this->frameFunctions[this->currentFrame])(this->display, x, y); + this->dirty |= (*this->frameFunctions[this->getNextFrameNumber()])(this->display, x1, y1); + break; + } + case FIXED: + this->dirty |= (*this->frameFunctions[this->currentFrame])(this->display, 0, 0); + break; + } +} + +void SSD1306Ui::drawIndicator() { + byte posOfCurrentFrame; + + switch (this->indicatorDirection){ + case LEFT_RIGHT: + posOfCurrentFrame = this->currentFrame; + break; + case RIGHT_LEFT: + posOfCurrentFrame = (this->frameCount - 1) - this->currentFrame; + break; + } + + for (byte i = 0; i < this->frameCount; i++) { + + const char *xbm; + + if (posOfCurrentFrame == i) { + xbm = this->activeSymbole; + } else { + xbm = this->inactiveSymbole; + } + + int x,y; + switch (this->indicatorPosition){ + case TOP: + y = 0; + x = 64 - (12 * frameCount / 2) + 12 * i; + break; + case BOTTOM: + y = 56; + x = 64 - (12 * frameCount / 2) + 12 * i; + break; + case RIGHT: + x = 120; + y = 32 - (12 * frameCount / 2) + 12 * i; + break; + case LEFT: + x = 0; + y = 32 - (12 * frameCount / 2) + 12 * i; + break; + } + + this->display->drawXbm(x, y, 8, 8, xbm); + } +} + +void SSD1306Ui::drawOverlays() { + for (int i=0;i<this->overlayCount;i++){ + this->dirty |= (*this->overlayFunctions[i])(this->display); + } +} + +int SSD1306Ui::getNextFrameNumber(){ + int nextFrame = (this->currentFrame + this->frameTransitionDirection) % this->frameCount; + if (nextFrame < 0){ + nextFrame = this->frameCount + nextFrame; + } + return nextFrame; +} + + + diff --git a/SSD1306Ui.h b/SSD1306Ui.h new file mode 100644 index 0000000000000000000000000000000000000000..d5d7257002dc9363d4fecf9a4f7c522936ef93f4 --- /dev/null +++ b/SSD1306Ui.h @@ -0,0 +1,202 @@ +/**The MIT License (MIT) + +Copyright (c) 2015 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 Daniel Eichhorn +*/ + +#pragma once + +#include <Arduino.h> +#include "ssd1306_i2c.h" + +enum AnimationDirection { + SLIDE_UP, + SLIDE_DOWN, + SLIDE_LEFT, + SLIDE_RIGHT +}; + +enum IndicatorPosition { + TOP, + RIGHT, + BOTTOM, + LEFT +}; + +enum IndicatorDirection { + LEFT_RIGHT, + RIGHT_LEFT +}; + +enum FrameState { + IN_TRANSITION, + FIXED +}; + +const char ANIMATION_activeSymbole[] PROGMEM = { + 0x00, 0x18, 0x3c, 0x7e, 0x7e, 0x3c, 0x18, 0x00 +}; + +const char ANIMATION_inactiveSymbole[] PROGMEM = { + 0x00, 0x0, 0x0, 0x18, 0x18, 0x0, 0x0, 0x00 +}; + +class SSD1306Ui { + private: + SSD1306 *display; + + // Global dirty flag to indicate that the display needs to be redraw. + bool dirty = true; + + // Symboles for the Indicator + IndicatorPosition indicatorPosition = BOTTOM; + IndicatorDirection indicatorDirection = LEFT_RIGHT; + + const char* activeSymbole = ANIMATION_activeSymbole; + const char* inactiveSymbole = ANIMATION_inactiveSymbole; + + // Values for the Frames + AnimationDirection frameAnimationDirection = SLIDE_RIGHT; + FrameState frameState = FIXED; + + int frameTransitionDirection = 1; + + int ticksPerFrame = 313; // ~ 5000ms at 60 FPS + int ticksPerTransition = 32; // ~ 500ms at 60 FPS + int ticksSinceLastStateSwitch = 0; + int currentFrame = 0; + + bool autoTransition = true; + + bool (**frameFunctions)(SSD1306 *display, int x, int y); + int frameCount = 0; + + // Values for Overlays + bool (**overlayFunctions)(SSD1306 *display); + int overlayCount = 0; + + + // Bookeeping for update + int updateInterval = 16; + unsigned long lastUpdate = 0; + + + int getNextFrameNumber(); + void drawIndicator(); + void drawFrame(); + void drawOverlays(); + void tick(); + + public: + + SSD1306Ui(SSD1306 *display); + + /** + * Initialise the display + */ + void init(); + + /** + * Configure the internal used target FPS + */ + void setTargetFPS(byte fps); + + // Automatic Controll + /** + * Enable automatic transition to next frame after the some time can be configured with `setTimePerFrame` and `setTimePerTransition`. + */ + void enableAutoTransition(); + + /** + * Disable automatic transition to next frame. + */ + void disableAutoTransition(); + + /** + * Set the direction if the automatic transitioning + */ + void setAutoTransitionForwards(); + void setAutoTransitionBackwards(); + + /** + * Set the approx. time a frame is displayed + */ + void setTimePerFrame(int time); + + /** + * Set the approx. time a transition will take + */ + void setTimePerTransition(int time); + + // Customize Indicator Position and style + /** + * Set the position of the indicator bar. + */ + void setIndicatorPosition(IndicatorPosition pos); + + /** + * Set the direction of the indicator bar. Defining the order of frames ASCENDING / DESCENDING + */ + void setIndicatorDirection(IndicatorDirection dir); + + /** + * Set the symbole to indicate an active frame in the indicator bar. + */ + void setActiveSymbole(const char* symbole); + + /** + * Set the symbole to indicate an inactive frame in the indicator bar. + */ + void setInactiveSymbole(const char* symbole); + + // Frame settings + + /** + * Configure what animation is used to transition from one frame to another + */ + void setFrameAnimation(AnimationDirection dir); + + /** + * Add frame drawing functions + */ + void setFrames(bool (*frameFunctions[])(SSD1306 *display, int x, int y), int frameCount); + + // Overlay + + /** + * Add overlays drawing functions that are draw independent of the Frames + */ + void setOverlays(bool (*overlayFunctions[])(SSD1306 *display), int overlayCount); + + // Manuell Controll + void nextFrame(); + void previousFrame(); + + // State Info + FrameState getFrameState(); + int getCurrentFrame(); + + + int update(); +}; + +