Problems with using MPU6050 DMP and I2Cdevlib

I've been trying to use the DMP functionality of the MPU6050 by using the I2Cdev library. I've consistently been getting error messages that seem to indicate that the MPU6050 class doesn't have the DMP commands which I'm using. The interesting thing is that the MPU6050_DMP6 example code compiles fine, and that I'm using most of the same code from this DMP example.

I'll post my error messages and code below because I exceeded 9000 characters.

Thanks for the help.

Here’s my code:

#include <helper_3dmath.h>
#include <MPU6050.h>
#include <MPU6050_6Axis_MotionApps20.h>
#include <MPU6050_9Axis_MotionApps41.h>

#include <I2Cdev.h>



#include "Servo.h"

#include "Wire.h"



#define ServoLpin 9
#define ServoRpin 10
#define PitchIn 8
#define YawIn 7
#define RollIn 6
#define ThrottleIn 5
#define MotorLpin 4
#define MotorRpin 3
#define interrupt 2
//defining all the pins up here in case I want to change wiring

#define YAW_MAX_ANGLE 50
#define PITCH_MAX_ANGLE 50
//Max allowable pitch and yaw angles in hover mode, in degrees

#define ROLL_MAX_RATE 30
//Max roll rate. The gyro's values are in degrees per second by default, meaning this is as well. 

//==========================================================================
//PID variables and constants
float pitchErrorSum, yawErrorSum, rollRateSum;
unsigned long elapsedTime, oldElapsedTime, dT;
float correctionPitch, correctionYaw, correctionRoll;
#define Kpp 1
#define Kpy 1
#define Kpr 1
#define Kip 1
#define Kiy 1
#define Kir 1
//defining Kp and Ki up here so I can easily change them
#define plimit 30
#define ylimit 30
//defining the pitch and yaw limits (i.e. the angle of the plane with a stick pushed to max)in degrees

//===========================================================================
//MPU 6050 I2C STUFF
uint8_t FIFObuffer[64];
//array used for storing the values of the MPU's FIFO buffer
uint16_t FIFOcount;
//stores number of bytes currently in FIFO buffer
uint16_t packetsize;
//stores FIFO packet size for the DMP
volatile bool dataready = false;
//variable indicating whether or not there's data from the IMU to be processed
//============================================================================

Servo MotorL;
Servo MotorR;
Servo ServoL;
Servo ServoR;
MPU6050 imu;
//creates Servo objects and an MPU6050 object using Jeff Rowe's library for IMUs

//========================================================================================
//MPU 6050 MATH
Quaternion QUAT;
//quaternion array for conversion to yaw, pitch, and roll
float YPR[3];
//array for yaw, pitch, and roll (1 = yaw, 2 = pitch, 3 = roll)
VectorFloat gravity;
//stores the gravity vector for use by the 3DMath library
int16_t rollRate, pitchRate, yawRate;
//stores the rate of roll around the x axis for rate stabilization
//the other two variables are just there in case I need them
//=========================================================================================


float yaw, pitch, roll, throttle;
//yaw, pitch, and roll from RC reciever, plus an extra channel for switching flight modes
float myaw, mpitch, mroll;
//measured yaw, pitch, and roll from MPU 6050
float pitchF, rollF, yawF;
//final, converted values
float ServoRo, ServoLo, MotorLo, MotorRo;
//Servos and motor positions out
void setup(){
  pinMode(ServoLpin, OUTPUT);
  pinMode(ServoRpin, OUTPUT);
  pinMode(PitchIn, INPUT);
  pinMode(YawIn, INPUT);
  pinMode(RollIn, INPUT);
  pinMode(ThrottleIn, INPUT);
  pinMode(MotorLpin, OUTPUT);
  pinMode(MotorRpin, OUTPUT);
  pinMode(interrupt, INPUT);

  Wire.begin();
  //joins I2C bus that the IMU is connected to
  
  imu.initialize();
  //initializes the IMU
  
  ServoL.attach(ServoLpin);
  ServoR.attach(ServoRpin);
  MotorR.attach(MotorRpin);
  MotorL.attach(MotorLpin);
  //attaching Servos to pins
 
  imu.dmpInitialize();
  imu.setDMPEnabled(true);
  attachInterrupt(digitalPinToInterrupt(interrupt), imugetdata, RISING);
  //initializing IMU DMP(onboard sensor fusion) and attaching an interrupt to pin 2, which it triggers to signal that data is ready


    imu.setXGyroOffset(0);
    imu.setYGyroOffset(0);
    imu.setZGyroOffset(0);
    imu.setZAccelOffset(1688);
   //calibrate this later
    packetsize = imu.dmpGetFIFOPacketSize();
    FIFOcount = imu.getFIFOcount();
  }

