Skip to content

Commit ec03933

Browse files
sjaeckeljefdriesen
authored andcommitted
Add support for the Cressi Nepto
The Cressi 'goa' protocol and data format has different versions. The current implementation only supports version 2 and 3 (both variants are mostly compatible with only some minor differences), but the Nepto uses version 4, which requires some refactoring: - The reply to the CMD_VERSION command has no longer a fixed size, and thus the receive function is changed to accept a dynamically sized buffer. - The size of the entries in the logbook can be 23 bytes (for versions 0-3) or 15 bytes (for version 4). - The Nepto is a freediving computer which supports sample rates less than one second. Therefore the sample time is internally changed from seconds to milliseconds. - Each version needs it own layout descriptor. The version number is either available directly in the device id data, or obtained indirectly from the firmware version.
1 parent 73218b4 commit ec03933

File tree

3 files changed

+389
-124
lines changed

3 files changed

+389
-124
lines changed

src/cressi_goa.c

Lines changed: 115 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,12 @@
3434

3535
#define ISINSTANCE(device) dc_device_isinstance((device), &cressi_goa_device_vtable)
3636

37-
#define CMD_VERSION 0x00
38-
#define CMD_SET_TIME 0x13
39-
#define CMD_EXIT_PCLINK 0x1D
40-
#define CMD_LOGBOOK 0x21
41-
#define CMD_DIVE 0x22
37+
#define CMD_VERSION 0x00
38+
#define CMD_SET_TIME 0x13
39+
#define CMD_EXIT_PCLINK 0x1D
40+
#define CMD_LOGBOOK 0x21
41+
#define CMD_DIVE 0x22
42+
#define CMD_LOGBOOK_V4 0x23
4243

4344
#define CMD_LOGBOOK_BLE 0x02
4445
#define CMD_DIVE_BLE 0x03
@@ -49,10 +50,8 @@
4950
#define ACK 0x06
5051

5152
#define SZ_DATA 512
52-
#define SZ_PACKET 10
53-
#define SZ_HEADER 23
53+
#define SZ_PACKET 12
5454

55-
#define FP_OFFSET 0x11
5655
#define FP_SIZE 6
5756

5857
#define NSTEPS 1000
@@ -64,6 +63,13 @@ typedef struct cressi_goa_device_t {
6463
unsigned char fingerprint[FP_SIZE];
6564
} cressi_goa_device_t;
6665

66+
typedef struct cressi_goa_conf_t {
67+
unsigned int logbook_cmd;
68+
unsigned int logbook_len;
69+
unsigned int logbook_fp_offset;
70+
unsigned int dive_fp_offset;
71+
} cressi_goa_conf_t;
72+
6773
static dc_status_t cressi_goa_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
6874
static dc_status_t cressi_goa_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
6975
static dc_status_t cressi_goa_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime);
@@ -81,6 +87,11 @@ static const dc_device_vtable_t cressi_goa_device_vtable = {
8187
cressi_goa_device_close /* close */
8288
};
8389

