03/04/2017

My KX3 TicTacMic !


KX3 Microphone with frequency display 

This project came about in the spring of 2016, but I never got to post it here.
At the time there was a discussion going on in the KX3 Yahoo group. about using a KX3 or KX2 for “HF Packing” (pedestrian mobile).

Wayne, N6KR, one of the owners of the Elecraft company, said it was a pity that you couldn’t see the display if you keep the rig in your backpack … so could an external microphone with a display be made ?

Was he serious or not, I don’t know, but it got me thinking … this should be easy with an Arduino !
So I picked some stuff : an Arduino Nano, a small OLED display, a miniature electret microphone, some pushbuttons, resistors, … and started experimenting.

Of course I also needed a small and light box to put it all in, and saw a box of TicTac’s  in my favourite colour … ORANGE … and here we are : the KX3 “TIC TAC” Microphone was born.



The orange box turned out to be a mistake, it made the display less visible, so for anyone building this project, better look for a box with a clear display.

The HARDWARE

The circuit is very simple :



The microphone part of the circuit is wired like the standard MH3 microphone*, with an electret MIC element going straight to the MIC and analog GND (resistors and a cap are provided inside the KX3). The UP and DOWN buttons (with resistors), and a PTT button go to the PTT input and digital GND. All this is connected with one half of a cable with a right-angle 4-pin TRRS connector I found on eBay.
[* One small difference : I didn’t use a toggle switch for the PTT, in the MH3 this disconnects the UP/DN buttons in  TX, but since they are shorted out anyway, there is no practical difference]

The serial communication to the KX3 ACC1 port is very simple too.
Most circuits use a MAX232 integrated circuit, but I found a simpler (and much cheaper !) way. 
The KX3 accepts TTL signals at its input without any problem, but the signals coming out of the KX3 are around 7V, too high for an Arduino. So for this I used a voltage divider with two resistors.
At the beginning I couldn’t get this circuit to work … until I realized that a MAX232 is not only converting RS232 to TTL levels, but also INVERTING the signals !
Luckily, the Arduino SoftwareSerial constructor has an optional argument to do just that : invert the signals … problem solved ! (see Arduino code below)

The Arduino reads the 4 programmable buttons via analog input A0. The buttons are wired along a series of resistors, forming a voltage divider chain. Each button press grounds another connection, generating another voltage on A0, so that it can be determined which button was pressed.

The OLED display is wired to the I2C bus on pins A4 (SDA) and A5(SCL), plus needs 5V and GND too.


I mounted everything on a thin single-sided PCB, ground plane on the back.
Arduinos from eBay typically come with some loose pins (not soldered), so I only mounted pins in the holes that I needed, and carefully drilled the holes for those pins. The pins that connect to GND are soldered directly to the back plane. Holes for the other pins are chamfered so they don’t touch the ground plane.


All connections were then made from pin to pin, Manhattan style ... since it was only a prototype.
Also, I was in a hurry to show off this project to Wayne at the Hamradio 2016 in Friedrichshafen.

As it turned out, Wayne was not at the fair, but Eric, WA6HHQ was … so I was able to show it to him and he looked interested. 
We had a nice chat, and Eric took some pictures of my little “baby” …  hi.
Of course I had to have a “selfie” with Eric in return !



Software 
[thanks to Tony N0RUA for getting me started with some code for reading info from a KX3]

For the test , I just programmed the display to show the operating frequency, and the buttons to send the first 4 keyer memories (for VOICE only the first two would be useable).
The mode display is not implemented yet. If you know your way around the Arduino, you can program the 4 PFn buttons to operate a full Menu , with options and settings, not limited to : switching bands, modes, power level, tuning rate, … and show all that info on the display.

Here the display with a power-on message, and a few seconds later the KX3 is set to 14.062 CW 
(now that I think about it, a bit stupid if you make a "microphone" ... but of course you could also have a paddle connected to the KX in your backpack ;-) :




After pressing the DOWN button for a while, the display shows the new frequency :


See code below, most should be clear from the comments. If not, feel free to ask more info in the comments section, or mail me direct to my address on QRZ.com

I also added comments what you should change for a KX2. I haven’t investigated if I can make a detection of what rig is connected, KX3 or KX2, and make it “auto switching”. 
I’ll leave that to the “wizards” at Elecraft, hi.

Have fun if you make this project ! And of course you may always send me a picture; I’ll gladly post it here.

73 – Luc ON7DQ (KF0CR)


Arduino Sketch :
// KX3 external Display with Remote Control
// by Luc - ON7DQ/KF0CR
// Project started : 6 June 2016
// Last revision : 18 June 2016

// What you need : a KX3 (or KX2) , of course !
// Arduino Nano
// Oled Display 128x64 pixels, Blue or Yelloww/Blue
// Any number of buttons on A0 (voltage divider trick)

