Sunday, March 21, 2021

A 6 meter cage dipole using window line.

It's springtime here,  that time of year where 6 meters occasionally opens up.   And I need a decent 6 meter antenna.    I'd love a beam, but I live in an RV and have very limited space.   So a dipole it is.

I've long been aware of cage dipoles and their broader bandwidth verses a regular dipole.  So I thought up a way to make a simple four wire cage dipole for 50 MHz.    Here is the design.

                                 (Click on image for full size, then right-click on it to download.)

In order to test the increase in bandwidth,  I initially just put a singe wire on each leg and swept it with my Blue VNA.   

(single wire dipole being measured)

And here is the sweep of the single wire dipole, showing a bandwidth of 2.6 MHz between the 2:1 points.   One interesting point I noticed.   Modeling and literature all point out that a half wave resonant dipole will have ~74 ohms impedance at it's resonant frequency.   We measured 73.8 ohms, how about that!

Then I finished building the cage and swept it again.

(The cage version being measured.)

Here's the result.   The bandwidth has increased by 77% to 4.6 MHz!

So there we go.  The cage design not only increased bandwidth, but also slightly lowered the impedance and SWR at it's lowest point.    Some might wonder why I didn't trim the antenna to raise the lowest SWR to the center of the band.  Well, 99% of my operation on 6 meters is down at the CW/SSB/digital end of the band, so that's where I wanted the lowest SWR.

Now I'm all ready for those coming 6 meter openings.

Thursday, December 24, 2020

A durable 2 meter, 3 element Yagi antenna built with PVC pipe and window line.

I wanted to build a  2 meter Yagi antenna so I could start playing around on 2 meter SSB and CW.

Initially,  I considered the tape measure Yagi.   

I'd helped another ham build one this past summer and I was impressed with it's performance.   There is a downside to the design though.  In even a mild breeze, the tape measure elements vibrate and sometimes flap around in the wind.

Also,  I live in an RV and travel a lot.   My antenna will be up and down often, and packed away every time I travel.  It has to be durable since it will undoubtedly get bumped around from time to time.

I also wanted a fairly wide bandwidth, providing a low SWR across the band so I could operate down in the SSB region or up in the FM area with good performance and no tuning required.

With durability and bandwidth in mind,  I decided to build the structure out of PVC pipe and the elements out of window line.   PVC is cheap and strong.  If the antenna ever broke from an impact, it would be easy and cheap to repair.   The *fat* elements created by the window line should provide for a broader bandwidth.

Initially, my plan was to use a folded dipole for the driven element.   I've seen many commercial Yagi antennas using one.  Folded dipoles are less noisy and a bit better at rejecting out-of-band signals, also providing a nice uniform pattern.

I spent a couple of days working on that idea, but had trouble getting it to work.   A folded dipole presents around 450 ohms of impedance at it's feed point so you need a matching network to match to the 50 ohm feedline.   However, in a Yagi,  the parasitic elements of the reflector and directors change things.  Yagi's usually have a lower impedance at the feedpoint of the driven element and I just couldn't get a good match to 50 ohms using a trombone coaxial match.

Abandoning that approach,  I decided to start with the tape measure yagi design and adapt it.

Pictured here is the resulting antenna and a plot of it's SWR over the 2 meter band.

As you can see, it's a success!  Performance is quite good.  Comparing the yagi, when rotate vertically, to a slim jim, I saw a big improvement in signal strength with a repeater that is 60 miles, or 96 kilometers away.
Slim Jim:  Repeater received signal strength, S3-S4.   Minimum power required to hit the repeater, 3 watts.
Yagi: Repeater received signal strength, S5. Minimum power required to hit the repeater, 1.5 watts.

A single S-unit indicates approximately 6db. That's 6db over the 3db gain of the slim jim, so the gain with the Yagi is around 9db.  Pretty good!  I tuned it to favor the lower end of the band a bit since I would mostly be using it down there for SSB and CW.  You can see by the plot from my Blue VNA that the bandwidth is good and broad as I'd hoped.

Here is the design of my antenna with dimensions. Click image for larger view, right-click on image to save it to your computer.

