ARDUINO ICOM CI-V address translator / transverter interface

Project presentation

To operate on the 4m (70 MHz) band, I run a transverter, which converts all RX and TX signals from 70 Mhz to 28 MHz. 

High-end ICOM transceivers have a a possibility to operate in TRANSVERTER mode : in this mode, the RF (to be considered as IF = intermediate frequency) in/out is available on the transverter port, and the display can present the operating frequency with a programmable  offset. 

This is very handy to operate, but unfortunately on the other hand the CI-V CAT data is always remaining at the IF frequency ...  so spots from the DX cluster cannot be directly passed to command the frequency, and in return, the electronic log will register QSO's at the IF-frequency instead of  the effective working frequency. Unless your logging / DX-cluster software is extremely smart and able to put your transceiver in TRANSVERTER mode or apply a frequency offset.

But even in this case, what with your PA (like the SPE),  if it is controlled for changing bands & antenna's by the CAT info  ?

Solution : the ARDUINO CI-V address translator and transverter interface hereby presented will handle all the above issues in a complete transparent manner, and can even power-up the transverter if required !

The project presented has been realized / tested with Icom IC-7600 TRX, MixW32 v2.16 logging software and OZ2M 4m transverter.

Functional description

First of all, the ICOM transceiver can be put into TRANSVERTER mode ( in and out ) by 2 methods:

Now, the Arduino module is 'virtually' inserted between the PC (CAT Controller) and your transceiver, and provides a separate CI-V output for the PA CAT data.  Physically, it is connected (simply plugged in with 3.5mm plug) as an extra device to the (existing) CAT line between PC and TRX, and provides a separate output port for the PA.

RED 3.5mm socket for PA, BLUE & GREEN for TRX / PC

The trick is that it will ijn fact function as a 'proxy server' between PC, TRX and PA:

Between PC and TRX, it will listen for commands (frequency & mode) coming from the PC towards a certain address, and retransmit these on the same CAT line to the TRX on the actual TRX address. And in return, frequency & mode information from the TRX will be translated towards the PC as coming from the 'virtual TRX' address. 

While translating addresses, the module will apply a frequency offset and take into account if the transverter must be powered-up and the TRX set to TRANSVERTER mode.   When the transverter is manually powered up, the module will check if the frequency offset is valid and possible (e.g. selecting on the TRX 20m  band will generate an ERROR). 

On the side, the actual operating frequency will be made available to the PA on a separate CAT port. The information will be sent in the CI-V  'tranceive' format, addressed  to all listeners.

The actual working frequency , mode and transverter status are displayed on a 2x 8 LCD  for convenience. A switched output (by relay conatcts) for powering-up the Transverter is provided, as alternative the transverter status can be fed to the module.

Power, relay contact and transverter status available on the DB9 connector

Limitations

The Icom CI-V CAT system is based on a single 'bus' where all data is exchanged based on addresses, but where collisions are possiible and likely ! 

Ideally, the proxy should have been inserted between 2 separate CAT lines (TRX-proxy and proxy-PC), but this would have required the capability of listening simultaneously on 2 lines - something that is not possible on low-end ARDUINO controllers, unless choosing the MEGA board, which offers 4 on board serial ports (see further K9JM Icom CI-V router). On the smaller Arduino boards, more than one serial port can be declared, but listening is only possible on one at time. On the other hand, transmitting simultaneously on more than one port ( in this case TRX-PC   and PA  CAT lines ) is possible !

So, when running on only one listening port , the timing becomes essential and critical. 

As is, the systems works nicely & smoothly with the described setup ( IC-7600 & MixW32 v2.16) , all running on 9k6. Only the essential commands (frequency & mode) are handled by the proxy, no other commands so far. When a command is passed from PC to TRX, the proxy will look for an acknowledge of TRX and emit a short beep as confirmation, but will not attempt to re-issue the command till the ack is received.  Output for SPE is available on 9k6 as well.

When the TRX is set into 'transceive' mode,  when rotating the VFO dial, the frequency info is smoothly retransmitted towards the PC, and off course mode changes are immediately transferred.

 

Construction

The project was built in an EDDYSTONE aluminium case 60 x 110 x 30 mm.PAy attention to decoupling of RF of all I / O lines.

 

More info about the CI-V protocol

More insight information about the CI-V protocol can be found on following sites, should you want to adapt the sketch or experiment yourself:

 

Arduino sketch

Below is the sketch  or download it here.   It was compiled with IDE version 1.0.6 - IMPORTANT : Should you get errors when compiling, use the same version of IDE !  You still can download previous versions from ARDUINO website .  

IMPORTANT: Please note that the software & device concept is presented 'as is' with no guarantees whatsoever and can be used free of license cost by individual  HAMs, but not  used for any commercial purpose, as the intellectual property remains entirely by the author.  



///////////////////////////////////////////////////////
//                                                   //
//     CI-V ICOM CAT TRANSLATOR FOR USE WITH         //
//          70 Mhz to 28 Mhz Transverters            //
//               & Power Amplifiers                  //
//                by ON7EQ 03/2016                   //
//                                                   //
///////////////////////////////////////////////////////

// compiled with IDE v 1.6.8

// see http://www.plicht.de/ekki/civ/index.html  for in depth explanation about CI-V

#include "SoftwareSerial.h"

// On arduino NANO pins 2 and 3 are interrupt capable --> use for RX lines !

// Declare CAT port to transceiver / control PC

#define TRX_RX  (2)
#define TRX_TX  (4)

// Declare CAT port to  PA

