/*
 * Copyright 2016 Mario Richter
 * This file is part of NeMo.
 *
 * NeMo is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * NeMo is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with NeMo.  If not, see <http://www.gnu.org/licenses/>.
 */

/*!
 * \file light.c
 * \ingroup light
 * \brief Routinen zur Steuerung der RGB-Stripes
 */

/*!
 * \defgroup light Routinen zur Lichtsteuerung
 */

#include "main.h"

uint8_t  lightMode = LIGHT_MODE_OFF;        //!< Aktueller Betriebsmodus
uint8_t  lightProgAct = 0;                  //!< Aktuell aktives Lichtprogramm (1-16, 0=kein Programm)
uint8_t  lightCmdStep;                      //!< Nummer des aktuellen Befehls

static uint8_t  tastStopCnt;                //!< Entprellzähler für Taster Stop
static uint8_t  tastPlayCnt;                //!< Entprellzähler für Taster Play
static uint8_t  tastNextCnt;                //!< Entprellzähler für Taster Next

lightCmdStruct lightCmdAct;                 //!< Aktuell auszuführender Befehl

uint8_t     lightPrograms[16][64];          //!< Namen der 16 Lichtprogramme
char        lightLocation[32];              //!< Einsatzort der NeLiS
char EEMEM  eepLightLocation[32];           //!< Einsatzort der NeLiS (EEPROM)

uint16_t  lightValRed = 0;                  //!< Aktueller PWM-Wert rot
uint16_t  lightValGrn = 0;                  //!< Aktueller PWM-Wert grün
uint16_t  lightValBlu = 0;                  //!< Aktueller PWM-Wert blau

#if KANAL_WEISS == 1
uint16_t  lightValWht = 0;                  //!< Aktueller PWM-Wert weiß
#endif

uint8_t   lightAutoStart;                   //!< Autostart für Lichtprogramme:
                                            //   Bits 8..4: Autostart (0x60=aus, 0x90=ein)
                                            //   Bits 3..0: Letztes Lichtprogramm
uint8_t   EEMEM eepLightAutoStart;          //!< Autostart für Lichtprogramme (EEPROM)


//! Tabelle für Helligkeitswerte - entnommen vom Projekt Sofabeleuchtung von www.zabex.de
static const uint16_t RGBNormierung[256] =
{
    0,1,2,4,7,12,18,24,33,42,53,65,79,94,111,129,
    149,170,193,217,243,270,299,330,362,396,432,469,508,549,592,636,
    682,730,779,830,883,938,995,1053,1114,1176,1240,1306,1373,1443,1514,1588,
    1663,1740,1819,1900,1983,2068,2155,2244,2334,2427,2522,2618,2717,2818,2920,3025,
    3131,3240,3351,3463,3578,3695,3814,3935,4058,4183,4310,4439,4570,4703,4839,4976,
    5116,5258,5401,5547,5695,5846,5998,6153,6309,6468,6629,6792,6957,7125,7294,7466,
    7640,7816,7995,8175,8358,8543,8730,8920,9111,9305,9501,9699,9900,10103,10308,10515,
    10725,10936,11150,11367,11585,11806,12029,12255,12482,12712,12945,13179,13416,13655,13897,14141,
    14387,14635,14886,15139,15394,15652,15912,16174,16439,16706,16976,17248,17522,17798,18077,18358,
    18642,18928,19216,19507,19800,20096,20394,20694,20997,21302,21609,21919,22231,22546,22863,23183,
    23505,23829,24156,24485,24817,25151,25488,25827,26168,26512,26858,27207,27558,27912,28268,28627,
    28988,29352,29718,30086,30457,30831,31207,31585,31966,32350,32735,33124,33515,33908,34304,34703,
    35104,35507,35913,36322,36733,37146,37562,37981,38402,38826,39252,39681,40112,40546,40982,41421,
    41863,42307,42753,43203,43654,44109,44566,45025,45487,45952,46419,46888,47361,47836,48313,48793,
    49276,49761,50249,50739,51232,51728,52226,52727,53231,53737,54245,54757,55271,55787,56306,56828,
    57353,57880,58409,58942,59477,60014,60554,61097,61643,62191,62742,63295,63851,64410,64971,65535
};


