Something stupid here ... help appreciated [SOLVED]

OK, call me stupid :-[ :smiley: I've been beating this to death all day but I can't see my mistake - all advice welcome !

Although my BT and my self-balancing robot code work separately, putting them together fails dismally - the robot balances reasonably well indefinitely, but as soon as I send it a command over BT it falls over, though the motors are still locked. It's as if it gets stuck in the BT loop, or never gets any more MPU6050 interrupts, not sure ...
I'm not understanding something here - please help :slight_smile:

Thanks,

David

#include "I2Cdev.h"
#include <PID_v1.h>
#include "MPU6050_6Axis_MotionApps20.h"
#include <SoftwareSerial.h>

const int HC06_YELLOW=11;
const int HC06_GREEN=12;

SoftwareSerial BT(HC06_YELLOW, HC06_GREEN);

MPU6050 mpu;

bool dmpReady = false; 聽
uint8_t mpuIntStatus;
uint8_t devStatus;
uint16_t packetSize; 聽
uint16_t fifoCount; 
uint8_t fifoBuffer[64];

Quaternion q;
VectorFloat gravity; 
float ypr[3];

int charIdx;
char cmdChar; 
char readChar;
String readString;

double setpoint= 179.5; 
double Kp = 28; 
double Kd = 1.75;
double Ki = 350; 

double input, output;
PID pid(&input, &output, &setpoint, Kp, Ki, Kd, DIRECT);

int enA = 9;
int in1 = 4;
int in2 = 5;

int enB = 10; 
int in3 = 6;
int in4 = 7;

double motorAoffset = 0.75;
double motorBoffset = 1.0;

volatile bool mpuInterrupt = false;

void dmpDataReady()
{
 聽mpuInterrupt = true;
}

void setup() 
{
 聽Serial.begin(115200);
 聽BT.begin(9600);
 聽
 聽 聽Serial.println(F("Initializing I2C devices..."));
 聽 聽mpu.initialize();

 聽 聽Serial.println(F("Testing device connections..."));
 聽 聽Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));

 聽 聽devStatus = mpu.dmpInitialize();

 聽mpu.setXAccelOffset(youroffset)

 聽mpu.setXAccelOffset(-4637);
 聽mpu.setYAccelOffset(605);
 聽mpu.setZAccelOffset(1943);
 聽mpu.setXGyroOffset(110);
 聽mpu.setYGyroOffset(33);
 聽mpu.setZGyroOffset(-4);
 聽
 聽// make sure it worked (returns 0 if so)
 聽if (devStatus == 0)
 聽{
 聽 聽// turn on the DMP, now that it's ready
 聽 聽Serial.println(F("Enabling DMP..."));
 聽 聽mpu.setDMPEnabled(true);

 聽 聽// enable Arduino interrupt detection
 聽 聽Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
 聽 聽attachInterrupt(0, dmpDataReady, RISING);
 聽 聽mpuIntStatus = mpu.getIntStatus();

 聽 聽// set our DMP Ready flag so the main loop() function knows it's okay to use it
 聽 聽Serial.println(F("DMP ready! Waiting for first interrupt..."));
 聽 聽dmpReady = true;

 聽 聽// get expected DMP packet size for later comparison
 聽 聽packetSize = mpu.dmpGetFIFOPacketSize();
 聽 聽 聽 聽
 聽 聽//setup PID
 聽 聽pid.SetMode(AUTOMATIC);
 聽 聽pid.SetSampleTime(10);
 聽 聽pid.SetOutputLimits(-255, 255); 聽
 聽}
 聽else
 聽{
 聽 聽 聽// ERROR!
 聽 聽 聽// 1 = initial memory load failed
 聽 聽 聽// 2 = DMP configuration updates failed
 聽 聽 聽// (if it's going to break, usually the code will be 1)
 聽 聽 聽Serial.print(F("DMP Initialization failed (code "));
 聽 聽 聽Serial.print(devStatus);
 聽 聽 聽Serial.println(F(")"));
 聽}

 聽// Initialise the Motor output pins
 聽pinMode(enA, OUTPUT);
 聽pinMode(in1, OUTPUT);
 聽pinMode(in2, OUTPUT);
 聽
 聽pinMode(enB, OUTPUT);
 聽pinMode(in3, OUTPUT);
 聽pinMode(in4, OUTPUT);

 聽//By default turn off both the motors
 聽Stop();
}