#define PA_RX   (3) // but PA is TX only ....
#define PA_TX   (5)


SoftwareSerial DebugSerial (0, 1);

SoftwareSerial CATserialTRX (TRX_RX, TRX_TX);
// Use pins 2 and 4 to talk to the CAT of TRX. 2 is the RX pin, 4 is the TX pin
// Connect the RX pin to the CAT output through a 4k7 resistor.


// Declare CAT port to PC, ATU, Power Amp, ..

SoftwareSerial CATserialPA (PA_RX, PA_TX);
// Use pins 3 and 5 to talk to the CAT of PA. 3 is the RX pin, 5 is the TX pin
// Connect the RX pin to the CAT output through a 4k7 resistor.


#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
// LiquidCrystal connect LCD display (rs, enable, d4, d5, d6, d7)  & connect rw of LCD to ground

LiquidCrystal lcd(12, 11, 10, 9, 8, 7);

////////////////////////////////////////////
////////// DEFINE KEY VARIABLES  ///////////
////////////////////////////////////////////

int cat_TRX_baud = 9600;               // TRX CAT baud set
int cat_PA_baud =  9600;               // PA CAT baud set
int DebugBaud =   38400;               // Debug Baud set

#define TRX_address   (122)            // 122 = HEX $7A = IC-7600
#define PROXY_address (102)            // 102 = HEX $66 = IC-746Pro / IC-7400

#define XVRTER_band    (112)           // 112 = $70 Mhz
#define XVRTER_band_m1 (105)           // 105 = $69 MHz - minus one
#define XVRTER_band_p1 (113)           // 113 = $71 MHz - plus one

#define IF_band  (40)                  // 40 = $28 MHz
#define IF_band_m1  (39)               // 39 = $27 MHz  - minus one
#define IF_band_p1  (41)               // 40 = $29 MHz  - plus one

#define RelayOutPin  (6)               // Relay output pin. H = XVERTER active , relay is energized

#define BeepPin  (A2)                   // Define output pin for BEEPER.  2.4 kHz beeper type SATG1205 for example

#define XVRTERpin (A0)                 //  Low = XVERTER active:  Use as 'hardware' indfication of Xverter active
#define Debugpin  (A1)                 //  Low = DEBUG  select  (--> monitor out on COM port)

// Remark: Pin 13 & on board LED = used to show data on CI-V line. (flashing when data is detected)


// Some standard Icom CI-V CAT addresses, do not alter !

byte all_TRX_address = 0;              // $00 = multicast address to all TRX listening on CAT line, this is 'CI-V transceiver mode'
byte PC_address = 224;                 // $E0 = address of PC controlling


/// SOME OTHER VARIABLES

byte TRVTRmode_HW = 0;                 // Transverter mode initiated by hardware line : 0 = OFF / 1 = Xverter ON
byte TRVTRmode_CAT = 0;                 // Transverter mode initiated by CAT command : 0 = OFF / 1 = Xverter ON

byte Overflow = 0;                     // Overflow flag

byte DEBUG = 0;                        // If no DEBUG mode, no output monitoring on RS-232

// For maximum performance, only run DEBUG mode when required !

byte beep1 = 0;                        // Beep alarm
byte beep2 = 0;                        // Beep alarm

byte Freq_command = 0; 
byte Mode_command = 0; 

int i;
int j;

int incomingTRX;                       //incoming character on TRX side
int incomingPC;                        //incoming character on PC side

int buffget_CATtrx[63] ;               // the receive buffer on TRX end
int buffget_CATpc[63] ;                // the receive buffer on PC end
int mem_freq_byte[15] ;                // the frequency bytes memory
int mem_mode_byte[15] ;                // the frequency bytes memory

int buffget_CATtrx_check[5];           // the receive buffer on TRX end for OK/NG response

long  lastCatTRX = 0;
long  lastCatPC = 0;

byte NoCat = 0;

int unsigned long l00_MHZ = 0;
int unsigned long MHZ = 0;
int unsigned long KHZ = 0;
int unsigned long HZ = 0;
int unsigned long QRG = 0;

int unsigned long catTime = 0;
int unsigned long TRXcatTime = 0;

byte heart [8] = {                    // build special character for 'heartbeat' indication
  B00000,
  B00000,
  B11011,
  B10101,
  B10001,
  B01010,
  B00100,

};

////////////////////////////////////////////////////////////
////////////////////   S E T U P  //////////////////////////
////////////////////////////////////////////////////////////


