[RFD] divecomputer time/date synchronization while downloading dives

Linus Torvalds torvalds at linux-foundation.org
Wed Jul 19 13:08:39 PDT 2017


I'm slightly OCD when it comes to clocks. My wrist watch is one of
those "radio time signal" ones. Seeing wall clocks that are the wrong
time drives me wild. I just can't take it. "You have *one* job, and
you suck at it".

When I was at the conference hotel in Beijing last month, I reacted to
how almost *every* clock I saw was off by minutes, sometimes hours.
The time display in the elevator? Off by a lot. The time in the room
thermostat? Completely random.

So guess what happens with dive computers? I react to them when they
have clocks that run slow or fast. My old Uemis would be off by
minutes when I hadn't used it in a couple of months, and it was
annoying to set the right time, and boy did I notice.

Anyway, what this all builds up to is that I'd actually like to just
set the time automatically when I connect the dive computer to my
laptop (or my cellphone, for that matter - usually those end up being
synchronized to the local time quite nicely).

And I just wrote the code for that for the Suunto EON Steel. Patch to
libdivecomputer attached in case anybody cares (and I was happy to
realize that it actually uses milliseconds for synchronization, but
then noticed that it may be just a protocol thing, and the extra
precision may get thrown away).

But I suspect people will hate that patch because it does that time
sync thing *unconditionally*. It would be good to have a check-box and
some way to actually set the "sync time" flag. But for that to happen,
we'd need not just the UI side, we'd need to have the libdivecomputer
interface too.

And yes, I know about "configure dive computer" and the "sync time"
hack there. But it's really an ostc-specific hack, not something that
can be used in general to synchronize the clock.

Thus the "request for discussion". I'd love to be able to pass in
flags like this to dc_device_open() or add a sane
"dc_device_set_feature()" interface or something..

                  Linus
-------------- next part --------------
 src/suunto_eonsteel.c | 124 ++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 104 insertions(+), 20 deletions(-)

diff --git a/src/suunto_eonsteel.c b/src/suunto_eonsteel.c
index c381668..4b49751 100644
--- a/src/suunto_eonsteel.c
+++ b/src/suunto_eonsteel.c
@@ -23,6 +23,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <zlib.h>	/* For crc32() */
+#include <time.h>
+#include <sys/time.h>
 
 #include "suunto_eonsteel.h"
 #include "context-private.h"
@@ -54,20 +56,25 @@ struct directory_entry {
 };
 
 // EON Steel command numbers and other magic field values
-#define INIT_CMD   0x00
-#define INIT_MAGIC 0x0001
-#define INIT_SEQ   0
+#define CMD_INIT	0x0000
+#define INIT_MAGIC	0x0001
+#define INIT_SEQ	0
 
-#define READ_STRING_CMD 0x0411
+#define CMD_READ_STRING	0x0411
 
-#define FILE_LOOKUP_CMD 0x0010
-#define FILE_READ_CMD   0x0110
-#define FILE_STAT_CMD   0x0710
-#define FILE_CLOSE_CMD  0x0510
+#define CMD_FILE_OPEN	0x0010
+#define CMD_FILE_READ	0x0110
+#define CMD_FILE_STAT	0x0710
+#define CMD_FILE_CLOSE	0x0510
 
