Thursday, June 21, 2012

Interfacing the Raymarine ST1000+ Autopilot

I understand that the Raymarine ST1000+ autopilot (and the similar ST2000+) can receive exterior heading data through the SeaTalk bus, specifically from a Raymarine ST40 fluxgate compass. So another project would be to use the heading output from my hi-resolution compass to drive the autopilot.

First I was interested to see what kind of data the autopilot puts on the SeaTalk bus as a talker. I used the following arrangement to monitor the SeaTalk bus.



The autopilot is connected to a serial port of a MAVRIC-IIB board through the SeaTalk interface circuit. The MAVRIC-IIB has an onboard MAX222 chip to provide level conversion between the serial port and the logic level pins of the ATMega128 microcontroller. The SeaTalk datagrams showing on the bus are resent in hex format to a computer running Hyperterminal, through a serial-to-USB converter.

The microcontroller code is an almost direct port of the “SeaTalk Monitor” code available on Thomas Knauf’s website. It can be found at the end of this post. In normal operation (standby or auto), the ST-1000+ sends continuously these 2 datagrams, each one at 1 Hz, with a half-second between them.


9C C1 07 00
84 C6 07 00 00 00 00 00 0F
9C C1 07 00
84 C6 07 00 00 00 00 00 0F
9C C1 07 00
84 C6 07 00 00 00 00 00 0F
9C C1 07 00
84 C6 07 00 00 00 00 00 0F


Here, the 9C datagram encodes the compass heading (in degrees) and the turning direction (left or right).

The 84 datagram also encodes the compass heading and turning direction, plus the autopilot course when in auto mode, and the currently active mode (standby, auto, vane or track mode).

We learn on Thomas Knauf’s site that the 89 datagram encodes the compass heading sent by the ST40 compass instrument, and can be read by the ST1000+ or ST2000+ autopilot. To test this behaviour, I modified the microcontroller code (not shown here) so that it sends an 89 datagram (89 02 04 00 20) after reading each of the 9C and 84 datagram, with a small delay to avoid collisions. The datagram sent (89 02 04 00 20) encodes a heading of 8 degrees.

The monitor then produced this kind of ouput:


9C 01 04 00
89 02 04 00 20
84 06 04 00 00 00 00 00 0F
89 02 04 00 20
9C 01 04 00
89 02 04 00 20
84 06 04 00 00 00 00 00 0F
89 02 04 00 20


From what I observed by playing with the autopilot in standby and auto mode, the 89 datagram is interpreted by the ST-1000+ as a request to perform a heading alignment to the value encoded in the datagram.

So if a custom heading is encoded in an 89 datagram sent on a regular basis, the autopilot will indeed adjusts its heading alignment if required to reproduce this value.


#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdio.h>

unsigned char resh;
char buffer[256];
char hex[]="0123456789ABCDEF";

unsigned int collision_ctr;
unsigned char in_ptr, out_ptr, limit_ptr;
char receiver_buf, byte_ctr;


unsigned char kbhit(void)
{
 // return nonzero if char waiting
 unsigned char b;
 b = 0;
 if(UCSR1A & (1 << RXC1))
  b = 1;
 return b;
}


int uart_putchar0(char c)
{
 loop_until_bit_is_set(UCSR0A, UDRE0);
 UDR0 = c;
 return 0;
}


int main(void)
{
  in_ptr = 0;
  out_ptr = 0;
  limit_ptr = 0;

  /* enable serial port UART1 */  /* Set baud rate : 4800 @ 16 MHz */
  UBRR1L = (unsigned char)(207);
  /* Enable receiver with 9 data bits  */
  UCSR1B = _BV(RXEN1) | _BV(UCSZ12);

  /* enable serial port UART0 */  /* Set baud rate : 9600 @ 16 MHz */
  UBRR0L = (unsigned char)(103);
  /* Enable transmitter  */
  UCSR0B =_BV(TXEN0);

  for(;;)
  {
    if(kbhit())
    {
      // check 9th bit before reading UDR1
      resh = UCSR1B;
      resh = (resh >> 1) & 0x01;

      receiver_buf = UDR1;
 
      if(resh)     //  9th bit set
      {
        if(byte_ctr)   // More characters expected => Collision
        {
          in_ptr = limit_ptr;    // Discard last datagram, restart from beginning 
          collision_ctr++;     // Count collision events 
        }
        buffer[in_ptr++] = '\r';  // Put new command on new line 
        buffer[in_ptr++] = '\n';
        byte_ctr = 255;  // Undefined datagram length, wait for next character
      }
      else
      {
        if(byte_ctr == 254)       // Attribute byte ? 
        byte_ctr = (receiver_buf & 0xF) + 2; // Read expected datagram length
      }

      if(byte_ctr)
      {            // Process valid data bytes, should always be true 
        buffer[in_ptr++] = hex[receiver_buf >>  4]; // Convert Data to hex 
        buffer[in_ptr++] = hex[receiver_buf & 0xF];
        buffer[in_ptr++] = ' ';                     // Seperate by space 
        if(!--byte_ctr)
          limit_ptr = in_ptr;        // Complete datagram ready for output
      }
    }
    else
    {
      if(out_ptr != limit_ptr)          // Characters waiting for Output ?
      {
        // Copy single character from buffer to screen
        uart_putchar0(buffer[out_ptr++]);
      }
    }
  }
  return 1;
}

No comments:

Post a Comment