Morse Code encoder

I am pretty new to programing and I'm trying to build a morse code encoder/decoder. While building the encoder I've come across a problem storing the dots and dashes. Everything seems to be working except saving 1's or 2's to the "Position" variables. I presume that there is some reason that I can't save variables to an array this way, but i can't figure out why. I was hoping that if I'm going about this in the wrong way, someone can point me in the right direction.

const int buttonPin = 2;
int x = 0;
int Position1 = 0;
int Position2 = 0;
int Position3 = 0;
int Position4 = 0;
int Position5 = 0;
int allPositions[5] = {
  Position1, Position2, Position3, Position4, Position5
};
int buttonState = 0;     // current state of the button
int lastButtonState = 0; // previous state of the button
int startPressed = 0;    // the time button was pressed
int endPressed = 0;      // the time button was released
int timeHold = 0;        // the time button is hold
int timeReleased = 0;    // the time button is released
char words[10];
char* dotsandDashes[5] = {};


char* letters[] = {
  ".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", // A-I
  ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", // J-R
  "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--.." // S-Z
};
char* numbers[] = {"-----", ".----", "..---", "...--", "....-", ".....", "-....",
                   "--...", "---..", "----."
                  };

void setup() {
  // initialize the button pin as a input:
  pinMode(buttonPin, INPUT);
  digitalWrite(buttonPin, HIGH);
  // initialize serial communication:
  Serial.begin(9600);
}
 int a = Position1 && Position2 && Position3 && Position4 && Position5;
void loop() {
  // read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);
  if (x < 5) {
    // button state changed
    if (buttonState != lastButtonState) {
      // the button was just pressed
      if (buttonState == LOW) {
        startPressed = millis();
        timeReleased = startPressed - endPressed;


        if (timeReleased >= 600) {
          Serial.print("^");
          if (Position1 == 1)
          {
            Serial.print("a");
            //
            switch(Position1, Position2, Position3, Position4, Position5){
              case 12000: Serial.print("a");
              break;
              
            }
            
            /* allPositions[0]={0};
             allPositions[1]={0};
             allPositions[2]={0};
             allPositions[3]={0};
             allPositions[4]={0};
             x=0;*/
          }

        }

        if (timeReleased >= 1000) {
          Serial.println(" ");
          x = 0;
        }

        // the button was just released
      } else {
        endPressed = millis();
        timeHold = endPressed - startPressed;

        if (timeHold <= 250 && timeHold > 40) {
          Serial.print(".");
          allPositions[x] = 1;
          x++;
          
        }


        if (timeHold >= 251) {
          Serial.print("-");
          allPositions[x] = 2;
          x++;
        }
      }Serial.print(Position1);
      Serial.print(Position2);
      Serial.print(Position3);
      Serial.print(Position4);
      Serial.print(Position5);


    }



  }
  // save the current state as the last state,
  //for next time through the loop
  lastButtonState = buttonState ;

}

I think the problem is how these definitions...

int Position1 = 0;
int Position2 = 0;
int Position3 = 0;
int Position4 = 0;
int Position5 = 0;
int allPositions[5] = {
  Position1, Position2, Position3, Position4, Position5
};

relate to this:

 if (timeHold <= 250 && timeHold > 40) {
          Serial.print(".");
          allPositions[x] = 1;
          x++;
          
        }


        if (timeHold >= 251) {
          Serial.print("-");
          allPositions[x] = 2;
          x++;
        }

and mostly just this:

allPositions[x] = 2;

I don't have any idea what your code is doing :wink:

