ppenguin
Published © GPL3+

Simple Morse Code Transceiver

Great for kids to learn about communication in the old days. Two Arduino Nano, a LED and a pushbutton, print a morse code alphabet and play!

BeginnerShowcase (no instructions)3,093
Simple Morse Code Transceiver

Things used in this project

Story

Read more

Schematics

morse transceiver

Fritzing bread board schematic for morse node.

Code

morse transceiver

Arduino
Simply upload to Arduino Nano #1 with radioNumber = 0 and then to #2 with radioNumber = 1.
/*
    Nov 2017 - morse tranceiver by ppenguin
    Based on below described.

    Simple morse code transceiver using 2 nodes consisting of NRF24 transceiver, a push button
    and a RGB led connected to an Arduino Nano.
    Nice for 2 kids to each sit in a room and experiment with "the old days" morse code communication.
    The LED will light green for outgoing code and red for incoming code (to have better feedback)

    Removed most serial code (except debugging) to enable stand alone operation
   
   Dec 2014 - TMRh20 - Updated
   Derived from examples by J. Coliz <maniacbug@ymail.com>
*/
/**
 * Example for efficient call-response using ack-payloads 
 * 
 * This example continues to make use of all the normal functionality of the radios including 
 * the auto-ack and auto-retry features, but allows ack-payloads to be written optionlly as well. 
 * This allows very fast call-response communication, with the responding radio never having to 
 * switch out of Primary Receiver mode to send back a payload, but having the option to switch to 
 * primary transmitter if wanting to initiate communication instead of respond to a commmunication. 
 */
 
#include <SPI.h>
#include "RF24.h"

/****************** User Config ***************************/
/***      Set this radio as radio number 0 or 1         ***/
bool radioNumber = 1;

/* Hardware configuration: Set up nRF24L01 radio on SPI bus plus pins 7 & 8 */
RF24 radio(9,10);  // use mysensor default pins since I'm used to that
/**********************************************************/
                                                                           // Topology
byte addresses[][6] = {"1Node","2Node"};              // Radio pipe addresses for the 2 nodes to communicate.

// Role management: Set up role.  This sketch uses the same software for all the nodes
// in this system.  Doing so greatly simplifies testing.  
typedef enum { role_ping_out = 1, role_pong_back } role_e;                 // The various roles supported by this sketch
const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"};  // The debug-friendly names of those roles
role_e role = role_pong_back;                                              // The role of the current running sketch

#define LONG 2      // value of morse long _
#define SHORT 1     // value of morse short .
#define ACK 3       // value of ACK payload (necessary?)
#define DELAY 10    // loop delay 
#define TLONG 250   // 250ms or longer is considered long
#define TLONGE 800  // max long period
#define TSHORT 170  // 170ms or shorter is considered short
#define MAXFAIL 10

#define PINBT 2     // digital input push button
#define PINL1 3     // digital output LED1
#define PINL2 4     // digital output LED2

byte payload = 0;                                                         // A single byte as payload
byte ackByte = ACK;
bool btpressed = false;
unsigned long lastpressed = 0;
byte fail = 0;

void setup(){

   
  Serial.begin(115200);
  Serial.println(F("RF24/examples/GettingStarted_CallResponse"));
  /*
  Serial.println(F("*** PRESS 'T' to begin transmitting to the other node"));
  */
   
  // Setup and configure radio

  radio.begin();

  radio.enableAckPayload();                     // Allow optional ack payloads
  radio.enableDynamicPayloads();                // Ack payloads are dynamic payloads
  
  if(radioNumber){
    radio.openWritingPipe(addresses[1]);        // Both radios listen on the same pipes by default, but opposite addresses
    radio.openReadingPipe(1,addresses[0]);      // Open a reading pipe on address 0, pipe 1
  }else{
    radio.openWritingPipe(addresses[0]);
    radio.openReadingPipe(1,addresses[1]);
  }
  radio.startListening();                       // Start listening  
  
  radio.writeAckPayload(1,&ackByte,1);          // Pre-load an ack-paylod into the FIFO buffer for pipe 1
  //radio.printDetails();

  pinMode(PINBT, INPUT_PULLUP);                 // high == off, but now de don't need an external resistor for the button
  pinMode(PINL1, OUTPUT);
  pinMode(PINL2, OUTPUT);
  
}