/* %%%%%%%%%%%%%%%%%%%% Ansteuerung der RGB-Stripes %%%%%%%%%%%%%%%%%%%% */

/*!
 * \ingroup light
 * \brief Initialisierung der Ausgänge für die LED-Stripes
 *
 * Diese Funktion initialisiert die Timer und die Portausgänge zur Ansteuerung
 * der RGB-LED-Stripes.
 */

void lightInit(void)
{
  uint8_t i;

  // Ausgänge zu den RGB-Treibern freigeben
  DDRB |= (1 << PB7) | (1 << PB6) | (1 << PB5);

  // Timer 1: 16 bit Fast PWM, OCR1x setzen bei BOTTOM, löschen bei compare match
  // kein Prescaler -> 16MHz / 65536 = 244Hz PWM-Frequenz
  TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << COM1C1) | (1 << WGM11);
  TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10);

  // ICR1 gibt den TOP Wert an: auf 65535 setzen für max. Auflösung
  ICR1 = 0xFFFF;

  OCR1A = 0;
  OCR1B = 0;
  OCR1C = 0;

#if KANAL_WEISS == 1
  // Ausgänge zum W-Treiber freigeben
    DDRE |= (1 << PE3);

    // Timer 3: 16 bit Fast PWM, OCR1x setzen bei BOTTOM, löschen bei compare match
    // kein Prescaler -> 16MHz / 65536 = 244Hz PWM-Frequenz
    TCCR3A = (1 << COM3A1) | (1 << COM3B1) | (1 << COM3C1) | (1 << WGM31);
    TCCR3B = (1 << WGM33) | (1 << WGM32) | (1 << CS30);

    // ICR1 gibt den TOP Wert an: auf 65535 setzen für max. Auflösung
    ICR3 = 0xFFFF;

    OCR3A = 0;
#endif

  // Namen der Lichtprogramme aus dem EEPROM holen
  for(i = 0; i < 16; i++)
  {
    if(i2cEepromReadSequential((uint16_t)i << 11, lightPrograms[i], 64) == 1)
    {
      lightMode = LIGHT_MODE_ERROR;
    }
  }

  // ----------------------------------
  // Autostart-Funktion
  lightAutoStart = eeprom_read_byte(&eepLightAutoStart);

  // Prüfe, ob gültiger Wert im oberen Nibble vorhanden ist (0x90=ein oder 0x60=aus)
  if((lightAutoStart & 0xF0) == EEP_AUTOSTART_ON)
  {
    lightProgStart((lightAutoStart & 0x0F) + 1);          // Autostart: Programm aktivieren
  }
  else if((lightAutoStart & 0xF0) != EEP_AUTOSTART_OFF)   // Wenn nicht ON, dann muss es OFF sein - ansonsten Wert neu schreiben.
  {
    // Kein gültiger Wert im EEPROM -> Standard-Wert schreiben
    lightAutoStart = EEP_AUTOSTART_OFF;
    eeprom_write_byte(&eepLightAutoStart, lightAutoStart);
  }

  // ----------------------------------
  // Einsatzort

  // String aus EEPROM holen
  eeprom_read_block(lightLocation, eepLightLocation, 32);

  // Prüfe, ob ein gültiger Name hinterlegt ist
  if(!IS_CHARACTER(lightLocation[0]))
  {
    // Falls nicht, Standardnamen verwenden und im EEPROM initialisieren
    sprintf(lightLocation, "Einsatzort");
    eeprom_write_block(lightLocation, eepLightLocation, 32);
  }
}


/*!
 * \ingroup light
 * \brief Scheduler für die Lichtsteuerung
 *
 * Diese Funktion ist der Scheduler der Lichtsteuerung. Sie wird zyklisch aufgerufen,
 * berechnet die im Zyklus neu auszugebenden RGB-PWM-Werte und schreibt diese in die
 * PWM-Register.
 */

