Morse Code programming

I have been trying for a little while to come up with a program for a morse code transmitter. It uses an led to flash what is typed into the serial monitor. The problem is that all of the code I have seen on the Internet uses delay and I can't use that because I am eventually going to have to have it send and receive at the same time. Can anyone one provide with something similar to delay that won't stop everything. I have been trying to work with millis, but it is getting complicated.

. . . - - - . . .

See the blink without delay example in the learning section, or playground.

Basically, you will watch the time going by, and when enough has elapsed you start/stop your action, and watch the time go by some for the next action.
Then you can be doing other stuff while that time is elapsing.

Yes. I have seen this. I am just having trouble implementing it into my morse code program. I understand how it works though.

So post what you have implemented and we can see where you are going wrong.

What I tried to do was have a millis counter for the period of a dot and dash and letter space. It seems like there must be an easier way then having a millis counter for each one. This program does upload and will light but not blink the led when those letters are typed.

#define dotPeriod 250
#define dashPeriod (dotPeriod*3)
#define relaxTime (dotPeriod)
#define letterSpace (dotPeriod*3)
#define wordSpace (dotPeriod*7)
#define flash ( digitalWrite(ledPin,ledState))
const int ledPin =  13;      // the number of the LED pin


int leddotState = LOW;  
int leddashState = LOW;
int ledwordspaceState = HIGH;
long previousdotMillis = 0;   
long previousdashMillis = 0;
long previouswordspaceMillis = 0;

void dot(){
  digitalWrite(ledPin,leddotState);
}
void dash(){

digitalWrite(ledPin,leddashState);
}
void wordspace(){
digitalWrite(ledPin,ledwordspaceState);
}
void setup(){
  Serial.begin(9600);
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);
}

void loop(){
   unsigned long currentdotMillis = millis();
   unsigned long currentdashMillis = millis();
    unsigned long currentwordspaceMillis = millis();
   if(currentdotMillis - previousdotMillis > dotPeriod) {
    // save the last time you blinked the LED 
    previousdotMillis = currentdotMillis;   

    // if the LED is off turn it on and vice-versa:
    if (leddotState == LOW)
      leddotState = HIGH;
    else
      leddotState = LOW;
  
  if(currentdashMillis - previousdashMillis > dashPeriod) {
    // save the last time you blinked the LED 
    previousdashMillis = currentdashMillis;   

    // if the LED is off turn it on and vice-versa:
    if (leddashState == LOW)
      leddashState = HIGH;
  }
    else
      leddashState = LOW;
      
      
   if(currentwordspaceMillis - previouswordspaceMillis > letterSpace) {
    // save the last time you blinked the LED 
    previouswordspaceMillis = currentwordspaceMillis;   

    // if the LED is off turn it on and vice-versa:
    if (ledwordspaceState == LOW)
      ledwordspaceState = HIGH;
    else
      ledwordspaceState = LOW;
           
      
      
     if (Serial.available() > 0) {
    int inByte = Serial.read();
switch (inByte) {
    case 'a':    
      dot();
      break;
    case 'b':    
      dot;
      dot;
      dash;
      break;
    case 'c':    
      dash;
      dot;
      dot;
      break;
    case 'd':    
     dot;
     dash;
     dot;
     dot;
      break;
    case 'e':    
   dash ;
   dot ;
   dash;
      break;
   case ' ':
   wordspace();
   break;

}
     }
  }
   }
}

I think you need to add some flags to these.
Basically, check if a dot, dash, or space is progress, if so and the time is elapsed, Then turn it off, maybe like this:

void dot(){
  digitalWrite(ledPin,leddotState);
  dot_in_progress = 1;
  dotEndmillis = millis() + dotPeriod;
  leddotState = HIGH;
  digitalWrite (ledPin, leddotState);
}

then within loop:

   if(  (dot_in_progress == 1) && (millis() >=dotEndmillis) ) { //
      dot_in_progress = 0;
      leddotState = LOW;
      digitalWrite (ledPin, leddotState);
   }

If I'm not mistaken, you need "()" when you call your subroutines. For instance, when you wish to run void dot(), your code should read: dot();

Yes I was just going to say that:-

  case 'b':   
      dot;
      dot;
      dash;

should be

  case 'b':   
      dot();
      dot();
      dash();

This is very helpful. Thanks

The other thing is this:-

void dot(){
  digitalWrite(ledPin,leddotState);
}
void dash(){
digitalWrite(ledPin,leddashState);
}

