MAVLink and Arduino: step by step

Hi there!

In my application I am trying to obtain simple parameters such as the roll, pitch and yaw angles (mavlink_msg_attitude), and the speeds in each direction (x, y and z - mavlink_msg_global_position_int).
If for the first ones I am able to do it without a problem (using the MAV_DATA_STREAM_EXTENDED_STATUS stream), for the speeds I have not been so lucky.
Whether using MAV_DATA_STREAM_POSITION or MAV_DATA_STREAM_ALL, I always get 2 values for each speed, even using a frequency of 0x01.
The worst of all is that, although the second value seems correct, the first is always absurd, for example, with the drone being stopped it gives me speeds in X of 30000.
What is this and how can I solve it?

Best regards

image

Hi, @Menno_Jorna:

The Holybro is just the modem. It just transmitts the signal and has nothing to do here. Regarding the PX4, what version are you using? (Model, version, manufacturer, firmware).

I can advance you that in my PX4 I had not all the messages implemented. I guess it is (human) time and (microcontroller) memory consuming. If you try to get something very specific, you will probably have to implement it yourself, but it is a tricky way I have never tried.

Kind regards,
Juan Pedro

Hi, @Jose_Pedro_Correia:

What is your flight controller? What is your code? Could it be linear and angular speeds? Might there be a division by (almost) 0 around? I need further details…

Kind regards,
Juan Pedro

Hi @jplopezll

  1. My flight controller is a Cube Orange PX4, and I’m using a ESP32 WifiModule in an Adafruit board, but it is programable in the Arduino IDE.

  2. This is my code:


    And this is the request of the streams (if I try with the STREAM_ALL the result is the same):

  3. This stream (GLOBAL_POSITION_INT) provides the ground speed in cm/s (so it’s not angular speed), and I only ask for those values, so I dont know what is this problem.
    When I increase the frequency to 0x05 (for example), I also get the first line of data incorrect, but all of the other 5 lines are correct (vx=0, vy=0, vz=0).
    I looked in Mission Planner and the values coming from the PX4 are correct, so I dont know what huge and ridiculous values are that.
    Also, in each cycle these wrong values are random, like: it goes from 30000 to -25244 to 523.

Best regards

have you fix this problem??
because I got same problem as you did. In my case 30/32/36/74 msg.id repeated
I want No.8channel data of Rc_channel_raw . If you clear this problem, plz save me!!

thx

Hi @jplopezll i feel very thankful for your work! and i have some question of my project
my project is this
i got quadcopter that has propulsion propeller that has extra thrust so i earn more velocity
i’ll control rear propeller by RC .so, i have to use RC_Channel data to mapping rc control to pwm out
but following your work i can’t get RC_channel msg#65 or RC_channel_raw#35
could you give me a advice for that?
best regards

Hi, @yunjae-Na9025:

Have you got heartbeats or other streams? You need to request the streams that include the info you want. For that, you need to look for in the documentation of your flight controller.

If you need assistance with that, better PM me. I am a bit busy right now, but summer is comming! :wink:

Kind regards,
Juan Pedro

@jplopezll First, I would like thank you for your great and useful post.

  1. I would like to know if you have tried to use MAVLink v2 with Arduino. If yes, were there any changes in the message parsing/packing functions?

  2. Is there a way to automatically detect the MAVLink protocol version from the received messages ?

Thanks in advance!

Hi, @mzahana:

Thank you very much for your kind words.

Yes, I have tried to use v2 with Arduino. It works, but it is memory hungry. Depending on what microcontroller you are using it can be difficult to handle. I had to reduce the message size to make it minimally operational but finaly I decided to use v1 instead as my needs were very basic.

About how to detect the version of the library, have a look here: https://mavlink.io/en/guide/mavlink_version.html. You can see the differences and how to detect the version. I have never used it as I always had access to select the version in both the flight controller and the arduino.

Kind regards,
Juan Pedro