void lightScheduler(void)
{
  uint16_t chars, maxChars;
  uint16_t i;
  uint8_t  j;
  int16_t  h;

  char     buf[8];

  switch(lightMode)
  {
    // --------------------------------------
    // LEDs aus
    case LIGHT_MODE_OFF:
    case LIGHT_MODE_ERROR:

      lightValRed = 0;
      lightValGrn = 0;
      lightValBlu = 0;
#if KANAL_WEISS == 1
      lightValWht = 0;
#endif
      break;

    // --------------------------------------
    // Pause
    case LIGHT_MODE_PAUSE:

      break;

    // --------------------------------------
    // Lichtprogramm aktiv
    case LIGHT_MODE_ACTIVE:

      switch(lightCmdAct.type)
      {
        case 'S':

          lightValRed = lightCmdAct.red;      // Neue Werte setzen
          lightValGrn = lightCmdAct.grn;
          lightValBlu = lightCmdAct.blu;
#if KANAL_WEISS == 1
          lightValWht = lightCmdAct.wht;
#endif

          lightCmdStep++;
          if(lightGetCommand(((uint16_t)(lightProgAct - 1) << 11) + 0x40 + (lightCmdStep * 0x08), &lightCmdAct) != 0)
            lightMode = LIGHT_MODE_ERROR;
          break;

        case 'P':

          if(lightCmdAct.delay > 0)           // Delay herunterzählen
            lightCmdAct.delay--;
          else
          {
            lightCmdStep++;
            if(lightGetCommand(((uint16_t)(lightProgAct - 1) << 11) + 0x40 + (lightCmdStep * 0x08), &lightCmdAct) != 0)
              lightMode = LIGHT_MODE_ERROR;
          }
          break;

        case 'F':

          if(lightCmdAct.delay > 0)
          {
            // Neuer Lichtwert = Alter Lichtwert - ((Alter Lichtwert - Zielwert) / Restanzahl Schritte)
            h = ((int32_t)lightValRed - (int32_t)lightCmdAct.red) / lightCmdAct.delay;
            lightValRed = (uint16_t)((int32_t)lightValRed - h);

            h = ((int32_t)lightValGrn - (int32_t)lightCmdAct.grn) / lightCmdAct.delay;
            lightValGrn = (uint16_t)((int32_t)lightValGrn - h);

            h = ((int32_t)lightValBlu - (int32_t)lightCmdAct.blu) / lightCmdAct.delay;
            lightValBlu = (uint16_t)((int32_t)lightValBlu - h);

#if KANAL_WEISS == 1
            h = ((int32_t)lightValWht - (int32_t)lightCmdAct.wht) / lightCmdAct.delay;
            lightValWht = (uint16_t)((int32_t)lightValWht - h);
#endif

            lightCmdAct.delay--;
          }
          else
          {
            lightCmdStep++;
            if(lightGetCommand(((uint16_t)(lightProgAct - 1) << 11) + 0x40 + (lightCmdStep * 0x08), &lightCmdAct) != 0)
              lightMode = LIGHT_MODE_ERROR;
          }
          break;

        case 'X':
        default:

          // Programmende, Neustart am Anfang -> wieder ersten Befehl holen
          lightCmdStep = 0;
          if(lightGetCommand(((uint16_t)(lightProgAct - 1) << 11) + 0x40, &lightCmdAct) != 0)
            lightMode = LIGHT_MODE_ERROR;
          break;
      }
  }

  lightSetPWMRed((lightValRed & 0xFF00) >> 8);
  lightSetPWMGreen((lightValGrn & 0xFF00) >> 8);
  lightSetPWMBlue((lightValBlu & 0xFF00) >> 8);

#if KANAL_WEISS == 1
  lightSetPWMWhite((lightValWht & 0xFF00) >> 8);
#endif

  // *********************************************************
  // KOMMUNIKATION
  // *********************************************************

  // Ausgabe des akt. Lichtprogramms auf die Schnittstelle
  if(subMenu == SM_GETLIGHTPROG)
  {
    // Ist der jeweilige Buffer aufnahmebereit?
    if( ((connIndex == 255) && (send_buf.read == send_buf.writ)) ||
        ((connIndex < TCP_MAX_CONNECTIONS) && (tcpConnList[connIndex].rxread == tcpConnList[connIndex].rxwrit)))
    {
      // Ein Eintrag kann max. 29 Zeichen haben:
      // 1                10                  20                  29
      // 0 0 1 > F ; 6 5 0 0 0 ; 1 2 3 ; 2 3 4 ; 1 2 3 ; 2 3 4 \r \n
      // Wir geben sicherheitshalber noch 2 Extra-Zeichen vor. Es müssen also vor
      // dem Eintrag noch mind. 31 Zeichen im Buffer frei sein.

      maxChars = (connIndex == 255) ? (BUFLEN - 31) : (TCP_RXTX_BUFLEN - 31);
      chars = 0;

      while(chars < maxChars)
      {
        // Befehlsnummer ausgeben
        chars += sprintf(buf, "%03u>", lightCmdInd + 1);
        sendString(connIndex, buf);

        switch(lightBuf[lightBufInd])
        {
          case 'F':         // Farbwechsel

            sendStringP(connIndex, PSTR("F;"));

            // Farbwechselzeit
            chars += sprintf(buf, "%u;", ((uint16_t)(lightBuf[lightBufInd + 2]) << 8) + (uint16_t)(lightBuf[lightBufInd + 3]));
            sendString(connIndex, buf);

            // Zielwert Rot
            chars += sprintf(buf, "%u;", lightBuf[lightBufInd + 4]);
            sendString(connIndex, buf);

            // Zielwert Grün
            chars += sprintf(buf, "%u;", lightBuf[lightBufInd + 5]);
            sendString(connIndex, buf);

            // Zielwert Blau
            chars += sprintf(buf, "%u", lightBuf[lightBufInd + 6]);
            sendString(connIndex, buf);

#if KANAL_WEISS == 1
            // Zielwert Weiss
            sprintf(buf, ";%u", lightBuf[lightBufInd + 7]);
            sendString(connIndex, buf);
#endif
            chars += 2;
            sendLine(connIndex, "");
            break;

          case 'S':         // Farbe setzen

            sendStringP(connIndex, PSTR("S;"));;

            // Zielwert Rot
            chars += sprintf(buf, "%u;", lightBuf[lightBufInd + 4]);
            sendString(connIndex, buf);

            // Zielwert Grün
            chars += sprintf(buf, "%u;", lightBuf[lightBufInd + 5]);
            sendString(connIndex, buf);

            // Zielwert Blau
            chars += sprintf(buf, "%u", lightBuf[lightBufInd + 6]);
            sendString(connIndex, buf);

#if KANAL_WEISS == 1
            // Zielwert Weiss
            sprintf(buf, ";%u", lightBuf[lightBufInd + 7]);
            sendString(connIndex, buf);
#endif
            chars += 2;
            sendLine(connIndex, "");
            break;

          case 'P':         // Pause

            sendStringP(connIndex, PSTR("P;"));

            // Pausenzeit
            chars += sprintf(buf, "%u", ((uint16_t)(lightBuf[lightBufInd + 2]) << 8) + (uint16_t)(lightBuf[lightBufInd + 3]));
            sendString(connIndex, buf);

            chars += 2;
            sendLine(connIndex, "");
            break;

          case 'X':         // Programmende

            sendStringP(connIndex, PSTR("X"));
            sendLine(connIndex, "");

            // Ende des Programms, Ausgabe jetzt beenden
            chars = maxChars;
        }

        // Wenn wir noch nicht beim Exit-Befehl sind, dann zum nächsten Index weitergehen
        if((lightBuf[lightBufInd] != 'X') && (lightCmdInd < 247))
        {
          // Befehlszähler erhöhen
          lightCmdInd++;

          if(lightBufInd >= 0x38)       // Ist aktuelle EEPROM-Page zu Ende?
          {
            lightBufInd = 0;            // Neue Page beginnen und aus EEPROM holen
            i  = ((uint16_t)(lightProgNr - 1) << 11) + 0x40;
            i += ((uint16_t)(lightCmdInd & 0xF8) << 3);

            if(i2cEepromReadSequential(i, lightBuf, 64) == 1)
            {
              lightMode = LIGHT_MODE_ERROR;
            }
          }
          else
          {
            lightBufInd += 0x08;        // Auf nächsten Befehl vorrücken
          }
        }
        else                            // Wir sind beim Exit-Befehl angekommen
        {
          chars = maxChars;
          lightCmdInd = 0;
          subMenu = SM_NONE;
        }
      }
    }
  }

  // Anzeige aller Lichtprogramme auf Schnittstelle
  if(subMenu == SM_GETPROGRAMS)
  {
    // Ist der jeweilige Buffer aufnahmebereit?
    if( ((connIndex == 255) && (send_buf.read == send_buf.writ)) ||
        ((connIndex < TCP_MAX_CONNECTIONS) && (tcpConnList[connIndex].rxread == tcpConnList[connIndex].rxwrit)))
    {
      // Eine Ausgabezeile hat max. 3 + 64 + 2 = 69 Zeichen:
      // 3 Zeichen Nummer + Space, 64 Zeichen Name, 2 Zeichen CR+LF
      // Vor einer neuen Ausgabe müssen also noch mind. 69 Zeichen vorhanden sein
      maxChars = (connIndex == 255) ? (BUFLEN - 69) : (TCP_RXTX_BUFLEN - 69);
      chars = 0;

      while((chars < maxChars) && (lightCmdInd < 16))
      {
        // Nur vorbelegte Programme ausgeben
        if(IS_CHARACTER(lightPrograms[lightCmdInd][0]))
        {
          // Programmnummer ausgeben - wir benutzen lightCmdInd als Laufvariable
          chars += sprintf(buf, "%02d ", lightCmdInd + 1);
          sendString(connIndex, buf);

          // Programmname ausgeben und Zeichen mitzählen
          j = 0;
          while((lightPrograms[lightCmdInd][j] != '\0') && (j < 64))
          {
            sendChar(connIndex, (char)lightPrograms[lightCmdInd][j]);
            chars++;
            j++;
          }

          // Zeile abschließen
          sendLine(connIndex, "");
          chars += 2;
        }

        lightCmdInd++;
      }

      // Alle Programme ausgegeben? -> Untermenü verlassen
      if(lightCmdInd == 16)
      {
        subMenu = SM_NONE;
      }
    }
  }
}