void loop()
{
 聽// if programming failed, don't try to do anything
 聽if (!dmpReady) return;

 聽// wait for MPU interrupt or extra packet(s) available
 聽while (!mpuInterrupt && fifoCount < packetSize)
 聽{
 聽 聽//no mpu data - performing PID calculations and output to motors 聽 聽 
 聽 聽pid.Compute(); 聽 
 聽 聽 聽 聽
 聽 聽//Print the value of Input and Output on serial monitor to check how it is working.
 聽 聽Serial.print(input); Serial.print(" =>"); Serial.println(output);
 聽 聽 聽 聽 聽 聽 聽 
 聽 聽if (input>150 && input<200){//If the Bot is falling 
 聽 聽 聽 聽 聽
 聽 聽 聽if (output>0) //Falling towards front 
 聽 聽 聽 聽Forward(); //Rotate the wheels forward 
 聽 聽 聽else if (output<0) //Falling towards back
 聽 聽 聽 聽Reverse(); //Rotate the wheels backward 
 聽 聽}
 聽 聽else //If Bot not falling
 聽 聽 聽Stop(); //Hold the wheels still

 聽 聽if (BT.available()){ 聽// if text arrived in from Serial serial
 聽 聽 聽charIdx = 0;
 聽 聽 聽while (BT.available()) 
 聽 聽 聽{
 聽 聽 聽 聽delay(3); 聽
 聽 聽 聽 聽readChar = BT.read();
 聽 聽 聽 聽if (charIdx == 0)
 聽 聽 聽 聽 聽cmdChar = readChar;
 聽 聽 聽 聽else
 聽 聽 聽 聽 聽readString += readChar;
 聽 聽 聽 聽charIdx++; 
 聽 聽 聽}
 聽 聽 聽if (readString.length() >0) 
 聽 聽 聽{ 聽

 聽 聽 聽 聽switch (cmdChar){
 聽 聽 聽 聽 聽case 'i':
 聽 聽 聽 聽 聽 聽Ki = readString.toDouble();
 聽 聽 聽 聽 聽 聽break;
 聽 聽 聽 聽 聽case 'd':
 聽 聽 聽 聽 聽 聽Kd = readString.toDouble();
 聽 聽 聽 聽 聽 聽break;
 聽 聽 聽 聽 聽case 'p':
 聽 聽 聽 聽 聽 聽Kp = readString.toDouble();
 聽 聽 聽 聽 聽 聽break;
 聽 聽 聽 聽 聽case 's':
 聽 聽 聽 聽 聽 聽setpoint = readString.toDouble();
 聽 聽 聽 聽 聽 聽break;
 聽 聽 聽 聽 聽case 'A':
 聽 聽 聽 聽 聽 聽motorAoffset = readString.toDouble();
 聽 聽 聽 聽 聽 聽break;
 聽 聽 聽 聽 聽case 'B':
 聽 聽 聽 聽 聽 聽motorBoffset = readString.toDouble();
 聽 聽 聽 聽 聽 聽break;
 聽 聽 聽 聽}
 聽 聽 聽
 聽 聽 聽 聽debugSettings(); 聽 聽 聽 聽

 聽 聽 聽 聽readString=""; 聽
 聽 聽 聽}
 聽 聽 聽BT.println("\n-----------------");
 聽 聽}
 聽}

 聽// reset interrupt flag and get INT_STATUS byte
 聽mpuInterrupt = false;
 聽mpuIntStatus = mpu.getIntStatus();

 聽// get current FIFO count
 聽fifoCount = mpu.getFIFOCount();

 聽// check for overflow (this should never happen unless our code is too inefficient)
 聽if ((mpuIntStatus & 0x10) || fifoCount == 1024)
 聽{
 聽 聽// reset so we can continue cleanly
 聽 聽 聽 聽mpu.resetFIFO();
 聽 聽 聽 聽Serial.println(F("FIFO overflow!"));

 聽 聽// otherwise, check for DMP data ready interrupt (this should happen frequently)
 聽}
 聽else if (mpuIntStatus & 0x02)
 聽{
 聽 聽// wait for correct available data length, should be a VERY short wait
 聽 聽while (fifoCount < packetSize)
 聽 聽 聽fifoCount = mpu.getFIFOCount();

 聽 聽// read a packet from FIFO
 聽 聽mpu.getFIFOBytes(fifoBuffer, packetSize);
 聽 聽 聽 聽
 聽 聽// track FIFO count here in case there is > 1 packet available
 聽 聽// (this lets us immediately read more without waiting for an interrupt)
 聽 聽fifoCount -= packetSize;

 聽 聽mpu.dmpGetQuaternion(&q, fifoBuffer); //get value for q
 聽 聽mpu.dmpGetGravity(&gravity, &q); //get value for gravity
 聽 聽mpu.dmpGetYawPitchRoll(ypr, &q, &gravity); //get value for ypr

 聽 聽input = ypr[2] * 180/M_PI + 180;
 聽}
}