Why do you have 5 position variables as well as an array 'allPositions'? Your code tests Position1 for the value 1 but never assign a value to the variable (so it's always 0).

All other positionX variables are never used except for the initialisation of the array and the calculation of the variable 'a'. So you don't need them.

I have never seen the below construction (I know, it's me). The compiler seems happy with it but please enlighten me what it is supposed to do?

            switch (Position1, Position2, Position3, Position4, Position5)
            {
              case 12000: Serial.print("a");
                break;
            }

Maybe you wanted to test the variable 'a' there?

Lastly, never use single character variables (a and x) as global variables; I actually don't use single character variable names at all. Try to find where your variable 'a' is actually used (as far as I can see, nowhere, so get rid of it). Rename them to something sensible (x is a counter by the looks of it).

Thank you for taking a look. I am trying to translate dots and dashes and empty spaces into 5 positions as morse code has a maximum of 5 dots or dash combinations per letter. Some letters are just one or two like "a" which is represented as ".-"

            switch (Position1, Position2, Position3, Position4, Position5)
            {
              case 12000: Serial.print("a");
                break;
            }

if you change "Position1" and "Position2" to 1, and 2, then it will print "a". But I just read you can't do this with switch. I thought the "Positions" in the array were could be saved one at a time, and then at the end of the if statement be reset to 0.

 if (timeReleased >= 600) {

call those values and compare them to the case's. Eventually there would be a case for every letter of the alphabet, but I wanted to make sure it worked for at least one letter first. I tried to clean up the code, and I changed the case switch to an if loop. But my main problem is the same.

Every time it goes through the loop I want it to save to the next Position. Position1, then Position2 ...
When the button has been released for a significant amount of time, I want it to examine all of the positions, and map that to whatever letter it corresponds to. Here is the code, a little more cleaned up:

const int buttonPin = 2;
int x = 0;
int Position1 = 1;
int Position2 = 2;
int Position3 = 0;
int Position4 = 0;
int Position5 = 0;
int allPositions[5] = {
  Position1, Position2, Position3, Position4, Position5
};
int buttonState = 0;     // current state of the button
int lastButtonState = 0; // previous state of the button
int startPressed = 0;    // the time button was pressed
int endPressed = 0;      // the time button was released
int timeHold = 0;        // the time button is hold
int timeReleased = 0;    // the time button is released


void setup() {
  // initialize the button pin as a input:
  pinMode(buttonPin, INPUT);
  digitalWrite(buttonPin, HIGH);
  // initialize serial communication:
  Serial.begin(9600);
}
int a = Position1 && Position2 && Position3 && Position4 && Position5;
void loop() {
  // read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);
  if (x < 5) {
    // button state changed
    if (buttonState != lastButtonState) {
      // the button was just pressed
      if (buttonState == LOW) {
        startPressed = millis();
        timeReleased = startPressed - endPressed;


        if (timeReleased >= 600) {
          Serial.print("^");
          if (Position1 == 1 && Position2 == 2 && Position3 ==0 && Position4 ==0 && Position5 ==0)
          {
            Serial.print("a");
            Position1= 0;
            Position2= 0;
            Position3= 0;
            Position4= 0;
            Position5= 0;
            
          }

        }

        if (timeReleased >= 1000) {
          Serial.println(" ");
          x = 0;
        }

        // the button was just released
      } else {
        endPressed = millis();
        timeHold = endPressed - startPressed;

        if (timeHold <= 250 && timeHold > 40) {
          Serial.print(".");
          allPositions[x] = 1;
          x++;

        }


        if (timeHold >= 251) {
          Serial.print("-");
          allPositions[x] = 2;
          x++;
        }
      }
    }
  }
  // save the current state as the last state,
  //for next time through the loop
  lastButtonState = buttonState ;

}

I think you need to use either allPositions, or Position1...Position4, but not both. You declare allPositions at the top, and read it twice but never use it.

This may be because you think that it contains the variables Position1 to Position5, but it doesn't. allPositions initially contains the same values as Position1 to Position5.

For example allPositions[0] is set to the value of Position1, (1 in this case) but thereafter is not connected to the variable Position1.

I'd suggest you either use allPositions or Position1 to Position2 but not both.

To use allPositions you could do:

int allPositions[5] = {1,2,0,0,0} ;


// Read the first position like this

thing = allPositions[0] ;

// Read the last position like this

thing = allPositions[4] ;

// Change a position:

allPositions[2] = 88 ;

I am trying to figure out the best way to save five variables (Position1 through Position 5), and at the end loop compare the positions to a key, and reset them. What I have right now is a bunch of nested if statements. I'm having a lot of difficulty figuring out how to iterate through the variables so that the first time you push the button it is saving to Position1 and the second time, position 2, so on.

I thought here:

int Position1 = 0;
int Position2 = 0;
int Position3 = 0;
int Position4 = 0;
int Position5 = 0;
int allPositions[5] = {
  Position1, Position2, Position3, Position4, Position5
};

I thought I could put all of the variables inside the allPositions array, and from there I could send them values from inside an if statement, and later compare them.

if (timeHold <= 250 && timeHold > 40) {
          Serial.print(".");
          allPositions[x] = 1;
          x++;

        }


        if (timeHold >= 251) {
          Serial.print("-");
          allPositions[x] = 2;
          x++;
        }

The code runs if x<5, and that part works. I thought that

allPositions[x] = 2;

would send the first button push to position one, and the second button push to position 2... Clearly I'm misunderstanding something, and it doesn't work that way. But I'm hoping that one of you lovely people will understand what I'm trying to do and point me in the right direction. The rest of the code seems to work and this is the last big problem in the way. Thanks!

Here's the full code

const int buttonPin = 2;
int x = 0;
int Position1 = 0;
int Position2 = 0;
int Position3 = 0;
int Position4 = 0;
int Position5 = 0;
int allPositions[5] = {
  Position1, Position2, Position3, Position4, Position5
};
int buttonState = 0;     // current state of the button
int lastButtonState = 0; // previous state of the button
int startPressed = 0;    // the time button was pressed
int endPressed = 0;      // the time button was released
int timeHold = 0;        // the time button is hold
int timeReleased = 0;    // the time button is released


void setup() {
  // initialize the button pin as a input:
  pinMode(buttonPin, INPUT);
  digitalWrite(buttonPin, HIGH);
  // initialize serial communication:
  Serial.begin(9600);
}
int a = Position1 && Position2 && Position3 && Position4 && Position5;
void loop() {
  // read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);
  if (x < 5) {
    // button state changed
    if (buttonState != lastButtonState) {
      // the button was just pressed
      if (buttonState == LOW) {
        startPressed = millis();
        timeReleased = startPressed - endPressed;


        if (timeReleased >= 600) {
          Serial.print("^");
          if (Position1 == 1 && Position2 == 2 && Position3 ==0 && Position4 ==0 && Position5 ==0)
          {
            Serial.print("a");
            Position1= 0;
            Position2= 0;
            Position3= 0;
            Position4= 0;
            Position5= 0;
            
          }

        }

        if (timeReleased >= 1000) {
          Serial.println(" ");
          x = 0;
        }

        // the button was just released
      } else {
        endPressed = millis();
        timeHold = endPressed - startPressed;

        if (timeHold <= 250 && timeHold > 40) {
          Serial.print(".");
          allPositions[x] = 1;
          x++;

        }


        if (timeHold >= 251) {
          Serial.print("-");
          allPositions[x] = 2;
          x++;
        }
      }
    }
  }
  // save the current state as the last state,
  //for next time through the loop
  lastButtonState = buttonState ;

}

Why did you start a new thread? You were getting answers here already. Do you think you will get nicer answers in a new thread?

Thank you, I'm going to try this. I made a more general post about what i'm trying to do http://forum.arduino.cc/index.php?topic=387795.0 Thank you for helping!

Sorry, I thought it was sort of a different question. I like, and appreciate the answers I was getting, but I took a wrong turn in how I asked the question. I think maybe the way I've approached the problem is wrong, and I wanted broader help with that. I did think that "morse code encoder" was a little flashier. These are my first posts, I didn't mean to spam the forum. Sorry! and Thanks!

Please do not cross-post. This wastes time and resources as people attempt to answer your question on multiple threads.

Threads merged.

  • Moderator

Riddar:
I thought I could put all of the variables inside the allPositions array, and from there I could send them values from inside an if statement, and later compare them.

That is the problem, you can't do that. You are not putting variables inside an array, you are putting values inside the array.

It is possible to do (using pointers) but not worth it in your case I think.

Just use allPositions[n] where n goes from 0 to 4.

Or...
Just reduce Dots + Dashs to a single integer value and store that value.
Magic Morse algorithm is bi-directional, so you can encode to a value or decode a value to the dots + dashes.

Decoding is shown here:
Encoding is shown here:

The algorithm will generate a number between 1 and 255 for every Morse Code combination decoded. For every DAH identified in the stream, a binary weight is applied based upon the position of the DAH... that is, first, second, third, fourth, or fifth element. DITs are not weighted but are counted with DAHs to achieve a total element count of DITs + DAHs in the character. The DAH-weights are bits 3-7 in a calculated index and the elements number make up bits 0-2.

Examples:

E = "DIT" Therefore, there are no DAHs and only one element. The pointer is %00000001 Index = 1
F = "DIT DIT DAH DIT" and the DAH is in the third position (bit 5 set) Pointer = %00100100 Index = 36

Ray

Thanks, Ray...really interesting algorithm!

Jack, W8TEE

struct morse_encoding_t
{
    // ENCODE MORSE ELEMENT COUNT AND ELEMENT TYPES
    // WITHIN SINGLE UNSIGNED BYTE
    // 3 BITS FOR ELEMENT COUNT
    // 5 BITS FOR ARRAY OF MORSE ELEMENTS ENCODED AS SINGLE BITS IN
    //   MSB -> LSB ORDER
    //      0 = DIT, 1 = DAH

    uint8_t count: 3;       // 3 bits for element count
    uint8_t elements: 5;    // 5 bit arrays of encoded elements
};

morse_encoding_t char2encoding(char ch)
{
    const morse_encoding_t em_alpha[] =
    {
          { 2, 0b01    }    // A  .-
        , { 4, 0b1000  }    // B  -...
        , { 4, 0b1010  }    // C  -.-.
        , { 3, 0b100   }    // D  -..
        , { 1, 0b0     }    // E  .
        , { 4, 0b0010  }    // F  ..-.
        , { 3, 0b110   }    // G  --.
        , { 4, 0b0000  }    // H  ....
        , { 2, 0b00    }    // I  ..
        , { 4, 0b0111  }    // J  .---
        , { 3, 0b101   }    // K  -.-
        , { 4, 0b0100  }    // L  .-..
        , { 2, 0b11    }    // M  --
        , { 2, 0b10    }    // N  -.
        , { 3, 0b111   }    // O  ---
        , { 4, 0b0110  }    // P  .--.
        , { 4, 0b1101  }    // Q  --.-
        , { 3, 0b010   }    // R  .-.
        , { 3, 0b000   }    // S  ...
        , { 1, 0b1     }    // T  -
        , { 3, 0b001   }    // U  ..-
        , { 4, 0b0001  }    // V  ...-
        , { 3, 0b011   }    // W  .--
        , { 4, 0b1001  }    // X  -..-
        , { 4, 0b1011  }    // Y  -.--
        , { 4, 0b1100  }    // Z  --..
    };
    
    const morse_encoding_t em_numeric[] =
    {
        // --- NUMERIC
          { 5, 0b11111 }    // 0  -----
        , { 5, 0b01111 }    // 1  .----
        , { 5, 0b00111 }    // 2  ..---
        , { 5, 0b00011 }    // 3  ...--
        , { 5, 0b00001 }    // 4  ....-
        , { 5, 0b00000 }    // 5  .....
        , { 5, 0b10000 }    // 6  -....
        , { 5, 0b11000 }    // 7  --...
        , { 5, 0b11100 }    // 8  ---..
        , { 5, 0b11110 }    // 9  ----.
    };

    switch ( ch )
    {
        case 'a' ... 'z': return em_alpha[ch - 'a'];
        case 'A' ... 'Z': return em_alpha[ch - 'A'];
        case '0' ... '9': return em_numeric[ch - '0'];
        case '/':         return { 5, 0b10010 };
    }

    return { 0, 0 };
}

Thanks for the replies! they look really interesting, and it'll take me a minute to wrap my head around it. If anyone is curious, I got the code working the way I wanted. It's really messy right now, but it translates the dots and dashes into numbers, and then the appropriate characters and prints them. If too many buttons were pressed it prints "??" and if an unknown combination of dots and dashes were pushed it prints "?"

I have a very long "if else" statement that goes through the whole alphabet that i'm trying to turn into a function, just to clean it up. I was also wondering if what I've done makes more since to write in switch/case. I appreciate those of you who have stayed with me. If anyone is curious this is what it looks like now. I deleted the most of the alphabet because it exceeded the character limit for posting

const int buttonPin = 2;
int x = 0;

int allPositions[5] = {0, 0, 0, 0, 0};
int placeOne=0;
int placeTwo=0;
int placeThree=0;
int placeFour=0;
int placeFive=0;
int buttonState = 0;     // current state of the button
int lastButtonState = 0; // previous state of the button
int startPressed = 0;    // the time button was pressed
int endPressed = 0;      // the time button was released
int timeHold = 0;        // the time button is hold
int timeReleased = 0;    // the time button is released


void setup() {
  // initialize the button pin as a input:
  pinMode(buttonPin, INPUT);
  digitalWrite(buttonPin, HIGH);
  // initialize serial communication:
  Serial.begin(9600);
}
void loop() {
  // read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);
  if (x < 7) {
              if(x==6){
      Serial.print("??");
      x=0;         
      allPositions[0] = 0;
      allPositions[1] = 0;
      allPositions[2] = 0;
      allPositions[3] = 0;
      allPositions[4] = 0;
    }
      
    // button state changed
    if (buttonState != lastButtonState) {
      // the button was just pressed
      if (buttonState == LOW) {
        startPressed = millis();
        timeReleased = startPressed - endPressed;


        if (timeReleased >= 600) {
          placeOne = allPositions[0];
          placeTwo = allPositions[1];
          placeThree = allPositions[2];
          placeFour = allPositions[3];
          placeFive = allPositions[4];

          if (placeOne==1 && placeTwo==2 && placeThree==0 && placeFour == 0 && placeFive == 0)
          {
            Serial.print("a");
            
          }

          }
                    else if (placeOne==2 && placeTwo==2 && placeThree==1 && placeFour == 1 && placeFive == 1)
          {
            Serial.print("7");
          }
                    else if (placeOne==2 && placeTwo==2 && placeThree==2 && placeFour == 1 && placeFive == 1)
          {
            Serial.print("8");
          }
                    else if (placeOne==2 && placeTwo==2 && placeThree==2 && placeFour == 2 && placeFive == 1)
          {
            Serial.print("9");
          }
                    else if (placeOne==2 && placeTwo==2 && placeThree==2 && placeFour == 2 && placeFive == 2)
          {
            Serial.print("0");
           
          }
          else
          {Serial.print("?");
          }
          allPositions[0] = 0;
          allPositions[1] = 0;
          allPositions[2] = 0;
          allPositions[3] = 0;
          allPositions[4] = 0;
          
          x=0;
          
        }

        if (timeReleased >= 1000) {
          Serial.print(" ");
          x = 0;
        }

        // the button was just released
      } else {
        endPressed = millis();
        timeHold = endPressed - startPressed;

        if (timeHold <= 250 && timeHold > 40) {
          Serial.print(""); //dot
          allPositions[x] = 1;
          x++;

        }


        if (timeHold >= 251) {
          Serial.print(""); //dash
          allPositions[x] = 2;
          x++;
        }
      }
    }
 lastButtonState = buttonState ;
  }
  // save the current state as the last state,
  //for next time through the loop

I was wondering if I could make a function that held all of the individual values (placeOne, placeTwo...) as well as the character it is going to print. I'm not sure how to declare the last variable (f).

void translation(int a, int b, int c, int d,int e, String f)
{

   if (placeOne==a && placeTwo==b && placeThree==c && placeFour == d && placeFive == e)
          {
            Serial.print(f);
          }}

lloyddean:

struct morse_encoding_t

Very clean implementation.

Ray

@lloyddean: Since Morse code doesn't use lowercase letters, couldn't you simplify it to:

   switch ( toupper(ch ) )
    {
        case 'A' ... 'Z': return em_alpha[ch - 'A'];
        case '0' ... '9': return em_numeric[ch - '0'];
        case '/':         return { 5, 0b10010 };
    }

That way, even if they are typing with upper and lower case, the code translation is correct.

Certainly could but that's a progression to awareness of functions outside of the documented Arduino API for the students it was written for.

lloyddean:
Certainly could but that's a progression to awareness of functions outside of the documented Arduino API for the students it was written for.

But isn't showing programmers new macros (or functions/methods) part of the reason more experienced programmers spend time here? I think that's a good thing for us to be doing here.

How much to overload a student with at any one time is a balancing act!

The students here are an unknown unlike those in front of me.

lloyddean:
How much to overload a student with at any one time is a balancing act!

The students here are an unknown unlike those in front of me.

I don't know how much of an overload it is to remove one case statement and add a macro to the source code. Still, with 40 years of teaching experience ending at a Big Ten university, I would expect any student of mine to embrace such a simple change. Indeed, I think most of the beginning programmers here are anxious to learn new things...I don't think they would be here otherwise.

I'm getting off point, so I'll stop here.