Just sets the LED pin low. I can't see any instruction that puts it high. You have a macro called flash defined but you never use it.

Mecatronicsstudent:
The problem is that all of the code I have seen on the Internet uses delay and I can't use that because I am eventually going to have to have it send and receive at the same time.

I don't have much help to offer, but I think it's funny that you want to communicate with morse asynchronously! I mean, typically, conversations with morse occur synchronously. I know it isn't a requirement or limitation of the encoding itself, but the practice of code is such that a great number of shortcuts that are basically "I'm done, go ahead, I'll wait" are already there.

Not that a machine has to care about that, assuming the send/recv data isn't a real conversation.

I have some sample code you can look at but you really don't have to care about the asyn receive like The Clever Monkey said. It makes no sense to transmit and receive at the same time.

It is a requirement of a school project. I could do this if I only had to send or receive one at a time. But here is mu updated code that still only lights it up, not blinks. As for the digital write ledpin leddotstate, all that is doing it writing whatever state the led is in. I should not automatically set it to low I don't think.
I am new to programming and do not know exactly how everything works yet.

#define dotPeriod 250
#define dashPeriod (dotPeriod3)
#define relaxTime (dotPeriod)
#define letterSpace (dotPeriod
3)
#define wordSpace (dotPeriod*7)
const int ledPin = 13; // the number of the LED pin
int dot_in_progress = 0;
int dash_in_progress = 0;

int leddotState = LOW;
int leddashState = LOW;
int ledwordspaceState = HIGH;
unsigned long dotEndmillis = millis() + dotPeriod;
unsigned long dashEndmillis = millis() + dashPeriod;
void dot(){
dotEndmillis;
dot_in_progress = 1;
leddotState = HIGH;
digitalWrite (ledPin, leddotState);
}
void dash(){
dashEndmillis;
dash_in_progress = 1;
leddashState = HIGH;
digitalWrite (ledPin, leddashState);
digitalWrite(ledPin,leddashState);
}
void wordspace(){
digitalWrite(ledPin,ledwordspaceState);
}
void setup(){
Serial.begin(9600);
// set the digital pin as output:
pinMode(ledPin, OUTPUT);
}

void loop(){

if( (dot_in_progress == 1) && (millis() >=dotEndmillis) ) { //
dot_in_progress = 0;
leddotState = LOW;
}
if( (dash_in_progress == 1) && (millis() >=dashEndmillis) ) { //
dash_in_progress = 0;
leddashState = LOW;
}

if (Serial.available() > 0) {
int inByte = Serial.read();
switch (inByte) {
case 'a':
dot();
break;
case 'b':
dot();
dot();
dash();
break;
case 'c':
dash();
dot();
dot();
break;
case 'd':
dot();
dash();
dot();
dot();
break;
case 'e':
dash();
dot();
dash();
break;
case ' ':
wordspace();
break;

}
}
}

You forgot the code tags!!

As for the digital write ledpin leddotstate, all that is doing it writing whatever state the led is in. I should not automatically set it to low I don't think.

If that is all you do then it will never blink like you are seeing.

what I have is another piece of code that should change the state according to the interval needed. All that does it write it to that state.

I made this program that reads the button responses and sends them to the pc as a usb keyboard. it runs the way you did, and it runs perfectly fine... the code may not be the most clean one, but it does its job :slight_smile:

#include "UsbKeyboard.h"

double cnt = 0;
int btnPin = 0;
double pauseCnt = 0;
boolean printCode = true;
int ledPin = 13;
int recPin = 1;
int psLengthPin = 6;
int psLengthPot = A0;
int dashLengthPot = A1;

double spd = 1;
double dashLength = 1;
boolean setSpd = true;
int buzzPin = 3;