/*!
 * \ingroup light
 * \brief Taster-Auswertung
 *
 * Diese Routine entprellt die Taster zur Lichtprogrammsteuerung und führt bei Erkennen
 * eines Tastendrucks die entsprechenden Aktionen aus.
 *
 * \sa ledAusgabe
 */

void tastAuswertung(void)
{
  uint8_t i, j;

  // --------------------------------
  // TASTER 1: STOP

  if(TAST_STOP_PRESSED)
  {
    if(tastStopCnt < TAST_DEBOUNCE_CNT)             // Ist Entprellzähler noch nicht am Entprellwert?
    {
      tastStopCnt++;                                // Entprellzähler hochzählen
      if(tastStopCnt == TAST_DEBOUNCE_CNT)          // Entprellwert jetzt erreicht?
      {
        lightMode = LIGHT_MODE_OFF;                 // Dann Lichtprogramm stoppen
        lightProgAct = 0;
      }
    }
  }
  else
  {
    tastStopCnt = 0;
  }

  // --------------------------------
  // TASTER 2: PLAY

  if(TAST_PLAY_PRESSED)
  {
    if(tastPlayCnt < TAST_DEBOUNCE_CNT)
    {
      tastPlayCnt++;
      if(tastPlayCnt == TAST_DEBOUNCE_CNT)        // Entprellwert erreicht?
      {
        if(lightMode == LIGHT_MODE_PAUSE)         // Wenn gerade pausiert ist ...
        {
          lightMode = LIGHT_MODE_ACTIVE;          // ... dann jetzt weitermachen
        }
        else if(lightMode == LIGHT_MODE_ACTIVE)   // Wenn gerade ein Programm läuft ...
        {
          lightMode = LIGHT_MODE_PAUSE;           // ... dann Programm pausieren
        }
        else                                      // Wenn gerade kein Programm läuft oder ein Fehler war ...
        {
          lightProgStart((lightAutoStart & 0x0F) + 1);  // ... zuletzt benutztes Programm starten
        }
      }
    }
  }
  else
  {
    tastPlayCnt = 0;
  }

  // --------------------------------
  // TASTER 3: NEXT

  if(TAST_NEXT_PRESSED)
  {
    if(tastNextCnt < TAST_DEBOUNCE_CNT)
    {
      tastNextCnt++;
      if(tastNextCnt == TAST_DEBOUNCE_CNT)        // Entprellwert erreicht?
      {
        // Nur weiterschalten, wenn gerade Programm läuft oder pausiert ist
        if((lightMode == LIGHT_MODE_ACTIVE) || (lightMode == LIGHT_MODE_PAUSE))
        {
          i = lightProgAct;                       // Nächstes Programm in der Reihe (-1 entfällt, da i nullbasiert!)
          if(i >= 16)                             // Max. Programmnummer = 15, ansonsten zurück auf 0
            i = 0;

          j = 0;                                  // Abbruchbedingung für Schleife

          // Nächstes Programm suchen
          while((i != (lightProgAct - 1)) && (j == 0))
          {
            if(IS_CHARACTER(lightPrograms[i][0])) // Gültiges Programm gefunden?
            {
              j = 1;
              lightProgStart(i+1);                // Dann Programm starten und im EEPROM merken
              lightAutoStart = (lightAutoStart & 0xF0) | i;
              eeprom_write_byte(&eepLightAutoStart, lightAutoStart);
            }
            else
            {
              i++;                                // Kein gültiges Programm -> weitersuchen
              if(i >= 16)                         // Überlauf berücksichtigen
                i = 0;
            }
          }
        }
      }
    }
  }
  else
  {
    tastNextCnt = 0;
  }
}


