Files
renderbug-cpp/src/platform/arduino/U8Display.cpp

239 lines
7.3 KiB
C++

#include <Figments.h>
#include <U8g2lib.h>
#include "../../Static.h"
#include <ArduinoLog.h>
#include "../../LogService.h"
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, 16, 15, 4);
class U8Display : public Task {
public:
U8Display() : Task("U8Display") {}
enum ScreenState {
BootSplash,
Running,
Message,
Idle = Running
};
void onStart() {
Log.trace("display: starting redraw thread");
xTaskCreatePinnedToCore(
&U8Display::redrawTask,
name,
2000,
this,
1,
&m_renderTask, 0
);
}
void onStop() {
Log.trace("display: stopping redraw thread");
vTaskDelete(m_renderTask);
}
void handleEvent(const InputEvent& evt) {
m_lastEvent = evt;
if (m_state == Idle) {
switch(evt.intent) {
case InputEvent::NetworkStatus:
m_nextState = Message;
m_message = evt.asBool() ? "Online!" : "Offline :[";
break;
case InputEvent::SetPattern:
m_nextState = Message;
m_message = "Pattern: " + String(evt.asString());
break;
case InputEvent::SetPower:
m_nextState = Message;
m_message = evt.asBool() ? "Power On" : "Power Off";
break;
case InputEvent::SaveConfigurationRequest:
m_nextState = Message;
m_message = "Settings Saved!";
break;
case InputEvent::FirmwareUpdate:
m_nextState = Message;
m_message = "Firmware update";
break;
case InputEvent::PreviousPattern:
m_nextState = Message;
m_message = "Previous Pattern";
break;
case InputEvent::NextPattern:
m_nextState = Message;
m_message = "Next Pattern";
break;
}
}
}
void drawSplash() {
//u8g2.setFont(u8g2_font_VCR_OSD_mr);
u8g2.setFont(u8g2_font_HelvetiPixelOutline_tr);
u8g2.setDrawColor(1);
uint8_t splashWidth = u8g2.getStrWidth("Renderbug!");
u8g2.drawStr(64 - splashWidth / 2, 32 - 15, "Renderbug!");
u8g2.setFont(u8g2_font_7x13B_mr);
u8g2.setCursor(15, 64 - 7);
u8g2.print("Version ");
u8g2.print(RENDERBUG_VERSION);
for(int i = 0; i < 3; i++) {
uint8_t sparkleX = (64 - splashWidth/2) + scale8(7-i, beatsin8(40)) * (splashWidth/7) + 5;
uint8_t sparkleY = scale8(3+i, beatsin8(40)) * 3 + 7;
u8g2.setDrawColor(2);
if (beatsin8(60*4) + i * 3 >= 170) {
u8g2.drawLine(sparkleX - 3, sparkleY - 3, sparkleX + 3, sparkleY + 3);
u8g2.drawLine(sparkleX + 3, sparkleY - 3, sparkleX - 3, sparkleY + 3);
} else if (beatsin8(60*4) + i * 2 >= 82) {
u8g2.drawLine(sparkleX - 4, sparkleY - 4, sparkleX + 2, sparkleY + 2);
u8g2.drawLine(sparkleX - 2, sparkleY - 2, sparkleX + 4, sparkleY + 4);
u8g2.drawLine(sparkleX + 4, sparkleY - 4, sparkleX - 2, sparkleY + 2);
u8g2.drawLine(sparkleX + 2, sparkleY - 2, sparkleX - 4, sparkleY + 4);
} else {
u8g2.drawLine(sparkleX - 2, sparkleY, sparkleX + 2, sparkleY);
u8g2.drawLine(sparkleX, sparkleY - 2, sparkleX, sparkleY + 2);
}
}
}
void drawMessage() {
//u8g2.setFont(u8g2_font_VCR_OSD_mr);
u8g2.setFont(u8g2_font_HelvetiPixelOutline_tr);
uint8_t splashWidth = u8g2.getStrWidth(m_message.c_str());
if (splashWidth >= 128) {
u8g2.setFont(u8g2_font_7x13B_mr);
splashWidth = u8g2.getStrWidth(m_message.c_str());
}
u8g2.drawStr(64 - splashWidth / 2, 32 - 15, m_message.c_str());
}
void drawTime() {
u8g2.setFont(u8g2_font_7x13O_tn);
u8g2.setCursor(0, 64);
struct tm timeinfo;
Platform::getLocalTime(&timeinfo);
uint8_t hour = timeinfo.tm_hour;
uint8_t minute = timeinfo.tm_min;
u8g2.print(hour);
u8g2.print(":");
u8g2.print(minute);
}
void drawState(ScreenState state) {
switch(state) {
case BootSplash:
drawSplash();
break;
case Message:
drawMessage();
break;
case Running:
uint8_t y = 11;
u8g2.setFont(u8g2_font_7x13B_mr);
u8g2.setCursor(0, y);
u8g2.print("FPS: ");
u8g2.setFont(u8g2_font_7x13O_tn);
u8g2.print(FastLED.getFPS());
y += 12;
u8g2.setCursor(0, y);
u8g2.setFont(u8g2_font_7x13B_mr);
u8g2.print("Last event: ");
y += 7;
u8g2.setCursor(10, y);
u8g2.setFont(u8g2_font_4x6_tr);
const char* intentName = LogService::intentName(m_lastEvent.intent);
if (intentName) {
u8g2.print(intentName);
} else {
u8g2.print("<");
u8g2.print(m_lastEvent.intent);
u8g2.print(">");
}
y += 12;
u8g2.setCursor(15, y);
u8g2.setFont(u8g2_font_7x13O_tf);
u8g2.print(LogService::eventValue(m_lastEvent));
drawTime();
break;
}
}
void loop() {
EVERY_N_MILLISECONDS(8) {
xTaskNotifyGive(m_renderTask);
}
}
private:
ScreenState m_state = BootSplash;
ScreenState m_nextState = BootSplash;
uint8_t m_transitionFrame = 0;
uint8_t m_screenStartTime = 0;
InputEvent m_lastEvent;
String m_message;
TaskHandle_t m_renderTask;
void redraw() {
if (m_state != m_nextState) {
EVERY_N_MILLISECONDS(8) {
constexpr uint8_t speed = 11;
if (m_transitionFrame <= 255 - speed) {
m_transitionFrame += speed;
} else {
m_transitionFrame = 255;
}
uint8_t offset = ease8InOutApprox(m_transitionFrame);
u8g2.clearBuffer();
if (m_transitionFrame <= 128) {
uint8_t width = scale8(128, offset * 2);
u8g2.setDrawColor(1);
drawState(m_state);
u8g2.drawBox(0, 0, width, 64);
u8g2.setDrawColor(2);
u8g2.drawBox(width, 0, 8, 64);
} else {
uint8_t width = scale8(128, offset/2)*2;
u8g2.setDrawColor(1);
drawState(m_nextState);
u8g2.setDrawColor(2);
u8g2.drawBox(std::max(0, width - 8), 0, 8, 64);
u8g2.setDrawColor(1);
u8g2.drawBox(width, 0, 128, 64);
}
u8g2.sendBuffer();
if (m_transitionFrame == 255) {
m_state = m_nextState;
m_screenStartTime = millis();
m_transitionFrame = 0;
}
}
} else {
u8g2.clearBuffer();
drawState(m_state);
u8g2.sendBuffer();
uint16_t screenTime = millis() - m_screenStartTime;
if (screenTime >= 7000 && m_state != Idle) {
m_nextState = Idle;
}
}
}
static void redrawTask(void* data) {
u8g2.begin();
U8Display* self = static_cast<U8Display*>(data);
self->m_screenStartTime = millis();
while (true) {
self->redraw();
ulTaskNotifyTake(0, portMAX_DELAY);
}
}
};
STATIC_ALLOC(U8Display);
STATIC_TASK(U8Display);