void loop(){
  if (imugetdata == true){
    getYPR();
  }
  myaw = YPR[0]*(180/3.14159);
  mpitch = YPR[1]*(180/3.14159);
  //gets pitch and yaw and converts to degrees

  imu.getRotation(&rollRate, &pitchRate, &yawRate);
  //getting roll rate, might as well get pitch and yaw rate while we're at it

  
  //gets yaw, pitch, and roll from the IMU
  getYPRT();
  //updates all of the values for stick positions
  
  oldElapsedTime = elapsedTime;
  elapsedTime = micros();
  dT = elapsedTime - oldElapsedTime;
  //gets the time between cycles to allow the PID function to accurately integrate over time
  
  pitchF = map(pitch, 0, 100, -1*PITCH_MAX_ANGLE, PITCH_MAX_ANGLE);
  yawF = map(yaw, 0, 100, -1*YAW_MAX_ANGLE, YAW_MAX_ANGLE);
  //converting pitch and yaw into target angles
  rollF = map(roll, 0, 100, -1*ROLL_MAX_RATE, ROLL_MAX_RATE);
  correctionPitch = PIpitch(pitchF);
  correctionYaw = PIyaw(yawF);
  correctionRoll = PIrollrate (rollF);
  //Where the magic happens

  elevonMix(correctionRoll, correctionPitch);
  throttleMix(correctionYaw, throttle);
  //Where the corrections get mixed then outputted
  
  

}
//===========================================================================================

float PIpitch (float pitchTargetAngle){
  float P, I, error; //both P and I are local variables that will be added together into a final result
  error = pitchTargetAngle - mpitch;
  P = Kpp*error; //finding the proportional element of the PI function
  pitchErrorSum = (error)*dT + pitchErrorSum; //integrating the pitch error...
  I = pitchErrorSum*Kip; //... and finding the I value
  correctionPitch = -.25 * (P + I)*(1/map(throttle, 0, 100, .25, 1)); //Adding up the P and I into our final correction and adjusting for the effect of the the throttle
  //on the effectiveness of the elevons (max cancels out to 1*P+I, min is 1/4*P*I, subject to further change
  return correctionPitch;
  
}

float PIyaw (float yawTargetAngle){
  float P, I, error; //both P and I are local variables that will be added together into a final result
  error = yawTargetAngle - myaw;
  P = Kpy*error; //finding the proportional element of the PI function
  yawErrorSum = error*dT + yawErrorSum; //integrating the pitch error...
  I = yawErrorSum*Kiy; //... and finding the I value
  correctionYaw = (-1 * (P + I)); //Adding up the P and I into our final correction, with no need to correct for throttle because yaw is controlled by motors
  return correctionYaw;
  
}



float PIrollrate (float targetRollRate){
  float P, I, error;
  error = targetRollRate - rollRate;
  P = Kpr*error;
  rollRateSum = rollRateSum + error*dT;
  I = rollRateSum*Kir;
  correctionRoll = -.25 * (P + I)*(1/map(throttle, 0, 100, .25, 1));
  return correctionRoll;
 
}

void imugetdata(){
  dataready = true;
}

void getYPR(){
  while(FIFOcount < packetsize){
    FIFOcount = imu.getFIFOcount();
    }
  imu.getFIFObytes(FIFObuffer, packetsize);
  //gets the bytes from the FIFO buffer after waiting for the buffer to completely fill
  imu.getGravity(&gravity, &QUAT);
  //calculates the value of gravity (inc. acceleration)for the accelerometer to determine orientation
  imu.dmpGetQuaternion(&QUAT, FIFOBuffer);
  //gets the quaternion values calculated by the IMU
  imu.dmpGetYawPitchRoll(YPR,&QUAT,&gravity);
  //converts these into Yaw, Pitch, and Roll, and stores them in an array 
  }

void getYPRT(){
  throttle = map(pulseIn(ThrottleIn, HIGH), 1000, 2000, 0, 100);
  roll = map(pulseIn(RollIn, HIGH), 1000, 2000, 0, 100);
  yaw = map(pulseIn(YawIn, HIGH), 1000, 2000, 0, 100);
  pitch = map(pulseIn(PitchIn, HIGH), 1000, 2000, 0, 100);  
}
  

  //All of these wait for a pin connected to the receiver to go HIGH, and then measures that time. Output is in microseconds. 
  //1500us +/- 500 is the "standard" range for Servos, so the limits for the map are 1000-2000. This is then mapped to a value from 0 to 100, with 50 being resting position of the stick.

