// NeoPixel Displays
//
// hiz 07/2025
/*

NeoPixel Strip  -> 2

LCD Display
  LCD Display adress to 0x27 
  SDA -> 21
  SCL -> 22

RTC Modul
RT_RSTpin    16  // Chip Enable  RST
RT_DATpin    13  // Input/Output DAT
RT_CLKpin    15  // Clock
*/
#include <SPI.h>  				// SPI für die Kommunikation
#include <Adafruit_NeoPixel.h>  // NeoPixel Displays
#include "font8x8_basic.h"   	// Font
#include <LiquidCrystal_I2C.h>  // LC Display
#include <DS1302.h>   			// RTC clock

LiquidCrystal_I2C lcd(0x27, 16, 2);  //LC Display


// SETUP YOUR OUTPUT PIN AND NUMBER OF PIXELS
#define PIN1 2
#define NUM_PIXELS 768
#define vel 100  // Velocity in milliseconds
int hell = 100;  // Helligkeit 0...255
Adafruit_NeoPixel strip1 = Adafruit_NeoPixel(NUM_PIXELS, PIN1, NEO_RGB + NEO_KHZ800);

// Poti an Pin G35 -> ADC1 CH7
const int HELL = 35;
int funkt = -1;

// RTC Modul
const int RT_RSTpin = 16;  // Chip Enable  RST
const int RT_DATpin = 13;  // Input/Output DAT
const int RT_CLKpin = 15;  // Serial Clock CLK
DS1302 rtc(RT_RSTpin, RT_DATpin, RT_CLKpin);

// für HSV nach RGB
struct RGB {
  unsigned char cR;
  unsigned char cG;
  unsigned char cB;
};
struct HSV {
  double cH;
  double cS;
  double cV;
};

/*************************************************************/
// SetUp
/*************************************************************/
void setup() {
  int cnt;
  unsigned long color[3];
  int ccnt;

  Serial.begin(9600);
  delay(100);
  Serial.println("Lauflichttest mit NeoPixelDisplays (je 256 Pixel)");
  Serial.println("HIZ 7/2025");
 
 // LC Display 2 Zeilig
  lcd.begin();
  lcd.backlight();
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("NeoPixels");
  lcd.setCursor(0, 1);
  lcd.print("HIZ 7/2025");
  strip1.begin();
  strip1.clear();

  // Farbtest
  pinMode(HELL, INPUT);
  pinMode(FUNKTION, INPUT);
  hell = analogRead(HELL) / 16;
  color[0] = strip1.Color(0, hell, 0);
  color[1] = strip1.Color(hell, 0, 0);
  color[2] = strip1.Color(0, 0, hell);
  for (ccnt = 0; ccnt < 3; ccnt++) {
    for (cnt = 0; cnt < NUM_PIXELS / 3; cnt++) {
      strip1.setPixelColor(cnt, color[ccnt]);
      strip1.setPixelColor(cnt + 256, color[(ccnt + 1) % 3]);
      strip1.setPixelColor(cnt + 512, color[(ccnt + 2) % 3]);
      if ((cnt % 8) == 0) strip1.show();
    }
  }
  strip1.show();
  StripClear();
	
  delay(1000);
}

/*************************************************************/
// Programmschleife
/*************************************************************/
void loop() {

  if (digitalRead(FUNKTION) == 0) {
    while (digitalRead(FUNKTION) == 0)
      ;
    funkt = (funkt + 1) % 5;
    delay(100);
  }

  lcd.setCursor(0, 1);

  switch (funkt) {
    case 0:
      lcd.print("Farbspiel    ");
      Farbspiel();
      break;
    case 1:
      lcd.print("Waterfall    ");
      Waterfall();
      break;
    case 2:
      lcd.print("HIZ.InVideo  SW  ");
      StripClear();
      HIZAusblack();
      break;
    case 3:
      lcd.print("HIZ.InVideo    ");
      StripClear();
      HIZAuscolor();
      break;
    case 4:
      lcd.print("Uhrzeit    ");
      StripClear();
      ZeitAus();
      break;
  }
}

/*************************************************************/
// Farbspiele von links nach rechts
/*************************************************************/
void Farbspiel() {
  int cnt;
  unsigned int wert;
  int Winkel;
  unsigned long color;
  int schritt = 1;
  int pixels = NUM_PIXELS;

  Winkel = 0;
  while (digitalRead(FUNKTION) != 0) {
    for (cnt = 0; cnt < pixels; cnt++) {
      hell = analogRead(HELL) / 16;
      struct HSV cdata = { Winkel, 1.0, (float)hell / 1023 };
      struct RGB value = HSVToRGB(cdata);
      color = strip1.Color(value.cG, value.cR, value.cB);
      strip1.setPixelColor(cnt, color);
      Winkel += schritt;
      if (Winkel > 360) {
        Winkel = 360;
        schritt = -schritt;
      }
      if (Winkel < 0) {
        schritt = -schritt;
        Winkel = 0;
      }
      if ((cnt % 8) == 0) strip1.show();
    }
    strip1.show();
  }
}