90+
static const cressi_goa_conf_t version_conf[] = {
91+
{ CMD_LOGBOOK, 23, 17, 12 },
92+
{ CMD_LOGBOOK_V4, 15, 3, 4 },
93+
};
94+
8495
static dc_status_t
8596
cressi_goa_device_send (cressi_goa_device_t *device, unsigned char cmd, const unsigned char data[], unsigned int size)
8697
{
@@ -128,7 +139,7 @@ cressi_goa_device_send (cressi_goa_device_t *device, unsigned char cmd, const un
128139
}
129140

130141
static dc_status_t
131-
cressi_goa_device_receive (cressi_goa_device_t *device, unsigned char data[], unsigned int size)
142+
cressi_goa_device_receive (cressi_goa_device_t *device, dc_buffer_t *output)
132143
{
133144
dc_status_t status = DC_STATUS_SUCCESS;
134145
dc_device_t *abstract = (dc_device_t *) device;
@@ -137,13 +148,16 @@ cressi_goa_device_receive (cressi_goa_device_t *device, unsigned char data[], un
137148
unsigned char packet[SZ_PACKET + 8];
138149

139150
if (transport == DC_TRANSPORT_BLE) {
140-
if (size) {
151+
if (output) {
141152
return DC_STATUS_INVALIDARGS;
142153
} else {
143154
return DC_STATUS_SUCCESS;
144155
}
145156
}
146157

158+
// Clear the output buffer.
159+
dc_buffer_clear (output);
160+
147161
// Read the header of the data packet.
148162
status = dc_iostream_read (device->iostream, packet, 4, NULL);
149163
if (status != DC_STATUS_SUCCESS) {
@@ -185,14 +199,11 @@ cressi_goa_device_receive (cressi_goa_device_t *device, unsigned char data[], un
185199
return DC_STATUS_PROTOCOL;
186200
}
187201

188-
// Verify the payload length.
189-
if (length != size) {
190-
ERROR (abstract->context, "Unexpected payload size (%u).", length);
191-
return DC_STATUS_PROTOCOL;
192-
}
193-
194-
if (length) {
195-
memcpy (data, packet + 5, length);
202+
if (length && output) {
203+
if (!dc_buffer_append (output, packet + 5, length)) {
204+
ERROR (abstract->context, "Could not append received data.");
205+
return DC_STATUS_NOMEMORY;
206+
}
196207
}
197208

198209
return status;
@@ -332,7 +343,7 @@ static dc_status_t
332343
cressi_goa_device_transfer (cressi_goa_device_t *device,
333344
unsigned char cmd,
334345
const unsigned char input[], unsigned int isize,
335-
unsigned char output[], unsigned int osize,
346+
dc_buffer_t *output,
336347
dc_buffer_t *buffer,
337348
dc_event_progress_t *progress)
338349
{
@@ -345,7 +356,7 @@ cressi_goa_device_transfer (cressi_goa_device_t *device,
345356
}
346357

347358
// Receive the answer from the dive computer.
348-
status = cressi_goa_device_receive (device, output, osize);
359+
status = cressi_goa_device_receive (device, output);
349360
if (status != DC_STATUS_SUCCESS) {
350361
return status;
351362
}
@@ -453,8 +464,14 @@ cressi_goa_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
453464
dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
454465
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
455466

467+
dc_buffer_t *id = dc_buffer_new(11);
468+
if (id == NULL) {
469+
ERROR (abstract->context, "Failed to allocate memory for the ID.");
470+
status = DC_STATUS_NOMEMORY;
471+
goto error_exit;
472+
}
473+
456474
// Read the version information.
457-
unsigned char id[9] = {0};
458475
if (transport == DC_TRANSPORT_BLE) {
459476
/*
460477
* With the BLE communication, there is no variant of the CMD_VERSION
@@ -471,7 +488,6 @@ cressi_goa_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
471488
};
472489
const size_t sizes[] = {5, 2, 2};
473490

474-
unsigned int offset = 0;
475491
for (size_t i = 0; i < C_ARRAY_SIZE(characteristics); ++i) {
476492
unsigned char request[sizeof(dc_ble_uuid_t) + 5] = {0};
477493

@@ -485,48 +501,89 @@ cressi_goa_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
485501
char uuidstr[DC_BLE_UUID_SIZE] = {0};
486502
ERROR (abstract->context, "Failed to read the characteristic '%s'.",
487503
dc_ble_uuid2str(characteristics[i], uuidstr, sizeof(uuidstr)));
488-
goto error_exit;
504+
goto error_free_id;
489505
}
490506

491507
// Copy the payload data.
492-
memcpy (id + offset, request + sizeof(dc_ble_uuid_t), sizes[i]);
493-
offset += sizes[i];
508+
if (!dc_buffer_append(id, request + sizeof(dc_ble_uuid_t), sizes[i])) {
509+
ERROR (abstract->context, "Insufficient buffer space available.");
510+
status = DC_STATUS_NOMEMORY;
511+
goto error_free_id;
512+
}
494513
}
495514
} else {
496-
status = cressi_goa_device_transfer (device, CMD_VERSION, NULL, 0, id, sizeof(id), NULL, NULL);
515+
status = cressi_goa_device_transfer (device, CMD_VERSION, NULL, 0, id, NULL, NULL);
497516
if (status != DC_STATUS_SUCCESS) {
498517
ERROR (abstract->context, "Failed to read the version information.");
499-
goto error_exit;
518+
goto error_free_id;
500519
}
501520
}
502521

522+
const unsigned char *id_data = dc_buffer_get_data(id);
523+
size_t id_size = dc_buffer_get_size(id);
524+
525+
if (id_size < 9) {
526+
ERROR (abstract->context, "Unexpected version length (" DC_PRINTF_SIZE ").", id_size);
527+
status = DC_STATUS_DATAFORMAT;
528+
goto error_free_id;
529+
}
530+
531+
// Get the device info.
532+
unsigned int model = id_data[4];
533+
unsigned int firmware = array_uint16_le (id_data + 5);
534+
unsigned int serial = array_uint32_le (id_data + 0);
535+
536+
// Get the data format version.
537+
unsigned int version = 0;
538+
if (id_size == 11) {
539+
version = array_uint16_le (id_data + 9);
540+
} else {
541+
if (firmware >= 161 && firmware <= 165) {
542+
version = 0;
543+
} else if (firmware >= 166 && firmware <= 169) {
544+
version = 1;
545+
} else if (firmware >= 170 && firmware <= 179) {
546+
version = 2;
547+
} else if (firmware >= 100 && firmware <= 110) {
548+
version = 3;
549+
} else if (firmware >= 200 && firmware <= 205) {
550+
version = 4;
551+
} else {
552+
ERROR (abstract->context, "Unknown firmware version (%u).", firmware);
553+
status = DC_STATUS_DATAFORMAT;
554+
goto error_free_id;
555+
}
556+
}
557+
558+
const cressi_goa_conf_t *conf = &version_conf[version >= 4];
559+
503560
// Emit a vendor event.
504561
dc_event_vendor_t vendor;
505-
vendor.data = id;
506-
vendor.size = sizeof (id);
562+
vendor.data = id_data;
563+
vendor.size = id_size;
507564
device_event_emit (abstract, DC_EVENT_VENDOR, &vendor);
508565

509566
// Emit a device info event.
510567
dc_event_devinfo_t devinfo;
511-
devinfo.model = id[4];
512-
devinfo.firmware = array_uint16_le (id + 5);
513-
devinfo.serial = array_uint32_le (id + 0);
568+
devinfo.model = model;
569+
devinfo.firmware = firmware;
570+
devinfo.serial = serial;
514571
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
515572

516573
// Allocate memory for the logbook data.
517574
logbook = dc_buffer_new(4096);
518575
if (logbook == NULL) {
519-
ERROR (abstract->context, "Failed to allocate memory.");
576+
ERROR (abstract->context, "Failed to allocate memory for the logbook.");
520577
status = DC_STATUS_NOMEMORY;
521-
goto error_exit;
578+
goto error_free_id;
522579
}
523580

524581
// Read the logbook data.
525582
if (transport == DC_TRANSPORT_BLE) {
526583
unsigned char args[] = {0x00};
527-
status = cressi_goa_device_transfer (device, CMD_LOGBOOK_BLE, args, sizeof(args), NULL, 0, logbook, &progress);
584+
status = cressi_goa_device_transfer (device, CMD_LOGBOOK_BLE, args, sizeof(args), NULL, logbook, &progress);
528585
} else {
529-
status = cressi_goa_device_transfer (device, CMD_LOGBOOK, NULL, 0, NULL, 0, logbook, &progress);
586+
status = cressi_goa_device_transfer (device, conf->logbook_cmd, NULL, 0, NULL, logbook, &progress);
530587
}
531588
if (status != DC_STATUS_SUCCESS) {
532589
ERROR (abstract->context, "Failed to read the logbook data.");
@@ -539,17 +596,17 @@ cressi_goa_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
539596
// Count the number of dives.
540597
unsigned int count = 0;
541598
unsigned int offset = logbook_size;
542-
while (offset >= SZ_HEADER) {
599+
while (offset >= conf->logbook_len) {
543600
// Move to the start of the logbook entry.
544-
offset -= SZ_HEADER;
601+
offset -= conf->logbook_len;
545602

546603
// Get the dive number.
547604
unsigned int number= array_uint16_le (logbook_data + offset);
548605
if (number == 0)
549606
break;
550607

551608
// Compare the fingerprint to identify previously downloaded entries.
552-
if (memcmp (logbook_data + offset + FP_OFFSET, device->fingerprint, sizeof(device->fingerprint)) == 0) {
609+
if (memcmp (logbook_data + offset + conf->logbook_fp_offset, device->fingerprint, sizeof(device->fingerprint)) == 0) {
553610
break;
554611
}
555612

@@ -563,7 +620,7 @@ cressi_goa_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
563620
// Allocate memory for the dive data.
564621
dive = dc_buffer_new(4096);
565622
if (dive == NULL) {
566-
ERROR (abstract->context, "Failed to allocate memory.");
623+
ERROR (abstract->context, "Failed to allocate memory for the dive.");
567624
status = DC_STATUS_NOMEMORY;
568625
goto error_free_logbook;
569626
}
@@ -572,16 +629,16 @@ cressi_goa_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
572629
offset = logbook_size;
573630
for (unsigned int i = 0; i < count; ++i) {
574631
// Move to the start of the logbook entry.
575-
offset -= SZ_HEADER;
632+
offset -= conf->logbook_len;
576633

577634
// Read the dive data.
578635
if (transport == DC_TRANSPORT_BLE) {
579636
unsigned char number[2] = {
580637
logbook_data[offset + 1],
581638
logbook_data[offset + 0]};
582-
status = cressi_goa_device_transfer (device, CMD_DIVE_BLE, number, 2, NULL, 0, dive, &progress);
639+
status = cressi_goa_device_transfer (device, CMD_DIVE_BLE, number, 2, NULL, dive, &progress);
583640
} else {
584-
status = cressi_goa_device_transfer (device, CMD_DIVE, logbook_data + offset, 2, NULL, 0, dive, &progress);
641+
status = cressi_goa_device_transfer (device, CMD_DIVE, logbook_data + offset, 2, NULL, dive, &progress);
585642
}
586643
if (status != DC_STATUS_SUCCESS) {
587644
ERROR (abstract->context, "Failed to read the dive data.");
@@ -591,12 +648,11 @@ cressi_goa_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
591648
const unsigned char *dive_data = dc_buffer_get_data (dive);
592649
size_t dive_size = dc_buffer_get_size (dive);
593650

594-
// Verify the header in the logbook and dive data are identical.
595-
// After the 2 byte dive number, the logbook header has 5 bytes
596-
// extra, which are not present in the dive header.
597-
if (dive_size < SZ_HEADER - 5 ||
598-
memcmp (dive_data + 0, logbook_data + offset + 0, 2) != 0 ||
599-
memcmp (dive_data + 2, logbook_data + offset + 7, SZ_HEADER - 7) != 0) {
651+
// Verify the dive number and the fingerprint in the logbook and dive
652+
// data are identical.
653+
if (dive_size < conf->dive_fp_offset + FP_SIZE ||
654+
memcmp (dive_data, logbook_data + offset, 2) != 0 ||
655+
memcmp (dive_data + conf->dive_fp_offset, logbook_data + offset + conf->logbook_fp_offset, FP_SIZE) != 0) {
600656
ERROR (abstract->context, "Unexpected dive header.");
601657
status = DC_STATUS_DATAFORMAT;
602658
goto error_free_dive;
@@ -608,12 +664,12 @@ cressi_goa_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
608664
// are prepended to the dive data, along with a small header containing
609665
// their size.
610666
const unsigned char header[] = {
611-
sizeof(id),
612-
SZ_HEADER,
667+
id_size,
668+
conf->logbook_len,
613669
};
614-
unsigned int headersize = sizeof(header) + sizeof(id) + SZ_HEADER;
615-
if (!dc_buffer_prepend(dive, logbook_data + offset, SZ_HEADER) ||
616-
!dc_buffer_prepend(dive, id, sizeof(id)) ||
670+
unsigned int headersize = sizeof(header) + id_size + conf->logbook_len;
671+
if (!dc_buffer_prepend(dive, logbook_data + offset, conf->logbook_len) ||
672+
!dc_buffer_prepend(dive, id_data, id_size) ||
617673
!dc_buffer_prepend(dive, header, sizeof(header))) {
618674
ERROR (abstract->context, "Out of memory.");
619675
status = DC_STATUS_NOMEMORY;
@@ -623,14 +679,16 @@ cressi_goa_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
623679
dive_data = dc_buffer_get_data (dive);
624680
dive_size = dc_buffer_get_size (dive);
625681

626-
if (callback && !callback(dive_data, dive_size, dive_data + headersize + FP_OFFSET - 5, sizeof(device->fingerprint), userdata))
682+
if (callback && !callback(dive_data, dive_size, dive_data + headersize + conf->dive_fp_offset, sizeof(device->fingerprint), userdata))
627683
break;
628684
}
629685

630686
error_free_dive:
631687
dc_buffer_free(dive);
632688
error_free_logbook:
633689
dc_buffer_free(logbook);
690+
error_free_id:
691+
dc_buffer_free(id);
634692
error_exit:
635693
return status;
636694
}
@@ -653,7 +711,7 @@ cressi_goa_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime
653711
new_time[4] = datetime->hour;
654712
new_time[5] = datetime->minute;
655713
new_time[6] = datetime->second;
656-
status = cressi_goa_device_transfer (device, CMD_SET_TIME, new_time, sizeof(new_time), NULL, 0, NULL, NULL);
714+
status = cressi_goa_device_transfer (device, CMD_SET_TIME, new_time, sizeof(new_time), NULL, NULL, NULL);
657715
if (status != DC_STATUS_SUCCESS) {
658716
ERROR (abstract->context, "Failed to set the new time.");
659717
return status;
@@ -673,7 +731,7 @@ cressi_goa_device_close (dc_device_t *abstract)
673731
return DC_STATUS_SUCCESS;
674732
}
675733

676-
status = cressi_goa_device_transfer (device, CMD_EXIT_PCLINK, NULL, 0, NULL, 0, NULL, NULL);
734+
status = cressi_goa_device_transfer (device, CMD_EXIT_PCLINK, NULL, 0, NULL, NULL, NULL);
677735
if (status != DC_STATUS_SUCCESS) {
678736
ERROR (abstract->context, "Failed to exit PC Link.");
679737
return status;

0 commit comments

Comments
 (0)