void elevonMix(float roll, float pitch){
float s1, s2; //just variables to plug into servo 1 and servo 2's positions
s1 = constrain (pitch + roll, 0, 1);
s2 = constrain (pitch - roll, 0, 1);
map(s1, 0, 1, 0, 180);//Mapping the PI function's output, which should have outputs between 0 and 1 most of the time with well-picked P and I values
map(s2, 0, 1, 0, 180);// to the Arduino Servo library's 0 to 180
ServoL.write(s1);
ServoR.write(s2);
}

void throttleMix(float yaw, float throttle){
//Essentially the same as the elevon mix, but with throttle instead. Cool.
float m1, m2;
m1 = constrain (throttle + yaw, 0, 1);
m2 = constrain (throttle - yaw, 0, 1);
map(m1, 0, 1, 0, 180);
map(m2, 0, 1, 0, 180);
MotorL.write(m1);//Motors and Servos are controlled in the exact same way. So it's the same function.
MotorR.write(m2);
}


}

Here's part 1 of my error message:

Arduino: 1.8.6 Hourly Build 2018/07/20 10:33 (Windows 10), Board: "Arduino/Genuino Uno"

In file included from C:\Users\Elendil\Desktop\stabilizedplane\Code\Stabilization_Sketch_v1.2\Stabilization_Sketch_v1.2.ino:3:0:

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_6Axis_MotionApps20.h:325:32: error: no 'uint8_t MPU6050::dmpInitialize()' member function declared in class 'MPU6050'

 uint8_t MPU6050::dmpInitialize() {

                                ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_6Axis_MotionApps20.h:548:34: error: no 'bool MPU6050::dmpPacketAvailable()' member function declared in class 'MPU6050'

 bool MPU6050::dmpPacketAvailable() {

                                  ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_6Axis_MotionApps20.h:575:66: error: no 'uint8_t MPU6050::dmpGetAccel(int32_t*, const uint8_t*)' member function declared in class 'MPU6050'

 uint8_t MPU6050::dmpGetAccel(int32_t *data, const uint8_t* packet) {

                                                                  ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_6Axis_MotionApps20.h:583:66: error: no 'uint8_t MPU6050::dmpGetAccel(int16_t*, const uint8_t*)' member function declared in class 'MPU6050'

 uint8_t MPU6050::dmpGetAccel(int16_t *data, const uint8_t* packet) {

                                                                  ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_6Axis_MotionApps20.h:591:67: error: no 'uint8_t MPU6050::dmpGetAccel(VectorInt16*, const uint8_t*)' member function declared in class 'MPU6050'

 uint8_t MPU6050::dmpGetAccel(VectorInt16 *v, const uint8_t* packet) {

                                                                   ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_6Axis_MotionApps20.h:599:71: error: no 'uint8_t MPU6050::dmpGetQuaternion(int32_t*, const uint8_t*)' member function declared in class 'MPU6050'

 uint8_t MPU6050::dmpGetQuaternion(int32_t *data, const uint8_t* packet) {

                                                                       ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_6Axis_MotionApps20.h:608:71: error: no 'uint8_t MPU6050::dmpGetQuaternion(int16_t*, const uint8_t*)' member function declared in class 'MPU6050'

 uint8_t MPU6050::dmpGetQuaternion(int16_t *data, const uint8_t* packet) {

                                                                       ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_6Axis_MotionApps20.h:617:71: error: no 'uint8_t MPU6050::dmpGetQuaternion(Quaternion*, const uint8_t*)' member function declared in class 'MPU6050'

 uint8_t MPU6050::dmpGetQuaternion(Quaternion *q, const uint8_t* packet) {

                                                                       ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_6Axis_MotionApps20.h:632:65: error: no 'uint8_t MPU6050::dmpGetGyro(int32_t*, const uint8_t*)' member function declared in class 'MPU6050'

 uint8_t MPU6050::dmpGetGyro(int32_t *data, const uint8_t* packet) {

                                                                 ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_6Axis_MotionApps20.h:640:65: error: no 'uint8_t MPU6050::dmpGetGyro(int16_t*, const uint8_t*)' member function declared in class 'MPU6050'

 uint8_t MPU6050::dmpGetGyro(int16_t *data, const uint8_t* packet) {

                                                                 ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_6Axis_MotionApps20.h:648:66: error: no 'uint8_t MPU6050::dmpGetGyro(VectorInt16*, const uint8_t*)' member function declared in class 'MPU6050'

 uint8_t MPU6050::dmpGetGyro(VectorInt16 *v, const uint8_t* packet) {

                                                                  ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_6Axis_MotionApps20.h:658:91: error: no 'uint8_t MPU6050::dmpGetLinearAccel(VectorInt16*, VectorInt16*, VectorFloat*)' member function declared in class 'MPU6050'

 uint8_t MPU6050::dmpGetLinearAccel(VectorInt16 *v, VectorInt16 *vRaw, VectorFloat *gravity) {

                                                                                           ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_6Axis_MotionApps20.h:666:92: error: no 'uint8_t MPU6050::dmpGetLinearAccelInWorld(VectorInt16*, VectorInt16*, Quaternion*)' member function declared in class 'MPU6050'

 uint8_t MPU6050::dmpGetLinearAccelInWorld(VectorInt16 *v, VectorInt16 *vReal, Quaternion *q) {

                                                                                            ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_6Axis_MotionApps20.h:678:68: error: no 'uint8_t MPU6050::dmpGetGravity(int16_t*, const uint8_t*)' member function declared in class 'MPU6050'

 uint8_t MPU6050::dmpGetGravity(int16_t *data, const uint8_t* packet) {

                                                                    ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_6Axis_MotionApps20.h:689:61: error: no 'uint8_t MPU6050::dmpGetGravity(VectorFloat*, Quaternion*)' member function declared in class 'MPU6050'

 uint8_t MPU6050::dmpGetGravity(VectorFloat *v, Quaternion *q) {

                                                             ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_6Axis_MotionApps20.h:700:56: error: no 'uint8_t MPU6050::dmpGetEuler(float*, Quaternion*)' member function declared in class 'MPU6050'

 uint8_t MPU6050::dmpGetEuler(float *data, Quaternion *q) {

                                                        ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_6Axis_MotionApps20.h:706:85: error: no 'uint8_t MPU6050::dmpGetYawPitchRoll(float*, Quaternion*, VectorFloat*)' member function declared in class 'MPU6050'

 uint8_t MPU6050::dmpGetYawPitchRoll(float *data, Quaternion *q, VectorFloat *gravity) {

                                                                                     ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_6Axis_MotionApps20.h:719:67: error: no 'uint8_t MPU6050::dmpProcessFIFOPacket(const unsigned char*)' member function declared in class 'MPU6050'

 uint8_t MPU6050::dmpProcessFIFOPacket(const unsigned char *dmpData) {

                                                                   ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_6Axis_MotionApps20.h:729:84: error: no 'uint8_t MPU6050::dmpReadAndProcessFIFOPacket(uint8_t, uint8_t*)' member function declared in class 'MPU6050'

 uint8_t MPU6050::dmpReadAndProcessFIFOPacket(uint8_t numPackets, uint8_t *processed) {

                                                                                    ^

And part 2:

uint8_t MPU6050::dmpGetMag(int16_t *data, const uint8_t* packet) {

                                                                ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_9Axis_MotionApps41.h:763:91: error: no 'uint8_t MPU6050::dmpGetLinearAccel(VectorInt16*, VectorInt16*, VectorFloat*)' member function declared in class 'MPU6050'

 uint8_t MPU6050::dmpGetLinearAccel(VectorInt16 *v, VectorInt16 *vRaw, VectorFloat *gravity) {

                                                                                           ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_9Axis_MotionApps41.h:771:92: error: no 'uint8_t MPU6050::dmpGetLinearAccelInWorld(VectorInt16*, VectorInt16*, Quaternion*)' member function declared in class 'MPU6050'

 uint8_t MPU6050::dmpGetLinearAccelInWorld(VectorInt16 *v, VectorInt16 *vReal, Quaternion *q) {

                                                                                            ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_9Axis_MotionApps41.h:783:68: error: no 'uint8_t MPU6050::dmpGetGravity(int16_t*, const uint8_t*)' member function declared in class 'MPU6050'

 uint8_t MPU6050::dmpGetGravity(int16_t *data, const uint8_t* packet) {

                                                                    ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_9Axis_MotionApps41.h:794:61: error: no 'uint8_t MPU6050::dmpGetGravity(VectorFloat*, Quaternion*)' member function declared in class 'MPU6050'

 uint8_t MPU6050::dmpGetGravity(VectorFloat *v, Quaternion *q) {

                                                             ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_9Axis_MotionApps41.h:805:56: error: no 'uint8_t MPU6050::dmpGetEuler(float*, Quaternion*)' member function declared in class 'MPU6050'

 uint8_t MPU6050::dmpGetEuler(float *data, Quaternion *q) {

                                                        ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_9Axis_MotionApps41.h:811:85: error: no 'uint8_t MPU6050::dmpGetYawPitchRoll(float*, Quaternion*, VectorFloat*)' member function declared in class 'MPU6050'

 uint8_t MPU6050::dmpGetYawPitchRoll(float *data, Quaternion *q, VectorFloat *gravity) {

                                                                                     ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_9Axis_MotionApps41.h:824:67: error: no 'uint8_t MPU6050::dmpProcessFIFOPacket(const unsigned char*)' member function declared in class 'MPU6050'

 uint8_t MPU6050::dmpProcessFIFOPacket(const unsigned char *dmpData) {

                                                                   ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_9Axis_MotionApps41.h:834:84: error: no 'uint8_t MPU6050::dmpReadAndProcessFIFOPacket(uint8_t, uint8_t*)' member function declared in class 'MPU6050'

 uint8_t MPU6050::dmpReadAndProcessFIFOPacket(uint8_t numPackets, uint8_t *processed) {

                                                                                    ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\libraries\MPU6050/MPU6050_9Axis_MotionApps41.h:859:40: error: no 'uint16_t MPU6050::dmpGetFIFOPacketSize()' member function declared in class 'MPU6050'

 uint16_t MPU6050::dmpGetFIFOPacketSize() {

                                        ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\Stabilization_Sketch_v1.2\Stabilization_Sketch_v1.2.ino: In function 'void setup()':

Stabilization_Sketch_v1.2:114:7: error: 'class MPU6050' has no member named 'dmpInitialize'

   imu.dmpInitialize();

       ^

Stabilization_Sketch_v1.2:125:22: error: 'class MPU6050' has no member named 'dmpGetFIFOPacketSize'

     packetsize = imu.dmpGetFIFOPacketSize();

                      ^

Stabilization_Sketch_v1.2:126:21: error: 'class MPU6050' has no member named 'getFIFOcount'

     FIFOcount = imu.getFIFOcount();

                     ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\Stabilization_Sketch_v1.2\Stabilization_Sketch_v1.2.ino: In function 'void getYPR()':

Stabilization_Sketch_v1.2:222:21: error: 'class MPU6050' has no member named 'getFIFOcount'

     FIFOcount = imu.getFIFOcount();

                     ^

Stabilization_Sketch_v1.2:224:7: error: 'class MPU6050' has no member named 'getFIFObytes'

   imu.getFIFObytes(FIFObuffer, packetsize);

       ^

Stabilization_Sketch_v1.2:226:7: error: 'class MPU6050' has no member named 'getGravity'

   imu.getGravity(&gravity, &QUAT);

       ^

Stabilization_Sketch_v1.2:228:7: error: 'class MPU6050' has no member named 'dmpGetQuaternion'

   imu.dmpGetQuaternion(&QUAT, FIFOBuffer);

       ^

Stabilization_Sketch_v1.2:228:31: error: 'FIFOBuffer' was not declared in this scope

   imu.dmpGetQuaternion(&QUAT, FIFOBuffer);

                               ^

Stabilization_Sketch_v1.2:230:7: error: 'class MPU6050' has no member named 'dmpGetYawPitchRoll'

   imu.dmpGetYawPitchRoll(YPR,&QUAT,&gravity);

       ^

C:\Users\Elendil\Desktop\stabilizedplane\Code\Stabilization_Sketch_v1.2\Stabilization_Sketch_v1.2.ino: At global scope:

Stabilization_Sketch_v1.2:267:1: error: expected declaration before '}' token

 }

 ^

exit status 1
'class MPU6050' has no member named 'dmpInitialize'

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

Thanks again.

For anyone who has this issue in the future, I resolved it by commenting out the include for MPU6050_9Axis_MotionApps.h and MPU6050.h. Must have been some strange interaction between these two class files.

changing the order of includes solved it for me (strangely)

#define MPU6050_INCLUDE_DMP_MOTIONAPPS20
#include "MPU6050_6Axis_MotionApps20.h"
#include "MPU6050.h"
#include "common.h"

instead of

#define MPU6050_INCLUDE_DMP_MOTIONAPPS20
#include "MPU6050.h"
#include "MPU6050_6Axis_MotionApps20.h"
#include "common.h"

1 Like