#include <SPI.h>
#include <U8x8lib.h>
#define CS A5
#define DC A4
// U8X8_SSD1306_128X64_NONAME_4W_HW_SPI u8x8(/* clock=*/ 13, /* data=*/ 11, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);
U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE);
#include <FastLED.h>
#define NUM_LEDS 16
CRGB leds[NUM_LEDS];
// отключить поддержку pressFor/holdFor/stepFor и счётчик степов (экономит 2 байта оперативки)
#define EB_NO_FOR
// отключить обработчик событий attach (экономит 2 байта оперативки)
#define EB_NO_CALLBACK
// отключить счётчик энкодера [VirtEncoder, Encoder, EncButton] (экономит 4 байта оперативки)
#define EB_NO_COUNTER
// отключить буферизацию энкодера (экономит 2 байта оперативки)
#define EB_NO_BUFFER
/*
Настройка таймаутов для всех классов
- Заменяет таймауты константами, изменить их из программы (SetXxxTimeout()) будет нельзя
- Настройка влияет на все объявленные в программе кнопки/энкодеры
- Экономит 1 байт оперативки на объект за каждый таймаут
- Показаны значения по умолчанию в мс
- Значения не ограничены 4000мс, как при установке из программы (SetXxxTimeout())
*/
#define EB_DEB_TIME 50 // таймаут гашения дребезга кнопки (кнопка)
#define EB_CLICK_TIME 500 // таймаут ожидания кликов (кнопка)
#define EB_HOLD_TIME 600 // таймаут удержания (кнопка)
#define EB_STEP_TIME 200 // таймаут импульсного удержания (кнопка)
#define EB_FAST_TIME 30 // таймаут быстрого поворота (энкодер)
#include <EncButton.h>
EncButton eb(3, 2, 4);
typedef enum a {
STILL,
RAINBOW,
RUNNING
} Mode;
typedef struct still_mode {
int brightness;
int rgb[3];
} Still_mode;
typedef struct rainbow {
int speed;
} Rainbow_mode;
typedef struct run_mode {
int speed;
} Running_mode;
typedef struct State {
Mode mode;
Still_mode still_mode;
Running_mode run_mode;
Rainbow_mode rainbow;
int chosen_str = 0;
bool is_setting_value = false;
bool hasChanged = true;
} State;
State state;
void handle_press() {
state.is_setting_value = !state.is_setting_value;
}
void handle_rotate() {
int step = eb.right() ? 1 : -1;
switch (state.mode) {
case STILL: {
if (state.is_setting_value) {
if (state.chosen_str == 0) {
state.mode = eb.right() ? RAINBOW : RUNNING;
} else {
state.still_mode.rgb[state.chosen_str - 1] = (state.still_mode.rgb[state.chosen_str - 1] + 255 + step) % 255;
}
} else {
state.chosen_str = (state.chosen_str + step + 4) % 4;
}
break;
}
case RAINBOW: {
if (state.is_setting_value) {
if (state.chosen_str == 0) {
state.mode = eb.right() ? RUNNING : STILL;
} else {
state.rainbow.speed = (state.rainbow.speed + 10 + step) % 10;
}
} else {
state.chosen_str = (state.chosen_str + step + 2) % 2;
}
break;
}
case RUNNING: {
if (state.is_setting_value) {
if (state.chosen_str == 0) {
state.mode = eb.right() ? STILL : RAINBOW;
} else {
state.run_mode.speed = (state.run_mode.speed + 10 + step) % 10;
}
} else {
state.chosen_str = (state.chosen_str + step + 2) % 2;
}
break;
}
}
}
void handle_action() {
switch (eb.action()) {
case EB_PRESS: {
handle_press();
Serial.println("pressed");
break;
}
case EB_TURN: {
handle_rotate();
Serial.println("rotated");
break;
}
}
// где-то внутри функции мы будем знать, что изменили состояние
// либо же, будем знать что любое событие изменяет состояние
state.hasChanged = true;
}
char strTmp[16] = {0};
void display() {
u8x8.clear();
// в простейшем случае, функция каждый раз перерисовывает содержание дисплея исходя из состояния.
// конечно, это можно оптимизировать, храня копию предыдущего состояния и перерисовывать только измененные элементы UI
switch (state.mode) {
case STILL: {display_still(); break;}
case RAINBOW: {display_rainbow(); break;}
case RUNNING: {display_running(); break;}
}
// u8x8.drawString(0,0, strTmp);
// u8x8.drawString(0,0,"Hello World!");
// u8x8.drawString(0,2,"Hello Wold");
// delay(100);
// Сильно упрощает жизнь sprintf
// sprintf(strTmp, "%s%s ", что-то == как-то ? "-" : " ", имена[state.поле]);
}
void display_still() {
sprintf(strTmp, "%c mode still", state.chosen_str == 0 ? '-' : ' ');
u8x8.drawString(0,0, strTmp);
sprintf(strTmp, "%c r %d", state.chosen_str == 1 ? '-' : ' ', state.still_mode.rgb[0]);
u8x8.drawString(0,2, strTmp);
sprintf(strTmp, "%c g %d", state.chosen_str == 2 ? '-' : ' ', state.still_mode.rgb[1]);
u8x8.drawString(0,4, strTmp);
sprintf(strTmp, "%c b %d", state.chosen_str == 3 ? '-' : ' ', state.still_mode.rgb[2]);
u8x8.drawString(0,6, strTmp);
}
void display_rainbow() {
sprintf(strTmp, "%c mode rainbow", state.chosen_str == 0 ? '-' : ' ');
u8x8.drawString(0,0, strTmp);
sprintf(strTmp, "%c speed %d", state.chosen_str == 1 ? '-' : ' ', state.rainbow.speed);
u8x8.drawString(0,1, strTmp);
}
void display_running() {
sprintf(strTmp, "%c mode running", state.chosen_str == 0 ? '-' : ' ');
u8x8.drawString(0,0, strTmp);
sprintf(strTmp, "%c speed %d", state.chosen_str == 1 ? '-' : ' ', state.run_mode.speed);
u8x8.drawString(0,1, strTmp);
}
void setup() {
state.mode = RAINBOW;
Serial.begin(9600);
//leds
FastLED.addLeds<NEOPIXEL, 6>(leds, NUM_LEDS);
//encoder
// eb.counter = 0;
//display
pinMode(CS, OUTPUT);
pinMode(DC, OUTPUT);
u8x8.begin();
u8x8.setFont(u8x8_font_chroma48medium8_r);
}
void loop(void) {
// leds[0] = CRGB::White; FastLED.show(); delay(30);
// leds[0] = CRGB::Black; FastLED.show(); delay(30);
eb.tick();
if(eb.action()) {
handle_action();
}
// display();
// не стоит каждый раз перерисовывать дисплей.
// Только если состояние изменилось
if (state.hasChanged) {
display();
state.hasChanged = false;
Serial.println("state changed");
}
// как часто стоит обновлять ленту уже зависит от режима
// led();
}