Fandom

Scratchpad

Uwatec Smart Protocol

215,974pages on
this wiki
Add New Page
Discuss this page0 Share

Ad blocker interference detected!


Wikia is a free-to-use site that makes money from advertising. We have a modified experience for viewers using ad blockers

Wikia is not accessible if you’ve made further modifications. Remove the custom ad blocker rule(s) and the page will load as expected.

This document contains my findings after investigating the protocol for the Uwatec Smart and new Aladin dive computers which use IrDA to transfer data to and from computers.

It is neither complete or correct, however it has allowed me to write a plugin to allow gdivelog to interact with said dive computers.

Setting up IrDA on Linux

This section is not a HOWTO, please see the Infrared HOWTO if this is what you want, these are simply my notes, which you may or may not find helpful.

I used Ubuntu 5.10 and a stir4200 based USB to IrDA adaptor.

When I plugged in the adaptor, Ubuntu recognised it and loaded the stir4200 and irda drivers. lsmod revealed the following:

...
stir4200               14372  0  
irda                  187612  1 stir4200
...

To bring the IrDA subsystem up:

$ sudo irattach irda0 -s

To check that it is running:

$ ps -A | grep ir

Which should return something like:

...
27421 ?        00:00:00 irattach 
27424 ?        00:00:00 irda0
...

Sometimes, I have not determined why or how, IrDA appears to stop working. To rectify this restart IrDA by killing irattach, unplugging and replugging the USB to IrDA adaptor and then using irattach again as above.

To test whether IrDA is up and running use irdadump from the irda-utils package.

$ sudo irdadump

When the dive computer is switched on and it's IrDA port is placed in range of the PC's IrDA port you should see something similar to the below as the output scrolls past:

Discovered: (40,1) 
daddr: ca5a9900  saddr: 9537c2d9  hints: 8000  name: Aladin Smart Pro

If you do not see this, IrDA is not working.

This is my entire knowledge of IrDA on Linux. I can be of no further assitance, so please do not ask. Sorry.

Connecting to the dive computer

Connections are made with the sockets interface.

sir_family should be set to AF_IRDA sir_addr should be set to the daddr returned during discovery sir_lsap_sel must be set to sir_name must be an empty string

Example C code:

#define MAX_DEVICES 10 
#define NUM_TRIES 5 
unsigned short int smart_discover( int fd ) 
{ 
  struct irda_device_list *list; 
  unsigned short int daddr = -1; 
  char *buf; 
  socklen_t len; 
  int i; 
  len = sizeof(struct irda_device_list) + 
    sizeof(struct irda_device_info) * 
      ( DISCOVER_MAX_DEVICES - 1 ); 
  buf = malloc( sizof(char) * len ); 
  if( buf ) { 
   /* Get list of discovered devices */ 
    for ( i=0 ; i < NUM_TRIES ; i++ ) { 
      if ( !getsockopt( fd, SOL_IRLMP, IRLMP_ENUMDEVICES,
        buf, &len) ) { 
        if (!i && len == 4) { 
          sleep(1);  
          continue; /* Ignore first empty entry */ 
        } 
        break; 
      } 
      if (errno == EAGAIN) sleep(1); 
      else {
        perror("discover"); 
        break; 
      } 
    } 
    list = (struct irda_device_list *) buf; 
    if( len > 4 && list->len > 0 ) 
      /* Find and use the first Smart device discovered */ 
      for (i = 0 ; i < list->len ; i++ ) { 
        if( !strncmp( list->dev[i].info, "Aladin Smart", 12 ) ) {
          /* discovered, return daddr */ 
          fprintf( stderr, "Discovered: %s\n",        
            list->dev[i].info); 
          daddr = list->dev[i].daddr; 
        } 
      }  
   } 
   free( buf ); 
 } 
 return daddr; 
} 
gint smart_connect(void) 
{ 
  int fd, rval; 
  struct sockaddr_irda peer; 
  fd = socket( AF_IRDA, SOCK_STREAM, 0 ); 
  if( fd < 0 ) return -1; /* Invalid socket */ 
  peer.sir_addr = smart_discover( fd ); 
  if( peer.sir_addr == -1 ) return -1; /* discover failed */  
  peer.sir_family = AF_IRDA; 
  peer.sir_lsap_sel = 1; 
  peer.sir_name[0]='\0'; 
  if( connect( fd, (struct sockaddr *) &peer, 
    sizeof( struct sockaddr_irda ) ) ) { 
    perror( "connect" ); 
    return -1; /* connect failed */ 
  } 
  fprintf( stderr, "Connected\n" );
  return fd; 
}

