Stairs/module/src/display.cpp

258 lines
5.8 KiB
C++

/*
* Stairs lighting
* Copyright 2017 (c) Mark van Renswoude
*
* https://git.x2software.net/pub/Stairs
*/
#include "display.h"
#include <Fonts/FreeSans12pt7b.h>
#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();
}