Francois PONSARD před 2 roky
revize
a85ce88d56
19 změnil soubory, kde provedl 1368 přidání a 0 odebrání
  1. 2 0
      AUTHORS
  2. 5 0
      CHANGELOG
  3. 21 0
      Makefile
  4. 133 0
      README
  5. 2 0
      delay
  6. 142 0
      fifo.c
  7. 3 0
      fifo.h
  8. 169 0
      pert_interf.c
  9. 7 0
      pert_interf.h
  10. binární
      pertd2
  11. 675 0
      pertd2.c
  12. 21 0
      pertd2.conf
  13. 97 0
      sendcommands/sendcommand.c
  14. 45 0
      sleep_us.c
  15. 1 0
      sleep_us.h
  16. 22 0
      test1.dat
  17. 11 0
      test2.dat
  18. 8 0
      test3.dat
  19. 4 0
      testrun

+ 2 - 0
AUTHORS

@@ -0,0 +1,2 @@
+Ron Lauzon
+	Original author

+ 5 - 0
CHANGELOG

@@ -0,0 +1,5 @@
+2007/03/05 RRL
+- Completely changed the interface to the deamon.  The daemon now listens
+  for commands from a fifo it creates instead of through files existing.
+
+  This allowed me to remove much code involving refreshing the data.

+ 21 - 0
Makefile

@@ -0,0 +1,21 @@
+pertd2:	pertd2.o pert_interf.o sleep_us.o fifo.o
+	cc	pertd2.o pert_interf.o sleep_us.o fifo.o -o pertd2
+
+debug:	pertd2.c pert_interf.o sleep_us.o fifo.o
+	cc -DDEBUG -c -o fifo.o fifo.c
+	cc -DDEBUG -c -o sleep_us.o sleep_us.c
+	cc -DDEBUG -c -o pert_interf.o pert_interf.c
+	cc -DDEBUG -c -o pertd2.o pertd2.c
+	cc	pertd2.o pert_interf.o sleep_us.o fifo.o -o pertd2
+
+pert_interf.o: pert_interf.c
+	cc    -c -o pert_interf.o pert_interf.c
+
+pertd2.o: pertd2.c pert_interf.h sleep_us.h fifo.h
+	cc    -c -o pertd2.o pertd2.c
+
+sleep_us.o: sleep_us.c
+	cc    -c -o sleep_us.o sleep_us.c
+
+fifo.o: fifo.c
+	cc    -c -o fifo.o fifo.c

+ 133 - 0
README

@@ -0,0 +1,133 @@
+pertd2 - A daemon that displays information on the Pertelian USB LCD display
+
+Overview:
+This program creates a fifo and listens to it for commands.  The commands
+can be used to turn the backlight on/off, set the data of each line to
+display, stop the program, etc.
+
+If the lines are more than the 20 chars allowed for each line, the data is 
+scrolled horizontally.
+
+The data will wrap into unused lines.  Ex: if line 1 has data, but line 2 does
+not, the data from line 1 will wrap into line 2 and both lines will scroll at
+double speed.
+
+If there is no data to display, the current date/time will display.
+
+The interface to the daemon is through a fifo.  The name and location
+of this fifo is set via a configuration file /etc/pertd2.conf
+Optionally, you can specify a configuration file on the pertd command line.
+
+The program is set to go daemon right away.  So when you run pertd2,
+you will get a prompt back immediately as it goes off into the background
+and not be terminated when you log off.
+
+Note that on my system (Mandriva 2007), when the user logs off, ownership
+of /dev/ttyUSB0 goes back to root.  So it's probably a good idea to run
+this daemon as root.  See the service directory for turning pertd2 into
+a service that runs when the system boots.
+
+Configuration:
+delay_time -
+   The interval between screen refreshes in ms.  (1000000ms = 1 second)
+   This will basically control how fast the Pertelian scrolls the data
+   across the screen.
+   Default: 500000 (1/2 second)
+
+char_delay -
+   The delay between each character sent to the Pertelian.  This delay prevents
+   the program from sending data too fast and causing garbled output on the
+   display.  Newer systems may not need any delay, so I made this configurable.
+   The delay is in ms.  1 ms is usually needed for old systems.  If your
+   display is very slow, reduce this to 0.  If the output is garbled, put it
+   to 1.
+   Default: 1
+
+device - 
+   The device the Pertelian is on.
+   Default: /dev/ttyUSB0
+
+fifo_name -
+   The name of the fifo that pertd2 creates.
+
+backlight_mgt -
+   Backlight management.  This indicates whether you want pertd2 to
+   manage the backlight or not.  Basically it manages it by turning the
+   backlight on when there is data to display and off when there is no data.
+   1 = pertd2 manages the backlight.  0 = it doesn't.
+   Default: 0
+
+Commands:
+backlight on
+backlight off
+stop
+line1
+line2
+line3
+line4
+delay time
+char delay
+backlight mgt on
+backlight mgt off
+
+You communicate by sending the command to the fifo.  ex:
+echo "backlight on" > /tmp/pertd2.fifo
+
+Most of the commands should be self-explanatory.
+
+The commands:
+
+backlight on
+backlight off
+stop
+backlight mgt on
+backlight mgt off
+
+are all single line commands.
+
+The "delay time" and "char delay" commands take a second line that specified
+the value.  ex:
+
+delay time
+500000
+
+The "line?" commands are a little more complex. ex:
+line1
+600
+==EOD==
+The quick brown fox jumped over the lazy dog.
+==EOD==
+
+The first line (line1) is the command, also telling pertd2 which line
+you want this data to go in.
+The second line (600) is the number of seconds pertd2 should display the
+data.  Basically an expiration time.  0 seconds means "don't expire."
+The third line is the end of data marker.  It will not display.
+The rest of the lines are the data to be displayed.  New lines will be
+converted to spaces.  The data goes from the first EOD marker until it
+sees another line with the EOD marker, or until it his end of file (i.e.
+the fifo is empty).
+
+So, you can put these lines into a file:
+line1
+12
+==EOD==
+19:26:59
+==EOD==
+line2
+12
+==EOD==
+Testing
+==EOD==
+
+then you can
+cat file > /tmp/pertd2.fifo
+to send the commands.
+
+The "sendcommands" directory contains examples of programs that send commands
+to the fifo.
+
+I release my code into the Public Domain.  Have fun, but use this code
+and the resulting program at your own risk. --Ron Lauzon
+
+2007/03/24 Created by Ron Lauzon