int recCode[6] = {0,0,0,0,0,0};
int recPlace = 0;
int morseLts[246] = {/*A*/1,2,0,0,0,0 , /*B*/2,1,1,1,0,0 , /*C*/2,1,2,1,0,0 , /*D*/2,1,1,0,0,0 , /*E*/1,0,0,0,0,0 , /*F*/1,1,2,1,0,0 , /*G*/2,2,1,0,0,0 , /*H*/1,1,1,1,0,0 , /*I*/1,1,0,0,0,0 , /*J*/1,2,2,2,0,0 , /*K*/2,1,2,0,0,0 , /*L*/1,2,1,1,0,0 , /*M*/2,2,0,0,0,0 , /*N*/2,1,0,0,0,0 , /*O*/2,2,2,0,0,0 , /*P*/1,2,2,1,0,0 , /*Q*/2,2,1,2,0,0 , /*R*/1,2,1,0,0,0 , /*S*/1,1,1,0,0,0 , /*T*/2,0,0,0,0,0 , /*U*/1,1,2,0,0,0 , /*V*/1,1,1,2,0,0 , /*W*/1,2,2,0,0,0 , /*X*/2,1,1,2,0,0 , /*Y*/2,1,2,2,0,0 , /*Z*/2,2,1,1,0,0 , /*1*/1,2,2,2,2,0 , /*2*/1,1,2,2,2,0 , /*3*/1,1,1,2,2,0 , /*4*/1,1,1,1,2,0 , /*5*/1,1,1,1,1,0 , /*6*/2,1,1,1,1,0 , /*7*/2,2,1,1,1,0 , /*8*/2,2,2,1,1,0 , /*9*/2,2,2,2,1,0 , /*0*/2,2,2,2,2,0 , /*enter*/ 2,1,1,1,2,1, /*esc*/ 2,1,2,2,1,1, /*bcksp*/ 1,2,2,1,1,2 , /*tab*/ 1,1,2,2,0,0 , /*space*/1,2,1,2,0,0};

void setup() {
  
  pinMode(btnPin, INPUT);
  digitalWrite(btnPin, HIGH);
  
  pinMode(ledPin, OUTPUT);
  digitalWrite (ledPin, HIGH);
  
  pinMode(recPin, OUTPUT);
  digitalWrite(recPin, LOW);
  
  pinMode(psLengthPin, INPUT);
  digitalWrite(psLengthPin, HIGH);
  
  pinMode(buzzPin, OUTPUT);
  digitalWrite(buzzPin, LOW);
  
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  digitalWrite(8, HIGH);
  digitalWrite(9, LOW);
  digitalWrite(10, LOW);
  
  TIMSK0&=!(1<<TOIE0);
  
  cli();
  
  usbDeviceDisconnect();
  delayMs(250);
  usbDeviceConnect();
  
  sei();
}

void loop() {
  
  UsbKeyboard.update();
  
  if(digitalRead(psLengthPin) == LOW) {
    
    spd = map(analogRead(psLengthPot), 0, 1024, 500, 10000);
    dashLength = map(analogRead(dashLengthPot), 0, 1024, 1000, 40000);
    //getSpd(spd);
  }
  else if(digitalRead(btnPin) == LOW) {
    setSpd = true;
    cnt++;
    pauseCnt = 0;
    printCode = true;
    digitalWrite(buzzPin, HIGH);
  }
  else {
    digitalWrite(buzzPin, LOW);
    pauseCnt++;
    if(cnt>(100)) {
      if(cnt<(dashLength/3)) {
        //Serial.print(cnt);
        //Serial.println("\tDot");
        
        if(recPlace<6) {
          recCode[recPlace] = 1;
          recPlace++;
        }
        
      }
      else if(cnt<dashLength) {
        //Serial.print(cnt);
        //Serial.println("\tDash");
        
        if(recPlace<6) {
          recCode[recPlace] = 2;
          recPlace++;
        }
        
      }
    }
    if(pauseCnt>=spd && printCode) {
        printCode = false;
        int checkVal = 0;
        int placeBuff = 0;
        boolean maySpace = true;
        
        for(int i=0; i<=240; i+=6) {
          checkVal = 0;
          for(int j=0; j<=5; j++) {
            if(recCode[j]==morseLts[i+j]) {
              checkVal++;
            }
            else {
              checkVal = 0;
            }
          }
          if(checkVal == 6) {
            digitalWrite(recPin, HIGH);
            //Serial.print((char)(97 + (i/4)));
            UsbKeyboard.sendKeyStroke(4+(i/6));
            digitalWrite(ledPin, !digitalRead(ledPin)); // Toggle status LED
            checkVal = 0;
            //maySpace = false;
            digitalWrite(recPin, LOW);
            break;
          }
        }        
        /*
        if(maySpace) {
          for(int i=0; i<=5; i++) {
            if(recCode[i] == morseLts[154+i]) {
              //Serial.print(" ");
              checkVal++;
            }
          }
          if(checkVal == 6) {
            digitalWrite(recPin, HIGH);
            //Serial.print(" ");
            UsbKeyboard.sendKeyStroke(44);
            digitalWrite(recPin, LOW);
          }
        }
        */
        for(int i=0; i<=5; i++) {
          recCode[i] = 0;
        }
        recPlace = 0;
      }
    cnt = 0;
  }
}