void debugSettings()
{
 聽BT.print("setpoint: 聽 聽 "); BT.println(setpoint);
 聽BT.print("yaw: 聽 聽 聽 聽 聽"); BT.println(ypr[1]);
 聽BT.print("pitch: 聽 聽 聽 聽"); BT.println(ypr[2]);
 聽BT.print("Kp: 聽 聽 聽 聽 聽 "); BT.println(Kp);
 聽BT.print("Kd: 聽 聽 聽 聽 聽 "); BT.println(Kd);
 聽BT.print("Ki: 聽 聽 聽 聽 聽 "); BT.println(Ki);
 聽BT.print("motorAoffset: "); BT.println(motorAoffset);
 聽BT.print("motorBoffset: "); BT.println(motorBoffset);
}
void Forward() // Code to rotate the wheel forward 
{
 聽 聽// Set Motor A forward
 聽digitalWrite(in1, LOW);
 聽digitalWrite(in2, HIGH);
 聽
 聽// Set Motor B forward
 聽digitalWrite(in3, LOW);
 聽digitalWrite(in4, HIGH);

 聽analogWrite(enA, output*motorAoffset); // Send PWM signal to motor A
 聽analogWrite(enB, output*motorBoffset); // Send PWM signal to motor B
}

void Reverse() // Code to rotate the wheel Backward 聽
{
 聽 聽// Set Motor A backward
 聽digitalWrite(in1, HIGH);
 聽digitalWrite(in2, LOW);
 聽
 聽// Set Motor B backward
 聽digitalWrite(in3, HIGH);
 聽digitalWrite(in4, LOW);

 聽analogWrite(enA, output*motorAoffset*-1); // Send PWM signal to motor A
 聽analogWrite(enB, output*motorBoffset*-1); // Send PWM signal to motor B
}

void Stop() // Code to stop both the wheels
{
 聽 聽// Set Motor A stop
 聽digitalWrite(in1, LOW);
 聽digitalWrite(in2, LOW);
 聽
 聽// Set Motor B stop
 聽digitalWrite(in3, LOW);
 聽digitalWrite(in4, LOW);

 聽analogWrite(enA, 0); // Send PWM signal to motor A
 聽analogWrite(enB, 0); // Send PWM signal to motor B
}

An easy to be SURE if the cause is the BT call is to time the call. Get and store the millis before the call. Get the millis after the call. Subtract and Serial.println() the difference.

Paul

Hmm, not sure what that's telling me but I put in the suggested timings and it just prints out "207 ms" and then doesn't respond to any further input, either BT commands or change of orientation/MPU6050 updates, the wheels just continue spinning.

...
聽  else //If Bot not falling
聽 聽 聽 Stop(); //Hold the wheels still

