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 }