-#define DIR_LOOKUP_CMD 0x0810
-#define READDIR_CMD    0x0910
-#define DIR_CLOSE_CMD  0x0a10
+#define CMD_DIR_OPEN	0x0810
+#define CMD_DIR_READDIR	0x0910
+#define CMD_DIR_CLOSE	0x0a10
+
+#define CMD_SET_TIME	0x0003
+#define CMD_GET_TIME	0x0103
+#define CMD_SET_DATE	0x0203
+#define CMD_GET_DATE	0x0303
 
 static dc_status_t suunto_eonsteel_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
 static dc_status_t suunto_eonsteel_device_foreach(dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
@@ -527,7 +534,7 @@ static int read_file(suunto_eonsteel_device_t *eon, const char *filename, dc_buf
 		return -1;
 	}
 	memcpy(cmdbuf+4, filename, len);
-	rc = send_receive(eon, FILE_LOOKUP_CMD,
+	rc = send_receive(eon, CMD_FILE_OPEN,
 		len+4, cmdbuf,
 		sizeof(result), result);
 	if (rc < 0) {
@@ -536,7 +543,7 @@ static int read_file(suunto_eonsteel_device_t *eon, const char *filename, dc_buf
 	}
 	HEXDUMP (eon->base.context, DC_LOGLEVEL_DEBUG, "lookup", result, rc);
 
-	rc = send_receive(eon, FILE_STAT_CMD,
+	rc = send_receive(eon, CMD_FILE_STAT,
 		0, NULL,
 		sizeof(result), result);
 	if (rc < 0) {
@@ -556,7 +563,7 @@ static int read_file(suunto_eonsteel_device_t *eon, const char *filename, dc_buf
 			ask = 1024;
 		put_le32(1234, cmdbuf+0);	// Not file offset, after all
 		put_le32(ask, cmdbuf+4);	// Size of read
-		rc = send_receive(eon, FILE_READ_CMD,
+		rc = send_receive(eon, CMD_FILE_READ,
 			8, cmdbuf,
 			sizeof(result), result);
 		if (rc < 0) {
@@ -591,11 +598,11 @@ static int read_file(suunto_eonsteel_device_t *eon, const char *filename, dc_buf
 		size -= got;
 	}
 
-	rc = send_receive(eon, FILE_CLOSE_CMD,
+	rc = send_receive(eon, CMD_FILE_CLOSE,
 		0, NULL,
 		sizeof(result), result);
 	if (rc < 0) {
-		ERROR(eon->base.context, "cmd FILE_CLOSE_CMD failed");
+		ERROR(eon->base.context, "cmd CMD_FILE_CLOSE failed");
 		return -1;
 	}
 	HEXDUMP(eon->base.context, DC_LOGLEVEL_DEBUG, "close", result, rc);
@@ -646,7 +653,7 @@ static int get_file_list(suunto_eonsteel_device_t *eon, struct directory_entry *
 	put_le32(0, cmd);
 	memcpy(cmd + 4, dive_directory, sizeof(dive_directory));
 	cmdlen = 4 + sizeof(dive_directory);
-	rc = send_receive(eon, DIR_LOOKUP_CMD,
+	rc = send_receive(eon, CMD_DIR_OPEN,
 		cmdlen, cmd,
 		sizeof(result), result);
 	if (rc < 0) {
@@ -657,7 +664,7 @@ static int get_file_list(suunto_eonsteel_device_t *eon, struct directory_entry *
 	for (;;) {
 		unsigned int nr, last;
 
-		rc = send_receive(eon, READDIR_CMD,
+		rc = send_receive(eon, CMD_DIR_READDIR,
 			0, NULL,
 			sizeof(result), result);
 		if (rc < 0) {
@@ -677,7 +684,7 @@ static int get_file_list(suunto_eonsteel_device_t *eon, struct directory_entry *
 			break;
 	}
 
-	rc = send_receive(eon, DIR_CLOSE_CMD,
+	rc = send_receive(eon, CMD_DIR_CLOSE,
 		0, NULL,
 		sizeof(result), result);
 	if (rc < 0) {
@@ -693,7 +700,7 @@ static int initialize_eonsteel(suunto_eonsteel_device_t *eon)
 	const unsigned char init[] = {0x02, 0x00, 0x2a, 0x00};
 	struct eon_hdr hdr;
 
-	if (send_cmd(eon, INIT_CMD, sizeof(init), init)) {
+	if (send_cmd(eon, CMD_INIT, sizeof(init), init)) {
 		ERROR(eon->base.context, "Failed to send initialization command");
 		return -1;
 	}
@@ -709,6 +716,81 @@ static int initialize_eonsteel(suunto_eonsteel_device_t *eon)
 	return 0;
 }
 
+/*
+ * We should have some way to say whether we want to
+ * synchronize time or not..
+ */
+static void verify_date_time(suunto_eonsteel_device_t *eon)
+{
+	unsigned char result[64], cmd[64];
+	int year, month, day;
+	int hour, min, sec, msec;
+	struct timeval tv;
+	struct tm *tm;
+	int rc;
+
+	rc = send_receive(eon, CMD_GET_DATE,
+		0, NULL,
+		sizeof(result), result);
+	if (rc < 4)
+		return;
+
+	year = array_uint16_le(result);
+	month = result[2];
+	day = result[3];
+	INFO(eon->base.context, "GET_DATE: %d/%d/%d", year, month, day);
+
+	rc = send_receive(eon, CMD_GET_TIME,
+		0, NULL,
+		sizeof(result), result);
+	if (rc < 8)
+		return;
+
+	year = array_uint16_le(result);
+	month = result[2];
+	day = result[3];
+	hour = result[4];
+	min = result[5];
+	msec = array_uint16_le(result+6);
+	sec = msec / 1000;
+	msec %= 1000;
+
+	INFO(eon->base.context, "GET_TIME: %d/%d/%d %d:%02d:%02d.%03d",
+		year, month, day, hour, min, sec, msec);
+
+	/* Make this conditional somehow */
+	if (gettimeofday(&tv, NULL))
+		return;
+	tm = localtime(&tv.tv_sec);
+	year = tm->tm_year + 1900;
+	month = tm->tm_mon + 1;
+	day = tm->tm_mday;
+	hour = tm->tm_hour;
+	min = tm->tm_min;
+	sec = tm->tm_sec;
+	msec = tv.tv_usec / 1000;
+
+	INFO(eon->base.context, "SET_TIME: %d/%d/%d %d:%02d:%02d.%03d",
+		year, month, day, hour, min, sec, msec);
+
+        msec += sec * 1000;
+
+	cmd[0] = year & 255;
+	cmd[1] = year >> 8;
+	cmd[2] = month;
+	cmd[3] = day;
+	cmd[4] = hour;
+	cmd[5] = min;
+	cmd[6] = msec & 255;
+	cmd[7] = msec >> 8;
+
+	rc = send_receive(eon, CMD_SET_TIME,
+		8, cmd,
+		sizeof(result), result);
+	if (rc < 0)
+		INFO(eon->base.context, "SET_TIME failed");
+}
+
 dc_status_t
 suunto_eonsteel_device_open(dc_device_t **out, dc_context_t *context, const char *name)
 {
@@ -745,6 +827,8 @@ suunto_eonsteel_device_open(dc_device_t **out, dc_context_t *context, const char
 		goto error_close;
 	}
 
+	verify_date_time(eon);
+
 	*out = (dc_device_t *) eon;
 
 	return DC_STATUS_SUCCESS;


More information about the subsurface mailing list