Add support for the Mares Bluelink Pro interface
The main difference with the serial communication is that the BLE communication uses data packets (with a maximum size of 20 bytes) instead of a continuous data stream. Occasionally, the device responds with an empty packet (with just the ACK and EOF byte) or with a short packet where one or more payload bytes are missing. The empty packet is most likely caused because the device didn't receive the second part of the command (with the parameters) in time. The missing bytes might be caused by a buffer overflow on the Bluelink Pro, when the data from the dive computer arrives faster over the serial link than the device can forward it over the slower bluetooth link to the host system. One way to address this problem is to lower the packet size from 256 bytes to only 128 bytes. The main disadvantage is that this also impacts the download speed considerably. In one particular example, the total download time increased from about 135 to 210 seconds (which is already slow compared to the 62 seconds the same download takes over usb)! An alternative solution is to simply retry the failed command. That way there is only a performance penalty for the few bad packets, and not for every single packet. In the above example, only two packets needed a retransmission. Note that the iconhd communication protocol uses no checksums. Hence it's not possible to detect corrupt data packets. Only short packets can be detected, because they result in a timeout.
This commit is contained in:
parent
88a814a616
commit
019a98f80c
@ -34,6 +34,7 @@ static int dc_filter_suunto (dc_transport_t transport, const void *userdata);
|
||||
static int dc_filter_shearwater (dc_transport_t transport, const void *userdata);
|
||||
static int dc_filter_hw (dc_transport_t transport, const void *userdata);
|
||||
static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata);
|
||||
static int dc_filter_mares (dc_transport_t transport, const void *userdata);
|
||||
|
||||
static dc_status_t dc_descriptor_iterator_next (dc_iterator_t *iterator, void *item);
|
||||
|
||||
@ -241,16 +242,16 @@ static const dc_descriptor_t g_descriptors[] = {
|
||||
{"Mares", "Airlab", DC_FAMILY_MARES_DARWIN , 1, DC_TRANSPORT_SERIAL, NULL},
|
||||
/* Mares Icon HD */
|
||||
{"Mares", "Matrix", DC_FAMILY_MARES_ICONHD , 0x0F, DC_TRANSPORT_SERIAL, NULL},
|
||||
{"Mares", "Smart", DC_FAMILY_MARES_ICONHD , 0x000010, DC_TRANSPORT_SERIAL, NULL},
|
||||
{"Mares", "Smart Apnea", DC_FAMILY_MARES_ICONHD , 0x010010, DC_TRANSPORT_SERIAL, NULL},
|
||||
{"Mares", "Smart", DC_FAMILY_MARES_ICONHD , 0x000010, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares},
|
||||
{"Mares", "Smart Apnea", DC_FAMILY_MARES_ICONHD , 0x010010, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares},
|
||||
{"Mares", "Icon HD", DC_FAMILY_MARES_ICONHD , 0x14, DC_TRANSPORT_SERIAL, NULL},
|
||||
{"Mares", "Icon HD Net Ready", DC_FAMILY_MARES_ICONHD , 0x15, DC_TRANSPORT_SERIAL, NULL},
|
||||
{"Mares", "Puck Pro", DC_FAMILY_MARES_ICONHD , 0x18, DC_TRANSPORT_SERIAL, NULL},
|
||||
{"Mares", "Puck Pro", DC_FAMILY_MARES_ICONHD , 0x18, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares},
|
||||
{"Mares", "Nemo Wide 2", DC_FAMILY_MARES_ICONHD , 0x19, DC_TRANSPORT_SERIAL, NULL},
|
||||
{"Mares", "Puck 2", DC_FAMILY_MARES_ICONHD , 0x1F, DC_TRANSPORT_SERIAL, NULL},
|
||||
{"Mares", "Quad Air", DC_FAMILY_MARES_ICONHD , 0x23, DC_TRANSPORT_SERIAL, NULL},
|
||||
{"Mares", "Smart Air", DC_FAMILY_MARES_ICONHD , 0x24, DC_TRANSPORT_SERIAL, NULL},
|
||||
{"Mares", "Quad", DC_FAMILY_MARES_ICONHD , 0x29, DC_TRANSPORT_SERIAL, NULL},
|
||||
{"Mares", "Quad Air", DC_FAMILY_MARES_ICONHD , 0x23, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares},
|
||||
{"Mares", "Smart Air", DC_FAMILY_MARES_ICONHD , 0x24, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares},
|
||||
{"Mares", "Quad", DC_FAMILY_MARES_ICONHD , 0x29, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares},
|
||||
/* Heinrichs Weikamp */
|
||||
{"Heinrichs Weikamp", "OSTC", DC_FAMILY_HW_OSTC, 0, DC_TRANSPORT_SERIAL, NULL},
|
||||
{"Heinrichs Weikamp", "OSTC Mk2", DC_FAMILY_HW_OSTC, 1, DC_TRANSPORT_SERIAL, NULL},
|
||||
@ -502,6 +503,19 @@ static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dc_filter_mares (dc_transport_t transport, const void *userdata)
|
||||
{
|
||||
static const char *bluetooth[] = {
|
||||
"Mares bluelink pro",
|
||||
};
|
||||
|
||||
if (transport == DC_TRANSPORT_BLE) {
|
||||
return dc_filter_internal_name ((const char *) userdata, bluetooth, C_ARRAY_SIZE(bluetooth));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_descriptor_iterator (dc_iterator_t **out)
|
||||
{
|
||||
|
||||
@ -45,6 +45,8 @@
|
||||
#define SMARTAIR 0x24
|
||||
#define QUAD 0x29
|
||||
|
||||
#define MAXRETRIES 4
|
||||
|
||||
#define ACK 0xAA
|
||||
#define EOF 0xEA
|
||||
|
||||
@ -72,6 +74,9 @@ typedef struct mares_iconhd_device_t {
|
||||
unsigned char version[140];
|
||||
unsigned int model;
|
||||
unsigned int packetsize;
|
||||
unsigned char cache[20];
|
||||
unsigned int available;
|
||||
unsigned int offset;
|
||||
} mares_iconhd_device_t;
|
||||
|
||||
static dc_status_t mares_iconhd_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
|
||||
@ -146,7 +151,79 @@ mares_iconhd_get_model (mares_iconhd_device_t *device)
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
mares_iconhd_transfer (mares_iconhd_device_t *device,
|
||||
mares_iconhd_read (mares_iconhd_device_t *device, unsigned char data[], size_t size)
|
||||
{
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
dc_transport_t transport = dc_iostream_get_transport(device->iostream);
|
||||
|
||||
size_t nbytes = 0;
|
||||
while (nbytes < size) {
|
||||
if (transport == DC_TRANSPORT_BLE) {
|
||||
if (device->available == 0) {
|
||||
// Read a packet into the cache.
|
||||
size_t len = 0;
|
||||
rc = dc_iostream_read (device->iostream, device->cache, sizeof(device->cache), &len);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
device->available = len;
|
||||
device->offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the minimum packet size.
|
||||
size_t length = (transport == DC_TRANSPORT_BLE) ? device->available : size - nbytes;
|
||||
|
||||
// Limit the packet size to the total size.
|
||||
if (nbytes + length > size)
|
||||
length = size - nbytes;
|
||||
|
||||
if (transport == DC_TRANSPORT_BLE) {
|
||||
// Copy the data from the cached packet.
|
||||
memcpy (data + nbytes, device->cache + device->offset, length);
|
||||
device->available -= length;
|
||||
device->offset += length;
|
||||
} else {
|
||||
// Read the packet.
|
||||
rc = dc_iostream_read (device->iostream, data + nbytes, length, &length);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
}
|
||||
|
||||
nbytes += length;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
mares_iconhd_write (mares_iconhd_device_t *device, const unsigned char data[], size_t size)
|
||||
{
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
dc_transport_t transport = dc_iostream_get_transport(device->iostream);
|
||||
|
||||
size_t nbytes = 0;
|
||||
while (nbytes < size) {
|
||||
// Set the maximum packet size.
|
||||
size_t length = (transport == DC_TRANSPORT_BLE) ? sizeof(device->cache) : size - nbytes;
|
||||
|
||||
// Limit the packet size to the total size.
|
||||
if (nbytes + length > size)
|
||||
length = size - nbytes;
|
||||
|
||||
// Write the packet.
|
||||
rc = dc_iostream_write (device->iostream, data + nbytes, length, &length);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
nbytes += length;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
mares_iconhd_packet (mares_iconhd_device_t *device,
|
||||
const unsigned char command[], unsigned int csize,
|
||||
unsigned char answer[], unsigned int asize)
|
||||
{
|
||||
@ -159,7 +236,7 @@ mares_iconhd_transfer (mares_iconhd_device_t *device,
|
||||
return DC_STATUS_CANCELLED;
|
||||
|
||||
// Send the command header to the dive computer.
|
||||
status = dc_iostream_write (device->iostream, command, 2, NULL);
|
||||
status = mares_iconhd_write (device, command, 2);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to send the command.");
|
||||
return status;
|
||||
@ -167,7 +244,7 @@ mares_iconhd_transfer (mares_iconhd_device_t *device,
|
||||
|
||||
// Receive the header byte.
|
||||
unsigned char header[1] = {0};
|
||||
status = dc_iostream_read (device->iostream, header, sizeof (header), NULL);
|
||||
status = mares_iconhd_read (device, header, sizeof (header));
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to receive the answer.");
|
||||
return status;
|
||||
@ -181,7 +258,7 @@ mares_iconhd_transfer (mares_iconhd_device_t *device,
|
||||
|
||||
// Send the command payload to the dive computer.
|
||||
if (csize > 2) {
|
||||
status = dc_iostream_write (device->iostream, command + 2, csize - 2, NULL);
|
||||
status = mares_iconhd_write (device, command + 2, csize - 2);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to send the command.");
|
||||
return status;
|
||||
@ -189,7 +266,7 @@ mares_iconhd_transfer (mares_iconhd_device_t *device,
|
||||
}
|
||||
|
||||
// Read the packet.
|
||||
status = dc_iostream_read (device->iostream, answer, asize, NULL);
|
||||
status = mares_iconhd_read (device, answer, asize);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to receive the answer.");
|
||||
return status;
|
||||
@ -197,7 +274,7 @@ mares_iconhd_transfer (mares_iconhd_device_t *device,
|
||||
|
||||
// Receive the trailer byte.
|
||||
unsigned char trailer[1] = {0};
|
||||
status = dc_iostream_read (device->iostream, trailer, sizeof (trailer), NULL);
|
||||
status = mares_iconhd_read (device, trailer, sizeof (trailer));
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to receive the answer.");
|
||||
return status;
|
||||
@ -212,6 +289,27 @@ mares_iconhd_transfer (mares_iconhd_device_t *device,
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
mares_iconhd_transfer (mares_iconhd_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize)
|
||||
{
|
||||
unsigned int nretries = 0;
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
while ((rc = mares_iconhd_packet (device, command, csize, answer, asize)) != DC_STATUS_SUCCESS) {
|
||||
// Automatically discard a corrupted packet,
|
||||
// and request a new one.
|
||||
if (rc != DC_STATUS_PROTOCOL && rc != DC_STATUS_TIMEOUT)
|
||||
return rc;
|
||||
|
||||
// Abort if the maximum number of retries is reached.
|
||||
if (nretries++ >= MAXRETRIES)
|
||||
return rc;
|
||||
|
||||
// Discard any garbage bytes.
|
||||
dc_iostream_purge (device->iostream, DC_DIRECTION_INPUT);
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
|
||||
@ -236,6 +334,9 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_
|
||||
memset (device->version, 0, sizeof (device->version));
|
||||
device->model = 0;
|
||||
device->packetsize = 0;
|
||||
memset (device->cache, 0, sizeof (device->cache));
|
||||
device->available = 0;
|
||||
device->offset = 0;
|
||||
|
||||
// Set the serial communication protocol (115200 8E1).
|
||||
status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_EVEN, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user