258 lines
5.8 KiB
C++
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();
|
|
}
|
|
|
|
|