聽 聽 if (BT.available()){聽 // if text arrived in from Serial serial
聽 聽 聽 t1=millis();
聽 聽 聽 charIdx = 0;
聽 聽 聽 while (BT.available()) 
聽 聽 聽 {
聽 聽 聽 聽 delay(3);聽 
聽 聽 聽 聽 readChar = BT.read();
聽 聽 聽 聽 if (charIdx == 0)
聽 聽 聽 聽 聽 cmdChar = readChar;
聽 聽 聽 聽 else
聽 聽 聽 聽 聽 readString += readChar;
聽 聽 聽 聽 charIdx++; 
聽 聽 聽 }
聽 聽 聽 if (readString.length() >0) 
聽 聽 聽 {聽 

聽 聽 聽 聽 switch (cmdChar){
聽 聽 聽 聽 聽 case 'i':
聽 聽 聽 聽 聽 聽 Ki = readString.toDouble();
聽 聽 聽 聽 聽 聽 break;
聽 聽 聽 聽 聽 case 'd':
聽 聽 聽 聽 聽 聽 Kd = readString.toDouble();
聽 聽 聽 聽 聽 聽 break;
聽 聽 聽 聽 聽 case 'p':
聽 聽 聽 聽 聽 聽 Kp = readString.toDouble();
聽 聽 聽 聽 聽 聽 break;
聽 聽 聽 聽 聽 case 's':
聽 聽 聽 聽 聽 聽 setpoint = readString.toDouble();
聽 聽 聽 聽 聽 聽 break;
聽 聽 聽 聽 聽 case 'A':
聽 聽 聽 聽 聽 聽 motorAoffset = readString.toDouble();
聽 聽 聽 聽 聽 聽 break;
聽 聽 聽 聽 聽 case 'B':
聽 聽 聽 聽 聽 聽 motorBoffset = readString.toDouble();
聽 聽 聽 聽 聽 聽 break;
聽 聽 聽 聽 }
聽 聽 聽 
聽 聽 聽 聽 debugSettings();聽 聽 聽 聽 

聽 聽 聽 聽 readString="";聽 
聽 聽 聽 }
聽 聽  
聽 聽 聽 t2=millis();
聽 聽 聽 Serial.print(t2-t1); Serial.println(" ms");
聽 聽 }

It is not a good idea to use the String (capital S) class on an Arduino as it can cause memory corruption in the small memory on an Arduino. This can happen after the program has been running perfectly for some time. Just use cstrings - char arrays terminated with '\0' (NULL).

Have a look at the examples in Serial Input Basics - simple reliable ways to receive data. There is also a parse example to illustrate how to extract numbers from the received text.

I suggest you ensure that every message sent to the Arduino is less than 64 bytes long so it fits in the Serial Input Buffer. Then (if you use my examples) you can call the function to retrieve the data AFTER every PID calculation. That way reading the serial data should not interfere with the balancing process.

What is the interval between your PID calculations ?

...R

Thanks Robin,

I converted the sketch to use a null-terminated char array and atof() to convert the input to double but get the same problem - process the received chars, robot falls over. I've checked all the obvious stuff - no buffer overruns or buggy indexing, and the received characters are being converted to the correct values.

pid.Compute() is being called every 1-2ms under "normal" circumstances, i.e. before I send any data to the BT to trigger the if (BT.available()).

Thanks for the samples - I'm off to read through that now, maybe I'll spot what I'm doing wrong.

David

Just an update ... I'm sure the BT reading code I've written is fine, but just in case I commented it all out and replaced it with

聽 聽 if (BT.available()){聽 // if text arrived in from Serial serial
聽 聽  while (BT.available())
聽 聽 聽 Serial.println(BT.read()); 
聽 聽 }

That's enough to cause the problem, which smells like a memory overwrite or an electrical problem or something. I've double and triple-checked all my wiring. The robot balances well until it gets input on the BT lines then it freezes. I've tried an Arduino and an Elgoo board - same problem on both, though the SoftwareSerial just doesn't work on the Elgoo (all characters except the first one are corrupted) so I used the hardware serial on that one.

