#include <digitalWriteFast.h>
#include <RotaryEncoder.h>
#include <Button_SL.hpp>
//
// Gobal constants
//
constexpr byte PIN_IN1 {2};
constexpr byte PIN_IN2 {3};
constexpr byte PIN_BTN {13};
constexpr int16_t VALUE_MIN {1}; // Counter minimum value
constexpr int16_t VALUE_MAX {1000}; // Counter maximum value
constexpr uint8_t MAX_DIGITS {4};
constexpr uint8_t MAX_SEGMENTS {8};
constexpr uint8_t DEC_PLACE_PIN[MAX_DIGITS] {A0, A1, A2, A3};
constexpr uint8_t DIGIT_PIN[MAX_SEGMENTS] {4, 5, 6, 7, 8, 9, 10, 11};
constexpr uint8_t SEGMENT_BITS[10] = {
// afbgcDde character
0b11101011, // 0
0b00101000, // 1
0b10110011, // 2
0b10111010, // 3
0b01111000, // 4
0b11011010, // 5
0b11011011, // 6
0b10101000, // 7
0b11111011, // 8
0b11111010, // 9
};
//
// Data definitions
//
//
// Extend the RotaryEncoder class with a lower and upper limit for a counter
//
class RotaryEncoderEnhanced : public RotaryEncoder {
public:
RotaryEncoderEnhanced(int pin1, int pin2, int min, int max, LatchMode mode = LatchMode::FOUR0)
: RotaryEncoder {pin1, pin2, mode}, valMin {min}, valMax {max} {}
int getMin() const { return valMin; }
int getMax() const { return valMax; }
private:
const int valMin; // Counter minimum value
const int valMax; // Counter maximum Value
};
//
// Global objects / variables
//
RotaryEncoderEnhanced encoder {PIN_IN1, PIN_IN2, VALUE_MIN, VALUE_MAX, RotaryEncoder::LatchMode::FOUR3};
Btn::ButtonSL btn {PIN_BTN};
//
// Function(s).
//
//////////////////////////////////////////////////////////////////////////////
/// @brief Query encoder
///
/// @param enc Ecoder object
/// @param value Value that changes with each encoder rotation
/// @param minValue Minimum value
/// @param maxValue Maximum value
/// @return true Value has changed
/// @return false no change
//////////////////////////////////////////////////////////////////////////////
bool queryEncoder(RotaryEncoderEnhanced &enc, int &value) {
byte flag {true};
enc.tick();
switch (enc.getDirection()) {
case RotaryEncoder::Direction::NOROTATION: flag = false; break;
case RotaryEncoder::Direction::CLOCKWISE: value = value < enc.getMax() ? value + 1 : enc.getMin(); break;
case RotaryEncoder::Direction::COUNTERCLOCKWISE: value = value > enc.getMin() ? value - 1 : enc.getMax(); break;
}
return flag;
}
void digitsOff() {
// Clear Digits
for (auto dPP : DEC_PLACE_PIN) { digitalWriteFast(dPP, HIGH); }
}
void digit(uint8_t nbr, uint8_t dpPinIdx) {
for (uint8_t i = 0; i < MAX_SEGMENTS; ++i) { digitalWriteFast(DIGIT_PIN[i], ((SEGMENT_BITS[nbr] >> i) & 0x01)); }
digitalWriteFast(DEC_PLACE_PIN[dpPinIdx], LOW);
}
void setDot(uint8_t pos) {
if (pos > 0 && pos < MAX_DIGITS) {
for (uint8_t i = 0; i < MAX_SEGMENTS; ++i) { digitalWriteFast(DIGIT_PIN[i], ((0b00000100 >> i) & 0x01)); }
digitalWriteFast(DEC_PLACE_PIN[pos], LOW);
}
}
void numberToDisplay(int16_t value, uint8_t dgt[], uint8_t dot) {
static uint8_t activeDigit {0};
switch (activeDigit) {
case 0: digit(dgt[0], 0); break;
case 1: if (value > 9) { digit(dgt[1], 1); } break;
case 2: if (value > 99) { digit(dgt[2], 2); } break;
case 3: if (value > 999) { digit(dgt[3], 3); } break;
case 4: setDot(dot); break;
}
if (++activeDigit == (MAX_DIGITS + 1)) { activeDigit = 0; }
}
void breakNumberDown(uint8_t dgt[], uint16_t nbr) {
dgt[3] = nbr / 1000;
nbr %= 1000;
dgt[2] = nbr / 100;
nbr %= 100;
dgt[1] = nbr / 10;
nbr %= 10;
dgt[0] = nbr;
}
//
// Hauptprogramm
//
void setup() {
for (auto dP : DIGIT_PIN) { pinMode(dP, OUTPUT); }
for (auto dPP : DEC_PLACE_PIN) { pinMode(dPP, OUTPUT); }
btn.begin();
}
void loop() {
static int16_t lastNumber {0};
static int16_t number {VALUE_MIN};
static uint8_t digits[MAX_DIGITS];
static uint16_t dotPos;
queryEncoder(encoder, number); // Check if encoder was turned
dotPos = (number == 1000) ? 3 : 0; // Show a dot at position 3 if number becomes 1000
if (btn.tick() != Btn::ButtonState::notPressed) {
number = VALUE_MIN;
}
if (number != lastNumber) {
breakNumberDown(digits, number);
lastNumber = number;
}
digitsOff(); // Number changed, so clear display for the new number
numberToDisplay(number, digits, dotPos);
}
Push Button (middle) to reset number.
Turn encoder (arrows) to
change the value