void delayMs(unsigned int ms)
{
  for (int i = 0; i < ms; i++) {
    delayMicroseconds(1000);
  }
}

int getSpd(int nr) {
  
  digitalWrite(8, LOW);
  digitalWrite(9, LOW);
  digitalWrite(10, LOW);
  
  if(nr>=0 && nr<1) {
    digitalWrite(8, HIGH);
  } else if(nr>=1 && nr<2) {
    digitalWrite(9, HIGH);
  } else if(nr>=2 && nr<=3) {
    digitalWrite(10, HIGH);
  }
}

it reads the values from 2 potmeters as length from dash/dot and length of the pause between two letters. this way you can set it up completely the way you want, at every speed you want. There is this led that flashes when the char is recognized, and the buzzer buzzes as long as you press the button

 if(  (dash_in_progress == 1) && (millis() >=dashEndmillis) ) { //
      dash_in_progress = 0;
      leddashState = LOW;
 }

When is this ever written out to the actual LED?

[code]
case 'b':    
      dot();
      dot();
      dash();
      break;

you need an off space in between the characters, otherwise one ends and it goes right into next:

      dot();
      character_space();
      dot();
      letter_space(); // or relaxTime, whatever you call this
      dash();
      letter_space();
      break;
void letter_space(){
  letterSpaceEndmillis = millis(); // capture the start time time of the space
  letter_space_in_progress = 1; // set flag
  }

then in loop:

 if(  (letter_space_in_progress == 1) && (millis() >=letter_spaceEndmillis) ) { //
      letter_space_in_progress = 0;
      // end of dot & dash already turn the ledPin off, no need to here
 }

I was thinking, you probably need to change this around some, to that each dot & dash & space, & then character, has time to finish before the next one starts:

if (Serial.available() > 0 && inbyte ==0) { // nothing in progress
int inByte = Serial.read(); //let inByte change for next switch:case call
// in Byte is nonzero now
}

then keep track of a dot/dash/space as it occurs:
void dot(){
dotEndmillis;
dot_in_progress = 1;
leddotState = HIGH;
digitalWrite (ledPin, leddotState);
}

// inByte is nonzero
case 'c':
dash();
letter_space();
dot();
letter_space();
dot();
letter_space();
// end each character by clearing inByte
inByte = 0;
break;
[/code]

This is still very linear tho, the program needs a way to know whether each dot/dash/space is done; if not don't start the next one, but do continue to do other stuff, and when a dot/dash/space is done, start the next one in the sequence, so maybe a switch:case inside the switch:case?

void loop(){

    if( (dot_in_progress == 1) && (millis() >=dotEndmillis) ) { //
      dot_in_progress = 0;
      digitalWrite (ledPin, LOW);  // turn off theLED
      ledPinState = LOW;  // note the LED state
    }
 if(  (dash_in_progress == 1) && (millis() >=dashEndmillis) ) { //
      dash_in_progress = 0;
      digitalWrite (ledPin, LOW);
      ledPinState = LOW;
 }
// same for letter space,word space

// then allow switch(inByte) if the previous dot/dash/space ended
if (ledPinState == 0){
switch (inByte){ 
:
:
    case 'c':    
     char_code = char_code+1;
      switch (char_code){
      case 1:
            dash(); 
            char_code = char_code+1;
            break;
      case 2:
            letter_space();
            char_code = char_code+1;
            break;
     case 3:
            dot();
            char_code = char_code+1;
            break;
     case 4:
       letter_space();
            char_code = char_code+1;
            break;
       case 5:
            dot();
            char_code = char_code+1;
            break;
       case 6:
            letter_space();
      // end each character by clearing inByte & char_code
      char_code = 0;
      inByte = 0;
      break;
    } // end switch char_code
: 
:
}// end switch inByte

Mecatronicsstudent:
I have been trying for a little while to come up with a program for a morse code transmitter. It uses an led to flash what is typed into the serial monitor. The problem is that all of the code I have seen on the Internet uses delay and I can't use that because I am eventually going to have to have it send and receive at the same time. Can anyone one provide with something similar to delay that won't stop everything. I have been trying to work with millis, but it is getting complicated.

You may have already seen this one: Morse Code Interpreter | Hackaday