I also discovered that Serial.begin(115200) is the only baud rate that works - 9600 or 19200 seem to stop the MPU6050 ever sending any interrupts. I tried two different MPU6050 boards - both the same.

So now I'm completely stumped ...

The Serial.print() statement is very slow.

The Arduino PID library is also very slow because it uses floating point maths. If you are only allowing 1 or 2 millisecs between calls to the library you may be just trying to get your Arduino to do too much.

Can you increase the PID interval to (say) 10 millisecs and see if that helps?

...R

dbdbdb:
OK, call me stupid :-[ :smiley: I've been beating this to death all day but I can't see my mistake - all advice welcome !

Its stupid not to include even a clue in your post title as to what the subject of your post is.

Thanks, great help srnet :slight_smile:

I think you're right Robin - it looks like it's all about the timing. I found loads of other pages reporting similar problems with Serial and MPU6050 together.

It looks like the FIFO buffer on the MPU6050 is getting smashed unless it's emptied fast enough. Just a delay(10) in the loop() will kill it, just the same as anything else that takes up time. So this combination that I'm using is OK for a robot that just balances but it won't work if I want to do anything else useful at the same time.

It might just it's a choice between either playing around trying to shave microseconds every time I add a new device to it, or move to a faster processor. I wanted to add an SR-04 next but I don't think that's a viable option. Maybe I need to ditch the PID library I'm using or something, either way I need to do a lot more research.

Thanks for your help anyway :slight_smile:

David

Do you really need to call PID every millisec? I can't imagine that your robot would fall far in 20 millisecs.

...R

Yes it does sound excessive, and following your suggestion I found that it can do with less for example if I check no less than every 15ms with this:

 if (millis()-last_pid > 15){
聽 聽 聽 pid.Compute();
聽 聽 聽 last_pid=millis();
聽 聽 聽 Serial.println("pid.Compute()");
聽 聽 }

then it just about balances but is very jittery, though I might be able to iron out the jitters by adjusting the PID values. 10ms is fine and 20ms is too much for it to compensate for.

This is helpful though, as it shows that I can free up some more processing time. It's also interesting that with a 10ms sampling rate I don't need to run the Serial port at 115200 baud - it works fine at 38400.

However the problem is unchanged - as soon as I send a character and trigger the BT read code

if (BT.available()){
聽 聽 聽 while (BT.available()) 
聽 聽 聽 聽 Serial.print(BT.read());
聽 聽 聽 Serial.println("");
}

the robot goes into freefall. The motors continue doing whatever they were doing before it crashed, i.e. stopped or spinning. Sometimes I get the message "FIFO overflow" displayed, but sometimes I think it crashes before it can output this. My suspicion is that some buffer is filling faster than it can be emptied, and is overflowing and corrupting the contents of the memory. I'll keep digging ...

dbdbdb:
However the problem is unchanged - as soon as I send a character and trigger the BT read code

if (BT.available()){

while (BT.available())
        Serial.print(BT.read());
      Serial.println("");
}

That code provoked a WTF response in my head.

What is the value of receiving a message (actually a single character) and then sending it out without ever using it?

...R

Yes, I mentioned earlier that this is a cut down version of my full code - if you remember I was originally reading into a String but then changed this on your advice to a cstring. The code then parses the received characters to set the Kp, Ki, Kd etc. parameters.

However I reduced the code to an absolute minimum to see which instruction triggers the problem, and in fact the "if BT.available()" is superfluous here too - the "while"/"read" loop is enough to make it happen.

dbdbdb:
Thanks, great help srnet :slight_smile:

But you did not change the title of your first post.

From the how to use this forum post;

6. Make a meaningful subject line

Try to summarize your problem with a helpful and informative subject line. Helpful subjects draw in people who might know the answer. Unhelpful ones are likely to be skipped.

Examples of unhelpful subject lines:

Noob here, help needed
Help me, quickly!
Got error
Lost, help me
Problem with my circuit
No idea what I am doing
Something stupid here ... help appreciated

I read a lot, and learned a lot.

In case it helps anyone in future who is trying to build a self-balancing robot with the same hardware as me (Uno, Lynxmotion dual H-bridge, couple of 12V 400rpm DC motors, MPU6050, HC-06, diymore power bank shield), this is the information and the code I've ended up with, which allows me to achieve my goal of tweaking the Kp, Ki, Kd, setpoint and motor offsets over bluetooth.

I still have a minor issue to solve - occasionally the robot still falls over when I read data, but it's recoverable. It seems it temporarily spends more resources reading data than staying balanced so it falls out of its tolerance zone too fast to recover itself, but it does carry on when I just stand it up vertically again. This is infinitely preferable to the original problem where the first call to BT.read() sent it into an endless loop that it never recovered from no matter what I did. I just need to balance the "balancing vs doing other stuff" times :slight_smile:

There seem to be a number of fundamental issues that together make the combination of Wire, i2cdevlib and the MPU6050 device somewhat fragile. Timing seems to play a major part, and there are some suggestions that it's possible for the hardware to get into a state that the core libraries don't handle, which result in an endless loop where an i2c reset might be more desirable in particular use cases like self-balancing robots/quadcopters. Others have suggested that the MPU code does unexpected things. Ultimately I couldn't find anything conclusive but some great investigative work has been done (thanks esp. to jrowberg , zhomeslice and batata004.

The discussion on Issues 路 jrowberg/i2cdevlib 路 GitHub is illuminating (in my case this one especially MPU6050 with DMP active hangs indefinitely using Arduino Wire library 路 Issue #252 路 jrowberg/i2cdevlib 路 GitHub), as are the threads Good news!!! DMP from MPU6050 can be used without interrupt pin - Programming Questions - Arduino Forum and MPU6050 freezing/crashing - Programming Questions - Arduino Forum.

All the best,

David

My working code:

#include "I2Cdev.h"
#include <PID_v1.h>
#include "MPU6050_6Axis_MotionApps20.h" 
#include <SoftwareSerial.h>

MPU6050 mpu;

// MPU control/status vars
bool dmpReady = false; 聽// set true if DMP init was successful
uint8_t mpuIntStatus; 聽 // holds actual interrupt status byte from MPU
uint8_t devStatus; 聽 聽 聽// return status after each device operation (0 = success, !0 = error)
uint16_t packetSize; 聽 聽// expected DMP packet size (default is 42 bytes)
uint16_t fifoCount; 聽 聽 // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer

// orientation/motion vars
Quaternion q; 聽 聽 聽 聽 聽 // [w, x, y, z] 聽 聽 聽 聽 quaternion container
VectorFloat gravity; 聽 聽// [x, y, z] 聽 聽 聽 聽 聽 聽gravity vector
float ypr[3]; 聽 聽 聽 聽 聽 // [yaw, pitch, roll] 聽 yaw/pitch/roll container and gravity vector

const int HC06_YELLOW=11; // pin 11 is Arduino RX
const int HC06_GREEN=12; 聽// pin 12 is Arduino TX

SoftwareSerial BT(HC06_YELLOW, HC06_GREEN); // Arduino RX to BT TX, TX 聽-> BT RX 

int charIdx;
char cmdChar; 聽 // last command character read (zero-th input) 聽 
char readChar; // stores incoming character from other device
String readString;

double setpoint=170; //set the value when the bot is perpendicular to ground using serial monitor. 

double Kp = 25;
double Kd = 1.8;
double Ki = 400; 

double input, output;
PID pid(&input, &output, &setpoint, Kp, Ki, Kd, DIRECT);

unsigned long t1 = millis();

// MOTOR CONTROLLER
// enA in1 in2 Result
// 聽L 聽N/A N/A Power off
// 聽H 聽 L 聽 L 聽Stop (Brake)
// 聽H 聽 H 聽 L 聽CW (Fast)
// 聽H 聽 L 聽 H 聽CCW (Fast)
// 聽H 聽 H 聽 H 聽Stop (Brake)
// 聽P 聽 H 聽 L 聽CW (Slow)
// 聽P 聽 L 聽 H 聽CCW (Slow)

int enA = 9;
int in1 = 4;
int in2 = 5;

int enB = 10; 
int in3 = 6;
int in4 = 7;

double motorAoffset = 0.96;
double motorBoffset = 1.0;

volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high

void dmpDataReady()
{
 聽mpuInterrupt = true;
}

void setup() 
{
 聽Wire.begin();
 聽TWBR = 24;

 聽Serial.begin(115200);
 聽while (!Serial);

 聽BT.begin(9600);

 聽Serial.println(F("Initialising I2C devices..."));
 聽mpu.initialize();

 聽Serial.println(F("Testing device connections..."));
 聽Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));

 聽mpu.setXAccelOffset(-4637);
 聽mpu.setYAccelOffset(605);
 聽mpu.setZAccelOffset(1943);
 聽mpu.setXGyroOffset(110);
 聽mpu.setYGyroOffset(33);
 聽mpu.setZGyroOffset(-4);
 聽
 聽devStatus = mpu.dmpInitialize();

 聽if (devStatus == 0)
 聽{
 聽 聽// turn on the DMP, now that it's ready
 聽 聽Serial.println(F("Enabling DMP..."));
 聽 聽mpu.setDMPEnabled(true);

 聽 聽Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
 聽 聽attachInterrupt(0, dmpDataReady, RISING);
 聽 聽mpuIntStatus = mpu.getIntStatus();

 聽 聽Serial.println(F("DMP ready! Waiting for first interrupt..."));
 聽 聽dmpReady = true;

 聽 聽packetSize = mpu.dmpGetFIFOPacketSize();
 聽 聽 聽 聽
 聽 聽//setup PID
 聽 聽pid.SetMode(AUTOMATIC);
 聽 聽pid.SetSampleTime(10);
 聽 聽pid.SetOutputLimits(-255, 255); 聽
 聽}
 聽else
 聽{
 聽 聽Serial.print(F("DMP Initialization failed (code "));
 聽 聽Serial.print(devStatus);
 聽 聽Serial.println(F(")"));
 聽}

 聽pinMode(enA, OUTPUT);
 聽pinMode(in1, OUTPUT);
 聽pinMode(in2, OUTPUT);
 聽
 聽pinMode(enB, OUTPUT);
 聽pinMode(in3, OUTPUT);
 聽pinMode(in4, OUTPUT);

 聽Stop();
}