/*************************************************************/
// Waterfall - von oben nach unten
/*************************************************************/
void Waterfall() {
  int cnt;
  int Winkel;
  unsigned long color;
  int schritt = 12;
  int pixels = NUM_PIXELS;
  int PixelNr;
  int Spalte;
  int Zeile;

  Winkel = 0;
  while (digitalRead(FUNKTION) != 0) {
    for (Zeile = 0; Zeile < 8; Zeile++) {
      hell = analogRead(HELL) / 16;
      struct HSV cdata = { Winkel, 1.0, (float)hell / 1023 };
      struct RGB value = HSVToRGB(cdata);
      color = strip1.Color(value.cG, value.cR, value.cB);
      for (Spalte = 0; Spalte < NUM_PIXELS / 8; Spalte++) {

        PixelNr = GetPixel(Spalte, Zeile);
        strip1.setPixelColor(PixelNr, color);
      }
      Winkel += schritt;
      if (Winkel > 360) {
        Winkel = 360;
        schritt = -schritt;
      }
      if (Winkel < 0) {
        schritt = -schritt;
        Winkel = 0;
      }
      strip1.show();
      delay(50);
    }
  }
}

/*************************************************************/
// Textausgabe HIZ.InVideo auf Schwarz
/*************************************************************/
void HIZAusblack() {
  int Winkel;
  unsigned long color;
  unsigned int backcolor = 0;
  int schritt = 1;
  int neocnt;  // Spalte auf dem Display
  struct HSV cdata;
  struct RGB value;

  Winkel = 0;
  while (digitalRead(FUNKTION) != 0) {
    // Farbe
    hell = analogRead(HELL) / 16;
    cdata = { Winkel, 1.0, (float)hell / 1023 };
    value = HSVToRGB(cdata);
    color = strip1.Color(value.cG, value.cR, value.cB);
    neocnt = 10;
    TextAus(neocnt, color, backcolor, "HIZ.InVideo");

    Winkel += schritt;
    if (Winkel > 360) {
      Winkel = 360;
      schritt = -schritt;
    }
    if (Winkel < 0) {
      schritt = -schritt;
      Winkel = 0;
    }
  }
}

/*************************************************************/
// Textausgabe HIZ.InVideo in Bunt
/*************************************************************/
void HIZAuscolor() {
  int Winkel;
  unsigned long color;
  unsigned int backcolor = 0;
  int schritt = 1;
  int neocnt;  // Spalte auf dem Display
  struct HSV cdata;
  struct RGB value;
  int lasthell = 0;

  Winkel = 0;
  while (digitalRead(FUNKTION) != 0) {
    // Farbe und ausgabe HIZ Weiss auf blau
    hell = analogRead(HELL) / 16;
   
   if (lasthell != hell) {           // nur wenn sich die hellikeit ändert
      lasthell=hell;                     
      color = strip1.Color(hell / 4, hell / 4, hell / 4);  // weiss
      cdata = { 240, 1.0, (float)hell / 1023 }; //blau
      value = HSVToRGB(cdata);
      backcolor = strip1.Color(value.cG, value.cR, value.cB);
      neocnt = 3;
      TextAus(neocnt, color, backcolor, " HIZ ");

      // Farbe und Ausgabe vom Punkt in Rot
      neocnt = 36;
      cdata = {0, 1.0, (float)hell / 1023 };
    value = HSVToRGB(cdata);
    color = strip1.Color(value.cG, value.cR, value.cB);
    backcolor = strip1.Color(0, 0, 0);     // Schwarz

      // Punkt zwischen HIZ und InVideo
      strip1.setPixelColor(GetPixel(neocnt, 7), color);
      strip1.setPixelColor(GetPixel(neocnt + 1, 7), color);
      strip1.setPixelColor(GetPixel(neocnt + 2, 7), color);
      strip1.setPixelColor(GetPixel(neocnt, 6), color);
      strip1.setPixelColor(GetPixel(neocnt + 1, 6), color);
      strip1.setPixelColor(GetPixel(neocnt + 2, 6), color);
      strip1.setPixelColor(GetPixel(neocnt, 5), color);
      strip1.setPixelColor(GetPixel(neocnt + 1, 5), color);
      strip1.setPixelColor(GetPixel(neocnt + 2, 5), color);
    }

    // Ausgabe InVideo in schillerden Farben auf schwarz
    neocnt = 41;
    backcolor = 0;
    cdata = { Winkel, 1.0, (float)hell / 1023 };
    value = HSVToRGB(cdata);
    color = strip1.Color(value.cG, value.cR, value.cB);
    TextAus(neocnt, color, backcolor, "InVideo");

    Winkel += schritt;
    if (Winkel > 360) {
      Winkel = 360;
      schritt = -schritt;
    }
    if (Winkel < 0) {
      schritt = -schritt;
      Winkel = 0;
    }
  }
}

