Possible Problem with Overflow?

I believe I may have an overflow on my micros. However, I'm just reading a serial input and my buzzer alarm which is a piezo is no longer working and I have no idea where it may be wrong. This is my first post so I apologize if this post isn't up to par with standards.

I'm using a normal Arduino UNO R3.

I've attached my code as a file as well as it's quite long in my opinion. I believe the issue may be with the Void buzzer loop.

#include <LiquidCrystal_I2C.h>
#include <Wire.h>

LiquidCrystal_I2C lcd(0x27, 20, 4); //Intializes LCD on proper I2C Address and screen resolution of 20x4 bits.
int bLED = 10;  //Variable for LEDs
int rLED = 9;  //Fail safe if alarm fails




//Structure to facilitate parsing of buzzer alarm and keep track of timing and frequency
struct buzzer  
{
  bool active = false;  //Buzzer intially off
  int pin = 8;  //Buzzer pin
  int freq = 1000; //sets frequency 
  unsigned long current_micro = 0;  //Datatype for Micros()
  unsigned long next_micro = 0; //Datatype for Micros()
};
buzzer alarm;



char c; // Sets up Variable
int  whichLine; // to keep track of which line of data we are parsing
int maxTemp = 80;  //Max Temperature (****CAN CHANGE TO ANY VALUE***)

// Structure to facilitate parsing of serial data and keep track of temps
struct temp
{
  char runningBuff[3];
  char snapshot[3];
  int  intOne = 0;
  int  intTwo = 0;
  int  value = 0;
};
temp lineOne;
temp lineTwo;



void setup()
{
  pinMode(bLED, OUTPUT); //sets LED as output
  pinMode(rLED, OUTPUT); //Sets LED as output
  pinMode (alarm.pin, OUTPUT); //Sets Buzzer as output
  lcd.init(); //Installs LCD
  lcd.backlight(); //Turns on LCD backlight
  lcd.print("Hello!");  //Shows LCD is working by displaying hello before anything else.
  delay(10);
  lcd.clear();  //Clears screen
  Serial.begin(9600); //Sets bit rate of serial monitor same as C# GUI.
}

void loop()
{
  read_serial();
  extract_temp();
  update_io();
  //debug();  //Debugging loop, remove "//" to enable debug
  buzzer();
}

void debug()
{
  lcd.setCursor(0,3);
  lcd.print(lineOne.snapshot[1]);
}
// function to update the LEDs and sound the buzzer
void update_io()
{
  if (lineOne.value > maxTemp)
  {
    // alarmy stuff!
    digitalWrite(rLED, HIGH); //Fail safe if alarm fails
    alarm.active = true;
  }

  else if (lineTwo.value > maxTemp)
  {
    // alarmy stuff!
    digitalWrite(rLED, HIGH); //Fail safe if alarm fails
    alarm.active = false;
  }

  else
  {
    // turn off the alarmy stuff!
    digitalWrite(rLED, LOW); //Fail safe if alarm fails
    alarm.active = false;
  }
}







//Alarm Function
void buzzer()
{
  if (alarm.active) //active alarm function
  {
    alarm.current_micro = micros(); //Keeps track of alarm time
    
    if(alarm.current_micro >= alarm.next_micro) 
    {
      alarm.pin ^= 1; //XOR Gate
      alarm.next_micro = micros()+((1000000/alarm.freq)/2);
    }
  }
  
  else
  {
    digitalWrite(alarm.pin, LOW);
  }
}







// function to parse the character data and get an integer value for temperature
void extract_temp()
{
  // convert the characers in snapshots to their respective integers
  lineOne.intOne = lineOne.snapshot[0] - '0';   // subtract the ascii value for zero to convert to integer
  lineOne.intTwo = lineOne.snapshot[1] - '0';

  lineTwo.intOne = lineTwo.snapshot[0] - '0';
  lineTwo.intTwo = lineTwo.snapshot[1] - '0';

  // combine the two individual int values into a single integer
  lineOne.value = lineOne.intOne*10 + lineOne.intTwo;
  lineTwo.value = lineTwo.intOne*10 + lineTwo.intTwo;
  
  //Used for debugging below
  //lineOne.value = lineOne.intOne + lineOne.intTwo*10;  
  //lineTwo.value = lineTwo.intOne + lineTwo.intTwo*10;

// Swapped (used for debugging issues)
  //lineOne.value = lineOne.intTwo*10 + lineOne.intOne;
  //lineTwo.value = lineTwo.intTwo*10 + lineTwo.intOne;
}


