How to return to SwitchCase from deep within code

Hi,

I am fairly new to the Arduino and i am building an Escape Room Puzzle, because it is fun and it helps me explore the Arduinos possibilities.

The puzzle that I am working on has several phases, which I mapped out as Cases in a Swith board.

One of these cases is a Morse Code.

switch (puzzleState) { 

   ...  
    
  case Morsing:
     Serial.println("Enter: Morsing");
     {
      morse();
      onlight();
      break;
     }
    
   ...

Morse is basicly the different letters I want to get send

void morse(){
  
    morseCodeSequence("-...");
    morseCodeSequence("....");
}

These letters are first being sequenced

void morseCodeSequence(char* sequence){
   int i = 0;   
   // Loop for each element in the array
   while (sequence[i] != NULL){
      dotOrDash(sequence[i]);     // Send out the dot or dash
      i++;                        // Increment to the next element in the array
   }
   delay(dotDelay * 3);  // gap between letters  
}

and within the sequence there is a code to actually send the signal to a LED.

void dotOrDash(char dotOrDash){
  digitalWrite(MorsePin, HIGH);
  if (dotOrDash == '.')
  {
    delay(dotDelay);
    Serial.print(".");          
  }
  else // must be a -
  {
    delay(dotDelay * 3); 
    Serial.print("-");          
  }   
  delay(dotDelay*2  ); // gap between flashes
}

All of this works very fine.

The problem is: the morse code shall stop immidiatly, once the Puzzle Chest is being opened and light shines on a lightresistor.

Mesuring the Light etc. is done by that little script:

bool  WaitForLight(){
   // when the sensor is covered, this value rises. When it receives more light, it falls. 
  currentReading = analogRead(ldrPin);
  delay(1000); // Slow down the output
  //if the chest si opened, some light will flow onto the sensor,
  //indicating that the puzzle is ready to be initialized
  return (currentReading < lightThreshhold);
}

And the question to you is, how do I implement/change the WaitForLight() procedure ideally into dotOrDash() so that it jumps back into the switchcase.

And the question to you is, how do I implement/change the WaitForLight() procedure ideally into dotOrDash() so that it jumps back into the switchcase.

you want to traverse back all the way to the morse() call, breaking out of the while and for loops.

Whilst this is probably not the approach I would take (I would handle this a state machines) - you could have morseCodeSequence() and dotOrDash() return a boolean which would tell the caller if it needs to abort. You would test each call and issue a return to the caller in case the detection is positive (and embed WaitForLight() into the most inner function dotOrDash())

To my way of thinking the answer is in your Title. Nothing should be "deep within code" if you want a responsive program.

Have a look at the code in Several Things at a Time and in Planning and Implementing a Program. Note how each function runs very briefly and returns to loop() so the next one can be called. And there may be dozens of calls to a function before it is actually time for it to do anything.

By going back to loop() there is always an opportunity for some other part of the program to take control.

...R

J-M-L:
You would test each call and issue a return to the caller in case the detection is positive (and embed WaitForLight() into the most inner function dotOrDash())

I tried this, but then I was still stuck in morse(). Maybe I do not use the return function properly?
How do i return back to the switchCase function?
Thought about using goto, but this seems to be a damnable sin :slight_smile:

May be something like this (typed here - so possible errors). The idea is to return true if you need to abort and those true cascade up the function call chain.

bool morse(){
    if (morseCodeSequence("-...")) return true;
    if (morseCodeSequence("....")) return true;
    return false;
}
bool morseCodeSequence(char* sequence){
   bool abortProcess = false;
   int i = 0;   
   // Loop for each element in the array
   while (sequence[i] != NULL){
      //while (digitalRead (KisteAufPin) == HIGH);
      if (dotOrDash(sequence[i])) {// Send out the dot or dash
         abortProcess = true; 
         break;
      }     
      i++;                        // Increment to the next element in the array
   }
   if (abortProcess) return true;
   delay(dotDelay * 3);  // gap between letters
   return false;
}
bool dotOrDash(char dotOrDash){

  digitalWrite(MorsePin, HIGH);
  if (dotOrDash == '.')
  {
    delay(dotDelay);
    Serial.print(".");          
  }
  else // must be a -
  {
    delay(dotDelay * 3); 
    Serial.print("-");          
  }
  delay(dotDelay*2  ); // gap between flashes
  return WaitForLight();  // or return (!WaitForLight()); not sure exactly what you want to do, return true if you want to abort  
}

J-M-L:
The idea is to return true if you need to abort and those true cascade up the function call chain.

That concept could work but it seems to me to be messy. How would any stage in the chain know that it needs to initiate an abort?

I much prefer to approach the problem from the other end - have something in loop() that sets a variable that lets the different functions know that they have permission to work (or not).

…R

I'm not sure they are even implemented for the Arduino, but would setjmp() and longjmp() work?

Robin2:
That concept could work but it seems to me to be messy. How would any stage in the chain know that it needs to initiate an abort?

We agree - as stated in my first answer

Whilst this is probably not the approach I would take (I would handle this a state machines)

A stage in the chain does know when to abort if a stage below asked for aborting or if it detects an abortion condition. That’s a “messy” way to unwind the LIFO stack of functions calls without resorting to goto or other constructs.

Exiting from Nested if/while/for is an example that have been used to document the value of the goto statement but In their original edition of The C Programming Language, Kernighan & Ritchie wrote:

C provides the infinitely-abusable goto statement, and labels to branch to. Formally, the goto is never necessary, and in practice it is almost always easy to write code without it.

You could implement this with co-operative multitasking (fibers) and re-entrant functions. C/C++ doesn't support this in the language level so you would need to implement it "manually". Every re-entrant function needs to be implemented as a class that it can store its state and a callstack which records this state. The re-entrancy can be implemented in generic way using some macro and switch-case abuse. Then in loop() you keep ticking the callstack with a timestep.

econjack:
I'm not sure they are even implemented for the Arduino, but would setjmp() and longjmp() work?

Of course they are. Doing that inside C code, from one function to another, it's like using a cannon to shoot yourself in the foot.

The "deep within code" indicates that we are a long way down the path of "blocking" code and it's going to take a lot of effort to dig ourselves out of that deep hole.

Go and read Robin2's excellent tutorial. If you don't understand it then here's my method of explaining it.

The loop() function should loop very fast. Thousands of times per second. Each time it has to do something, like blink a light, don't sit there and wait for the thousand milliseconds for the blink to finish. Just turn on the light and record the time you turned it on. Then loop() can check thousands of times per second if it's time to turn the light off.

The Morse code is going to be a state machine, inside another state machine.

This should get you out of the morse code when light is sensed:

void morseCodeSequence(char* sequence)
{
  int i = 0;
  // Loop for each element in the array
  while (sequence[i] != NULL)
  {
    // Cancel sequence if light is seen
    if (WaitForLight())
      return;
    dotOrDash(sequence[i]);     // Send out the dot or dash
    i++;                        // Increment to the next element in the array
  }
  delay(dotDelay * 3);  // gap between letters
}

bool  WaitForLight()
{
  // when the sensor is covered, this value rises. When it receives more light, it falls.
  currentReading = analogRead(ldrPin);

  //if the chest si opened, some light will flow onto the sensor,
  //indicating that the puzzle is ready to be initialized
  return (currentReading < lightThreshhold);
}

johnwasser:
This should get you out of the morse code when light is sensed:

IMHO it would be much simpler to check for the light BEFORE the function is called that produces the next item of the morse code and then NOT call that function if the light has been sensed.

Morse code is ridiculously slow compared to an Arduino. Lot's of other things can be done while the I/O pin representing a morse DOT is HIGH. And an Arduino could type a novel in the time while a DASH is HIGH :slight_smile:

...R

Yeah - that approach of returning true at some points is still very granular, and will be prone to delays you embed into each function and where you check for the "abort" event. you could test multiple times in a given function of course, but this makes it even worth design....

so workable as a "hack" if OP doesn't feel like redesigning the code from the ground up as state machines but clearly far from being ideal.

Robin2:
IMHO it would be much simpler to check for the light BEFORE the function is called that produces the next item of the morse code and then NOT call that function if the light has been sensed.

Is that not exactly what I did?

    // Cancel sequence if light is seen
    if (WaitForLight())
      return;
    dotOrDash(sequence[i]);     // Send out the dot or dash

If it hasn't already been said, I think you need to state-machine the f**k out of this thing; break it into microtasks and return control to loop() as often as possible. Consider making the state variables global so you can manipulate them from higher up if a SM reset or stoppage is required.

johnwasser:
Is that not exactly what I did?

    // Cancel sequence if light is seen

if (WaitForLight())
     return;
   dotOrDash(sequence[i]);     // Send out the dot or dash

It does not seem so to me. I presume your WaitForLight() function collects some data. In my mind its equivalent would be a boolean variable so the code would be

if (WaitForLight == true) { // not sure if it should be true or false
   return;
}
// etc

...R

Robin2:
It does not seem so to me. I presume your WaitForLight() function collects some data. In my mind its equivalent would be a boolean variable so the code would be

if (WaitForLight == true) { // not sure if it should be true or false

return;
}
// etc




...R

Did you not look at the declaration of "WaitForLight()"? It just checks an analog input against a threshold and returns true if the box is open.

When does your boolean variable check the analog input???

@johnwasser

Thanx!! Works like a charm. It still runs through the morse() procedure, but without the delay, it does not take any time! Really helpful.

@State machine

What you guys are saying is forget about delay and use millis() like in the blink without delay example. Afterwards rearrange the three levels of morse(), MorseCodeSequencer() and dotOrDash().

I do get the millis idea and I will make sure to make good use of it in my future coding, but I have no idea how to arrange my procedures in a fashion, that I can return to the case morse. Can you elaborate.

johnwasser:
When does your boolean variable check the analog input???

In my concept it would be set in a function called from loop()

…R

I want to know more about these novels the Arduino can write. Monkeys at typewriters?

a7