Utility to set the time on a Hayes Stack Chronograph

Initial import

jcs.org bb6d2e5c

+252
+2
.gitignore
··· 1 + chronosync 2 + chronosync.o
+13
LICENSE
··· 1 + Copyright (c) 2021 joshua stein <jcs@jcs.org> 2 + 3 + Permission to use, copy, modify, and distribute this software for any 4 + purpose with or without fee is hereby granted, provided that the above 5 + copyright notice and this permission notice appear in all copies. 6 + 7 + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+32
Makefile
··· 1 + # vim:ts=8 2 + 3 + CC ?= cc 4 + CFLAGS ?= -O2 5 + CFLAGS += -Wall -Wunused -Wmissing-prototypes -Wstrict-prototypes -Wunused 6 + 7 + PREFIX ?= /usr/local 8 + BINDIR ?= $(PREFIX)/bin 9 + MANDIR ?= $(PREFIX)/man/man1 10 + 11 + INSTALL_PROGRAM ?= install -s 12 + INSTALL_DATA ?= install 13 + 14 + PROG = chronosync 15 + OBJS = chronosync.o 16 + 17 + all: $(PROG) 18 + 19 + $(PROG): $(OBJS) 20 + $(CC) $(OBJS) $(LDPATH) $(LIBS) -o $@ 21 + 22 + $(OBJS): *.c 23 + $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ 24 + 25 + install: all 26 + mkdir -p $(DESTDIR)$(BINDIR) 27 + $(INSTALL_PROGRAM) $(PROG) $(DESTDIR)$(BINDIR) 28 + 29 + clean: 30 + rm -f $(PROG) $(OBJS) 31 + 32 + .PHONY: all install clean
+37
README.md
··· 1 + ## chronosync 2 + 3 + A small utility to set the clock on a 4 + [Hayes Stack Chronograph](http://atari8bitads.blogspot.com/2017/06/theres-no-better-time.html) 5 + over its serial port. 6 + 7 + ### Synopsis 8 + 9 + chronosync [-d] [-s serial speed] <serial device> 10 + 11 + ### Compiling 12 + 13 + Compile with a BSD Make: 14 + 15 + $ make 16 + 17 + Install: 18 + 19 + # make install 20 + 21 + Run and pass argument of the serial device connected to the Chronograph: 22 + 23 + # chronosync /dev/cua02 24 + 25 + The computer's local time is sent to the Chronograph. 26 + 27 + ### Notes 28 + 29 + Since the Chronograph only supports setting the time to minute precision with 30 + `ATST`, `chronosync` will sleep until zero seconds of the next minute before 31 + sending the time. 32 + 33 + The clock on the Chronograph will probably only need to be set once per day to 34 + stay accurate, so running this from cron once a day should suffice: 35 + 36 + # sync time at 2am 37 + 1 59 * * * /usr/local/bin/chronosync /dev/cua02
+168
chronosync.c
··· 1 + /* 2 + * Copyright (c) 2021 joshua stein <jcs@jcs.org> 3 + * 4 + * Permission to use, copy, modify, and distribute this software for any 5 + * purpose with or without fee is hereby granted, provided that the above 6 + * copyright notice and this permission notice appear in all copies. 7 + * 8 + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 + */ 16 + 17 + #include <err.h> 18 + #include <errno.h> 19 + #include <fcntl.h> 20 + #include <poll.h> 21 + #include <stdio.h> 22 + #include <stdlib.h> 23 + #include <string.h> 24 + #include <termios.h> 25 + #include <time.h> 26 + #include <unistd.h> 27 + #include <vis.h> 28 + #include <sys/ioctl.h> 29 + #include <sys/time.h> 30 + #include <sys/types.h> 31 + 32 + static int debug = 0; 33 + static char vbuf[512]; 34 + static int serial_fd = -1; 35 + 36 + void usage(const char *); 37 + void serial_setup(int, speed_t); 38 + size_t serial_read(void *, size_t); 39 + size_t serial_write(const void *, size_t); 40 + 41 + void 42 + usage(const char *progname) 43 + { 44 + fprintf(stderr, "usage: %s [-d] [-s serial speed] <serial device>\n", 45 + progname); 46 + exit(1); 47 + } 48 + 49 + void 50 + serial_setup(int fd, speed_t speed) 51 + { 52 + struct termios tty; 53 + 54 + if (ioctl(fd, TIOCEXCL) != 0) 55 + err(1, "ioctl(TIOCEXCL)"); 56 + if (tcgetattr(fd, &tty) < 0) 57 + err(1, "tcgetattr"); 58 + 59 + tty.c_cflag |= CREAD; 60 + tty.c_cflag &= ~CSIZE; 61 + tty.c_cflag |= CS8; /* 8-bit characters */ 62 + tty.c_cflag &= ~PARENB; /* no parity bit */ 63 + tty.c_cflag &= ~CSTOPB; /* only need 1 stop bit */ 64 + 65 + /* setup for non-canonical mode */ 66 + tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); 67 + tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); 68 + tty.c_oflag &= ~OPOST; 69 + 70 + /* fetch bytes as they become available */ 71 + tty.c_cc[VMIN] = 1; 72 + tty.c_cc[VTIME] = 1; 73 + 74 + cfsetspeed(&tty, speed); 75 + 76 + if (tcsetattr(fd, TCSAFLUSH, &tty) != 0) 77 + err(1, "tcsetattr"); 78 + } 79 + 80 + size_t 81 + serial_read(void *buf, size_t nbytes) 82 + { 83 + size_t ret; 84 + 85 + ret = read(serial_fd, buf, nbytes); 86 + 87 + if (debug && ret > 0) { 88 + strvisx(vbuf, buf, ret, VIS_NL | VIS_CSTYLE | VIS_OCTAL); 89 + printf("<<< %s\n", vbuf); 90 + } 91 + 92 + return ret; 93 + } 94 + 95 + size_t 96 + serial_write(const void *buf, size_t nbytes) 97 + { 98 + if (debug) { 99 + strvisx(vbuf, buf, nbytes, VIS_NL | VIS_CSTYLE | VIS_OCTAL); 100 + printf(">>> %s\n", vbuf); 101 + } 102 + 103 + return write(serial_fd, buf, nbytes); 104 + } 105 + 106 + int 107 + main(int argc, char *argv[]) 108 + { 109 + struct timeval tp; 110 + struct tm *tm; 111 + char *serial_dev = NULL; 112 + char buf[64]; 113 + int serial_speed = B1200; 114 + int ch; 115 + 116 + if (argc == 1) 117 + usage(argv[0]); 118 + 119 + while ((ch = getopt(argc, argv, "ds:")) != -1) { 120 + switch (ch) { 121 + case 'd': 122 + debug = 1; 123 + break; 124 + case 's': 125 + serial_speed = (unsigned)strtol(optarg, NULL, 0); 126 + if (errno) 127 + err(1, "invalid serial port speed value"); 128 + break; 129 + default: 130 + usage(argv[0]); 131 + } 132 + } 133 + argc -= optind; 134 + argv += optind; 135 + 136 + if (argc == 0) 137 + usage(argv[0]); 138 + 139 + serial_dev = argv[0]; 140 + serial_fd = open(serial_dev, O_RDWR|O_SYNC); 141 + if (serial_fd < 0) 142 + err(1, "open %s", serial_dev); 143 + 144 + serial_setup(serial_fd, serial_speed); 145 + 146 + for (;;) { 147 + gettimeofday(&tp, NULL); 148 + tm = localtime(&tp.tv_sec); 149 + if (debug) 150 + printf("currently: %02d:%02d:%02d\n", tm->tm_hour, 151 + tm->tm_min, tm->tm_sec); 152 + if (tm->tm_sec == 0) { 153 + if (debug) 154 + printf("setting chronograph to %02d%02d00\n", 155 + tm->tm_hour, tm->tm_min); 156 + sprintf(buf, "\rATST%02d%02d00\r", tm->tm_hour, 157 + tm->tm_min); 158 + serial_write(buf, strlen(buf)); 159 + break; 160 + } else { 161 + usleep(500000); /* half a sec */ 162 + } 163 + } 164 + 165 + close(serial_fd); 166 + 167 + return 0; 168 + }