void setup() {

  // Check if DEBUG mode

  digitalWrite(Debugpin, HIGH);          // activate pull-up

  if (analogRead (Debugpin) > 512) {
    DEBUG = 0;
  }

  else        {
    DEBUG  = 1;
  }


  if (DEBUG == 1) DebugSerial.begin(DebugBaud);   // debug

  CATserialTRX.begin(cat_TRX_baud);
  CATserialPA.begin(cat_PA_baud);

  pinMode (BeepPin, OUTPUT);
  digitalWrite(BeepPin, LOW);
  tone (BeepPin, 2400, 200);

  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  lcd.clear();

  // Print a message to the LCD.

  lcd.print("ON7EQ v4");
  lcd.setCursor(0, 1);
  lcd.print("CATproxy");

  delay (1000);
  lcd.clear();
  lcd.print("lsn $");
  if (PROXY_address < 10) lcd.print("0");
  lcd.print(PROXY_address , HEX);
  lcd.setCursor(0, 1);
  lcd.print("snd $");
  if (TRX_address < 10) lcd.print("0");
  lcd.print(TRX_address , HEX);
  delay (3000);
  lcd.clear();

  if ( DEBUG  == 1)  {
    lcd.print("  DEBUG ");
    lcd.setCursor(0, 1);
    lcd.print("Ser ");
    if (DebugBaud == 9600) lcd.print("9k6");
    if (DebugBaud == 19200) lcd.print("19k2");
    if (DebugBaud == 38400) lcd.print("38k4");
    delay (3000);
  }

  pinMode(13, OUTPUT);

  pinMode(RelayOutPin, OUTPUT);
  digitalWrite(RelayOutPin, LOW);

  digitalWrite(TRX_TX, HIGH);          // activate pull-up
  digitalWrite(PA_TX, HIGH);           // activate pull-up
  
  digitalWrite(XVRTERpin, HIGH);       // activate pull-up


  // LISTEN on TRX port

  CATserialTRX.listen();  // declare the TRX port to listen,  buffer is as well flushed

  if (DEBUG == 1 ) {
    DebugSerial.println ("");
    DebugSerial.println ("********** PROXY CI-V *************");
    DebugSerial.println ("");
    DebugSerial.print ("Listening for PC on $");
    if (PROXY_address < 10) DebugSerial.print ("0");
    DebugSerial.println(PROXY_address , HEX);
    DebugSerial.print ("Sending to TRX on   $");
    if (TRX_address < 10) DebugSerial.print ("0");
    DebugSerial.println(TRX_address , HEX);
    DebugSerial.println ("");
    DebugSerial.println ("********** PROXY CI-V *************");
    DebugSerial.println ("");
    DebugSerial.println ("");
    delay (2000);
    while (CATserialTRX.available()) CATserialTRX.read(); // clean buffer
  }

  // create special character heartbeat

  lcd.createChar(1, heart);
  lcd.setCursor (4, 0);
  lcd.write (1);

  catTime = millis() ;      // reset timer
  TRXcatTime = millis();    // reset timer

  while (CATserialTRX.available()) CATserialTRX.read(); // clean buffer
}

///////////////////////////////////////////
////////////  L   O   O  P  ///////////////
///////////////////////////////////////////


