Missing header files in MAVLink library

I have cross posted this to PX4 slack. For anyone who wants to help, there is additional information in this googlegroup: https://groups.google.com/g/mavlink/c/sN88hoCPA8k/m/Bd8iqCjmAAAJ
The last post says

UPDATE: I pasted only the common folder in the libraries, and drag the “protocol.h”, “mavlink_types.h”, etc., files into that folder.
Now I manage to get some messages (the ones I was getting before I tried this, from and older mavlink library), but not all of them.

It’s very odd because they are all in the same library, in the same folder, but I can get some but the ones I want I can’t. The “common” folder of the “c_library_v2” is the only folder that I have there.
Why is this happening?

Please confirm you have common/mavlink.h in the project. This should import most of what you need.

I personally can’t help you more than that.

Thank you very much!

Yes, I have “common/mavlink.h” file in there, which is the only header file that I include in the Arduino project.
My only question (for anyone who wants to help me) is why can I receive the older messages, but not the ones I want? In my point of view there should be no difference between them, since they are in the same folder.

Best regards

It looks like the Arduino mavlink library is possibly generated from an older version of mavlink. I’m trying to follow your issue on the mavlink google group, but it’s hard to parse without seeing the error message. The only include you should need is <common/mavlink.h> (or mavlink.h depending on your path)

The message ids should not have changed though. OPTICAL_FLOW is still #100 and OPTICAL_FLOW_RAD is #106. Same with ODOMETRY and GLOBAL_POSITION_INT.

Is it that the mavlink streams from the PX4 are different on your CubeOrange autopilot? Possibly you were using the NORMAL mavlink mode and now you have EXTVISION which sends out ODOMETRY instead of GLOBAL_POSITION_INT.

Check your parameter MAV_#_MODE on the autopilot.

  1. The thing is that Arduino doesn’t send a error message. The code compiles fine, which means that it understands the Message ID that I ask it to receive. For example, in this figure, it knows that it has to receive both attitude and odometry, and since it compiles without errors it shows that it has both IDs well defined. But although the attitude values are perfectly fine, the odometry ones are 0 everytime.

image

  1. Yes, it can be that, I think. The Flight Controller is in my lab so I can only check the parameter tomorrow, but I will definitely try that. Just two questions:
  • what does that parameter MAV_#_MODE means exactly?
  • If I have in fact the EXTVISION mode what do I have to do to make it work?

Best regards

On the PX4 Autopilot Firmware you have parameters you set. MAV_0_MODE sets the streaming mode for the 0th mavlink instance. MAV_1_MODE for the 1st instance. Setting this parameter to “0” results in the NORMAL stream. “8” would be the EXTVISION stream. See the links in my previous comment to see what each stream mode sends out.

See Full Parameter Reference · PX4 v1.9.0 User Guide for descriptions of each parameters.

I suggest you connect a QGroundControl instance to the serial connection your Arduino usually connects and you can use the Mavlink Inspector feature to see the incoming mavlink messages in real-time. Or write a mavlink lister in python with pymavlink that prints out the messages.

You say the velocity in the ODOMETRY message are zeros? roll, pitch, and yaw in your ATTITUDE message will be in radians while vx, vy, vz in your ODOMETRY message are in meters per second. Does the quaternion (q[4]) reported in the ODOMETRY message match the corresponding roll, pitch, yaw Euler sequence?

Are you displaying enough digits? Is the aircraft moving faster than 1 meter/second in your tests? Does the aircraft have a Position lock?

When I try the code I have the MAVlink Inspector in QGroundControl open, and I can see that the parameters I ask are perfectly shown there.

Yes, the angle values from ATTITUDE are fine, but the ones from ODOMETRY (vx, vy and vz) and OPTICAL_FLOW_RAD (distance) show everytime a “0.00” value.

Regarding the quaternion, all I know is the definition:
image

For now, I am just trying to print those values in the Arduino Serial Monitor. The aircraft is not built, I am just testing the Flight Controller separately

Yes, the angle values from ATTITUDE are fine, but the ones from ODOMETRY (vx, vy and vz) and OPTICAL_FLOW_RAD (distance) show everytime a “0.00” value.

