/* Spass mit der LED des Langweilometers
 *
 * Aufrufbeispiel: ./frontled
 *
 * Compilieren mit:
 *    gcc -Wall -o frontled frontled.c
 *
 * Die Meldung "'GPIO_Read' defined but not used" kann
 * ignoriert werden (die Funktion ist halt fuer alle
 * Faelle im Quellcode enthalten)
 *
 * Das Programm muss ständig laufen, am besten wird es
 * mit dem Aufruf
 *     /home/pi/frontled &
 * in der Datei /etc/rc.local eingetragen
 * Eventuell muss es SUID root gesetzt werden:
 *    sudo chown root.root frontled
 *    sudo chmod 4711 frontled
 *
 * 19.01.2015  JPL
 */

#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <ctype.h>
#include <time.h>

/* Symbolische Namen fuer die Datenrichtung und die Daten  */
#define IN  0
#define OUT 1

#define LOW  0
#define HIGH 1

/* Datenpuffer fuer die GPIO-Funktionen */
#define MAXBUFFER 100

/* LED Pin, Stiftleiste Pin 13 */
#define LED 27

/* Datei fuer die Werteuebergabe */
#define FILENAME "/home/pi/boring"

/* Funktionsprototypen */
static int GPIO_Export(int pin);
static int GPIO_Unexport(int pin);
static int GPIO_Direction(int pin, int dir);
static int GPIO_Read(int pin);
static int GPIO_Write(int pin, int value);

int delay(unsigned long millis);
void beenden(int dummy);

int main(int argc, char **argv)
  {
  int err;                /* Fehlervariable */
  time_t epoc, oldepoc;   /* 32-Bit-Zahl    */
  struct stat attr;       /* Filestatus     */
  int i;                  /* Schleifenzaehler */

  /************* Beginn Initialisierung *************/
  /* Signalhandler setzen */
  signal(SIGINT,beenden);

  /* bin ich root? */
  if (geteuid() != 0)
    {
    fprintf(stderr, "You must be root!\n");
    return 1;
    }

  /* Init GPIO-Pins */
  err = GPIO_Export(LED);
  err += GPIO_Direction(LED, OUT);
  err += GPIO_Write(LED, 0);
  if (err > 0)
    {
    fprintf(stderr, "Can not init GPIO!\n");
    return 2;
    }
  /************** Ende Initialisierung **************/

  /* get file modification date */
  stat(FILENAME, &attr);
  oldepoc = attr.st_mtime;

  for(;;)
    {
    /* get file modification date */
    stat(FILENAME, &attr);
    epoc = attr.st_mtime;
    if (epoc > oldepoc)
      { /* file has been changed, lightshow :-) */
      for (i = 0; i < 50; i++)
        {
        GPIO_Write(LED, 1);
        delay(50);
        GPIO_Write(LED, 0);
        delay(50);
        }
      oldepoc = epoc;
      }
    else
      { /* normal heartbeat */
      GPIO_Write(LED, 1);
      delay(50);
      GPIO_Write(LED, 0);
      delay(100);
      GPIO_Write(LED, 1);
      delay(50);
      GPIO_Write(LED, 0);
      delay(1000);
      }
    }
  return 0;   /* never reached */
  }


/*
 * STRG-C behandeln, aufraeumen
 */
void beenden(int dummy)
  {
  GPIO_Write(LED, 0);
  GPIO_Unexport(LED);
   delay(100);
  printf("\nHasta la vista, baby!\n");
  exit(0);
  }


/*
 * Delay (warten) - Zeitangabe in Millisekunden
 */
int delay(unsigned long millis)
  {
  struct timespec ts;
  int err;

  ts.tv_sec = millis / 1000;
  ts.tv_nsec = (millis % 1000) * 1000000L;
  err = nanosleep(&ts, (struct timespec *)NULL);
  return (err);
  }


/* GPIO-Pin aktivieren
 * Schreiben Pinnummer nach /sys/class/gpio/export
 * Ergebnis: 0 = O.K., -1 = Fehler
 */