Hi @jplopezll ,
This is a really extensive guide for Arduino and MAVLink! I am now currently trying to interface the 3DR Pixhawk 1 Flight Controller with Arduino Uno so that I can send Pixhawk information such as attitude the Arduino board. The TX pin from Pixhawk will go to Pin 9 (RX) and RX pin to Pin 10 (TX). I have tried to alter your code to suit my needs, but when I run it, it just gives me gibberish. After some debugging, I noticed the code cannot print the string when I pass Serial.print(“Hello World”) after (if(mavlink_parse_char(MAVLINK_COMM_0, c, &msg, &status)) {).
The vehicle is a quadcopter and the code is shown below. Could you please help me find what is wrong with my code?

/* MAVLInk_DroneLights

by Juan Pedro López

This program was developed to connect an Arduino board with a Pixhawk via MAVLink

with the objective of controlling a group of WS2812B LED lights on board of a quad

The current version of the program is working properly.

TO DO:

Move STREAMS request to RC_CHANNELS to use values in logic
Add RC_CHANNLES_RAW messages monitoring: move #30 to RC_CHANNELS_RAW (#35)
http://mavlink.org/messages/common#RC_CHANNELS_RAW
Look for message on low battery:
To be tested: http://mavlink.org/messages/common#PARAM_REQUEST_READ
To be checked: http://mavlink.org/messages/common#SYS_STATUS
Potential implementation of other alarms, like high intensity
You can restrict the maximum package size with this parameter in mavlink_types.h:

#ifndef MAVLINK_MAX_PAYLOAD_LEN_
// it is possible to override this, but be careful! Defa_
#define **MAVLINK_MAX_PAYLOAD_LEN 255 ///< Maximum payload length_
#endif_
*/

// In case we need a second serial port for debugging
#define SOFT_SERIAL_DEBUGGING // Comment this line if no serial debugging is needed

#ifdef SOFT_SERIAL_DEBUGGING
// Library to use serial debugging with a second board
#include <SoftwareSerial.h>
SoftwareSerial pxSerial(9,10); // RX, TX
#endif

//#include “mavlink.h”
#include “C:/Users/Power User/Documents/Arduino/libraries/mavlink_2/common/mavlink.h”
//#include “common/mavlink_msg_request_data_stream.h”

// Mavlink variables
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 = 60; // # of heartbeats to wait before activating STREAMS from Pixhawk. 60 = one minute.
int num_hbs_pasados = num_hbs;

// FastLed setup
// How many leds in your strip?
#define NUM_LEDS 8
#define BRIGHTNESS 255 // Put 0 to switch off all leds

void setup() {
// MAVLink interface start
Serial.begin(9600);

#ifdef SOFT_SERIAL_DEBUGGING
// [DEB] Soft serial port start
Serial.begin(57600);
Serial.println(“MAVLink starting.”);
pxSerial.begin(57600);
#endif
}

void loop() {

// MAVLink
/* The default UART header for your MCU */
int sysid = 1; ///< ID 20 for this airplane. 1 PX, 255 ground station
int compid = 158; ///< The component sending the message
int type = MAV_TYPE_QUADROTOR; ///< This system is an airplane / fixed wing

// Define the system type, in this case an airplane -> on-board controller
// uint8_t system_type = MAV_TYPE_FIXED_WING;
uint8_t system_type = MAV_TYPE_GENERIC;
uint8_t autopilot_type = MAV_AUTOPILOT_INVALID;

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 = 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,0, &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) {
// Guardamos la última vez que se cambió el modo
previousMillisMAVLink = currentMillisMAVLink;

#ifdef SOFT_SERIAL_DEBUGGING
pxSerial.write(buf,len);
//mySerial.println(“Ardu HB”);
#else
Serial.write(buf, len);
#endif

//Mav_Request_Data();
num_hbs_pasados++;
if(num_hbs_pasados>=num_hbs) {
// Request streams from Pixhawk
#ifdef SOFT_SERIAL_DEBUGGING
Serial.println(“Streams requested!”);
#endif
Mav_Request_Data();
num_hbs_pasados=0;
}

}

// Check reception buffer
comm_receive();
}

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

