/* * SibKeyerQSK.ino * The Simpler Is Better Morse Code Keyer * (But not so simple that it doesn't do everything your want it to do.) * Full QSK version, * see the SibKeyer.ino source code for an even simpler version. * * Author: G. Forrest Cook, W0RIO * License: GPL Version 3. * * This is a basic keyer design with only a few features including: * - Dot and Dash paddle inputs * - A manual key input which doubles as a TX tune control. * - Buffered inputs via a 74LS14 to keep static zaps away from the * microprocessor and invert the logic for pull-down key inputs * - dot and dash memories for (mode B) iambic keying * - Separate TX and RX outputs with a programmable delay for QSK operation * - A Keyer Active output which can be used to mute a receiver * - or activate a VFO * - A dash-on output for implementing optional two-tone CW (FSCW) * - A dot-on output for implementing optional two-tone CW (FSCW) * - The Dash and Dot outputs can also be used for optional eye-candy LEDs * - An Analog speed control for smooth speed adjustment * * It would not be too difficult to add a simple memory keyer to this code. * That could be useful for sending a CQ message with your call sign. * * This has been developed on an Arduino Nano but should work with * other Atmega328P based Arduinos such as the Uno, it will also work * with a standalone Atmega328P IC. * * Project Started: Dec 13, 2024 * Version: see the Serial.println statement below * * The keyer is clocked by an interrupt signal on digital pin 2 (IRQ 0) * which is generated by an NMOS or CMOS 555 timer chip. * This produces a wide-range analog-adjustable speed control. * The first clock pulse from the 555 takes longer than the rest, * by counting multiple clock pulses for dot, dash and space timing, the * first pulse timing error is minimized. * * A completely optional, but fun addition to this keyer involves connecting * differently colored LEDs to the following output pins using 1K current * limit resistors to ground: Active, RxOut, TxOut, DotOut, and Dashout. * This will produce a light show when the keyer is running. * */ // Define the I/O pins const int ClockIrq = 2; // 555 timer output (interrupt 0) const int Int1 = 3; // Unused interrupt const int DotKey = 4; // Dot paddle (buffered) const int DashKey = 5; // Dash paddle (buffered) const int TuneKey = 6; // Tune switch const int Active = 7; // 555 timer control and keyer active signal const int TxOut = 8; // Transmit output const int RxOut = 9; // Receive output for QSK const int DashOut = 10; // dash output for two-tone CW const int DotOut = 11; // dot output for two-tone CW const int Dout12 = 12; const int NanoLED = 13; // Red LED on Nano const int Dotcount = 10; // clock pulses for dot on time const int Spacecount = 10; // clock pulses for space time const int Dashcount = 30; // clock pulses for dash on time const int ClockTout = 500; // clock pulses before shutting clock down const int QSKtime = 5; // delay between TX and RX for QSK (mSec) // adjust for your qsk hardware, set to // a max of about 15mS, 5 mS is about right const long ActiveTout = 500; // stop the 555 clock after idle time (mSec) unsigned long Mstime; // running milliseconds counter volatile byte Tcount = 0; // interrupt routine element time counter byte dotmem = 0; // dot paddle pressed memory byte dashmem = 0; // dash paddle pressed menory void setup() { pinMode (ClockIrq, INPUT); pinMode (Int1, OUTPUT); // unused irq pin pinMode (Active, OUTPUT); pinMode (DotKey, INPUT); pinMode (DashKey, INPUT); pinMode (TuneKey, INPUT); pinMode (TxOut, OUTPUT); pinMode (RxOut, OUTPUT); pinMode (DashOut, OUTPUT); pinMode (DotOut, OUTPUT); pinMode (Dout12, OUTPUT); pinMode (NanoLED, OUTPUT); // Set initial output values digitalWrite(Active, LOW); digitalWrite(TxOut, LOW); digitalWrite(RxOut, HIGH); digitalWrite(DashOut, LOW); digitalWrite(DotOut, LOW); Serial.begin (19200); // Initialize serial port, set the baud rate Blink13 (5); // blink the onboard LED at startup attachInterrupt(0, timerint, RISING); // Int 0 causes dacstep to run. Mstime = millis(); // mark the real time Serial.println (F("W0RIO SIB Keyer with QSK, rev: 19 Dec, 2024")); } void loop() { if (dotmem) { dotmem=0; sendDot(); } else if (digitalRead (DotKey)) sendDot(); if (dashmem) { dashmem=0; sendDash(); } else if (digitalRead (DashKey)) sendDash(); if (digitalRead (TuneKey)) { digitalWrite(Active, HIGH); // start the 555 timer and active sig digitalWrite(RxOut, LOW); delay(QSKtime); // qsk delay digitalWrite(TxOut, HIGH); while (digitalRead (TuneKey)); // loop while tune mode is pressed digitalWrite(TxOut, LOW); delay(QSKtime); // qsk delay digitalWrite(RxOut, HIGH); Mstime = millis(); // mark the real time } // Stop the 555 timer after the timeout period. if (millis() >= Mstime + ActiveTout) digitalWrite(Active, LOW); } void sendDot() { digitalWrite(Active, HIGH); // start the 555 timer and active sig Tcount=Dotcount; digitalWrite(DotOut, HIGH); // sig only active during dots digitalWrite(RxOut, LOW); delay(QSKtime); // qsk delay digitalWrite(TxOut, HIGH); while (Tcount) // check for dash paddle if (digitalRead (DashKey)) dashmem=1; Tcount=Spacecount; digitalWrite(TxOut, LOW); delay(QSKtime); // qsk delay digitalWrite(RxOut, HIGH); digitalWrite(DotOut, LOW); // sig only active during dots while (Tcount) // check for dash paddle if (digitalRead (DashKey)) dashmem=1; Mstime = millis(); // mark the real time } void sendDash() { digitalWrite(Active, HIGH); // start the 555 timer and active sig Tcount=Dashcount; digitalWrite(DashOut, HIGH); // sig only active during dashes digitalWrite(RxOut, LOW); delay(QSKtime); // qsk delay digitalWrite(TxOut, HIGH); while (Tcount) // check for dot paddle if (digitalRead (DotKey)) dotmem=1; Tcount=Spacecount; digitalWrite(TxOut, LOW); delay(QSKtime); // qsk delay digitalWrite(RxOut, HIGH); digitalWrite(DashOut, LOW); while (Tcount) // check for dot paddle if (digitalRead (DotKey)) dotmem=1; Mstime = millis(); // mark the real time } void timerint() { if (Tcount) Tcount--; // count element counter down to 0 } void Blink13(byte count) // blink the red LED on Digital 13 { byte i; for (i=0; i