static int GPIO_Export(int pin)
  {
        char buffer[MAXBUFFER];    /* Output Buffer   */
        ssize_t bytes;             /* Datensatzlaenge */
        int fd;                    /* Filedescriptor  */

        fd = open("/sys/class/gpio/export", O_WRONLY);
        if (fd < 0)
    {
                perror("Kann nicht auf export schreiben!\n");
                return(-1);
          }
        bytes = snprintf(buffer, MAXBUFFER, "%d", pin);
        write(fd, buffer, bytes);
        close(fd);
        return(0);
  }

/* GPIO-Pin deaktivieren
 * Schreiben Pinnummer nach /sys/class/gpio/unexport
 * Ergebnis: 0 = O.K., -1 = Fehler
 */
static int GPIO_Unexport(int pin)
  {
  char buffer[MAXBUFFER];    /* Output Buffer   */
  ssize_t bytes;             /* Datensatzlaenge */
  int fd;                    /* Filedescriptor  */

  fd = open("/sys/class/gpio/unexport", O_WRONLY);
  if (fd < 0)
    {
    perror("Kann nicht auf unexport schreiben!\n");
    return(-1);
    }
  bytes = snprintf(buffer, MAXBUFFER, "%d", pin);
  write(fd, buffer, bytes);
  close(fd);
  return(0);
  }

/* Datenrichtung GPIO-Pin festlegen
 * Schreiben Pinnummer nach /sys/class/gpioXX/direction
 * Ergebnis: 0 = O.K., -1 = Fehler
 */
static int GPIO_Direction(int pin, int dir)
  {
  char path[MAXBUFFER];      /* Buffer fuer Pfad   */
  int fd;                    /* Filedescriptor  */
  int res;                   /* Ergebnis von write */

  snprintf(path, MAXBUFFER, "/sys/class/gpio/gpio%d/direction", pin);
  fd = open(path, O_WRONLY);
  if (fd < 0)
    {
    perror("Kann Datenrichtung nicht setzen (open)!\n");
    return(-1);
    }
  switch (dir)
    {
    case IN : res = write(fd,"in",2); break;
    case OUT: res = write(fd,"out",3); break;
    }
  if (res < 0)
    {
    perror("Kann Datenrichtung nicht setzen (write)!\n");
    return(-1);
    }
  close(fd);
  return(0);
  }

/* vom GPIO lesen
 * Ergebnis: -1 = Fehler, 0/1 = Portstatus
 */
static int GPIO_Read(int pin)
  {
  char path[MAXBUFFER];         /* Buffer fuer Pfad     */
  int fd;                       /* Filedescriptor       */
  char result[MAXBUFFER] = {0}; /* Buffer fuer Ergebnis */

  snprintf(path, MAXBUFFER, "/sys/class/gpio/gpio%d/value", pin);
  fd = open(path, O_RDONLY);
  if (fd < 0)
    {
    perror("Kann vom GPIO nicht lesen (open)!\n");
    return(-1);
    }
  if (read(fd, result, 3) < 0)
    {
    perror("Kann vom GPIO nicht lesen (read)!\n");
    return(-1);
    }
  close(fd);
  return(atoi(result));
  }

/* auf GPIO schreiben
 * Ergebnis: -1 = Fehler, 0 = O.K.
 */
static int GPIO_Write(int pin, int value)
  {
  char path[MAXBUFFER];      /* Buffer fuer Pfad   */
  int fd;                    /* Filedescriptor     */
  int res;                   /* Ergebnis von write */

  snprintf(path, MAXBUFFER, "/sys/class/gpio/gpio%d/value", pin);
  fd = open(path, O_WRONLY);
  if (fd < 0)
    {
    perror("Kann auf GPIO nicht schreiben (open)!\n");
    return(-1);
    }
  switch (value)
    {
    case LOW : res = write(fd,"0",1); break;
    case HIGH: res = write(fd,"1",1); break;
    }
  if (res < 0)
    {
    perror("Kann auf GPIO nicht schreiben (write)!\n");
    return(-1);
    }
  close(fd);
  return(0);
  }