/*!
 * \ingroup light
 * \brief LED-Ausgabe
 *
 * Diese Routine gibt den aktuellen Status auf den drei LEDs neben den Tastern aus.
 *
 * \sa tastAuswertung
 */

void ledAusgabe(void)
{
  switch(lightMode)
  {
    case LIGHT_MODE_OFF:      // Leerlauf: Nur obere grüne LED anschalten
      LED_GRUEN_AN;
      LED_DUO_AUS;
      LED_ROT_AUS;
      break;

    case LIGHT_MODE_PAUSE:    // Pause: Obere LED grün, mittlere rot
      LED_GRUEN_AN;
      LED_DUO_ROT;
      LED_ROT_AUS;
      break;

    case LIGHT_MODE_ACTIVE:   // Aktiv: Obere LED grün, mittlere grün
      LED_GRUEN_AN;
      LED_DUO_GRUEN;
      LED_ROT_AUS;
      break;

    default:                  // Fehler: Nur untere rote LED
      LED_GRUEN_AUS;
      LED_DUO_AUS;
      LED_ROT_AN;
  }
}


/*!
 * \ingroup light
 * \brief Neues Lichtprogramm starten
 *
 * Mit dieser Funktion wird ein neues Lichtprogramm gestartet, dessen Programmnummer (1-16)
 * der Funktion mit übergeben werden muss.
 *
 * \param progNr Nummer des zu startenden Lichtprogramms (1-16)
 *
 * \return 0: Programm gestartet; 1: falsche Programmnummer; 2: kein Programm; 3: Fehler beim EEPROM-Zugriff
 * \sa lightScheduler
 */

