#include <Arduino.h>
#include <U8x8lib.h>
#include <EncButton.h>
#include <FastLED.h>
#define NUM_LEDS 8
const uint8_t ledPin = 5;
const uint8_t s1Pin = 3;
const uint8_t s2Pin = 4;
const uint8_t btnPin = 2;
CRGB leds[NUM_LEDS];
//U8X8_SH1106_128X64_NONAME_HW_I2C
U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE);
EncButton enc(s1Pin, s2Pin, btnPin);
typedef enum {
value,
rainbow,
other,
} Mode;
const char *modeName[] = {
"value",
"rainbow",
"other",
};
typedef enum {
mode,
speed,
hue,
r,
g,
b,
none
} MainMenu;
const char *menuName[] = {
"mode",
"speed",
"hue",
"r",
"g",
"b",
"none",
};
typedef enum {
right,
left,
confirm
} Action;
typedef struct State {
Mode mode = Mode::value;
MainMenu menu = MainMenu::none;
MainMenu menuHover = MainMenu::mode;
uint8_t valueHue = 0;
uint8_t valueR = 0;
uint8_t valueG = 0;
uint8_t valueB = 0;
uint8_t rainbowSpeed = 0;
bool hasChanged = true;
} State;
State state;
uint8_t getMenuValue(MainMenu menu) {
switch (menu) {
case r:
return state.valueR;
case g:
return state.valueG;
case b:
return state.valueB;
case hue:
return state.valueHue;
case speed:
return state.rainbowSpeed;
}
return 0;
}
const MainMenu modeMenus[3][MainMenu::none - 1] = {
{MainMenu::mode, MainMenu::hue, MainMenu::r, MainMenu::g, MainMenu::b},
{MainMenu::mode, MainMenu::speed, MainMenu::none, MainMenu::none, MainMenu::none},
{MainMenu::mode, MainMenu::speed, MainMenu::none, MainMenu::none, MainMenu::none},
};
MainMenu maxMenu() {
auto menus = modeMenus[state.menu];
for (int i = 0; i <= MainMenu::b; ++i) {
MainMenu mode = menus[i];
if (mode == none) {
return menus[i - 1];
}
}
return MainMenu::b;
}
char strTmp[16] = {0};
void display() {
sprintf(strTmp, "%s%s ", state.menuHover == mode ? "-" : " ", menuName[mode]);
u8x8.drawString(0, 0, strTmp);
sprintf(strTmp, " %8s", modeName[state.mode]);
if (state.menu == MainMenu::mode) u8x8.inverse();
u8x8.drawString(6, 0, strTmp);
if (state.menu == MainMenu::mode) u8x8.noInverse();
auto menus = modeMenus[state.mode];
for (int i = 1; i < MainMenu::b; ++i) {
auto m = menus[i];
const int line = (i + 1) * 1;
if (m != MainMenu::none) {
auto hovered = state.menuHover == m;
sprintf(strTmp, "%s%-5s", hovered ? "-" : " ", menuName[m]);
u8x8.drawString(0, line, strTmp);
if (state.menu == m) u8x8.inverse();
sprintf(strTmp, "%3d", getMenuValue(m));
u8x8.drawString(6, line, strTmp);
if (state.menu == m) u8x8.noInverse();
} else {
sprintf(strTmp, "%10s", " ");
u8x8.drawString(0, line, strTmp);
}
}
}
uint8_t next(uint8_t i, uint8_t max) {
return i == max ? 0 : i + 1;
}
uint8_t prev(uint8_t i, uint8_t max) {
return i == 0 ? max : i - 1;
}
void hoverNext() {
Serial.println(state.menuHover);
state.menuHover = MainMenu(next(state.menuHover, maxMenu()));
}
void actualizeHue() { state.valueHue = rgb2hsv_approximate(CRGB(state.valueR, state.valueG, state.valueB)).hue; }
void actualizeRGB() {
static CRGB tmp;
hsv2rgb_rainbow(CHSV(state.valueHue, 1, 1), tmp);
state.valueR = tmp.r;
state.valueG = tmp.g;
state.valueB = tmp.b;
}
void hoverPrev() { state.menuHover = MainMenu(prev(state.menuHover, maxMenu())); }
void modeNext() { state.mode = Mode(next(state.mode, Mode::other)); }
void modePrev() { state.mode = Mode(prev(state.mode, Mode::other)); }
void hueNext() {
state.valueHue = next(state.valueHue, 255);
actualizeRGB();
}
void huePrev() {
state.valueHue = prev(state.valueHue, 255);
actualizeRGB();
}
void rNext() {
state.valueR = next(state.valueR, 255);
actualizeHue();
}
void rPrev() {
state.valueR = prev(state.valueR, 255);
actualizeHue();
}
void gNext() {
state.valueG = next(state.valueG, 255);
actualizeHue();
}
void gPrev() {
state.valueG = prev(state.valueG, 255);
actualizeHue();
}
void bNext() {
state.valueB = next(state.valueB, 255);
actualizeHue();
}
void bPrev() {
state.valueB = prev(state.valueB, 255);
actualizeHue();
}
void handleAction(Action action) {
if (action == Action::right) {
if (state.menu == none) {
hoverNext();
} else if (state.menu == MainMenu::mode) {
modeNext();
} else if (state.menu == MainMenu::r) {
rNext();
} else if (state.menu == MainMenu::g) {
gNext();
} else if (state.menu == MainMenu::b) {
bNext();
} else if (state.menu == MainMenu::hue) {
hueNext();
}
state.hasChanged = true;
} else if (action == Action::left) {
if (state.menu == none) {
hoverPrev();
} else if (state.menu == MainMenu::mode) {
modePrev();
} else if (state.menu == MainMenu::r) {
rPrev();
} else if (state.menu == MainMenu::g) {
gPrev();
} else if (state.menu == MainMenu::b) {
bPrev();
} else if (state.menu == MainMenu::hue) {
huePrev();
}
state.hasChanged = true;
} else if (action == Action::confirm) {
if (state.menu == MainMenu::none) {
state.menu = state.menuHover;
} else {
state.menu = MainMenu::none;
}
state.hasChanged = true;
}
}
void led() {
if (state.mode == value) {
FastLED.showColor(CRGB(state.valueR, state.valueG, state.valueB));
}
}
void setup(void) {
Serial.begin(9600);
u8x8.begin();
FastLED.addLeds<WS2812, ledPin, GRB>(leds, NUM_LEDS);
u8x8.setFont(u8x8_font_chroma48medium8_r);
}
void loop(void) {
enc.tick();
if (enc.right()) handleAction(Action::right);
if (enc.left()) handleAction(Action::left);
if (enc.click()) handleAction(Action::confirm);
if (state.hasChanged) {
display();
led();
state.hasChanged = false;
}
}