Arduino runs very slow, too many calculations or modules? [Solved]

[Solved] -> See below

I'm trying to create a Bomb Prop for use in our Airsoft games.
For this purpose I have connected multiple modules (listed below) to my Arduino.

Arduino Nano V3 Atmega328P CH340 Chip
1x 2004 LCD Display with I2C Adapter
2x 7-Segment Display with 4 digits using I2C
1x 4x4 Matrix Keypad with PCF8574 I/O Expander
3x Push Buttons
1x Flip Switch

I tested each module by itself and had no problems with them.
When using all modules at the same time, I can send out hard coded signals and they work just fine.
I've managed to get the most basic version of the Game running.

The problems start once I try to program the the full game into the Arduino.
First I have 2 switch statements. The first to switch between different game modes and the second to switch between different phases in these game modes. (ATM there is just one game mode).
I need to calculate different values (Game Time Left, Bomb Time Left...) and convert them to Seconds and minutes to display them on the 7-Segment Displays, while also checking for Input on the buttons and Key Matrix.
So the code runs 2 switch statements, a few calculations, sends signals to the displays and checks for input.
At this point the loop runs very slow. It takes over a second for input to be recognized and the 7-seg displays don't seem to work (I guess because the loop is to slow). The LCD Display works just fine, other than the slow updates.
I can't imagine how slow it will be when I finish coding this game mode and add some more.

The point at which it starts to run slower, is when I add the Timer calculations. If I remove them, it works perfectly fine.
So I'm assuming the switch statements are not the problem. Could the problem be the millis() function?

Is it possible to use the Arduino for this purpose or do I have to many modules?
Is the problem the lengthy code? How can I structure my code to run more smoothly?
I used the two switch statements to only have part of the code loop depending on the game mode and stage of the game.
Is there a better way to structure it?
Sorry in advance for the messy code.
(The part that slows everything down is right after 'case 2: //Place Bomb')

#include <LiquidCrystal_I2C.h>
#include <Keypad_I2C.h>
#include "SevenSegmentTM1637.h"

//Constant Variables to change in Code
const int segBacklight = 100;

/*
  0  - Main Menu,
  10 - Gamemode Bomb
    10 - Setup
    12 - Place Bomb
    14 - Defuse Bomb
    16 - Victory Screen
  20 - Gamemode Domination
  30 - Gamemode Virus
*/
unsigned int GameMode = 10;
unsigned int Phase = 0;

unsigned gameTime = 60;
unsigned bombTime = 30;

unsigned gameStartTime;
unsigned bombStartTime;

//Clock and Data Pin for both 7-Segment Displays
const byte dsp1_clk = 10;
const byte dsp1_dio = 11;
const byte dsp2_clk = 12;
const byte dsp2_dio = 13;

//Input pins for the Buttons
const byte butL = 2;
const byte butM = 3;
const byte butR = 4;

//Input for the SafeSwitch
const byte sfsw = 5;

//Create the Objects for the LCD, 7-Segment and Keypad
LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 20, 4);

SevenSegmentTM1637 dsp1(dsp1_clk, dsp1_dio);
SevenSegmentTM1637 dsp2(dsp2_clk, dsp2_dio);

char hexaKeys[4][4] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
byte colPins[4] = {7, 6, 5, 4};
byte rowPins[4] = {3, 2, 1, 0};

Keypad_I2C I2C_Keypad(makeKeymap(hexaKeys), rowPins, colPins, 4, 4, 0x20, PCF8574);

void setup() {
  //Pinmodes for Buttons and Buzzer
  pinMode(butL, INPUT);
  pinMode(butM, INPUT);
  pinMode(butR, INPUT);
  pinMode(sfsw, INPUT);
  pinMode(buzz, OUTPUT);

  //Initializing the LCD
  lcd.init();
  lcd.backlight();

  //Initializing the Keypad
  I2C_Keypad.begin();

  //Initializing the 7-Seg 1
  dsp1.begin();
  dsp1.setBacklight(segBacklight);

  //Initializing the 7-Seg 2
  dsp2.begin();
  dsp2.setBacklight(segBacklight);

  Serial.begin(9600);
}

void loop() {

  switch (GameMode) {
    case 0:
      {
        //Gamemode for Main Menu
      }
      break;

    case 10:
      {
        GamemodeBomb();
      }
      break;

    //Error retrieving Gamemode
    default:
      {
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Error occured");
        lcd.setCursor(0, 1);
        lcd.print("Unknown Gamemode");
        lcd.setCursor(0, 2);
        lcd.print("Gamemode: ");
        lcd.setCursor(0, 3);
        lcd.print(GameMode);
      }

  }
}