uint8_t lightProgStart(uint8_t progNr)
{
  uint8_t buf;

  // Prüfe, ob Programmnummer gültig
  if((progNr == 0) || (progNr > 16))
    return 1;

  // Prüfe, ob neues Programm vorhanden ist (Name eingetragen)
  if(i2cEepromReadSequential((uint16_t)(progNr - 1) << 11, &buf, 1) == 1)
    return 3;

  if(!IS_CHARACTER(buf))
    return 2;

  // Ersten Befehl holen
  if(lightGetCommand(((uint16_t)(progNr - 1) << 11) + 0x40, &lightCmdAct) != 0)
  {
    lightMode = LIGHT_MODE_ERROR;
    return 3;
  }

  lightProgAct = progNr;
  lightCmdStep = 0;
  lightMode = LIGHT_MODE_ACTIVE;

  return 0;
}


/*!
 * \ingroup light
 * \brief Befehl aus EEPROM holen und decodieren
 *
 * Mit dieser Funktion wird ein Programm-Befehl aus dem EEPROM geholt, dekodiert und
 * in die angegebene Struktur übertragen.
 *
 * \param addr Adresse des zu holenden Befehls
 * \param dest Zeiger auf die Programmbefehl-Struktur, in die der Befehl übernommen werden soll
 *
 * \return 0: Befehl übertragen, 1: Fehler beim EEPROM-Zugriff, 2: ungültiger Befehl
 * \sa lightScheduler
 */