+ 2 - 0
delay

@@ -0,0 +1,2 @@
+char delay
+0

+ 142 - 0
fifo.c

@@ -0,0 +1,142 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+#define BUF_SIZE 512
+char buffer[BUF_SIZE];
+char *bufp=buffer;
+int n = 0;
+int fifo_fd;
+
+int get_one_char() {
+	/* If the buffer is empty */
+	if (n <= 0) {
+		/* Get some stuff */
+		n = read(fifo_fd,buffer,BUF_SIZE);
+
+		/* If we got some stuff */
+		if (n > 0) {
+			/* Point back to the start of the buffer */
+			bufp = buffer;
+		} else { /* we didn't get any stuff */
+			return(EOF);
+		}
+	}
+	n--; /* Decrement the number of chars in the buffer */
+	return(*bufp++);
+}
+
+char *read_line() {
+	char *line, *temp_line;
+	int current_max_line;
+	int i;
+	int char_read;
+
+#ifdef DEBUG
+  printf("read_line - start\n");
+#endif
+
+	/* Any data in the fifo? */
+	char_read = get_one_char();
+
+	/* No. */
+	if (char_read == EOF) {
+		return(NULL); /* No data */
+	}
+	if (char_read == 0) {
+		return(NULL); /* No data */
+	}
+
+	/* We have data */
+
+	/* Allocate some memory to put the line */
+	current_max_line = BUF_SIZE;
+	line = (char *)malloc(current_max_line);
+	i = 0;
+
+  /* While we didn't hit the end of line */
+	while((char_read != '\n')
+     && (char_read != EOF)
+ 		 && (char_read != 0)) {
+
+		/* If our buffer is too small */
+		if (i >= current_max_line) {
+			/* Reallocate it */
+			temp_line = line;
+			line = (char *)malloc(current_max_line+BUF_SIZE);
+			memcpy(line,temp_line,current_max_line);
+			current_max_line+=BUF_SIZE;
+			free(temp_line);
+		}
+
+		/* Put the new character into the line */
+		line[i] = char_read;
+
+		/* Get the next character */
+		i++;
+		char_read = get_one_char();
+		}
+	line[i] = '\0'; /* terminate the string */
+
+#ifdef DEBUG
+  printf("read_line: line=%s\n",line);
+#endif
+
+	return(line);
+}
+
+void close_fifo(char *fifo_file_name) {
+  /* If we opened it, close it */
+  if (fifo_fd >= 0) {
+    close(fifo_fd);
+    }
+
+#ifdef DEBUG
+  printf("FIFO closed:%s\n",fifo_file_name);
+#endif
+
+  /* We are done - clean up */
+  unlink(fifo_file_name);
+
+#ifdef DEBUG
+  printf("FIFO deleted\n");
+#endif
+}
+
+int open_fifo(char *fifo_file_name) {
+  /* Make the fifo */
+  if (mkfifo(fifo_file_name,O_RDWR|O_CREAT|O_NOCTTY) != 0) {
+    fprintf(stderr,"Error creating FIFO: %s\n",fifo_file_name);
+    return(0);
+  }
+
+#ifdef DEBUG
+  printf("FIFO created\n");
+#endif
+
+  /* Set the permissions on the fifo
+     so that everyone can write to it
+     but only owner can read from it. */
+  if (chmod(fifo_file_name,S_IWUSR|S_IRUSR|S_IWGRP|S_IWOTH) < 0) {
+    fprintf(stderr,"Unable to change permissions on FIFO\n");
+		close_fifo(fifo_file_name);
+    return(0);
+    }
+
+  /* Open the fifo for reading */
+  if ((fifo_fd=open(fifo_file_name,O_RDONLY|O_NONBLOCK)) < 0) {
+    fprintf(stderr,"Error opening FIFO for reading\n");
+		close_fifo(fifo_file_name);
+    return(0);
+    }
+
+#ifdef DEBUG
+  printf("FIFO opened\n");
+#endif
+
+	return(1);
+}