Design notes:   (Pipe dimensions are in inches, I'm not sure of the metric equivalents,)

The physical structure is common PVC pipe and fittings that you can buy at any hardware store.
The boom is made with 3/4 inch PVC pieces.  The element supports are 1/2 inch pieces.
The T pieces at the ends of the boom are 3/4 to 1/2 adapter T's
I couldn't find a cross part with 1/2 inch and 3/4 inch holes, so I used a 3/4 inch cross and adapters to       go down to the 1/2 inch element supports.
The mast connects to a PVC slip on part used to hang PVC pipe from a ceiling.  It slides onto the 3/4 inch boom pipe and has a threaded opening.  A 3/4 inch pipe to thread adapter allows the antenna to be screwed onto the mast when setting up the antenna. 
I cut the ends of the element supports at a 45 degree angle to eliminate the pipes from whistling when there's wind.  (plus it just looks cool!)   I also put the elements on the inside edge at the ends so I could set the antenna down without damaging the wires.   
*Be sure you cut the boom pieces so the distance between the actual elements are correct!*

Window line:
I suggest first cutting three lengths of window line, using the lengths specified in the drawing.    Then fit them against the element supports, trimming out the middle part where the window line wires go around the boom.   Once that's done, short the ends as shown before mounting them to the supports.   Make sure they are perfectly centered on the supports with exactly the same distance from the center of the boom to each end of the element.

Elements notes:  (It's a finicky antenna, be certain to get all dimensions precisely correct!)

    Director and Reflector:
    Cut the window line middle part out in the center so the wires go around the boom cleanly.
    All elements ends:
    The ends of each element have the two wires of the window line shorted together, electrically making the window line act like one fat conductor.   I found it best to bare a bit of the window line and then make a shorting wire out of a short piece of 14 gauge solid copper wire.  This way, during tuning, if you have to shorten the element, all you need to do is shave a bit more insulation off the window line.  Then heat the solder connections and slide the shorting wire down a bit, trimming off the stub remaining.
All elements, mounting to the PVC:
Short screws of any type can be used.   You want the window line tight against the PVC for nice and straight elements.  Use a drill bit just slightly smaller than the diameter of the threads on your screws and you can drill through the window line middle into the pvc, then thread the screw in.  It will bite into the PVC and provide a strong mount.
Also, it is very important to make sure the element is centered on the boom.  The more precise you are here the better the performance.   Measure from the center of the boom out to the end of the element and make sure it's the same distance to each end.
You also want to make sure the window line stays flat and parallel to the other elements.  Make sure your holes are centered in the window line and they follow a straight line along the pipe.

When cutting the 3/4 inch boom pieces, make sure the distance between the window line elements is correct.  The 3/4 pipe will go into the cross and T pieces about 1 inch.

Hairpin match and coax connection to driven element:
Pictured here is the center of the driven element and the connection to the coax and hairpin, notes below the photos.

I used eye ends and ground lugs to make the connection between the coax, hairpin and window line.  (more on the hairpin further down.)
Additionally, there are two short pieces of 14 gauge solid wire to make the connection to the other side of the window line.   The gap there matches the gap at the connections, about 1 inch.
I used a long screw to make the strain relief for the coax.  It also stands the coax off of the antenna when it's vertical.  You don't want the coax in line with the antenna elements.
Also,  it's a good idea to seal up the coax end with silicone or liquid tape to prevent moisture from getting into the coax.  (I hadn't yet sealed it when I took this picture.)

This is the most finicky part of the antenna.   The tape measure design called for 5 inches or 12.7 centimetres length of 12 or 14 gauge wire.  I used 14 gauge solid copper wire.   You want to use solid wire so it maintains it's shape.
Also,  that length is to the connection point, so if you use eyes like I did, the actual wire will be about 4.75 inches or 12 centimetres.
The shape of the hairpin matters and is part of the tuning. (next section)  I started with a semi-circular shape.  You will end up pinching it down during tuning.

You'll need an antenna analyser to tune it.   The antenna will have to be away from metal objects also.  A short temporary mast of 5 or 6 feet should keep it well up from the ground.
First, we need to make sure the resonant point is where you want it in the band.  To shift it you adjust the length of the driven elements.  If you followed my design precisely, it should be around 145Mhz right off. 
If you need to move the resonant frequency up, you will shorten the elements slightly.
Note!  It doesn't take much change!  Shaving only 1/4 inch off the driven element will shift it by around 1.5 to 2 MHz!
If you do need to change the length of the driven element slightly, you will probably want to also change the reflector and director elements by exactly the same amount.

Once you have the lowest SWR point at the frequency desired, we can fiddle with the hairpin to tweak the match and get the lowest SWR at resonance.
Start to pinch in the shape and check SWR.  You should be able to get it right down to almost 1:1 at the resonant point. This won't change the resonant frequency, just the impedance match.

Additional notes:
For SSB or CW, you'll want the Yagi horizontal.  For FM and repeaters higher up in the band, the orientation should be vertical.   The PVC hanger that I use allows the antenna to easily be rotated.  To lock it in place, I drilled a hole through the hanger into the boom and inserted a screw.   I drilled a second hole into the boom when rotated the other way.  This way you can change the orientation by backing out the screw, rotating the antenna and putting the screw back into the other boom hole to lock it.

I'm very pleased with the operation of the antenna.  I'm sure it will give me many years of use and contacts.   For my use on my RV, I use broom holders to allow me to snap it on the side with a short 3/4 PVC mast and easily rotate it to point in the direction I choose. (pictured below)

I've been able to hit repeaters that I could hardly hear with the slim jim and am starting to play around with making SSB and CW contacts.   I also did a video on this project with some demos of the antenna in action.   Linked below.

Saturday, June 16, 2018

A simple arduino controlled 630 meter CW transmitter for amateur radio.

   This year, two new experimental bands became official,  the 2200 meter band and the 630 meter band.

   I wanted to start experimenting with the 630 meter band, 472kHz to 479kHz, but I don't have a radio capable of transmitting on that band.  So I decided to build one.

Where to begin?  Well, a CW transmitter is the easiest choice.  All you need to do is turn a carrier on and off with a Morse code key, so you only need an oscillator and a power amplifier.

   While searching around for other projects on that band, I found a wonderfully simple power amplifier circuit already designed by Rog, GW3UEP over in Whales.  His original write up can be found here:


   I've also been looking for a use for an Si5351 clock generator break-out board I was given last year.  It's a versatile little chip, capable of generating up to three simultaneous outputs from 100kHz up to 200Mhz.  It generates a square wave, and the power amplifier expects a square wave, so they should work well together.

   After a couple of weeks of work, I had a completed and working transmitter capable of producing around 6 watts of power.


Theory of operation:

    The arduino and Si5351 will generate a signal within the band, from 472 to 479 kHz.  A Morse code key or electronic keyer will be used to switch the signal on and off. 

    Frequency selection is accomplished by a single potentiometer and a spot tuning push button.  When you press the button, a low level signal will be sent out of the receiver output jack, allowing you to dial the transmitter to your desired frequency.  The band is so narrow, only 7kHz wide, that this tuning method is very quick and easy.

   Keying of the transmitter is accomplished by first switching a relay that grounds the receiver output jack to protect your receiver, waiting a microsecond, switching a T/R relay that powers up the amp and switches the antenna jack to the amplifier, waiting a microsecond, and finally turning on the Si5351 output at the desired frequency.

   Releasing the Key will turn off the Si5351 output.

   A timer is employed for a 'tail',  waiting 1 second before releasing the relays to switch back into receive mode.  This is to prevent the relays chattering away with every dit and dah while sending.  That timeout can be changed in the software, and could theoretically allow for full break-in operation if you don't mind banging your relays constantly.  That's up to you, I like having a bit of a tail.  Since this timer is software controlled, there's nothing stopping you from adding an additional pot and modifying the software to make the tail adjustable if you choose.

Here is the schematic of the completed unit.

   Circuit description:
       12-13.6 volts DC comes in and is immediately filtered by a .01uf capacitor to prevent RF leaking back out the power line.  There's also a large diode right at the power connector in reverse bias to protect the radio from reverse polarity connection.  You should have a 2-3 amp fuse in your power cord for safety.

       The power is routed directly to the two relays and a power switch.  I ran it directly to the relays due to the physical layout in my unit, but you could run the relay feed through the power switch as well if you choose.

       Beyond the power switch we have a 7805 regulator to provide 5V to the arduino nano and Si5351 board.  You should put a small heat sink on the 7805, it will get pretty warm.

       The arduino is the brains of the machine, providing frequency control and keying.  It takes input from three items.  The spot tuning push button,  a pot for tuning, and a jack for connecting your Morse code key or electronic keyer output.   The tuning pot can be any value from 1K up to 10K.

       Connected to the arduino we have an Si5351 break-out board.  These are available from etherkit or adafruit at a low cost.

Etherkit link

Adafruit link

       The output of the Si5351 is low, around 3.3 volts.  Not enough to switch on the power MOSFET in the amplifier, so I needed a simple driver to bring that voltage level up to around 11 volts.  This is accomplished through a single NPN transistor.  You can use any generic switching transistor like the 2N3904 or 2N2222.

       Additionally, the second output of the Si5351 is used for spot tuning and runs to a wire that's wrapped around the receiver output's center conductor.  When that output is enabled, plenty of signal couples to the receiver out for you to hear it and tune to your frequency.

      Keying is accomplished by simply switching the CLK0 output of the Si5351 on and off.

      The power amplifier is a class E amp.  The incoming signal switches the MOSFET on and off at frequency.  The resulting high power square wave is smoothed by L2 and C2 into a sine wave.   The low pass filter provided by L3 and C3-C4 will suppress the second order and above harmonics by more than 50db as required by the FCC's rules.   The second order harmonic falls around 900kHz, within the AM broadcast band, so it's important to suppress that to prevent interference.

     I built it as the author designed it, Manhatten style on a flat piece of PCB.  There is a small heat sink on the MOSFET and I cut the copper around the sink to isolate it from ground.  This is IMPORTANT since the MOSTFET's tab is at V+ and would cause a short if not isolated.  Also be sure to isolate the mounting screw on the back side of the board.

    The three coils are easy to wind.  Use magnet wire and follow the turn counts listed in the schematic.   If you have a 3D printer, I designed coil forms at the correct dimensions that can be downloaded from thingiverse at the link below.

3D printable coil forms.

     Two relays are employed.  RLA1 is a DPDT type.  One half of it switches power on to the MOSFET, the other half switches the antenna jack from the receiver output to the amplifier output.
RLA2  is a DPST type that grounds the receiver output jack during transmit in order to protect your receiver from excessive RF from the amplifier.

    NOTE:   It should be possible to adapt this transmitter to any of the amateur bands by changing L2, L3, and C2-C5, then modifying the arduino software for the different frequency range.  You'll have to research class E RF amplifiers to calculate the appropriate values.


   The arduino software can be downloaded from github here:

630 meter arduino sketch

   You WILL need to add the etherkit Si5351 library in your arduino IDE.  Go to the sketch menu and select 'Include Library' 'manage libraries'.  In the library manager, type 'etherkit' into the search box and add the Etherkit Si5351 library.   This library will work with the adafruit break out board as well.  I chose this library since it allows you to pass a direct frequency rather than the complicated method adafruits library requires.

   I have liberally commented the software to make it easier to follow. 

   The first section and setup routine define variables that are used through the program,  setup the hardware I/O pins, and defines an interrupt routing.

   The rest of the program can be broken down into three sections.   The main loop,  the interrupt routine,  and a tuning function.

   The main loop handles the keying and tuning.   It checks the spot button and if pressed, calls the tuning function.   I ONLY change the tuning while the button is pressed.  This is to prevent 'jitter' of your frequency while transmitting.   It then checks for key down or key up and switches the relays, turns the Si5351 output on or off as needed.

   The tuning function reads the tune pot and calculates a number from 0 to 7000, the width of the 630 meter band.  The resulting number is added to 472000, the bottom edge of the band, and stored in the variable 'freq'.  'freq' is then multiplied by 100 to bring it to the value the etherkit library expects, in 1/100ths of a Hz.

   The interrupt routine is set to run 1000 times a second and is used to decriment a counter for the tail delay.  I used an interrupt routine for precision since I know it will run exactly 1000 times a second.  I can then use a variable as a timer within the program.

Video:  (please watch these before you ask questions, you'll probably find your answers.)

   I produced a series of videos on the transmitter.  You can follow the entire process, my trials and errors through the project.   Skip right to the last video if you just want to see the finished design.

Video #1 is me talking about the idea and initial design of the transmitter
Video 1,  the design.

Video #2 is my initial hardware build in a small case.
Video 2. hardware nearly done. (or so I thought!)

Video #3 is a talk about the arduino software and a demo of the operation of the hardware.
Video 3. The software is done.

Video #4 is the final hardware build of the complete and working unit.
Video 4. The hardware redesign and demo

   This little transmitter performs extremely well and I'm very happy with the results.  If you choose to build it, I'm sure you'll find it to be a nice little workhorse. 

73, and maybe see you on the air sometime.

Kevin,  KB9RLW

Saturday, June 3, 2017

Step by step install of the SDRPlay RSP2 under Ubuntu 16.04 and other debian based linux.

    The SDRPlay RSP2 is an excellent bit of kit.   A highly sensitive SDR with multiple antenna inputs,  input filtering, 10 Mhz bandwidth, and a range from 2Ghz all the way down to 1Khz.   On the windows side, they provide a very nice SDR program called SDR uno.

   Those of us not running windows, however, have a few choices in software.   GQRX is a popular program, built on GNU-radio.  There's a couple of others that escape my mind, but the newcomer, CubucSDR, is starting to stand out.

   After much searching and experimenting,  I have come up with a list of steps that will build and install the needed support frameworks and software to get you up and running with CubicSDR and your SDRPlay.  
CubicSDR manual is here, worth reading through, you might get confused in the software without it.

    There are a few software packages that are needed for the SDRPlay to work.   SoapySDR is a framework for talking to several SDR hardware types, Cubic relies on it.   We'll also need the specific soapy module for the SDRPlay, and a few other packages needed to build cubic.

   I have created two scripts that largely automate the installation.   You can download them using the following link:

                    Click to go to the github page.

And here's a video where I talk about this whole project.

  Now, the instructions.

   You will need to create a folder on your desktop named 'sdrplay' and put these script files there. You will also need to download the driver for linux from the SDRPlay site and also place it in that folder.  Do not install it yet!

   Open a terminal and change into your newly created directory with the command:

cd ~/Desktop/sdrplay

    Now, you can run the first script.  This will install build software from ubuntu's repositories, along with dependencies needed by the packages we will be building.   It will also clone sever github projects, including soapySDR and CubicSDR.

   The script MUST be run as root, so use sudo to execute it:

sudo ./

   It will take awhile and will ask you to type 'y' once for the installs.   Once it's completed,  you will need to run the installer from the SDRPlay site.  Again, you need to make sure you run their script as root with sudo.  If it's not executable,  you can run:

sudo chmod +x ./{scriptname}

(where {scriptname} is the name of the downloaded file)

It will show you a license agreement you need to spacebar through and hit 'y' to accept.

   After their script is done, you can run the install script part two, again with sudo:

sudo ./

  This one will also take awhile while building CubicSDR.   Once done, you should be all ready to go!   CubicSDR won't automatically be added to any menus, you'll have to do that yourself.  You can also run it from within the terminal by simply typing CubicSDR.    If you had the SDRPlay plugged in during this installation,  unplug it for a few seconds and then plug it back in.  When you run Cubic, it should find the unit and list it for selection.

ADDENDUM:  These scripts also work on the raspberry Pi 3 with the current version of raspian.  Obviously you need to download the raspberry pi version of the hardware driver from the SDRPlay site.  
There's just one extra step to get pulse audio working under raspian.   You need to modify one of the autostart scripts.
In a terminal,  type:

sudo pico /etc/xdg/autostart/pulseaudio.desktop

Scroll down and find the line:


Change that line to:

Exec=/bin/sh -c "Sleep 5; start-pulseaudio-x11"

Ctrl-o to write the changes,  Ctrl-x to exit.  Now reboot the pi and the pulse audio server will start.  This probably fixes other things that rely on pulse as well.

Have fun!

Kevin,  KB9RLW

The scripts are pasted below.  you can follow through their steps manually with copy/paste into your terminal. (don't forget sudo).  Or you can copy each in it's entirety and paste it into an editor, save it, mark it as executable and then run it.

#/*    script 1 below /*

#! /bin/bash
# This script should install and build all needed stuff for
# using the SDRPlay on Ubuntu 16.04 or later.  It must be
# run as root.
# First we install needed dependancies.

apt-get install git build-essential automake cmake g++ swig
apt-get install libgtk2.0-dev libpulse-dev libpython-dev python-numpy.
apt-get install mesa-utils libeglw1-mesa libglw1-mesa-dev
apt-get install freeglut3-dev freeglut3

# Now we'll git the projects we need

git clone
tar -xvjf wxWidgets-3.1.1.tar.bz2
git clone
git clone
git clone
git clone

# Build liquid-dsp

cd liquid-dsp
./configure --enable-fftoverride
make -j4
make install
cd ..

# Build wxwidgets

cd wxWidgets-3.1.1/
mkdir -p ~/Develop/wxWidgets-staticlib
./configure --with-opengl --disable-shared --enable-monolithic --with-libjpeg --with-libtiff --with-libpng --with-zlib --disable-sdltest --enable-unicode --enable-display --enable-propgrid --disable-webkit --disable-webview --disable-webviewwebkit --prefix=`echo ~/Develop/wxWidgets-staticlib` CXXFLAGS="-std=c++0x" --with-libiconv=/usr
make -j4
make install
cd ..

echo "."
echo "First part of installation done.   Now run the SDRPlay file you"
echo "downloaded from their site.  Be sure to use sudo to run it!"

#/* end of script 1 /*

#/* script 2 below /*

#! /bin/bash
# This script should install and build all needed stuff for
# using the SDRPlay on Ubuntu 16.04 or later.  It must be
# run as root.
# This is part two.
# Here we build SoapySDR

cd SoapySDR
mkdir build
cd build
cmake ..
make -j4
make install
cd ..
cd ..

# Now we build the SDRPlay module for Soapy

cd SoapySDRPlay
mkdir build
cd build
cmake ..
make install
cd ..
cd ..

# And we build SoapyRemote

cd SoapyRemote
mkdir build
cd build
cmake ..
make install
cd ..
cd ..

# And finally,  we build Cubic.  This takes awhile!

cd CubicSDR
mkdir build
cd build
cmake ../ -DCMAKE_BUILD_TYPE=Release -DwxWidgets_CONFIG_EXECUTABLE=~/Develop/wxWidgets-staticlib/bin/wx-config
make install
cd ..
cd ..
rm -R ~/Develop

# now we change permissions on these root-owned folders so the user can
# delete them at their leisure.

chmod -R 0777 ./*

# And we're done.   Cubic should work with the SDRPlay.  The user can
# run it from the terminal with CubicSDR or add this command to a menu.

#/* end of script 2 /*

Monday, May 1, 2017

The Arduino based Super Simple Keyer

     I decided to build my own keyer.  I needed a new Arduino project and this seemed like the perfect one.    I had a few ideas for the design.

     I wanted a keyer that would take multiple inputs,  paddles, straight key, and computer keyboard.  The idea being you can send however you like, without needing to change around cables, just send.

     There are many arduino morse projects around already.  I suppose I could have just downloaded an existing one, dropped it in and ran with it.  But I wanted to tackle the challenge of coding it myself.

     Once I'd sorted the hardware out,  I started to work on the software.   It turned out to be a bigger challenge than I'd expected!   It's tricky, the timing of reading the paddles and keying.   I came up with an interesting solution to timing when it comes to keying the rig.

     You see, the main loop in the arduino sketch can vary in how fast it executes, based on how much work it has to do.   My solution was to use a timer in the chip and schedule an interrupt.

     All processors have timers for this.   They run at a set speed and can interrupt the processor, forcing it to execute a small routine at precise intervals.  This is useful in robotics, for example, like in a flying drone.  You would need adjustments to the motor speeds to be occurring fast and regularly.

    So, in my keyer,  there is an interrupt that is running a keying routine once every millisecond.  That's 1000 times per second,  precisely.   That allows me to control the timing of the keying exactly and produce clean, consistent Morse out to the radio.    This routing watches a buffer that I load with events using the characters period,  dash, and comma.    Period and dash are obviously the Morse elements, and the comma is a delay equivalent to a dot.

    So to send an 'A' for example,  you'd load the event buffer with  .,-,   dot comma dash comma.   The interrupt routine would immediately key the transmitter with cleanly spaced precise Morse.

    The hardware is extremely simple.  The schematic is below.   The pins chosen are arbitrary and can be changed to suit your build if needed.  They are referenced in the first few lines of the code and you change those to reflect the pins you use.   The keying is handled by a simple transistor switch.  You could use an opto-isolator if you wish,  but I've had no trouble keying modern rigs this way.

   The software is below.  Simply copy and paste it into the Arduino IDE to upload it to your board.  Upon startup it will briefly key the radio for a fraction of a second and then be ready to accept input.  I wired an additional input jack for a straight key.  That was simply parallel to the keying output and done for convenience.

    For keyboard input, the code monitors the serial input.  When the Arduino is powered by a USB connection to your computer, it will create a serial port.   Simply run a terminal program and connect it to that port to allow you to send keyboard CW.  You want to set your terminal program to 9600 baud, no parity, 1 stop bit and 8 data bits.  Type to send at the current speed.  Simple.

   The code is up to version 1.4.   Paddle routines have been heavily worked on,  2 memories have been added.    shft-alt-1 to load memory one,  alt-1 to send memory one.  Same for memory 2,  shft-alt-2 and alt-2.

Code pasted below:


  Super Simple Keyer Ver 1.4  Arduino based keyer
  Written Apr. 30, 2017 by Kevin Loughin, KB9RLW

Ver 1
  Initial release, keyer working ok, not iambic

Ver 1.2
  First iambic code, dependent on timing, not perfect

Ver 1.3
  Rewrite of keyer logic, iambic and dot dash buffering working!

Ver 1.4
  Tweaked paddles a bit.  Added 2 memories.  shft-alt-1 to set mem 1, shft-alt-2 to set memory 2
  alt-1 to send memory 1, alt-2 to send memory 2.
  Tweaked character spacing for keyboard decode. Added BT using the minus sign.


//  Pins we're going to use.  Change as needed.
const int wpmraw = A7;  //Pot sense for speed
const int dotin = 4;  //dot sense pin
const int dashin = 2; //dash sense pin
const int keyout = 13; //keying output

// declaring variables we'll use in program

int delayval = 150;  //delay between elements in milliseconds
String event = "";  //event contains the elements currently being sent, .=dot -=dash ,=space
String memory1 = ""; //user memory 1
String memory2 = ""; //user memory 2
String membuff; // buffer for memory sending
int dotlen = 150;  //current dot length in milliseconds
int dashlen = (dotlen * 3);  //dash length in milliseconds
int active = 0; //keying handler flag
int delaycount = 0; // delay counter in interrup handler
int dotstate = 1; // dot paddle input pins reading
int dotread = 0;
int dashstate = 1; //dash paddle input pins reading
int lastdotstate = 1;
int lastdashstate = 1;
int dashread;
int lastdashdebounce;
int lastdotdebounce;
int eventbuffer = 0;  // event buffering
int dashhalf;
int lastchar = 1;
int c;
int dotbuffer;
int dashbuffer;
int dashdelay;
int dotdelay;
int flag;

void setup() {
  // Turn off keying pin right away

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

  // setup input pins
  pinMode (dotin, INPUT);
  pinMode (dashin, INPUT);
  digitalWrite(dotin, HIGH);
  digitalWrite(dashin, HIGH);

  //  setup the interrupt
    OCR0A = 0xAF;
  TIMSK0 |= _BV(OCIE0A);

//  interrupt handler
 if (  event.length() > 0  ) //do we have anything to do?
   if ( !active ) //are we not currently sending?
     if ( event.charAt(0) == '.')  //do we need to send a dot?
      digitalWrite(keyout, HIGH); //key on
      delaycount = dotlen;
      active = 1;
      lastchar = 1;
     if ( event.charAt(0) == '-' ) //do we need to send a dash?
      digitalWrite(keyout, HIGH); //key on
      delaycount = dashlen;
      active = 1;
      lastchar = 2;
     if ( event.charAt(0) == ',' )  //do we need to delay between elements?
       delaycount = dotlen;
      digitalWrite(keyout, LOW); //key off
      active = 1;

    if ( active ) // we're already active, count and reset when done
        if ( !delaycount )  //are we done with this element?
          active = 0;
          digitalWrite(keyout, LOW); //just make sure the key is off
          event.remove(0, 1);

void loop()
  // read the speed and set timing

dotlen = ( 200 - (.146 * analogRead(wpmraw)) );
dashlen = ( 3 * dotlen );

//  Read paddle inputs

          // read the dash pin
  dashread = digitalRead(dashin);
   if ( dashread != lastdashstate )
      lastdashdebounce = millis();
    if ( (millis() - lastdashdebounce) > 40 )
      if ( dashread != dashstate )
        dashstate = dashread;
    lastdashstate = dashread;
  // read the dot pin
  dotread = digitalRead(dotin);
 if (dotread != lastdotstate)
      lastdotdebounce = millis();
    if ((millis() - lastdotdebounce) > 40)
      if (dotread != dotstate)
        dotstate = dotread;
    lastdotstate = dotread;

// Keyer logic

while (!dotstate || !dashstate )  //   if paddles pressed, do
    if ( event.length() < 3 )  // don't bother unless there's no more than 1 chars in buffer

      if ( !dotstate && !dashstate ) // both paddles?
        if ( lastchar == 1 && ( delaycount < dashlen ))
          event = String(event + "-,");
          lastchar = 2;break;
        if ( lastchar == 2 && ( delaycount < dashlen ))
          event = String(event + ".,");
          lastchar = 1;break;

      if ( !dotstate && dashstate )  // only dot paddle
        if ( lastchar == 1 && ( delaycount < dashlen) && event.length() < 1)
          event = String(event + ".,");break;
        if ( lastchar == 2 && event.length() < 3)
          event = String(event + ".,");break;

      if ( dotstate && !dashstate )  // only dash paddle
        if ( lastchar == 1 && (delaycount < dotlen))
          event = String(event + "-,");break;
        if ( lastchar == 2 && (delaycount < (dotlen * 2)) & event.length() < 2)
          event = String(event + "-,");break;

//  Keyboard routines

  c =;
  // check for special keys
  if ( c == 27 ) // alt key pressed
      flag = 1;
      c =;
  if ( flag && c == 33 ) // shft-alt-1, memory 1 define
    flag = 0;
    c = 0;
  if ( flag && c == 64 ) // shift-alt-2, memory 2 define
      flag = 0;
      c = 0;
  if ( flag && c == 49 ) // alt 1, send memory 1
      flag = 0;
      c = 0;
  if ( flag && c == 50 ) // alt 2, send memory 2
      flag = 0;
      c = 0;
  decoder(c);  // call the keyboard decoder to load this event

void loadmemory(int num) // interact with user to load a memory
  String inData;
  char received;
  Serial.println( "." );
  Serial.print( "Enter the contents for memory " );
  Serial.println( num );

    while ( received != 13 )
      if (Serial.available())
        received =;
        inData += received;
  if ( num == 1 ) // loading memory1
      memory1 = inData;
  if ( num == 2 ) // loading memory2
      memory2 = inData;
  Serial.print( "Memory " );
  Serial.print( num );
  Serial.println( " loaded with" );
  Serial.println( inData );
  inData == "";

void sendmemory(int num) // send memory number num
  if ( num == 1 )
    membuff = memory1;
    membuff = memory2;
  Serial.println( "." );
  Serial.println( membuff );
  for(int leng = membuff.length(); leng > 0; leng--)
      decoder(membuff.charAt( membuff.length() - leng ));
void decoder(int in) // function to decode char for sending and load send buffer
      case ' ' : event = String(event + ",,"); break;
      case 'a' : event = String(event + ".,-,,,"); break;
      case 'b' : event = String(event + "-,.,.,.,,,"); break;
      case 'c' : event = String(event + "-,.,-,.,,,"); break;
      case 'd' : event = String(event + "-,.,.,,,"); break;
      case 'e' : event = String(event + ".,,,"); break;
      case 'f' : event = String(event + ".,.,-,.,,,"); break;
      case 'g' : event = String(event + "-,-,.,,,"); break;
      case 'h' : event = String(event + ".,.,.,.,,,"); break;
      case 'i' : event = String(event + ".,.,,,"); break;
      case 'j' : event = String(event + ".,-,-,-,,,"); break;
      case 'k' : event = String(event + "-,.,-,,,"); break;
      case 'l' : event = String(event + ".,-,.,.,,,"); break;
      case 'm' : event = String(event + "-,-,,,"); break;
      case 'n' : event = String(event + "-,.,,,"); break;
      case 'o' : event = String(event + "-,-,-,,,"); break;
      case 'p' : event = String(event + ".,-,-,.,,,"); break;
      case 'q' : event = String(event + "-,-,.,-,,,"); break;
      case 'r' : event = String(event + ".,-,.,,,"); break;
      case 's' : event = String(event + ".,.,.,,,"); break;
      case 't' : event = String(event + "-,,,"); break;
      case 'u' : event = String(event + ".,.,-,,,"); break;
      case 'v' : event = String(event + ".,.,.,-,,,"); break;
      case 'w' : event = String(event + ".,-,-,,,"); break;
      case 'x' : event = String(event + "-,.,.,-,,,"); break;
      case 'y' : event = String(event + "-,.,-,-,,,"); break;
      case 'z' : event = String(event + "-,-,.,.,,,"); break;
      case 'A' : event = String(event + ".,-,,,"); break;
      case 'B' : event = String(event + "-,.,.,.,,,"); break;
      case 'C' : event = String(event + "-,.,-,.,,,"); break;
      case 'D' : event = String(event + "-,.,.,,,"); break;
      case 'E' : event = String(event + ".,,,"); break;
      case 'F' : event = String(event + ".,.,-,.,,,"); break;
      case 'G' : event = String(event + "-,-,.,,,"); break;
      case 'H' : event = String(event + ".,.,.,.,,,"); break;
      case 'I' : event = String(event + ".,.,,,"); break;
      case 'J' : event = String(event + ".,-,-,-,,,"); break;
      case 'K' : event = String(event + "-,.,-,,,"); break;
      case 'L' : event = String(event + ".,-,.,.,,,"); break;
      case 'M' : event = String(event + "-,-,,,"); break;
      case 'N' : event = String(event + "-,.,,,"); break;
      case 'O' : event = String(event + "-,-,-,,,"); break;
      case 'P' : event = String(event + ".,-,-,.,,,"); break;
      case 'Q' : event = String(event + "-,.,-,-,,,"); break;
      case 'R' : event = String(event + ".,-,.,,,"); break;
      case 'S' : event = String(event + ".,.,.,,,"); break;
      case 'T' : event = String(event + "-,,,"); break;
      case 'U' : event = String(event + ".,.,-,,,"); break;
      case 'V' : event = String(event + ".,.,.,-,,,"); break;
      case 'W' : event = String(event + ".,-,-,,,"); break;
      case 'X' : event = String(event + "-,.,.,-,,,"); break;
      case 'Y' : event = String(event + "-,.,-,-,,,"); break;
      case 'Z' : event = String(event + "-,-,.,.,,,"); break;
      case '0' : event = String(event + "-,-,-,-,-,,,"); break;
      case '1' : event = String(event + ".,-,-,-,-,,,"); break;
      case '2' : event = String(event + ".,.,-,-,-,,,"); break;
      case '3' : event = String(event + ".,.,.,-,-,,,"); break;
      case '4' : event = String(event + ".,.,.,.,-,,,"); break;
      case '5' : event = String(event + ".,.,.,.,.,,,"); break;
      case '6' : event = String(event + "-,.,.,.,.,,,"); break;
      case '7' : event = String(event + "-,-,.,.,.,,,"); break;
      case '8' : event = String(event + "-,-,-,.,.,,,"); break;
      case '9' : event = String(event + "-,-,-,-,.,,,"); break;
      case '.' : event = String(event + ".,-,.,-,.,-,,,"); break;
      case '-' : event = String(event + "-,.,.,.,-,,,"); break;
      case '/' : event = String(event + "-,.,.,-,.,,,"); break;

Thursday, January 5, 2017

Giving a voice to someone who's lost the ability to speak.

As they say, necessity can be the mother of invention.  

    In December of 2016 I lost my best friend of over 30 years.   He had suffered a major stroke and was in a coma for just over a week.   He came out of the coma, but could not speak, and could only move his hands a bit and feet.   He was in there, he could communicate by squeezing his hand, but that was all.

   I immediately dreamed up a way of giving him a voice back.   This project is the result.

   Unfortunately,  my friend had a second stroke and didn't survive.   I decided to go ahead with the project though,  I'm hopeful that it can help someone out there somewhere.   I call it,  The CW Voice Box.

   Morse code is one of the oldest ways of sending letters with a simple on off signal.   Short patterns of long and short tones.  An ideal way for someone who can only squeeze, bite, or blink to be able to spell out words.    This project is exactly that.  You key in Morse code, it speaks the letters.   A voice for the voiceless.

   There are other devices out there to accomplish this, it's not a new idea.   However,  those devices are expensive!   With the technology we have today,  you can build my voice box for around 45 bucks.    Maybe a little more if you want a fancy case.

The parts list:

     Computer:    Raspberry Pi zero.     I recommend the starter pack over at Adafruit: $24.95
Adafruit Raspi budget pack.

     USB sound card:     There's many available on Amazon,  here's one that works:   $5.99
USB sound card

     8 Gig micro SD card.    $7

     5 volt audio amp:   There's lots of these to choose from,  one example below:  $7
Audio amp

     A speaker of some type.   I'll leave that up to you.

     You'll also need some bits of wire, some kind of a case, and an input device.    Regarding input devices....    What you need will depend entirely upon the capabilities of the person you are trying to help.    There are commercial squeeze bulb switches,   bite switches,   and other devices available.   I'll leave the input method up to you.    The only requirement is that the person needs to be able to input long and short signals in a pattern.

     Putting it all together:

     I've already done the hard work on the software.   I have built a Raspian image for your Pi zero that will boot directly into the morse decoding speech software.   You can download the image from the link below.

Custom Raspian image zip file.

(Many thanks to for offering to host the file.)

    Write this image to the micro SD card and put it in the pi.

     The starter pack will come with a 2Amp power supply,  plug that into the pi.
      Using the USB adapter, you can plug in the usb audio dongle to the second micro usb port on the pi.
      Your input device will need to connect to GPIO pin 7 on the pi, and also to ground.

      The audio amp will need 5 volts.   You can tap that from pin 2 on the pi.  It also needs a ground connection.

      Audio from the USB dongle goes to the input of the amp, and obviously the speaker will connect to it's output.

      When you plug in the power supply,  the pi should boot and within about 20 seconds it will speak the word  "Ready".    you're good to go.   Simply start keying in Morse and it will speak each letter, number, and some punctuation.

     It will reliably decode Morse from around 5 words per minute speed, up to about 10 words per minute speed.   To get an idea of the speed,  watch the demo towards the end of my video about the project,  linked below.

Full video.

    The software is built upon a great educational python program on the raspberry pi site.   I modified the software to make it compatibly with python3,  re-ordered it's lookup table for speed, and integrated espeak speech synthesis.    The raspian image was further modified to fix espeak errors with alsa, and set to boot to command line and run the decoder on boot.

    The original morse code decoder project on the raspberry site is linked below.

Morse decode I built upon.

My modified source code follows:


# modified code based on a tutorial project on the raspberry pi organizations web site.
# original project can be found here:
import pygame
import time
from RPi import GPIO
import _thread
from array import array
from pygame.locals import *
from morse_lookup import *
from espeak import espeak

pygame.mixer.pre_init(44100, -16, 1, 1024)

class ToneSound(pygame.mixer.Sound):
    def __init__(self, frequency, volume):
        self.frequency = frequency
        pygame.mixer.Sound.__init__(self, self.build_samples())

    def build_samples(self):
        period = int(round(pygame.mixer.get_init()[0] / self.frequency))
        samples = array("h", [0] * period)
        amplitude = 2 ** (abs(pygame.mixer.get_init()[1]) - 1) - 1
        for time in range(period):
            if time < period / 2:
                samples[time] = amplitude
                samples[time] = -amplitude
        return samples

def wait_for_keydown(pin):
    while GPIO.input(pin):

def wait_for_keyup(pin):
    while not GPIO.input(pin):

def decoder_thread():
    global key_up_time
    global buffer
    new_word = False
    while True:
        key_up_length = time.time() - key_up_time
        if len(buffer) > 0 and key_up_length >= 1.5:
            new_word = True
            bit_string = "".join(buffer)
            del buffer[:]
        elif new_word and key_up_length >= 4.5:
            new_word = False
            sys.stdout.write(" ")

tone_obj = ToneSound(frequency = 800, volume = .03)

pin = 7
GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)

DOT = "."
DASH = "-"

key_down_time = 0
key_down_length = 0
key_up_time = 0
buffer = []

_thread.start_new_thread(decoder_thread, ())


while True:
    key_down_time = time.time() #record the time when the key went down #the -1 means to loop the sound
    key_up_time = time.time() #record the time when the key was released
    key_down_length = key_up_time - key_down_time #get the length of time it was held down for
    buffer.append(DASH if key_down_length > 0.3 else DOT)


And the source as well.


import sys
from espeak import espeak

morse_code_lookup = {
    ".":    "E",
    "-":    "T",
    ".-":    "A",
    "---":    "O",
    "..":    "I",
    "-.":    "N",
    "...":    "S",
    "....":    "H",
    ".-.":    "R",
    "-..":    "D",
    ".-..":    "L",
    "-.-.":    "C",
    "..-":    "U",
    "--":    "M",
    ".--":    "W",
    "..-.":    "F",
    "--.":    "G",
    "-.--":    "Y",
    ".--.":    "P",
    "-...":    "B",
    "...-":    "V",
    "-.-":    "K",
    ".---":    "J",
    "-..-":    "X",
    "--.-":    "Q",
    "--..":    "Ze",
    ".----":    "1",
    "..---":    "2",
    "...--":    "3",
    "....-":    "4",
    ".....":    "5",
    "-....":    "6",
    "--...":    "7",
    "---..":    "8",
    "----.":    "9",
    "-----":    "0",
    "..--..":    "question mark",
    ".-.-.-":    "period"

def try_decode(bit_string):
    if bit_string in morse_code_lookup.keys():
#        sys.stdout.write(morse_code_lookup[bit_string])
#        sys.stdout.flush()


    Please share,  I hope this project will get out there and find someone who can use it.    I would be absolutely thrilled to hear back from someone who was able to give a voice back to somebody who'd lost theirs.



Sunday, August 14, 2016

Cheap and easy to build digital modes USB interface for Ham Radio.

The DuinoVOX  Arduino powered digital modes USB interface for ham radio.

   I wanted a better computer to radio interface than what I was using,  straight connection from the computers audio jacks.    Being unemployed for awhile, I couldn't afford $100 to buy a Signalink commercial interface, so I built my own.  Also, I wanted to use an Arduino in a ham radio project.  It came out so well,  that I've decided to publish it here.   If you build it and like it, please let me know.
 Below is the schematic.   I did a youtube video on the project,  watchable at this link.

Circuit Description:
   The USB sound dongle can be purchased on for less than $10.  Search for "USB sound adapter" and you'll find dozens of them.   Look for one with a split plastic case so you can take it apart.  You'll need to tap +5V and ground at the USB connector, the outside pins of the 4 pin connector.
   You can used 1/8" jacks to plug in to it, or desolder the existing jacks and tap audio right off the PCB with thin coax or other shielded cable.
   The audio coming from the USB sound device is low voltage.  1.7 volts P-to-P max volume.  Since the Arduino will only sense positive voltage,  that's 900mv at best.   Depending on the point in time that you sample the signal, you can catch it anywhere from zero on up to .9 V within the waveform.  Not great.
   So,  Q1 is a simple amplifier that takes the input signal up to the full 5V on peaks.
   The amplified signal passes through D1 and charges C4.   D1 prevents the cap from discharging back through the transistor, so you get an accumulated voltage up to around 4.4V DC during a full volume signal.  This gives an excellent range on the input to the Arduino, and allows for sensitivity adjustment to be done in software.
  R6 bleeds the capacitor down when the audio stops.
  Q2 takes the PTT signal from the Arduino and grounds the radios PTT line to key it into transmit.


C1 and C2,  I used non polarized caps since I had them on hand, but regular electrolytics will work fine.  If you're concerned about isolation, you could use two 600:600 Ohm transformers instead.
C4 is not critical.  If you don't have a .22,  put two .1 in parallel
R7 and R8 don't have to be 10K, can be anything from 10K up, don't have to match either.
C3 can be anything from 1uf up to 10uf

One important tip on using this interface.  On your computer, you'll want to set the playback volume for the interface sound card to maximum,  then use the drive control knob to set the audio level to your rig.  This gives the vox circuit plenty of audio to work with.

The Arduino source code is pasted below.  You should be able to copy paste it right into the Arduino IDE.   If you have trouble,  let me know and I'll email the file to you.   Thanks and 73.

If you build this and love it,  and you'd like to leave me a tip,  my email registered with paypal is