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(); +}; + + diff --git a/examples/SSD1306Demo/SSD1306Demo.ino b/examples/SSD1306Demo/SSD1306Demo.ino index df7eb61a125bd839a3360d3b39031c0452943c09..721e02a96a72ea3a1bb2541cbfc2d67cfd95a916 100644 --- a/examples/SSD1306Demo/SSD1306Demo.ino +++ b/examples/SSD1306Demo/SSD1306Demo.ino @@ -1,5 +1,3 @@ -#include <ArduinoJson.h> - /**The MIT License (MIT) Copyright (c) 2015 by Daniel Eichhorn @@ -26,6 +24,7 @@ See more at http://blog.squix.ch */ #include <Wire.h> #include "ssd1306_i2c.h" +#include "SSD1306Ui.h" #include "images.h" // if you are using a ESP8266 module with NodeMCU @@ -48,105 +47,125 @@ See more at http://blog.squix.ch // Initialize the oled display for address 0x3c // sda-pin=14 and sdc-pin=12 -SSD1306 display(0x3c, NODEMCU_D6, NODEMCU_D5); +SSD1306 display(0x3c, NODEMCU_D6, NODEMCU_D5); +SSD1306Ui ui ( &display ); // this array keeps function pointers to all frames // frames are the single views that slide from right to left -void (*frameCallbacks[])(int x, int y) = {drawFrame1, drawFrame2, drawFrame3, drawFrame4}; +bool (*frames[])(SSD1306 *display, int x, int y) = { drawFrame1, drawFrame2, drawFrame3, drawFrame4 }; // how many frames are there? int frameCount = 4; +bool (*overlays[])(SSD1306 *display) = { msOverlay }; +int overlaysCount = 1; + void setup() { Serial.begin(115200); Serial.println(); Serial.println(); - - // initialize dispaly - display.init(); + + + ui.setTargetFPS(30); + + ui.setActiveSymbole(activeSymbole); + ui.setInactiveSymbole(inactiveSymbole); + + // You can change this to + // TOP, LEFT, BOTTOM, RIGHT + ui.setIndicatorPosition(BOTTOM); + + // Defines where the first frame is located in the bar. + ui.setIndicatorDirection(LEFT_RIGHT); + + // You can change the transition that is used + // SLIDE_LEFT, SLIDE_RIGHT, SLIDE_TOP, SLIDE_DOWN + ui.setFrameAnimation(SLIDE_LEFT); + + // Add frames + ui.setFrames(frames, frameCount); + + // Add overlays + ui.setOverlays(overlays, overlaysCount); + + // Inital UI takes care of initalising the display too. + ui.init(); + display.flipScreenVertically(); - // set the drawing functions - display.setFrameCallbacks(frameCount, frameCallbacks); - // how many ticks does a slide of a frame take? - display.setFrameTransitionTicks(10); - // defines how many ticks the driver waits between frame transitions - display.setFrameWaitTicks(150); - - display.clear(); - display.display(); - + } void loop() { - if (display.getFrameState() == display.FRAME_STATE_FIX) { - // do something which consumes a lot of time in a moment - // when there is no transition between frames going on. - // This will keep transitions smooth; - } - - // clear the frame - display.clear(); - - // Tell the driver to render the next frame. - // This enables the frame mode including the transition - // and the drawing of the frame indicators - display.nextFrameTick(); + int remainingTimeBudget = ui.update(); - // Even in frame mode you can draw static elements. - // But they won't be transitioned - display.setFont(ArialMT_Plain_10); - display.setTextAlignment(TEXT_ALIGN_LEFT); - display.drawString(0, 54, "20:54"); + if (remainingTimeBudget > 0) { + // You can do some work here + // Don't do stuff if you are below your + // time budget. + delay(remainingTimeBudget); + } +} - // copy the buffer to the display - display.display(); +bool msOverlay(SSD1306 *display) { + display->setTextAlignment(TEXT_ALIGN_RIGHT); + display->setFont(ArialMT_Plain_10); + display->drawString(128, 0, String(millis())); + return true; } -void drawFrame1(int x, int y) { +bool drawFrame1(SSD1306 *display, int x, int y) { // draw an xbm image. // Please note that everything that should be transitioned // needs to be drawn relative to x and y - display.drawXbm(x + 34, y + 14, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_bits); - } - void drawFrame2(int x, int y) { + // if this frame need to be refreshed at the targetFPS you need to + // return true + display->drawXbm(x + 34, y + 14, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_bits); + return false; +} + +bool drawFrame2(SSD1306 *display, int x, int y) { // Demonstrates the 3 included default sizes. The fonts come from SSD1306Fonts.h file // Besides the default fonts there will be a program to convert TrueType fonts into this format - display.setTextAlignment(TEXT_ALIGN_LEFT); - display.setFont(ArialMT_Plain_10); - display.drawString(0 + x, 0 + y, "Arial 10"); + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(ArialMT_Plain_10); + display->drawString(0 + x, 10 + y, "Arial 10"); + + display->setFont(ArialMT_Plain_16); + display->drawString(0 + x, 20 + y, "Arial 16"); - display.setFont(ArialMT_Plain_16); - display.drawString(0 + x, 10 + y, "Arial 16"); + display->setFont(ArialMT_Plain_24); + display->drawString(0 + x, 34 + y, "Arial 24"); - display.setFont(ArialMT_Plain_24); - display.drawString(0 + x, 24 + y, "Arial 24"); + return false; } -void drawFrame3(int x, int y) { +bool drawFrame3(SSD1306 *display, int x, int y) { // Text alignment demo - display.setFont(ArialMT_Plain_10); + display->setFont(ArialMT_Plain_10); // The coordinates define the left starting point of the text - display.setTextAlignment(TEXT_ALIGN_LEFT); - display.drawString(0 + x, 0 + y, "Left aligned (0,0)"); + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->drawString(0 + x, 11 + y, "Left aligned (0,10)"); // The coordinates define the center of the text - display.setTextAlignment(TEXT_ALIGN_CENTER); - display.drawString(64 + x, 20, "Center aligned (64,20)"); + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->drawString(64 + x, 22, "Center aligned (64,22)"); // The coordinates define the right end of the text - display.setTextAlignment(TEXT_ALIGN_RIGHT); - display.drawString(128 + x, 40, "Right aligned (128,40)"); + display->setTextAlignment(TEXT_ALIGN_RIGHT); + display->drawString(128 + x, 33, "Right aligned (128,33)"); + return false; } -void drawFrame4(int x, int y) { - // Demo for drawStringMaxWidth: +bool drawFrame4(SSD1306 *display, int x, int y) { + // Demo for drawStringMaxWidth: // with the third parameter you can define the width after which words will be wrapped. // Currently only spaces and "-" are allowed for wrapping - display.setTextAlignment(TEXT_ALIGN_LEFT); - display.setFont(ArialMT_Plain_10); - display.drawStringMaxWidth(0 + x, 0 + y, 128, "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore."); + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(ArialMT_Plain_10); + display->drawStringMaxWidth(0 + x, 10 + y, 128, "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore."); + return false; } diff --git a/examples/SSD1306Demo/images.h b/examples/SSD1306Demo/images.h index 8f5de149d1733553de8cbd7d3dbef167b9551aec..c280b555ce6010e5879f757fb4abff7bd090f466 100644 --- a/examples/SSD1306Demo/images.h +++ b/examples/SSD1306Demo/images.h @@ -1,28 +1,50 @@ #define WiFi_Logo_width 60 #define WiFi_Logo_height 36 const char WiFi_Logo_bits[] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, - 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, - 0xFF, 0x03, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, - 0x00, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 0x83, 0x01, 0x80, 0xFF, 0xFF, 0xFF, - 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, - 0xC0, 0xFF, 0xFF, 0x7C, 0x00, 0x60, 0x0C, 0x00, 0xC0, 0x31, 0x46, 0x7C, - 0xFC, 0x77, 0x08, 0x00, 0xE0, 0x23, 0xC6, 0x3C, 0xFC, 0x67, 0x18, 0x00, - 0xE0, 0x23, 0xE4, 0x3F, 0x1C, 0x00, 0x18, 0x00, 0xE0, 0x23, 0x60, 0x3C, - 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x03, 0x60, 0x3C, 0x1C, 0x70, 0x18, 0x00, - 0xE0, 0x07, 0x60, 0x3C, 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, - 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, - 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x8F, 0x71, 0x3C, - 0x1C, 0x70, 0x18, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x08, 0x00, - 0xC0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x1F, - 0x00, 0x00, 0x06, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x07, 0x00, - 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xF8, 0xFF, 0xFF, - 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, - 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, - 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - }; + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0x03, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 0x83, 0x01, 0x80, 0xFF, 0xFF, 0xFF, + 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, + 0xC0, 0xFF, 0xFF, 0x7C, 0x00, 0x60, 0x0C, 0x00, 0xC0, 0x31, 0x46, 0x7C, + 0xFC, 0x77, 0x08, 0x00, 0xE0, 0x23, 0xC6, 0x3C, 0xFC, 0x67, 0x18, 0x00, + 0xE0, 0x23, 0xE4, 0x3F, 0x1C, 0x00, 0x18, 0x00, 0xE0, 0x23, 0x60, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x03, 0x60, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x07, 0x60, 0x3C, 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, + 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x8F, 0x71, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x08, 0x00, + 0xC0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x1F, + 0x00, 0x00, 0x06, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x07, 0x00, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xF8, 0xFF, 0xFF, + 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + +const char activeSymbole[] PROGMEM = { + B00000000, + B00000000, + B00011000, + B00100100, + B01000010, + B01000010, + B00100100, + B00011000 +}; + +const char inactiveSymbole[] PROGMEM = { + B00000000, + B00000000, + B00000000, + B00000000, + B00011000, + B00011000, + B00000000, + B00000000 +}; \ No newline at end of file diff --git a/ssd1306_i2c.cpp b/ssd1306_i2c.cpp index 37c628cb33d9d85f2107c42b1e0d31225f48227f..2bd40d445c661bff2ca0685d2956c84983449272 100644 --- a/ssd1306_i2c.cpp +++ b/ssd1306_i2c.cpp @@ -29,23 +29,20 @@ Credits for parts of this code go to Mike Rankin. Thank you so much for sharing! #include <Wire.h> -SSD1306::SSD1306(int i2cAddress, int sda, int sdc) -{ +SSD1306::SSD1306(int i2cAddress, int sda, int sdc) { myI2cAddress = i2cAddress; mySda = sda; mySdc = sdc; - } void SSD1306::init() { Wire.begin(mySda, mySdc); - Wire.setClock(400000); + Wire.setClock(400000); sendInitCommands(); resetDisplay(); } -void SSD1306::resetDisplay(void) -{ +void SSD1306::resetDisplay(void) { displayOff(); clear(); display(); @@ -53,30 +50,29 @@ void SSD1306::resetDisplay(void) } void SSD1306::reconnect() { - Wire.begin(mySda, mySdc); + Wire.begin(mySda, mySdc); } -void SSD1306::displayOn(void) -{ +void SSD1306::displayOn(void) { sendCommand(0xaf); //display on } -void SSD1306::displayOff(void) -{ - sendCommand(0xae); //display off +void SSD1306::displayOff(void) { + sendCommand(0xae); //display off } void SSD1306::setContrast(char contrast) { sendCommand(0x81); - sendCommand(contrast); + sendCommand(contrast); } + void SSD1306::flipScreenVertically() { sendCommand(0xA0 | 0x1); //SEGREMAP //Rotate screen 180 deg - sendCommand(0xC8); //COMSCANDEC Rotate screen 180 Deg } + void SSD1306::clear(void) { - memset(buffer, 0, (128*64 / 8)); + memset(buffer, 0, (128 * 64 / 8)); } void SSD1306::display(void) { @@ -88,10 +84,8 @@ void SSD1306::display(void) { sendCommand(0x0); sendCommand(0x7); - for (uint16_t i=0; i<(128*64/8); i++) { // send a bunch of data in one xmission - //Wire.begin(mySda, mySdc); Wire.beginTransmission(myI2cAddress); Wire.write(0x40); for (uint8_t x=0; x<16; x++) { @@ -102,18 +96,17 @@ void SSD1306::display(void) { yield(); Wire.endTransmission(); } - } void SSD1306::setPixel(int x, int y) { if (x >= 0 && x < 128 && y >= 0 && y < 64) { - + switch (myColor) { - case WHITE: buffer[x+ (y/8)*128] |= (1 << (y&7)); break; - case BLACK: buffer[x+ (y/8)*128] &= ~(1 << (y&7)); break; - case INVERSE: buffer[x+ (y/8)*128] ^= (1 << (y&7)); break; + case WHITE: buffer[x + (y/8)*128] |= (1 << (y&7)); break; + case BLACK: buffer[x + (y/8)*128] &= ~(1 << (y&7)); break; + case INVERSE: buffer[x + (y/8)*128] ^= (1 << (y&7)); break; } } } @@ -122,15 +115,15 @@ void SSD1306::setChar(int x, int y, unsigned char data) { for (int i = 0; i < 8; i++) { if (bitRead(data, i)) { setPixel(x,y + i); - } - } + } + } } // Code form http://playground.arduino.cc/Main/Utf8ascii byte SSD1306::utf8ascii(byte ascii) { - if ( ascii<128 ) // Standard ASCII-set 0..0x7F handling - { lastChar=0; - return( ascii ); + if ( ascii<128 ) { // Standard ASCII-set 0..0x7F handling + lastChar=0; + return( ascii ); } // get previous input @@ -147,7 +140,7 @@ byte SSD1306::utf8ascii(byte ascii) { } // Code form http://playground.arduino.cc/Main/Utf8ascii -String SSD1306::utf8ascii(String s) { +String SSD1306::utf8ascii(String s) { String r= ""; char c; for (int i=0; i<s.length(); i++) @@ -192,7 +185,7 @@ void SSD1306::drawString(int x, int y, String text) { currentCharWidth = pgm_read_byte(myFontData + CHAR_WIDTH_START_POS + charCode); // Jump to font data beginning currentCharStartPos = CHAR_WIDTH_START_POS + numberOfChars; - + for (int m = 0; m < charCode; m++) { currentCharStartPos += pgm_read_byte(myFontData + CHAR_WIDTH_START_POS + m) * charHeight / 8 + 1; @@ -201,26 +194,24 @@ void SSD1306::drawString(int x, int y, String text) { currentCharByteNum = ((charHeight * currentCharWidth) / 8) + 1; // iterate over all bytes of character for (int i = 0; i < currentCharByteNum; i++) { - + currentByte = pgm_read_byte(myFontData + currentCharStartPos + i); //Serial.println(String(charCode) + ", " + String(currentCharWidth) + ", " + String(currentByte)); // iterate over all bytes of character for(int bit = 0; bit < 8; bit++) { //int currentBit = bitRead(currentByte, bit); - + currentBitCount = i * 8 + bit; charX = currentBitCount % currentCharWidth; charY = currentBitCount / currentCharWidth; if (bitRead(currentByte, bit)) { - //Serial.println(String(charX) + ", " + String(charY)); - setPixel(startX + cursorX + charX, startY + charY); - //setPixel(charX, charY); + setPixel(startX + cursorX + charX, startY + charY); } - } - yield(); + } + yield(); } cursorX += currentCharWidth; @@ -248,7 +239,7 @@ void SSD1306::drawStringMaxWidth(int x, int y, int maxLineWidth, String text) { startsAt = endsAt + 1; } } - + } drawString(x, y + lineNumber * lineHeight, text.substring(startsAt)); } @@ -275,28 +266,28 @@ void SSD1306::setFont(const char *fontData) { void SSD1306::drawBitmap(int x, int y, int width, int height, const char *bitmap) { for (int i = 0; i < width * height / 8; i++ ){ unsigned char charColumn = 255 - pgm_read_byte(bitmap + i); - for (int j = 0; j < 8; j++) { + for (int j = 0; j < 8; j++) { int targetX = i % width + x; int targetY = (i / (width)) * 8 + j + y; if (bitRead(charColumn, j)) { - setPixel(targetX, targetY); + setPixel(targetX, targetY); } } - } + } } void SSD1306::setColor(int color) { - myColor = color; + myColor = color; } void SSD1306::drawRect(int x, int y, int width, int height) { for (int i = x; i < x + width; i++) { setPixel(i, y); - setPixel(i, y + height); + setPixel(i, y + height); } for (int i = y; i < y + height; i++) { setPixel(x, i); - setPixel(x + width, i); + setPixel(x + width, i); } } @@ -314,28 +305,24 @@ void SSD1306::drawXbm(int x, int y, int width, int height, const char *xbm) { } for (int i = 0; i < width * height / 8; i++ ){ unsigned char charColumn = pgm_read_byte(xbm + i); - for (int j = 0; j < 8; j++) { + for (int j = 0; j < 8; j++) { int targetX = (i * 8 + j) % width + x; int targetY = (8 * i / (width)) + y; if (bitRead(charColumn, j)) { - setPixel(targetX, targetY); + setPixel(targetX, targetY); } } - } + } } -void SSD1306::sendCommand(unsigned char com) -{ - //Wire.begin(mySda, mySdc); +void SSD1306::sendCommand(unsigned char com) { Wire.beginTransmission(myI2cAddress); //begin transmitting Wire.write(0x80); //command mode Wire.write(com); Wire.endTransmission(); // stop transmitting } -void SSD1306::sendInitCommands(void) -{ - +void SSD1306::sendInitCommands(void) { sendCommand(DISPLAYOFF); sendCommand(NORMALDISPLAY); sendCommand(SETDISPLAYCLOCKDIV); @@ -363,53 +350,4 @@ void SSD1306::sendInitCommands(void) sendCommand(NORMALDISPLAY); sendCommand(0x2e); // stop scroll sendCommand(DISPLAYON); - -} - -void SSD1306::nextFrameTick() { - myFrameTick++; - if (myFrameTick==myFrameWaitTicks && myFrameState == 0 || myFrameTick==myFrameTransitionTicks && myFrameState == 1) { - myFrameState = (myFrameState + 1) % 2; - if (myFrameState==FRAME_STATE_FIX) { - myCurrentFrame = (myCurrentFrame + 1) % myFrameCount; - } - myFrameTick = 0; - } - drawIndicators(myFrameCount, myCurrentFrame); - - switch(myFrameState) { - case 0: - (*myFrameCallbacks[myCurrentFrame])(0, 0); - break; - case 1: - (*myFrameCallbacks[myCurrentFrame])(-128 * myFrameTick / myFrameTransitionTicks, 0); - (*myFrameCallbacks[(myCurrentFrame + 1) % myFrameCount])(-128 * myFrameTick / myFrameTransitionTicks + 128, 0); - break; - } - -} -void SSD1306::drawIndicators(int frameCount, int activeFrame) { - for (int i = 0; i < frameCount; i++) { - const char *xbm; - if (activeFrame == i) { - xbm = active_bits; - } else { - xbm = inactive_bits; - } - drawXbm(64 - (12 * frameCount / 2) + 12 * i,56, 8, 8, xbm); - } -} -void SSD1306::setFrameCallbacks(int frameCount, void (*frameCallbacks[])(int x, int y)) { - myFrameCount = frameCount; - myFrameCallbacks = frameCallbacks; -} - -void SSD1306::setFrameWaitTicks(int frameWaitTicks) { - myFrameWaitTicks = frameWaitTicks; -} -void SSD1306::setFrameTransitionTicks(int frameTransitionTicks) { - myFrameTransitionTicks = frameTransitionTicks; -} -int SSD1306::getFrameState() { - return myFrameState; -} +} \ No newline at end of file diff --git a/ssd1306_i2c.h b/ssd1306_i2c.h index 078868cbd3ea2c68b5e4a04dde042e8c7e965de2..a3cb628d797cf7779dd23d7285a430315d6edfb9 100644 --- a/ssd1306_i2c.h +++ b/ssd1306_i2c.h @@ -29,21 +29,10 @@ Credits for parts of this code go to Mike Rankin. Thank you so much for sharing! #include <Arduino.h> #include "SSD1306Fonts.h" -#define active_width 8 -#define active_height 8 -const char active_bits[] PROGMEM = { - 0x00, 0x18, 0x3c, 0x7e, 0x7e, 0x3c, 0x18, 0x00 }; - -#define inactive_width 8 -#define inactive_height 8 -const char inactive_bits[] PROGMEM = { - 0x00, 0x0, 0x0, 0x18, 0x18, 0x0, 0x0, 0x00 }; - #define BLACK 0 #define WHITE 1 #define INVERSE 2 - #define WIDTH_POS 0 #define HEIGHT_POS 1 #define FIRST_CHAR_POS 2 @@ -88,19 +77,10 @@ private: int mySda; int mySdc; uint8_t buffer[128 * 64 / 8]; - int myFrameState = 0; - int myFrameTick = 0; - int myCurrentFrame = 0; - int myFrameCount = 0; - int myFrameWaitTicks = 100; - int myFrameTransitionTicks = 25; int myTextAlignment = TEXT_ALIGN_LEFT; int myColor = WHITE; byte lastChar; const char *myFontData = ArialMT_Plain_10; - void (**myFrameCallbacks)(int x, int y); - - public: // Create the display object connected to pin sda and sdc @@ -190,32 +170,4 @@ public: // ArialMT_Plain_10, ArialMT_Plain_16, ArialMT_Plain_24 void setFont(const char *fontData); - // Sets the callback methods of the format void method(x,y) - void setFrameCallbacks(int frameCount, void (*frameCallbacks[])(int x, int y)); - - // Tells the framework to move to the next tick. The - // current visible frame callback will be called once - // per tick - void nextFrameTick(void); - - // Draws the frame indicators. In a normal setup - // the framework does this for you - void drawIndicators(int frameCount, int activeFrame); - - // defines how many ticks a frame should remain visible - // This does not include the transition - void setFrameWaitTicks(int frameWaitTicks); - - // Defines how many ticks should be used for a transition - void setFrameTransitionTicks(int frameTransitionTicks); - - // Returns the current state of the internal state machine - // Possible values: FRAME_STATE_FIX, FRAME_STATE_TRANSITION - // You can use this to detect when there is no transition - // on the way to execute operations that would - int getFrameState(); - - const int FRAME_STATE_FIX = 0; - const int FRAME_STATE_TRANSITION = 1; - -}; +}; \ No newline at end of file