Add U8X8 carousel screens
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
#ifndef CAROUSEL_H
|
||||
#define CAROUSEL_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <U8g2lib.h>
|
||||
|
||||
#include "screens.h"
|
||||
|
||||
class Carousel {
|
||||
public:
|
||||
Carousel(Screen *screens, size_t screenCount);
|
||||
|
||||
void begin(unsigned long now);
|
||||
void update(unsigned long now);
|
||||
void next(unsigned long now);
|
||||
void render(U8X8 &display) const;
|
||||
bool consumeDirty();
|
||||
|
||||
private:
|
||||
Screen *screens_;
|
||||
size_t screenCount_;
|
||||
size_t currentIndex_;
|
||||
unsigned long nextAutoAdvanceAt_;
|
||||
unsigned long manualModeUntil_;
|
||||
bool dirty_;
|
||||
|
||||
void scheduleNextAdvance(unsigned long now);
|
||||
void drawIndicators(U8X8 &display) const;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef DISPLAY_CONFIG_H
|
||||
#define DISPLAY_CONFIG_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
namespace display_config {
|
||||
|
||||
constexpr uint8_t kNextButtonPin = A4;
|
||||
|
||||
constexpr unsigned long kAutoAdvanceIntervalMs = 10000;
|
||||
constexpr unsigned long kManualHoldIntervalMs = 60000;
|
||||
constexpr unsigned long kButtonDebounceMs = 150;
|
||||
constexpr unsigned long kDebugLogIntervalMs = 500;
|
||||
|
||||
constexpr uint8_t kDisplayColumns = 16;
|
||||
constexpr uint8_t kDisplayRows = 8;
|
||||
constexpr uint8_t kIndicatorRow = 7;
|
||||
constexpr uint8_t kIndicatorSpacing = 2;
|
||||
|
||||
} // namespace display_config
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef SCREENS_H
|
||||
#define SCREENS_H
|
||||
|
||||
#include <U8g2lib.h>
|
||||
|
||||
struct Screen {
|
||||
const char *title;
|
||||
void (*render)(U8X8 &display);
|
||||
};
|
||||
|
||||
void renderTimeScreen(U8X8 &display);
|
||||
void renderWeatherScreen(U8X8 &display);
|
||||
void renderTemperatureScreen(U8X8 &display);
|
||||
void renderHumidityScreen(U8X8 &display);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,104 @@
|
||||
#include "carousel.h"
|
||||
|
||||
#include "display_config.h"
|
||||
|
||||
Carousel::Carousel(Screen *screens, size_t screenCount)
|
||||
: screens_(screens),
|
||||
screenCount_(screenCount),
|
||||
currentIndex_(0),
|
||||
nextAutoAdvanceAt_(0),
|
||||
manualModeUntil_(0),
|
||||
dirty_(true) {}
|
||||
|
||||
void Carousel::begin(unsigned long now) {
|
||||
currentIndex_ = 0;
|
||||
manualModeUntil_ = 0;
|
||||
dirty_ = true;
|
||||
scheduleNextAdvance(now);
|
||||
Serial.print("carousel: begin index=");
|
||||
Serial.print(currentIndex_);
|
||||
Serial.print(" nextAutoAdvanceAt=");
|
||||
Serial.println(nextAutoAdvanceAt_);
|
||||
}
|
||||
|
||||
void Carousel::update(unsigned long now) {
|
||||
if (screenCount_ == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (manualModeUntil_ != 0 && now < manualModeUntil_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (now >= nextAutoAdvanceAt_) {
|
||||
currentIndex_ = (currentIndex_ + 1) % screenCount_;
|
||||
dirty_ = true;
|
||||
scheduleNextAdvance(now);
|
||||
Serial.print("carousel: auto advance index=");
|
||||
Serial.print(currentIndex_);
|
||||
Serial.print(" now=");
|
||||
Serial.print(now);
|
||||
Serial.print(" nextAutoAdvanceAt=");
|
||||
Serial.println(nextAutoAdvanceAt_);
|
||||
}
|
||||
}
|
||||
|
||||
void Carousel::next(unsigned long now) {
|
||||
if (screenCount_ == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentIndex_ = (currentIndex_ + 1) % screenCount_;
|
||||
manualModeUntil_ = now + display_config::kManualHoldIntervalMs;
|
||||
dirty_ = true;
|
||||
scheduleNextAdvance(manualModeUntil_);
|
||||
Serial.print("carousel: manual advance index=");
|
||||
Serial.print(currentIndex_);
|
||||
Serial.print(" now=");
|
||||
Serial.print(now);
|
||||
Serial.print(" manualModeUntil=");
|
||||
Serial.print(manualModeUntil_);
|
||||
Serial.print(" nextAutoAdvanceAt=");
|
||||
Serial.println(nextAutoAdvanceAt_);
|
||||
}
|
||||
|
||||
void Carousel::render(U8X8 &display) const {
|
||||
if (screenCount_ == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
screens_[currentIndex_].render(display);
|
||||
drawIndicators(display);
|
||||
}
|
||||
|
||||
bool Carousel::consumeDirty() {
|
||||
const bool wasDirty = dirty_;
|
||||
dirty_ = false;
|
||||
return wasDirty;
|
||||
}
|
||||
|
||||
void Carousel::scheduleNextAdvance(unsigned long now) {
|
||||
nextAutoAdvanceAt_ = now + display_config::kAutoAdvanceIntervalMs;
|
||||
}
|
||||
|
||||
void Carousel::drawIndicators(U8X8 &display) const {
|
||||
char indicatorLine[display_config::kDisplayColumns + 1];
|
||||
|
||||
for (uint8_t i = 0; i < display_config::kDisplayColumns; ++i) {
|
||||
indicatorLine[i] = ' ';
|
||||
}
|
||||
indicatorLine[display_config::kDisplayColumns] = '\0';
|
||||
|
||||
const int totalWidth =
|
||||
static_cast<int>((screenCount_ - 1) * display_config::kIndicatorSpacing) + 1;
|
||||
const int startX = (display_config::kDisplayColumns - totalWidth) / 2;
|
||||
|
||||
for (size_t i = 0; i < screenCount_; ++i) {
|
||||
const int x = startX + static_cast<int>(i * display_config::kIndicatorSpacing);
|
||||
if (x >= 0 && x < display_config::kDisplayColumns) {
|
||||
indicatorLine[x] = (i == currentIndex_) ? '*' : 'o';
|
||||
}
|
||||
}
|
||||
|
||||
display.drawString(0, display_config::kIndicatorRow, indicatorLine);
|
||||
}
|
||||
+56
-6
@@ -2,17 +2,67 @@
|
||||
#include <U8g2lib.h>
|
||||
#include <Wire.h>
|
||||
|
||||
#include "carousel.h"
|
||||
#include "display_config.h"
|
||||
#include "screens.h"
|
||||
|
||||
// SH1106 128x64 OLED over hardware I2C, rotated 180 degrees for flipped mounting.
|
||||
U8G2_SH1106_128X64_NONAME_F_HW_I2C display(U8G2_R2, U8X8_PIN_NONE);
|
||||
U8X8_SH1106_128X64_NONAME_HW_I2C display(U8X8_PIN_NONE, SCL, SDA);
|
||||
|
||||
Screen screens[] = {
|
||||
{"Local Time", renderTimeScreen},
|
||||
{"Forecast", renderWeatherScreen},
|
||||
{"Temperature", renderTemperatureScreen},
|
||||
{"Humidity", renderHumidityScreen},
|
||||
};
|
||||
|
||||
Carousel carousel(screens, sizeof(screens) / sizeof(screens[0]));
|
||||
|
||||
unsigned long lastButtonPressAt = 0;
|
||||
int lastNextButtonState = LOW;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
pinMode(display_config::kNextButtonPin, INPUT);
|
||||
|
||||
display.begin();
|
||||
display.clearBuffer();
|
||||
display.setFont(u8g2_font_ncenB08_tr);
|
||||
display.drawStr(18, 32, "Hello, world!");
|
||||
display.sendBuffer();
|
||||
display.setFlipMode(1);
|
||||
display.setFont(u8x8_font_chroma48medium8_r);
|
||||
display.clearDisplay();
|
||||
carousel.begin(millis());
|
||||
|
||||
Serial.println("setup: display initialized");
|
||||
Serial.print("setup: next button pin=");
|
||||
Serial.println(display_config::kNextButtonPin);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
delay(1000);
|
||||
const unsigned long now = millis();
|
||||
const int nextButtonState = digitalRead(display_config::kNextButtonPin);
|
||||
|
||||
if (nextButtonState != lastNextButtonState) {
|
||||
Serial.print("button: state changed to ");
|
||||
Serial.print(nextButtonState);
|
||||
Serial.print(" at ");
|
||||
Serial.println(now);
|
||||
}
|
||||
|
||||
if (lastNextButtonState == HIGH && nextButtonState == LOW &&
|
||||
now - lastButtonPressAt >= display_config::kButtonDebounceMs) {
|
||||
Serial.print("button: press detected at ");
|
||||
Serial.println(now);
|
||||
carousel.next(now);
|
||||
lastButtonPressAt = now;
|
||||
}
|
||||
|
||||
lastNextButtonState = nextButtonState;
|
||||
|
||||
carousel.update(now);
|
||||
if (carousel.consumeDirty()) {
|
||||
Serial.print("display: redraw at ");
|
||||
Serial.println(now);
|
||||
carousel.render(display);
|
||||
}
|
||||
|
||||
delay(100);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#include "screens.h"
|
||||
|
||||
namespace {
|
||||
|
||||
void clearContentRows(U8X8 &display) {
|
||||
for (uint8_t row = 0; row < 7; ++row) {
|
||||
display.clearLine(row);
|
||||
}
|
||||
}
|
||||
|
||||
void drawScreenFrame(U8X8 &display, const char *title, const char *value) {
|
||||
clearContentRows(display);
|
||||
display.drawString(0, 0, title);
|
||||
display.drawString(0, 2, "--------------");
|
||||
display.drawString(0, 4, value);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void renderTimeScreen(U8X8 &display) {
|
||||
drawScreenFrame(display, "Local Time", "12:34");
|
||||
}
|
||||
|
||||
void renderWeatherScreen(U8X8 &display) {
|
||||
drawScreenFrame(display, "Forecast", "Sunny");
|
||||
}
|
||||
|
||||
void renderTemperatureScreen(U8X8 &display) {
|
||||
drawScreenFrame(display, "Temperature", "21.5 C");
|
||||
}
|
||||
|
||||
void renderHumidityScreen(U8X8 &display) {
|
||||
drawScreenFrame(display, "Humidity", "45 %");
|
||||
}
|
||||
Reference in New Issue
Block a user