-
Notifications
You must be signed in to change notification settings - Fork 17.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add MAVLink receiver support, RADIO_RC_CHANNELS #24577
Conversation
The PR for the proposed message is mavlink/mavlink#1919 which would need to be added to ArduPilot's mavlink fork too. |
yes, something needs to be done in the ArduPilot's mavlink fork. It is frankly however not so clear (to me) what the best action would be; I see a couple of approaches. I admit that I'm also not totally sure (lack of knowledge) how the technical aspect of handling changes in submodules would work here. I think it would be very useful if you guys could come up with an opinion - or even decission - if
Maybe you find a chance to briefly discuss this in a dev call. THX! |
I'd like to ask again if you maybe could find time to briefly discuss this topic in general and come up with an opinion or even decission on it. I continue to argue, and see more and more signs pointing towards it, that this is just what "many" users would want to have, one connection for both mavlink and rc handling. What would be helpful to know is your general opinion. Like This PR is broken and needs an addition downstream in mavlink/pymavlink, sure, but easy enough. The question I wonder about is if it is worth to spend the effort. I'd be very happy to spend the effort, iff there is slight indication it may work out. Thx. |
IMO, think that this makes a lot of sense and should be explored. As wireless links evolve it seems ideal to have a single link that can handle both MAVLink + RC. |
65e6da7
to
ba71fd6
Compare
modules/Micro-XRCE-DDS-Client
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove the unrelated and likely unintentional changes to submodules like the XRCE Client lib.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Try git checkout origin/master -- modules/Micro-XRCE-DDS-Client
assuming your remote is origin
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MANY thx. Found a different workaround in the mean time. Thx again :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated changes need removed prior to merge.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
needs an autotest, and needs cleanup of the submodule changes (remove ChibiOS and XRCE submodule changes)
on your question, the concept is good I think, just needs a bit of re-work
libraries/GCS_MAVLink/GCS.h
Outdated
@@ -746,6 +747,9 @@ class GCS_MAVLINK | |||
mavlink_message_t _channel_buffer; | |||
mavlink_status_t _channel_status; | |||
|
|||
// for mavlink radio | |||
mavlink_radio_t _mavlink_radio_packet; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
better to make this a pointer and allocate on first receiver so we don't pay for it on every connection
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is actually not needed for this PR but a leftover. Will be needed for a followup PR doing link statistics. Field removed.
@@ -6407,6 +6410,12 @@ bool GCS_MAVLINK::accept_packet(const mavlink_status_t &status, | |||
return true; | |||
} | |||
|
|||
// handle messages from a mavlink receiver | |||
if ((msg.compid == MAV_COMP_ID_TELEMETRY_RADIO) && | |||
(msg.msgid == MAVLINK_MSG_ID_RADIO_RC_CHANNELS)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this bypasses signing checks, for a packet that can control the vehicle, this is very dangerous, we must not bypass those checks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I 100% agree that signing checks should not be bypassed, but I can't see anything in the function accept_packet() which would do something related to signing.
Could you maybe drop a hint of what I'm missing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll need to look when not on my phone, I thought signing used this for a bypass check
@@ -140,6 +140,13 @@ bool MAVLink_routing::check_and_forward(GCS_MAVLINK &in_link, const mavlink_mess | |||
int16_t target_component = -1; | |||
get_targets(msg, target_system, target_component); | |||
|
|||
// handle messages from a mavlink receiver as if it had target_system = our system, target_component = 0 | |||
if ((msg.compid == MAV_COMP_ID_TELEMETRY_RADIO) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does the msg not have target_system?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no. Pl see the post on this below. #24577 (comment).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This precludes having redundant receivers on-board.
You can target the message easily enough - we use the same technique on things like ADSB receivers. If no explicit target has been specified on the device then it waits for a heartbeat to arrive and (after filtering for appropriate vehicle type and MAV_TYPE
). It then locks onto the flight controller, targetting it and using a source system the same as that flight controller. component ID can default to TELEMETRY_RADIO but should also be configurable (and that ID should have no relevance to the way the packet is processed on the flight controller, to allow for redundancy)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename files for case sensitivity (i.e. MAVLink not Mavlink)
I think I like this concept.
if ((msg.compid == MAV_COMP_ID_TELEMETRY_RADIO) && | ||
(msg.msgid == MAVLINK_MSG_ID_RADIO_RC_CHANNELS)) { | ||
target_system = mavlink_system.sysid; | ||
target_component = 0; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't look great
Add fields to the packet for target system/component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
concerning targets, pl see post below. #24577 (comment).
@olliw42 please ping on code-review discord channel when ready for re-review |
This work looks like it may also be helpful for the in-flight dev work to add mavlink as a serial protocol to ELRS. |
ba71fd6
to
c8570f5
Compare
first off, many thx for the positive general response, this is very helpfull and encouraging. on the topic of target fields for this message. It does not have any. The reasons are outlined in the first post of mavlink/mavlink#1921, in the two subchapters "Routing", and in last post in its last subchapter. This is a very relevant "detail" and should be done right, and thus discussed well. |
You can put the target field. |
This PR needs an associated PR to https://github.com/ArduPilot/mavlink in order to bring in the RADIO_RC_CHANNELS message. Which is a serious problem for me. It seems that github does not allow to have forks of both the main repo and a fork of that main repo. Unfortunately I do have a fork of https://github.com/mavlink/mavlink, of which https://github.com/ArduPilot/mavlink is a fork of. It seems I'm doomed. What can I do? In the meanwhile, I have rebased and updated the respective PR to the main repo: mavlink/mavlink#1919. I also add a post there listening the discussion points, like the targets topic. |
I sure have thought about this but found it not to achieve the desired goals, that is, comes with costs which outweight the benefits. IMHO. In my mind the message should "exist" only on the system, but not outside of it. However, if it is generally considered acceptable that these messages are normally broadcasted, even beyond the system, then there is no issue and I'm trying to solve a non-issue. I could reproduce here now the arguments and discussion in the linked posts,but I'm not sure how helpfull it would be to reproduce all arguments, in essentially just different words. Maybe I can kindly ask you to read the two respective paragraphs in the first post of mavlink/mavlink#1921. I have just reread them and find then sufficiently ok. The reasons IMHO are largely the same reasons for why you find special code for The extend, let me mention few things which are different to a normal MAVLink component (autopilot, gimbal, companion,...)
Each of these point can be debated, with pros and cons. Note also that the RADIO_RC_CHANNELS message (and others of this set not part of this PR) must not be emitted by anything else than a mavlink receiver (without this constraint it is indeed easily possible to create all sorts of situations producing conflicts) |
I have read your other post before answering. Normally, your intend for this message is that it is send directly from the receiver to whatever will handle its msg content and should prevent the forwarding by the special handling ? I got it right ? What I say is : instead of doing implicity mavlink broadcast, add the target field. It will do explicit mavlink broadcast. Then we can still apply the special handling. It shouldn't change anything on your case and PR. |
many thx for your further comments. Your reasoning is clear, but I do have a bit of a different perspective on it.
absolutely
here I divert. I read "send directly" to imply a fully targeted message, going just from the receiver to exactly one other endpoint (target_sysid != 0 and target_compid != 0 in mavlink routing speach). I instead think that normally it should be send to all components of the system, autopilot, gimbal, companion, camera, .... whatever. That is target_sysid != 0 and target_compid == 0, in mavlink routing speach.
I agree with you in adding the target fields in so far as it is better to add them now for just in case. One drawback of the design of RADIO_RC_CHANNELS is that it can't be extended later (at least not without dismaying cost). So, any mistake now will bite later. In that sense it's better to just add them. If you guys would not mind having the speical handling, problem seems to be solved. However: Let's think for a moment about how it would read in the standard's docs. I mean, eventually the message should become standard and move into common.xml, right. We would essentially suggest to write: "The message has target fields but these should not be normally used and in fact each implementation should add special code to handle the message properly in the normal cases."? I would find that pretty odd in terms standardization, and I have doubts it would run well. Also, if that special code is there, you can't get around it by pure mavlink means (i.e. not changing the code), so the target_sysid field will be irrelevant, and all you could do is to play with target_compid. I want to mention again that there never ever should be a situation there one would send that message to anything else than components of the system it is connected to. (in particular it must not send e.g. from the ground to the vehicle, or from the vehicle to the ground). That is target_sysid must always be that of the system. So in the docs we actually would write "The message has target fields but these should not be normally used and in fact each implementation should add special code to handle the message properly in the normal cases. In addition target_sysid must be that of the system the receiver is connected to". In my mind the two things "special code to handle routing" and "target fields for normal mavlink routing" are kind of mutually exclusive options. I will not mind all that, if that is what the preference is. I'm just not convinced. :) |
c8570f5
to
7c7f636
Compare
@olliw42 ok, I understand and agree on why it doesn't have target fields |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm stuck on my phone for this review, so hard to look in detail sorry
} | ||
|
||
// update the field, so that the backend can see it | ||
memcpy(&(mavlink_radio.rc_channels), packet, sizeof(mavlink_radio_rc_channels_t)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be a structure assignment instead of memcpy?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added virtual handlers to backend, which makes this field and line go altogether. Done.
// handle mavlink radio | ||
void handle_radio_rc_channels(const mavlink_radio_rc_channels_t* packet); | ||
struct { | ||
mavlink_radio_rc_channels_t rc_channels; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to be chewing a chunk of ram per instance? We have a lot of instances
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is located in the frontend, so should be "just" one of them, as I understand it.
Very usefull comment. Added virtual handlers to backend, which makes this field go altogether. Nice. Done.
|
||
uint16_t rc_chan[MAX_RCIN_CHANNELS]; | ||
for (uint8_t i = 0; i < count; i++) { | ||
rc_chan[i] = ((int32_t)frontend.mavlink_radio.rc_channels.channels[i] * 5) / 32 + 1500; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A comment explaining this maths would be nice
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice. done.
@@ -61,3 +61,7 @@ | |||
#ifndef AP_RCPROTOCOL_FASTSBUS_ENABLED | |||
#define AP_RCPROTOCOL_FASTSBUS_ENABLED AP_RCPROTOCOL_BACKEND_DEFAULT_ENABLED && AP_RCPROTOCOL_SBUS_ENABLED | |||
#endif | |||
|
|||
#ifndef AP_RCPROTOCOL_MAVLINK_RADIO_ENABLED | |||
#define AP_RCPROTOCOL_MAVLINK_RADIO_ENABLED AP_RCPROTOCOL_BACKEND_DEFAULT_ENABLED |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe default enable only on 2M flash boards? I haven't looked at flash cost of the feature
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@@ -6407,6 +6410,12 @@ bool GCS_MAVLINK::accept_packet(const mavlink_status_t &status, | |||
return true; | |||
} | |||
|
|||
// handle messages from a mavlink receiver | |||
if ((msg.compid == MAV_COMP_ID_TELEMETRY_RADIO) && | |||
(msg.msgid == MAVLINK_MSG_ID_RADIO_RC_CHANNELS)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll need to look when not on my phone, I thought signing used this for a bypass check
7c7f636
to
20c1d70
Compare
|
@tridge
|
it would be much appreciated if you maybe could find time again to briefly discuss this PR in the dev call. It seems to me it has made progress, and it would be sad if it couldn't be finished. Many thx in advance. |
@jlpoltrack thankfully volunteered to submit the PR for merging the RDAIO_RC_CHANNELS message into your mavlink repo: ArduPilot/mavlink#343. so, the point which is left to settle are the signing concerns. |
Addendum: Addendum II: |
@peterbarker @khancyr @tridge devcall maybe? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking good, I just have concerns about the lack of targetting.
My major concern on the lack of targetting of the mavlink message is that (a) it's relatively easily solveable in a user-friendly manner, and that if you've got swarms of vehicles seeing each other's traffic you really don't want vehicles seeing other vehicle's messages! If you have your RC receiver feeding into e.g. cmavnode
- then you're likely to get that sort of problem!
@@ -140,6 +140,13 @@ bool MAVLink_routing::check_and_forward(GCS_MAVLINK &in_link, const mavlink_mess | |||
int16_t target_component = -1; | |||
get_targets(msg, target_system, target_component); | |||
|
|||
// handle messages from a mavlink receiver as if it had target_system = our system, target_component = 0 | |||
if ((msg.compid == MAV_COMP_ID_TELEMETRY_RADIO) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This precludes having redundant receivers on-board.
You can target the message easily enough - we use the same technique on things like ADSB receivers. If no explicit target has been specified on the device then it waits for a heartbeat to arrive and (after filtering for appropriate vehicle type and MAV_TYPE
). It then locks onto the flight controller, targetting it and using a source system the same as that flight controller. component ID can default to TELEMETRY_RADIO but should also be configurable (and that ID should have no relevance to the way the packet is processed on the flight controller, to allow for redundancy)
not necessarily, the different receivers could be given different system id's. I agree however that this is not what one may like, but fortunately MAVLink provides a canonical pattern for how to handle that. One should define multiple MAV_COMP_ID_TELEMETRY_RADIO, i.e. also in additon MAV_COMP_ID_TELEMETRY_RADIO2, MAV_COMP_ID_TELEMETRY_RADIO3, ... These are not currently existing in MAVLink. But could be added any time, on desire.
we are approaching a limit cycle. I had explained why it's not a good option to having to wait for a heartbeat. I very well know this mechanism and use it e.g. also in the STorM32 controller and it's working great there too. But not so well here. Let me try again. In this approach the rc channels data are send out only after a flight controller has been detected. This is - IMHO - absolutely not hwat one wants, as a user. E.g.,
I just see a bag full of inconveniences. Let's put it that way: If that would be what the final result would be I would actually be totally not interested in using such a receiver...
that's why there are these routing rules which say that the message should be routed as if it had target_system = system id and target_component = 0 so as to not ever go beyond it's attached system. :) |
20c1d70
to
774203c
Compare
I absolutely do not subscribe to the
I'm sorry, I must have missed that argument somewhere. Can you point that out somewhere, please?
Or manually configuring a target system/component on the unit.
And if users want to use a separate RC receiver for the camera vs the flight controller? In the case you here you'd need to configure the source system manually on the device providing hte mavlink message. Also a good use of target system/component - but does require configuration
Not without configuring the source system manually. Configurability of the source component is important to allow for redundancy.
.. no, just configuration. 99% of cases out there you plug-and-play. 1% of the cases require (and probably desire!) configuration.
We shouldn't need to add routing rules for new messages, and "legacy" software NOT having this filtering/routing (root cause of tacking routing onto MAVLink based on magic field names/explicit listing) mean that these messages would leak and create havoc. |
many thx for taking your time and looking at it again, but I think it's the type of discussion I don't want to be in. Concerning heartbeat and type and id, I normally couldn't agree more, and I believe that some years back it was me who worked hard to convince the mavlink folks that this is how it has to be, and get respective language into the docs. Yet for the mavlink receiver I came (and still come) to a different conclusion, IMHO it just adds loads of complexity for no purpose except of clinging to a principle, using a sledgehammer to crack a nut. It's a weired discussion. There is absolutely nothing in ArduPilot which would adhere to that mavlink principle you've mentioned, to the contrary. I just looked up again how some mavlink components are handled, to reasure that's accurate. For mavlink components like GPS, optical flow, proximity, rangefinder, OSD, the handling is as crude as it can be, and far off any principle like the one you suggested. Gimbal and camera are somewhat better, since here there is indeed a find phase, yet it's strictly required to use predefined gimbal and camera ids, in obvious contrast to your position. The greemsy driver (and possibly others) breaks mavlink principles also in other fundamental ways. So, frankly, it feels like been drawn into a discussion which ArduPilot hasn't settled for itself, and most of the time is happy to violate, and all what is clear to me is that I don't want to be in that discussion. Concerning ease of use, user friendliness, we are not going to share common ground. All this configuration talk may indeed look easy and natural to you, but it's a reason for the sentiment that ArduPilot is - comparatively - complicated to set up and use. ArduPilot's mindset doesn't appear to match up with most user's idea of ease of use, and to be frank, I here will trust my own judgment much more than that of any ArduPilot member. So, where to go from here. Your comments imply that the mavlink receiver has to also emit a heartbeat, and that the discovery of that component should be based on it and moreover be untight from any predefined ids. If that is the position, I'd like to say that it's not me who is going to pursue this or even try to implement this. In that case, it probably would be appropiate to just close that PR. If however all we are talking about is the routing and target id thing, i.e. no heartbeat and type and etc. pp, then we may have a deal. It's easy enough to modify the above code and the xml definition, and I will be fine with doing this (see #25838 for how it may look like). To sum up, I see two options:
|
@peterbarker I find myself on the fence with this: as we've discussed, I agree that ensuring that the implementation covers as many use cases as possible is important, but I think we also need to ensure that we don't let perfect be the enemy of good. Would getting this in as a compile/custom build server option provide sufficient protection for you? I think that if we do that, it provides both a safety net and a base to continue to evolve this. |
@peterbarker @auturgy what's up?
no good? dead horse? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very partial review only; I was only looking for the handling of the failure flags
rc_chan[i] = ((int32_t)packet->channels[i] * 5) / 32 + 1500; | ||
} | ||
|
||
bool failsafe = (packet->flags & RADIO_RC_CHANNELS_FLAGS_FAILSAFE); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not sufficient. If the values are frozen we don't want to use them either!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what you mean by "froozen"
the case when frames are not received is handled by the upper level code
if you mean handling missing frames bit: IMHO this is exactly as for sbus
This is the first piece of adding what should be called a "MAVLink receiver".
This PR requires that the storm32.xml mavlink dialect file in https://github.com/ArduPilot/mavlink/tree/master/message_definitions/v1.0 is updated to that in the main mavlink repository.
The idea is simple, namely a receiver which does send its RC channel data (and other data such as link statistics) not via common protocols like SBus or CRSF but via a MAVLink message. Since it's such an obvious concept and so much desired, at least in the hobby world, I don't really know what else to say to make the case.
This message is implemented since in a while in my BetaPilot/Copter fork as well as in the mLRS project which provides a practical example of a MAVLink receiver, and also establishes a proof of concept, as well as demonstrates that the code PR'ed here works (in the setups used by those who tested/use it).
The RADIO_RC_CHANNELS mavlink message is currently located in the storm32.xml with ID 60045, but I'm happy to remove it from there and move it to any other location with any other ID you guys would suggest.