<SOLVED> Strange Arduino program behavior

I have an Arduino program that operates turn signals for a motorcycle based on a PY503 gyro with its 4x pin connected to Arduino pin A2 and the left turn button connected to pin 9 and the right turn button connected to pin 10.
Here is how it functions:

  1. Press left button < 333ms and the left blinker blinks 4 times (lane change)
  2. Press the left button > 333ms and < 2000 ms and it blinks until either is true:
    a. A button is pressed and released (cancel)
    b. The gyro turns left, then right, then stops turning (What a motorcycle does in a left turn)
  3. Press the left button > 2000ms and both the left and right directional lights blink until cancelled with a subsequent press of either button..
  4. The same is true for the right button except the gyro turn requirements are reversed.

Both 1 and 2 above are working just fine but #3 (emergency flashers) has a problem.
Line 108 has Serial.println("Ready to Cancel"); and the emergency flashers work if that line is not commented out. If I comment out that line, the system goes through the motions but the flashers do not blink.
(By goes through the motions I mean after holding a button for > 2000ms, nothing flashes, and the first subsequent brief button press doesn’t “appear” to change anything but the second brief press puts it into lane change mode. That means it exited the Emergency mode with the first press and then cancelled with the second.

This is 100% repeatable, #3 works when the Serial.print is functioning and no blinks occur if I comment it out.

I have spent several hours trying to resolve this. I do not print while inside an interrupt routine. The code has been shuffled around to change the order of things in the main loop.

This seems bazaar to me. Anyone have some ideas on what is happening?

Here is the code:

#include <elapsedMillis.h>
#include<Wire.h>
#include "TimerOne.h"
#include <MsTimer2.h>
#define variance 75
#define pressed  0

int g = 0;
int gyroLeft;
int gyroRight;
boolean BlinkLeft = false;
boolean BlinkRight = false;
boolean BlinkBoth = false;
typedef struct data_type
{
  int leftButton;
  int rightButton;
  int len;
  boolean turn;
  boolean change;
  boolean cancel;
} data_t;
data_t d;
elapsedMillis timer0;

void setup() {
  Serial.begin(115200);
  pinMode(9, INPUT_PULLUP);           // Left directional Switch
  pinMode(10, INPUT_PULLUP);          // Right directional Switch
  pinMode(11, OUTPUT);                // Left directional output
  pinMode(12, OUTPUT);                // Right directional output
  pinMode(13, OUTPUT);                // LED circuit board indicator
  Timer1.initialize(500000);          // initialize timer1, and set a 1/2 second period
  Timer1.attachInterrupt(Timer1Int);  // attaches interruptk() as a timer overflow interrupt
  MsTimer2::set(4000, Timer2Int);     // 4 second period
  MsTimer2::stop();                   // timer off
  BlinkOff();
  g = ReadGyro();
  gyroLeft = g + variance;
  gyroRight = g - variance;
}

void Timer1Int()  // Comes here every .5 seconds for blinking
{
  if (BlinkLeft)
  {
    digitalWrite(11, digitalRead(11) ^ 1);    // Left
    digitalWrite(13, digitalRead(13) ^ 1);    // LED
  }
  else if (BlinkRight)
  { digitalWrite(12, digitalRead(12) ^ 1);    // Right
    digitalWrite(13, digitalRead(13) ^ 1);    // LED
  }
  else if (BlinkBoth)
  {
    digitalWrite(11, digitalRead(11) ^ 1);    // Left
    digitalWrite(12, digitalRead(12) ^ 1);    // Right
    digitalWrite(13, digitalRead(13) ^ 1);    // LED
  }
  else
    BlinkOff();
}


void Timer2Int()  // lane change ends after 5 seconds
{
  BlinkLeft = false;
  BlinkRight = false;
  MsTimer2::stop();
}

void loop()
{
  String dir;
  int l, r;
  d.len = 0;
  ReadButtons(true);                  // blocked read
  if (d.len > 0)                      // is it valid?
  {
    // Serial.println("Start");
    if (d.len <= 333)                       // lane change
    {
      d.change = true;
      d.turn = false;
      d.cancel = false;
      if (d.leftButton == pressed)
        BlinkLeft = true;
      if (d.rightButton == pressed)
        BlinkRight = true;
      MsTimer2::start();
    }
    if (d.len > 333 and d.len <= 2000)       // Turn
    {
      d.change = false;
      d.turn = true;
      d.cancel = false;
      if (d.leftButton == pressed)
        BlinkLeft = true;
      if (d.rightButton == pressed)
        BlinkRight = true;
    }
    if (d.len > 2000)                       // Emergency flashers
    {
      BlinkBoth = true;
      BlinkLeft = false;
      BlinkRight = false;
      delay(100);
      Serial.println("Ready to Cancel");
      while (ReadButtons(false) == false); // Wait for button press (Cancel);
      delay(100);
      while (ReadButtons(false) == true);  // Wait for button release
      BlinkBoth = false;
    }
  }
  if (d.turn)
    Turn();
}

void Turn() // blink until turn completed or cancelled
{
  int g, i;
  g = ReadGyro();
  while (d.turn)
  {
    if (BlinkLeft)
    {
      if (WaitForLeft())
        d.turn = false;
      if (d.turn)
        if (WaitForRight())
          break;
      if (d.turn)
        WaitForStraight();
      Reset(false);
      d.turn = false;
    }
    else if (BlinkRight)
    {
      //watch(d.turn, d.change, BlinkLeft, BlinkRight, d.leftButton, d.rightButton, g);
      if (WaitForRight())
        d.turn = false;
      if (d.turn)
        if (WaitForLeft())
          d.turn = false;
      if (d.turn)
        WaitForStraight();
      Reset(false);
      d.turn = false;
    }
  }
}

void BlinkOff()
{
  digitalWrite(11, LOW);
  digitalWrite(12, LOW);
  digitalWrite(13, LOW);
  BlinkLeft = false;
  BlinkRight = false;
  BlinkBoth = false;
}

int ReadGyro()
{
  int i;
  g = 0;
  for (i = 0; i < 20; i++)
    g += analogRead(10);
  return (g / 20);
}

void ShowGyro(int a)
{
  if (a < gyroRight)
    Serial.println("right");
  else if (a > gyroLeft)
    Serial.println("left");
}

boolean ReadButtons(bool blocking)
{
  boolean result = false;
  int l, r;
  d.leftButton = digitalRead(9);
  d.rightButton = digitalRead(10);
  if (d.leftButton == pressed | d.rightButton == pressed)
  {
    result = true;
  }
  if (blocking)
  {
    timer0 = 0;
    d.len = 0;
    l = d.leftButton;
    r = d.rightButton;
    while (l == pressed | r == pressed)
    {
      l = digitalRead(9);
      r = digitalRead(10);
    }
    if (timer0 > 10)
      d.len = timer0;
  }
  return result;
}

boolean WaitForLeft()
{
  g = ReadGyro();
  while (g < gyroLeft)             // wait for left turn
  {
    g = ReadGyro();
    if (CheckCancel())
      return true;
  }
  return false;
}

boolean WaitForRight()
{
  g = ReadGyro();
  while (g > gyroRight)             // wait for right turn
  {
    g = ReadGyro();
    if (CheckCancel())
      return true;
  }
  return false;
}

boolean WaitForStraight()
{
  int i;
  g = ReadGyro();
  while (g < gyroRight | g > gyroLeft)
  {
    g = ReadGyro();
    if (CheckCancel())
      return true;
  }
  return false;
}

void Reset(boolean prt)
{
  d.change = false;
  d.turn = false;
  d.cancel = true;
  d.leftButton = 1;
  d.rightButton = 1;
  d.len = 0;
  BlinkLeft  = false;
  BlinkRight = false;
  BlinkOff();
  if (prt)
    Serial.println("Canceled");
}

boolean CheckCancel()
{
  int but = ReadButtons(false);         // cancel if button pressed
  if (but)
  {
    while (ReadButtons(false));       // Wait for button release
    Reset(true);
    return true;
  }
  return false;
}

The syntax of this line (and several other similar lines) is confusing, because it uses a bitwise OR "|" rather than a logical OR "||". I'm not sure that would make a difference in the behavior, but it might, so try correcting it.

  if (d.leftButton == pressed | d.rightButton == pressed)

Thank you for that. I made that change but unfortunately it didn't help this particulat issue.

When global variables are used for communicating with interrupt routines, they must be declared "volatile".

Tutorial here.

jremington:
When global variables are used for communicating with interrupt routines, they must be declared "volatile".

Tutorial here.

Bingo! That fixed it. I can tell you right now that I never would have found that one on my own. I knew about a couple things to keep in mind regarding interrupt routines, but not that one.

Thank you very much for the spot-on pointer.

Best regards to both responders.