void GamemodeBomb() {

  switch (Phase) {
    case 0:  //Setup
      {
        lcd.clear();
        lcd.home();
        lcd.print("Place Bomb and");
        lcd.setCursor(0, 1);
        lcd.print("flip the Switch...");
        lcd.setCursor(0, 3);
        lcd.print("Bomb is STANDBY ");
        gameStartTime = millis();
        Phase = 2;
      }
      break;

    case 2: //Place Bomb
      {
        int gameTimeLeft = (gameTime - (millis() - gameStartTime)) / 1000;
        int gameTimeLeftSec = gameTimeLeft % 60;
        int gameTimeLeftMin = gameTimeLeft / 60;

        char timer[10];
        sprintf(timer, "%02d%02d", gameTimeLeftMin, gameTimeLeftSec);
        dsp1.print(timer);
        dsp2.print(timer);

        dsp1.setColonOn(gameTimeLeftSec % 2 == 0);
        dsp2.setColonOn(gameTimeLeftSec % 2 == 0);


        if (digitalRead(sfsw) == 1) {
          if (digitalRead(butM) == 1) {
            lcd.setCursor(0, 3);
            lcd.print("Bomb is ACTIVE  ");
          } else {
            lcd.setCursor(0, 3);
            lcd.print("Bomb is ARMED   ");
          }
        } else {
          lcd.setCursor(0, 3);
          lcd.print("Bomb is STANDBY ");
        }
      }
      break;

    case 4: //Defuse Bomb
      {

      }
      break;

    case 6: //Victory Screen
      {

      }
      break;

    //Error retrieving Gamemode
    default:
      {
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Error occured");
        lcd.setCursor(0, 1);
        lcd.print("Unknown Phase");
        lcd.setCursor(0, 2);
        lcd.print("Phase: ");
        lcd.setCursor(0, 3);
        lcd.print(Phase);
      }
  }
}

Edit:
After about 3 hours of painfully adding Serial.println to every line and commenting out different lines I have finally found the two parts of the code that caused the problem. Thanks to anyone who tried to find a solution.

First thing was a minor error in the formula for gameTime, where I subtracted the time since the game started (millis() - gameStartTime), which is in milliseconds, from the time the game should run (gameTime) which is in seconds. This resulted in gameTimeLeft being negative which apparently caused some problems with the 7-Segment library.

After taking care of that, I had no problems with the speed of the calculations, but the display wasn't showing the correct timer. After some research I found that doing calculations with millis() should not involve an int. Changing gameTime to an unsigned long did the trick.

I can still notice some minor lags in the speed of the loop, mostly just the buzzer beeping a few milliseconds longer than it should every now and then, but thats probably because of the terrible code that will most likely be rewritten.

again thanks to everyone for taking a look at this,

        int gameTimeLeft = (gameTime - (millis() - gameStartTime)) / 1000;
        int gameTimeLeftSec = (millis() / 1000) % 60;//gameTimeLeft % 60;
        int gameTimeLeftMin = (millis() / 1000) / 60;//gameTimeLeft / 60;

You calculate gameTimeLeft and then throw it in the garbage can. Your comments are not true.

Yeah sorry,
the comments were from trouble shooting,
I tried to use different values and not those from the gameTimeLeft to see if it works

I changed the code above to what it should be

Okay, but in future please don't edit code that you've already posted, or comments about it. It makes the thread history seem illogical and can make the the replies seem like nonsense.

You didn't report the current status of the code. I assume "not working". Please try changing your logic so the screen is not updated every time through loop() - that should only happen when there is new data to write.

You should only do this once:

          lcd.setCursor(0, 3);
          lcd.print("Bomb is STANDBY ");

After that, leave the screen alone until something changes.

makes sense,
thanks for the info, will keep it in mid

I'll probably rewrite my code, so I can take care of that.
I found some guides on Menu systems and a library for it.

Finally found the problem.
I added the solution to the post.

Thanks everyone for taking a look

bluegru:
Finally found the problem.
I added the solution to the post.

Then, no one will figure it out if they have the same or similar problem.

Post your solution here, not in the original post.

It's right below the Question,
but I can add it here too

bluegru:
After about 3 hours of painfully adding Serial.println to every line and commenting out different lines I have finally found the two parts of the code that caused the problem. Thanks to anyone who tried to find a solution.

First thing was a minor error in the formula for gameTime, where I subtracted the time since the game started (millis() - gameStartTime), which is in milliseconds, from the time the game should run (gameTime) which is in seconds. This resulted in gameTimeLeft being negative which apparently caused some problems with the 7-Segment library.

After taking care of that, I had no problems with the speed of the calculations, but the display wasn't showing the correct timer. After some research I found that doing calculations with millis() should not involve an int. Changing gameTime to an unsigned long did the trick.

I can still notice some minor lags in the speed of the loop, mostly just the buzzer beeping a few milliseconds longer than it should every now and then, but thats probably because of the terrible code that will most likely be rewritten.

again thanks to everyone for taking a look at this,

bluegru:
It's right below the Question,
but I can add it here too

Thanks. Generally when someone is looking for help with a similar problem, they scroll to the bottom of the thread to see how it was resolved- they won't read the whole thread searching for the solution.