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.
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;
}