+ 3 - 0
fifo.h

@@ -0,0 +1,3 @@
+char *read_line();
+int open_fifo();
+void close_fifo();

+ 169 - 0
pert_interf.c

@@ -0,0 +1,169 @@
+/*
+ * Code from:
+ * A small demo program to show how to drive the Pertelian X2040 USB LCD
+ * display from Linux.
+ *
+ * Note that this comes without any warranty. 
+ * This worked fine for me, and I am unaware of any problem, but if this
+ * program causes a problem to you or your hardware you are on your own and
+ * I will not be liable for damages in any way.
+ * If this is not acceptable to you, you are not allowed to use this program.
+ *
+ * Otherwise you are free to use this program as you see fit provided that
+ * - you do not pretent having written this yourself
+ * - changes you make are clearly identified as such
+ *
+ * Information on how to program the device mainly came from
+ * http://pertelian.com/index.php?option=com_content&task=view&id=27&Itemid=33
+ * and some small local experiments
+ *
+ *   Frans Meulenbroeks 
+ */
+
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <unistd.h>
+#include "sleep_us.h"
+
+/* Ron Lauzon - 7/16/2006
+ * Changed DELAY to mean milliseconds and removed the delay()
+ * function.  I replaced it with a sleep_us for DELAY microseconds.
+ * A 1 ms pause seems sufficient.  No pause will generate garbled
+ * output on the display */
+
+/* Ron Lauzon - 3/14/2007
+ * Changed the device interface to use file handles instead of FILE.
+ * This allows me to set serial port attributes (like speed).
+ * It also allowed me to remove the delay completely.
+ */
+
+/* DELAY is constant used to generate some delay. 
+ * You might want to tweak it for your hardware.
+ * If the value is too small some or all of the data is going to be garbled
+ * or the initialisation will fail.
+ * If the value is too large you'll have to wait quite a while....
+ */
+/*#define DELAY 1 */
+
+int pert_fd; /* file descriptor to the Pertelian */
+unsigned int char_delay;
+
+const static unsigned char rowoffset[4] =
+	{ 0x80, 0x80+0x40, 0x80 + 0x14, 0x80 + 0x40 + 0x14 };
+/* offsets to address the various rows. 
+ * the addressing structure is a little bit odd. actually row 3 is a
+ * continuation of row 1 and row4 of row2
+ */
+
+/*
+ * delay
+ *     This function introduces some delay. After writing a character to 
+ *     the display one needs to wait a short while. This is achieved by the 
+ *     code below. Actually there are two different delays for the device
+ *     but this code does not make the distinction.
+ */
+/*void delay(int n) {
+	volatile int i;
+	for (i = 0; i < n; i++) {
+		}
+	} */
+
+/*
+ * wrtch
+ *     This function writes a character byte to the device
+ */
+void wrtch(unsigned char c) {
+	if (write(pert_fd,&c,1) != 1)
+		fprintf(stderr,"Error writing to pert: %s\n",c);
+	tcdrain(pert_fd);
+	sleep_us(char_delay);
+	}
+
+/*
+ * putcode
+ *  This function writes a code byte to the device
+ */
+void putcode(unsigned char code) {
+	wrtch((char)0xfe);
+	wrtch(code);
+	sleep_us(char_delay);
+	}
+
+/*
+ * wrt
+ *     This function writes a character string to the device (0 terminated)
+ *     it writes the data to whereever the cursor is pointing
+ */
+void wrt(char *p) {
+	while(*p) {
+		wrtch(*p);
+		p++;
+		}
+	}
+
+/*
+ * wrtln
+ *     This function writes a character string to a specific row
+ */
+void wrtln(int row, char *p) {
+	putcode(rowoffset[row]);
+	wrt(p);
+	}
+
+/* display_init - initialize the Pertelian */
+void display_init(char *device_name) {
+	struct termios term;
+
+	pert_fd = open(device_name, O_WRONLY | O_NONBLOCK);
+	if (pert_fd < 0) {
+		fprintf(stderr, "Cannot open device %s for writing !\n", device_name);
+		exit(EXIT_FAILURE);
+		}
+
+	/* Get the tty config */
+  if (tcgetattr(pert_fd,&term) < 0) {
+		fprintf(stderr,"Unable to get terminal attributes\n");
+		exit(EXIT_FAILURE);
+	}
+
+	/* set the terminal attributes */
+	term.c_cflag = CRTSCTS | CS8 | CLOCAL | CREAD;
+	term.c_iflag = ICRNL | IGNPAR;
+	term.c_oflag = 0;
+	term.c_lflag = 0;
+	term.c_cc[VMIN] = 1;
+	term.c_cc[CTIME] = 0;
+	cfsetispeed(&term,B38400);
+	cfsetospeed(&term,B38400);
+
+	/* Flush any trash on the line */
+	tcflush(pert_fd,TCIFLUSH);
+
+	/* Sent the new attributes to the port */
+	if (tcsetattr(pert_fd,TCSANOW,&term) < 0) {
+		fprintf(stderr,"Unable to set terminal attributes\n");
+		exit(EXIT_FAILURE);
+	}
+
+	/* We are connected - now set up the Pertelian */
+	putcode(0x38); /* initialise display (8 bit interface, shift to right */
+	putcode(0x06); /* cursor move direction to the right, no automatic shift */
+	putcode(0x10); /* move cursor on data write */
+	putcode(0x0c); /* cursor off */
+	putcode(0x01); /* clear display */
+	}
+
+void display_close() {
+	close(pert_fd);
+	}
+
+void backlight(int on) {
+	if (on)
+		putcode(0x03); /* backlight on (3 = on, 2 = off) */
+	else
+		putcode(0x02);
+}

