/**************************************************************************** seatalk_wireless_remote_rx_1.c USE AT YOUR OWN RISK! This program is a remote control receiver for Raytheon's Autohelm ST4000 autopilot using the Seatalk(tm) network protocol. It also switches the Raytheon instrument lamps on and off. The following Seatalk protocol is extracted from Thomas Knauf's web site: www.thomasknauf.de/seatalk.htm Message protocol FROM wireless transmitter * The command simply tells what key (row/col) was pressed. Key-to-Seatalk associations are done in the receiver. * Each 9600 baud message contains a command and checksum: 0xff // wake up the receiver 0xff // wake up the receiver 0xff // wake up the receiver 'J' 'S' 'F' cMsgCode // which key was pressed 'J' + 'S' + 'F' + cMsgCode // checksum Message protocol TO Seatalk bus * Each 4800 baud message contains between 3 and 18 characters: * COMMAND byte (the only byte with the command-bit set) ATTRIBUTE byte, specifying the total length of the message in the least significant nibble: Most significant 4 bits: 0 or part of a data value Least significant 4 bits: Number of additional bytes beyond the mandatory data byte DATA byte (mandatory, meaning than the smallest message is 3 bytes) DATA bytes (optional, up to 15 additional data bytes, meaning that longest messages is 18 bytes) Serial Data Transmission 11 bits are transmitted for each byte: * 1 Start bit (0V) * 8 Data Bits (least significant bit transmitted first, bit ON = +12V) * 1 Command/Data bit (+12V if command byte, 0V if other) * 1 Stop bit (+12V) Collision Management Bus should be idle for at least 2mS (+12V for at least 10/4800 seconds). Transmitter listens to it's own transmission and recognizes when its message has been corrupted by a another talker. In this case it abandons the remaining bytes in the message, waits for the bus to become free again, and then retransmits the whole message after a delay. LAMP INTENSITY CODES ------------------------------ 30 00 00 Lamp off 30 00 0C Lamp full on KEYSTROKE CODES ------------------------------ 86 11 01 FE AUTO 86 11 02 FD STANDBY 86 11 03 FC TRACK 86 11 05 FA -1 86 11 06 F9 -10 86 11 07 F8 +1 86 11 08 F7 +10 86 11 04 FB DISP? +5 +5 | | 14 4 ---------- | B0 A0 |-17-- out to Seatalk (transistor driver) | A1 |-18-- in from Seatalk (transistor buffer) | B2 A2 |-1--- LED | B3 | | B1 |-7--- input from Parallax 433MHz receiver | B5 B2 |-8--- debug serial output | B6 B4 |-10-- GND (LVP) | | 6MHz XTAL-15-| 16F628 | XTAL-16-| | ---------- 5 | Gnd KEYBOARD ASSIGNMENTS C1 C2 C3 +------------------------+ R1 | -1 Track +1 | | | R2 | -5 Auto +5 | | | R3 | -10 Standby +10 | | | R4 | Lamp x DISP | +------------------------+ ***************************************************************************/ /* The following include should contain 16F84 or 16F628. */ #include <16F628.h> #include#fuses HS, NOPROTECT, PUT, BROWNOUT, NOMCLR, NOLVP, NOCPD, NOWDT #use fast_io ( A ) #use delay ( clock = 6000000 ) #use rs232 ( baud = 9600, rcv = PIN_B1, xmit = PIN_B2, ERRORS ) #byte PORT_A = 5 #bit SEATALK_TX_OUT = PORT_A.0 #bit SEATALK_RX_IN = PORT_A.1 #bit LED = PORT_A.2 #define LAMP_OFF 0x00 #define LAMP_ON 0x0C #define PLUS_ONE 0x07 #define MINUS_ONE 0x05 #define PLUS_TEN 0x08 #define MINUS_TEN 0x06 #define STANDBY 0x02 #define AUTO 0x01 #define TRACK 0x03 #define DISP 0x04 #define KEY_NONE 0 #define KEY_C1_R1 1 #define KEY_C1_R2 2 #define KEY_C1_R3 3 #define KEY_C1_R4 4 #define KEY_C2_R1 5 #define KEY_C2_R2 6 #define KEY_C2_R3 7 #define KEY_C2_R4 8 #define KEY_C3_R1 9 #define KEY_C3_R2 10 #define KEY_C3_R3 11 #define KEY_C3_R4 12 void SendKeystrokeMsg ( char cData ); void SendLampMsg ( char cX ); char SendByte ( char cError, char cCommand, char cData ); char SendBit ( char cError, char cBit ); void CheckBus ( void ); char ParseMsg ( void ); char GetNextChar ( void ); static char cLampState, cKey, cBuffer [ 15 ], cWritePointer, cReadPointer; void main ( void ) { char cKey, cX; delay_ms ( 100 ); // programming delay SEATALK_TX_OUT = LOW; // allow output to float set_tris_a ( 0b11111010 ); // A0, A2 are outputs, A1 is input setup_counters ( RTCC_INTERNAL, WDT_18MS ); // 256 * 4uS = 1.024mS timer wrap port_b_pullups ( TRUE ); cLampState = LAMP_OFF; // preset toggle for ( cX = 0; cX < 10; cX++ ) // power-on flash LED { LED = HIGH; delay_ms ( 50 ); LED=LOW; delay_ms ( 50 ); } //printf ( "READY\n\r" ); cWritePointer = 0; cReadPointer = 0; for ( cX = 0; cX < 15; cX++ ) // clear buffer { cBuffer [ cX ] = 0; } enable_interrupts ( INT_RDA ); enable_interrupts ( GLOBAL ); //while(1){SendBit ( NO, HIGH );SendBit ( NO, LOW );} while ( TRUE ) // do forever { cKey = ParseMsg(); // wait for valid message to come in switch ( cKey ) { case KEY_C1_R1: { SendKeystrokeMsg ( MINUS_ONE ); break; } case KEY_C1_R2: { SendKeystrokeMsg ( MINUS_ONE ); SendKeystrokeMsg ( MINUS_ONE ); SendKeystrokeMsg ( MINUS_ONE ); SendKeystrokeMsg ( MINUS_ONE ); SendKeystrokeMsg ( MINUS_ONE ); break; } case KEY_C1_R3: { SendKeystrokeMsg ( MINUS_TEN ); break; } case KEY_C1_R4: { if ( cLampState == LAMP_OFF ) { cLampState = LAMP_ON; } else { cLampState = LAMP_OFF; } SendLampMsg ( cLampState ); // send DISP keystroke break; } case KEY_C2_R1: { SendKeystrokeMsg ( TRACK ); break; } case KEY_C2_R2: { SendKeystrokeMsg ( AUTO ); break; } case KEY_C2_R3: { SendKeystrokeMsg ( STANDBY ); break; } case KEY_C2_R4: { // not assigned break; } case KEY_C3_R1: { SendKeystrokeMsg ( PLUS_ONE ); break; } case KEY_C3_R2: { SendKeystrokeMsg ( PLUS_ONE ); SendKeystrokeMsg ( PLUS_ONE ); SendKeystrokeMsg ( PLUS_ONE ); SendKeystrokeMsg ( PLUS_ONE ); SendKeystrokeMsg ( PLUS_ONE ); break; } case KEY_C3_R3: { SendKeystrokeMsg ( PLUS_TEN ); break; } case KEY_C3_R4: { SendKeystrokeMsg ( DISP ); break; } } delay_ms ( 50 ); } } void SendKeystrokeMsg ( char cData ) { char cError; do { disable_interrupts ( INT_RDA ); // turn off interrupt to avoid jitter in bit time CheckBus(); // wait for bus to be idle LED = HIGH; // LED on during message cError = SendByte ( NO, YES, 0x86 ); // command: keystroke cError = SendByte ( cError, NO, 0x11 ); // data: remote control, 1 extra byte (4 total) cError = SendByte ( cError, NO, cData ); // data: PlusOne key cError = SendByte ( cError, NO,~cData ); // data: inverted data enable_interrupts ( INT_RDA ); // turn interrupt back on delay_ms ( 10 ); // LED visible delay LED = LOW; // LED off } while ( cError == YES ); // repeat if message was corrupted } void SendLampMsg ( char cX ) { char cError; do { disable_interrupts ( INT_RDA ); // turn off interrupt to avoid jitter in bit time CheckBus(); // wait for bus to be idle LED = HIGH; cError = LOW; // default no error cError = SendByte ( NO, YES, 0x30 ); // command: lamp cError = SendByte ( cError, NO, 0x00 ); // data: 00 cError = SendByte ( cError, NO, cLampState ); // data: lamp state enable_interrupts ( INT_RDA ); // turn interrupt back on delay_ms ( 10 ); // LED visible delay LED = LOW; // LED off } while ( cError == YES ); // repeat if message was corrupted } char SendByte ( char cError, char cCommand, char cData ) { char cX; if ( cError != YES ) // if no error from previous { cError = SendBit ( cError, HIGH ); // start bit (0V) for ( cX = 0; cX < 8; cX++ ) { cError = SendBit ( cError, ~cData & 0x01 ); // LSB data bit cData >>= 1; // shift right } cError = SendBit ( cError, cCommand ? LOW : HIGH ); // set if command byte, clear if data byte cError = SendBit ( cError, LOW ); // stop bit (+12V) } return ( cError ); } char SendBit ( char cError, char cBit ) { char cX; // this is bit-banged code, it must be adjusted to give 208uS bit times (4800 baud) if ( cError != YES ) // if no error from previous { SEATALK_TX_OUT = cBit; // send bit to output for ( cX = 0; cX < 7; cX++ ) // check output bit periodically { delay_us ( 12 ); // 12 adjust if ( SEATALK_RX_IN == !cBit ) // check if output bit is corrupted by another talker { return ( HIGH ); // return collision error } } return ( LOW ); // return no error, bit sucessfully sent } else { return ( HIGH ); // simply return collision error from before } } void CheckBus ( void ) { char cX; for ( cX = 0; cX < 255; cX++ ) // assumes output is floating to +12V for ~5mS { if ( SEATALK_RX_IN == HIGH ) // check if output bit is corrupted by another talker { cX = 0; // reset count to zero } delay_us ( 7 ); } } #int_rda void int_handler () { if ( kbhit() ) { cBuffer [ cWritePointer ] = getch(); cWritePointer++; if ( cWritePointer >= 15 ) { cWritePointer = 0; } } } char GetNextChar ( void ) { char cX; while ( cWritePointer == cReadPointer ); // wait for character to come in via interrupt cX = cBuffer [ cReadPointer ]; cReadPointer++; if ( cReadPointer >= 15 ) { cReadPointer = 0; } //printf ( " %c\n\r", cX ); return ( cX ); } char ParseMsg ( void ) { char cCmd, cChk, cKey; while ( TRUE ) { while ( TRUE ) { cKey = GetNextChar(); if ( cKey != 'J' ) { break; } cKey = GetNextChar(); if ( cKey != 'S' ) { break; } cKey = GetNextChar(); if ( cKey != 'F' ) { break; } cCmd = GetNextChar(); // command cChk = GetNextChar(); // checksum if ( ( 'J' + 'S' + 'F' + cCmd ) == cChk ) { return ( cCmd ); //done } } } }