Goto statment isnt working

i have been working on a game lately, and i chose to use while true loops, and exit them in he middle with a goto statement. i have done exactly what the reference says, but it doesnt seem o work. here is my program (with about half cut out). its a little long...

start_screen:
    bool start = false;
    while(true) {
      //start screen
      mylcd.Set_Text_Back_colour(CYAN);
      mylcd.Fill_Screen(CYAN);
      mylcd.Set_Text_colour(MAGENTA);
      mylcd.Set_Text_Size(14);
      mylcd.Print_String("NOSE", 0, 0);
      mylcd.Print_String("DIVE", 0, 100);
      mylcd.Set_Draw_color(MAGENTA);
      mylcd.Draw_Fast_HLine(0, 430, 320);
      mylcd.Draw_Fast_HLine(0, 431, 320);
      mylcd.Draw_Fast_HLine(0, 432, 320);
      mylcd.Draw_Fast_HLine(0, 433, 320);
      mylcd.Draw_Fast_HLine(0, 434, 320);
      mylcd.Set_Text_Size(4);
      mylcd.Print_String("Difficulty:", 5, 439);
      mylcd.Print_String("High Score:", 5, 390);
      timerstart = millis();
      while(true) {
        if(millis() - timerstart >= 1000) {
          break;
        }
        if(analogRead(3) == 1023) {
          start = true;
        }
      }
      if(start == true) {
        break;
      }
    }

  restart:
    //generate sprite 1 variables
    randomSeed(analogRead(A0));
    int side = random(2);
    randomSeed(analogRead(A0));
    int length = random(50, 170);
  
    spr1side = side;
    spr1length = length;
  
    if (side == 1) {
      x1spr1 = mylcd.Get_Display_Width()-length;
      y1spr1 = mylcd.Get_Display_Height()-10; //-i
      x2spr1 = mylcd.Get_Display_Width();
      y2spr1 = mylcd.Get_Display_Height(); //-i
    }
    else {
      x1spr1 = 0;
      y1spr1 = mylcd.Get_Display_Height()-10; //-i
      x2spr1 = length;
      y2spr1 = mylcd.Get_Display_Height(); //-i
    }
  
    //generate sprite 2 variables
    randomSeed(analogRead(A0));
    side = random(2);
    randomSeed(analogRead(A0));
    length = random(50, 170);
  
    spr2side = side;
    spr2length = length;
  
    if (side == 1) {
      x1spr2 = mylcd.Get_Display_Width()-length;
      y1spr2 = mylcd.Get_Display_Height()-10; //-i
      x2spr2 = mylcd.Get_Display_Width();
      y2spr2 = mylcd.Get_Display_Height(); //-i
    }
    else {
      x1spr2 = 0;
      y1spr2 = mylcd.Get_Display_Height()-10; //-i
      x2spr2 = length;
      y2spr2 = mylcd.Get_Display_Height(); //-i
    }
  
    //actually generate sprite 1
    mylcd.Set_Draw_color(DARK_GREY);
    mylcd.Fill_Rectangle(x1spr1, y1spr1-spr1height, x2spr1, y2spr1-spr1height);
    
  
    for(int i = 1; i < 25; ++i) {
      //new frame
      mylcd.Set_Draw_color(LIGHT_GREY);
      mylcd.Fill_Rectangle(rock_x, 230, rock_x+20, 250);
      timerstart = millis();
      while(timerstart - millis() <= 200) {
        if(pressed1 == false) {
          if (analogRead(1) > 1000) {
            if (rock_x > 0) {
              rock_x -= 10;
              pressed1 = true;
            }
          }
        }
        if(pressed5 == false) {
          if (analogRead(5) > 1000) {
            if (rock_x < 300) {
              rock_x += 10;
              pressed5 = true;
            }
          }
        }
      }
      mylcd.Set_Draw_color(CYAN);
      mylcd.Fill_Rectangle(rock_x, 230, rock_x+20, 250);
      mylcd.Set_Draw_color(CYAN);
      mylcd.Fill_Rectangle(x1spr1, y1spr1-spr1height, x2spr1, y2spr1-spr1height);
      spr1height += 10;
      mylcd.Set_Draw_color(DARK_GREY);
      mylcd.Fill_Rectangle(x1spr1, y1spr1-spr1height, x2spr1, y2spr1-spr1height);
      if (spr1height == 230) {
        if (spr1side == 0) {
          if (rock_x < spr1length) {
            --lives;
          }
        }
        else {
          if ((rock_x+20) > (mylcd.Get_Display_Width() - spr1length)) {
            --lives;
          }
        }
      }
      if (lives == 0) {
        lives = 3;
        //TERMINATE
        goto death_screen;
        //TERMINATE
      }
      load_lives(lives);
    }
    //add sprite 2
    for(int i = 1; i < 25; ++i) {
      //new frame
      mylcd.Set_Draw_color(CYAN);
      mylcd.Fill_Rectangle(rock_x, 230, rock_x+20, 250);
      timerstart = millis();
      while(timerstart - millis() <= 200) {
        if(pressed1 == false) {
          if (analogRead(1) > 1000) {
            if (rock_x > 0) {
              rock_x -= 10;
              pressed1 = true;
            }
          }
        }
        if(pressed5 == false) {
          if (analogRead(5) > 1000) {
            if (rock_x < 300) {
              rock_x += 10;
              pressed5 = true;
            }
          }
        }
      }
      mylcd.Set_Draw_color(LIGHT_GREY);
      mylcd.Fill_Rectangle(rock_x, 230, rock_x+20, 250);
      mylcd.Set_Draw_color(CYAN);
      mylcd.Fill_Rectangle(x1spr1, y1spr1-spr1height, x2spr1, y2spr1-spr1height);
      mylcd.Fill_Rectangle(x1spr2, y1spr2-spr2height, x2spr2, y2spr2-spr2height);
      spr1height += 10;
      spr2height += 10;
      mylcd.Set_Draw_color(DARK_GREY);
      mylcd.Fill_Rectangle(x1spr1, y1spr1-spr1height, x2spr1, y2spr1-spr1height);
      mylcd.Fill_Rectangle(x1spr2, y1spr2-spr2height, x2spr2, y2spr2-spr2height);
      if (spr1height == 230) {
        if (spr1side == 0) {
          if (rock_x < spr1length) {
            --lives;
          }
        }
        else {
          if ((rock_x+20) > (mylcd.Get_Display_Width() - spr1length)) {
            --lives;
          }
        }
      }
      if (spr2height == 230) {
        if (spr2side == 0) {
          if (rock_x < spr2length) {
            --lives;
          }
        }
        else {
          if ((rock_x+20) > (mylcd.Get_Display_Width() - spr2length)) {
            --lives;
          }
        }
      }
      if (lives == 0) {
        lives = 3;
        //TERMINATE
        goto death_screen;
        //TERMINATE
      }
      load_lives(lives);
    }
  
  
  
  int side = 0;
  int length = 0;
  
  //begin automated section of game
  while(true) {
    //re-randomize sprite 1 size
    randomSeed(analogRead(A0));
    side = random(2);
    randomSeed(analogRead(A0));
    length = random(50, 170);
    
    spr1side = side;
    spr1length = length;
    
    if (side == 1) {
      x1spr1 = mylcd.Get_Display_Width()-length;
      y1spr1 = mylcd.Get_Display_Height()-10; //-i
      x2spr1 = mylcd.Get_Display_Width();
      y2spr1 = mylcd.Get_Display_Height(); //-i
    }
    else {
      x1spr1 = 0;
      y1spr1 = mylcd.Get_Display_Height()-10; //-i
      x2spr1 = length;
      y2spr1 = mylcd.Get_Display_Height(); //-i
    }
    
    mylcd.Set_Draw_color(CYAN);
    mylcd.Fill_Rectangle(rock_x, 230, rock_x+20, 250);
    timerstart = millis();
    while(timerstart - millis() <= 200) {
      if(pressed1 == false) {
        if (analogRead(1) > 1000) {
          if (rock_x > 0) {
            rock_x -= 10;
            pressed1 = true;
          }
        }
      }
      if(pressed5 == false) {
        if (analogRead(5) > 1000) {
          if (rock_x < 300) {
            rock_x += 10;
            pressed5 = true;
          }
        }
      }
    }
    
      mylcd.Set_Draw_color(LIGHT_GREY);
      mylcd.Fill_Rectangle(rock_x, 230, rock_x+20, 250);
  mylcd.Set_Draw_color(CYAN);
  mylcd.Fill_Rectangle(x1spr1, y1spr1-spr1height, x2spr1, y2spr1-spr1height);
  mylcd.Fill_Rectangle(x1spr2, y1spr2-spr2height, x2spr2, y2spr2-spr2height);
  spr1height = 0;
  spr2height += 10;
  mylcd.Set_Draw_color(DARK_GREY);
  mylcd.Fill_Rectangle(x1spr1, y1spr1-spr1height, x2spr1, y2spr1-spr1height);
  mylcd.Fill_Rectangle(x1spr2, y1spr2-spr2height, x2spr2, y2spr2-spr2height);
  if (spr1height == 230 ) {
      if (spr1side == 0) {
        if (rock_x < spr1length) {
          --lives;
        }
      }
      else {
        if ((rock_x+20) > (mylcd.Get_Display_Width() - spr1length)) {
          --lives;
        }
      }
    }
    if (spr2height == 230) {
      if (spr2side == 0) {
        if (rock_x < spr2length) {
          --lives;
        }
      }
      else {
        if ((rock_x+20) > (mylcd.Get_Display_Width() - spr2length)) {
          --lives;
        }
      }
    }
    if (lives == 0) {
      lives = 3;
      //TERMINATE
      goto death_screen;
      //TERMINATE
    }
  load_lives(lives);
  delay(slowness);
    
  for(int i = 1; i < 24; ++i) {
    //new frame
    mylcd.Set_Draw_color(CYAN);
    mylcd.Fill_Rectangle(rock_x, 230, rock_x+20, 250);
    timerstart = millis();
      while(timerstart - millis() <= 200) {
        if(pressed1 == false) {
          if (analogRead(1) > 1000) {
            if (rock_x > 0) {
              rock_x -= 10;
              pressed1 = true;
            }
          }
        }
        if(pressed5 == false) {
          if (analogRead(5) > 1000) {
            if (rock_x < 300) {
              rock_x += 10;
              pressed5 = true;
            }
          }
        }
      }
    mylcd.Set_Draw_color(LIGHT_GREY);
    mylcd.Fill_Rectangle(rock_x, 230, rock_x+20, 250);
    mylcd.Set_Draw_color(CYAN);
    mylcd.Fill_Rectangle(x1spr1, y1spr1-spr1height, x2spr1, y2spr1-spr1height);
    mylcd.Fill_Rectangle(x1spr2, y1spr2-spr2height, x2spr2, y2spr2-spr2height);
    spr1height += 10;
    spr2height += 10;
    mylcd.Set_Draw_color(DARK_GREY);
    mylcd.Fill_Rectangle(x1spr1, y1spr1-spr1height, x2spr1, y2spr1-spr1height);
    mylcd.Fill_Rectangle(x1spr2, y1spr2-spr2height, x2spr2, y2spr2-spr2height);
    if (spr1height == 230) {
      if (spr1side == 0) {
        if (rock_x < spr1length) {
          --lives;
        }
      }
      else {
        if ((rock_x+20) > (mylcd.Get_Display_Width() - spr1length)) {
          --lives;
        }
      }
    }
    if (spr2height == 230) {
      if (spr2side == 0) {
        if (rock_x < spr2length) {
          --lives;
        }
      }
      else {
        if ((rock_x+20) > (mylcd.Get_Display_Width() - spr2length)) {
          --lives;
        }
      }
    }
    if (lives == 0) {
      lives = 3;
      //TERMINATE
      goto death_screen;
      //TERMINATE
    }
    load_lives(lives);
  }
  
  //re-randomize sprite 2 size
  randomSeed(analogRead(A0));
  side = random(2);
  randomSeed(analogRead(A0));
  length = random(50, 170);
  
  spr2side = side;
  spr2length = length;
  
  if (side == 1) {
    x1spr2 = mylcd.Get_Display_Width()-length;
    y1spr2 = mylcd.Get_Display_Height()-10; //-i
    x2spr2 = mylcd.Get_Display_Width();
    y2spr2 = mylcd.Get_Display_Height(); //-i
  }
  else {
    x1spr2 = 0;
    y1spr2 = mylcd.Get_Display_Height()-10; //-i
    x2spr2 = length;
    y2spr2 = mylcd.Get_Display_Height(); //-i
  }
  
  mylcd.Set_Draw_color(CYAN);
    mylcd.Fill_Rectangle(rock_x, 230, rock_x+20, 250);
    timerstart = millis();
      while(timerstart - millis() <= 200) {
        if(pressed1 == false) {
          if (analogRead(1) > 1000) {
            if (rock_x > 0) {
              rock_x -= 10;
              pressed1 = true;
            }
          }
        }
        if(pressed5 == false) {
          if (analogRead(5) > 1000) {
            if (rock_x < 300) {
              rock_x += 10;
              pressed5 = true;
            }
          }
        }
      }
    mylcd.Set_Draw_color(LIGHT_GREY);
    mylcd.Fill_Rectangle(rock_x, 230, rock_x+20, 250);
  mylcd.Set_Draw_color(CYAN);
  mylcd.Fill_Rectangle(x1spr1, y1spr1-spr1height, x2spr1, y2spr1-spr1height);
  mylcd.Fill_Rectangle(x1spr2, y1spr2-spr2height, x2spr2, y2spr2-spr2height);
  spr1height += 10;
  spr2height = 0;
  mylcd.Set_Draw_color(DARK_GREY);
  mylcd.Fill_Rectangle(x1spr1, y1spr1-spr1height, x2spr1, y2spr1-spr1height);
  mylcd.Fill_Rectangle(x1spr2, y1spr2-spr2height, x2spr2, y2spr2-spr2height);
  if (spr1height == 230) {
      if (spr1side == 0) {
        if (rock_x < spr1length) {
          --lives;
        }
      }
      else {
        if ((rock_x+20) > (mylcd.Get_Display_Width() - spr1length)) {
          --lives;
        }
      }
    }
    if (spr2height == 230) {
      if (spr2side == 0) {
        if (rock_x < spr2length) {
          --lives;
        }
      }
      else {
        if ((rock_x+20) > (mylcd.Get_Display_Width() - spr2length)) {
          --lives;
        }
      }
    }
    if (lives == 0) {
      lives = 3;
      //TERMINATE
      goto death_screen;
      //TERMINATE
    }
  load_lives(lives);
  delay(slowness);
  
  for(int i = 1; i < 24; ++i) {
    //new frame
    mylcd.Set_Draw_color(CYAN);
    mylcd.Fill_Rectangle(rock_x, 230, rock_x+20, 250);
    timerstart = millis();
      while(timerstart - millis() <= 200) {
        if(pressed1 == false) {
          if (analogRead(1) > 1000) {
            if (rock_x > 0) {
              rock_x -= 10;
              pressed1 = true;
            }
          }
        }
        if(pressed5 == false) {
          if (analogRead(5) > 1000) {
            if (rock_x < 300) {
              rock_x += 10;
              pressed5 = true;
            }
          }
        }
      }
    mylcd.Set_Draw_color(LIGHT_GREY);
    mylcd.Fill_Rectangle(rock_x, 230, rock_x+20, 250);
    mylcd.Set_Draw_color(CYAN);
    mylcd.Fill_Rectangle(x1spr1, y1spr1-spr1height, x2spr1, y2spr1-spr1height);
    mylcd.Fill_Rectangle(x1spr2, y1spr2-spr2height, x2spr2, y2spr2-spr2height);
    spr1height += 10;
    spr2height += 10;
    mylcd.Set_Draw_color(DARK_GREY);
    mylcd.Fill_Rectangle(x1spr1, y1spr1-spr1height, x2spr1, y2spr1-spr1height);
    mylcd.Fill_Rectangle(x1spr2, y1spr2-spr2height, x2spr2, y2spr2-spr2height);
    if (spr1height == 230) {
      if (spr1side == 0) {
        if (rock_x < spr1length) {
          --lives;
        }
      }
      else {
        if ((rock_x+20) > (mylcd.Get_Display_Width() - spr1length)) {
          --lives;
        }
      }
    }
    if (spr2height == 230) {
      if (spr2side == 0) {
        if (rock_x < spr2length) {
          --lives;
        }
      }
      else {
        if ((rock_x+20) > (mylcd.Get_Display_Width() - spr2length)) {
          --lives;
        }
      }
    }
    if (lives == 0) {
      lives = 3;
      //TERMINATE
      goto death_screen;
      //TERMINATE
    }
    load_lives(lives);
    delay(slowness);
  }
}
}
death_screen:
  bool start = false;
  while(true) {
    //start screen
    mylcd.Set_Text_Back_colour(CYAN);
    mylcd.Fill_Screen(CYAN);
    mylcd.Set_Text_colour(MAGENTA);
    mylcd.Set_Text_Size(14);
    mylcd.Print_String("GAME", 0, 0);
    mylcd.Print_String("OVER", 0, 100);
    mylcd.Set_Draw_color(MAGENTA);
    mylcd.Draw_Fast_HLine(0, 430, 320);
    mylcd.Draw_Fast_HLine(0, 431, 320);
    mylcd.Draw_Fast_HLine(0, 432, 320);
    mylcd.Draw_Fast_HLine(0, 433, 320);
    mylcd.Draw_Fast_HLine(0, 434, 320);
    mylcd.Set_Text_Size(4);
    mylcd.Print_String("Difficulty:", 5, 439);
    mylcd.Print_String("High Score:", 5, 390);
    timerstart = millis();
    while(true) {
      if(millis() - timerstart >= 1000) {
        break;
      }
      if(analogRead(3) == 1023) {
        start = true;
      }
    }
    if(start == true) {
      break;
    }
  }