/*************************************************************/
// Textausgabe UhrZeit
/*************************************************************/
void ZeitAus() {
  int neocnt = 0;
  int Winkel;
  unsigned long color;
  unsigned long backcolor;
  int schritt = 1;
  char buf[20];
  struct HSV cdata;
  struct RGB value;

  backcolor = 0;
  Winkel = 0;
  while (digitalRead(FUNKTION) != 0) {
    // Zeit aus RTC-Modul
    Time t = rtc.time();
    // Farbe
    hell = analogRead(HELL) / 16;
    cdata = { Winkel, 1.0, (float)hell / 1023 };
    value = HSVToRGB(cdata);
    color = strip1.Color(value.cG, value.cR, value.cB);
    // ausgeben Zeit in HH:MM:SS
    snprintf(buf, sizeof(buf), "%02d:%02d:%02d", t.hr, t.min, t.sec);
    TextAus(4, color, backcolor, (String)buf);

    // Datum in Komplentärfarbe
    cdata = { (Winkel + 180) % 360, 1.0, (float)hell / 1023 };
    value = HSVToRGB(cdata);
    color = strip1.Color(value.cG, value.cR, value.cB);
    snprintf(buf, sizeof(buf), "%02d", t.day);
    TextAus(64, color, backcolor, (String)buf);
    snprintf(buf, sizeof(buf), "%02d", t.mon);
    TextAus(64 + 32 - 13, color, backcolor, (String)buf);
    // Punkt zwischen Tag und Monat
    strip1.setPixelColor(GetPixel(64 + 15, 6), color);
    strip1.setPixelColor(GetPixel(64 + 16, 6), color);
    strip1.setPixelColor(GetPixel(64 + 15, 5), color);
    strip1.setPixelColor(GetPixel(64 + 16, 5), color);
    // neue Farbe
    Winkel += schritt;
    if (Winkel > 360) {
      Winkel = 360;
      schritt = -schritt;
    }
    if (Winkel < 0) {
      schritt = -schritt;
      Winkel = 0;
    }
  }
}

/*************************************************************/
// Textausgabe an Position mit Farbe und Hintergrundfarbe
/*************************************************************/
void TextAus(int neocnt, unsigned long color, unsigned long backcolor, String text) {

  char Buchstabe;
  int tcnt;
  int ascWert;
  char Bitwert;
  int Spalte;
  int Zeile;
  int aktpos;

  //Serial.println(text);

  for (tcnt = 0; tcnt < text.length(); tcnt++) {
    Buchstabe = text.charAt(tcnt);
    //Serial.print((String)Buchstabe + " ");
    // Asciiwert in der Tabelle  ausgeben Bitmuster
    for (Zeile = 0; Zeile < 8; Zeile++) {
      Bitwert = font8x8_basic[Buchstabe][Zeile];
      for (Spalte = 0; Spalte < 7; Spalte++) {
        aktpos = GetPixel(Spalte + neocnt, Zeile);
        if (((Bitwert >> (Spalte)) & 1) > 0) {
          strip1.setPixelColor(aktpos, color);
        } else {
          strip1.setPixelColor(aktpos, backcolor);  //strip1.Color(2, 2, 2));
        }
      }
    }
    if (Buchstabe == 0x30) neocnt++;
    neocnt += 7;
  }
  strip1.show();
}


/*************************************************************/
// Spalte und Zeile in Pixelnummer umrechnen
/*************************************************************/
int GetPixel(int Spalte, int Zeile) {
  int Pixel;
  Pixel = Spalte * 8;
  if ((Spalte % 2) == 0) {
    Pixel = Pixel + Zeile;
  } else {
    Pixel = Pixel + (7 - Zeile);
  }
  return (Pixel);
}

/*************************************************************/
// Strip zurücksetzen alles aus;
/*************************************************************/
void StripClear() {
  int cnt;
  for (cnt = 0; cnt < NUM_PIXELS; cnt++) {
    strip1.setPixelColor(cnt, 0);
  }
  strip1.show();
}

/*************************************************************/
//Farbwinkel 0..360. in RGB 0..255
/*************************************************************/
struct RGB HSVToRGB(struct HSV hsv) {
  double r = 0, g = 0, b = 0;

  if (hsv.cS == 0) {
    r = hsv.cV;
    g = hsv.cV;
    b = hsv.cV;
  } else {
    int i;
    double f, p, q, t;

    if (hsv.cH == 360)
      hsv.cH = 0;
    else
      hsv.cH = hsv.cH / 60;

    i = (int)trunc(hsv.cH);
    f = hsv.cH - i;

    p = hsv.cV * (1.0 - hsv.cS);
    q = hsv.cV * (1.0 - (hsv.cS * f));
    t = hsv.cV * (1.0 - (hsv.cS * (1.0 - f)));

    switch (i) {
      case 0:
        r = hsv.cV;
        g = t;
        b = p;
        break;

      case 1:
        r = q;
        g = hsv.cV;
        b = p;
        break;

      case 2:
        r = p;
        g = hsv.cV;
        b = t;
        break;

      case 3:
        r = p;
        g = q;
        b = hsv.cV;
        break;

      case 4:
        r = t;
        g = p;
        b = hsv.cV;
        break;

      default:
        r = hsv.cV;
        g = p;
        b = q;
        break;
    }
  }
  struct RGB rgb;
  rgb.cR = r * 255;
  rgb.cG = g * 255;
  rgb.cB = b * 255;
  return rgb;
}
