/** * CW Keyer * * The CW keyer handles either a straight key or an iambic / paddle key. * They all use just one analog input line. This is how it works. * The analog line has the internal pull-up resistor enabled. * When a straight key is connected, it shorts the pull-up resistor, analog input is 0 volts * When a paddle is connected, the dot and the dash are connected to the analog pin through * a 10K and a 2.2K resistors. These produce a 4v and a 2v input to the analog pins. * So, the readings are as follows : * 0v - straight key * 1-2.5 v - paddle dot * 2.5 to 4.5 v - paddle dash * 2.0 to 0.5 v - dot and dash pressed * * The keyer is written to transparently handle all these cases * * Generating CW * The CW is cleanly generated by unbalancing the front-end mixer * and putting the local oscillator directly at the CW transmit frequency. * The sidetone, generated by the Arduino is injected into the volume control */ // in milliseconds, this is the parameter that determines how long the tx will hold between cw key downs #define CW_TIMEOUT (600l) #define PADDLE_DOT 1 #define PADDLE_DASH 2 #define PADDLE_BOTH 3 #define PADDLE_STRAIGHT 4 //we store the last padde's character //to alternatively send dots and dashes //when both are simultaneously pressed static char lastPaddle = 0; static boolean isStraightKey = false; //reads the analog keyer pin and reports the paddle byte getPaddle(){ int paddle = analogRead(ANALOG_KEYER); if (paddle > 800) // above 4v is up return 0; if (paddle > 600) // 4-3v is dot return PADDLE_DASH; else if (paddle > 300) //1-2v is dash return PADDLE_DOT; else if (paddle > 50) return PADDLE_BOTH; //both are between 1 and 2v else return PADDLE_STRAIGHT; //less than 1v is the straight key } /** * Starts transmitting the carrier with the sidetone * It assumes that we have called cwTxStart and not called cwTxStop * each time it is called, the cwTimeOut is pushed further into the future */ void cwKeydown(){ keyDown = 1; //tracks the CW_KEY tone(CW_TONE, (int)sideTone); digitalWrite(CW_KEY, 1); cwTimeout = millis() + CW_TIMEOUT; } /** * Stops the cw carrier transmission along with the sidetone * Pushes the cwTimeout further into the future */ void cwKeyUp(){ keyDown = 0; //tracks the CW_KEY noTone(CW_TONE); digitalWrite(CW_KEY, 0); cwTimeout = millis() + CW_TIMEOUT; } /** * The keyer handles the straight key as well as the iambic key * This module keeps looping until the user stops sending cw * if the cwTimeout is set to 0, then it means, we have to exit the keyer loop * Each time the key is hit the cwTimeout is pushed to a time in the future by cwKeyDown() */ void cwKeyer(){ byte paddle; lastPaddle = 0; while(1){ paddle = getPaddle(); // do nothing if the paddle has not been touched, unless // we are in the cw mode and we have timed out if (!paddle){ if (0 < cwTimeout && cwTimeout < millis()){ cwTimeout = 0; keyDown = 0; stopTx(); } if (!cwTimeout) return; //if a paddle was used (not a straight key) we should extend the space to be a full dash //by adding two more dots long space (one has already been added at the end of the dot or dash) if (cwTimeout > 0 && lastPaddle != PADDLE_STRAIGHT) active_delay(cwSpeed * 2); // got back to the begining of the loop, if no further activity happens on the paddle or the straight key // we will time out, and return out of this routine active_delay(5); continue; } Serial.print("paddle:");Serial.println(paddle); // if we are here, it is only because the key or the paddle is pressed if (!inTx){ keyDown = 0; cwTimeout = millis() + CW_TIMEOUT; startTx(TX_CW); updateDisplay(); } // star the transmission) // we store the transmitted character in the lastPaddle cwKeydown(); if (paddle == PADDLE_DOT){ active_delay(cwSpeed); lastPaddle = PADDLE_DOT; } else if (paddle == PADDLE_DASH){ active_delay(cwSpeed * 3); lastPaddle = PADDLE_DASH; } else if (paddle == PADDLE_BOTH){ //both paddles down //depending upon what was sent last, send the other if (lastPaddle == PADDLE_DOT) { active_delay(cwSpeed * 3); lastPaddle = PADDLE_DASH; }else{ active_delay(cwSpeed); lastPaddle = PADDLE_DOT; } } else if (paddle == PADDLE_STRAIGHT){ while (getPaddle() == PADDLE_STRAIGHT) active_delay(1); lastPaddle = PADDLE_STRAIGHT; } cwKeyUp(); //introduce a dot long gap between characters if the keyer was used if (lastPaddle != PADDLE_STRAIGHT) active_delay(cwSpeed); } }