/* * SibKeyer.ino * The Simpler Is Better Morse Code Keyer * (But not so simple that it doesn't do everything your want it to do) * * This is the bare-bones minimalistic version, see SibKeyerQSK.ino for * the full QSK version with extra blinky lights. * Note that the schematic shows several sections that are only used for * the QSK version of this sketch, they can be omitted for this version. * See https://www.solorb.com/elect/hamcirc/SibKeyer/ for details. * * Author: G. Forrest Cook, W0RIO * License: GPL Version 3. * * This is a minimalistic 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 * - Positive and inverted TX outputs * - A Keyer Active output which can be used for automatic T/R switching, * muting a receiver and activating a VFO * - An Analog speed control for smooth speed adjustment * * 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 17, 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. */ // Define the I/O pins const int ClockIrq = 2; // 555 timer output (interrupt 0) const int Int1 = 3; // interrupt 1 (unused) 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 Dout9 = 9; // (unused) const int Dout10 = 10; // (unused) const int Dout11 = 11; // (unused) const int Dout12 = 12; // (unused) const int NanoLED = 13; // Red LED on the Arduino Nano // Define the time constants const int Dotcount = 10; // clock pulses for dot on time const int Dashcount = 30; // clock pulses for dash on time const int Spacecount = 10; // clock pulses for space after dot and dash const int ClockTout = 500; // timeout before shutting clock down (mSec) const int ACTtime = 5; // delay between Active and TX (mSec) const int TuneDeb = 10; // manual key debounce time (mSec) const long ActiveTout = 500; // stop the 555 clock after idle time (mSec) // Global variables 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 byte Idle = 1; // The opposite of Active void setup() { // Set the parallel lines for input and output pinMode (ClockIrq, INPUT); pinMode (Int1, INPUT); pinMode (DotKey, INPUT); pinMode (DashKey, INPUT); pinMode (TuneKey, INPUT); pinMode (Active, OUTPUT); pinMode (TxOut, OUTPUT); pinMode (Dout9, OUTPUT); pinMode (Dout10, OUTPUT); pinMode (Dout11, OUTPUT); pinMode (Dout12, OUTPUT); pinMode (NanoLED, OUTPUT); // Set initial output values digitalWrite(Active, LOW); digitalWrite(TxOut, LOW); digitalWrite(Dout9, LOW); digitalWrite(Dout10, LOW); digitalWrite(Dout11, LOW); attachInterrupt(0, timerint, RISING); // Int 0 runs timerint() Serial.begin (19200); // Initialize serial port, set the baud rate Serial.println (F("W0RIO SibKeyer Minimal Version, rev: 8 Apr, 2025")); Mstime = millis(); // mark the real time } void loop() { if (Dotmem || digitalRead (DotKey)) // send dot if dot mem or paddle on { sendDot(); Dotmem=0; } if (Dashmem || digitalRead (DashKey)) // send dash if dash mem or paddle on { sendDash(); Dashmem=0; } if (digitalRead (TuneKey)) // manual key and TX tune button tuneTX(); // Lower the Active line and stop the 555 timer after idle timeout period. if (millis() - Mstime >= ActiveTout) { digitalWrite(Active, LOW); Idle = 1; } } void sendDot() { if (Idle) { digitalWrite(Active, HIGH); // turn Active high, start 555 timer delay (ACTtime); // let Active settle before TX on Idle = 0; } digitalWrite(TxOut, HIGH); // turn on TX line Tcount=Dotcount; while (Tcount) // check for dash paddle during dot if (digitalRead (DashKey)) Dashmem=1; digitalWrite(TxOut, LOW); Tcount=Spacecount; while (Tcount); // delay for trailing space Mstime = millis(); // mark the real time for active timer } void sendDash() { if (Idle) { digitalWrite(Active, HIGH); // turn Active high, start 555 timer delay (ACTtime); // let Active settle before TX on Idle = 0; } digitalWrite(TxOut, HIGH); // turn on TX line Tcount=Dashcount; while (Tcount) // check for dot paddle during dash if (digitalRead (DotKey)) Dotmem=1; digitalWrite(TxOut, LOW); Tcount=Spacecount; while (Tcount); // delay for trailing space Mstime = millis(); // mark the real time for active timer } void tuneTX() { if (Idle) { digitalWrite(Active, HIGH); // turn Active high, start 555 timer delay (ACTtime); // let Active settle before TX on Idle = 0; } digitalWrite(TxOut, HIGH); // turn on TX line while (digitalRead (TuneKey)) // loop while key/tune switch is on delay (TuneDeb); digitalWrite(TxOut, LOW); Mstime = millis(); // mark the real time for active timer } void timerint() { if (Tcount) Tcount--; // count element counter down to 0 }