+ 7 - 0
pert_interf.h

@@ -0,0 +1,7 @@
+#define PERTD_VERSION 2007030500
+#define PERT_DISPLAY_WIDTH 20
+void wrtln(int row, char *p);
+void display_init(char *device_name);
+void display_close();
+void backlight(int on);
+extern int char_delay;

binární
pertd2


+ 675 - 0
pertd2.c

@@ -0,0 +1,675 @@
+/* Daemon for the Pertelian LCD display.
+
+   This program will horizontally scroll information across the display.
+
+	 Written by Ron Lauzon - 2007
+	 I release this code into the Public Domain.
+	 Use this at your own risk.
+*/
+
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include "fifo.h"
+#include "pert_interf.h"
+
+# define BUF_SIZE 512
+
+#ifdef DEBUG
+#define CONFIG_FILE_NAME "./pertd2.conf"
+#else
+#define CONFIG_FILE_NAME "/etc/pertd2.conf"
+#endif
+
+/* Commands */
+char *command_text[] = {"backlight on",
+                    "backlight off",
+                    "stop",
+                    "line1",
+                    "line2",
+                    "line3",
+                    "line4",
+                    "delay time",
+                    "char delay",
+                    "backlight mgt on",
+                    "backlight mgt off",
+                    NULL};
+enum command_enum {
+	backlight_on = 0,
+	backlight_off,
+	stop,
+	line1,
+	line2,
+	line3,
+	line4,
+	delay_time_cmd,
+	char_delay_cmd,
+	backlight_mgt_on,
+	backlight_mgt_off,
+	error_cmd=999};
+
+/* Config information */
+char *fifo_name = NULL;
+char *device_name = NULL;
+unsigned int delay_time = 0;
+int backlight_mgt = 0;
+
+/* Information about the data to display */
+char *lines[4]; /* The line text */
+int pos[4]; /* The position we are in the line */
+time_t refresh_time[4]; /* Time data was last refreshed */
+int timeout[4]; /* Number of seconds before we expire the line */
+int backlight_status; /* 0 - backlight not on, 1 - backlight on */
+
+/* This routine puts the program into "daemon" mode.
+ * i.e. it disconnects from the current session so that
+ * it's not automatically killed when the user logs off.
+ */
+int daemon_init() {
+	pid_t pid;
+	FILE *pidfile;
+
+#ifdef DEBUG
+	return(0); /* Don't go daemon in debug mode */
+#endif
+	/* Fork off 8-) */
+	if ( (pid=fork()) < 0)
+		return(-1);  /* If error - leave */
+	else if (pid != 0)
+		exit(0);     /* parent exits */
+
+	/* Child continues */
+
+	/* Become session leader */
+	setsid();
+
+	return(0);
+  }
+
+
+int is_numeric(char *buffer) {
+	int i;
+
+	/* Look through all the chars in the buffer */
+	for (i=0; i<strlen(buffer); i++) {
+		/* If we find a character that isn't a digit */
+		if (!isdigit(buffer[i])) {
+				/* It was bad data */
+				return(0); /* false - not numeric */
+			}	
+	}
+	/* By the time we get here, all the characters are digits */
+	return(1); /* true - numeric */
+}
+
+int read_config(char *config_file_name) {
+	FILE *config_file;
+	char line[1024];
+	char *parm;
+	char *value;
+
+#ifdef DEBUG
+	printf("Opening config file %s\n",config_file_name);
+#endif
+
+	char_delay = 1; /* default correctly for old systems */
+
+	/* Try to open the config file */
+	if ((config_file = fopen(config_file_name,"r")) != NULL) {
+
+		/* Read lines from the config file */
+		while(fgets(line,1024,config_file) != NULL) {
+
+			/* Trim the new line */
+			line[strlen(line)-1] = '\0';
+
+			/* If comment, ignore */
+			if (line[0] == '#')
+				continue;
+ 			/* If no = in the string, ignore */
+			if (strchr(line,'=') == NULL)
+				continue;
+
+			/* We found a parm/value pair.  Point to the value. */
+			value=strchr(line,'=');
+			value[0] = '\0';
+			value++;
+
+			/* Now, process the parm */
+			if (strcmp(line,"fifo_name") == 0) {
+				fifo_name = (char *)malloc(strlen(value)+1);
+				strcpy(fifo_name,value);
+		  }
+			if (strcmp(line,"device") == 0) {
+				device_name = (char *)malloc(strlen(value)+1);
+				strcpy(device_name,value);
+      }
+			if (strcmp(line,"delay_time") == 0) {
+				delay_time=atoi(value);
+		  }
+			if (strcmp(line,"char_delay") == 0) {
+				char_delay=atoi(value);
+		  }
+			if (strcmp(line,"backlight_mgt") == 0) {
+				backlight_mgt=atoi(value);
+		  }
+		}
+		fclose(config_file);
+	}
+
+	/* Set defaults for any values not in the config file */
+	if (fifo_name == NULL) {
+			fifo_name = (char *)malloc(1024);
+			strcpy(fifo_name,"/tmp/pertd2.fifo");
+	}
+	if (device_name == NULL) {
+		device_name = (char *)malloc(1024);
+		strcpy(device_name,"/dev/ttyUSB0");
+		}
+	if (delay_time == 0)
+		delay_time = 500000; /* 1/2 second (500,000 milliseconds) */
+}
+
+int get_command() {
+	char *command;
+	int i;
+
+	command = read_line();
+	if (command == NULL) {
+		return(error_cmd); /* No data */
+	}
+
+	/* lower case the command */
+	for(i=0;i<strlen(command);i++)
+		command[i] = tolower(command[i]);
+
+#ifdef DEBUG
+	printf("Command:%s\n",command);
+#endif
+
+	/* Loop through the commands */
+	i = 0;
+	while(command_text[i] != NULL) {
+		/* If we found the command text */
+		if (strcmp(command,command_text[i]) == 0) {
+			free(command); /* memory leaks are bad */
+#ifdef DEBUG
+			printf("Command_num:%d\n",i);
+#endif
+			/* Return that command number */
+   		return(i);
+		}
+		i++;
+	}
+
+	/* If we got here, we didn't find the command
+	 * so the data is invalid.  Throw it out. */
+	free(command); /* memory leaks are bad */
+	return(error_cmd); /* bad data */
+}
+
+int get_timeout() {
+	char *timeout;
+	int i;
+	int int_timeout;
+
+	timeout = read_line();
+	if (timeout == NULL) {
+		return(-1); /* No data */
+	}
+
+  /* Validate that the timeout is a number */
+	if (!is_numeric(timeout)) {
+		/* If it wasn't a digit, it was bad data */
+		free(timeout);
+		return(-1); /* bad data */
+	}
+
+#ifdef DEBUG
+	printf("Timeout:%s\n",timeout);
+#endif
+
+	/* Convert the buffer into a int and return it */
+	int_timeout = atoi(timeout);
+	free(timeout);
+	return(int_timeout);
+}
+
+char *get_line() {
+	char *EOD;
+	char *current_line, *temp;
+	char *line_buffer;
+	int current_length;
+
+	/* Get EOD string */
+	EOD = read_line();
+	if (EOD == NULL) {
+		return(NULL); /* no data */
+	}
+
+	/* We have data */
+
+	/* Allocate a buffer for the line */
+	current_length = 1;
+	line_buffer = (char *)malloc(current_length);
+	line_buffer[0] = '\0';
+
+	/* Read the next line from the fifo */
+	current_line = read_line();
+
+	/* While we didn't hit the end of data marker
+   * and we didn't run out of fifo data */
+	while ((current_line != NULL)
+      && (strcmp(current_line,EOD) != 0)) {
+		/* If our buffer is too small */
+		if ((strlen(current_line) + strlen(line_buffer)+1) > current_length) {
+			/* Allocate new buffer and copy the old data over */
+			temp = line_buffer;
+			line_buffer = (char *)malloc(current_length+BUF_SIZE);
+			memcpy(line_buffer,temp,current_length);
+			current_length += BUF_SIZE;
+			free(temp);
+		}
+
+		/* Add the current line to the buffer */
+		if (strlen(line_buffer) > 0)
+			strcat(line_buffer," ");
+		strcat(line_buffer,current_line);
+
+		free(current_line); /* memory leaks are bad */
+
+		current_line = read_line();
+	}
+
+	if (current_line != NULL)
+		free(current_line); /* memory leaks are bad */
+	free(EOD);
+	return(line_buffer);
+}
+
+void process_delay_time() {
+	char *cur_line;
+
+  /* Get the delay time */
+	cur_line = read_line();
+#ifdef DEBUG
+	printf("Read delay_time:%s\n",cur_line);
+#endif
+
+	/* No delay time - exit */
+	if (cur_line == NULL)
+		return;
+	if (strlen(cur_line) == 0)
+		return;
+
+	/* The delay time must be a number */
+	if (is_numeric(cur_line)) {
+		delay_time = atoi(cur_line);
+		free(cur_line);
+#ifdef DEBUG
+		printf("Processed delay_time:%d\n",delay_time);
+#endif
+		return;
+	}
+
+	/* If it's not a number, it's bad data - ignore the command */
+}
+
+void process_char_delay() {
+	char *cur_line;
+
+  /* Get the delay time */
+	cur_line = read_line();
+
+	/* No char delay - exit */
+	if (cur_line == NULL)
+		return;
+	if (strlen(cur_line) == 0)
+		return;
+
+	/* The delay time must be a number */
+	if (is_numeric(cur_line)) {
+		char_delay = atoi(cur_line);
+		free(cur_line);
+		return;
+	}
+
+	/* If it's not a number, it's bad data - ignore the command */
+}
+
+void process_line(int line_num) {
+	int cur_timeout;
+	char *cur_line;
+	time_t now;
+
+	time(&now);
+	cur_timeout = get_timeout();
+	if (cur_timeout < 0) {
+		cur_timeout = 0;
+	}
+
+	cur_line = get_line();
+
+	if (lines[line_num] != NULL)
+		free(lines[line_num]);
+
+	lines[line_num] = cur_line;
+	timeout[line_num] = cur_timeout;
+	refresh_time[line_num] = now;
+}
+
+int data_to_display() {
+	int temp;
+	int i;
+	temp = 0;
+
+	/* Look through all the lines */
+	for (i=0; i<4; i++) {
+		/* If the line isn't empty */
+		if (lines[i] != NULL) {
+			if (strlen(lines[i]) > 0) {
+				return 1; /* we have data */
+			}
+		}
+	}
+
+	/* We only get here when no lines have data */
+	return 0;
+}
+
+void display_date_time() {
+	time_t now_t;
+	struct tm *now_tm;
+	char line2[PERT_DISPLAY_WIDTH+1], line3[PERT_DISPLAY_WIDTH+1];
+
+	/* First we need to get the current time */
+	now_t = time(NULL);
+
+	/* Now, we need to convert the time_t to something useful */
+	now_tm = localtime(&now_t);
+
+	/* Format the date/time into a date line and a time line */
+	/* DDD MMM DD, YYYY - 16 chars pad 4 spaces */
+	strftime(line2,PERT_DISPLAY_WIDTH+1,"  %a %b %d, %Y  ",now_tm);
+	/* HH:MM:SS - 8 chars - pad 12 spaces */
+	strftime(line3,PERT_DISPLAY_WIDTH+1,"      %T      ",now_tm);
+	line2[PERT_DISPLAY_WIDTH] = '\0';
+	line3[PERT_DISPLAY_WIDTH] = '\0';
+
+#ifdef DEBUG
+	printf("line2= %s\n",line2);
+	printf("line3= %s\n",line3);
+#endif
+
+	/* Put the lines on the display */
+	wrtln(1,line2);
+	wrtln(2,line3);
+}
+
+/* Copy over PERT_DISPLAY_WIDTH chars of data into line - wrapping around 
+ * when at the end of the data */
+char *fill_line(int lineno,int offset) {
+	static char temp_line[21];
+	int i;
+	int temp_pos;
+
+	/* If the line contains no data */
+	if (lines[lineno] == NULL) {
+		/* If we are on the first line */
+		if (lineno == 0) {
+			/* No data to display - return blanks to clear line */
+			memset(temp_line,' ',PERT_DISPLAY_WIDTH);
+      temp_line[PERT_DISPLAY_WIDTH] = '\0';
+			return(temp_line);
+		} else {
+      /* Try to fill with some of the previous line */
+      return(fill_line(lineno-1,offset + PERT_DISPLAY_WIDTH));
+    }
+	}
+	if (strlen(lines[lineno]) == 0) {
+    /* If we are on the first line */
+    if (lineno == 0) {
+    	/* No data to display - return blanks to clear line */
+			memset(temp_line,' ',PERT_DISPLAY_WIDTH);
+			temp_line[PERT_DISPLAY_WIDTH] = '\0';
+			return(temp_line);
+		} else {
+			/* Try to fill with some of the previous line */
+			return(fill_line(lineno-1,offset + PERT_DISPLAY_WIDTH));
+			}
+	}
+
+	/* If there are PERT_DISPLAY_WIDTH or less chars to display */
+	if (strlen(lines[lineno]) < 21) {
+		/* If this is the first time called */
+   	if (offset == 0) {
+				/* Return the line and pad with blanks */
+		    strcpy(temp_line,(lines[lineno]));
+		    for (i=strlen(lines[lineno]); i<PERT_DISPLAY_WIDTH; i++)
+			    temp_line[i] = ' ';
+		    temp_line[PERT_DISPLAY_WIDTH] = '\0';
+		    return(temp_line);
+		} else { /* not the first time called - return blanks */
+        /* No data to display - return blanks to clear line */
+		    memset(temp_line,' ',PERT_DISPLAY_WIDTH);
+        temp_line[PERT_DISPLAY_WIDTH] = '\0';
+		    return(temp_line);
+        }
+		}
+
+    /* If there's not enough data to display */
+    if (strlen(lines[lineno]) <= offset) {
+        /* No data to display - return blanks to clear line */
+        memset(temp_line,' ',PERT_DISPLAY_WIDTH);
+        temp_line[PERT_DISPLAY_WIDTH] = '\0';
+        return(temp_line);
+    }
+        
+	/* If there are more than PERT_DISPLAY_WIDTH chars to display,
+	 * display PERT_DISPLAY_WIDTH starting at the last position
+	 * displayed */
+	temp_pos = pos[lineno] + offset;
+	while (temp_pos > strlen(lines[lineno]))
+		temp_pos = temp_pos - strlen(lines[lineno]);
+	if (temp_pos < 0)
+		temp_pos = 0;
+
+	/* For each char in the temp line */
+	for (i=0; i<PERT_DISPLAY_WIDTH; i++) {
+		/* If we are pointing beyond the original line,
+		 * start over at the beginning */
+		if (temp_pos >= strlen(lines[lineno]))
+			temp_pos = 0;
+			/* Copy over 1 char from the original line */
+      temp_line[i] = lines[lineno][temp_pos];
+			temp_pos++;
+	}
+
+	/* Increment where we start */
+	pos[lineno]++;
+
+	/* If we went past the end of the line,
+	 * start over */
+	if (pos[lineno] >= strlen(lines[lineno]))
+		pos[lineno] = 0;
+
+	/* Force an end of line */
+	temp_line[PERT_DISPLAY_WIDTH] = '\0';
+	return(temp_line);
+}
+
+int main(int argc, char *argv[]) {
+	int stop_indicated;
+	int processing_command;
+	int i;
+	time_t now;
+
+	/* Read config file */
+	if (argc > 1) {
+		/* We have an argument - try to read the config file
+		 * using that argument */
+	  if (!read_config(argv[1])) {
+			/* If we failed - try to read the default config file */
+	  	read_config(CONFIG_FILE_NAME);
+		}
+	} else {
+		/* No parm - use default config file */
+	  read_config(CONFIG_FILE_NAME);
+	}
+
+#ifdef DEBUG
+	printf("Device = %s\n",device_name);
+	printf("Fifo = %s\n",fifo_name);
+	printf("Delay time = %d\n",delay_time);
+	printf("Char Delay time = %d\n",char_delay);
+#endif
+
+	/* Initialize arrays */
+	time(&now);
+	for(i=0;i<4;i++) {
+		lines[i] = NULL;
+		refresh_time[i] = now;
+		timeout[i] = 0;
+		pos[i] = 0;
+	}
+
+	/* Initialize the Pertelian */
+  display_init(device_name);
+
+	/* The backlight is on when the program starts */
+	backlight_status = 1;
+	backlight(1);
+
+	/* Go daemon */
+	if (daemon_init() != 0) {
+		fprintf(stderr,"Failed to go daemon\n");
+		return(1);
+		}
+
+	/* Try to open the fifo */
+	if (!open_fifo(fifo_name)) {
+		fprintf(stderr,"Error creating FIFO: %s\n",fifo_name);
+		return(1);
+	}
+
+	/* Loop until we are told to stop */
+	stop_indicated = 0;
+	while (!stop_indicated) {
+	 	time(&now);
+
+		/* Get the command from the fifo */
+	  processing_command = 1;
+		while (processing_command) {
+			switch (get_command()) {
+				case backlight_on:
+					backlight(1);
+					backlight_status = 1;
+					break;
+				case backlight_off:
+					backlight(0);
+					backlight_status = 0;
+					break;
+				case stop:
+					stop_indicated = 1;
+					break;
+				case line1:
+					process_line(0);
+					break;
+				case line2:
+					process_line(1);
+					break;
+				case line3:
+					process_line(2);
+					break;
+				case line4:
+					process_line(3);
+					break;
+				case delay_time_cmd:
+					process_delay_time();
+					break;
+				case char_delay_cmd:
+					process_char_delay();
+					break;
+				case backlight_mgt_on:
+					backlight_mgt = 1;
+					break;
+				case backlight_mgt_off:
+					backlight_mgt = 0;
+					break;
+				default:
+					processing_command = 0; /* no command to process */
+					break;
+			}
+		}
+
+#ifdef DEBUG
+		printf("main:processed command\n");
+#endif
+
+		/* Refresh the display */
+		for(i=0;i<4;i++) {
+			/* If the line has data */
+			if (lines[i] != NULL) {
+				/* If we are supposed to time the line out */
+				if (timeout[i] > 0) {
+					/* If the line is old */
+					if (difftime(now,refresh_time[i]) > timeout[i]) {
+						/* Get rid of the line */
+						free(lines[i]); /* Memory leaks are bad */
+						lines[i] = NULL;
+					}
+				}
+			}
+
+			/* Write out the line to the display */
+			wrtln(i,fill_line(i,0));
+		}
+
+		/* If there was no data to display */
+		if (!data_to_display()) {
+			/* Let's put the date/time on the display */
+			display_date_time();
+
+			/* If the backlight is on,
+			 * and backight mgt is on,
+			 * turn the backlight off */
+			if ((backlight_status == 1)
+			&&  (backlight_mgt == 1))	{
+				backlight(0);
+				backlight_status=0;
+			}
+		} else {
+			/* There was data to display.
+			 * If the backlight is off,
+			 * and backlight mangement is on,
+			 * turn the backlight on */
+			if ((backlight_status == 0)
+			&&  (backlight_mgt ==1))	{
+				backlight(1);
+				backlight_status=1;
+			}
+		}
+
+		/* Pause for a bit */
+  	sleep_us(delay_time);
+	}
+
+	/* Display a useful message on the Pertelian */
+	wrtln(0,"                    ");
+	wrtln(1,"pertd daemon stopped");
+	wrtln(2,"                    ");
+	wrtln(3,"                    ");
+
+	/* Clean up the connect to the Pertelian */
+	display_close();
+
+	/* Done - clean up */
+	close_fifo(fifo_name);
+
+	return(0);
+}