void loop()
{
 聽if (!dmpReady) return;

 聽while (!mpuInterrupt) {

 聽 聽pid.Compute();
 聽 聽 聽 聽
 聽 聽// If falling take corrective action
 聽 聽 聽 聽 聽 聽 聽 
 聽 聽if (input > 150 && input < 200){ 
 聽 聽 聽if (output > 0) 聽 聽 聽 // Falling towards front 
 聽 聽 聽 聽Forward(); 聽 聽 聽 聽 聽// Rotate the wheels forward 
 聽 聽 聽else if (output < 0) 聽// Falling towards back
 聽 聽 聽 聽Reverse(); 聽 聽 聽 聽 聽// Rotate the wheels backward 
 聽 聽}
 聽 聽else 聽 聽 聽// not falling or fallen too far to recover
 聽 聽 聽Stop(); // so hold the wheels still

 聽 聽if (BT.available())
 聽 聽 聽readInput();
 聽}

 聽// got interrupt - MPU data may be available
 聽mpuInterrupt = false;
 聽fifoCount = mpu.getFIFOCount();
 聽 聽
 聽if (fifoCount == 1024) {
 聽 聽mpu.resetFIFO();
 聽 聽Serial.println(F("FIFO overflow!"));
 聽}
 聽else {
 聽 聽if (fifoCount % packetSize != 0) {
 聽 聽 聽mpu.resetFIFO();
 聽 聽 聽Serial.println(F("Packet is corrupted!"));
 聽 聽}
 聽 聽else {
 聽 聽 聽while (fifoCount >= packetSize) {
 聽 聽 聽 聽mpu.getFIFOBytes(fifoBuffer,packetSize);
 聽 聽 聽 聽fifoCount -= packetSize;
 聽 聽 聽} 聽 聽
 聽 聽 聽mpu.dmpGetQuaternion(&q,fifoBuffer);
 聽 聽 聽mpu.dmpGetGravity(&gravity,&q);
 聽 聽 聽mpu.dmpGetYawPitchRoll(ypr,&q,&gravity); 聽 聽 聽 聽 聽
 聽 聽 聽 聽 聽 
 聽 聽 聽// my MPU is mounted sideways so use ypr[2] for pitch
 聽 聽 聽input = ypr[2] * 180/M_PI + 180;
 聽 聽}
 聽}
}

