/**************************************************************************** LEVEL_TX.C This program is level sensor. It transmits in Seatalk(tm) network protocol. WORKING CODE Initial diagnostic sequence: * Flashes the card address 1-4 flashes. * One second pause. * Without powering the probes, flashes shorted FETs in physical order starting from the LED side of the card * One second pause. * Powering the probes, flashes FETs in the on-state to show liquid level. * One second pause. * Normal operation thereafter. The following Seatalk protocol is extracted from Thomas Knauf's web site: www.thomasknauf.de/seatalk.htm Message protocol * 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). Listens to it's own transmission and recognizes when its message has been corrupted by a second 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. CODES ------------------------------ FE 01 0x yy Tank address x is yy percent full +5 +5 | | 14 4 ---------- PP ----6-| |-17-- out to Seatalk (transistor driver) P0 ----7-| |-18-- in from Seatalk (transistor buffer) P1 ----8-| | P2 ----9-| | P3 ---10-| 16F628 | P4 ---11-| | A0 ---12-| | A1 ---13-| | A0 A1 4MHz XTAL-15-| | 0 f f XTAL-16-| | 1 0 f ---------- 2 f 0 5 3 0 0 | Gnd -------------------------- | | | BOARD LAYOUT | | | 100% | O | 80% | O | 20% | O | 60% | O O | +12V 40% | O O | Data Probe Power | O O | Gnd -------------------------- ***************************************************************************/ /* The following include should contain 16F84 or 16F628. */ #include < 16F628A.h > #include < jonsinc.h > #fuses HS, NOPROTECT, PUT, NOWDT, BROWNOUT, NOMCLR, NOLVP #use fast_io ( A ) #use standard_io ( B ) #use delay ( clock = 6000000, restart_wdt ) #byte PORT_A = 5 #byte PORT_B = 6 #bit TX_OUT = PORT_A.0 #bit RX_IN = PORT_A.1 #bit LED = PORT_A.2 #define PROBE_POWER PIN_B0 #define PROBE_20 PIN_B1 #define PROBE_40 PIN_B2 #define PROBE_60 PIN_B3 #define PROBE_80 PIN_B4 #define PROBE_100 PIN_B5 #define ADDR_0 PIN_B6 #define ADDR_1 PIN_B7 #define SEATALK_MSGNUM 0xFE #define CMD 1 #define DATA 0 // determine unused seatalk message number void MeasureLevel ( void ); void Blink ( char cFlag ); void SendMsg ( char cData ); char SendByte ( char cError, char cCommand, char cData ); char SendBit ( char cBit, char cError ); void CheckBus ( void ); static char cAddress, cLevel_20, cLevel_40, cLevel_60, cLevel_80, cLevel_100; static char cBuffer [ 10 ]; static long iLevel; void main ( void ) { char cX, cY; delay_ms ( 150 ); // wait for 75mS PUT TX_OUT = LOW; // allow output to float LED = LOW; set_tris_a ( 0b11111010 ); // A0, A2 are outputs, A1 is input output_low ( PROBE_POWER ); // default off setup_counters ( RTCC_INTERNAL, WDT_2304MS ); // 256 * 4uS = 1.024mS timer wrap port_b_pullups ( TRUE ); cAddress = input_b(); cAddress ^= 0b11000000; // invert bits 6 and 7 cAddress &= 0b11000000; // mask on bits 6 and 7 cAddress >>= 6; // shift address to 0 bit index // blink the card address upon initialization (1, 2, 3, or 4 blinks) for ( cY = 0; cY < cAddress + 1; cY++ ) { Blink ( 0 ); } // FET SHORTS CHECK // diag: blink short if FET is off; long if on // blink them in order of 60-40-20-80-100%, RB3-RB2-RB1-RB4-RB5, the physical // order on the board of the FETS starting from the LED end, works whether or // not the probes are in water because the probe power is low. delay_ms ( 1000 ); output_low ( PROBE_POWER ); // turn probe power off MeasureLevel(); // do measures (without turning on probe power) to check for shorted FETs Blink ( !cLevel_60 ); Blink ( !cLevel_40 ); Blink ( !cLevel_20 ); Blink ( !cLevel_80 ); Blink ( !cLevel_100 ); // ACTUAL PROBE READING // diag: blink short if FET is off; long if on // blink them in order of 20-40-60-80-100%, RB1-RB2-RB3-RB4-RB5, the logical order delay_ms ( 1000 ); output_high ( PROBE_POWER ); // turn probe power on to do actual level sensing MeasureLevel(); // do measures (without turning on probe power) to check for shorted FETs Blink ( cLevel_20 ); Blink ( cLevel_40 ); Blink ( cLevel_60 ); Blink ( cLevel_80 ); Blink ( cLevel_100 ); // initialize averaging buffer for ( cX = 0; cX < 10; cX++ ) { cBuffer [ cX ] = 0; } for ( cX = 0; cX < cAddress; cX++ ) { delay_ms ( 300 ); // variable startup delay based on address } delay_ms ( 2000 ); // main loop while ( TRUE ) // do forever { restart_wdt(); // get the reading output_high ( PROBE_POWER ); // turn probe power on MeasureLevel(); // insert the reading into the buffer for ( cX = 0; cX < 9; cX++ ) { cBuffer [ cX ] = cBuffer [ cX + 1 ]; // move value down one location cBuffer [ 9 ] = iLevel; // insert new reading } // average the readings for ( cX = 0; cX < 9; cX++ ) { iLevel += cBuffer [ cX ]; // accumulate readings } iLevel = iLevel / 10; // take average // send the message SendMsg ( iLevel ); LED = ON; delay_ms ( 50 ); LED = OFF; delay_ms ( 950 ); for ( cX = 0; cX < cAddress; cX++ ) { delay_us ( 1 ); // variable adder delay based on address } } } void MeasureLevel ( void ) { iLevel = 0; // init at zero cLevel_20 = OFF; // init at zero cLevel_40 = OFF; // init at zero cLevel_60 = OFF; // init at zero cLevel_80 = OFF; // init at zero cLevel_100 = OFF; // init at zero delay_ms ( 2 ); // delay to allow RC of 1meg resistor to rise // each probe simply adds 20% to the total if ( input ( PROBE_20 ) == HIGH ) // if 20 probe is immersed { cLevel_20 = ON; iLevel += 20; } if ( input ( PROBE_40 ) == HIGH ) // if 40 probe is immersed { cLevel_40 = ON; iLevel += 20; } if ( input ( PROBE_60 ) == HIGH ) // if 60 probe is immersed { cLevel_60 = ON; iLevel += 20; } if ( input ( PROBE_80 ) == HIGH ) // if 80 probe is immersed { cLevel_80 = ON; iLevel += 20; } if ( input ( PROBE_100 ) == HIGH ) // if 100 probe is immersed { cLevel_100 = ON; iLevel += 20; } output_low ( PROBE_POWER ); // turn probe power off } void Blink ( char cLength ) { LED = ON; if ( cLength == ON ) { delay_ms ( 800 ); } else { delay_ms ( 50 ); } LED = OFF; delay_ms ( 300 ); } void SendMsg ( char cData ) { char cError, cX; do { CheckBus(); //wait for bus to be idle cError = SendByte ( NO, CMD, SEATALK_MSGNUM ); // command cError = SendByte ( cError, DATA, 0x01 ); // 1 extra data byte (4 total) cError = SendByte ( cError, DATA, cAddress ); // card address cError = SendByte ( cError, DATA, cData ); // level data if ( cError == YES ) // if bit error occured { for ( cX = 0; cX < 55; cX++ ) // flash LED dimmly for two seconds { LED = ON; delay_ms ( 9 ); LED = OFF; delay_ms ( 27 ); } } } while ( cError == YES ); // repeat if message was corrupted } char SendByte ( char cError, char cCommand, char cData ) { char cX; if ( cError != YES ) { cError = SendBit ( HIGH, cError ); // start bit (0V) for ( cX = 0; cX < 8; cX++ ) { cError = SendBit ( ~cData & 0x01, cError ); // LSB data bit cData >>= 1; // shift right } cError = SendBit ( cCommand ? LOW : HIGH, cError ); // set if command byte, clear if data byte cError = SendBit ( LOW, cError ); // stop bit (+12V) } return ( cError ); } char SendBit ( char cBit, char cError ) { char cX, cY; // depending on crystal, this code adjusted to give 208uS bit times (4800 baud) if ( cError != YES ) // if no incoming error { TX_OUT = cBit; // send bit to output for ( cX = 0; cX < 8; cX++ ) { delay_us ( 10 ); if ( RX_IN == !cBit ) // check if output bit is corrupted by another talker { return ( HIGH ); // return collision error } } return ( LOW ); // return no error } } void CheckBus ( void ) { char cX; for ( cX = 0; cX < 255; cX++ ) // assumes output is floating to +12V for ~5mS { if ( RX_IN == HIGH ) // check if output bit is corrupted by another talker { cX = 0; // reset count to zero restart_wdt(); // CCS compiler doesn't put CLRWDT into short delay_us, apparently } delay_us ( 7 ); } }