# Sending and recieving heartbeats to BlueROV from surface computer using pymavlink

**URL:** <https://discuss.bluerobotics.com/t/sending-and-recieving-heartbeats-to-bluerov-from-surface-computer-using-pymavlink/10940>\
**Category:** Blue Robotics Software\
**Tags:** pymavlink\
**Created:** [November 1, 2021, 11:22pm UTC](https://discuss.bluerobotics.com/t/sending-and-recieving-heartbeats-to-bluerov-from-surface-computer-using-pymavlink/10940 "2021-11-01T23:22:19Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![joshholder](https://avatars.discourse-cdn.com/v4/letter/j/ce7236/32.png) [@joshholder](https://discuss.bluerobotics.com/u/joshholder)\
**Post date:** [November 1, 2021, 11:22pm UTC](https://discuss.bluerobotics.com/t/sending-and-recieving-heartbeats-to-bluerov-from-surface-computer-using-pymavlink/10940/1 "2021-11-01T23:22:19Z")

</div>

Hi,

We are trying to use the BlueROV as a testbed for various autonomous underwater control algorithms, so I’m trying to connect to and fire thrusters on the BlueROV through a surface computer running Python.

Thus far, I’ve been able to connect to the ROV via a UDP port as mentioned [here](https://www.ardusub.com/developers/pymavlink.html#run-pymavlink-on-the-surface-computer), and send commands to fire individual thrusters using MAV\_CMD\_DO\_MOTOR\_TEST as mentioned [here](https://discuss.bluerobotics.com/t/single-thruster-control/2970).

However, when I try to send motor commands or read sensor data for an extended period of time, QGroundControl plays a sound saying “Communication lost” and it seems that I am no longer getting updated data.

I’ve read that you need to send and recieve heartbeat commands at at least 1Hz, so I attempted to execute the following code ([inspired from here](https://mavlink.io/en/mavgen_python/#heartbeat)) in a loop while reading sensor data, but I’m still getting the “communication lost message.”

```python
master.mav.heartbeat_send(
            6, #MAVTYPE = MAV_TYPE_GCS
            8, #MAVAUTOPILOT = MAV_AUTOPILOT_INVALID
            128, # MAV_MODE = MAV_MODE_FLAG_SAFETY_ARMED, have also tried 0 here
            0,0)

```

Does anyone know why I might still be struggling to connect with this setup? Do I need explicit code to receive heartbeats from the BlueROV as well? If so, is it anything more than simply `master.wait_heartbeat()`?

Thanks for the help in advance - I’m still new to this framework so I’m very much in the learning process.

---

<div class="post-metadata">

**Author:** ![EliotBR](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.bluerobotics.com/eliotbr/32/6937_2.png) [@EliotBR](https://discuss.bluerobotics.com/u/EliotBR)\
**Post date:** [November 2, 2021, 3:52pm UTC](https://discuss.bluerobotics.com/t/sending-and-recieving-heartbeats-to-bluerov-from-surface-computer-using-pymavlink/10940/2 "2021-11-02T15:52:34Z")

</div>

Hi @joshholder, welcome to the forum 🙂

Great to see you’ve looked into this before asking about it. This has been asked about a few times recently, and today I had a thought for a hacky workaround that would make it possible, which I’ve described (and implemented) [here](https://discuss.bluerobotics.com/t/how-to-control-thrusters-independently/9870/11).

> [@joshholder](#):
>
> However, when I try to send motor commands or read sensor data for an extended period of time, QGroundControl plays a sound saying “Communication lost” and it seems that I am no longer getting updated data.

Are you using port 14550 for pymavlink, or have you set up a separate endpoint for it? Two programs aren’t supposed to be able to access the same port at the same time, and 14550 is what QGC uses by default.

> [@joshholder](#):
>
> Do I need explicit code to receive heartbeats from the BlueROV as well? If so, is it anything more than simply “master.wait\_heartbeat()”?

Receiving heartbeats isn’t a requirement (since the other end doesn’t know or care who is listening to its heartbeats). `master.wait_heartbeat()` is the correct code to wait for one though, when it’s relevant to do so.

---

<div class="post-metadata">

**Author:** ![joshholder](https://avatars.discourse-cdn.com/v4/letter/j/ce7236/32.png) [@joshholder](https://discuss.bluerobotics.com/u/joshholder)\
**Post date:** [November 15, 2021, 5:34pm UTC](https://discuss.bluerobotics.com/t/sending-and-recieving-heartbeats-to-bluerov-from-surface-computer-using-pymavlink/10940/3 "2021-11-15T17:34:07Z")

</div>

Thanks @EliotBR ! I’ll try using that workaround - it seems way more robust than using DO\_MOTOR\_TEST. I also changed my port from the one QGC uses, which fixed the issue of QGC saying disconnected.

However, even after doing the above and adding consistent heartbeats I’m still having issues reading sensor data. I’m grabbing attitude data using the following command:

att = self.master.recv\_match(type=‘ATTITUDE’,blocking=True).to\_dict()

Using this syntax, when I physically move the ROV the sensor readings I’m getting don’t seem to update, or if they do, they update by drifting to the correct values rather than actually tracking the movement. I’m not sure if this is even a heartbeat issue at this point, but is there any reason you can think of why my sensor readings would be off? I’ve also tried grabbing data from the AHRS ports, with the same result.

---

<div class="post-metadata">

**Author:** ![EliotBR](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.bluerobotics.com/eliotbr/32/6937_2.png) [@EliotBR](https://discuss.bluerobotics.com/u/EliotBR)\
**Post date:** [November 16, 2021, 3:37am UTC](https://discuss.bluerobotics.com/t/sending-and-recieving-heartbeats-to-bluerov-from-surface-computer-using-pymavlink/10940/4 "2021-11-16T03:37:28Z")

</div>

Not sure why your readings are delayed. I’ve just tried the following (both with and without QGC open) and didn’t have noticeable delay when moving the ROV around:

1. Add a mavlink server at port 14660 in the mavproxy settings ([http://192.168.2.2:2770/mavproxy](http://192.168.2.2:2770/mavproxy)):

```auto
--out udpin:0.0.0.0:14660

```

2. Press “Restart MAVProxy”
3. Connect using pymavlink:

```python
from rc_override_example import Autopilot

# OR: with Autopilot.connect_to_server(ip='192.168.2.2', port=14660) as sub:
with Autopilot('udpout:192.168.2.2:14660', client=False) as sub:
    # press CTRL+C to quit
    while "testing attitude updates":
        # get the attitude
        msg = sub.recv_match(type='ATTITUDE', blocking=True)
        # extract parameters of interest
        roll, pitch, yaw = msg.roll, msg.pitch, msg.yaw
        # print to terminal/console
        print(f'{roll = :.3f}, {pitch = :.3f}, {yaw = :.3f}')

```

* * *

It also worked fine with a mavlink client instead of a server:

```auto
--out udpout:192.168.2.1:14660

```

connecting with

```auto
# OR: with Autopilot.connect_to_client(port=14660) as sub:
with Autopilot('udpin:0.0.0.0:14660', client=True) as sub:
    ...

```

* * *

If that doesn’t work for you/help with your issue, which versions of Companion and ArduSub are you using?

If it does help but you’d rather use your own code structure, feel free to post the code you’re using and I’ll see if I can spot any issues, or try to replicate the issues you’re having here 🙂

---

<div class="post-metadata">

**Author:** ![joshholder](https://avatars.discourse-cdn.com/v4/letter/j/ce7236/32.png) [@joshholder](https://discuss.bluerobotics.com/u/joshholder)\
**Post date:** [November 17, 2021, 4:59pm UTC](https://discuss.bluerobotics.com/t/sending-and-recieving-heartbeats-to-bluerov-from-surface-computer-using-pymavlink/10940/5 "2021-11-17T16:59:38Z")

</div>

@EliotBR My code was extremely similar to yours, just using a different port. However, even after switching to 14660, I’m still having issues with sensors not updating. Just in case though, here’s a version of my code that doesn’t seem to work:

```auto
from pymavlink import mavutil
import time

if __name__ == " __main__":
    # --out udpout:192.168.2.1:14660 in http://192.168.2.2:2770/mavproxy list
    master = mavutil.mavlink_connection('udpin:0.0.0.0:14660')
    master.wait_heartbeat()

    for i in range(800):
        #in real code this is sending in a separate thread
        master.mav.heartbeat_send(
                6, #MAVTYPE = 
                8, #MAVAUTOPILOT
                128, # MAV_MODE = 
                0,0) #MAVSTATE =

        att = master.recv_match(type='ATTITUDE',blocking=True).to_dict()
        print(att['roll'],att['pitch'],att['yaw'])
        time.sleep(.2)

```

According to [http://192.168.2.2:2770/system](http://192.168.2.2:2770/system), my companion computer software is version 0.0.17, and my ArduSub version is 3.5.4. I see [here](https://bluerobotics.com/learn/bluerov2-software-setup/#update-software) that the current version of the companion computer and ArduSub are 0.0.29 and 4.0.3, but I don’t see a button on [http://192.168.2.2:2770/system](http://192.168.2.2:2770/system) which tells me to update the software, and given that I can’t seem to connect to wifi, I looked at [this](https://discuss.bluerobotics.com/t/software-updates/1128) tutorial which says that ROVs purchased after June 2017 shouldn’t need to update. Given that, I’m not sure if you still think that software version could be an issue. If so, should I manually rewrite the SD card as described in the link?

---

<div class="post-metadata">

**Author:** ![EliotBR](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.bluerobotics.com/eliotbr/32/6937_2.png) [@EliotBR](https://discuss.bluerobotics.com/u/EliotBR)\
**Post date:** [November 18, 2021, 6:28am UTC](https://discuss.bluerobotics.com/t/sending-and-recieving-heartbeats-to-bluerov-from-surface-computer-using-pymavlink/10940/6 "2021-11-18T06:28:13Z")

</div>

> [@joshholder](#):
>
> my companion computer software is version 0.0.17, and my ArduSub version is 3.5.4.

Generally our first support response when someone has issues with software that’s older than the latest stable version is to ask them to update so we have an understood baseline to work from.

> [@joshholder](#):
>
> I looked at [this](https://discuss.bluerobotics.com/t/software-updates/1128) tutorial which says that ROVs purchased after June 2017 shouldn’t need to update.

That post (which was also made in 2017) is specifically referring to updating **to** ArduSub 3.5. ROVs bought later than June 2017 would already be on ArduSub 3.5 or later, so they don’t need to update to ArduSub 3.5. That’s unrelated to the new releases we’ve made since then, including various bug fixes, improvements, and new features 🙂

> [@joshholder](#):
>
> I can’t seem to connect to wifi  
> …  
> should I manually rewrite the SD card as described in the link?

I’m not sure why you’re unable to access wifi, but there have been a few different issues with that for previous versions of Companion. Yes, I’d recommend you manually flash 0.0.29 onto your Companion SD card using our [Installing Companion instructions](https://www.ardusub.com/quick-start/installing-companion.html) 🙂

Once that’s complete you’ll then need to:

1. Connect Companion to wifi (should hopefully now be possible)
2. Update Companion to 0.0.30 from the System page (should be quite a quick update; we haven’t built an image for 0.0.30 because 0.0.31 is planned for release very soon, but it fixes an external issue that stops ArduSub firmwares from downloading so is required)
3. Update your ArduSub firmware to the latest Stable (4.0.3)

# Your Actual Issue

While I would still recommend you upgrade your software, your actual issue here is the `time.sleep(.2)` call. Pymavlink is just receiving the mavlink messages that mavproxy sends (i.e. it’s not _requesting_ anything), and attitude messages are generally sent faster than the 5Hz that you’re getting+printing messages at. That means your computer is collecting messages faster than you’re processing them, and each time you request the next one it gives you the oldest one it’s got (which leads to the large (and growing) delay between when you move the ROV vs when those moves show up in the printed values).

There are multiple ways of dealing with that, but the main principle is making sure you read the values faster than they’re arriving, so your program doesn’t fall behind. Likely the most intended way (including how the pymavlink developers would probably want heartbeats to be handled) is with periodic events that only trigger at their desired frequency:

```auto
from pymavlink import mavutil
import time

# --out udpout:192.168.2.1:14660 in http://192.168.2.2:2770/mavproxy list
master = mavutil.mavlink_connection('udpin:0.0.0.0:14660')
# ensure valid connection
master.wait_heartbeat()

heartbeat = mavutil.periodic_event(1) # 1 Hz heartbeat
display = mavutil.periodic_event(5) # 5 Hz output (every 0.2 seconds)

start = time.time()
duration = 160 # seconds

while time.time() - start < duration:
    if heartbeat.trigger():
        master.mav.heartbeat_send(
            mavutil.mavlink.MAV_TYPE_GCS,
            mavutil.mavlink.MAV_AUTOPILOT_INVALID,
            0,0,0)
    
    # read messages every iteration (to not fall behind)
    att = master.recv_match(type='ATTITUDE', blocking=True)
    
    if display.trigger():
        print(att.roll, att.pitch, att.yaw)

```

> [@joshholder](#):
>
> ```python
> #in real code this is sending in a separate thread
> master.mav.heartbeat_send(
> 
> ```

I would note that while having the heartbeat in a separate thread can definitely be convenient, [it’s not recommended as the default approach](https://mavlink.io/en/mavgen_python/#:~:text=Generally%20it%20should%20be%20sent%20from%20the%20same%20thread%20as%20all%20other%20messages%20(to%20ensure%20that%20the%20heartbeat%20is%20only%20published%20when%20the%20thread%20is%20healthy).) in the mavlink docs, and is also the main [issue that’s been raised with my `mavactive.py` proposal](https://github.com/ArduPilot/pymavlink/pull/577#issuecomment-898986574). The reasoning behind that is almost certainly that threads make debugging harder, and could in theory allow regular heartbeats to continue when the vehicle/controller is not in fact still ok/healthy.

I personally think that should be an individual choice (e.g. if you’re comfortable with threads and understand the potential pitfalls you could choose to use them when it’s appropriate), but I agree that threads aren’t perfect for heartbeats “in general”, and should be used carefully (if at all).

---

<div class="post-metadata">

**Author:** ![joshholder](https://avatars.discourse-cdn.com/v4/letter/j/ce7236/32.png) [@joshholder](https://discuss.bluerobotics.com/u/joshholder)\
**Post date:** [November 18, 2021, 9:50pm UTC](https://discuss.bluerobotics.com/t/sending-and-recieving-heartbeats-to-bluerov-from-surface-computer-using-pymavlink/10940/7 "2021-11-18T21:50:11Z")

</div>

Thank you so much! You managed to identify the critical gap in understanding that I had - sensor readings as well as thruster firings are working as of now.

So to clarify, within the mavlink\_connection() object, it stores a list of received messages. When calling master.recv\_match(type=‘ATTITUDE’), it grabs the first received ATTITUDE message on the list, returns it, and then removes it from the list. By calling recv\_match constantly, we keep the first entry on the list updated to the piece of data that was received most recently.

Follow-up question in this case: given that the list of received messages is always growing, is there some upper limit when messages start being culled from the list? Otherwise, wouldn’t the amount of data grow without bound?

Also, do you have recommendations for documentation sources for mavlink itself? I completely missed this subtlety in my research, and even now I’m not able to find great documentation on periodic\_event() or other mavlink functions.

---

<div class="post-metadata">

**Author:** ![EliotBR](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.bluerobotics.com/eliotbr/32/6937_2.png) [@EliotBR](https://discuss.bluerobotics.com/u/EliotBR)\
**Post date:** [November 19, 2021, 12:02am UTC](https://discuss.bluerobotics.com/t/sending-and-recieving-heartbeats-to-bluerov-from-surface-computer-using-pymavlink/10940/8 "2021-11-19T00:02:32Z")

</div>

> [@joshholder](#):
>
> Thank you so much! You managed to identify the critical gap in understanding that I had - sensor readings as well as thruster firings are working as of now.

Great to hear! 🙂

> [@joshholder](#):
>
> So to clarify, within the mavlink\_connection() object, it stores a list of received messages. When calling master.recv\_match(type=‘ATTITUDE’), it grabs the first received ATTITUDE message on the list, returns it, and then removes it from the list.

It’s not a list - it’s a stream of packets (the main difference being it’s only one-way - once you’ve received a message you can’t un-receive it, or go back through previous ones unless you’ve saved them yourself in some other data structure). Some pointers/notes:

- Calling `recv_match` with a type check parses that stream:
  1. converts the earliest data into a message
  2. stores the message in the `master.messages` dictionary (keeps track of the latest message of each type)
  3. returns the message if it’s one of the requested types, otherwise continues parsing
  4. If it gets through all the data that it’s received (no more messages available to read) without finding the requested type then it returns `None` by default.

- If you set `blocking=True` then instead of returning `None` when it runs out of data it does a blocking wait and just waits for more data until eventually the requested message type arrives (if it ever does).
- If you set a `timeout` then it will read messages either until it runs out of them or until the timeout has been reached.
- If both blocking and timeout are set then the timeout acts as an early-stop, that prevents endlessly blocking if the message isn’t arriving.
- For completeness, calling `recv_match` with no type check just tries to get and return _any_ message, if one is available, otherwise returns `None` (timeout and blocking apply as normal).

> [@joshholder](#):
>
> Follow-up question in this case: given that the list of received messages is always growing, is there some upper limit when messages start being culled from the list? Otherwise, wouldn’t the amount of data grow without bound?

Your operating system has a [default UDP buffer length](https://stackoverflow.com/questions/52795025/udp-buffers-where-are-they-whats-guaranteed), but it’s not a circular buffer (i.e. if it gets full then old packets aren’t overwritten - new ones are dropped). From what I can tell the [pymavlink UDP option](https://github.com/ArduPilot/pymavlink/blob/master/mavutil.py#L1008-L1033) doesn’t attempt to override/specify a buffer length, so it will depend on your operating system.

> [@joshholder](#):
>
> Also, do you have recommendations for documentation sources for mavlink itself? I completely missed this subtlety in my research

There seems to be another slight misconception here - Pymavlink is a Python interface to the MAVLink protocol, they’re not the same thing (i.e. there are [several other interfaces to the MAVLink protocol](https://mavlink.io/en/getting_started/use_libraries.html), in different programming languages). The [MAVLink documentation](https://mavlink.io/en/) is a bit tricky to navigate, but has reasonably good coverage of the mavlink protocol, available messages ([common](https://mavlink.io/en/messages/common.html) and [ardupilotmega](https://mavlink.io/en/messages/ardupilotmega.html) for ArduSub), and relevant [micro-services](https://mavlink.io/en/services/).

Pymavlink provides several utilities to make working with MAVLink easier, but has quite minimal documentation, and [the documentation that does exist](https://mavlink.io/en/mavgen_python/#overview) is written as though you already know how MAVLink works and happen to want to use a Python implementation of it (which is the case for the main developers of Pymavlink, but makes it not particularly beginner friendly).

The idea is seemingly to “lead by example”, in the sense that some [examples are provided in the pymavlink repository](https://github.com/ArduPilot/pymavlink/tree/master/examples), and if you want more information about particular functionality then you should go look in the code for how it’s implemented. We (Blue Robotics) have [our own set of ArduSub-specific examples](https://www.ardusub.com/developers/pymavlink.html) for small snippets of functionality, but the [`rc_override_example` gist](https://gist.github.com/ES-Alexander/ee1dd479dd728b7cef1bf936f9114e71) I made a few weeks ago is the first ‘complete program’ example we have.

> [@joshholder](#):
>
> even now I’m not able to find great documentation on periodic\_event() or other mavlink functions.

[`periodic_event`](https://github.com/ArduPilot/pymavlink/blob/master/mavutil.py#L1755-L1776) is a Python class which I found (quite recently) while looking through the `mavutil` implementation file, although it’s also used in some of the Pymavlink repo examples. It’s not related to MAVLink at all (it’s just Python code that checks whether a specified amount of time has passed) - it’s in `mavutil` because it’s a useful utility for working with MAVLink connections.

Because of Python’s popularity, Pymavlink is a reasonably common starting point for people wanting to start interfacing with MAVLink devices. The lack of beginner friendly docs is becoming enough of a support issue on our end that it will likely end up being one of our/my priorities early next year to write some decent introductory documentation that makes it easier for people to get started. I’m not yet sure if that would be best done generally and contributed to the official Pymavlink documentation, or if it makes more sense to target it at ArduSub and put it in our own docs. At minimum, we very much agree that the current Pymavlink docs don’t make it super easy to get started with development, and that’s important enough to us and the users of ArduSub that we’re planning to help change/fix it 🙂

---

<div class="post-metadata">

**Author:** ![isso](https://avatars.discourse-cdn.com/v4/letter/i/c57346/32.png) [@isso](https://discuss.bluerobotics.com/u/isso)\
**Post date:** [May 18, 2024, 4:39pm UTC](https://discuss.bluerobotics.com/t/sending-and-recieving-heartbeats-to-bluerov-from-surface-computer-using-pymavlink/10940/9 "2024-05-18T16:39:32Z")

</div>

> [@EliotBR](#):
>
> ```auto
> from pymavlink import mavutil
> import time
> 
> # --out udpout:192.168.2.1:14660 in http://192.168.2.2:2770/mavproxy list
> master = mavutil.mavlink_connection('udpin:0.0.0.0:14660')
> # ensure valid connection
> master.wait_heartbeat()
> 
> heartbeat = mavutil.periodic_event(1) # 1 Hz heartbeat
> display = mavutil.periodic_event(5) # 5 Hz output (every 0.2 seconds)
> 
> start = time.time()
> duration = 160 # seconds
> 
> while time.time() - start < duration:
> if heartbeat.trigger():
> master.mav.heartbeat_send(
> mavutil.mavlink.MAV_TYPE_GCS,
> mavutil.mavlink.MAV_AUTOPILOT_INVALID,
> 0,0,0)
>     
> # read messages every iteration (to not fall behind)
> att = master.recv_match(type='ATTITUDE', blocking=True)
>     
> if display.trigger():
> print(att.roll, att.pitch, att.yaw)
> 
> ```

Hi @EliotBR,

I’ve found your reply here while searching about RC override via pymavlink for arduplane.  
I’m trying to send RC overrides using a python script with pymavlink in RealFlight SITL. I noticed that RC input overrides are not matching RC outputs while flying in AUTO mode.

Here’s a brief walkthrough of what I did to investigate the issue:

- I inputted a square PWM in AUTO mode preceded by the wait command: `vehicle.wait_heartbeat()` . RCOUT matches RCIN in this case. This was in SITL where the plane was stationary, and I was just testing RC overrides.

![image](https://us1.discourse-cdn.com/flex019/uploads/bluerobotics/original/2X/2/230815d85726073e2426925b0965212ec4304adc.png)

- The, I tried the same setup while flying, but RC output did not match RC input.

---

<div class="post-metadata">

**Author:** ![isso](https://avatars.discourse-cdn.com/v4/letter/i/c57346/32.png) [@isso](https://discuss.bluerobotics.com/u/isso)\
**Post date:** [May 18, 2024, 4:40pm UTC](https://discuss.bluerobotics.com/t/sending-and-recieving-heartbeats-to-bluerov-from-surface-computer-using-pymavlink/10940/10 "2024-05-18T16:40:26Z")

</div>

![image](https://us1.discourse-cdn.com/flex019/uploads/bluerobotics/original/2X/4/4922f0dd4b13d651c1ded9ed94ca1073f89a4e52.png)

Could there be any considerations or configurations that I’m missing?

I’d really appreciate your help.