void readInput()
{ 聽
 聽charIdx = 0;

 聽while (BT.available()) 
 聽{
 聽 聽readChar = BT.read();
 聽 聽if (charIdx == 0)
 聽 聽 聽cmdChar = readChar;
 聽 聽else
 聽 聽 聽readString += readChar;
 聽 聽charIdx++; 
 聽}
 聽if (readString.length() >0) 
 聽{ 聽
 聽 聽switch (cmdChar){
 聽 聽 聽case 'i':
 聽 聽 聽 聽Ki = readString.toDouble();
 聽 聽 聽 聽break;
 聽 聽 聽case 'd':
 聽 聽 聽 聽Kd = readString.toDouble();
 聽 聽 聽 聽break;
 聽 聽 聽case 'p':
 聽 聽 聽 聽Kp = readString.toDouble();
 聽 聽 聽 聽break;
 聽 聽 聽case 's':
 聽 聽 聽 聽setpoint = readString.toDouble();
 聽 聽 聽 聽break;
 聽 聽 聽case 'A':
 聽 聽 聽 聽motorAoffset = readString.toDouble();
 聽 聽 聽 聽break;
 聽 聽 聽case 'B':
 聽 聽 聽 聽motorBoffset = readString.toDouble();
 聽 聽 聽 聽break;
 聽 聽}
 聽 聽debugSettings(); 聽 聽 聽 聽
 聽 聽readString=""; 聽
 聽}
}

