Farnsworth CW Auto Caller
An Arduino CW 'recorder' that plays back iambic paddle presses at your character speed and spacing
Description
Automatic CW callers only send at a set speed with predetermined character spacing. It’s helpful to have your auto caller send at the same speed and spacing you do which can be a handy way to broadcast your receiving capabilities and not have people replying with such short character spacing. Simply flip the switch, use the paddles to “record” your call, flip the switch back to “passthru” mode, and then press the button to have the same paddle presses with the same timing sent to the radio for transmit.
The Code
// Farnsworth Auto CQ Caller for iambic paddles
#define leftButtonPinOut 8
#define rightButtonPinOut 7
#define leftButtonPinIn 2
#define rightButtonPinIn 4
#define led 13
#define recordSwitch 12
#define playbackButton 11
int leftButtonState;
int rightButtonState;
int recordSwitchState;
int playbackButtonState;
int arrayPointer = 0;
long time;
long startTime;
int lastLeftButtonState;
int lastRightButtonState;
int lastPlaybackButtonState;
long playTime;
long prevTimePress;
boolean hasRecorded = false;
boolean recordMemCleared = false;
boolean memExceeded = false;
int interrupt;
struct paddleTime
{
long timePress;
int leftButtonRecState:1;
int rightButtonRecState:1;
};
paddleTime keyTimes[120]; // array to hold key-up/key-down times 120 max at this time
void setup ()
{
pinMode(leftButtonPinIn, INPUT);
pinMode(rightButtonPinIn, INPUT);
pinMode(rightButtonPinOut, OUTPUT);
pinMode(leftButtonPinOut, OUTPUT);
pinMode(recordSwitch, INPUT);
pinMode(playbackButton, INPUT);
digitalWrite(leftButtonPinIn, HIGH);
digitalWrite(rightButtonPinIn, HIGH);
digitalWrite(recordSwitch, HIGH);
digitalWrite(playbackButton, HIGH);
pinMode(led, OUTPUT);
}
void loop()
{
recordSwitchState = digitalRead(recordSwitch);
if (recordSwitchState == LOW) // recording
{
delay(20); // debounce for rusty switches
if (recordMemCleared == false) // clear out the memory for a new recording
{
paddleTime paddleMark = {0,0,0};
for (arrayPointer=0; arrayPointer < 120; arrayPointer++)
{
paddleMark.timePress = -1;
paddleMark.leftButtonRecState = HIGH;
paddleMark.rightButtonRecState = HIGH;
keyTimes[arrayPointer]=paddleMark;
}
recordMemCleared = true;
memExceeded = false;
hasRecorded = true;
arrayPointer = 0;
}
leftButtonState = digitalRead(leftButtonPinIn);
rightButtonState = digitalRead(rightButtonPinIn);
digitalWrite(led, HIGH);
paddleTime paddleMark = {0,0,0};
if (arrayPointer > 120) // Arduino's tiny memory exceeded!
{
digitalWrite(leftButtonPinOut, LOW); // these two write lows prevent the paddles from being stuck down in the event of jitter
digitalWrite(rightButtonPinOut, LOW);
digitalWrite (led, HIGH);
delay(300);
digitalWrite (led, LOW);
delay(600);
memExceeded = true;
}
else
{
if (leftButtonState != lastLeftButtonState || rightButtonState != lastRightButtonState)
{
digitalWrite(leftButtonPinOut, !leftButtonState);
digitalWrite(rightButtonPinOut, !rightButtonState);
time = millis();
paddleMark.timePress = time;
paddleMark.leftButtonRecState = leftButtonState;
paddleMark.rightButtonRecState = rightButtonState;
lastLeftButtonState = leftButtonState;
lastRightButtonState = rightButtonState;
keyTimes[arrayPointer]=paddleMark;
Serial.print(paddleMark.leftButtonRecState);
Serial.println(paddleMark.rightButtonRecState);
delay(20);
arrayPointer++;
}
}
} // end recording phase
if (recordSwitchState == HIGH) // not recording, passthrough mode
{
recordMemCleared = false;
arrayPointer = 0;
digitalWrite(led, LOW);
leftButtonState = digitalRead(leftButtonPinIn);
rightButtonState = digitalRead(rightButtonPinIn);
playbackButtonState = digitalRead(playbackButton);
if (playbackButtonState == LOW && lastPlaybackButtonState == HIGH && hasRecorded == true && memExceeded == false) // playback mode
{
interrupt = 0;
Serial.println("playing back");
digitalWrite(led, HIGH);
paddleTime paddleMark = {0,0,0};
paddleMark = keyTimes[0];
startTime = paddleMark.timePress;
for (arrayPointer = 0; arrayPointer < 120; arrayPointer++)
{
leftButtonState = digitalRead(leftButtonPinIn);
rightButtonState = digitalRead(rightButtonPinIn);
if (leftButtonState == LOW || rightButtonState == LOW) // interrupted by paddle press
{
interrupt = 1;
}
if (paddleMark.timePress == -1 || interrupt == 1) // out of bounds or interrupted
{
// do nothing
}
else
{
paddleMark = keyTimes[arrayPointer];
if (arrayPointer > 1) // was 1 -- something is wrong here, as it keeps the initial delay on the first run but not subsequent recordings
{
playTime =(paddleMark.timePress - prevTimePress);
if (playTime > 0)
{
delay(playTime);
}
}
if (paddleMark.leftButtonRecState == -1)
{
digitalWrite(leftButtonPinOut, LOW);
}
else
{
digitalWrite(leftButtonPinOut, HIGH);
}
if (paddleMark.rightButtonRecState == -1)
{
digitalWrite(rightButtonPinOut, LOW);
}
else
{
digitalWrite(rightButtonPinOut, HIGH);
}
prevTimePress = paddleMark.timePress;
}
}
delay(200); // debounce
lastPlaybackButtonState = playbackButtonState;
}
if (playbackButtonState == HIGH && lastPlaybackButtonState == LOW) // nothing
{
lastPlaybackButtonState = playbackButtonState;
}
digitalWrite (leftButtonPinOut, !leftButtonState);
digitalWrite (rightButtonPinOut, !rightButtonState);
}
}
Last modified on 2015-01-01