#include <RotaryEncoder.h>
#include <LiquidCrystal_I2C.h>
#include "PwmTimer1.hpp"
#include "Button_SL.hpp"
constexpr byte MAX_HERTZ{15};
constexpr byte MIN_HERTZ{1};
constexpr byte pin_in1{2};
constexpr byte pin_in2{3};
constexpr byte pin_btn{4};
constexpr byte pin_led{13};
#ifndef LCD20x4
constexpr byte I2C_ADDR{0x27};
constexpr byte LCD_COLUMNS{16};
constexpr byte LCD_LINES{2};
#else
constexpr byte I2C_ADDR{0x3F};
constexpr byte LCD_COLUMNS{20};
constexpr byte LCD_LINES{4};
#endif
enum class ProgState : byte { adjust = 0, start, running, idle };
enum class InputState : byte { hertz = 0, duty };
struct EncoderData {
byte pos;
byte newPos;
byte hertz;
byte dutyc;
};
using namespace Btn;
ButtonSL btn{pin_btn};
LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLUMNS, LCD_LINES);
// RotaryEncoder encoder(pin_in1, pin_in2, RotaryEncoder::LatchMode::FOUR0);
RotaryEncoder encoder(pin_in1, pin_in2, RotaryEncoder::LatchMode::FOUR3);
// RotaryEncoder encoder(pin_in1, pin_in2, RotaryEncoder::LatchMode::TWO03);
EncoderData encData{0, 0, MIN_HERTZ, 10};
bool askEncoder(EncoderData &, InputState);
void outputLcd(char*, EncoderData&, InputState);
void setup() {
Serial.begin(115200);
Serial.println("Start");
btn.begin();
btn.releaseOn();
btn.setDebounceTime_ms(150);
lcd.init();
lcd.backlight();
Pwm.begin(encData.hertz, encData.dutyc);
}
void loop() {
static bool lastBtnAction{false};
static char buffer[LCD_COLUMNS + 1]; // Achtung: buffer an Textlänge + 1 anpassen. Sonst gibt es komische Effekte.
static ProgState state{ProgState::adjust};
static InputState inState{InputState::hertz};
if (askEncoder(encData, inState)) {
state = ProgState::adjust;
}
switch (state) {
case ProgState::adjust:
Pwm.stopPwm();
buffer[0] = ' ';
outputLcd(buffer, encData, inState);
state = ProgState::idle;
break;
case ProgState::start:
buffer[0] = 'R';
lcd.setCursor(0, 1);
lcd.print(buffer[0]);
state = ProgState::running;
[[fallthrough]] // https://en.cppreference.com/w/cpp/language/attributes/fallthrough
case ProgState::running:
Pwm.setHz(encData.hertz);
Pwm.setDutyCycle(encData.dutyc);
Pwm.startPwm();
state = ProgState::idle;
break;
default: break;
}
// Tasterabfrage
switch (btn.tick()) {
case ButtonState::longPressed:
lastBtnAction = !lastBtnAction;
if (lastBtnAction) {
state = ProgState::start; // Will swtich PWM on
} else {
state = ProgState::adjust; // Will switch PWM off
}
break;
case ButtonState::shortPressed:
switch (inState) {
case InputState::duty: inState = InputState::hertz; break;
case InputState::hertz: inState = InputState::duty; break;
default: break;
}
outputLcd(buffer, encData, inState);
break;
default: break;
}
}
bool askEncoder(EncoderData &data, InputState inState) {
encoder.tick();
data.newPos = encoder.getPosition();
if (data.pos != data.newPos) {
data.pos = data.newPos;
int dir = static_cast<int>(encoder.getDirection());
switch (inState) {
case InputState::hertz:
if (dir > 0) {
data.hertz = (++data.hertz > MAX_HERTZ) ? MIN_HERTZ : data.hertz;
}
if (dir < 0) {
data.hertz = (--data.hertz < MIN_HERTZ) ? MAX_HERTZ : data.hertz;
}
break;
case InputState::duty:
if (dir > 0) {
data.dutyc = (++data.dutyc > 100) ? 0 : data.dutyc; // 100% or less
}
if (dir < 0) {
data.dutyc = (--data.dutyc > 100) ? 100 : data.dutyc; // Unsigned! -1 becomes 255
}
break;
default: break;
}
return true;
}
return false;
}
void outputLcd(char *text, EncoderData &ed, InputState is) {
bool isRunning = (text[0] == 'R') ? true : false;
sprintf(text, "%3d Hz Tv: %3d%%", ed.hertz, ed.dutyc);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(text);
memset(text, 32, LCD_COLUMNS);
text[LCD_COLUMNS + 1] = '\0';
switch (is) {
case InputState::hertz: text[2] = '^'; break;
case InputState::duty: text[14] = '^'; break;
default: break;
}
if (isRunning) {
text[0] = 'R';
}
lcd.setCursor(0, 1);
lcd.print(text);
}