Show a 0.00 value on your Arduino or in QGC MAVlink Inspector? What values of velocity x,y,z do you expect to see?

Regarding the quaternion

A quaternion is a representation of attitude. This can be converted to Euler angles (roll, pitch, yaw for example) with some loss of information. q = [1 0 0 0] is equivalent to 0 roll, 0 pitch, and 0 yaw. What attitude quaternion is being sent in the ODOMETRY mavlink message?

In the Arduino Serial Monitor. In QGC the values are ok. Even when a move the Flight Controller in my hand, the velocities are 0, and they should be different.

I am not trying to ask for the quaternion, even because I am having this problem of not receiving the ODOMETRY message. But if I did, I believe that it would send the positions x, y and z of the drone (the figure I sent in the last message), since it is the predefined type.

I am not trying to ask for the quaternion

I understand that, but it helps narrow down the issue if the quaternion is the identity (1,0,0,0) or some other value.

In the Arduino Serial Monitor. In QGC the values are ok.

Then this could be a print issue in or some bug in how you are displaying the values, since QGC shows you are getting good mavlink messages out the serial port. You’ll have to share your code. You’ll have to share the rest of your code to debug this.

Ok, tommorrow I will try to ask for that quaternion, but I don’t think that Arduino will be able to read it.

Yes, but the thing is that for the ATTITUDE message I print it in the same way as the ODOMETRY, and its result is ok.

Sure, here it comes:

#include <mavlink.h>

unsigned long previousMillisMAVLink = 0;     // will store last time MAVLink was transmitted and listened
unsigned long next_interval_MAVLink = 1000;  // next interval to count
const int num_hbs = 5;                      // # of heartbeats to wait before activating STREAMS from Pixhawk. 60 = one minute.
int num_hbs_pasados = num_hbs;


String str_reply, inputs;
float roll, pitch, yaw;
int16_t vx, vy, vz;
uint16_t compass_angle;
float height, velx, vely, velz;
int i, j;
int subS_l;
int index_of_S;

int teste = 5;

void setup() {
  // MAVLink interface start
     Serial1.begin(57600);
     Serial.begin(57600);
     Serial.println("MAVLink starting.");  
 // Serial.begin(115200);
  delay(10);
}

void loop() {

   inputs = "";
   i=1;
   j=1;

             // MAVLink config
  /* The default UART header for your MCU */ 
  int sysid = 1;                   ///< ID 20 for this airplane. 1 PX, 255 ground station
  int compid = 2;                ///< The component sending the message
  int type = 2;                                            //  int type = MAV_TYPE_QUADROTOR;   ///< This system is an airplane / fixed wing
 
  // Define the system type, in this case a Quadcopter -> on-board controller
  uint8_t system_type = 0;                  //  uint8_t system_type = MAV_TYPE_GENERIC;
  uint8_t autopilot_type = 12;           //    uint8_t autopilot_type = MAV_AUTOPILOT_PX4;            //uint8_t autopilot_type = MAV_AUTOPILOT_ARDUPILOTMEGA;
 
  uint8_t system_mode = 0;    //     uint8_t system_mode = MAV_MODE_PREFLIGHT; ///< Booting up
  uint32_t custom_mode = 0;                 ///< Custom mode, can be defined by user/adopter
  uint8_t system_state = 3;    //      uint8_t system_state = MAV_STATE_STANDBY; ///< System ready for flight

    // Initialize the required buffers
  mavlink_message_t msg;
  uint8_t buf[MAVLINK_MAX_PACKET_LEN];

      // Pack the message
  //mavlink_msg_heartbeat_pack(sysid,compid, &msg, type, autopilot_type, system_mode, custom_mode, system_state);
  mavlink_msg_heartbeat_pack(1,2, &msg, type, autopilot_type, system_mode, custom_mode, system_state); 
 
  // Copy the message to the send buffer
  uint16_t len = mavlink_msg_to_send_buffer(buf, &msg);
 
  // Send the message with the standard UART send function
  // uart0_send might be named differently depending on the individual microcontroller / library in use.
  unsigned long currentMillisMAVLink = millis();
  if (currentMillisMAVLink - previousMillisMAVLink >= next_interval_MAVLink) {
    // Timing variables
    previousMillisMAVLink = currentMillisMAVLink;
    
    Serial1.write(buf, len);
    
    //Mav_Request_Data();
    num_hbs_pasados++;
    if(num_hbs_pasados>=num_hbs){
      // Request streams from Pixhawk
     Mav_Request_Data();
     num_hbs_pasados=0;
    }
     }

  // Check reception buffer
  comm_receive();

      Serial.println("New UDP Client.");          // print a message out in the serial port
   
      // str_reply="AB ";
      str_reply = roll;
      str_reply+=" ";
      str_reply += pitch;
      str_reply+=" ";
      str_reply += yaw;
      str_reply+=" ";
      str_reply += vx;
      str_reply+=" ";
      str_reply += vy;
      str_reply+=" ";
      str_reply += vz;
      str_reply+=" ";
      str_reply += height;
      str_reply+=" Testes: ";
      str_reply += velx;
      str_reply+=" ";
      str_reply += vely;
      str_reply+=" ";
      str_reply += velz;
       }
     
       Serial.println(str_reply);

  } 
}