// Libraries needed
#include <Wire.h> // needed for I2C
#include <SPI.h>
#include <SoftwareSerial.h>  // for comms to KX3
// replaced the Graphics libs by ASCII only libs >> lots of memory saved !
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"

// KX3 Serial comms
#define BAUD_RATE 9600       // KX3 serial speed
#define LOOP_DELAY 500       // determines rate of polling the KX3

// serial connection to the KX3 :
// RX = KX3 to PC  : to pin 6 via voltage divider (3k9 in series/10k to ground)
// TX = PC to KX3  : direct to pin 7
SoftwareSerial mySerial(6, 7, true);  // (RX, TX, invert)
                                      // invert the bits because no MAX232 is used

// The Oled Display
SSD1306AsciiWire oled;

// 4 buttons + resistor divider chain go to analog pin A0
// define button names
#define btn0      0
#define btn1      1
#define btn2      2
#define btn3      3
#define btnNONE   4

String str = "", freq = "";
char   ch;
int adc_key_in  = 0;
int key         = 0;

void setup()
{
  Serial.begin(9600);
  Serial.println(F("KX3 TicTacMic by ON7DQ"));
  
  // initialize I2C and OLED display
  Wire.begin();
  oled.begin(&Adafruit128x64, 0x3C);
  oled.setFont(Arial_bold_14);
  oled.clear();
  oled.println("ON7DQ TicTacMic");
  oled.println("  for KX3   ");
  delay (2000);
  oled.clear();
  oled.print("FREQ - MODE"); // note : mode not implemented yet

  // connect to KX3
  mySerial.begin(BAUD_RATE);
  mySerial.println("AI0;"); // disable auto info on the KX3
  //  option : do other settings in KX3 (not used here)
  mySerial.println("FA00014062000;"); // set VFO A to some frequency
  mySerial.println("MD3;"); // set CW mode ...
  //  other examples :
  //  mySerial.println("MD6;"); // set DATA mode ...
  //  mySerial.println("DT3;"); // then set submode for PSK-D
  //  mySerial.println("KY VVV DE ON7DQ;"); // send a test msg
  
}

void loop()
{ showFrequencyAndMode(); // mode not implemented yet

  key = read_LCD_buttons();

  switch (key)   // depending on which button was pushed, we perform an action
  {
    case btn0:
      {
        mySerial.println("SWT11;SWT19;"); // send msg 1
        break;
      }
    case btn1:
      {
        mySerial.println("SWT11;SWT27;"); // send msg 2
        break;
      }
    case btn2:
      {
        mySerial.println("SWT11;SWT20;"); // send msg 3
        break;
      }
    case btn3:
      {
        mySerial.println("SWT11;SWT28;"); // send msg 4
                                          // change to  "SWT11;SWT16;" for KX2
        break;
      }
    case btnNONE:
      {
        // do nothing (for now)
        break;
      }
  }
  delay(LOOP_DELAY);
}


// ********** functions

// read the buttons
int read_LCD_buttons()
{
  int adc_key_in = 0;
  for (int i = 0; i < 3; i++) {
    adc_key_in += analogRead(0);      // read the value from the buttons on pin 5 = A0
    delay(2);
  }
  adc_key_in /= 3; // average from 3 reads

  // for checking actual key values :
  //Serial.print ("Key value : ");
  //Serial.println (adc_key_in); delay(100);
  
  // my buttons when read are centered at these values: 0, 131, 319, and 495
  // we add approx 50 to those values and check to see if we are close
  if (adc_key_in > 1000) return btnNONE; 
    // We make this the 1st option for speed reasons since it will be the most likely result
  if (adc_key_in < 50)   return btn3;
  if (adc_key_in < 180)  return btn2;
  if (adc_key_in < 370)  return btn1;
  if (adc_key_in < 550)  return btn0;
  return btnNONE;  // when all else fails, return this...
}

// Display frequency (mode not implemented yet)
void showFrequencyAndMode() {
  //get FREQUENCY
  mySerial.println("FA;");
  // wait for FA00000000000;
  while (mySerial.available() > 0 ) {
    ch = mySerial.read();
    if (ch != ';') str += ch;
    else {
      freq = formatFrequency(str);
      str = "";
    }
  }

  // send to display
  
  oled.setCursor(0, 2);
  oled.clearToEOL();
  oled.print(freq);
}

String formatFrequency(String vfo) {
  String freq = "";

  // e.g. convert '07' to '7'
  freq += String(vfo.substring(5, 7).toInt());

  //freq += ".";
  freq += vfo.substring(7, 10);
  freq += ".";
  freq += vfo.substring(10, 12);
  Serial.print(F("F="));
  Serial.println(freq);
  return freq;
}