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:

GW3UEP PA

   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.


Software:

   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.
http://cubicsdr.readthedocs.io/en/latest/index.html

    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 download. Let me know if this link is broken.


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 ./Install_script-pt1.sh

   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 ./Install_script-pt2.sh

  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:

Exec=start-pulseaudio-x11

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 https://github.com/jgaeddert/liquid-dsp
wget https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.0/wxWidgets-3.1.0.tar.bz2
tar -xvjf wxWidgets-3.1.0.tar.bz2
git clone https://github.com/pothosware/SoapySDR.git
git clone https://github.com/pothosware/SoapySDRPlay.git
git clone https://github.com/pothosware/SoapyRemote.git
git clone https://github.com/cjcliffe/CubicSDR.git

# Build liquid-dsp

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

# Build wxwidgets

cd wxWidgets-3.1.0/
mkdir -p ~/Develop/wxWidgets-staticlib
./autogen.sh
./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
ldconfig
cd ..
cd ..

# Now we build the SDRPlay module for Soapy

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

# And we build SoapyRemote

cd SoapyRemote
mkdir build
cd build
cmake ..
make
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
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);
  Serial.begin(9600);

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


//  interrupt handler
SIGNAL(TIMER0_COMPA_vect)
{
 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
     {
       delaycount--;
        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;
        }
      }
    }
break;
  }
delay(12);

//  Keyboard routines

