pi_clock

Crude and unpolished clock for Raspberry Pi
git clone https://git.e1e0.net/pi_clock.git
Log | Files | Refs | README | LICENSE

clock.c (6850B)


      1 /*
      2  * Pi I2C LCD display clock.
      3  *
      4  * Copyright (c) 2020 Paco Esteban <paco@e1e0.net>
      5  *
      6  * Permission to use, copy, modify, and distribute this software for any
      7  * purpose with or without fee is hereby granted, provided that the above
      8  * copyright notice and this permission notice appear in all copies.
      9  *
     10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  */
     18 
     19 #define _GNU_SOURCE
     20 
     21 #include <linux/limits.h>
     22 #include <err.h>
     23 #include <errno.h>
     24 #include <stdio.h>
     25 #include <stdlib.h>
     26 #include <string.h>
     27 #include <time.h>
     28 #include <unistd.h>
     29 #include <wiringPi.h>
     30 
     31 #include "lcd_i2c.h"
     32 
     33 #define MAX_LINE_LEN	    17  /* 16 char display + '\0' */
     34 #define LCD_LINES	    2
     35 #define I2C_ADDRESS	    0x3f
     36 #define BUTTON1		    0
     37 #define BUTTON2		    2
     38 #define DEF_R_INTER	    120 /* default 30s (250ms * 120) */
     39 #define DEF_SLEEP_INTER	    250
     40 #define DEF_BL_BEFORE	    0
     41 #define DEF_BL_AFTER	    7
     42 #define TAG_MAX_LEN	    7
     43 
     44 #define PIC_VERSION	    "v0.6.1"
     45 
     46 lcd_i2c_t	    *init_lcd(void);
     47 char *		     setmonth(int);
     48 char *		     setwday(int);
     49 char *		     getExtTag(char *);
     50 void		     usage();
     51 int		     init_pins(void);
     52 int		     print_line_lcd(lcd_i2c_t *, int, char *);
     53 
     54 int
     55 main(int argc, char *argv[])
     56 {
     57 	time_t	    t;
     58 	struct tm  *tm;
     59 	char	   *linebuff = NULL, *str_wday = NULL, *str_month = NULL,
     60 		   *extFile = NULL, *script = NULL, *extTag = NULL, *tz = NULL;
     61 	long	    gmt_offset;
     62 	uint8_t	    backlight_status = 0, vflag = 0, eflag = 0;
     63 	int	    readCount = 0, tmp_on_cycles = 0, ch;
     64 	int	    bl_before = DEF_BL_BEFORE, bl_after = DEF_BL_AFTER,
     65 		    readInterval = DEF_R_INTER, sleepInterval = DEF_SLEEP_INTER;
     66 
     67 	while ((ch = getopt(argc, argv, "S:a:b:e:r:s:t:v")) != -1) {
     68 		switch (ch) {
     69 		case 'S':
     70 			sleepInterval = atoi(optarg);
     71 			break;
     72 		case 'a':
     73 			bl_after = atoi(optarg);
     74 			break;
     75 		case 'b':
     76 			bl_before = atoi(optarg);
     77 			break;
     78 		case 'e':
     79 			eflag = 1;
     80 			if (strlen(optarg) > PATH_MAX)
     81 				err(1, "path too long");
     82 			extFile = strdup(optarg);
     83 			break;
     84 		case 'r':
     85 			readInterval = atoi(optarg);
     86 			break;
     87 		case 's':
     88 			if (strlen(optarg) > PATH_MAX)
     89 				err(1, "path too long");
     90 			script = strdup(optarg);
     91 			break;
     92 		case 't':
     93 			tz = strdup(optarg);
     94 			break;
     95 		case 'v':
     96 			vflag = 1;
     97 			break;
     98 		default:
     99 			usage();
    100 		}
    101 	}
    102 
    103 	if (vflag) {
    104 		fprintf(stderr, "clock %s\n", PIC_VERSION);
    105 		return 0;
    106 	}
    107 
    108 	lcd_i2c_t *lcd = init_lcd();
    109 	if (lcd == NULL)
    110 		errx(1,
    111 		    "Error intialising PCF8574 at address i2c 0x%02x: %s\n",
    112 		    I2C_ADDRESS,
    113 		    strerror(errno)
    114 		);
    115 
    116 	if (init_pins() == -1)
    117 		errx(1, "Unable to setup wiringPi: %s\n", strerror(errno));
    118 
    119 	/* force reading of the tag file on init (if present) */
    120 	readCount = readInterval;
    121 
    122 	for (;;) {
    123 		t = time(NULL);
    124 		tm = localtime(&t);
    125 
    126 		if (tm->tm_hour >= bl_before && tm->tm_hour <= bl_after) {
    127 			if (backlight_status
    128 			    && !tmp_on_cycles
    129 			    && (NULL == getenv("TZ"))) {
    130 				LCD_I2C_BACKLIGHT_OFF(lcd);
    131 				backlight_status = 0;
    132 			}
    133 		} else {
    134 			if (!backlight_status) {
    135 				LCD_I2C_BACKLIGHT_ON(lcd);
    136 				backlight_status = 1;
    137 			}
    138 		}
    139 
    140 		gmt_offset = tm->tm_gmtoff / 3600;
    141 
    142 		if (eflag && strlen(extFile) > 2) {
    143 			/* read external file only interval * sleep */
    144 			if (readCount == readInterval) {
    145 				free(extTag);
    146 				if ((extTag = getExtTag(extFile)) == NULL)
    147 					err(1, "Error getting external tag\n");
    148 				readCount = 0;
    149 			}
    150 			readCount++;
    151 			asprintf(&linebuff, " %02d:%02d:%02d %-6s",
    152 			    tm->tm_hour, tm->tm_min, tm->tm_sec, extTag);
    153 		} else {
    154 			asprintf(&linebuff, " %02d:%02d:%02d GMT%+ld ",
    155 			    tm->tm_hour, tm->tm_min, tm->tm_sec, gmt_offset);
    156 		}
    157 
    158 		if (print_line_lcd(lcd, 0, linebuff) == -1)
    159 			err(1, "cannot print line0\n");
    160 
    161 		str_wday = setwday(tm->tm_wday);
    162 		if (str_wday == NULL)
    163 			err(1, "str_day");
    164 		str_month = setmonth(tm->tm_mon);
    165 		if (str_month == NULL)
    166 			err(1, "str_month");
    167 		asprintf(&linebuff, "%s, %s %2d %4d",
    168 		    str_wday, str_month, tm->tm_mday, 1900+tm->tm_year);
    169 
    170 		if (print_line_lcd(lcd, 1, linebuff) == -1)
    171 			err(1, "cannot print line1\n");
    172 
    173 		free(str_wday);
    174 		free(str_month);
    175 
    176 		if (digitalRead(BUTTON1) == LOW) {
    177 			if (backlight_status) {
    178 				if (script != NULL) {
    179 					fprintf(stderr, "calling '%s'\n",
    180 					    script);
    181 					system(script);
    182 				} else {
    183 					if (extFile != NULL) {
    184 						eflag = !eflag;
    185 					}
    186 				}
    187 			} else {
    188 				LCD_I2C_BACKLIGHT_ON(lcd);
    189 				backlight_status = 1;
    190 				tmp_on_cycles = 16;
    191 			}
    192 			delay(250);
    193 		}
    194 		if (digitalRead(BUTTON2) == LOW) {
    195 			eflag = 0;
    196 			if (tz == NULL)
    197 				tz = strdup("PST8PDT");
    198 			if (getenv("TZ"))
    199 				unsetenv("TZ");
    200 			else
    201 				setenv("TZ", tz, 1);
    202 			tzset();
    203 			delay(250);
    204 		}
    205 
    206 		if (tmp_on_cycles > 0)
    207 			tmp_on_cycles--;
    208 
    209 		delay(sleepInterval);
    210 	}
    211 
    212 	free(extFile);
    213 	free(script);
    214 	free(lcd);
    215 
    216 	return 0;
    217 }
    218 
    219 char *
    220 setmonth(int month)
    221 {
    222 	char months[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
    223 		"Aug", "Sep", "Oct", "Nov", "Dec" };
    224 	if (month > 11) {
    225 		return NULL;
    226 	}
    227 	return strdup(months[month]);
    228 }
    229 
    230 char *
    231 setwday(int wday)
    232 {
    233 	char days[7][4] = { "Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat" };
    234 	if (wday > 6) {
    235 		return NULL;
    236 	}
    237 	return strdup(days[wday]);
    238 }
    239 
    240 char *
    241 getExtTag(char *tagFile) {
    242 	FILE *fp;
    243 	char *buff = malloc(TAG_MAX_LEN);
    244 	int len = 0;
    245 
    246 	fp = fopen(tagFile, "r");
    247 	if (fp == NULL) {
    248 		return NULL;
    249 	}
    250 
    251 	if (fgets(buff, TAG_MAX_LEN, fp) == NULL)
    252 		return NULL;
    253 
    254 	fclose(fp);
    255 
    256 	len = strlen(buff);
    257 	/* if buff < TAG_MAX_LEN we remove \n */
    258 	if( buff[len-1] == '\n' )
    259 		buff[len-1] = 0x20;
    260 
    261 	return buff;
    262 }
    263 
    264 /* Print usage and exit  */
    265 void
    266 usage(void)
    267 {
    268 	fprintf(stderr,
    269 	    "usage: clock [-v] [-b <hour>] [-a <hour>] [-s <script>] \n "
    270 	    "\t[-S <miliseconds>] [-r <readCount>] [-e <tagfile>]\n"
    271 	    "\t[-t <alt_timezone>]\n");
    272 	exit(1);
    273 }
    274 
    275 lcd_i2c_t *
    276 init_lcd(void)
    277 {
    278 	lcd_i2c_t *lcd = malloc(sizeof(lcd_i2c_t));
    279 	if (lcd_i2c_setup(lcd, I2C_ADDRESS) == -1){
    280 		return NULL;
    281 	}
    282 
    283 	lcd_i2c_init(lcd);
    284 	lcd_i2c_clear(lcd);
    285 
    286 	return lcd;
    287 }
    288 
    289 int
    290 init_pins(void)
    291 {
    292 	if (wiringPiSetup() < 0)
    293 		return -1;
    294 
    295 	pinMode(BUTTON1, INPUT);
    296 	pullUpDnControl(BUTTON1, PUD_UP);
    297 	pinMode(BUTTON2, INPUT);
    298 	pullUpDnControl(BUTTON2, PUD_UP);
    299 
    300 	return 0;
    301 }
    302 
    303 int
    304 print_line_lcd(lcd_i2c_t *lcd, int line, char *content)
    305 {
    306 	if (strlen(content) > MAX_LINE_LEN)
    307 		return -1;
    308 
    309 	if (line < 0 || line > LCD_LINES)
    310 		return -1;
    311 
    312 	lcd->x=0;
    313 	lcd->y=line;
    314 	lcd_i2c_gotoxy(lcd, lcd->x, lcd->y);
    315 	lcd_i2c_puts(lcd, content);
    316 
    317 	return 0;
    318 }