Sunteți pe pagina 1din 6

Communicating with ICOM Radios from An Arduino 

 
The Hardware 
 
The IC‐V bus uses 5‐volt logic which is ideal for most Adruino processors, although 
you can use the 3.3‐volt varieties but then you'll have to use some  level 
conversion on the send and receive serial lines. This bus is a single wire system 
that requires an open collector configuration to allow the reading and writing of 
multiple devices on a single line.  There are a zillion ways to implement this but I 
found the following circuit to be reliable plus I had the parts in my junk box. 
 

The Code 

The following code illustrates how to format a command, send the command, 
read any response and then parse the result.  I have not tested this on all versions 
of the Arduino so it’s always possible I’ve overlooked something.  If you discover 
something that you think I should know, send it along and I will update this 
document.  You will also notice that this only covers two or three of the hundreds 
of command sequences documented in the Icom “CI‐V Reference Guide”, plus the 
available commands vary between radio models, so you’re on your own there. 

If you run into a problem you can’t solve on your own, go to the group first.  And 
then if you’re still stuck, drop me a line and I’ll see if I can figure it out.  Keep in 
mind that I’m far from being an expert, and I’m getting old. 

Dave K6DHL 
dave@leddon.net 
 

// We need an additional serial port for the exclusive use of the CI‐V connection, although  
// you could just use Serial, but then you'd have to swap cables around every time you wanted  
// to change the code or debug a problem using the serial monitor. 
 
// If you are using either the Due, Mega or Zero etc. and want to use one of the additional  
// native serial ports Serial1, Serial2, or Serial3, enter your choice below 
#define Icom Serial2 
 
// If you are using an Arduino with only a single serial port, uncomment the following block of 
// code.  About now you're probably asking yourself why I didn't just use a #ifdef block instead of  
// commenting out the code.  It's because I get a compiler error when instantiating SoftwareSerial 
// within the block. Go figure. 
 
//#define RX_Digital_Pin 10  // any uncommitted digital pin 
//#define TX_Digital_Pin 11  // any uncommitted digital pin 
//#include <SoftwareSerial.h>  // this is not supported on the Arduino Due 
//SoftwareSerial Icom(RX_Digital_Pin, TX_Digital_Pin); // RX, TX 
 
// Normally one would use enum Band_Type { Main, Sub } but the compiler doesn't like passing  
// enum types as arguments unless the enum is defined in a header file, just another loveable  
// idiosyncrasy of the Arduino compiler.  To avoid that added complication, I just pass the  
// arguments as integers. 
#define Main 0 
#define Sub 1 
 
// Set this to match the CI‐V Address in the radio.  0x98 is the default for the IC‐7610 
#define Icom_Address 0x98 
 
// Global Variables 
boolean Radio_Ready = false; 
String inString = "";     // a string to hold incoming ascii 
int Main_RF_Gain = 0;  // a place holder for the RF gain so we can restore it after muting 
int Sub_RF_Gain = 0;  // ditto 
 
void setup() 

  Serial.begin(9600); 
  while (!Serial);     // wait for serial port to connect. 
  Icom.begin(19200);   // Must match the CI‐V Baud Rate setting in the radio 
  while (!Icom);     // wait for Icom to connect. 
  Radio_Ready = Is_Radio_Online(); 
  if (Radio_Ready) Serial.println("Radio is online"); else Serial.println("Radio is offline"); 
}; 
 
void loop() 

  if (Radio_Ready) 
  { 
    Serial.print("Frequency = "); 
    Serial.println(Icom_Frequency()); 
  }; 
  delay(2000); 
}; 
 
 
boolean Is_Radio_Online() 

  if (Icom_Frequency() == 0) return false; else return true; 
}; 
 
 
byte bcdToDec(byte val) 

  return ((val / 16 * 10) + (val % 16)); 
}; 
 
byte DecToBcd(byte val) 

  byte Upper = val / 10; 
  byte Lower = val % 10; 
  return Upper * 16 + Lower; 
}; 
 
// Send a command and receive a response 
void Send_Icom_Command(byte *Command, int Byte_Count, byte *Response, int &Length) 

  int i; 
  byte Value; 
 
  delay(50); // Allow time for any previous output to complete 
  while (Icom.available() > 0) Icom.read(); // Empty input buffer 
  for (i = 0; i < Byte_Count; i++) Icom.write(Command[i]); 
  delay(50); // Allow radio time to respond 
 
  Length = 0; 
  while (Icom.available() > 0) 
  { 
    Value = Icom.read(); 
    Response[Length++] = Value; 
  }; 
}; 
 