void loop(void) {

  /* loop initial/primary function is
       (1) poll push button
       (2) check whether msg received
     If the push button was pushed and we have a result (either short or long) it is sent (role change)
  */

  btpressed = !digitalRead(PINBT);  // invert because we use internal pullup
  if (btpressed && lastpressed == 0) {
    lastpressed = millis();  // button first pressed after last release
    Serial.print("Button down");
    digitalWrite(PINL1, 1);
  }
 
  if (!btpressed && lastpressed != 0) { // button release
    Serial.print("Button release");
    digitalWrite(PINL1, 0);
    if (millis() - lastpressed <= TSHORT) {
      payload = SHORT;
      Serial.print("Set payload SHORT");
    } else if ((millis() - lastpressed >= TLONG) && (millis() - lastpressed <= TLONGE)) {
      payload = LONG;
      Serial.print("Set payload LONG");  
    } else {
      payload = 0;
    }
    lastpressed = 0;
    // role = role_ping_out;  // we have data now, so we send it
  }

  
/****************** Ping Out Role ***************************/
  //if (role == role_ping_out){                             // Radio is in ping mode
  if (payload > 0) {                                        // we only need to send if we have a payload

    byte gotByte;                                           // Initialize a variable for the incoming response (only for ACK)
    
    radio.stopListening();                                  // First, stop listening so we can talk.      
    Serial.print(F("Now sending "));                         // Use a simple byte counter as payload
    Serial.println(payload);
    
    unsigned long time = millis();                          // Record the current millisecond count   
                                                            
    if ( radio.write(&payload,1) ) {                        // Send payload to the other radio 
        /* if(!radio.available()){                             // If nothing in the buffer, we got an ack but it is blank
            Serial.print(F("Got blank response. round-trip delay: "));
            Serial.print(millis()-time);
            Serial.println(F(" milliseconds"));     
        }else{ */      
            while(radio.available() ){                      // If an ack with payload was received
                radio.read( &gotByte, 1 );                  // Read it, and display the response time
                unsigned long timer = millis();
                if (gotByte == ACK) {                       // only interested in ACK now
                  Serial.print(F("Got ACK response "));
                  Serial.print(gotByte);
                  Serial.print(F(" round-trip delay: "));
                  Serial.print(timer - time);
                  Serial.println(F(" milliseconds"));
                  break; // one ACK is enough, now continue listening
                }
            }
        //}
        payload = 0; // reset payload after sucessfuly sending
    
    } else { 
      fail++;
      if (fail >= MAXFAIL) {
        fail = 0;
        payload = 0;    // give up and start listening
      }
    }          
    
    delay(DELAY);  // Try again later
  }


/****************** Pong Back Role ***************************/
  // if ( role == role_pong_back ) {
  if (payload == 0) {                              // nothing to send, so listen
    radio.startListening();
    
    byte pipeNo, gotByte;                          // Declare variables for the pipe and the byte received
    while( radio.available(&pipeNo)){              // Read all available payloads
      digitalWrite(PINL2, 0); // default LED off
      radio.read( &gotByte, 1 );              
                                                   // Since this is a call-response. Respond directly with an ack payload.
                                                   // Ack payloads are much more efficient than switching to transmit mode to respond to a call
      radio.writeAckPayload(pipeNo,&ackByte, 1 );  // This can be commented out to send empty payloads.
      Serial.print(F("sent ACK"));
      
      // handle user output for received byte
      Serial.println(gotByte);  
      unsigned long ttime = 0;
      if (gotByte == LONG) ttime = millis() + TLONG;
      else if (gotByte == SHORT) ttime = millis() + int(TSHORT/3);
      
      while (millis() < ttime) {
        digitalWrite(PINL2, 1);  
      }
      digitalWrite(PINL2, 0);
    }
  }
}

Credits

ppenguin

ppenguin

1 project • 1 follower

Comments