void loop() {

  // Check if DEBUG mode

  if (analogRead (Debugpin) > 512) {
    DEBUG = 0;
  }

  else        {
    DEBUG  = 1;
  }

  // Check if XVERTER enabled by HW line

  if (analogRead (XVRTERpin) < 512)  {    //Yes
    if (TRVTRmode_HW == 0) {
      tone(BeepPin, 2000, 20);
      delay(60);
      tone(BeepPin, 2500, 20);
    }
    TRVTRmode_HW = 1;
  }
  else {                                  // No
    if (TRVTRmode_HW == 1)  {
      tone(BeepPin, 2500, 20);
      delay(60);
      tone(BeepPin, 2000, 20);
    }
    TRVTRmode_HW = 0;
  }

  lcd.setCursor(0, 0);

  if ((TRVTRmode_HW == 1) or (TRVTRmode_CAT == 1)) {
    lcd.print("XVTR");
    digitalWrite(RelayOutPin, HIGH);
  }
  else {
    lcd.print("Norm");
    digitalWrite(RelayOutPin, LOW);
  }

  //-------------------------------------------------------------------
CAT_TRX_next:           // CAT TRX is CAT line for commands to/from TRX
  //-------------------------------------------------------------------

  if ((millis() - catTime) > 5000) {
    NoCat = 1;
    lcd.setCursor(0, 0);
    lcd.print(" NO CAT ");
    lcd.setCursor(0, 1);
    lcd.print("SIGNAL !");
    digitalWrite(RelayOutPin, LOW);
    if (beep1 == 0) {
      beep1 = 1;
      tone (BeepPin, 2400, 10);
    }

  }
  else if ((millis() - TRXcatTime) > 8000) {
    lcd.setCursor(0, 0);
    lcd.print("NO REPLY");
    lcd.setCursor(0, 1);
    lcd.print("FROM TRX");
    digitalWrite(RelayOutPin, LOW);
    if (beep2 == 0) {
      beep2 = 1;
      tone (BeepPin, 2200, 30);
      delay(100);
      tone (BeepPin, 2400, 30);
      delay(100);
      tone (BeepPin, 2600, 30);
    }
  }

  else {NoCat = 0; }
  
  // START OF DATAGRAM CAPTURE

  if (CATserialTRX.available() > 0) {          // There are bytes available in the buffer ?



    if (CATserialTRX.overflow()) {             // indicate overflow in DEBUG mode
      if (DEBUG == 1 )DebugSerial.println(">>>>>>>>>  SoftwareSerial overflow !!!");
      while (CATserialTRX.available()) CATserialTRX.read(); // clean buffer
    }


    incomingTRX = CATserialTRX.read();

    if ((incomingTRX == 254)   ) {            // look for an FE = start of CAT sequence , no overflow (mutilated string ?)
      goto CAT_TRX_start;                     // it is possibly a start !
    }
  }

  delay (1);  // allow buffer to fill


  goto CAT_TRX_next;                         // no specific byte received, so keep monitoring buffer

  //-------------------------------------------------------------------
CAT_TRX_start:                                   // 1st byte was a 254
  //-------------------------------------------------------------------

  delay (20);                                     // delay is required to allow buffer to fill : too low = buffer not filled ! Too high = not smooth response

  for ( i = 0; i < 10; i++) {                     // search for end,  9 bytes is the longest datagram

    if (CATserialTRX.available() > 0) {           // there is dat in buffer !

      buffget_CATtrx[i] = CATserialTRX.read();     // load buffget with next characters

      String byte_string = String(buffget_CATtrx[i], HEX);

      // Sniffer !

      if (DEBUG == 1 )  {
        if (buffget_CATtrx[i] < 10) DebugSerial.print ("0");  // format Serial monitor print
        DebugSerial.print (byte_string);
        DebugSerial.print (" ");
      }
      if (buffget_CATtrx[i] ==  253   ) {
        if (DEBUG == 1) DebugSerial.println ("");
        goto process_string_TRX;
      }                                           // end of string detected

      delay( 1 );                                 //delay 1 ms if true, leave time for buffer to work
    }
  }

  goto CAT_TRX_start;                             // it is possibly a start !


  //-------------------------------------------------------------------
process_string_TRX:                   // Process the string on TRX line
  //-------------------------------------------------------------------

  catTime = millis();                    // Reset timestamp CAT data received
  beep1 = 0;

  if ((buffget_CATtrx[0] == 254)) {      // is the 2nd character again an FE ?

    goto valid_string_TRX;               // yes, now process string !
  }


  goto CAT_TRX_next;                     // no, capture another CAT string !

  //-------------------------------------------------------------------
valid_string_TRX:                    // Process the string on TRX line
  //-------------------------------------------------------------------


  if (DEBUG == 1 ) DebugSerial.println (""); // CR  on serial sniffer

  digitalWrite(13, HIGH);
  lcd.setCursor(4, 0);
  lcd.write(1);

  ////////////////////////////////////////////////////////////////////
  ///////// Case : Command string  from PC to PROXY/TRX
  ////////////////////////////////////////////////////////////////////

  // --> check origin & destination of datagram  !

  if ((buffget_CATtrx[1] == PROXY_address) and  ( buffget_CATtrx[2] ==  PC_address )  )   {   // to proxy address  from PC

    NoCat = 0; // clear CAT alert

    /////////////// check command $03 or $04 = read frequency or mode

    if ((buffget_CATtrx[3] == 3) or (buffget_CATtrx[3] == 4)) {

      // translate on CAT line
      //CATserialTRX.flush();  
      delay (20);
      digitalWrite(13, LOW);
      lcd.setCursor(4, 0);
      lcd.write (1);


      // DEBUG

      if (DEBUG == 1 ) {
        //DebugSerial.println ("  ");
        DebugSerial.print ("read ");
        if (buffget_CATtrx[3] == 3) DebugSerial.print ("freq ");
        if (buffget_CATtrx[3] == 4) DebugSerial.print ("mode ");
        DebugSerial.print ("command (PROXY --> TRX) :  ");
        DebugSerial.print("FE FE ");  // $FE
        DebugSerial.print(TRX_address, DEC);
        DebugSerial.print(" ");
        DebugSerial.print(PC_address, DEC);
        DebugSerial.print(" ");
        DebugSerial.print(buffget_CATtrx[3], DEC);
        DebugSerial.print(" ");
        DebugSerial.print("FD");   // $FD
        DebugSerial.println(" ");
        DebugSerial.println(" ");
      }
      // retransmit  and change destination address to physical address TRX

      CATserialTRX.write(254);  // $FE
      CATserialTRX.write(254);
      CATserialTRX.write(TRX_address);
      CATserialTRX.write(PC_address);
      CATserialTRX.write(buffget_CATtrx[3]);
      CATserialTRX.write(253);   // $FD

    }

    /////////////// check command $00 or $05 as 5th byte in datagram = set frequency

    if (((buffget_CATtrx[3] == 0) or (buffget_CATtrx[3] == 5)) and (buffget_CATtrx[8] == 0)  ) {  // CATtrx[8] == 0 because no QSY possible if QRG > 100 Mhz

     Freq_command = 1;
                        
          mem_freq_byte[5] = buffget_CATtrx[5] ;          // put bytes in memory
          mem_freq_byte[6] = buffget_CATtrx[6] ;
          mem_freq_byte[7] = buffget_CATtrx[7] ;               
            
      //
      // Check if Tranverter must be ON

      // Must be switched on

      if ((buffget_CATtrx[7] ==  XVRTER_band_m1) or  (buffget_CATtrx[7] ==  XVRTER_band)  or (buffget_CATtrx[7] ==  XVRTER_band_p1)) {

        if (TRVTRmode_CAT == 0) {
          tone(BeepPin, 2000, 20);
          delay(40);
          tone(BeepPin, 2500, 20);
          //CATserialTRX.flush();  
          delay(20);
          TRVTRmode_CAT = 1;
          CATserialTRX.write(254);        // $FE
          CATserialTRX.write(254);
          CATserialTRX.write(TRX_address);
          CATserialTRX.write(PC_address);
          CATserialTRX.write(26);         // $1A
          CATserialTRX.write(05);         // $05
          CATserialTRX.write((byte)00);
          CATserialTRX.write(113);        // $71
          CATserialTRX.write(01);         // $01  command to set XVERTER ON
          CATserialTRX.write(253);        // $FD
        }
      }

      // Must be switched off

      else {
        if (TRVTRmode_CAT == 1) {
          tone(BeepPin, 2500, 20);
          delay(40);
          tone(BeepPin, 2000, 20);
          delay(20);
          TRVTRmode_CAT = 0;

          CATserialTRX.write(254);              // $FE
          CATserialTRX.write(254);
          CATserialTRX.write(TRX_address);
          CATserialTRX.write(PC_address);
          CATserialTRX.write(26);               // $1A
          CATserialTRX.write(05);               // $05
          CATserialTRX.write((byte)00);
          CATserialTRX.write(113);              // $71
          CATserialTRX.write((byte)00);         // command to set XVERTER in AUTO mode
          CATserialTRX.write(253);              // $FD
        }
      }

      delay (5);
      digitalWrite(13, LOW);
      lcd.setCursor(4, 0);
      lcd.write (1);

      // DEBUG

      if (DEBUG == 1 ) {
       
        DebugSerial.print ("set frequency command (PROXY --> TRX) :  ");
        DebugSerial.print("FE FE ");  // $FE
        DebugSerial.print(TRX_address, DEC);
        DebugSerial.print(" ");
        DebugSerial.print(PC_address, DEC);
        DebugSerial.print(" ");
        DebugSerial.print(buffget_CATtrx[3], DEC);
        DebugSerial.print(" ");
        DebugSerial.print(buffget_CATtrx[4], DEC);
        DebugSerial.print(" ");
        DebugSerial.print(buffget_CATtrx[5], DEC);
        DebugSerial.print(" ");
        DebugSerial.print(buffget_CATtrx[6], DEC);
        DebugSerial.print(" ");
        DebugSerial.print(buffget_CATtrx[7], HEX);
        DebugSerial.print(" ");
        DebugSerial.print(buffget_CATtrx[8], DEC);
        DebugSerial.print(" ");
        DebugSerial.print("FD");   // $FD
        DebugSerial.println(" ");
        DebugSerial.println(" ");
      }

      // retransmit  and change destination address to physical address TRX
      
      delay(20); 
      CATserialTRX.write(254);  // $FE
      CATserialTRX.write(254);
      CATserialTRX.write(TRX_address);
      CATserialTRX.write(PC_address);

      // 3 possible bytes :
      //CATserialTRX.write((byte)00);           // Set Frequency Command with no reply
      CATserialTRX.write((byte)05);             // Set Frequency Command with reply
      //CATserialTRX.write(buffget_CATtrx[3]);  // Repeat original byte

      CATserialTRX.write((byte)00);             // Assume precision up to 100 Hz, no more required
      //CATserialTRX.write(buffget_CATtrx[4]);

      CATserialTRX.write(buffget_CATtrx[5]);
      CATserialTRX.write(buffget_CATtrx[6]);

      if ((TRVTRmode_HW == 1) or (TRVTRmode_CAT == 1)) {      // 70 MHz Xverter is active !

        if      (buffget_CATtrx[7] ==  XVRTER_band_m1) CATserialTRX.write(IF_band_m1);    // 69 MHz
        else if (buffget_CATtrx[7] ==  XVRTER_band)    CATserialTRX.write(IF_band);       // 70 MHz
        else if (buffget_CATtrx[7] ==  XVRTER_band_p1) CATserialTRX.write(IF_band_p1);    // 71 MHz
        else    (CATserialTRX.write(153));          // If Xverter active, and not 69/70/71 MHz, send 99 Mhz = frequency setting not accepted !
      }

      else  {
        CATserialTRX.write(buffget_CATtrx[7]);       // No Xverter active !
      }

      CATserialTRX.write((byte)00);                  // frequency is always < 100 MHz
      CATserialTRX.write(253);                       // $FD
      } 
      // end set frequency command
    

    //////////////// check command $01 or $06 = set mode & filter

    if ((buffget_CATtrx[3] == 1) or (buffget_CATtrx[3] == 6)) {
            
      // translate on CAT line
      
      delay (20);
      digitalWrite(13, LOW);
      lcd.setCursor(4, 0);
      lcd.write (1);

      // retransmit  and change destination address to physical address TRX

      CATserialTRX.write(254);          // $FE
      CATserialTRX.write(254);
      CATserialTRX.write(TRX_address);
      CATserialTRX.write(PC_address);
      
      // 2 possible bytes :
      CATserialTRX.write(01);           //  $01 for 3rd byte, no response required
      //CATserialTRX.write(buffget_CATtrx[3]);
      
      CATserialTRX.write(buffget_CATtrx[4]);
      CATserialTRX.write(buffget_CATtrx[5]);
      if (buffget_CATtrx[6] != 253) CATserialTRX.write(buffget_CATtrx[6]);  //possibly byte for filter width setting

      CATserialTRX.write(253);         // $FD

    } // end command set mode & filter

  }

  ////////////////////////////////////////////////////////////////////
  ///////// Case : REPLY  from TRX to PC
  ////////////////////////////////////////////////////////////////////

  // --> check origin & destination of datagram  !


  if (((buffget_CATtrx[1] == PC_address) ) and  ( buffget_CATtrx[2] ==  TRX_address )  )   {   // From TRX to PC Controller

    TRXcatTime = millis ();          //reset timer
    beep2 = 0 ;
    delay (20);
    digitalWrite(13, LOW);
    lcd.setCursor(4, 0);
    lcd.write (1);

   //  Check for confirmation of frequency change received & valid 
   //  if success, response from rig will be : $FE $FE to-adr fm-adr $FB $FD 
   
    if (Freq_command == 1){

    if ((buffget_CATtrx[3] == 251) and (buffget_CATtrx[4] == 253))  { 
            tone (BeepPin, 2400, 5);          // emit beep to confirm QSY request performed
            Freq_command = 0;
            goto parse_next_datagram;
           }

       else  {              // NOK, need to re-issue frequency command 1 time
  
       delay (20);
        
      // Check if Tranverter must be ON

      // Must be switched on

      if ((mem_freq_byte[7] ==  XVRTER_band_m1) or  (mem_freq_byte[7] ==  XVRTER_band)  or (mem_freq_byte[7] ==  XVRTER_band_p1)) {

        if (TRVTRmode_CAT == 0) {
          
          TRVTRmode_CAT = 1;
          CATserialTRX.write(254);          // $FE
          CATserialTRX.write(254);
          CATserialTRX.write(TRX_address);
          CATserialTRX.write(PC_address);
          CATserialTRX.write(26);           // $1A
          CATserialTRX.write(05);           // $05
          CATserialTRX.write((byte)00);
          CATserialTRX.write(113);          // $71
          CATserialTRX.write(01);           // $01  command to set XVERTER ON
          CATserialTRX.write(253);          // $FD
        }
      }

      // Must be switched off

      else {
        if (TRVTRmode_CAT == 1) {
          
          TRVTRmode_CAT = 0;
          CATserialTRX.write(254);          // $FE
          CATserialTRX.write(254);
          CATserialTRX.write(TRX_address);
          CATserialTRX.write(PC_address);
          CATserialTRX.write(26);           // $1A
          CATserialTRX.write(05);           // $05
          CATserialTRX.write((byte)00);
          CATserialTRX.write(113);          // $71
          CATserialTRX.write((byte)00);     // command to set XVERTER in AUTO mode
          CATserialTRX.write(253);          // $FD
        }
      }
      
      // retransmit  and change destination address to physical address TRX
      delay (20);
      CATserialTRX.write(254);              // $FE
      CATserialTRX.write(254);
      CATserialTRX.write(TRX_address);
      CATserialTRX.write(PC_address);

      CATserialTRX.write((byte)00);         // Set Frequency Command with no reply
        // CATserialTRX.write((byte)05);    // Set Frequency Command with reply --> could cause loop !
        //CATserialTRX.write(mem_freq_byte[3]);
      
      CATserialTRX.write((byte)00);         // Assume precision up to 100 Hz, no more required
        //CATserialTRX.write(mem_freq_byte[4]);

      CATserialTRX.write(mem_freq_byte[5]);
      CATserialTRX.write(mem_freq_byte[6]);


      if ((TRVTRmode_HW == 1) or (TRVTRmode_CAT == 1)) {      // 70 MHz Xverter is active !

        if      (mem_freq_byte[7] ==  XVRTER_band_m1) CATserialTRX.write(IF_band_m1);
        else if (mem_freq_byte[7] ==  XVRTER_band)    CATserialTRX.write(IF_band);
        else if (mem_freq_byte[7] ==  XVRTER_band_p1) CATserialTRX.write(IF_band_p1);
        else    (CATserialTRX.write(153));          // If Xverter active, and not 69/70/71 MHz, send 99 Mhz = frequency setting not accepted !
      }

      else  {
            CATserialTRX.write(mem_freq_byte[7]);       // No Xverter active !
            }

      CATserialTRX.write((byte)00);                    // frequency is always < 100 MHz
      CATserialTRX.write(253);                         // $FD
      
      } // end re issue frequency command

  
   } // end confirm OK


parse_next_datagram:   

    ///////// reply to request with frequency info: Bbte 4 must be $03

    if ((buffget_CATtrx[3] == 03)) {

      // DEBUG : serial print

      if (DEBUG == 1 ) {
        DebugSerial.println ("  ");
        DebugSerial.print ("reply (PROXY --> PC) : Frequency =  ");
        DebugSerial.print("FE FE ");  // $FE
        DebugSerial.print(PC_address, DEC);
        DebugSerial.print(" ");
        DebugSerial.print(PROXY_address, DEC);
        DebugSerial.print(" ");
        DebugSerial.print(3, DEC);
        DebugSerial.print(" ");
        DebugSerial.print(buffget_CATtrx[4], DEC);
        DebugSerial.print(" ");
        DebugSerial.print(buffget_CATtrx[5], DEC);
        DebugSerial.print(" ");
        DebugSerial.print(buffget_CATtrx[6], DEC);
        DebugSerial.print(" ");
        DebugSerial.print(buffget_CATtrx[7], DEC);

        DebugSerial.print(" ");
        DebugSerial.print(buffget_CATtrx[8], DEC);
        DebugSerial.print(" ");
        DebugSerial.print("FD");   // $FD
        DebugSerial.println(" ");
        DebugSerial.println(" ");
      }

      // Send freqency info on TRX-PC CAT line
  
      delay(20);
      CATserialTRX.write(254);                  // $FE
      CATserialTRX.write(254);
      CATserialTRX.write(PC_address);
      CATserialTRX.write(PROXY_address);
      CATserialTRX.write(03);                  // Frequency !
      CATserialTRX.write(buffget_CATtrx[4]);
      CATserialTRX.write(buffget_CATtrx[5]);
      CATserialTRX.write(buffget_CATtrx[6]);

      if ((TRVTRmode_HW == 1)  or (TRVTRmode_CAT == 1))  {                           // 70 MHz Xverter
        if (buffget_CATtrx[7] < IF_band_m1)  CATserialTRX.write(153);               // Error : send 99 MHz        
        if (buffget_CATtrx[7] == IF_band_m1)  CATserialTRX.write(XVRTER_band_m1);
        if (buffget_CATtrx[7] == IF_band)     CATserialTRX.write(XVRTER_band);
        if (buffget_CATtrx[7] == IF_band_p1)  CATserialTRX.write(XVRTER_band_p1);
        if (buffget_CATtrx[7] > IF_band_p1)  CATserialTRX.write(153);              // Error : send 99 MHz     
      }

      else       {
        CATserialTRX.write(buffget_CATtrx[7]);   // No Xverter
        }

      CATserialTRX.write(((byte)00));
      CATserialTRX.write(253);         // $FD

 
      /////////////// Send freqency info on PA CAT line, in CI-V mode, originating from TRX

      CATserialPA.write(254);          // $FE
      CATserialPA.write(254);
      CATserialPA.write((byte)00);
      CATserialPA.write(TRX_address);
      CATserialPA.write((byte)00);     // Frequency as CI-V multicast 
      CATserialPA.write(buffget_CATtrx[4]);
      CATserialPA.write(buffget_CATtrx[5]);
      CATserialPA.write(buffget_CATtrx[6]);

      if ((TRVTRmode_HW == 1) or (TRVTRmode_CAT == 1)) {       // 70 MHz Xverter is active
        if (buffget_CATtrx[7] < IF_band_m1)  CATserialPA.write(153);  // error ! send 99 MHz = not permitted
        if (buffget_CATtrx[7] == IF_band_m1)  CATserialPA.write(XVRTER_band);  // always send 70 MHz
        if (buffget_CATtrx[7] == IF_band)     CATserialPA.write(XVRTER_band);
        if (buffget_CATtrx[7] == IF_band_p1)  CATserialPA.write(XVRTER_band);  // always send 70 MHz
        if (buffget_CATtrx[7] > IF_band_p1)  CATserialPA.write(153);  // error ! send 99 MHz = not permitted     
      }
      else  {
        CATserialPA.write(buffget_CATtrx[7]);   // No Xverter
      }

      CATserialPA.write((byte)00);          // frequency always < 100 MHz
      CATserialPA.write(253);               // $FD

      //  PRINT operating QRG on LCD Display

      l00_MHZ = (buffget_CATtrx[8]);                // Will always be zero ....
      l00_MHZ = l00_MHZ - (((l00_MHZ / 16) * 6));   // Transform bytes ICOM CAT


      MHZ = (buffget_CATtrx[7]);
      MHZ = MHZ - (((MHZ / 16) * 6));               // Transform bytes ICOM CAT

      KHZ = buffget_CATtrx[6];
      KHZ = KHZ - (((KHZ / 16) * 6));               // Transform bytes ICOM CAT

      HZ = buffget_CATtrx[5];
      HZ = HZ - (((HZ / 16) * 6));                  // Transform bytes ICOM CAT

      QRG = ((MHZ * 10000) + (KHZ * 100) + (HZ * 1)); // QRG variable stores frequency in MMkkkH  format

      if (NoCat == 1) {   // clear 1 st line
          lcd.setCursor(0, 0);
          lcd.print("        ");
          }
      

      lcd.setCursor(0, 1);

      if ((TRVTRmode_HW == 1) or (TRVTRmode_CAT == 1)) {       // 70 MHz Xverter
        if (buffget_CATtrx[7] < IF_band_m1)  lcd.print("ERR");
        if (buffget_CATtrx[7] == IF_band_m1)  lcd.print(" 69");     
        if (buffget_CATtrx[7] == IF_band) lcd.print(" 70");
        if (buffget_CATtrx[7] == IF_band_p1)  lcd.print(" 71");
        if (buffget_CATtrx[7] > IF_band_p1)  lcd.print("ERR");
      }
      else {
        if (MHZ < 100) lcd.print(" ");
        if (MHZ < 10)  lcd.print(" ");
        lcd.print(MHZ, DEC);
      }

      lcd.print(".");

      if (KHZ < 10)  lcd.print("0");
      lcd.print(KHZ, DEC);

      if (HZ < 10)  lcd.print("0");
      lcd.print(HZ, DEC);


    }     // end of frequency info

    // mode info: Byte 4 must be $04

    if ((buffget_CATtrx[3] == 4)) {
      //CATserialTRX.flush();  
      delay(20);
      CATserialTRX.write(254);  // $FE
      CATserialTRX.write(254);
      CATserialTRX.write(PC_address);
      CATserialTRX.write(PROXY_address);
      CATserialTRX.write(4);  // Mode !
      CATserialTRX.write(buffget_CATtrx[4]);
      CATserialTRX.write(253);   // $FD

      lcd.setCursor(5, 0);
      if      (buffget_CATtrx[4] == 00 )  lcd.print("LSB");
      else if (buffget_CATtrx[4] == 01 )  lcd.print("USB");
      else if (buffget_CATtrx[4] == 02 )  lcd.print("AM ");
      else if (buffget_CATtrx[4] == 03 )  lcd.print("CW ");
      else if (buffget_CATtrx[4] == 04 )  lcd.print("FSK");
      else if (buffget_CATtrx[4] == 05 )  lcd.print("FM ");
      else if (buffget_CATtrx[4] == 06 )  lcd.print("CWR");
      else lcd.print(" - ");

      //heartbeat
      lcd.setCursor (4, 0);
      lcd.print(" ");


    } // End mode filter info

  } // end datagram From TRX to PC Controller


  // CI-V 'transceive mode' in multicast

  if ((buffget_CATtrx[1] == 0)  and  (buffget_CATtrx[2] ==  TRX_address ) and  (buffget_CATtrx[3] == 0)) {    // Frequency info to ALL from Transceiver

    TRXcatTime = millis ();  //reset timer

    // translate address to :  To PC from proxy

    delay (20);  // allow PC to receive data
    digitalWrite(13, LOW);
    lcd.setCursor(4, 0);
    lcd.write (1);

    CATserialTRX.write(254);  // $FE
    CATserialTRX.write(254);
    CATserialTRX.write(PC_address);
    // CATserialTRX.write((byte)00);          // Address to all in CI-V multicast : not recommended !
    CATserialTRX.write(PROXY_address);
    CATserialTRX.write(3);  // frequency info
    // CATserialTRX.write((byte)00);          // Frequency in CI-V multicast : not recommended !
    CATserialTRX.write(buffget_CATtrx[4]);
    CATserialTRX.write(buffget_CATtrx[5]);
    CATserialTRX.write(buffget_CATtrx[6]);

    if ((TRVTRmode_HW == 1) or (TRVTRmode_CAT == 1)) {       // 70 MHz Xverter active
        if (buffget_CATtrx[7] < IF_band_m1)  CATserialTRX.write(153); // Error : send 99 MHz        
        if (buffget_CATtrx[7] == IF_band_m1)  CATserialTRX.write(XVRTER_band_m1);
        if (buffget_CATtrx[7] == IF_band)     CATserialTRX.write(XVRTER_band);
        if (buffget_CATtrx[7] == IF_band_p1)  CATserialTRX.write(XVRTER_band_p1);
        if (buffget_CATtrx[7] > IF_band_p1)  CATserialTRX.write(153);  // Error : send 99 MHz 
    }
    else           {
      CATserialTRX.write(buffget_CATtrx[7]); // No Xverter
    }

    CATserialTRX.write((byte)00); // always < 100 MHz
    CATserialTRX.write(253);   // $FD


// Send freqency info on PA CAT line, in CI-V mode, originating from TRX

      CATserialPA.write(254);  // $FE
      CATserialPA.write(254);
      CATserialPA.write((byte)00);
      CATserialPA.write(TRX_address);
      CATserialPA.write((byte)00);  // Frequency as CI-V multicast to all!
      CATserialPA.write(buffget_CATtrx[4]);
      CATserialPA.write(buffget_CATtrx[5]);
      CATserialPA.write(buffget_CATtrx[6]);

      if ((TRVTRmode_HW == 1) or (TRVTRmode_CAT == 1)) {       // 70 MHz Xverter is active
        if (buffget_CATtrx[7] < IF_band_m1)  CATserialPA.write(153);  // error ! send 99 MHz = not permitted
        if (buffget_CATtrx[7] == IF_band_m1)  CATserialPA.write(XVRTER_band);  // always send 70 MHz
        if (buffget_CATtrx[7] == IF_band)     CATserialPA.write(XVRTER_band);
        if (buffget_CATtrx[7] == IF_band_p1)  CATserialPA.write(XVRTER_band);  // always send 70 MHz
        if (buffget_CATtrx[7] > IF_band_p1)  CATserialPA.write(153);  // error ! send 99 MHz = not permitted 
      }
      else  {
        CATserialPA.write(buffget_CATtrx[7]);   // No Xverter
      }

      CATserialPA.write((byte)00);          // frequency always < 100 MHz
      CATserialPA.write(253);               // $FD

   

  //  PRINT operating QRG on LCD display

    l00_MHZ = (buffget_CATtrx[8]);
    l00_MHZ = l00_MHZ - (((l00_MHZ / 16) * 6));   // Transform bytes ICOM CAT

    MHZ = (buffget_CATtrx[7]);
    MHZ = MHZ - (((MHZ / 16) * 6));               // Transform bytes ICOM CAT

    KHZ = buffget_CATtrx[6];
    KHZ = KHZ - (((KHZ / 16) * 6));               // Transform bytes ICOM CAT

    HZ = buffget_CATtrx[5];
    HZ = HZ - (((HZ / 16) * 6));                  // Transform bytes ICOM CAT

    QRG = ((MHZ * 10000) + (KHZ * 100) + (HZ * 1)); // QRG variable stores frequency in MMkkkH  format

    if (NoCat == 1) {   // clear 1 st line
          lcd.setCursor(0, 0);
          lcd.print("        ");
          }
    
    lcd.setCursor(0, 1);

    if ((TRVTRmode_HW == 1) or (TRVTRmode_CAT == 1)) {       // 70 MHz Xverter active
      if (buffget_CATtrx[7] < IF_band_m1)   lcd.print("ERR");
      if (buffget_CATtrx[7] == IF_band_m1)  lcd.print(" 69");     
      if (buffget_CATtrx[7] == IF_band)     lcd.print(" 70");
      if (buffget_CATtrx[7] == IF_band_p1)  lcd.print(" 71");
      if (buffget_CATtrx[7] > IF_band_p1)   lcd.print("ERR");
    }
    else   {
      if (MHZ < 100) lcd.print(" ");
      if (MHZ < 10)  lcd.print(" ");
      lcd.print(MHZ, DEC);
    }

    lcd.print(".");

    if (KHZ < 10)  lcd.print("0");
    lcd.print(KHZ, DEC);

    if (HZ < 10)  lcd.print("0");
    lcd.print(HZ, DEC);

    CATserialTRX.flush();                                 // Wait till TX buffer is flushed
    while (CATserialTRX.available()) CATserialTRX.read(); // clear RX buffer, no process of own transmission !!!

    lcd.setCursor(4, 0);
    lcd.print(" ");

  } //end of CI-V 'transceive mode' in multicast// End of loop