#include <U8g2lib.h>
#include <Wire.h> // library requires for IIC communication
#include <FastLED.h>
#define NUM_LEDS 8
#define ENCODER_BTN 4
#define ENCODER_CLK 2
#define ENCODER_DT 3
#define LINE_HEIGHT 13
#define TEXT_HEIGHT 9
long time = 0;
CRGB leds[NUM_LEDS];
byte initial = 0; // изменение оттенка LED
U8G2_SSD1306_128X64_NONAME_2_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE); // initialization for the used OLED display
/* States */
class State {
protected:
int lines;
public:
virtual void RenderLed();
virtual void RenderText(int, bool);
virtual void KeyUp(int);
virtual void KeyDown(int);
int GetLinesCount() {
return lines;
}
};
class StaticState : public State {
private:
uint8_t arr[4]{12, 13, 230, 255};
public:
StaticState() {
lines = 5;
}
void RenderLed() {
for (int i = 0; i < NUM_LEDS; i++) {
leds[i].setRGB(arr[0], arr[1], arr[2]);
}
FastLED.setBrightness(arr[3]);
FastLED.show();
}
void RenderText(int activeLine, bool isSelected) {
u8g2.firstPage();
u8g2.setFont(u8g2_font_t0_14_mr);
u8g2.setFontMode(1);
u8g2.setDrawColor(1);
do {
if (!isSelected) {
u8g2.drawStr(115, (activeLine + 1) * LINE_HEIGHT - 2, "<-");
} else {
u8g2.drawBox(0, activeLine * LINE_HEIGHT, 128, LINE_HEIGHT);
}
u8g2.setDrawColor(2);
u8g2.drawStr(5, LINE_HEIGHT - 2, "mode");
u8g2.drawStr(60, LINE_HEIGHT - 2, "static");
char strTmp[5] = {0};
sprintf(strTmp, "%d", arr[0]);
u8g2.drawStr(5, 2 * LINE_HEIGHT - 2, "red");
u8g2.drawStr(60, 2 * LINE_HEIGHT - 2, strTmp);
sprintf(strTmp, "%d", arr[1]);
u8g2.drawStr(5, 3 * LINE_HEIGHT - 2, "green");
u8g2.drawStr(60, 3 * LINE_HEIGHT - 2, strTmp);
sprintf(strTmp, "%d", arr[2]);
u8g2.drawStr(5, 4 * LINE_HEIGHT - 2, "blue");
u8g2.drawStr(60, 4 * LINE_HEIGHT - 2, strTmp);
sprintf(strTmp, "%d", arr[3]);
u8g2.drawStr(5, 5 * LINE_HEIGHT - 2, "bright");
u8g2.drawStr(60, 5 * LINE_HEIGHT - 2, strTmp);
} while ( u8g2.nextPage() );
}
void KeyUp(int activeLine) {
if (activeLine == 0) return;
arr[activeLine - 1]++;
}
void KeyDown(int activeLine) {
if (activeLine == 0) return;
arr[activeLine - 1]--;
}
};
class FireState : public State {
private:
uint8_t arr[1]{255};
long lastRendered = 0;
public:
FireState() {
lines = 2;
}
void RenderLed() {
fadeToBlackBy(leds, NUM_LEDS, 2);
int pos = beatsin16(13, 0, NUM_LEDS - 1);
leds[pos] += CHSV(initial++, 255, 192);
FastLED.setBrightness(arr[0]);
FastLED.show();
}
void RenderText(int activeLine, bool isSelected) {
u8g2.firstPage();
u8g2.setFont(u8g2_font_t0_14_mr); // set custom font
u8g2.setFontMode(1);
u8g2.setDrawColor(1);
do {
if (!isSelected) {
u8g2.drawStr(115, (activeLine + 1) * LINE_HEIGHT - 2, "<-");
} else {
u8g2.drawBox(0, activeLine * LINE_HEIGHT, 128, LINE_HEIGHT);
}
u8g2.setDrawColor(2);
u8g2.drawStr(5, LINE_HEIGHT - 2, "mode");
u8g2.drawStr(60, LINE_HEIGHT - 2, "fire");
char strTmp[5] = {0};
sprintf(strTmp, "%d", arr[0]);
u8g2.drawStr(5, 2 * LINE_HEIGHT - 2, "hue");
u8g2.drawStr(60, 2 * LINE_HEIGHT - 2, strTmp);
} while ( u8g2.nextPage() );
}
void KeyUp(int activeLine) {
if (activeLine == 0) return;
arr[activeLine - 1]++;
}
void KeyDown(int activeLine) {
if (activeLine == 0) return;
arr[activeLine - 1]--;
}
};
class SnakeState : public State {
private:
long lastRendered = 0;
uint8_t arr[4]{100, 100, 100, 100}; //r g b speed
int snakeLength;
int headPos;
public:
SnakeState() {
lines = 5;
snakeLength = 5;
headPos = 0;
}
void RenderLed() {
uint8_t red = arr[0];
uint8_t green = arr[1];
uint8_t blue = arr[2];
uint8_t speed = arr[3];
if ((time - lastRendered) * 2 > (255 - (long)arr[3]) * 3) {
fill_solid(leds, NUM_LEDS, CRGB::Black);
for (int i = 0; i < snakeLength; i++) {
int pos = (headPos - i + NUM_LEDS) % NUM_LEDS;
leds[pos] = CRGB(red, green, blue);
}
headPos = (headPos + 1 + NUM_LEDS) % NUM_LEDS;
lastRendered = time;
}
FastLED.setBrightness(255);
FastLED.show();
}
void RenderText(int activeLine, bool isSelected) {
u8g2.firstPage();
u8g2.setFont(u8g2_font_t0_14_mr);
u8g2.setFontMode(1);
u8g2.setDrawColor(1);
do {
if (!isSelected) {
u8g2.drawStr(115, (activeLine + 1) * LINE_HEIGHT - 2, "<-");
} else {
u8g2.drawBox(0, activeLine * LINE_HEIGHT, 128, LINE_HEIGHT);
}
u8g2.setDrawColor(2);
u8g2.drawStr(5, LINE_HEIGHT - 2, "mode");
u8g2.drawStr(60, LINE_HEIGHT - 2, "snake");
char strTmp[5] = {0};
sprintf(strTmp, "%d", arr[0]);
u8g2.drawStr(5, 2 * LINE_HEIGHT - 2, "red");
u8g2.drawStr(60, 2 * LINE_HEIGHT - 2, strTmp);
sprintf(strTmp, "%d", arr[1]);
u8g2.drawStr(5, 3 * LINE_HEIGHT - 2, "green");
u8g2.drawStr(60, 3 * LINE_HEIGHT - 2, strTmp);
sprintf(strTmp, "%d", arr[2]);
u8g2.drawStr(5, 4 * LINE_HEIGHT - 2, "blue");
u8g2.drawStr(60, 4 * LINE_HEIGHT - 2, strTmp);
sprintf(strTmp, "%d", arr[3]);
u8g2.drawStr(5, 5 * LINE_HEIGHT - 2, "speed");
u8g2.drawStr(60, 5 * LINE_HEIGHT - 2, strTmp);
} while ( u8g2.nextPage() );
}
void KeyUp(int activeLine) {
if (activeLine == 0) return;
arr[activeLine - 1]++;
}
void KeyDown(int activeLine) {
if (activeLine == 0) return;
arr[activeLine - 1]--;
}
};
/* StateManager */
class StateManager {
private:
int statesNumber = 3;
State* states[3]{new StaticState(), new SnakeState(), new FireState()};
int currentState = 0;
int activeLine = 0;
bool isSelected = 0;
bool stateChanged = 1;
public:
void RenderText() {
if (stateChanged) {
states[currentState]->RenderText(activeLine, isSelected);
stateChanged = 0;
}
}
void RenderLed() {
states[currentState]->RenderLed();
}
void LineUp() {
activeLine++;
if (activeLine == states[currentState]->GetLinesCount()) {
activeLine = 0;
}
}
void LineDown() {
activeLine--;
if (activeLine == -1) {
activeLine = states[currentState]->GetLinesCount() - 1;
}
}
void button () {
isSelected = !isSelected;
stateChanged = 1;
}
void Up() {
if (isSelected) {
if (activeLine == 0) {
currentState++;
if (currentState == statesNumber) {
currentState = 0;
}
} else {
states[currentState]->KeyUp(activeLine);
}
} else {
LineDown();
}
stateChanged = true;
}
void Down() {
if (isSelected) {
if (activeLine == 0) {
currentState--;
if (currentState == -1) {
currentState = statesNumber - 1;
}
} else {
states[currentState]->KeyDown(activeLine);
}
} else {
LineUp();
}
stateChanged = true;
}
};
class Actions {
private:
int lastClk = HIGH;
bool lastBtn = 0;
public:
void handleAction(StateManager &stateManager) {
int cur = !digitalRead(ENCODER_BTN);
if (cur != lastBtn && cur == 1) {
stateManager.button();
Serial.println("button");
}
lastBtn = cur;
int newClk = digitalRead(ENCODER_CLK);
if (newClk != lastClk) {
lastClk = newClk;
int dtValue = digitalRead(ENCODER_DT);
if (newClk == LOW && dtValue == HIGH) {
Serial.println("Up");
stateManager.Up();
}
if (newClk == LOW && dtValue == LOW) {
Serial.println("Down");
stateManager.Down();
}
}
}
};
static Actions actions;
static StateManager stateManager;
void setup() {
pinMode(ENCODER_BTN, INPUT_PULLUP);
pinMode(ENCODER_CLK, INPUT);
pinMode(ENCODER_DT, INPUT);
Serial.begin(9600);
FastLED.addLeds<NEOPIXEL, 5>(leds, NUM_LEDS);
u8g2.begin(); // start the u8g2 library
stateManager.RenderText();
}
void loop() {
time = millis();
actions.handleAction(stateManager);
stateManager.RenderText();
stateManager.RenderLed();
}