My New 30m CW QRP TX is finished

A new TX is born

This new TX is re-using PA strip I tried in the QRSS MEPT I've changed the Arduino code and added a rotary encoder and LCD.

Basic Architecture

I've also built a "QSK" board that handles the antenna changeover relay, TX VFO enable/disable and keying the driver stage of the PA strip.

It's still a work in progress....

The boards on the bench :

RBN Spots of me calling CQ with 1W - using the antenna c/o QSK board etc.

Final Build

I repurposed an enclosure from a previous project and started cramming everything in.

And here it is, finished and incorporated into the shack

The final feature set:

  • Arduino (Seeeduino nano) controlling an SI5351 clock board

  • LCD Display

    • Frequency
    • RX / TX status
    • Approximate TX Power setting (calculated from PA supply Voltage measured by the Arduino ADC)
  • Semi-QSK

    • Internal Antenna C/O relay
    • RX Mute line (ground to mute)
  • Variable TX Output power via voltage regulator on DC supply to final class-C IRF510 MOSFET

    • Output adjustable 0.1W -> 5W

Separate TX and RX

I considered making a transceiver by putting the phasing RX [[..:radio:topics:arduino:30m_phasing_rx|]] and this TX together in one box, with a single Arduino / SI5351 but I decided to keep them totally separate. This harks back to an earlier time when this was a common way of operating. There is much more versatility from this approach.

Arduino Sketch

#include <rgb_lcd.h>
#include <si5351.h>

static const long bandStart = 10100000;          // start of VFO range
static const long bandEnd =   10150000;          // end of VFO range
static const long bandInit =  10120000;          // where to initially set the frequency
volatile long freq = 10116000;                   // the current freq
volatile long oldfreq = 0;                      // the previous freq
volatile long radix = 1000;                     // How much to change the frequency by, clicking the Up Down switches
volatile long oldradix = 0;                     // the previous radix
volatile int TX = 0;                            // 0=RX, 1=TX
volatile int oldTX = 1;                         // the old TX
unsigned int encoderA, encoderB, encoderC = 1;  // rotary encoder variables

int PAVoltPin = A0;
int PAVolt = 0;
int oldPAVolt = 0;

// Rotary encoder pins and other inputs
static const int rotAPin = 4;
static const int rotBPin = 3;
static const int radixPin = 2;
static const int PTTPin = 5;

int digit1 = 0;
int digit2 = 0;
int digit3 = 0;
int digit4 = 0;
int digit5 = 0;
int digit6 = 0;
int digit7 = 0;
int digit8 = 0;

double TXPower = 0.0;


float  Yint = 1.74E-6;
float   Slope = 2.18;

// Instantiate the Objects
rgb_lcd lcd;              // 3F the address of the LCD
Si5351 si5351;


void setup()
{
  // Set up I/O pins
  pinMode(rotAPin, INPUT);
  digitalWrite(rotAPin, HIGH);                    // internal pull-up enabled
  pinMode(rotBPin, INPUT);
  digitalWrite(rotBPin, HIGH);                    // internal pull-up enabled
  pinMode(radixPin, INPUT);
  digitalWrite(radixPin, HIGH);                   // internal pull-up enabled
  pinMode(PTTPin, INPUT_PULLUP);
  //digitalWrite(PTTPin, HIGH);                      // internal pull-up disabled
  pinMode(13, OUTPUT);
  analogReference( INTERNAL );

  // Initialize the display
  lcd.begin(16, 2);
//  lcd.backlight();
  lcd.noCursor();

  // Initialize the DDS
  si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, -32500);         // 62100 is the specific calibration factor for this Si5351 board
  si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_4MA);   // 2 mA for HB mixers


}


void loop()
{
  CheckEncoder();
  CheckRadixSwitch();
  CheckPTTPin();
  CheckPAVolts();
}

