#define BLYNK_TEMPLATE_ID "TMPL6_NeSrwZR"
#define BLYNK_TEMPLATE_NAME "SBR"
#define BLYNK_AUTH_TOKEN "YL3I1G5pwTiagFxBlMnTKt4rdY0kmbwl"
#define BLYNK_PRINT Serial

#include <WiFi.h>
#include <Wire.h>
#include <MPU6050_tockn.h>
#include <BlynkSimpleEsp32.h>
#include <PID_v1.h>

char auth[] = BLYNK_AUTH_TOKEN;
char ssid[] = "Wokwi-GUEST";
char pass[] = "";

MPU6050 mpu6050(Wire);

double Setpoint = 0;  // Setpoint for balancing
double Input, Output;

double Kp;
double Ki;
double Kd;

PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);

// Motor control pins
int leftStepPin = 32;
int leftDirPin = 33;
int rightStepPin = 14;
int rightDirPin = 12;
int enable = 19;

// Motor control state
enum MotorState {
  MOTOR_IDLE,
  MOTOR_STEP_HIGH,
  MOTOR_STEP_LOW
};
MotorState motorState = MOTOR_IDLE;
unsigned long motorLastStepTime = 0;
const int motorStepInterval = 10000; // microseconds

// Define a maximum speed limit for the motor
const int maxMotorSpeed = 500; // Adjust this value based on your requirements

BLYNK_WRITE(V9) {
  Kp = param.asDouble();
}

BLYNK_WRITE(V10) {
  Ki = param.asDouble();
}

BLYNK_WRITE(V11) {
  Kd = param.asDouble();
}

void setup() {
  Serial.begin(9600);
  Blynk.begin(auth, ssid, pass);
  Wire.begin();
  mpu6050.begin();
  mpu6050.calcGyroOffsets(true);

  pinMode(leftStepPin, OUTPUT);
  pinMode(leftDirPin, OUTPUT);
  pinMode(rightStepPin, OUTPUT);
  pinMode(rightDirPin, OUTPUT);
  pinMode(enable, OUTPUT);
  digitalWrite(enable, LOW);

  myPID.SetOutputLimits(-500, 500);
  myPID.SetSampleTime(10);
  myPID.SetMode(AUTOMATIC);
}

void loop() {
  Blynk.run();

  mpu6050.update();
  double pitch = mpu6050.getAngleY(); // Tilt angle
  double gyroY = mpu6050.getGyroY(); // Gyro Y output

  // Sensor fusion for PID input
  Input = pitch; // Adjust the ratio based on testing

  myPID.SetTunings(Kp, Ki, Kd);
  myPID.Compute();

  // Non-blocking motor control
  unsigned long currentTime = micros();
  if (currentTime - motorLastStepTime >= motorStepInterval) {
    motorLastStepTime = currentTime;

    // Inversely scale the PID output for motor speed
    int motorSpeed = map(abs(Output*2), 0, 500, maxMotorSpeed, 0);

    // Limit the motor speed to the maximum defined
    motorSpeed = min(motorSpeed, maxMotorSpeed);

    // Threshold for considering the output as zero
    const double zeroThreshold = 5.0;

    // Check if the PID output is close enough to zero
    if (abs(Output) < zeroThreshold) {
      digitalWrite(enable, HIGH); // Stop the motors
    } else {
      digitalWrite(enable, LOW); // Enable the motors
      digitalWrite(leftDirPin, Output < 0 ? HIGH : LOW);
      digitalWrite(rightDirPin, Output > 0 ? HIGH : LOW);
      analogWrite(leftStepPin, motorSpeed);
      analogWrite(rightStepPin, motorSpeed);
    }
  }

  // Reduced frequency of Serial output for debugging
  static unsigned long lastPrintTime = 0;
  if (millis() - lastPrintTime > 500) {
    Serial.print("Pitch: "); Serial.print(pitch);
    Serial.print(" | Gyro Y: "); Serial.print(gyroY);
    Serial.print(" | PID Input: "); Serial.print(Input);
    Serial.print(" | Output: "); Serial.println(Output);
    lastPrintTime = millis();
  }
}
A4988
A4988