Jump to content

Arduinos, signal transmission and Morse codes


Delay

Recommended Posts

Our CS is strange.
Remember how I complained that 11th grade no longer includes object-oriented programming languages?

Well, now we're working with Arduinos (which run on basically C), but we're not taught about C at all. Basically "Here's a microcontroller. Now do things with it!".

We're given examples of what you can do and the exact code that does what is required, but that's not how you learn. That's how you copy. Thus I reject those.
So instead I've forced myself to learn basic C syntax as quickly as possible so that I can do these things myself. And I suck at learning fast - it took a relatively long time just to get somewhat comfortable with Python, and now I have to learn C?
At least Python was voluntary!

 

BY THE WAY: I don't even know if anything I wrote here is even possible with the language/hardware in any way. I'm not responsible for any headaches/violent outbursts that reading this text may cause.
(The task is to - via LED and photosensor - transmit a text (=string?) from one controller to another, where it is interpreted.
For sending the text I thought of converting all characters in the string to Morse code (store that in another string) and then turn on the LED based on the characters in this new string.
That way I wouldn't have to repeatedly type instructions to turn the lamp on/off, instead I could just assign one letter to represent the action of a long/short/"space" signal (simplifying the transmission from 26 unique patterns to just 3 options), go through that sequence element-by-element and turn on/off the LED accordingly.
That would be the sender... And please don't be harsh about this idea not working because of reasons I don't know, or that there are so many more efficient/safer ways to transmit information tham Morse code. A)I'm dumb, B)I need to get acquainted with the language first in order to see how much nonsense I actually wrote here.
The receiver? Well, I haven't thought about that as much yet. I don't even have any clue as to how that could even work.)

Edited by Delay
Link to comment
Share on other sites

1 hour ago, Delay said:

we're going to build a text wall and make the readers pay for it

I think you can program Arduino with Python, but I'm not really sure. C isn't hard to work with.

Anyways, you're basically doing a Visible Light Communication project, it's pretty cool stuff.

I can give you a rough sketch on how I'd do the receiving part of the system, it would go like this:

/** This code is a rough skeleton and has not been tested. EXPECT THE NEED TO CHANGE IT. **/


// Setting up our variables. RESISTOR is the analog pin you've connected the photoresistor to,
// DOT_TIME and DASH_TIME are how long you've set the dot and dash signals to in your transmitter,
// TOLERANCE is there so that you can adjust it for the photoresistor's delay (it takes a while for
// its resistance to actually change, about 10ms).
#define RESISTOR A0, DOT_TIME 200, DASH_TIME 600, TOLERANCE 20

// These are our time variables for keeping track of the signal.
unsigned long TIME_0, TIME_1, DELTA_TIME;

// Self explanatory.
int VOLTS, THRESHOLD;

void setup(){
	
	
	pinMode(RESISTOR, INPUT); // Setting up the photoresitor's analog pin as input.
	TIME_0, TIME_1 = millis(); // Setting up the time variables to current time.

	THRESHOLD, VOLTS = analogRead(RESISTOR); // Let's calibrate the sensor to ambient lighting.

}

void loop(){
	
	VOLTS = analogRead(RESISTOR); // Update the variable with current readings.
	
	TIME_0, TIME_1 = millis(); // Set the time variables to current time.
	
	while(VOLTS < THRESHOLD){ // Assuming the resistance increases when it's being lit. If it drops, just change it to VOLTS > THRESHOLD.

		TIME_1 = millis(); // While the pin voltage is below the threshold, set TIME_1 to current time so that we can know how long it was lit later.
		VOLTS = analogRead(RESISTOR); // Update the variable with current readings.
      
        // You could do an if statement so that if the signal's been going on for too long, discard it by setting TIME_0 and TIME_1
      	// to millis() and breaking out of the while() loop.

	}

	DELTA_TIME = TIME_1 - TIME_0; // Calculate how long the LED stayed on for.
    THRESHOLD = VOLTS; //Assuming the LED's off by now, updating the threshold for current lighting.

	if( (DELTA_TIME < DOT_TIME + TOLERANCE) && (DELTA_TIME > DOT_TIME - TOLERANCE) ){ // Checking if it was a dot. If it isn't, it must be a dash, or nothing.

		//do your dot things

	} else if( (DELTA_TIME < DASH_TIME + TOLERANCE) && (DELTA_TIME > DASH_TIME - TOLERANCE) ){

		//do your dash things

	}
  
}

