Talking to open collector bus serial device

I'm trying to talk to an Icom amateur radio (model 706MKIIG) using the radio's 5v TTL open collector bus serial interface (http://www.plicht.de/ekki/civ/civ-p0a.html). The interface is exactly compatible with the Arduino serial output, as far as I can tell, since I have no problem sending commands to the radio at high data rates. The only difference is that instead of using TX, RX and ground pins, the radio uses data and ground.

The problem comes when I try to read data from the radio. I reset the Arduino. The Arduino commands the radio to spit back its frequency. After 1 sec, the Arduino spits out the contents of the serial buffer. In the serial monitor I see the command sent by the Arduino (because it's an open collector bus) and the radio's response. Perfect!

I try to repeat the exchange. But from now on, the serial buffer spews junk (numbers around 0x30). Occasionally I can make out the Arduino's command, and then see part of the radio's response. Then more junk. I try flushing the serial buffer before each request but Serial.flush() doesn't seem to do anything.

I measure the voltage of the data bus and it is always 5v, except for a dip when a command is sent/recieved. If the line is high, why am I seeing all that junk? Why do I get a perfect call and response only after the Arduino resets? Why doesn't Serial.flush seem to do anything?

Here is the wiring a kind man told me to try:

Radio ground -- Arduino ground
Radio data -- Arduino RX
Radio data -- 2k2 pullup -- 5v
Radio data -- 1N914 -- Arduino TX (cathode on Arduino TX side)

Here is the code:

void setup() {
  Serial.begin(19200);
}


void loop() {

  Serial.flush();
  // order radio to send frequency
  Serial.write(0xFE);
  Serial.write(0xFE);
  Serial.write(0x58);
  Serial.write(0xE0);
  Serial.write(0x03);
  Serial.write(0xFD); 
  
  delay(1000);
 
  Serial.println();
  
  for(int i=1;i<=17;i++) {
    
    int response = Serial.read();
    Serial.print(response,HEX);Serial.print(' ');    //get response
 }
  
  Serial.println();

  delay(1000);
  
}

Serial monitor:

þþXàý //Arduino's request, looks weird because it's raw data
FE FE 58 E0 3 FD FE FE E0 58 3 0 76 46 1 0 FD // Arduino's request (red) Radio's response (blue) as read from serial buffer. Perfect!
þþXàý
D A 46 45 20 46 45 20 35 38 20 45 30 20 33 20 46
þþXàý
44 20 46 45 20 46 45 20 45 30 20 35 38 20 33 20 30
þþXàý
20 37 36 20 34 36 20 31 20 30 20 46 44 20 D A FE
þþXàý
FE 58 E0 3 FD FE FE E0 58 3 0 76 D A 44 20 41 // You can see the Arduino's request and part of the radio's response, between lots of junk. Why?
þþXàý
20 34 36 20 34 35 20 32 30 20 34 36 D A 34 34 20
þþXàý
32 30 20 34 36 20 34 35 20 32 30 20 D A 32 30 20

You haven't got the correct wiring set up for the radio. It uses the ICOM CI-V bus protocol. I have the IC735 which uses the same protocol and interface. I'll see if I can find (or make) a diagram of the interface that you need. I was thinking of hooking up my Arduino to the radio but at the moment it works fine talking to the N1MM logger program on the PC so I haven't tried too hard yet.

Pete

You also seem to have connected your radio's Tx and Rx to the same pins used by the serial monitor so they are going to confuse each other.

Pete

I've seen the computer interfaces for CI-V, but the problem is that the computer needs a level conversion, while the Arduino doesn't.

I'm not very knowledgeable about circuits, but the interface I have now looks like it should work fine, based on the schematic. Radio low = bus low. Arduino TX low = bus low. Could the voltage drop across the diode be causing problems?

It also looks fine under multimeter testing - the bus is high when idle, and if either the Arduino or the radio transmits, the bus dips low. If I manually ground either the TX pin or the radio's data pin to simulate data transfer, the bus goes low - from 5v to a few millivolts. Arrgh.

shakes fist at heavens

I'm trying to get my CI-V interface to work with the Arduino. I'll let you know when I've got it working.

Pete

One piece of the puzzle: Apparently Arduino 1.0's Serial.flush does something other than flush the serial buffer, for reasons that elude me. With Arduino 0023, I can flush the serial buffer successfully before a command is issued to minimize the junk. Now the Arduino can search through the buffer to find "0xFD 0xFD" and capture the desired data.

Still, it would be nice to not have to do this.

I'll try to use a scope to see why the Arduino RX is receiving stuff despite the the rock-solid high logic level measured by the multimeter.

Using Arduino 0023's Serial.flush() and the interface described above, I can read the radio's frequency with this code:

int incoming[18] ;
int read_freq[4] ;

void setup() {
  Serial.begin(19200); 
}

void loop() {
  
  Serial.flush();
  Serial.print(0xFE,BYTE);
  Serial.print(0xFE,BYTE);
  Serial.print(0x58,BYTE);  // 706MKIIG's address
  Serial.print(0xE0,BYTE);
  Serial.print(0x03,BYTE);
  Serial.print(0xFD,BYTE);
  
  delay(50);
  
  for(int i=0;i<17;i++)
    incoming[i] = Serial.read(); 
  
  Serial.println();
  
  for (int i=0;i<17;i++) {
    Serial.print(incoming[i], HEX);
    Serial.print(" "); 
  }
  
  Serial.println();
  
  read_freq[0] = (incoming[15] / 16 % 16);
  read_freq[1] = (incoming[15] % 16 * 100) + (incoming[14] / 16 % 16 * 10) + (incoming[14] % 16);
  read_freq[2] = (incoming[13] / 16 % 16 * 100) + (incoming [13] % 16 * 10) + (incoming[12] / 16 % 16);
  read_freq[3] = (incoming [12] % 16 * 100) + (incoming[11] / 16 % 16 * 10) + (incoming[11] % 16);

  for(int i=0;i<=3;i++) {
   Serial.print(read_freq[i]);
   Serial.print(" "); 
  }

  Serial.println();

  delay(3000);

}

This program flushes the serial buffer, sends a command, reads the first 17 numbers in the serial buffer corresponding to the Arduino's command and the radio's response, decodes the BCD data and puts it in a four-element array (GHz, MHz, kHz, and Hz).

This code blithely assumes that the first 17 things in the buffer are what it wants.

I tried connecting my IC735 using just the diode but I couldn't get the Arduino to receive anything even though I could send stuff.

But I did get a TTL-TTL interface working with the Arduino. The one I had been using was for connection to RS-232 which only requires one transistor in each direction. But two transistors in each direction are required for TTL-TTL. I've attached a hand-drawn diagram of the circuit - use at your own risk.
I wrote a program to test the interface and it can set the VFO or the frequency and also read the radio's response. It can also read what the radio sends when you spin the dial or change the mode etc.
The sketch is for V1.0 and uses the SoftwareSerial library so that the rig is on pins 2 and 3 which the USB to be connected as well so that you can monitor what's going on.

// Simple test of an IC735 interface. This just switches the VFO
// from A to B and back again once every second
// or sets the frequency to 28000 kHz and increments it by 100Hz every second
// depending upon whether VFO is defined here
//#define VFO

// If you start up the serial monitor you should see the response to either
// the set vfo or set frequency command which will be:
// FEFE0104FBFD
// The two FE bytes are the "preamble".
// The 01 is the address the packet is being sent to which in this case
// is the Arduino. The "computer" end is usually set to 0x01.
// The 04 is the device sending the packet which is the address assigned
// (by ICOM) to the IC735. The ICOM706 will send 0x58 here.
// The FB is the positive acknowledgement of the vfo or frequency command
// The FD is the end of the packet.

// However, I don't understand why the serial monitor never shows the 
// command that was transmitted. The ICOM CI-V bus is a single wire interface
// and anything transmitted is also looped around back into the receiver.
// Transmission and reception are obviously working so, hmmmmmmmmmmmm.



#include <SoftwareSerial.h>
#define rxPin 2
#define txPin 3
#define LED_PIN 13

// Set up the ICOM transceiver address - pick whichever one is appropriate
// The IC735 is 04
char rig_addr = 0x04;
// The 706mkii
//char rig_addr = 0x58;

// The computer address is 0x01 and is "hard-coded" since there's only
// one device other than the rig itself on the bus and that is the Arduino


// set up a new serial port
SoftwareSerial mySerial =  SoftwareSerial(rxPin, txPin);

unsigned char vfo = 0;
void setup() {
  // define pin modes for tx, rx, led pins:
  pinMode(rxPin, INPUT);
  digitalWrite(rxPin,1);
  pinMode(txPin, OUTPUT);
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);
  
  mySerial.begin(9600);
  Serial.begin(9600);
}

long frequency = 28000000;

unsigned char ichar;


void send_freq(long freq)
{
  char f[16],*fp;
  int i;
  unsigned char ob;
  
  sprintf(f,"%08ld",freq);
  fp = &f[6];
  // For now send all 8 digits
  for(i=0;i<4;i++) {
    // convert the ascii digits to pairs of BCD digits in one byte
    ob = (*fp++ & 0x0f) << 4;
    ob |= *fp++ & 0x0f;
    mySerial.write(ob);
    fp -= 4;
  }  
}


void loop() {
  char is[16];

#ifdef VFO
  // Send command to switch VFO
  mySerial.write(0xFE);
  mySerial.write(0xFE);
  mySerial.write(rig_addr);
  mySerial.write(0x01);
  // set vfo command
  mySerial.write(0x07);
  mySerial.write(vfo);
  mySerial.write(0xFD);
#else
  // send command to change the frequency
  mySerial.write(0xFE);
  mySerial.write(0xFE);
  mySerial.write(rig_addr);
  mySerial.write(0x01);
  // Set frequency
  mySerial.write(0x05);
  // The frequency
  send_freq(frequency);
  mySerial.write(0xFD);
  frequency += 100;
#endif
  // wait until everything has been sent (V1 !)
//  mySerial.flush();
  // The icom CI-V interface reflects back everything that
  // is sent, so this soaks it up and throws it away
  while(mySerial.available() > 0) {
    ichar = mySerial.read();
    if(ichar == 0xFD)Serial.println("FD");
    else {
      sprintf(is,"%02X",ichar);
      Serial.write(is);
    }
  }
  digitalWrite(LED_PIN, vfo);
  vfo ^= 1;
  delay(1000); 
}

Pete

I'm getting very reliable results with a bunch of nested IF statements that check for the preamble.

I also made a little browser-based rig control thing. To send a command, it changes src of an Iframe, with the radio command contained in the URL's query string. The Arduino+Ethernet receives the command, sends it to the radio, and radio/Arduino's response is visible in the Iframe. With Jquery, you can have scroll wheel tuning that is just as good as Ham Radio Deluxe's, without the bloatware and the always-on PC.

Hi there

I have been able to communicate with the icom rig(706mk2g) using the above code to switch the transceiver to a frequency and increment it 10 khz which is fine i have not been able to receive data from the icom rig like display frequency even if the transceive option is set to On @ 9k6 still no joy ,Anybody out there know of how to get a beter circuit as a buffer for the rx side of the CIV data as i tried the diagram with the 2n2222 ttl to ttl converter both channels work for the tx side (testing them independently )but in the rx no data is received ..No scope to test it snif

Any clues out there i can also not get any info on the serial monitor after getting some basic code that sends all data received from the rig to the arduino rx port .

Any help would be appreciated

You must not call Serial.read() until you've checked there is at least one character in the
buffer with Serial.available() - otherwise you'll just see nonsense.

The "garbage" is caused by your program/wiring. You need to connect the radio to two other pins and use softwareSerial to do the comms. Remember that ANTTHING you send goes to both the radio AND the monitor.

This is the first line of "grabage"

D A 46 45 20 46 45 20 35 38 20 45 30 20 33 20 46

If you look up the ascii you find if read cr lf F E space F E space 5 8 space E 0 space 3 space F

This code is causing the juck

  Serial.println();
  
  for(int i=1;i<=17;i++) {
    
    int response = Serial.read();
    Serial.print(response,HEX);Serial.print(' ');    //get response
 }
  
  Serial.println();

looks like the radio is echoing the rubbish its getting from you.

@MarkT

You must not call Serial.read() until you've checked there is at least one character in the
buffer with Serial.available() - otherwise you'll just see nonsense.

Read returns -1 if there is no data that's why it returns an Int.

Mark