ThingPulse OLED SSD1306
Dependents: Turtle_RadioShuttle mbed-os5-F303-18650-Manager-tp4056 Kretanje_kroz_izbornike_OLED128x64_4tipke
OLEDDisplayUi.cpp
00001 /** 00002 * The MIT License (MIT) 00003 * 00004 * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn 00005 * Copyright (c) 2018 by Fabrice Weinberg 00006 * Copyright (c) 2019 by Helmut Tschemernjak - www.radioshuttle.de 00007 * 00008 * Permission is hereby granted, free of charge, to any person obtaining a copy 00009 * of this software and associated documentation files (the "Software"), to deal 00010 * in the Software without restriction, including without limitation the rights 00011 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 00012 * copies of the Software, and to permit persons to whom the Software is 00013 * furnished to do so, subject to the following conditions: 00014 * 00015 * The above copyright notice and this permission notice shall be included in all 00016 * copies or substantial portions of the Software. 00017 * 00018 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 00019 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00020 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 00021 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 00022 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 00023 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 00024 * SOFTWARE. 00025 * 00026 * ThingPulse invests considerable time and money to develop these open source libraries. 00027 * Please support us by buying our products (and not the clones) from 00028 * https://thingpulse.com 00029 * 00030 */ 00031 00032 #include "OLEDDisplayUi.h" 00033 00034 void LoadingDrawDefault(OLEDDisplay *display, LoadingStage* stage, uint8_t progress) { 00035 display->setTextAlignment(TEXT_ALIGN_CENTER); 00036 display->setFont(ArialMT_Plain_10); 00037 display->drawString(64, 18, stage->process); 00038 display->drawProgressBar(4, 32, 120, 8, progress); 00039 }; 00040 00041 00042 OLEDDisplayUi::OLEDDisplayUi(OLEDDisplay *display) { 00043 this->display = display; 00044 00045 indicatorPosition = BOTTOM; 00046 indicatorDirection = LEFT_RIGHT; 00047 activeSymbol = ANIMATION_activeSymbol; 00048 inactiveSymbol = ANIMATION_inactiveSymbol; 00049 frameAnimationDirection = SLIDE_RIGHT; 00050 lastTransitionDirection = 1; 00051 ticksPerFrame = 151; // ~ 5000ms at 30 FPS 00052 ticksPerTransition = 15; // ~ 500ms at 30 FPS 00053 frameCount = 0; 00054 nextFrameNumber = -1; 00055 overlayCount = 0; 00056 indicatorDrawState = 1; 00057 loadingDrawFunction = LoadingDrawDefault; 00058 updateInterval = 33; 00059 state.lastUpdate = 0; 00060 state.ticksSinceLastStateSwitch = 0; 00061 state.frameState = FIXED; 00062 state.currentFrame = 0; 00063 state.frameTransitionDirection = 1; 00064 state.isIndicatorDrawen = true; 00065 state.manuelControll = false; 00066 state.userData = NULL; 00067 shouldDrawIndicators = true; 00068 autoTransition = true; 00069 } 00070 00071 void OLEDDisplayUi::init() { 00072 this->display->init(); 00073 } 00074 00075 void OLEDDisplayUi::setTargetFPS(uint8_t fps){ 00076 float oldInterval = this->updateInterval; 00077 this->updateInterval = ((float) 1.0 / (float) fps) * 1000; 00078 00079 // Calculate new ticksPerFrame 00080 float changeRatio = oldInterval / (float) this->updateInterval; 00081 this->ticksPerFrame *= changeRatio; 00082 this->ticksPerTransition *= changeRatio; 00083 } 00084 00085 // -/------ Automatic controll ------\- 00086 00087 void OLEDDisplayUi::enableAutoTransition(){ 00088 this->autoTransition = true; 00089 } 00090 void OLEDDisplayUi::disableAutoTransition(){ 00091 this->autoTransition = false; 00092 } 00093 void OLEDDisplayUi::setAutoTransitionForwards(){ 00094 this->state.frameTransitionDirection = 1; 00095 this->lastTransitionDirection = 1; 00096 } 00097 void OLEDDisplayUi::setAutoTransitionBackwards(){ 00098 this->state.frameTransitionDirection = -1; 00099 this->lastTransitionDirection = -1; 00100 } 00101 void OLEDDisplayUi::setTimePerFrame(uint16_t time){ 00102 this->ticksPerFrame = (uint16_t) ( (float) time / (float) updateInterval); 00103 } 00104 void OLEDDisplayUi::setTimePerTransition(uint16_t time){ 00105 this->ticksPerTransition = (uint16_t) ( (float) time / (float) updateInterval); 00106 } 00107 00108 // -/------ Customize indicator position and style -------\- 00109 void OLEDDisplayUi::enableIndicator(){ 00110 this->state.isIndicatorDrawen = true; 00111 } 00112 00113 void OLEDDisplayUi::disableIndicator(){ 00114 this->state.isIndicatorDrawen = false; 00115 } 00116 00117 void OLEDDisplayUi::enableAllIndicators(){ 00118 this->shouldDrawIndicators = true; 00119 } 00120 00121 void OLEDDisplayUi::disableAllIndicators(){ 00122 this->shouldDrawIndicators = false; 00123 } 00124 00125 void OLEDDisplayUi::setIndicatorPosition(IndicatorPosition pos) { 00126 this->indicatorPosition = pos; 00127 } 00128 void OLEDDisplayUi::setIndicatorDirection(IndicatorDirection dir) { 00129 this->indicatorDirection = dir; 00130 } 00131 void OLEDDisplayUi::setActiveSymbol(const uint8_t* symbol) { 00132 this->activeSymbol = symbol; 00133 } 00134 void OLEDDisplayUi::setInactiveSymbol(const uint8_t* symbol) { 00135 this->inactiveSymbol = symbol; 00136 } 00137 00138 00139 // -/----- Frame settings -----\- 00140 void OLEDDisplayUi::setFrameAnimation(AnimationDirection dir) { 00141 this->frameAnimationDirection = dir; 00142 } 00143 void OLEDDisplayUi::setFrames(FrameCallback* frameFunctions, uint8_t frameCount) { 00144 this->frameFunctions = frameFunctions; 00145 this->frameCount = frameCount; 00146 this->resetState(); 00147 } 00148 00149 // -/----- Overlays ------\- 00150 void OLEDDisplayUi::setOverlays(OverlayCallback* overlayFunctions, uint8_t overlayCount){ 00151 this->overlayFunctions = overlayFunctions; 00152 this->overlayCount = overlayCount; 00153 } 00154 00155 // -/----- Loading Process -----\- 00156 00157 void OLEDDisplayUi::setLoadingDrawFunction(LoadingDrawFunction loadingDrawFunction) { 00158 this->loadingDrawFunction = loadingDrawFunction; 00159 } 00160 00161 void OLEDDisplayUi::runLoadingProcess(LoadingStage* stages, uint8_t stagesCount) { 00162 uint8_t progress = 0; 00163 uint8_t increment = 100 / stagesCount; 00164 00165 for (uint8_t i = 0; i < stagesCount; i++) { 00166 display->clear(); 00167 this->loadingDrawFunction(this->display, &stages[i], progress); 00168 display->display(); 00169 00170 stages[i].callback(); 00171 00172 progress += increment; 00173 yield(); 00174 } 00175 00176 display->clear(); 00177 this->loadingDrawFunction(this->display, &stages[stagesCount-1], progress); 00178 display->display(); 00179 00180 delay(150); 00181 } 00182 00183 // -/----- Manuel control -----\- 00184 void OLEDDisplayUi::nextFrame() { 00185 if (this->state.frameState != IN_TRANSITION) { 00186 this->state.manuelControll = true; 00187 this->state.frameState = IN_TRANSITION; 00188 this->state.ticksSinceLastStateSwitch = 0; 00189 this->lastTransitionDirection = this->state.frameTransitionDirection; 00190 this->state.frameTransitionDirection = 1; 00191 } 00192 } 00193 void OLEDDisplayUi::previousFrame() { 00194 if (this->state.frameState != IN_TRANSITION) { 00195 this->state.manuelControll = true; 00196 this->state.frameState = IN_TRANSITION; 00197 this->state.ticksSinceLastStateSwitch = 0; 00198 this->lastTransitionDirection = this->state.frameTransitionDirection; 00199 this->state.frameTransitionDirection = -1; 00200 } 00201 } 00202 00203 void OLEDDisplayUi::switchToFrame(uint8_t frame) { 00204 if (frame >= this->frameCount) return; 00205 this->state.ticksSinceLastStateSwitch = 0; 00206 if (frame == this->state.currentFrame) return; 00207 this->state.frameState = FIXED; 00208 this->state.currentFrame = frame; 00209 this->state.isIndicatorDrawen = true; 00210 } 00211 00212 void OLEDDisplayUi::transitionToFrame(uint8_t frame) { 00213 if (frame >= this->frameCount) return; 00214 this->state.ticksSinceLastStateSwitch = 0; 00215 if (frame == this->state.currentFrame) return; 00216 this->nextFrameNumber = frame; 00217 this->lastTransitionDirection = this->state.frameTransitionDirection; 00218 this->state.manuelControll = true; 00219 this->state.frameState = IN_TRANSITION; 00220 this->state.frameTransitionDirection = frame < this->state.currentFrame ? -1 : 1; 00221 } 00222 00223 00224 // -/----- State information -----\- 00225 OLEDDisplayUiState* OLEDDisplayUi::getUiState(){ 00226 return &this->state; 00227 } 00228 00229 00230 int8_t OLEDDisplayUi::update(){ 00231 #ifdef ARDUINO 00232 unsigned long frameStart = millis(); 00233 #elif __MBED__ 00234 Timer t; 00235 t.start(); 00236 unsigned long frameStart = t.read_ms(); 00237 #else 00238 #error "Unkown operating system" 00239 #endif 00240 int8_t timeBudget = this->updateInterval - (frameStart - this->state.lastUpdate); 00241 if ( timeBudget <= 0) { 00242 // Implement frame skipping to ensure time budget is keept 00243 if (this->autoTransition && this->state.lastUpdate != 0) this->state.ticksSinceLastStateSwitch += ceil((double)-timeBudget / (double)this->updateInterval); 00244 00245 this->state.lastUpdate = frameStart; 00246 this->tick(); 00247 } 00248 #ifdef ARDUINO 00249 return this->updateInterval - (millis() - frameStart); 00250 #elif __MBED__ 00251 return this->updateInterval - (t.read_ms() - frameStart); 00252 #else 00253 #error "Unkown operating system" 00254 #endif 00255 } 00256 00257 00258 void OLEDDisplayUi::tick() { 00259 this->state.ticksSinceLastStateSwitch++; 00260 00261 switch (this->state.frameState) { 00262 case IN_TRANSITION: 00263 if (this->state.ticksSinceLastStateSwitch >= this->ticksPerTransition){ 00264 this->state.frameState = FIXED; 00265 this->state.currentFrame = getNextFrameNumber(); 00266 this->state.ticksSinceLastStateSwitch = 0; 00267 this->nextFrameNumber = -1; 00268 } 00269 break; 00270 case FIXED: 00271 // Revert manuelControll 00272 if (this->state.manuelControll) { 00273 this->state.frameTransitionDirection = this->lastTransitionDirection; 00274 this->state.manuelControll = false; 00275 } 00276 if (this->state.ticksSinceLastStateSwitch >= this->ticksPerFrame){ 00277 if (this->autoTransition){ 00278 this->state.frameState = IN_TRANSITION; 00279 } 00280 this->state.ticksSinceLastStateSwitch = 0; 00281 } 00282 break; 00283 } 00284 00285 this->display->clear(); 00286 this->drawFrame(); 00287 if (shouldDrawIndicators) { 00288 this->drawIndicator(); 00289 } 00290 this->drawOverlays(); 00291 this->display->display(); 00292 } 00293 00294 void OLEDDisplayUi::resetState() { 00295 this->state.lastUpdate = 0; 00296 this->state.ticksSinceLastStateSwitch = 0; 00297 this->state.frameState = FIXED; 00298 this->state.currentFrame = 0; 00299 this->state.isIndicatorDrawen = true; 00300 } 00301 00302 void OLEDDisplayUi::drawFrame(){ 00303 switch (this->state.frameState){ 00304 case IN_TRANSITION: { 00305 float progress = (float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition; 00306 int16_t x = 0, y = 0, x1 = 0, y1 = 0; 00307 switch(this->frameAnimationDirection){ 00308 case SLIDE_LEFT: 00309 x = -this->display->width() * progress; 00310 y = 0; 00311 x1 = x + this->display->width(); 00312 y1 = 0; 00313 break; 00314 case SLIDE_RIGHT: 00315 x = this->display->width() * progress; 00316 y = 0; 00317 x1 = x - this->display->width(); 00318 y1 = 0; 00319 break; 00320 case SLIDE_UP: 00321 x = 0; 00322 y = -this->display->height() * progress; 00323 x1 = 0; 00324 y1 = y + this->display->height(); 00325 break; 00326 case SLIDE_DOWN: 00327 default: 00328 x = 0; 00329 y = this->display->height() * progress; 00330 x1 = 0; 00331 y1 = y - this->display->height(); 00332 break; 00333 } 00334 00335 // Invert animation if direction is reversed. 00336 int8_t dir = this->state.frameTransitionDirection >= 0 ? 1 : -1; 00337 x *= dir; y *= dir; x1 *= dir; y1 *= dir; 00338 00339 bool drawenCurrentFrame; 00340 00341 00342 // Prope each frameFunction for the indicator Drawen state 00343 this->enableIndicator(); 00344 (this->frameFunctions[this->state.currentFrame])(this->display, &this->state, x, y); 00345 drawenCurrentFrame = this->state.isIndicatorDrawen; 00346 00347 this->enableIndicator(); 00348 (this->frameFunctions[this->getNextFrameNumber()])(this->display, &this->state, x1, y1); 00349 00350 // Build up the indicatorDrawState 00351 if (drawenCurrentFrame && !this->state.isIndicatorDrawen) { 00352 // Drawen now but not next 00353 this->indicatorDrawState = 2; 00354 } else if (!drawenCurrentFrame && this->state.isIndicatorDrawen) { 00355 // Not drawen now but next 00356 this->indicatorDrawState = 1; 00357 } else if (!drawenCurrentFrame && !this->state.isIndicatorDrawen) { 00358 // Not drawen in both frames 00359 this->indicatorDrawState = 3; 00360 } 00361 00362 // If the indicator isn't draw in the current frame 00363 // reflect it in state.isIndicatorDrawen 00364 if (!drawenCurrentFrame) this->state.isIndicatorDrawen = false; 00365 00366 break; 00367 } 00368 case FIXED: 00369 // Always assume that the indicator is drawn! 00370 // And set indicatorDrawState to "not known yet" 00371 this->indicatorDrawState = 0; 00372 this->enableIndicator(); 00373 (this->frameFunctions[this->state.currentFrame])(this->display, &this->state, 0, 0); 00374 break; 00375 } 00376 } 00377 00378 void OLEDDisplayUi::drawIndicator() { 00379 00380 // Only draw if the indicator is invisible 00381 // for both frames or 00382 // the indiactor is shown and we are IN_TRANSITION 00383 if (this->indicatorDrawState == 3 || (!this->state.isIndicatorDrawen && this->state.frameState != IN_TRANSITION)) { 00384 return; 00385 } 00386 00387 uint8_t posOfHighlightFrame = 0; 00388 float indicatorFadeProgress = 0; 00389 00390 // if the indicator needs to be slided in we want to 00391 // highlight the next frame in the transition 00392 uint8_t frameToHighlight = this->indicatorDrawState == 1 ? this->getNextFrameNumber() : this->state.currentFrame; 00393 00394 // Calculate the frame that needs to be highlighted 00395 // based on the Direction the indiactor is drawn 00396 switch (this->indicatorDirection){ 00397 case LEFT_RIGHT: 00398 posOfHighlightFrame = frameToHighlight; 00399 break; 00400 case RIGHT_LEFT: 00401 default: 00402 posOfHighlightFrame = this->frameCount - frameToHighlight; 00403 break; 00404 } 00405 00406 switch (this->indicatorDrawState) { 00407 case 1: // Indicator was not drawn in this frame but will be in next 00408 // Slide IN 00409 indicatorFadeProgress = 1 - ((float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition); 00410 break; 00411 case 2: // Indicator was drawn in this frame but not in next 00412 // Slide OUT 00413 indicatorFadeProgress = ((float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition); 00414 break; 00415 } 00416 00417 //Space between indicators - reduce for small screen sizes 00418 uint16_t indicatorSpacing = 12; 00419 if (this->display->getHeight() < 64 && (this->indicatorPosition == RIGHT || this->indicatorPosition == LEFT)) { 00420 indicatorSpacing = 6; 00421 } 00422 00423 uint16_t frameStartPos = (indicatorSpacing * frameCount / 2); 00424 const uint8_t *image; 00425 00426 uint16_t x = 0,y = 0; 00427 00428 00429 for (uint8_t i = 0; i < this->frameCount; i++) { 00430 00431 switch (this->indicatorPosition){ 00432 case TOP: 00433 y = 0 - (8 * indicatorFadeProgress); 00434 x = (this->display->width() / 2) - frameStartPos + 12 * i; 00435 break; 00436 case BOTTOM: 00437 y = (this->display->height() - 8) + (8 * indicatorFadeProgress); 00438 x = (this->display->width() / 2) - frameStartPos + 12 * i; 00439 break; 00440 case RIGHT: 00441 x = (this->display->width() - 8) + (8 * indicatorFadeProgress); 00442 y = (this->display->height() / 2) - frameStartPos + 2 + 12 * i; 00443 break; 00444 case LEFT: 00445 default: 00446 x = 0 - (8 * indicatorFadeProgress); 00447 y = (this->display->height() / 2) - frameStartPos + 2 + indicatorSpacing * i; 00448 break; 00449 } 00450 00451 if (posOfHighlightFrame == i) { 00452 image = this->activeSymbol; 00453 } else { 00454 image = this->inactiveSymbol; 00455 } 00456 00457 this->display->drawFastImage(x, y, 8, 8, image); 00458 } 00459 } 00460 00461 void OLEDDisplayUi::drawOverlays() { 00462 for (uint8_t i=0;i<this->overlayCount;i++){ 00463 (this->overlayFunctions[i])(this->display, &this->state); 00464 } 00465 } 00466 00467 uint8_t OLEDDisplayUi::getNextFrameNumber(){ 00468 if (this->nextFrameNumber != -1) return this->nextFrameNumber; 00469 return (this->state.currentFrame + this->frameCount + this->state.frameTransitionDirection) % this->frameCount; 00470 }
Generated on Wed Jul 13 2022 11:33:40 by
1.7.2
Helmut Tschemernjak