Skip to content

Commit 6f4e4ee

Browse files
jefdriesenGreg McLaughlindivinglog
committed
Add support for the Cressi BLE protocol variant
The BLE variant of the Cressi protocol uses a completely different set of commands compared to the existing usb-serial protocol. Most of the packet framing bytes have been removed as well. But the most surprising change is the fact that there is no variant of the CMD_VERSION available. The corresponding information must be obtained by reading some secondary characteristics instead. Co-authored-by: Greg McLaughlin <[email protected]> Co-authored-by: Sven Knoch <[email protected]>
1 parent 88d78a3 commit 6f4e4ee

File tree

2 files changed

+220
-51
lines changed

2 files changed

+220
-51
lines changed

src/cressi_goa.c

Lines changed: 157 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
#include <stdlib.h> // malloc, free
2424
#include <assert.h> // assert
2525

26+
#include <libdivecomputer/ble.h>
27+
2628
#include "cressi_goa.h"
2729
#include "context-private.h"
2830
#include "device-private.h"
@@ -36,6 +38,9 @@
3638
#define CMD_LOGBOOK 0x21
3739
#define CMD_DIVE 0x22
3840

41+
#define CMD_LOGBOOK_BLE 0x02
42+
#define CMD_DIVE_BLE 0x03
43+
3944
#define HEADER 0xAA
4045
#define TRAILER 0x55
4146
#define END 0x04
@@ -77,6 +82,7 @@ cressi_goa_device_send (cressi_goa_device_t *device, unsigned char cmd, const un
7782
{
7883
dc_status_t status = DC_STATUS_SUCCESS;
7984
dc_device_t *abstract = (dc_device_t *) device;
85+
dc_transport_t transport = dc_iostream_get_transport (device->iostream);
8086

8187
if (size > SZ_PACKET) {
8288
ERROR (abstract->context, "Unexpected payload size (%u).", size);
@@ -100,10 +106,15 @@ cressi_goa_device_send (cressi_goa_device_t *device, unsigned char cmd, const un
100106

101107
// Wait a small amount of time before sending the command. Without
102108
// this delay, the transfer will fail most of the time.
103-
dc_iostream_sleep (device->iostream, 100);
109+
unsigned int delay = transport == DC_TRANSPORT_BLE ? 2000 : 100;
110+
dc_iostream_sleep (device->iostream, delay);
104111

105112
// Send the command to the device.
106-
status = dc_iostream_write (device->iostream, packet, size + 8, NULL);
113+
if (transport == DC_TRANSPORT_BLE) {
114+
status = dc_iostream_write (device->iostream, packet + 4, size + 1, NULL);
115+
} else {
116+
status = dc_iostream_write (device->iostream, packet, size + 8, NULL);
117+
}
107118
if (status != DC_STATUS_SUCCESS) {
108119
ERROR (abstract->context, "Failed to send the command.");
109120
return status;
@@ -117,9 +128,18 @@ cressi_goa_device_receive (cressi_goa_device_t *device, unsigned char data[], un
117128
{
118129
dc_status_t status = DC_STATUS_SUCCESS;
119130
dc_device_t *abstract = (dc_device_t *) device;
131+
dc_transport_t transport = dc_iostream_get_transport (device->iostream);
120132

121133
unsigned char packet[SZ_PACKET + 8];
122134

135+
if (transport == DC_TRANSPORT_BLE) {
136+
if (size) {
137+
return DC_STATUS_INVALIDARGS;
138+
} else {
139+
return DC_STATUS_SUCCESS;
140+
}
141+
}
142+
123143
// Read the header of the data packet.
124144
status = dc_iostream_read (device->iostream, packet, 4, NULL);
125145
if (status != DC_STATUS_SUCCESS) {
@@ -179,6 +199,7 @@ cressi_goa_device_download (cressi_goa_device_t *device, dc_buffer_t *buffer, dc
179199
{
180200
dc_status_t status = DC_STATUS_SUCCESS;
181201
dc_device_t *abstract = (dc_device_t *) device;
202+
dc_transport_t transport = dc_iostream_get_transport (device->iostream);
182203

183204
const unsigned char ack[] = {ACK};
184205
const unsigned int initial = progress ? progress->current : 0;
@@ -193,27 +214,43 @@ cressi_goa_device_download (cressi_goa_device_t *device, dc_buffer_t *buffer, dc
193214
unsigned int size = 2;
194215
unsigned int nbytes = 0;
195216
while (nbytes < size) {
196-
// Read the data packet.
197217
unsigned char packet[3 + SZ_DATA + 2];
198-
status = dc_iostream_read (device->iostream, packet, sizeof(packet), NULL);
199-
if (status != DC_STATUS_SUCCESS) {
200-
ERROR (abstract->context, "Failed to receive the answer.");
201-
return status;
202-
}
203218

204-
// Verify the checksum of the packet.
205-
unsigned short crc = array_uint16_le (packet + sizeof(packet) - 2);
206-
unsigned short ccrc = checksum_crc16_ccitt (packet + 3, sizeof(packet) - 5, 0x0000, 0x0000);
207-
if (crc != ccrc) {
208-
ERROR (abstract->context, "Unexpected answer checksum.");
209-
return DC_STATUS_PROTOCOL;
210-
}
211-
212-
// Send the ack byte to the device.
213-
status = dc_iostream_write (device->iostream, ack, sizeof(ack), NULL);
214-
if (status != DC_STATUS_SUCCESS) {
215-
ERROR (abstract->context, "Failed to send the ack byte.");
216-
return status;
219+
if (transport == DC_TRANSPORT_BLE) {
220+
// Read the data packet.
221+
unsigned int packetsize = 0;
222+
while (packetsize < SZ_DATA) {
223+
size_t len = 0;
224+
status = dc_iostream_read (device->iostream, packet + 3 + packetsize, SZ_DATA - packetsize, &len);
225+
if (status != DC_STATUS_SUCCESS) {
226+
ERROR (abstract->context, "Failed to receive the answer.");
227+
return status;
228+
}
229+
230+
packetsize += len;
231+
}
232+
} else {
233+
// Read the data packet.
234+
status = dc_iostream_read (device->iostream, packet, sizeof(packet), NULL);
235+
if (status != DC_STATUS_SUCCESS) {
236+
ERROR (abstract->context, "Failed to receive the answer.");
237+
return status;
238+
}
239+
240+
// Verify the checksum of the packet.
241+
unsigned short crc = array_uint16_le (packet + sizeof(packet) - 2);
242+
unsigned short ccrc = checksum_crc16_ccitt (packet + 3, sizeof(packet) - 5, 0x0000, 0x0000);
243+
if (crc != ccrc) {
244+
ERROR (abstract->context, "Unexpected answer checksum.");
245+
return DC_STATUS_PROTOCOL;
246+
}
247+
248+
// Send the ack byte to the device.
249+
status = dc_iostream_write (device->iostream, ack, sizeof(ack), NULL);
250+
if (status != DC_STATUS_SUCCESS) {
251+
ERROR (abstract->context, "Failed to send the ack byte.");
252+
return status;
253+
}
217254
}
218255

219256
// Get the total size from the first data packet.
@@ -243,25 +280,45 @@ cressi_goa_device_download (cressi_goa_device_t *device, dc_buffer_t *buffer, dc
243280
}
244281
}
245282

246-
// Read the end byte.
247-
unsigned char end = 0;
248-
status = dc_iostream_read (device->iostream, &end, 1, NULL);
249-
if (status != DC_STATUS_SUCCESS) {
250-
ERROR (abstract->context, "Failed to receive the end byte.");
251-
return status;
252-
}
283+
if (transport == DC_TRANSPORT_BLE) {
284+
// Read the end bytes.
285+
unsigned char end[16] = {0};
286+
size_t len = 0;
287+
status = dc_iostream_read (device->iostream, end, sizeof(end), &len);
288+
if (status != DC_STATUS_SUCCESS) {
289+
ERROR (abstract->context, "Failed to receive the end bytes.");
290+
return status;
291+
}
253292

254-
// Verify the end byte.
255-
if (end != END) {
256-
ERROR (abstract->context, "Unexpected end byte (%02x).", end);
257-
return DC_STATUS_PROTOCOL;
258-
}
293+
// Verify the end bytes ("EOT xmodem").
294+
const unsigned char validate[16] = {
295+
0x45, 0x4F, 0x54, 0x20, 0x78, 0x6D, 0x6F, 0x64,
296+
0x65, 0x6D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
297+
if (memcmp (end, validate, sizeof(validate)) != 0) {
298+
ERROR (abstract->context, "Unexpected end bytes.");
299+
return DC_STATUS_PROTOCOL;
300+
}
301+
} else {
302+
// Read the end byte.
303+
unsigned char end = 0;
304+
status = dc_iostream_read (device->iostream, &end, 1, NULL);
305+
if (status != DC_STATUS_SUCCESS) {
306+
ERROR (abstract->context, "Failed to receive the end byte.");
307+
return status;
308+
}
259309

260-
// Send the ack byte to the device.
261-
status = dc_iostream_write (device->iostream, ack, sizeof(ack), NULL);
262-
if (status != DC_STATUS_SUCCESS) {
263-
ERROR (abstract->context, "Failed to send the ack byte.");
264-
return status;
310+
// Verify the end byte.
311+
if (end != END) {
312+
ERROR (abstract->context, "Unexpected end byte (%02x).", end);
313+
return DC_STATUS_PROTOCOL;
314+
}
315+
316+
// Send the ack byte to the device.
317+
status = dc_iostream_write (device->iostream, ack, sizeof(ack), NULL);
318+
if (status != DC_STATUS_SUCCESS) {
319+
ERROR (abstract->context, "Failed to send the ack byte.");
320+
return status;
321+
}
265322
}
266323

267324
return status;
@@ -328,8 +385,10 @@ cressi_goa_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t
328385
goto error_free;
329386
}
330387

331-
// Set the timeout for receiving data (3000 ms).
332-
status = dc_iostream_set_timeout (device->iostream, 3000);
388+
// Set the timeout for receiving data (3000 - 5000 ms).
389+
dc_transport_t transport = dc_iostream_get_transport (device->iostream);
390+
int timeout = transport == DC_TRANSPORT_BLE ? 5000 : 3000;
391+
status = dc_iostream_set_timeout (device->iostream, timeout);
333392
if (status != DC_STATUS_SUCCESS) {
334393
ERROR (context, "Failed to set the timeout.");
335394
goto error_free;
@@ -382,6 +441,7 @@ cressi_goa_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
382441
{
383442
dc_status_t status = DC_STATUS_SUCCESS;
384443
cressi_goa_device_t *device = (cressi_goa_device_t *) abstract;
444+
dc_transport_t transport = dc_iostream_get_transport (device->iostream);
385445
dc_buffer_t *logbook = NULL;
386446
dc_buffer_t *dive = NULL;
387447

@@ -391,10 +451,49 @@ cressi_goa_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
391451

392452
// Read the version information.
393453
unsigned char id[9] = {0};
394-
status = cressi_goa_device_transfer (device, CMD_VERSION, NULL, 0, id, sizeof(id), NULL, NULL);
395-
if (status != DC_STATUS_SUCCESS) {
396-
ERROR (abstract->context, "Failed to read the version information.");
397-
goto error_exit;
454+
if (transport == DC_TRANSPORT_BLE) {
455+
/*
456+
* With the BLE communication, there is no variant of the CMD_VERSION
457+
* command available. The corresponding information must be obtained by
458+
* reading some secondary characteristics instead:
459+
* 6E400003-B5A3-F393-E0A9-E50E24DC10B8 - 5 bytes
460+
* 6E400004-B5A3-F393-E0A9-E50E24DC10B8 - 2 bytes
461+
* 6E400005-B5A3-F393-E0A9-E50E24DC10B8 - 2 bytes
462+
*/
463+
const dc_ble_uuid_t characteristics[] = {
464+
{0x6E, 0x40, 0x00, 0x03, 0xB5, 0xA3, 0xF3, 0x93, 0xE0, 0xA9, 0xE5, 0x0E, 0x24, 0xDC, 0x10, 0xB8},
465+
{0x6E, 0x40, 0x00, 0x04, 0xB5, 0xA3, 0xF3, 0x93, 0xE0, 0xA9, 0xE5, 0x0E, 0x24, 0xDC, 0x10, 0xB8},
466+
{0x6E, 0x40, 0x00, 0x05, 0xB5, 0xA3, 0xF3, 0x93, 0xE0, 0xA9, 0xE5, 0x0E, 0x24, 0xDC, 0x10, 0xB8},
467+
};
468+
const size_t sizes[] = {5, 2, 2};
469+
470+
unsigned int offset = 0;
471+
for (size_t i = 0; i < C_ARRAY_SIZE(characteristics); ++i) {
472+
unsigned char request[sizeof(dc_ble_uuid_t) + 5] = {0};
473+
474+
// Setup the request.
475+
memcpy (request, characteristics[i], sizeof(dc_ble_uuid_t));
476+
memset (request + sizeof(dc_ble_uuid_t), 0, sizes[i]);
477+
478+
// Read the characteristic.
479+
status = dc_iostream_ioctl (device->iostream, DC_IOCTL_BLE_CHARACTERISTIC_READ, request, sizeof(dc_ble_uuid_t) + sizes[i]);
480+
if (status != DC_STATUS_SUCCESS) {
481+
char uuidstr[DC_BLE_UUID_SIZE] = {0};
482+
ERROR (abstract->context, "Failed to read the characteristic '%s'.",
483+
dc_ble_uuid2str(characteristics[i], uuidstr, sizeof(uuidstr)));
484+
goto error_exit;
485+
}
486+
487+
// Copy the payload data.
488+
memcpy (id + offset, request + sizeof(dc_ble_uuid_t), sizes[i]);
489+
offset += sizes[i];
490+
}
491+
} else {
492+
status = cressi_goa_device_transfer (device, CMD_VERSION, NULL, 0, id, sizeof(id), NULL, NULL);
493+
if (status != DC_STATUS_SUCCESS) {
494+
ERROR (abstract->context, "Failed to read the version information.");
495+
goto error_exit;
496+
}
398497
}
399498

400499
// Emit a vendor event.
@@ -419,7 +518,12 @@ cressi_goa_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
419518
}
420519

421520
// Read the logbook data.
422-
status = cressi_goa_device_transfer (device, CMD_LOGBOOK, NULL, 0, NULL, 0, logbook, &progress);
521+
if (transport == DC_TRANSPORT_BLE) {
522+
unsigned char args[] = {0x00};
523+
status = cressi_goa_device_transfer (device, CMD_LOGBOOK_BLE, args, sizeof(args), NULL, 0, logbook, &progress);
524+
} else {
525+
status = cressi_goa_device_transfer (device, CMD_LOGBOOK, NULL, 0, NULL, 0, logbook, &progress);
526+
}
423527
if (status != DC_STATUS_SUCCESS) {
424528
ERROR (abstract->context, "Failed to read the logbook data.");
425529
goto error_free_logbook;
@@ -467,7 +571,14 @@ cressi_goa_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
467571
offset -= SZ_HEADER;
468572

469573
// Read the dive data.
470-
status = cressi_goa_device_transfer (device, CMD_DIVE, logbook_data + offset, 2, NULL, 0, dive, &progress);
574+
if (transport == DC_TRANSPORT_BLE) {
575+
unsigned char number[2] = {
576+
logbook_data[offset + 1],
577+
logbook_data[offset + 0]};
578+
status = cressi_goa_device_transfer (device, CMD_DIVE_BLE, number, 2, NULL, 0, dive, &progress);
579+
} else {
580+
status = cressi_goa_device_transfer (device, CMD_DIVE, logbook_data + offset, 2, NULL, 0, dive, &progress);
581+
}
471582
if (status != DC_STATUS_SUCCESS) {
472583
ERROR (abstract->context, "Failed to read the dive data.");
473584
goto error_free_dive;

0 commit comments

Comments
 (0)