Utility to set the time on a Hayes Stack Chronograph
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
32static int debug = 0;
33static char vbuf[512];
34static int serial_fd = -1;
35
36void usage(const char *);
37void serial_setup(int, speed_t);
38size_t serial_read(void *, size_t);
39size_t serial_write(const void *, size_t);
40
41void
42usage(const char *progname)
43{
44 fprintf(stderr, "usage: %s [-d] [-s serial speed] <serial device>\n",
45 progname);
46 exit(1);
47}
48
49void
50serial_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
80size_t
81serial_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
95size_t
96serial_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
106int
107main(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}