Problem acquiring reliable data from MPU6050 sensor (Kalman filter?)

Hey everyone I'm trying to create an Arduino module that is connected to an MPU6050 accellerometer + gyroscope sensor and I need for it to digital print (roll/pitch/yaw) for me to simulate it's position using processing.
The code overall works but the data is not reliable, the "yaw" drifts a bit even when the sensor is steady but all of them drift a lot when I move the sensor and then I position it back on a flat surface.
This is the code I'm using:

#include <Wire.h>

const int MPU = 0x68; // MPU6050 I2C address
const int BUTTON_PIN = 4;  // To reset and calibrate the accelerometer and gyro with a push button

float AccX, AccY, AccZ;
float GyroX, GyroY, GyroZ;
float accAngleX, accAngleY, gyroAngleX, gyroAngleY, gyroAngleZ;
float roll, pitch, yaw;
float AccErrorX, AccErrorY, GyroErrorX, GyroErrorY, GyroErrorZ;
float elapsedTime, currentTime, previousTime;
int c = 0;
int ledPin = 2; // Define the pin connected to the transistor's base (To activate a 5V laser Light)

// New variables to handle calibration
int buttonState = 0;
int lastButtonState = 0;
bool doCalibration = false;

void setup() {
  pinMode(BUTTON_PIN, INPUT);
  pinMode(ledPin, OUTPUT);

 Serial.begin(19200);
  Wire.begin();                      // Initialize comunication
  Wire.beginTransmission(MPU);       // Start communication with MPU6050 // MPU=0x68
  Wire.write(0x6B);                  // Talk to the register 6B
  Wire.write(0x00);                  // Make reset - place a 0 into the 6B register
  Wire.endTransmission(true);        //end the transmission
  
  /*
  // Configure Accelerometer Sensitivity - Full Scale Range (default +/- 2g)
  Wire.beginTransmission(MPU);
  Wire.write(0x1C);                  //Talk to the ACCEL_CONFIG register (1C hex)
  Wire.write(0x10);                  //Set the register bits as 00010000 (+/- 8g full scale range)
  Wire.endTransmission(true);
  // Configure Gyro Sensitivity - Full Scale Range (default +/- 250deg/s)
  Wire.beginTransmission(MPU);
  Wire.write(0x1B);                   // Talk to the GYRO_CONFIG register (1B hex)
  Wire.write(0x10);                   // Set the register bits as 00010000 (1000deg/s full scale)
  Wire.endTransmission(true);
  delay(20);
  */
  // Call this function if you need to get the IMU error values for your module
  calculate_IMU_error();
  delay(20);
}