// STREAMS that can be requested
/*

Definitions are in common.h: enum MAV_DATA_STREAM
MAV_DATA_STREAM_ALL=0, // Enable all data streams
MAV_DATA_STREAM_RAW_SENSORS=1, /* Enable IMU_RAW, GPS_RAW, GPS_STATUS packets.
MAV_DATA_STREAM_EXTENDED_STATUS=2, /* Enable GPS_STATUS, CONTROL_STATUS, AUX_STATUS
MAV_DATA_STREAM_RC_CHANNELS=3, /* Enable RC_CHANNELS_SCALED, RC_CHANNELS_RAW, SERVO_OUTPUT_RAW
MAV_DATA_STREAM_RAW_CONTROLLER=4, /* Enable ATTITUDE_CONTROLLER_OUTPUT, POSITION_CONTROLLER_OUTPUT, NAV_CONTROLLER_OUTPUT.
MAV_DATA_STREAM_POSITION=6, /* Enable LOCAL_POSITION, GLOBAL_POSITION/GLOBAL_POSITION_INT messages.
MAV_DATA_STREAM_EXTRA1=10, /* Dependent on the autopilot
MAV_DATA_STREAM_EXTRA2=11, /* Dependent on the autopilot
MAV_DATA_STREAM_EXTRA3=12, /* Dependent on the autopilot
MAV_DATA_STREAM_ENUM_END=13,
Data in PixHawk available in:
Battery, amperage and voltage (SYS_STATUS) in MAV_DATA_STREAM_EXTENDED_STATUS
Gyro info (IMU_SCALED) in MAV_DATA_STREAM_EXTRA1
*/
// To be setup according to the needed information to be requested from the Pixhawk
const int maxStreams = 2;
const uint8_t MAVStreams[maxStreams] = {MAV_DATA_STREAM_EXTENDED_STATUS, MAV_DATA_STREAM_EXTRA1};
const uint16_t MAVRates[maxStreams] = {0x02,0x05};

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

  • mavlink_msg_request_data_stream_pack(system_id, component_id,
  • &msg,
  • target_system, target_component,
  • MAV_DATA_STREAM_POSITION, 10000000, 1);
  • mavlink_msg_request_data_stream_pack(uint8_t system_id, uint8_t component_id,
  • mavlink_message_t* msg,
  • uint8_t target_system, uint8_t target_component, uint8_t req_stream_id,
  • uint16_t req_message_rate, uint8_t start_stop)

*/
mavlink_msg_request_data_stream_pack(2, 200, &msg, 1, 0, MAVStreams[i], MAVRates[i], 1);
uint16_t len = mavlink_msg_to_send_buffer(buf, &msg);
#ifdef SOFT_SERIAL_DEBUGGING
pxSerial.write(buf,len);
#else
Serial.write(buf, len);
#endif
}

// Request: PARAM_REQUEST_LIST. Only for full log recording
/*

Primitive: mavlink_msg_param_request_list_pack(uint8_t system_id, uint8_t component_id, mavlink_message_t* msg,
uint8_t target_system, uint8_t target_component)
/
/
// Configure
uint8_t system_id=2;
uint8_t component_id=200;
// mavlink_message_t* msg;
uint8_t target_system=1;
uint8_t target_component=0;
// Pack
mavlink_msg_param_request_list_pack(system_id, component_id, &msg,
target_system, target_component);
uint16_t len = mavlink_msg_to_send_buffer(buf, &msg);

// Send
#ifdef SOFT_SERIAL_DEBUGGING
pxSerial.write(buf,len);
#else
Serial.write(buf, len);
#endif
*/
}