Data Format

All data returned from a Smart dive computer is big endian i.e. is returned most significant byte first.

For example, if a double word with the byte sequence 0x01 0x02 0x03 0x04 is returned by the dive computer, it needs to converted to 0x04 0x03 0x02 0x01 to be a valid double word on a PC.

Downloading

Once the connection is made, the following is done to download data from the dive computer to the PC:


Step 1 - Handshake

write 1 byte value = 0x1b
read 1 byte. This must = 0x01
write 5 bytes: 0x1c, 0x10, 0x27, 0, 0
read 1 byte. This must be = 0x01

If either of the returned bytes have a value other than 0x01, then the handshake has failed.


Step 2 - Get the internal time of dive computer

write byte: 0x26
read 4 bytes

This is a big endian double word equal to the number of semiseconds (2 semisecond = 1 seconds) since 2000-01-01 00:00:00. The time of the PC should be taken at the same time this value is retrieved so that the two can be related and the start date and time of individual dives can be calculated.

Example C code:

unsigned int get_reference_time_semiseconds(void)
{
  /* Returns 2000-01-01 00:00:00 in semiseconds */
  struct tm t;
  t.tm_sec = 0;
  t.tm_min = 0;
  t.tm_hour = 0;
  t.tm_mday = 1;
  t.tm_mon = 0;
  t.tm_year = 100;
  return mktime(&t) * 2;
}
unsigned long get_system_time_semiseconds(void)
{
  /* returns system time in semiseconds since */
  /* 2000-01-01 00:00:00.                     */
  struct timeval tv;
  gettimeofday( &tv, NULL );
  return ( tv.tv_sec * 2 ) + ( tv.tv_usec / 500000 ) 
    - get_reference_time_semiseconds();
}
time_t calculate_dive_start_date_time_seconds( 
  unsigned long sys_time_semiseconds,
  unsigned long smart_internal_time_semiseconds,
  unsigned long smart_dive_time_semiseconds )
{
  /* returns the start date and time of a dive as seconds      */
  /* since epoc. Adjusting for DST is also needed - see below. */
  return ( ( smart_dive_time_semiseconds +
    sys_time_semiseconds - smart_internal_time_semiseconds ) 
    + get_reference_time_semiseconds() ) / 2;
}

To adjust for daylight saving, use the following:

IF sys_time is DST AND dive_time is not DST
  THEN increment dive start time by 1 hour
IF sys_time is not DST AND dive_time is DST
  THEN decrement dive start time by 1 hour


Step 3 - get the serial number of the dive computer

write byte: 0x20
read 4 bytes

This is a big endian double word.


Step 4 - get dive computer type

 write byte: 0x16
 read byte

The meaning of the read byte is interpreted as such:</para>

  • 0x10 : SmartPRO
  • 0x12 : Aladin Tec or Prime
  • 0x14 : SmartCOM
  • 0x18 : SmartTEC


Step 5 - get the number of byte of data to be returned by dive computer

write 9 bytes: 0xc6, from when (4 bytes), 0x10, 0x27, 0, 0

The "from when (4 bytes)" above are a big endian double word containing the date and time, in semi-seconds from 2000-01-01 00:00:00, from which to start downloading dives. If you want to download all dives they should be filled with zeros. To convert system time to "smart" time:

unsigned long convert_sys_time_seconds_to_smart_time(time_t timer)
{
  /* Convert given seconds from epoc (timer) to smart time */
  /* in semiseconds.                                       */
  /* This can be used to tell the smart dive computer from */
  /* what date and time to down load dives from.           */
  return timer * 2 - get_reference_time_semiseconds();
}
read 4 bytes

The read bytes are a big endian double word containing the number of bytes that will be returned by the dive computer. See the section Internal Time.


Step 6 - download the data

write 9 bytes: 0xc6, from when (4 bytes), 0x10, 0x27, 0, 0

The "from when (4 bytes)" 4 bytes should be the same as step 5 - it does not make sense for them not to be.

Read <=32 byte blocks until the required number of 
bytes (determined in step 5) have been recieved.

Also on Fandom

Random wikia