// function to read in the serial data
void read_serial()
{
  if (Serial.available() > 0) //If serial is reading any information it will run loop program below.
  {
    //delay(100);
    //lcd.clear(); //Clears screen again
    lcd.setCursor(0, 0); //sets characters on top left of LCD
    whichLine = 1;    // Just started reading so currently working on the first line
    int charCount = 1;



    while (Serial.available() > 0)
    {
      digitalWrite (bLED, HIGH); //If reading LED will turn on    TODO: turn this off when done reading
      c = Serial.read();  //Serial Reading set as variable "c"


      // keep track of the previous two characters that were read on each line
       if ( whichLine == 1 )
      {
        lineOne.runningBuff[0] = lineOne.runningBuff[1];
        lineOne.runningBuff[1] = lineOne.runningBuff[2];
        lineOne.runningBuff[2] = c;
      }
      else if ( whichLine == 2 )
      {
        lineTwo.runningBuff[0] = lineTwo.runningBuff[1];
        lineTwo.runningBuff[1] = lineTwo.runningBuff[2];
        lineTwo.runningBuff[2] = c;
      }


      if(charCount <=33)
      {
        if (c == '-') //If string has "-"
        {
          lcd.setCursor(0, 1); //Lowers cursor down and lets Line 2 be read right below
          whichLine = 2;       // set to other line
        }
        else if(c == '_') //if line has "_'
        {
          lcd.print((char)223); //Prints Degree Symbol


          if ( whichLine == 1 )
          {
            lineOne.snapshot[0] = lineOne.runningBuff[0];
            lineOne.snapshot[1] = lineOne.runningBuff[1];
          }
          else if ( whichLine = 2 )
          {
            lineTwo.snapshot[0] = lineTwo.runningBuff[0];
            lineTwo.snapshot[1] = lineTwo.runningBuff[1];
          }

        }
        else if(c == '#')
        {
          lcd.print((char)35);
        }
        else
        {
          lcd.write(c);
        }
      }
    }
  }
}

Arduino-MonitorFinal.ino (5.44 KB)

This is not the way to use micros()

alarm.current_micro = micros();

You should ALWAYS use subtraction for micros() and millis() and then there is never an overflow problem - like this

if (micros() - previousMicros >= interval) {

and NEVER calculate future times like this

alarm.next_micro = micros()+((1000000/alarm.freq)/2);

Just save the latest value of micros() and calculate the interval after which the next time action should happen.

Have a look at how the code is organized in Several Things at a Time. And see Using millis() for timing. A beginners guide if you need more explanation. (micros() is used the same way as millis() )

...R

The alarm system was working at one point, however I have spare arduinos laying around and decided to see if it was an overflow issue. However, it seems this program doesn't actually work anymore? any ideas?

Aren't the things in reply #1, ideas?

Yes, go with the subtraction method. micros() overflows every 71 minutes, subtraction takes care of it.

Or use millis(), that overflows every 49 days, subtraction works there also.

Here's why:

unsigned long math.

Time before the rollover: 0xfffffff0
Time after the rollover 0x000000f0

So later time - earlier time = difference.
0x000000f0 - 0xfffffff0 = 0xffffffff 0000 0100

But, that's 64 bit math on a windows computer. Arduino only has 32 bits, so the extra 8 fs get dropped as the calculation is done, and the result is 0x00000100, as expected. Works the same for millis() and micros(). 2^32-1 milliseconds - 49+ days, 2^32-1 milliseconds = 71+ hours. And the beat goes on ...

So I realized I had an easier way of doing it by setting the Piezo to be written on Pin5 via PWM and analog outputs. Thanks anywyas and I actually see where I went wrong with the Micros() and millis().

if you use millis() or micros(), you need to write the rollover-safe code. See the notes and warnings part of millis() function reference