#include <Adafruit_NeoPixel.h>
#include <LiquidCrystal_I2C.h> // https://github.com/johnrickman/LiquidCrystal_I2C
#include <Button_SL.hpp> // https://github.com/DoImant/Button_SL
#include <Streaming.h> // https://github.com/janelia-arduino/Streaming
Print &cout {Serial};
// If the search in an array was unsuccessful in terms of the search, -1 is returned.
constexpr int NO_VALID_INDEX {-1};
constexpr uint8_t ANALOG_INPUT_PINS[] = {A0, A1, A2, A3}; // Potentiometer-Eingänge
constexpr uint8_t NUM_ANALOG_VALUES {sizeof(ANALOG_INPUT_PINS) / sizeof(ANALOG_INPUT_PINS[0])};
// Namen der Potentiometer
const char *potiNames[NUM_ANALOG_VALUES] = {
"Sound", // Poti 1
"Discord", // Poti 2
"YouTube", // Poti 3
"Game" // Poti 4
};
// NeoPixel-Setup für 5 LEDs (5 LEDs, auch wenn es nur 4 Potis gibt)
Adafruit_NeoPixel strip(5, 6, NEO_GRB + NEO_KHZ800); // 5 LEDs an Pin 6
LiquidCrystal_I2C lcd(0x27, 16, 2); // I2C-Adresse 0x27, 16 Zeichen, 2 Zeilen
Btn::ButtonSL btn {2};
int analogSliderValues[NUM_ANALOG_VALUES];
template <size_t N> int hasChanged(int (&oldVal)[N], int (&newVal)[N]) {
int result {NO_VALID_INDEX};
for (size_t index = 0; index < N; ++index) {
if (oldVal[index] != newVal[index]) {
oldVal[index] = newVal[index];
if (result < 0) result = index;
}
}
return result;
}
template <size_t N> int nextNonZero(int (&data)[N], size_t start) {
// Search from actal index to end
if (start < N - 1) {
for (size_t index = start + 1; index < N; ++index) {
if (data[index]) { return index; }
}
}
// Search from begin to actal index
for (size_t index = 0; index < start; ++index) {
if (data[index]) { return index; }
}
return 0;
}
template <size_t N> void updateSliderValues(const uint8_t (&analogPins)[N], int (&data)[N]) {
for (size_t i = 0; i < N; i++) {
data[i] = analogRead(analogPins[i]); // Potentiometerwert lesen
}
}
template <size_t N> void setLeds(int (&data)[N]) {
// Farben der LEDs festlegen
int colors[5][3] = {
{255, 255, 255}, // LED 5: Weiß für Poti 1 (Sound)
{0, 0, 255}, // LED 4: Blau für Poti 2 (Discord)
{255, 0, 0 }, // LED 3: Rot für Poti 3 (YouTube)
{255, 255, 0 }, // LED 2: Gelb für Poti 4 (Game)
{0, 255, 0 } // LED 1: Grün (bleibt aktiv, aber ohne Poti)
};
// LEDs basierend auf Potentiometerwerten einstellen
for (size_t i = 0; i < N; i++) {
if (analogSliderValues[i] == 0) {
strip.setPixelColor(N - i, 0, 0, 0); // LED aus, wenn Potentiometer auf 0
} else {
strip.setPixelColor(N - i, colors[i][0], colors[i][1], colors[i][2]); // Farbe einstellen
}
}
// LED 5 (Grün) bleibt immer an, da sie keinen Poti hat
strip.setPixelColor(0, colors[N][0], colors[N][1], colors[N][2]);
strip.show(); // LEDs anzeigen
}
void displaySliderOnLCD(int analogValue, const char *const potiName) {
char buffer[17] {0};
// Zeigt den Wert des Potentiometers in der ersten Zeile
lcd.setCursor(0, 0); // Erste Zeile, erste Spalte
snprintf(buffer, sizeof(buffer), "%-10s %3ld", potiName, map(analogValue, 0, 1023, 0, 100));
lcd.print(buffer);
// Zweiter Schritt: Balkenanzeige in der zweiten Zeile
int barLength = map(analogValue, 0, 1023, 0, 16); // Länge für LCD (16 Zeichen)
lcd.setCursor(0, 1); // Zur zweiten Zeile wechseln
memset(buffer, ' ', sizeof(buffer)); // fill (complete) Buffer with spaces
memset(buffer, '|', barLength); // fill bar characters
lcd.print(buffer);
}
template <size_t N> void sendSliderValues(int (&data)[N]) {
size_t index {0};
for (const auto &val : data) {
// cout <<_WIDTH(val,4);
cout << val;
(++index < N) ? cout << '|' : cout << '\n';
}
}
void setup() {
// Potentiometer als Eingänge konfigurieren
for (auto &pin : ANALOG_INPUT_PINS) { pinMode(pin, INPUT); }
// Serielle Kommunikation initialisieren
Serial.begin(9600);
btn.begin();
// NeoPixel-Strip initialisieren
strip.begin();
strip.show();
strip.setBrightness(255); // Helligkeit der LEDs (0-255)
// Taster konfigurieren
pinMode(2, INPUT_PULLUP); // Taster an Pin 2 mit Pullup-Widerstand
// LCD initialisieren
lcd.init(); // LCD initialisieren
lcd.backlight(); // LCD-Hintergrundbeleuchtung einschalten
}
void loop() {
static int oldSliderValues[NUM_ANALOG_VALUES] {-1};
static int lastSliderIndex {0};
if (btn.tick() != Btn::ButtonState::notPressed) {
lastSliderIndex = (lastSliderIndex + 1) % NUM_ANALOG_VALUES;
displaySliderOnLCD(analogSliderValues[lastSliderIndex], potiNames[lastSliderIndex]);
}
updateSliderValues(ANALOG_INPUT_PINS, analogSliderValues);
auto result = hasChanged(oldSliderValues, analogSliderValues);
if (result != NO_VALID_INDEX) {
// if a value becomes 0 show the next Slider value > 0
lastSliderIndex = (!analogSliderValues[result]) ? nextNonZero(analogSliderValues, result) : result;
// Potentiometer-Werte aktualisieren und LEDs einstellen
setLeds(analogSliderValues);
// Den zuletzt angepassten Poti auf dem LCD anzeigen
displaySliderOnLCD(analogSliderValues[lastSliderIndex], potiNames[lastSliderIndex]);
// Potentiometerwerte über die serielle Schnittstelle senden
sendSliderValues(analogSliderValues); // Simuliert das Senden der Werte (deej nutzt serielle Daten)
}
}