All time units are in milliseconds, by the way.

Edited by Guest
Link to comment
Share on other sites

45 minutes ago, Delay said:

The receiver? Well, I haven't thought about that as much yet. I don't even have any clue as to how that could even work.)

Does it have to work with variable speeds, or can it receive at a set speed?  The second is much simpler.

 

Spoiler

Start with an empty string.

Wait for the light to turn on.  Mark the time[0].

Wait for the light to turn off.  Mark the time[1].

Subtract time[0] from time[1].  If time_difference < certain_length, add a '.' to the string.  If it's longer, add a '-'.

Wait for the light to turn on again.  Periodically check the time difference, and if it exceeds a different certain length, you have a complete character.  Check it against a list, and output the decoded character.  Empty the decode string.  Continue checking the time, and if it exceeds another length, output a space.  If you have output a number of repeated spaces, break out of the loop (otherwise it will run forever).

Continue to loop through this until you hit the break condition.

That's more or less how I've done it, but it's been a long time since I wrote the code (modified from someone's morse reader I found online somewhere).

Link to comment
Share on other sites

When subtracting the times you can also wish to use a gray zone between the dot and dash durations.

if  tmin < t < tmax then this is...

tmin, s tmax, s Description
0 0.25 error
0.25 0.5 dot
0.5 1.0 error
1.0 1.5 dash
1.5 inf error (add timeout at, say, 2.0 s, to avoid an endless waiting for the end of dash)


 

Edited by kerbiloid
Link to comment
Share on other sites

Properly, a dash should be three times as long as a dot, the spacing between dots and dashes within a character should be the same as a dot, the space between characters should be as long as a dash, and spacing between words should be seven times the dot.

Dot: 1 unit
Dash: 3 units
Intracharacter: 1 unit
Intercharacter: 3 units
Word break: 7 units

For 5 words per minute, a dot is 0.24 seconds and a dash is 0.72 seconds.

Link to comment
Share on other sites

I am aware of the fact that I'm basically using this thread as a dump for C questions now, but I can guarantee you that with a non-0% chance, this is the last question. Tomorrow I have CS, so it's my last day of asking and this question is kinda important.

I looked around a bit on (guess what) the internet and found out that what I thought of as a simple task may not be so simple after all: Add a sequence of letters to a string based on the character of another. If other people are to be trusted, then a simple return-statement doesn't do what I intend it to do.

Here's the idea I had:

//please ignore my strange indentation of brackets and code. Don't know how C code is usually indented.

char MorseCode(index){     //this is effectively a big table with all Morse codes and their letters, Imagine this going from A to Z
  
  if (String[index] == (A | a)){     //is the letter in question A or a?
    return "KL ";
  	}
  if (String[index] == (B | b)){     //is the letter B or b?
    return "LKKK ";
  	}
  if (String[index] == (C | c)){     //is the letter C or c?
    return "LKLK ";
  	}
  }

This would be called by either a for or while loop (probably while, though) during every iteration.
Probably with a line like "MString = MString + MorseCode(number_of_index)", where number_of_index in incremented by 1 at the end of each loop and as long as this new index is still valid.

The expected result is that I end up with the same list as in the beginning, but with one new encoded letter added to the end of the list.

Link to comment
Share on other sites

52 minutes ago, Delay said:

The expected result is that I end up with the same list as in the beginning, but with one new encoded letter added to the end of the list. 

Don't forget you need to add spaces between the characters, or "Hello world" comes out as ".....-...-..--- .-----.-..-..-..", which is not readable.
Nevermind, I notice you do have a space.

 

52 minutes ago, Delay said:

char MorseCode(index)
...

Your program expects the function to return a single character.  What you are returning, however, is a string.  If I am recalling correctly, returning a string from a function is not a trivial exercise in C.