void comm_receive() {

mavlink_message_t msg;
mavlink_status_t status;

// Echo for manual debugging
// Serial.println("—Start—");

#ifdef SOFT_SERIAL_DEBUGGING
while(pxSerial.available()>0) {
uint8_t c = pxSerial.read();
#else
while(Serial.available()>0) {
uint8_t c = Serial.read();
#endif

// Try to get a new message
if(mavlink_parse_char(MAVLINK_COMM_0, c, &msg, &status)) {

// Handle message
switch(msg.msgid) {
case MAVLINK_MSG_ID_HEARTBEAT: // #0: Heartbeat
{
// E.g. read GCS heartbeat and go into
// comm lost mode if timer times out
#ifdef SOFT_SERIAL_DEBUGGING
Serial.println(“PX HB”);
#endif
}
break;

case MAVLINK_MSG_ID_SYS_STATUS:  // #1: SYS_STATUS
  {
    /* Message decoding: PRIMITIVE
     *    mavlink_msg_sys_status_decode(const mavlink_message_t* msg, mavlink_sys_status_t* sys_status)
     */
    //mavlink_message_t* msg;
    mavlink_sys_status_t sys_status;
    mavlink_msg_sys_status_decode(&msg, &sys_status);
    #ifdef SOFT_SERIAL_DEBUGGING
    Serial.print("PX SYS STATUS: ");
    Serial.print("[Bat (V): ");
    Serial.print(sys_status.voltage_battery);
    Serial.print("], [Bat (A): ");
    Serial.print(sys_status.current_battery);
    Serial.print("], [Comms loss (%): ");
    Serial.print(sys_status.drop_rate_comm);
    Serial.println("]");
    #endif
    }
 break;

case MAVLINK_MSG_ID_PARAM_VALUE:  // #22: PARAM_VALUE
  {
    /* Message decoding: PRIMITIVE
     *    mavlink_msg_param_value_decode(const mavlink_message_t* msg, mavlink_param_value_t* param_value)
     */
    //mavlink_message_t* msg;
    mavlink_param_value_t param_value;
    mavlink_msg_param_value_decode(&msg, &param_value);
    #ifdef SOFT_SERIAL_DEBUGGING
    Serial.println("PX PARAM_VALUE");
    Serial.println(param_value.param_value);
    Serial.println(param_value.param_count);
    Serial.println(param_value.param_index);
    Serial.println(param_value.param_id);
    Serial.println(param_value.param_type);
    Serial.println("------ Fin -------");
    #endif
    }
break;

case MAVLINK_MSG_ID_RAW_IMU:  // #27: RAW_IMU
  {
    /* Message decoding: PRIMITIVE
     *    static inline void mavlink_msg_raw_imu_decode(const mavlink_message_t* msg, mavlink_raw_imu_t* raw_imu)
     */
    mavlink_raw_imu_t raw_imu;
    mavlink_msg_raw_imu_decode(&msg, &raw_imu);
    #ifdef SOFT_SERIAL_DEBUGGING
//mySerial.println("PX RAW IMU");
//mySerial.println(raw_imu.xacc);
#endif
}
break;

case MAVLINK_MSG_ID_ATTITUDE:  // #30
  {
    /* Message decoding: PRIMITIVE
     *    mavlink_msg_attitude_decode(const mavlink_message_t* msg, mavlink_attitude_t* attitude)
     */
    mavlink_attitude_t attitude;
    mavlink_msg_attitude_decode(&msg, &attitude);
#ifdef SOFT_SERIAL_DEBUGGING
Serial.println("PX ATTITUDE");
//mySerial.println(attitude.roll);

#endif
}
break;

default:
#ifdef SOFT_SERIAL_DEBUGGING
Serial.print("— Otros: “);
Serial.print(”[ID: “);
Serial.print(msg.msgid);
Serial.print(”], [seq: “);
Serial.print(msg.seq);
Serial.println(”]");
#endif
break;
}
}
}
}

Hi @FranklinMyn:

First let’s have a look at the “gibberish”. Looks like you have not set correctly the bauds in your serial monitor. In the code you have 57600, please check that you have set that same baud rate in your monitor.

Kind regards,
Juan Pedro

Hi @jplopezll,
Thanks for your quick reply! I have changed the connector pins since it seems like the connector pins are not connecting properly and that gives it gibberish. For now, the Serial monitor keeps showing the 0tros, which is the default options for the code. What should I do to get the attitude and other parameters? Should I get another mavlink library?
The serial monitor screenshot:


There are some values under PX Attitude and PX RAW IMU. Are those the required values? Moreover, how can I see other parameters such as the battery amount from “MAVLINK_MSG_ID_SYS_STATUS”?
Also, I would like to obtain latitude, longitude, and altitude values from MAVLINK_MSG_ID_GLOBAL_POSITION_INT: So I put this code in the function comm_receive().
case MAVLINK_MSG_ID_GLOBAL_POSITION_INT: // #33
{
//see in the file of mavlink_msg_global_position_int.h in \common directory

      mavlink_global_position_int_t global_position;
      mavlink_msg_global_position_int_decode(&msg, &global_position);
 
      //int32_t alt; ///< Altitude in meters, expressed as * 1000 (millimeters), above MSL
      //int32_t relative_alt; ///< Altitude above ground in meters, expressed as * 1000 (millimeters)
      #ifdef SOFT_SERIAL_DEBUGGING
      Serial.print("alt:");  
      Serial.println(global_position.alt);
      Serial.print("rel_alt:");  
      Serial.println(global_position.relative_alt);
      Serial.print("time:");  
      Serial.println(global_position.time_boot_ms);
      #endif
    }
break;

However, the serial monitor does not show anything except this:

Hi, @FranklinMyn:

It looks like you have a double initialisation of the serial port and that you do not fully understand what the code does. According to your comment, in the setup you have:

// MAVLink interface start
Serial.begin(9600);

#ifdef SOFT_SERIAL_DEBUGGING
// [DEB] Soft serial port start
Serial.begin(57600);
Serial.println(“MAVLink starting.”);
pxSerial.begin(57600);
#endif

As you have defined you want SOFT_SERIAL_DEBUGGING, you are initialising the serial port first at 9600 bauds, then at 57600 bauds. I am wondering what is the wiring of your setup.

Please read carefully the post again. It indicates what is the purpose of using the software serial library and how you must connect your wires: software serial pins connect to the flight controller; board UART must go to your computer for debugging.

If you prefer so, send me a PM with the layout and the connections, I can try to assist you.

KR,
Juan Pedro

Oh I see. I have already changed the baud rate, and it seems the serial monitor is now showing the position data. Thanks, and I will PM you if I need any further help.

Hi, @FranklinMyn:

Good news!

And good luck, KR,
Juan Pedro

Hi Juan Pedro
thank you so much for these information.I am new on this topic and little bit confused.I run your code I got these messages on the serial port.I want to get gps information but I do not know how I can do it.
Do you have any suggestions?

Hi, @Gizem_Karabiber:

I can see that you are already receiving GPS messages (ids 24 and 33). You have all the details on what message brings what info here (link to message #33). Now re-read slowly the post to decode them and extract the data you need. I think the examples are pretty self-explanatory.

If you have more specific questions, you can always PM me.

KR,
Juan Pedro

Hi @jplopezll
Thank you very much your answer, That is work.
I want to consult you.can I send the coordinate information of destination point from arduino to pixhawk?My purpose here is to be able to send the coordinate to the plane while the plane is in the air.
Do you have any suggestions?
thanks in advance

Hi, @Gizem_Karabiber:

Yes, it is possible. You have to check the documentation of your flight controller to ensure it supports missions.

KR,
Juan Pedro

Nice article!
Works like a charm. I’d tested it with mega board and random MatekF405 FC.

Thanks and regards.