// Send a command without an expected response 
void Send_Icom_Command(byte *Command, int Byte_Count) 

  for (int i = 0; i < Byte_Count; i++) Icom.write(Command[i]); 
}; 
 
 
int Icom_Frequency() 

  byte In[50]; 
  byte Decimal; 
  int i = 0; 
  int Length; 
  int Frequency = 0; 
  int Multiplier = 1; 
  byte Send_Freq[] = { 0xFE, 0xFE, Icom_Address, 0xE0, 0x3, 0xFD }; 
  boolean Valid_Frequency = false; 
 
  Send_Icom_Command(Send_Freq, sizeof(Send_Freq), In, Length); 
 
  Valid_Frequency = ((In[6] == 0xFE) & (In[7] == 0xFE) & (In[8] == 0xE0) & (In[9] == Icom_Address)); 
  if (!Valid_Frequency) return 0; 
 
  // Frequency Data Example: 
  // Frequency = 7175650 Hz 
  // Data = FE FE 98 E0 3 FD FE FE E0 98 3 50 56 17 7 0 FD 
  // Frequency digits start at byte 11 
 
  for (i = 11; i < 16; i++) 
  { 
    Decimal = bcdToDec(In[i]); 
    Frequency += Decimal * Multiplier; 
    Multiplier *= 100; 
  }; 
 
  return Frequency; 
}; 
 
 
int Icom_Read_RF_Gain() 

  byte In[50]; 
  byte bcd; 
  int i = 0; 
  int Length; 
  int Gain = 0; 
  int Multiplier = 1; 
  byte Read_RF_Gain[] = { 0xFE, 0xFE, Icom_Address, 0xE0, 0x14, 0x2, 0xFD }; 
 
  Send_Icom_Command(Read_RF_Gain, sizeof(Read_RF_Gain), In, Length); 
 
  for (i = 14; i > 12; i‐‐) 
  { 
    bcd = bcdToDec(In[i]); 
    Gain += bcd * Multiplier; 
    Multiplier *= 100; 
  }; 
 
  return Gain; 
}; 
 
// I use this so I don't have to hear the squeal of the antenna analyzer signal coming 
// from my tuner.  After tuning I call Restore_RF_Gain(). 
void Mute_RF_Gain() 

  Icom_Set_Band(Main); 
  delay(50); 
  Main_RF_Gain = Icom_Read_RF_Gain(); 
  Icom_Write_RF_Gain(50); 
  Icom_Set_Band(Sub); 
  delay(50); 
  Sub_RF_Gain = Icom_Read_RF_Gain(); 
  Icom_Write_RF_Gain(50); 
}; 
 
void Restore_RF_Gain() 

  Icom_Set_Band(Sub); 
  delay(50); 
  Icom_Write_RF_Gain(Sub_RF_Gain); 
  Icom_Set_Band(Main); 
  delay(50); 
  Icom_Write_RF_Gain(Main_RF_Gain); 
}; 
 
//  The IC‐7610 is essentially two radios in a single box.  Some commands affect the currently active radio only 
// so we must first switch to either the main or sub band before the execution of any of those commands.  
void Icom_Set_Band(int Band) 

  int Length; 
  byte Command[] = { 0xFE, 0xFE, Icom_Address, 0xE0, 0x7, 0xD0, 0xFD }; 
  if (Band == Sub) Command[5] = 0xD1; 
  Send_Icom_Command(Command, sizeof(Command)); 
}; 
 
void Icom_Write_RF_Gain(int Gain) 

  byte In[50]; 
  byte MSD; 
  byte LSD; 
  int Length; 
  byte Write_RF_Gain[] = { 0xFE, 0xFE, Icom_Address, 0xE0, 0x14, 0x2, 0x2, 0x55, 0xFD }; 
 
  MSD = Gain / 100; 
  LSD = Gain % 100; 
  Write_RF_Gain[6] = DecToBcd(MSD); 
  Write_RF_Gain[7] = DecToBcd(LSD); 
  Send_Icom_Command(Write_RF_Gain, sizeof(Write_RF_Gain), In, Length); 
}; 
 
 

CI‐V Connector Settings 

These are the settings in the IC‐7610 that could affect the operation of the 
preceding code: 
 
CI‐V Baud Rate  19200 
CI‐V Address  98h 
CI‐V Transceive  ON 
CI‐V USB Port  Unlink from [REMOTE] 
 
It is interesting to note that I share the CI‐V bus with an Expert 1.3‐FA amplifier 
(requires Transceive ON) and, so far, haven’t seen any evidence of collisions.  
Normally I would leave Transceive OFF. 
 

S-ar putea să vă placă și