You can pass the encoded string in by reference, and append to it in the function.  Or declare it a global variable and append it in the function.

 

You could also use a switch, instead of the ifs.

int Encode(char in_char, char *buffer)
{
  in_char = toupper(inchar);
  int flag = 0;

  switch (in_char)
  {
    case 'A':
      <append '.- ' to buffer>
      flag = 1;
      break;
        
    case 'B':
      <append '-... ' to buffer>
      flag = 1;
      break;
        
    case 'C':
      <append '-.-. ' to buffer>
      flag = 1;
      break;
...        
    case 'Z':
      <append '--.. ' to buffer>
      flag = 1;
  }

  return flag;
}

But I don't remember the C syntax for doing the append, and my books are in a box somewhere.

The flag allows you to check outside the function if it was able to get a valid match.  If it did, it will return 1 (true), else 0 (false).

Edited by razark
Cause some idiot forgot to add spaces after characters...
Link to comment
Share on other sites

1 hour ago, razark said:

But I don't remember the C syntax for doing the append, and my books are in a box somewhere.

 

strcat() or strncat(), live in string.h. strcat_s() or strncat_s() as the safer new-age variant.

If you don't want to walk on foot and write your own string concatenation, i mean :-)

 

Sauce: https://en.cppreference.com/w/c/string/byte/strcat

 

Edited by Green Baron
Link to comment
Share on other sites

3 hours ago, Green Baron said:

If you don't want to walk on foot and write your own string concatenation, i mean

When I took the class, we didn't use strings.  They were just treated as an array of characters.  That's stuck with me, and I keep forgetting string handling in C.

Link to comment
Share on other sites

8 hours ago, razark said:

You could also use a switch, instead of the ifs.