void Mav_Request_Data()
{
  mavlink_message_t msg;
  uint8_t buf[MAVLINK_MAX_PACKET_LEN];

  // To be setup according to the needed information to be requested from the Pixhawk
  const int  maxStreams = 1;
  const uint8_t MAVStreams[maxStreams] = {MAV_DATA_STREAM_ALL};   
  const uint16_t MAVRates[maxStreams] = {0x05};

  
  for (int i=0; i < maxStreams; i++) {

    mavlink_msg_request_data_stream_pack(1, 2, &msg, 0, 0, MAVStreams[i], MAVRates[i], 1);
    uint16_t len = mavlink_msg_to_send_buffer(buf, &msg);
    Serial1.write(buf, len);

  }
}

void comm_receive() {

  mavlink_message_t msg;

  mavlink_status_t status;

  // Read meanwhile there is data from the FC

  while(Serial1.available()>0) {

    //cnt++;

    uint8_t c = Serial1.read();

    // Try to get a new message

    if(mavlink_parse_char(MAVLINK_COMM_0, c, &msg, &status)) {

      //cnt++;

      // Handle message

      switch(msg.msgid) {

        case MAVLINK_MSG_ID_HEARTBEAT:  // #0: Heartbeat

          {

            // Heartbeat received from FC

            Serial.println("Flight Controller HeartBeat");

          }

          break;


         case MAVLINK_MSG_ID_ATTITUDE:  // #30
          {

            mavlink_attitude_t attitude;
            mavlink_msg_attitude_decode(&msg, &attitude);

            roll=attitude.roll;
            pitch=attitude.pitch;
            yaw=attitude.yaw;
          }

          case MAVLINK_MSG_ID_GLOBAL_POSITION_INT:   // #33
          {
            mavlink_global_position_int_t global_position_int;
            mavlink_msg_global_position_int_decode(&msg, &global_position_int);

            vx=global_position_int.vx;
            vy=global_position_int.vy;
            vz=global_position_int.vz;
            compass_angle=global_position_int.hdg;
          }

          case MAVLINK_MSG_ID_ODOMETRY:   // #331
          {
             
            mavlink_odometry_t odometry;
            mavlink_msg_odometry_decode(&msg, &odometry);

           // Serial.println(mavlink_msg_global_position_int_get_vx(&msg));
            velx=odometry.vx;
            vely=odometry.vy;
            velz=odometry.vz;
          }

          case MAVLINK_MSG_ID_OPTICAL_FLOW_RAD: //#106 
          {
            mavlink_optical_flow_rad_t optical_flow_rad;
            mavlink_msg_optical_flow_rad_decode(&msg, &optical_flow_rad);
            height = optical_flow_rad.distance;*/

          } 

      }  

    }
  }
}

My guess is that this case is not being hit and the values are left at their initial values. Set velx to something ridiculous like 999.99. Or add a print statement in this case.