void CheckPAVolts()
{

PAVolt = analogRead(PAVoltPin);

if ( TX == 1 )
  {
    PAVolt = PAVolt / 0.92;
  }
 
if ( abs(PAVolt/10 - oldPAVolt) > 1 )
{

 

  TXPower = Yint * pow(PAVolt,Slope);
  UpdateDisplay();
  oldPAVolt = PAVolt/10;

}

//  lcd.print(PAVolt,0);
 // lcd.noCursor();
  
}
void CheckEncoder()
{
  byte encoderA = digitalRead(rotAPin);
  byte encoderB = digitalRead(rotBPin);

  if ((encoderA == HIGH) && (encoderC == LOW))
  {
    if (encoderB == HIGH)
      // Decrease frequency
      freq = constrain(freq - radix, bandStart, bandEnd);
    else
      // Increase frequency
      freq = constrain(freq + radix, bandStart, bandEnd);
  }
  encoderC = encoderA;

  if (freq != oldfreq)
  {
    UpdateDisplay();
    SendFrequency();
    oldfreq = freq;
  }
}

void CheckRadixSwitch()
{
  if (digitalRead(radixPin) == 0)
  {
    radix = radix / 10;
    if (radix < 10)
      radix = 10000;
    delay(200);
  }

  if (radix != oldradix)
  {
    UpdateDisplay();
    oldradix = radix;
  }
}



void CheckPTTPin()
{
  if (digitalRead(PTTPin) == 0)
  {
    TX = 1;
    digitalWrite(13, HIGH);
  }
  
  if (digitalRead(PTTPin) == 1)
   {
    TX = 0;
    digitalWrite(13, LOW);
   }
   
  if (TX != oldTX)
  {
    UpdateDisplay();

    SendFrequency();
    oldTX = TX;
  }
}

void UpdateDisplay()
{
  // freq

    digit1 = (freq%10);
    digit2 = ((freq/10)%10);
    digit3 = ((freq/100)%10);
    digit4 = ((freq/1000)%10);   
    digit5 = ((freq/10000)%10);
    digit6 = ((freq/100000)%10);
    digit7 = ((freq/1000000)%10);
    digit8 = ((freq/10000000)%10);


  lcd.setCursor(0,0);
  lcd.print(digit8);
  
  lcd.setCursor(1,0);
  lcd.print(digit7);
  
  lcd.setCursor(2,0);
  lcd.print(",");
  
  lcd.setCursor(3,0);
  lcd.print(digit6);
  
  lcd.setCursor(4,0);
  lcd.print(digit5);
  
  lcd.setCursor(5,0);
  lcd.print(digit4);
  
  lcd.setCursor(6,0);
  lcd.print(".");
  
  lcd.setCursor(7,0);
  lcd.print(digit3);
  
  lcd.setCursor(8,0);
  lcd.print(digit2); 

//  lcd.setCursor(9,0);
//  lcd.print(digit1);

  lcd.setCursor(10,0);
  lcd.print("kHz");
  
 // lcd.print(freq);
  // mode
  lcd.setCursor(0, 1);
  lcd.print("CW TX ");
 // lcd.setCursor(10, 1);
  // PTT
  
  lcd.print(TXPower,1);
  lcd.print("W");
  lcd.setCursor(11, 1);
  if (TX == 1)
    lcd.print("[TX]");
  if (TX == 0)
    lcd.print("[RX]");
  if (radix == 10000)
  lcd.setCursor(4,0);
  if (radix == 1000)
  lcd.setCursor(5, 0);
  if (radix == 100)
  lcd.setCursor(7, 0);
  if (radix == 10)
  lcd.setCursor(8,0);
//  if (radix == 1)
//  lcd.setCursor(9,0);
  lcd.cursor();
}




void SendFrequency()
{
    if (TX == 1)                                                         
    {
      si5351.set_freq((( freq) * 100ULL), SI5351_CLK0);   
      si5351.output_enable(SI5351_CLK0, 1);
    }
    else                                                                 
    {
       si5351.output_enable(SI5351_CLK0, 0);
    }
}

Comments