I'd say ifs and else ifs are better here. After all it's not supposed to be a highly complex and convoluted script (relative to the fact that it's the first major exposure to a "real" programming language).
It should be very easy to read and understand, and ifs seem to be the best way to accomplish that.

Edited by Delay
Link to comment
Share on other sites

You don't really need to encode it first though, you can just make a for loop iterating over the char array (input text) and transmitting each letter appropriately at a time, way simpler

Edited by Guest
Link to comment
Share on other sites

26 minutes ago, Aperture Science said:

You don't really need to encode it first though, you can just make a for loop iterating over the char array (input text) and transmitting each letter appropriately at a time, way simpler

Thinking back, this is how I did it.  Much simpler indeed.

Link to comment
Share on other sites

2 minutes ago, razark said:

Thinking back, this is how I did it.  Much simpler indeed.

I don't think so. You'd have to write the same thing over and over again for every letter. Mine transmits based on just 3 possible signals (long, short, space) being reused all the time.
Additionally it's so much easier to edit. If you want to add the digits 0-9 all you have to do is add 10 more combinations.

Link to comment
Share on other sites

3 minutes ago, Delay said:

I don't think so. You'd have to write the same thing over and over again for every letter. Mine transmits based on just 3 possible signals (long, short, space) being reused all the time.
Additionally it's so much easier to edit. If you want to add the digits 0-9 all you have to do is add 10 more combinations.

Instead of translating the whole string, appending it, and then sending it, you're translating one character and sending it.  You're still only dealing with dit, dah, or space.  You can avoid messy string operations.

 

 

Link to comment
Share on other sites

1 minute ago, razark said:

You can avoid messy string operations.

Sure, but can add a new character with just 2 lines; the if-condition and what the character equals.
Rather than writing the individual instructions for turning the lamp on and off every single time.

Unless of course there is another way to implement "reusable" that gets called when needed, but is operating independently from the actual letter and I didn't think of it. That way it would still just be three functions.

Link to comment
Share on other sites

switch (in_char)
  {
    case 'A':
      send_dit();
      send_dah();
      send_space();
      break;
        
    case 'B':
      send_dah();
      send_dit();
      send_dit();
      send_dit();
      send_space();
      break;
        
<and so on>
}






void send_dit()
{
  <lamp on>
  <delay>
  <lamp off>
  <delay>
}

void send_dah()
{
  <lamp on>
  <delay * 3>
  <lamp off>
  <delay>
}

void send_space()
{
  <delay * 2>
}

Functional
Readable
Elegant

Pick two out of three.

 

(I should really go look up the code I used to do this.  I'm going by rough memory and making it up as I go.)

Edited by razark
Link to comment
Share on other sites

36 minutes ago, Delay said:

Sure, but can add a new character with just 2 lines; the if-condition and what the character equals.
Rather than writing the individual instructions for turning the lamp on and off every single time.

Unless of course there is another way to implement "reusable" that gets called when needed, but is operating independently from the actual letter and I didn't think of it. That way it would still just be three functions.

You're going to have to iterate over the encoded string anyways if you decide to make a Morse string. Might as well just avoid it and only iterate over the input string. You'll achieve the same effect with less effort.

Link to comment
Share on other sites

1 minute ago, Aperture Science said:

You'll achieve the same effect with less effort.

That may be true. I suspect I was too stubborn to see this option.

@razark Are switch statements also called or must they be part of the loop I use them in?
(Human) Accessibility is the main reason I'm asking: Being able to put it out front - or so I think - would make things easier to understand as you didn't have to search for it in the loop.

Link to comment
Share on other sites

3 minutes ago, Delay said:

Are switch statements also called or must they be part of the loop I use them in?

The switch statement would be used in exactly the same place as the repeated if statements you had.  They're pretty much identical in flow control in most uses.  I just tend to find switch a bit easier for my reading.

The string of if statements does have a downside if you do not have a return inside the if.  If your character is 'A', you'd end up doing 26 comparisons with the 'if' method (without returns).  With the switch, it'll hit the break and stop running the comparisons.  Because you're not always going to have a return inside the if, I find it better to use the switch as general practice.

Link to comment
Share on other sites

6 hours ago, razark said:

When I took the class, we didn't use strings.  They were just treated as an array of characters.  That's stuck with me, and I keep forgetting string handling in C.

C strings are literally (null-terminated) character arrays, so of course they'd be treated that way. There are a few special string-handling functions in libc (strcat(), strlen(), etc.) but they're all just operations over character arrays.

Link to comment
Share on other sites

6 minutes ago, IncongruousGoat said:

C strings are literally (null-terminated) character arrays, so of course they'd be treated that way. There are a few special string-handling functions in libc (strcat(), strlen(), etc.) but they're all just operations over character arrays.

Yeah.  The professor went so far as to say something like "Strings do not exist in C.  Do not even bother asking about string functions."

Link to comment
Share on other sites

1 minute ago, razark said:

Yeah.  The professor went so far as to say something like "Strings do not exist in C.  Do not even bother asking about string functions."

Shame on your professor then. Strings do exist in C, and there are plenty of functions to handle them. It just so happens that they're also null-terminated character arrays.

Link to comment
Share on other sites

1 minute ago, IncongruousGoat said:

Shame on your professor then. Strings do exist in C, and there are plenty of functions to handle them. It just so happens that they're also null-terminated character arrays.

Indeed.  It's still stuck in my head to avoid strings after... 23 years.

Link to comment
Share on other sites

Your professor probably meant that there is no specific datatype "string" in C, which is correct. As @IncongruousGoat said, a string is a char array, hopefully with \0 at the end (an ASCII/UTF 0 (nul) character, not NULL which in many C implementations is a (void *)0 or just 0(*) !). There are a lot of useful functions in string.h to manipulate char arrays, aka strings. But these will produce epic failures if the programmer forgot for example to add the terminating \0, or hasn't taken care of grabbing and releasing memory in the correct order :-).

The strcat() function can be implemented as two consecutive loops to get to the end of a and then copy b at the end of a, ideally using pointer arithmetic, or if the lengths are known and stored somewhere, as a call to memcpy(), which is pretty fast. The latter being the "on foot" way i meant. 

(*) g++ actually expands to 0, gcc to (void *)0

Edited by Green Baron
Link to comment
Share on other sites

This thread is quite old. Please consider starting a new thread rather than reviving this one.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...