/* * Stairs lighting * Copyright 2017 (c) Mark van Renswoude * * https://git.x2software.net/pub/Stairs */ #include "display.h" #include #include "config.h" #include "icons.h" #include "global.h" const uint8_t WaitAnimationInterval = 150; void Display::init() { mDisplay = new Adafruit_SSD1306(128, 32, &Wire, -1); mDisplay->begin(SSD1306_SWITCHCAPVCC, 0x3C); } void Display::update() { // TODO invalidate when comm state changes switch (state) { case State::WaitingForComm: drawWaitingForComm(); break; case State::Linking: case State::LinkingRequest: drawLinking(); break; case State::LinkingSet: drawModuleIndex(); break; case State::DisplayModuleIndex: if (mLastDrawnState == state && currentTime - mLastStateChange >= DisplayIdleTimeout) { state = State::DisplayOff; off(); } else drawModuleIndex(); break; case State::DisplayOff: off(); break; } setLastDrawnState(); } void Display::drawWaitingForComm() { if (mLastDrawnState == State::WaitingForComm && currentTime - mLastWaiting < WaitAnimationInterval) return; checkOn(); mDisplay->clearDisplay(); drawTitle("Waiting for signal"); uint8_t xOffset = (mWaitAnimationStep == 1 || mWaitAnimationStep == 2) ? WaitCursorSegmentWidth : 0; uint8_t yOffset = (mWaitAnimationStep == 2 || mWaitAnimationStep == 3) ? WaitCursorSegmentHeight : 0; drawRotatedBitmap(xOffset, mDisplay->height() - (WaitCursorSegmentHeight * 2) + yOffset, WaitCursorSegment, WaitCursorSegmentWidth, WaitCursorSegmentHeight, mWaitAnimationStep); mDisplay->display(); mWaitAnimationStep++; if (mWaitAnimationStep > 3) mWaitAnimationStep = 0; mLastWaiting = currentTime; } void Display::drawModuleIndex() { uint8_t moduleIndex = settings.getModuleIndex(); if (mLastDrawnState == State::DisplayModuleIndex && mLastModuleIndex == moduleIndex) return; checkOn(); mDisplay->clearDisplay(); drawTitle("Steps"); drawCommIcon(); mDisplay->setFont(&FreeSans12pt7b); mDisplay->setCursor(0, mDisplay->height() - 1); if (moduleIndex == ModuleIndexUndefined) { mDisplay->print("Not set"); } else { uint8_t firstStep = (moduleIndex * 2) + 1; mDisplay->print(firstStep); mDisplay->print(" - "); mDisplay->print(firstStep + 1); } mDisplay->display(); } void Display::drawLinking() { if (mLastDrawnState == State::Linking) return; checkOn(); mDisplay->clearDisplay(); drawTitle("Steps"); drawCommIcon(); mDisplay->setFont(&FreeSans12pt7b); mDisplay->setCursor(0, mDisplay->height() - 1); mDisplay->print("Click to set"); mDisplay->display(); } void Display::setLastDrawnState() { if (state != mLastDrawnState) { mLastDrawnState = state; mLastStateChange = currentTime; } } void Display::off() { if (mLastDrawnState != State::DisplayOff) { // I've had trouble waking the display up again, so just clear it for now mDisplay->ssd1306_command(SSD1306_DISPLAYOFF); mDisplay->ssd1306_command(SSD1306_CHARGEPUMP); mDisplay->ssd1306_command(0x10); // disable charge pump //mDisplay->clearDisplay(); //mDisplay->display(); } } void Display::checkOn() { if (mLastDrawnState == State::DisplayOff) { mDisplay->ssd1306_command(SSD1306_CHARGEPUMP); mDisplay->ssd1306_command(0x14); // enable charge pump mDisplay->ssd1306_command(SSD1306_DISPLAYON); } } void Display::drawTitle(const char* title) { mDisplay->setTextSize(1); mDisplay->setTextColor(WHITE); mDisplay->setFont(NULL); mDisplay->setCursor(0, 0); mDisplay->print(title); } void Display::drawCommIcon() { mDisplay->drawBitmap(mDisplay->width() - IconCommWidth, 0, currentTime - comm.getPacketStartTime() <= CommIdleTimeout ? IconCommOn : IconCommOff, IconCommWidth, IconCommHeight, 1); } typedef void (*WritePixelProc)(Adafruit_SSD1306* display, int8_t boundsX, int8_t boundsY, int8_t boundsW, int8_t boundsH, int8_t relativeX, int8_t relativeY); void writePixel0(Adafruit_SSD1306* display, int8_t boundsX, int8_t boundsY, int8_t boundsW, int8_t boundsH, int8_t relativeX, int8_t relativeY) { display->writePixel(boundsX + relativeX, boundsY + relativeY, 1); } void writePixel90(Adafruit_SSD1306* display, int8_t boundsX, int8_t boundsY, int8_t boundsW, int8_t boundsH, int8_t relativeX, int8_t relativeY) { display->writePixel(boundsX + boundsW - relativeY, boundsY + relativeX, 1); } void writePixel180(Adafruit_SSD1306* display, int8_t boundsX, int8_t boundsY, int8_t boundsW, int8_t boundsH, int8_t relativeX, int8_t relativeY) { display->writePixel(boundsX + boundsW - relativeX, boundsY + boundsH - relativeY, 1); } void writePixel270(Adafruit_SSD1306* display, int8_t boundsX, int8_t boundsY, int8_t boundsW, int8_t boundsH, int8_t relativeX, int8_t relativeY) { display->writePixel(boundsX + relativeY, boundsY + boundsH - relativeX, 1); } void Display::drawRotatedBitmap(int8_t x, int8_t y, const uint8_t bitmap[], int8_t w, int8_t h, uint8_t rotation) { int16_t byteWidth = (w + 7) / 8; int8_t byte = 0; mDisplay->startWrite(); WritePixelProc writePixel; switch (rotation) { case 1: // 90 degrees writePixel = writePixel90; break; case 2: // 180 degrees writePixel = writePixel180; break; case 3: // 270 degrees writePixel = writePixel270; break; default: // 0 degrees writePixel = writePixel0; break; } for(int8_t relativeY = 0; relativeY < h; relativeY++) { for(int8_t relativeX = 0; relativeX < w; relativeX++) { if(relativeX & 7) byte <<= 1; else byte = pgm_read_byte(&bitmap[relativeY * byteWidth + relativeX / 8]); if (byte & 0x80) writePixel(mDisplay, x, y, w, h, relativeX, relativeY); } } mDisplay->endWrite(); }