Spota One

8W single-channel CW transceiver

A deluxe version of the CW Pixie (the most minimalist and affordable DIY radio)

  • Direct-conversion receiver covering both sidebands
  • Fixed frequency operation on the 60, 40, 30, 20, 17, or 15-meter bands, depending on hardware configuration
  • Operating voltage of 7 to 12 V with an output power of 2 to 8W
  • CW mode with audio filter, internal keyer and memory
  • AGC
  • RIT
  • Internal speaker

The PCB was designed using EasyEDA and manufactured by JLCPCB. Those in the know can download the schematic and order the pcb here

https://oshwlab.com/dl1cr/spotaone1_copy_copy

Here is a clip of the first tests with Uwe (DK8OA) as the operator. The prototype is still built in „Manhattan style.“

Source code in a very early version

/////////////////////////////////////////////////////////////////////////////
// CW Keyer for DigiSpark Attiny85
// Version 28/12/2023 V2 by DL1CR 
// 
// txON HIGH // txOFF LOW     
/////////////////////////////////////////////////////////////////////////////   
#include "Wire.h"
#include <EEPROM.h>
/////////////////////////////////////////////////////////////////////////////   
#define dotPort 0
#define outPort 1  
#define dashPort 2  
#define modePort 3  
#define tonePort 4 
/////////////////////////////////////////////////////////////////////////////
//  CQ CQ DE DL1CR/P  und  TEST DE DL1CR 
char *mem[] = {"-.-. --.- -.-. --.-  -.. .  -.. .-.. .---- -.-. .-. -..-. .--.           ",
               "- . ... -  -.. .  -.. .-.. .---- -.-. .-.       "}; 
////////////////////////////////////////////////////////////////////////////
int count = 0;
int ticks = 80;  // 80 ticks = 15wpm  
bool dotMem = false;
bool dashMem = false;
////////////////////////////////////////////////////////////////////////////
void setup()  {
  // Port setzen
  digitalWrite(outPort, LOW); // txOFF LOW  
  pinMode(outPort, OUTPUT);
  pinMode(dotPort, INPUT_PULLUP); 
  pinMode(dashPort, INPUT_PULLUP); 
  pinMode(modePort, INPUT_PULLUP); 

  EEPROM.get(0, ticks);
  if (ticks > 125 | ticks < 50) ticks = 80;   // 10-25wpm
}

void  txOn() {
  digitalWrite(outPort, HIGH);   // !!!
  // tone(tonePort, 700);
}

void  txOff() {
  digitalWrite(outPort, LOW);  // !!! 
  // noTone(tonePort);
}

boolean dash() {
  return !digitalRead(dashPort);
}

boolean dot() {
  return !digitalRead(dotPort);
}

boolean squeeze() {
  return (dash() && dot());
}

void doDot() { 
  count = 0;
  dotMem = false;
  txOn();
  while (count < ticks) {
    if (dash()) dashMem = true;
    delay(1);
    ++count;
  }
  count = 0;
  txOff();
    while (count < ticks) {
     if (dash()) dashMem = true; 
    delay(1);
    ++count;
  }
}

void doDash() {
  count = 0;
  dashMem = false;
  txOn();
  while (count < (ticks * 3)) {
    if (dot()) dotMem = true;
    delay(1);
    ++count;
  }
  count = 0;
  txOff();
  while (count < ticks) {
    if (dot()) dotMem = true;  
    delay(1);
    ++count;
  }
}

void checkPaddle() { 
  if (dot()) doDot();
  if (dash()) doDash();
}

void checkMode() { 
  if(digitalRead(modePort)) return; 

  if (dot()) 
    memoryMode(0); // cq
  else 
    if (dash())
      memoryMode(1); // test
    else
      wpmMode();
}    
    
void wpmMode() { 
  while (!squeeze()) {
     txOn();
     delay (ticks); 
     txOff();
     delay (ticks); 
     if(dot())
        --ticks;
     if(dash())
        ++ticks;
  }
  EEPROM.put (0, ticks);
}

void memoryMode(int typ) { 
  String cw = String(mem[typ]);
  
  while (43){
    for (int i=0; i<cw.length(); i++) {
    
      switch (cw.charAt(i)) {
        case '.':
          txOn();
          delay (ticks);
          txOff();
          delay(ticks);
          break;
      
        case '-':
          txOn();
          delay (3*ticks);
          txOff();
          delay (ticks);
          break;

        case ' ':
          delay (3*ticks);
          break;
      } // switch

      if(squeeze()) return; // Abbruch 
   } //for
  } //while
}

void loop() {
  if (dotMem) doDot();
  if (dashMem) doDash();
  
  count = 0;
  checkPaddle();
  checkMode();
}