+ 21 - 0
pertd2.conf

@@ -0,0 +1,21 @@
+# Configuration file for pertd2
+
+# Delay (in milliseconds) pertd will wait between scrolling the data
+# 1000000 = 1 second
+# default = 500000 = 1/2 second
+delay_time=1000000
+
+# Delay (in milliseconds) pertd will put between each character.
+# Newer systems should set this to 0.
+# If that produces garbled output on your display, change it to 1.
+char_delay=0
+
+# The USB device the Pertelian is on
+device=/dev/ttyUSB0
+
+# Backlight managment.  0 = you manage the backlight
+#                       1 = pertd2 manages the backlight
+backlight_mgt=1
+
+# Fifo file name
+fifo_name=/tmp/pertd2.fifo

+ 97 - 0
sendcommands/sendcommand.c

@@ -0,0 +1,97 @@
+/* This is an example of how to write a program that sends commands to 
+ * the pertd2 deamon */
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+int main() {
+	FILE *fifo;
+	char *fifo_name = "/tmp/pertd2.fifo";
+	struct stat fifo_stat;
+
+	/* Let's get some info on the fifo */
+	if (stat(fifo_name,&fifo_stat) != 0) {
+		printf("Unable to stat %s\n",fifo_name);
+		/* If we can't stat the fifo, it's not there - we are done */
+		return(1);
+	}
+
+	/* At this point, you can further interrigate the stat struct of the
+	 * fifo to see if you can write to it, etc.  Such code is left to
+	 * you do to as an exercise 8-) */
+
+	/* Since the fifo exists, let's open it */
+	if ((fifo = fopen(fifo_name,"a")) == 0) {
+		printf("Unable to open %s\n",fifo_name);
+		/* Obviously, we can't write to it if we can't open it.
+		 * Note that we do need to check for existance first.  If the 
+		 * file doesn't exist, the fopen will create it, which will
+		 * cause problems when pertd2 starts up. */
+		return(1);
+	}
+
+	/* We can use 1 big printf to print the command */
+	fprintf(fifo,"line4\n30\n==EOD==\nThis is line 4\n==EOD==\n");
+
+	/* Or we can do it with several printf's */
+	fprintf(fifo,"line2\n");
+	fprintf(fifo,"30\n");
+	fprintf(fifo,"==EOD==\n");
+	fprintf(fifo,"This is line 2\n");
+	fprintf(fifo,"==EOD==\n");
+
+  /* Or by using fputs */
+	fputs("line3\n",fifo);
+	fputs("30\n",fifo);
+	fputs("==EOD==\n",fifo);
+	fputs("This is line 3\n",fifo);
+	fputs("==EOD==\n",fifo);
+
+	fputs("line1\n",fifo);
+	fputs("0\n",fifo);
+	fputs("==EOD==\n",fifo);
+	fputs("This is a really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("really, really, really,\n",fifo);
+	fputs("long line\n",fifo);
+	fputs("==EOD==\n",fifo);
+
+	fclose(fifo);
+}

+ 45 - 0
sleep_us.c

@@ -0,0 +1,45 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <stddef.h>
+
+/* From Advanced Programming in the Unix Environment
+ * by W. Richard Stevens. */
+
+/* Sleep for a number of microseconds */
+void sleep_us1(unsigned int nusecs) {
+	usleep(nusecs);
+}
+
+void sleep_us(unsigned int nusecs) {
+	struct timeval tval;
+
+	/* If we don't need to sleep then leave */
+	if (nusecs < 1) {
+		return;
+	}
+
+	tval.tv_sec = nusecs / 1000000L;
+	tval.tv_usec = nusecs % 1000000L;
+	select(0,NULL, NULL, NULL, &tval);
+}
+
+void sleep_us_loop(unsigned int nusecs) {
+	volatile int i;
+	for (i = 0; i < nusecs*200; i++) {
+		}
+}
+
+void sleep_us_ns(unsigned int nusecs) {
+	struct timespec tval;
+	/* Input is number of microseconds.
+	 * 1 second = 1000000 microseconds.
+	 * 1 second = 1000000000 nanoseconds
+	 */
+	tval.tv_sec = nusecs / 1000000L;
+	/* Note that nsec = microseconds */
+	tval.tv_nsec = nusecs % 1000000L;
+	/* So we need to conert it to nanoseconds */
+	tval.tv_nsec = tval.tv_nsec * 1000;
+	nanosleep(&tval,&tval);
+}

+ 1 - 0
sleep_us.h

@@ -0,0 +1 @@
+void sleep_us(unsigned int nusecs);

+ 22 - 0
test1.dat

@@ -0,0 +1,22 @@
+line1
+13
+==EOD==
+Good day and welcome to
+line 1
+==EOD==
+line2
+30
+==EOD==
+This is line2
+==EOD==
+line3
+0
+==EOD==
+This line should never expire.
+==EOD==
+line4
+3
+==EOD==
+Short lived line
+==EOD==
+

+ 11 - 0
test2.dat

@@ -0,0 +1,11 @@
+line1
+12
+==EOD==
+19:26:59
+==EOD==
+line2
+12
+==EOD==
+Testing
+==EOD==
+

+ 8 - 0
test3.dat

@@ -0,0 +1,8 @@
+line1
+3
+==EOD==
+hello
+there this is a test
+this is only a test
+==EOD==
+

+ 4 - 0
testrun

@@ -0,0 +1,4 @@
+#!/bin/bash
+rm -f /tmp/pertd2.fifo
+make debug
+./pertd2 ./pertd2.conf