Arduino IDE >> File >> Preferences >> Check the box labeled Display Line Numbers

and say what line number it''s on.

the reference for goto pg 204 of The C Programming Language says "the identifier must be a label located in the current function"

while your posted code does not look like a valid program, it looks like your using a goto labels instead of a function

From [Language Reference](file:///home/ray/.arduino/arduino-1.8.15/reference/www.arduino.cc/en/Reference/Goto.html)

Example

for(byte r = 0; r < 255; r++){
    for(byte g = 255; g > -1; g--){
        for(byte b = 0; b < 255; b++){
            if (analogRead(0) > 250){ goto bailout;}
            // more statements ... 
        }
    }
}
bailout:


me personal I don't help people that use the command "goto"

using goto leads to really messy code that is very hard to understand and to maintain.
You will need more and more hours to follow and debug your own code with each line you add.

It is well invested time and it will save you many many hours of time for debugging
to learn how to use functions and to NOT use the command goto.

You might think I have come so far I just need a little bit.....
Well try it. I'm pretty sure it will turn out to eat up a lot of hours.
If your favorite hobby is to spin fast in circles for weeks only making minimal progress
to finish your project and this is the only way you feel good. It is the right decision to keep the command goto.

best regards Stefan

Much better to have a function and use return to bail out.

Let’s see who can write the most convoluted code with delay() and go to…

Your indentation does not match the program structure so reformat it first then I might be clear what the problem is.
Your goto can't jump into the middle of a block.
Also, turn compiler warnings on.

there's at least one acceptable way of using gotos that is explained on pg 50 in The C Programming Language

I want to quote that page

3.8 Goto and labels
C provides the infinitely-abusable goto statement, and labels to branch to.

Formally, the goto statement is never necessary,

and in practice it is almost always easy to write code without it.

We have not used goto in this book.

Nevertheless, there are a few situations where gotos may find a place. The most common is to abandon processing in some
deeply nested structure, such as breaking out of two or more loops at once. The break statement cannot be used directly since
it only exits from the innermost loop. Thus:

 for ( ... )
 for ( ... ) {
 ...
 if (disaster)
 goto error;
 }
 ...
 error:
 /* clean up the mess */

This organization is handy if the error-handling code is non-trivial, and if errors can occur in several places.
A label has the same form as a variable name, and is followed by a colon. It can be attached to any statement in the same
function as the goto. The scope of a label is the entire function.
As another example, consider the problem of determining whether two arrays a and b have an element in common. One
possibility is

 for (i = 0; i < n; i++)
 for (j = 0; j < m; j++)
 if (a[i] == b[j])
 goto found;
 /* didn’t find any common element */
 ...
 found:
 /* got one: a[i] == b[j] */
 ...

Code involving a goto can always be written without one, though perhaps at the price of some repeated tests or an extra
variable. For example, the array search becomes

 found = 0;
 for (i = 0; i < n && !found; i++)
 for (j = 0; j < m && !found; j++)
 if (a[i] == b[j])
 found = 1;
 if (found)
 /* got one: a[i-1] == b[j-1] */
 ...
 else
 /* didn’t find any common element */
 ...

With a few exceptions like those cited here, code that relies on goto statements is generally harder to understand and to
maintain than code without gotos. Although we are not dogmatic about the matter, it does seem that goto statements should
be used rarely, if at all.

best regards Stefan

as you nearly asked for it:
Real Programmers aren't afraid to use GOTOs.

1 Like

No I'm not a "real programmer" and I'm proud of it.
best regards Stefan

i think that explains a lot (curious what you think you are)?

more experienced developers know when to bend the rules ... if there are any

i believe there is an art to developing code that not only works, but seems intuitively obvious. gotos can make it obvious, rather than convoluted.

I'm a "hooby-keyboard-typer" that most of the time follows the rule
"one function does one single thing"
accidently the letter-sequences typed by me have - at least for me -
some useful functionality when compiled and uploaded into a microcontroller.

best regards Stefan

again, that explains a lot

so you develop code for yourself, meeting your own requirements.

you don't have to write code that meets the requirements of systems engineers, that needs to be reviewed by others and potential debugged by others. you don't have organizational guidelines for how to write code that make style consistent for everyone in the organization.

you've probably never been asked to modify code you wrote a year ago that you can't remember writing (and when you look at wonder why the hell you did it the way you did)

you've probably not had to spend a week+ trying to debug a problem found by a customer, only reproducible at the customer's site and written by someone else

...

Oh dear. Is this going to devolve into another delay() VS millis() thread?

So I have to add some more information:

34 years ago I started programming as a students-afternoon-job in a company
using Borland-Pascal 3.0. That company had some coding standards:

  • very accurate formatting
  • very accurate self-explainig names for everything "the name explains it not the comment"
  • the rule one procedure does one thing (in pascal a procedure is a what in C is function)

before that I was writing basic-spaghetti-code on a C64
then changed to a IBM-compatible 286 writing programs in Borland Pascal

In 1990 in my community service (the replacement for "serve as a soldier")
I developed a medical-equipment management-software based on dBase IV
not using Memo-fields for important data because through thoroughly testing I discovered that this was a weakness of dBase IV where data could get lost easily. And a software for calculating the payments for the community service. The hospital used this software very long. Until no more needle-printers where available)