Should you have break in each case of your switch statement? This could be causing a fallthrough when the early cases are hit, overwriting the later cases with 0s.

          case MAVLINK_MSG_ID_ODOMETRY:   // #331
          {
             
            mavlink_odometry_t odometry;
            mavlink_msg_odometry_decode(&msg, &odometry);

           // Serial.println(mavlink_msg_global_position_int_get_vx(&msg));
            velx=odometry.vx;
            vely=odometry.vy;
            velz=odometry.vz;
          }

I think those are good ideas, I will try both in about 9 hours and then write here the results.
Best regards

Ok, new Update. I tried everything you said, and these were the results:

The predefined value for that parameter was the MAV_0_MODE, which is the normal one. I changed that to EXTVISION just to test and the result was even worse, I lost velocity in the data flow and some results were missing.

I asked for that, but the result was [0 0 0 0].

I’ve added breaks in all of them, but the only thing that happened was that I only received the first one (ATTITUDE).

This was the biggest surprise! I thought that the cases “ODOMETRY” and “OPTICAL_FLOW_RAD” were not being hitted too. But then I changed my code to print the name of the message if the cases were hitted, and it printed! So the code is recognizing the message ID.
Here is a screenshot of the new code and the Serial Monitor results:


Why do you think this is happening?

I think you have some bugs in your code.

The reason you are hitting the odometry and optical flow cases in your print statements is due to fall through because you don’t have “breaks” - not because you are receiving the messages. You are only getting the ATTITUDE message which continues on and evaluates the code until it hits a “break” or end of the switch.

Keep things orthogonal. Take a step back and get each feature functioning independent of the rest of the system.

I don’t understand. Aren’t the cases there to check if one of the messages that are being received have the same name as the written in the case? Because, if I miss one letter in the MAVLINK_MSG_ID_ODOMETRY definition it sends an error compiling the code.

Right now I switched the ODOMETRY for the LOCAL_POSITION_NED and it works fine for that msg ID, so I am guessing that’s not it. There’s some error in the livrary or whatever, at least with the ODOMETRY and the OPTICAL_FLOW_RAD messages

I noticed that there is no break; on the line preceding case MAVLINK_MSG_ID_OPTICAL_FLOW_RAD: // #106.
What prevents Serial.println("optflow"); from executing when processing a MAVLINK_MSG_ID_ODOMETRY?
https://discuss.px4.io/uploads/default/original/2X/b/bb2e2c245494edf5de37dfce1f34279e4f18ca3b.png

Hi!
I believe that when I write the cases, the code only executes the code inside the case when it reads a message that has the same ID as the one expressed in the case line.
For example, if MAVLink does not send the ODOMETRY message, the code doesn’t execute the Serial.print.
But I can be wrong, I don’t know exactly. But since if I misspell the MSG_ID the Arduino gives as error compiling, I believe that thinking right.

  1. comment out every case but ODOMETRY. Does “odometry” still print over the serial monitor?

  2. Are you 100% sure ODOMETRY is coming over the serial line connected to the Arduino? Is Mavlink Inspector plugged into this serial port or into the USB port on the autopilot? These are two different mavlink instances with different modes and messages. Create a test script in python that generates the desired messages and use that instead of the autopilot.

  3. Read up on switch statements. Are you tracking the output of the below code?

int a = 0
switch(a) {
case 0: 
    println("0");
case 1:
    println("1");
case 2:
    println("2");
}

outputs:

0
1
2

if not, convince yourself the above output is correct.

1 Like

Yes, it does.

Yes, I guess so. MAVLink inspector is plugged into the serial port.

NEW UPDATE:
I substituted the ODOMETRY and OPTICAL_FLOW_RAD for other message IDs that send similar values, namely the LOCAL_POSITION_NED (vx, vy, vz) and the DISTANCE_SENSOR (current_distance).
The first one works fine, but the distance from the optical flow assumes big random values, although it should have a maximum of 255. Only very rarely the sent values make sense. This problem is the same as this one that I had some time ago, for other message IDs, for which no one answered me.
Any sugestion?

Keep everything orthogonal. Then put the pieces together once they are tested and validated to be working correctly.

1 Like