void loop() {
    buttonState = digitalRead(BUTTON_PIN); //I added this code because the accellerometer mesurment were not stable but the aim would be to remove it
  if (buttonState == HIGH && lastButtonState == LOW) {
    doCalibration = true;
  }
  lastButtonState = buttonState;

  if (doCalibration) {
    calculate_IMU_error();
    doCalibration = false;
    gyroAngleX = gyroAngleY = yaw = 0; // resetting angles when calibrating
  }
  // === Read acceleromter data === //
 digitalWrite(ledPin, HIGH);
   Wire.beginTransmission(MPU);
  Wire.write(0x3B); // Start with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
  //For a range of +-2g, we need to divide the raw values by 16384, according to the datasheet
  AccX = (Wire.read() << 8 | Wire.read()) / 16384.0; // X-axis value
  AccY = (Wire.read() << 8 | Wire.read()) / 16384.0; // Y-axis value
  AccZ = (Wire.read() << 8 | Wire.read()) / 16384.0; // Z-axis value
  // Calculating Roll and Pitch from the accelerometer data
  accAngleX = (atan(AccY / sqrt(pow(AccX, 2) + pow(AccZ, 2))) * 180 / PI) + 0.00 ; // AccErrorX ~(0.58) See the calculate_IMU_error()custom function for more details
  accAngleY = (atan(-1 * AccX / sqrt(pow(AccY, 2) + pow(AccZ, 2))) * 180 / PI) + 0.00; // AccErrorY ~(-1.58)
  // === Read gyroscope data === //
  previousTime = currentTime;        // Previous time is stored before the actual time read
  currentTime = millis();            // Current time actual time read
  elapsedTime = (currentTime - previousTime) / 1000; // Divide by 1000 to get seconds
  Wire.beginTransmission(MPU);
  Wire.write(0x43); // Gyro data first register address 0x43
  Wire.endTransmission(false);
  Wire.requestFrom(MPU, 6, true); // Read 4 registers total, each axis value is stored in 2 registers
  GyroX = (Wire.read() << 8 | Wire.read()) / 131.0; // For a 250deg/s range we have to divide first the raw value by 131.0, according to the datasheet
  GyroY = (Wire.read() << 8 | Wire.read()) / 131.0;
  GyroZ = (Wire.read() << 8 | Wire.read()) / 131.0;
  // Correct the outputs with the calculated error values
  GyroX = GyroX +0.99; // GyroErrorX ~(-0.56)
  GyroY = GyroY +0.20; // GyroErrorY ~(2)
  GyroZ = GyroZ -0.01; // GyroErrorZ ~ (-0.8)
  // Currently the raw values are in degrees per seconds, deg/s, so we need to multiply by sendonds (s) to get the angle in degrees
  gyroAngleX = gyroAngleX + GyroX * elapsedTime; // deg/s * s = deg
  gyroAngleY = gyroAngleY + GyroY * elapsedTime;
  yaw =  yaw + GyroZ * elapsedTime;
  // Complementary filter - combine acceleromter and gyro angle values
  roll = 0.96 * gyroAngleX + 0.04 * accAngleX;
  pitch = 0.96 * gyroAngleY + 0.04 * accAngleY;
  
  // Print the values on the serial monitor
  Serial.print(roll);
  Serial.print("/");
  Serial.print(pitch);
  Serial.print("/");
  Serial.println(yaw);
}
void calculate_IMU_error() {
  // We can call this funtion in the setup section to calculate the accelerometer and gyro data error. From here we will get the error values used in the above equations printed on the Serial Monitor.
  // Note that we should place the IMU flat in order to get the proper values, so that we then can the correct values
  // Read accelerometer values 200 times
  while (c < 200) {
    Wire.beginTransmission(MPU);
    Wire.write(0x3B);
    Wire.endTransmission(false);
    Wire.requestFrom(MPU, 6, true);
    AccX = (Wire.read() << 8 | Wire.read()) / 16384.0 ;
    AccY = (Wire.read() << 8 | Wire.read()) / 16384.0 ;
    AccZ = (Wire.read() << 8 | Wire.read()) / 16384.0 ;
    // Sum all readings
    AccErrorX = AccErrorX + ((atan((AccY) / sqrt(pow((AccX), 2) + pow((AccZ), 2))) * 180 / PI));
    AccErrorY = AccErrorY + ((atan(-1 * (AccX) / sqrt(pow((AccY), 2) + pow((AccZ), 2))) * 180 / PI));
    c++;
  }
  //Divide the sum by 200 to get the error value
  AccErrorX = AccErrorX / 200;
  AccErrorY = AccErrorY / 200;
  c = 0;
  // Read gyro values 200 times
  while (c < 200) {
    Wire.beginTransmission(MPU);
    Wire.write(0x43);
    Wire.endTransmission(false);
    Wire.requestFrom(MPU, 6, true);
    GyroX = Wire.read() << 8 | Wire.read();
    GyroY = Wire.read() << 8 | Wire.read();
    GyroZ = Wire.read() << 8 | Wire.read();
    // Sum all readings
    GyroErrorX = GyroErrorX + (GyroX / 131.0);
    GyroErrorY = GyroErrorY + (GyroY / 131.0);
    GyroErrorZ = GyroErrorZ + (GyroZ / 131.0);
    c++;
  }
  //Divide the sum by 200 to get the error value
  GyroErrorX = GyroErrorX / 200;
  GyroErrorY = GyroErrorY / 200;
  GyroErrorZ = GyroErrorZ / 200;
  // Print the error values on the Serial Monitor
  Serial.print("AccErrorX: ");
  Serial.println(AccErrorX);
  Serial.print("AccErrorY: ");
  Serial.println(AccErrorY);
  Serial.print("GyroErrorX: ");
  Serial.println(GyroErrorX);
  Serial.print("GyroErrorY: ");
  Serial.println(GyroErrorY);
  Serial.print("GyroErrorZ: ");
  Serial.println(GyroErrorZ);
}

I added a calibration button so that when I press it all the parameters go back to 0 and I can calibrate it but that only works on a flat surface and I have to do it every few seconds after that it gets messed up again so it's not practical.

I think I could try to use a Kalman filter but I don't know how to include it in my code.

With 6DOF sensors like the MPU-6050, there is no horizon reference, so the yaw angle is relative to the starting orientation.

Unless the gyro is calibrated and offsets removed, then the output angles will drift.

Most people collect a few hundred gyro readings at startup (with the sensor held still), average them, and subtract the averages from later gyro readings taken during normal operation. It looks like the code you posted does something like that, but I did not check it for validity.

The ancient complementary filter is not valid for large changes in orientation and has never worked well. I suggest to try the Mahony filter posted here.

Thanks I will try that !
Is there any sensor that is cost effective but also appropriate for my project?
Maybe a BNO055 like this one ?

https://amzn.eu/d/59M1nvy

I need it to be always as accurate as possible even if the sensor goes through 360 degrees changes.
A friend of mine suggested me I need to plug the mpu6050 sensor to a different current source rather then directly to Arduino, do you think that's a thing ?

The BNO055 is quite old and does not work very well.

The MPU-6050 has long been obsolete and what you have is probably a clone. Also, it is a 3.3V sensor and should not be connected to 5V Arduinos without using logic level shifters.

There are many other, modern and much improved 6- and 9-DOF sensors available from Adafruit, Sparkfun, Pololu and the like.

Thanks ! Do you have experience with one of them/have a favourite? Or know for which of them I'm more likely to find libraries and examples because I'm a complete newbie.