From 2002 to 2007 I worked for a special-purpose-engineering company that developed and build customer laser-engraving machines and steel-engraver machines for the automotive industrie. I developed the controls for those machines based on PCs using Delphi and realtime-cards with another 486-processor running borland pascal programs that did control the steppermotors as Windows is not a realtime OS. The control-software was running in multiple threads and used a lot of state-machines (of course)

The main requirement was 99.5% reliability of the machines. We had to think of every possible nonsense the maintenance-people might do to the machine and still the control has to catch it when-ever possible in automatic and in manual mode. As all the machines where everywhere in europe distant-diagnosis through extensive logfiles was very important.

The software had to be adapted to a lot of different MES (Manufacturing Executing Systems)
Siemens Profibus, Siemens Sicalis, VW PMON, Audi, BMW, Opel Magna Steyr, SAAB, KIA, Porsche, Bugatti, Daimler

The hardest debug-job I have done was on a rarely occuring fault on a profibus-communication
It turned out that the newer and much faster PC104-486-processors used on a software developped on PC104-386-processors was so fast that only sometimes something executed just a little bit too fast to be correct at a certain point in the program.

And yes I did not spend a week+ on this debugging-job: I spend two days. Because I did it without any guessings just logging to the screen or to arrays very throroughly where ever possible.

So I have quite some experience in working in teams and to fullfil a lot of external requirements of different kinds.

You could not know because I have never told it.

What I kept from this is:

  • use selfexplaining names for everything
  • one function does one thing
  • test every function throuroghly with even the most crazy values
  • if something is weird invest the time to add even more debug-output to really comprehend what is going on.

So there are even more attitudes and more programming-styles on earth a single person can think of.

Today I'm just programming as a hobby a little bit home-automation and datalogging

best regards Stefan

The most common "unavoidable" use of goto is in leaving deeply nested blocks without leaving the function (which can always be accomplished with return).

But we are using C++ here, not C, and C++ has an exception mechanism -- try/catch blocks. These give a structured way of handling aborting out of those nested loops using the throw command. Use this rather than goto.

1 Like

What, even the "Never use delay()" rule? :rofl:

(Sorry...)

Never tried that on an AVR.

Hmmm.