while(Serial.available())
 {
  c = Serial.read();
  Serial.write(c);
  // check for special keys
  if ( c == 27 ) // alt key pressed
    {
      flag = 1;
      c = Serial.read();
    }
  if ( flag && c == 33 ) // shft-alt-1, memory 1 define
    {
    loadmemory(1);
    flag = 0;
    c = 0;
    }
  if ( flag && c == 64 ) // shift-alt-2, memory 2 define
    {
      loadmemory(2);
      flag = 0;
      c = 0;
    }
  if ( flag && c == 49 ) // alt 1, send memory 1
    {
      sendmemory(1);
      flag = 0;
      c = 0;
    }
  if ( flag && c == 50 ) // alt 2, send memory 2
    {
      sendmemory(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 );
  Serial.println();

    while ( received != 13 )
    {
      if (Serial.available())
      {
        received = Serial.read();
        Serial.write(received);
        inData += received;
      }
    }
  if ( num == 1 ) // loading memory1
    {
      memory1 = inData;
    }
  if ( num == 2 ) // loading memory2
    {
      memory2 = inData;
    }
  Serial.println();
  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;
  else
    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
{
       switch(in)
  {
      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 bitchen.com 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:

----------------------------------------------------

#!/usr/bin/python3
# modified code based on a tutorial project on the raspberry pi organizations web site.
# original project can be found here:
# https://www.raspberrypi.org/learning/morse-code-virtual-radio/worksheet/
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)
pygame.init()

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

    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
            else:
                samples[time] = -amplitude
        return samples

def wait_for_keydown(pin):
    while GPIO.input(pin):
        time.sleep(0.01)

def wait_for_keyup(pin):
    while not GPIO.input(pin):
        time.sleep(0.01)

def decoder_thread():
    global key_up_time
    global buffer
    new_word = False
    while True:
        time.sleep(.01)
        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)
            try_decode(bit_string)
            del buffer[:]
        elif new_word and key_up_length >= 4.5:
            new_word = False
            sys.stdout.write(" ")
            sys.stdout.flush()

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

pin = 7
GPIO.setmode(GPIO.BOARD)
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, ())

print("Ready")
espeak.synth("Ready")

while True:
    wait_for_keydown(pin)
    key_down_time = time.time() #record the time when the key went down
    tone_obj.play(-1) #the -1 means to loop the sound
    wait_for_keyup(pin)
    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
    tone_obj.stop()
    buffer.append(DASH if key_down_length > 0.3 else DOT)


------------------------------

And the morse_lookup.py source as well.

---------------------------------

#!/usr/bin/python
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])
        espeak.synth(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.

    Thanks,

Kevin.

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.

https://www.youtube.com/watch?v=FHshPd52l-w




Circuit Description:
 
   The USB sound dongle can be purchased on Amazon.com 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.

BUILD NOTES:

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 loughkb@yahoo.com

Monday, October 19, 2015

How to properly shuffle virtual cards in software.

Last night, during one of many bouts with insomnia, I was attempting to bring on sleep by playing a mindless game,  Solitaire.   I say mindless since it requires only the tiniest bit of skill to play.  All you have to do is not miss a play.  The game will be won if it is winnable,  it depends on the order the cards come out of the deck.

This particular version of the game,  installed from the Chrome web store,  looked beautiful,  had wonderful card animation, nice sound, and terrible card shuffling!  After 30 lost games in a row,  I was quite annoyed, and further from sleep than when I began.

How do I know it was bad shuffling?   Well it was obvious by the regular discovery of two or three of the same number of card in succession.  Three Aces in a row,  or fives, etc.  This poor shuffle, at least in Solitaire,  leads to unwinnable games far more often than winnable games.

A poor shuffle might be advantageous in Poker,  to one player at least, but still a bad thing in the bigger picture.

I see this often.  Poor shuffling in a card game program.    I always remember my high school days and a certain programming contest a few friends and I had.

It was 1983.  My high school had several Apple ][+ computers in the library, as well as a couple in the vocational electronics class I was taking.   This was the early days of personal computers and almost nobody had one at home.   

Myself and a few friends, classic nerds of the day, were very much in love with these little machines.  We often fought over who would get time on them after class.  Our parents had become accustom to us staying after school until the janitor kicked us out and we trudged home to a cold dinner.   We amused ourselves with programming contests we’d come up with.  Each hoping to achieve the position of alpha male in our geeky clique.

The contest of one particular week grew out of a problem one of our group was having with trying to write a good blackjack game.    You see, we were programming in BASIC at the time, and it was not a very fast language.   The Apple ][+ had a CPU running at around 1Mhz.  BASIC, being an interpreted language was slow,  very slow,  unbelievably slow by today's standards.   Akin to comparing a formula one car to a cart drawn by a donkey.

Doug,  the kid working on the blackjack game, was rather cocky.  He was similar to Dr. Sheldon Cooper on The Big Bang Theory.  Very smart, yes, but so incredibly full of himself that we tolerated him only.   His problem was that it took well over two minutes for his card shuffle to complete at the beginning of the game.   He proposed a contest to come up with a better sorting routine.

By the end of the week, when we gathered to present our results, I was the clear winner and not by a narrow margin!    There were four of us, not counting Doug.   Doug had managed to get his shuffle down to one minute and thirty two seconds while sacrificing a bit of randomness.  Faster, but the shuffle was poor with many cards adjacent to their brothers.

The other three guys varied with one being a bit slower, and the other two beating Doug by just a couple of seconds.   All three also had somewhat poor shuffles.

My shuffle took all of four seconds.    Four,  yes,  only four seconds, in BASIC, on an Apple ][+.   Additionally, my shuffled deck was thoroughly shuffled with hardly a single instance of card pairs left. For awhile thereafter, Doug wasn't so cocky.

No,  I didn’t cheat, or use machine language.  It was a method that I came up with that I wish all programmers would use in modern card games.   Not for the speed, that hardly matters on today's incredibly fast machines.  No,  I wish programmers would use my method for the thorough randomness of the shuffled deck.

Here’s how it works.   Now,  I can’t present this in C, or Python,  .NET, or any modern language.   I’m not going to bore you with BASIC since it’s basically a dead language now.   I’m going to attempt to convey the idea,  it’s simple enough.

So first you need the deck to be shuffled.   In BASIC I simply loaded an array with numbers to represent the cards,  one through fifty two. I guess you'd use a table or something today.

Now, to shuffle them  you need a simple loop.   The loop with run fifty two times with a counter that’s incremented during each pass.   This counter will point to the position in the deck we’re working with.

For each pass of the loop,  you take the current card and exchange it with a random position in the deck.   So for pass one,  we’re working with the first card in the deck.  Pick a random number between one and fifty two and swap those two ‘cards’.

After only one pass through the loop, you’ll already have a deck that is shuffled better than what most modern card games end up with.   And it will happen incredibly fast.   Now, run the loop two or three times and you have a properly and thoroughly shuffled deck of cards.

Easy.   Simple.   So please,  programmers,  do this and properly shuffle your cards.  It will make for a much more realistic flow of the card game.

Thanks.

Wednesday, April 10, 2013

A new adventure, 3D printing

This is only the first of many posts to come.   I've ordered a 3D printer, of sorts.  More like a pile of parts.

The most common 3D printer design among hobbyists, is the completely open Prusa Mendel.  One company sells all of the common parts for a base model as a kit.  Not a bad deal if you're inclined towards building things.   Their main page is nwreprap.com if you are interested in going this route.

I will be chronicling my build in this blog, with updates and photos.  After the project is complete, I will continue to post updates about modifications, improvements, and projects I accomplish with the 'printer'.

People call them 3D printers, but really, they are additive cnc machines.  Moving a tool head, the extruder, around using the same technique and programming code that industrial milling machines use.

I'm currently waiting for the parts to arrive.  Spending my time researching, planning, and learning software.   I'll dedicate a post this weekend to software, there's a lot of ground to cover there.  

Hopefully, I'll be able to start the build next week.  Stay tuned!