uint8_t lightGetCommand(uint16_t addr, lightCmdStruct * dest)
{
  uint8_t buf[8];

  // Befehl holen
  if(i2cEepromReadSequential(addr, buf, 8) != 0)
    return 1;
  else
  {
    if((buf[0] == 'S') || (buf[0] == 'P') || (buf[0] == 'F') || (buf[0] == 'X'))
    {
      dest->type  = buf[0];
      dest->delay = ((uint16_t)buf[2] << 8) + (uint16_t)buf[3];
      dest->red   = (uint16_t)buf[4] << 8;
      dest->grn   = (uint16_t)buf[5] << 8;
      dest->blu   = (uint16_t)buf[6] << 8;
#if KANAL_WEISS == 1
      dest->wht   = (uint16_t)buf[7] << 8;
#endif
      return 0;
    }
    else
    {
      return 2;
    }
  }
}


/*!
 * \ingroup light
 * \brief Setzen des PWM-Ausgangs für die LED-Stripes (Rot)
 *
 * Mit dieser Funktion wird der PWM-Ausgang für den Rot-Kanal der LED-Stripes gesetzt.
 * Als Eingangswert werden Helligkeitsstufen von 0-255 erwartet, die über eine
 * exponentielle Kennlinie in einen Wert von 0-65535 umgesetzt werden und in die
 * PWM-Register geschrieben werden. Dadurch ergibt sich ein linear ansteigender
 * Helligkeitseindruck.
 *
 * \param red Eingangswert für den roten Kanal (0-255)
 *
 * \sa lightSetPWMGreen, lightSetPWMBlue
 */

void lightSetPWMRed(uint8_t red)
{
  OCR1A = RGBNormierung[red];
}


/*!
 * \ingroup light
 * \brief Setzen des PWM-Ausgangs für die LED-Stripes (Grün)
 *
 * Mit dieser Funktion wird der PWM-Ausgang für den Grün-Kanal der LED-Stripes gesetzt.
 * Als Eingangswert werden Helligkeitsstufen von 0-255 erwartet, die über eine
 * exponentielle Kennlinie in einen Wert von 0-65535 umgesetzt werden und in die
 * PWM-Register geschrieben werden. Dadurch ergibt sich ein linear ansteigender
 * Helligkeitseindruck.
 *
 * \param green Eingangswert für den grünen Kanal (0-255)
 *
 * \sa lightSetPWMRed, lightSetPWMBlue
 */

void lightSetPWMGreen(uint8_t green)
{
  OCR1B = RGBNormierung[green];
}


/*!
 * \ingroup light
 * \brief Setzen des PWM-Ausgangs für die LED-Stripes (Blau)
 *
 * Mit dieser Funktion wird der PWM-Ausgang für den Blau-Kanal der LED-Stripes gesetzt.
 * Als Eingangswert werden Helligkeitsstufen von 0-255 erwartet, die über eine
 * exponentielle Kennlinie in einen Wert von 0-65535 umgesetzt werden und in die
 * PWM-Register geschrieben werden. Dadurch ergibt sich ein linear ansteigender
 * Helligkeitseindruck.
 *
 * \param blue Eingangswert für den blauen Kanal (0-255)
 *
 * \sa lightSetPWMRed, lightSetPWMGreen
 */

void lightSetPWMBlue(uint8_t blue)
{
  OCR1C = RGBNormierung[blue];
}


#if KANAL_WEISS == 1
/*!
 * \ingroup light
 * \brief Setzen des PWM-Ausgangs für die LED-Stripes (Weiss)
 *
 * Mit dieser Funktion wird der PWM-Ausgang für den Weiss-Kanal der LED-Stripes gesetzt.
 * Als Eingangswert werden Helligkeitsstufen von 0-255 erwartet, die über eine
 * exponentielle Kennlinie in einen Wert von 0-65535 umgesetzt werden und in die
 * PWM-Register geschrieben werden. Dadurch ergibt sich ein linear ansteigender
 * Helligkeitseindruck.
 *
 * \param white Eingangswert für den weißen Kanal (0-255)
 *
 * \sa lightSetPWMRed, lightSetPWMGreen, lightSetPWMBlue
 */

void lightSetPWMWhite(uint8_t white)
{
  OCR3A = RGBNormierung[white];
}
#endif



