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();
}

