1 /* Simple daemon to communicate with Newhaven I2C LCD module.
3 * This is a trivial interface for Newhaven LCD module connected to a Linux machine
4 * over an I2C interface. Any standard Linux I2C bus should work (including USB I2C
5 * adapters that correctly hook into the i2c kernel driver framework).
7 * Opens a named pipe at /dev/lcd which can be written to by other applications.
8 * Text written to this file will appear on the LCD.
9 * Send a bell ('\b')to clear the screen.
11 * Tested with NHD-0420D3Z-FL-GBW (Digikey part number NHD-0420D3Z-FL-GBW-ND).
12 * http://www.newhavendisplay.com/specs/NHD-0420D3Z-FL-GBW.pdf
13 * http://www.newhavendisplay.com/index.php?main_page=product_info&cPath=253&products_id=922
15 * FIXME how to do other commands etc.
16 * FIXME some rate limiting should be added... the I2C controller on the LCD
17 * module gets confused if you write to it too fast.
20 * To enable I2C mode of LCD, Pads of R1 must be jumpered on back of LCD
21 * See the manual for the LCD module.
23 * ./lcdd -a 0x47 -d /dev/i2c-4 -f /dev/lcd -n
25 * Hugo Vincent, June 2009 - https://github.com/hugovincent/i2c-lcdd
26 * Daemonization based on code by Peter Lombardo <peter@lombardo.info>, 2006
30 #define _GNU_SOURCE // for strsignal()
37 #include <sys/types.h>
39 #include <sys/ioctl.h>
47 #include <linux/i2c-dev.h>
49 #define DAEMON_NAME "lcdd"
50 #define PID_FILE "/var/run/lcdd.pid"
56 static int daemonize = 1, running = 1, have_lcd = 1;
57 static jmp_buf my_jmp_buf;
59 #define LOG_MSG(format, ...) if (daemonize == 1) \
60 syslog(LOG_INFO, format, ##__VA_ARGS__); \
62 printf("i2c-lcdd: " format "\n", ##__VA_ARGS__);
64 void usage(int argc, char **argv)
68 printf("Usage: %s -a 0x50 -d /dev/i2c-3 -f /dev/lcd -n -?\n", argv[0]);
69 printf(" Options:\n");
70 printf(" -a\tI2C slave address (in hex) for LCD module\n");
71 printf(" -d\tI2C device\n");
72 printf(" -f\tnamed pipe for input\n");
73 printf(" -n\tDon't daemonize.\n");
74 printf(" -?\tShow this help screen.\n");
78 void signal_handler(int sig)
80 LOG_MSG("received signal %s - exiting", strsignal(sig));
84 void i2c_send(int fd, uint8_t *buf, int count)
88 if (write(fd, buf, count) != count)
90 LOG_MSG("error writing to I2C: %s", strerror(errno));
91 longjmp(my_jmp_buf, EXIT_FAILURE);
98 static char tmp[1024];
99 strncpy(tmp, buf, count);
101 printf("I2C send: \"%s\"\n", tmp);
106 uint8_t linenum_to_cursor(int linenum)
126 #define min(a, b) ((a <= b) ? a : b)
128 void handle_pipe_input(int i2c_fd, char *buffer, int size)
130 // FIXME this should handle escape sequences (other than \b, \n) too
132 static int line_number = 0;
135 if (size >= 1 && buffer[0] == '\b')
137 uint8_t bytes[] = {0xFE, 0x51};
138 i2c_send(i2c_fd, bytes, sizeof(bytes));
143 // Split input into lines
144 char *line = strtok(buffer, "\n");
147 // Because of weird cursor positioning on the LCD module, if we write
148 // more than 20 characters on line 1 it'll flow over onto line 3, (or
149 // off line 2 on to line 4). Thus, we limit lines to 20 chars.
151 i2c_send(i2c_fd, line, min(strlen(line), 20));
152 line_number = (line_number < 3) ? (line_number + 1) : 0;
154 // Go to specified line
155 uint8_t bytes[] = {0xFE, 0x45, 0x0};
156 bytes[2] = linenum_to_cursor(line_number);
157 i2c_send(i2c_fd, bytes, sizeof(bytes));
160 line = strtok(NULL, "\n");
164 int main(int argc, char **argv)
166 char device[64] = "/dev/i2c-3", fifo_name[64] = "/dev/lcd";
167 int addr = LCD_ADDR, addr_sscanf_ret = 1;
169 // Setup signal handling before we start
170 signal(SIGHUP, signal_handler);
171 siginterrupt(SIGHUP, 1);
172 signal(SIGQUIT, signal_handler);
173 siginterrupt(SIGQUIT, 1);
174 signal(SIGTERM, signal_handler);
175 siginterrupt(SIGTERM, 1);
176 signal(SIGINT, signal_handler);
177 siginterrupt(SIGINT, 1);
180 while( (c = getopt(argc, argv, "a:d:f:n?")) != -1) {
183 addr_sscanf_ret = sscanf(optarg, "0x%x", &addr);
186 strncpy(device, optarg, sizeof(device));
189 strncpy(fifo_name, optarg, sizeof(fifo_name));
202 // Setup syslog logging
205 setlogmask(LOG_UPTO(LOG_INFO));
206 openlog(DAEMON_NAME, LOG_CONS, LOG_USER);
208 LOG_MSG("daemon starting up");
210 // Check sscanf above worked (delayed until after syslog is initialized)
211 if (addr_sscanf_ret != 1)
212 LOG_MSG("supplied address invalid, using 0x%02x", addr);
214 // Our process ID and Session ID
219 // Fork off the parent process
223 LOG_MSG("failed to fork daemon process");
227 // If we got a good PID, then we can exit the parent process
231 // Change the file mode mask
234 // Create a new SID for the child process
238 LOG_MSG("failed to set daemon process SID");
242 // Change the current working directory
243 if ((chdir("/")) < 0)
245 LOG_MSG("failed to change daemon working directory");
249 // Close out the standard file descriptors
252 close(STDOUT_FILENO);
254 close(STDERR_FILENO);
257 //************************************************************************
259 addr >>= 1; // to suit LCD addressing sematics of Newhaven LCD modules
261 // Initialise FIFO on which to accept input
262 if (mkfifo(fifo_name, S_IRWXU) < 0)
266 LOG_MSG("error creating named pipe for input: %s", strerror(errno));
271 LOG_MSG("warning, named pipe already exists: %s", strerror(errno));
272 // FIXME unlink and reopen
276 // Connect to I2C LCD and set slave address
277 int i2c_fd = open(device, O_RDWR);
280 LOG_MSG("error opening device: %s", strerror(errno));
284 if (ioctl(i2c_fd, I2C_SLAVE, addr) < 0)
286 LOG_MSG("error setting I2C slave address: %s", strerror(errno));
292 if (setjmp(my_jmp_buf) == EXIT_FAILURE)
297 LOG_MSG("could not open I2C LCD, printing to stdout for debugging");
301 LOG_MSG("using Nehaven LCD attached to %s at real address 0x%x", device, addr);
304 uint8_t bytes[] = {0xFE, 0x41}; // LCD on
305 i2c_send(i2c_fd, bytes, sizeof(bytes));
306 bytes[1] = 0x51; // LCD clear
307 i2c_send(i2c_fd, bytes, sizeof(bytes));
309 uint8_t *string = (uint8_t*)" Initialising...";
310 i2c_send(i2c_fd, string, strlen((char*)string));
315 struct pollfd fds = { .events = POLLIN };
317 // Main Loop: read on named pipe, output on LCD
323 LOG_MSG("waiting for connection on %s...", fifo_name);
325 fifo_fd = open(fifo_name, O_RDONLY); // this will block until other end of pipe is opened
330 LOG_MSG("error opening named pipe for input: %s", strerror(errno));
335 LOG_MSG("connected, accepting input on %s", fifo_name);
339 // Wait for input on the named pipe
340 int poll_ret = poll(&fds, 1, 1000);
341 if (poll_ret == 1 && fds.revents | POLLIN)
343 memset(&buffer, 0, sizeof(buffer));
344 int bytes_read = read(fifo_fd, &buffer, sizeof(buffer));
346 // Write input to LCD
348 handle_pipe_input(i2c_fd, buffer, bytes_read);
349 else if (bytes_read == 0)
352 LOG_MSG("named pipe closed");
359 LOG_MSG("error reading from named pipe: %s", strerror(errno));
363 else if (poll_ret < 0 && running)
365 LOG_MSG("error waiting for input on named pipe: %s", strerror(errno));
370 // Timeout in poll(), no need to worry (timeout is only enabled as insurance anyway)
374 LOG_MSG("daemon exiting cleanly");
391 LOG_MSG("daemon exiting with failure");