void debugSettings()
{
 聽BT.print("setpoint: 聽 聽 "); BT.println(setpoint);
 聽BT.print("yaw: 聽 聽 聽 聽 聽"); BT.println(ypr[1]);
 聽BT.print("pitch: 聽 聽 聽 聽"); BT.println(ypr[2]);
 聽BT.print("Kp: 聽 聽 聽 聽 聽 "); BT.println(Kp);
 聽BT.print("Kd: 聽 聽 聽 聽 聽 "); BT.println(Kd);
 聽BT.print("Ki: 聽 聽 聽 聽 聽 "); BT.println(Ki);
 聽BT.print("motorAoffset: "); BT.println(motorAoffset);
 聽BT.print("motorBoffset: "); BT.println(motorBoffset);
 聽BT.println("\n-----------------");
}

void Forward() // rotate wheels forward 
{
 聽 聽// Motor A
 聽digitalWrite(in1, LOW);
 聽digitalWrite(in2, HIGH);
 聽
 聽// Motor B
 聽digitalWrite(in3, LOW);
 聽digitalWrite(in4, HIGH);

 聽analogWrite(enA, output*motorAoffset);
 聽analogWrite(enB, output*motorBoffset);
}

void Reverse() // rotate wheels Backward 聽
{
 聽digitalWrite(in1, HIGH);
 聽digitalWrite(in2, LOW);
 聽
 聽digitalWrite(in3, HIGH);
 聽digitalWrite(in4, LOW);

 聽analogWrite(enA, output*motorAoffset*-1);
 聽analogWrite(enB, output*motorBoffset*-1);
}

void Stop() // stop both wheels
{
 聽digitalWrite(in1, LOW);
 聽digitalWrite(in2, LOW);
 聽
 聽digitalWrite(in3, LOW);
 聽digitalWrite(in4, LOW);

 聽analogWrite(enA, 0);
 聽analogWrite(enB, 0);
}