Mavlink - my own class

Hi friends!
I’d like to write my own class that will allow me to receive my data from FC via Mavlink. I tried to find in repo the needed file with classes of mavlink, but didnt find anything. I found only directory GCS_MAVLink (in https://github.com/ArduPilot/ardupilot/tree/master/libraries), but, as I understand, this is for a ground station. Help, what do I need to find the file that I can edit (write my own class)? I have a very simple task - get my own data from UART of FC and receive them together with GPS-coordinates to the ground station. (every 100 ms). I try to do it more than three weeks, but didnt get success…(((

Seems to be a popular question lately. You probably don’t need a custom message type or class.

If you are compiling your own custom firmware, look at gcs::send_named_float().

If you want to avoid custom firmware and use Lua, instead, the following topics discuss ways to use the gcs:send_named_float() binding via serial or I2C:
Unsupported Sensors? Try Arduino + Lua! - Blog - ArduPilot Discourse

Display data send by companion computer in Mission Planner quick tab - Ground Control Software / Mission Planner - ArduPilot Discourse

1 Like

Dear Yuri_Rage,
Thank you very much for your response. I have read many of your posts and watched many of your videos on YouTube. You are a guru and a professional in ArduPilot.
I have already written some code that I am trying to use to transmit my data. Would you have the opportunity to look at it and correct it using the gcs::send_named_float() function? I have included the code below.
uart_satelite.lua (1.0 KB)

" If you are compiling your own custom firmware, look at [gcs::send_named_float()]"
I am not quite sure how to do this in my own firmware. If you could provide some more information on how to do this, I would be very grateful.

Might be more beneficial to just look at the example I posted yesterday for starter-one in the topic linked above. Your code is simple and straightforward but lacks some safeguards and (possibly) unnecessarily converts numbers to strings. My example overcomes that and should be somewhat easy to incorporate.

1 Like

Just use Lua. There’s no need for custom firmware if you’re using a serial port.

2 Likes

Thank you very much for your help. I will definitely study your example and use it. If I have any questions, may I contact you again?
Sincerely, Alexander, Ukraine

Just reply here, and I will continue to help as I can.

If you are bound to ingest serial data as ASCII characters that represent a number, the following snippet replaces the loop in my linked example. The MAX_BUFFER check provides a mechanism to flush the serial buffer without causing the Lua script to exceed its allocated runtime in the event of serial traffic backlog.

    local buffer = ''
    while port:available() > 0 do
        buffer = buffer .. string.char(port:read())
        if buffer:len() > MAX_BUFFER then
            return update, RUN_INTERVAL_MS
        end
    end

    if buffer:len() > 0 then
        -- insert error checking logic here
        local val = tonumber(buffer)
        if val then  -- tonumber returns nil on error, which evaluates to false
            gcs:send_named_float('VAL', val)
        end
    end
1 Like

Super! Awesome! Thank you so much!

As a further example for anyone reading, an alternative to string concatenation in the buffer variable assignment line could use string.format().

buffer = ('%s%c'):format(buffer, port:read())

…is functionally equivalent to:

buffer = buffer .. string.char(port:read())

1 Like

Does it make sense to use this method? (for example).
local myVar = string.pack(“I4I4ff”, 0xFEFEFE, 0, myPacket[1], myPacket[2])

I tried to use it, but I think your suggestion is the better way

string.pack() only makes sense if you’re trying to use raw binary types that are byte encoded. It’s probably easiest to stick with ASCII if you’re dealing with floating point numbers.

1 Like

Yuri, thank you! Today, I will start writing the code taking your advice into account. Thanks again!

Hi Yuri_Rage.
Write new code for my task. But cant understand - sometimes I get double data from my UART. I send from my arduino digital numbers from 1 to 6 every 5 ms for test (send as a string). And read every ten digitals and send them with data from gps to ground station. But sometimes I get, for example, instead of 123456123456 ... the such number: 612123456 or 123455612. Maybe, I need to insert some delay when I read data from UART? But I dont understand how to do it.
My code below:
uart_satelite_v2.lua (1.7 KB)

miss_plan

I’d venture a guess that you’re getting a mismatch in sending/polling timing.

First, 200Hz is at or potentially beyond the limit of what’s possible with Lua scripting. We should probably slow that down.

Whenever I make a project using asynchronous comms like this one, I typically poll for data at twice the rate that it is sent. While somewhat inefficient, I’ve found it to be the simplest way to ensure that I reliably get the data I want. Even so, I typically implement some sort of error checking so that I can validate the data received. This can be as simple as checking for the proper number of bytes, though it should probably also contain some sort of checksum or header/footer checking.

For now, I think you should slow the Arduino down to 50Hz (send data every 20ms), and then poll for it in the Lua script at 100Hz (run the script every 10ms). Trying to run any faster may become unreliable based on my observations with the scripting engine on an H743 processor.

1 Like

Yuri, thank you so much for your response and advice. You are absolutely right that too high of a frequency will introduce distortions into the operation of this script. Therefore, I am thinking of doing the following:

  1. Collect all the data into an array of 40 elements.
  2. Implement a synchronous version of UART ))), specifically: Arduino reads the port of the flight controller, and if a valid keyword is received, it starts transmitting the array.
  3. Since the length of the array is 40 elements, and each element represents data for 5 milliseconds, the array covers a range of 200 milliseconds. I read this array and then I read the data from the GPS and send it through the flight controller to the ground station. I achieve a polling and transmission frequency of 5 Hz.
    I would like to hear your opinion on this concept. Am I thinking in the right direction, or is this not the most optimal option?

Best regards, Alexander

If your goal is to simply collect and log the data, then batch sampling like this will probably work fine.

If you want to act on it in a timely fashion, obviously you are introducing delays, but trying to craft a 200Hz GCS based control loop is likely a fool’s errand.

1 Like

Yuri_Rage, thanks.
Your opinion is very important to me. Yes, my goal is to collect data and mirror them on the google-map in real-time. But I dont know exactly how long my data will be passed (this is 12 commands like this: gcs:send_named_float('value1', valueOne)). I couldnt find any information on how long one command is.

It’s a 32 bit floating point number, so I guess I was a bit incorrect about sending in batches - you won’t be sending multiple coordinates in those frames. I still have no earthly idea why you need the data at 200Hz just for a map display.

GPS data is updated at 20Hz, maximum (more often 5Hz, and sometimes as slow as 1Hz, depending on hardware), with the EKF filling in the gaps (at least on the autopilot…no idea what your 3rd party hardware is doing that ArduPilot can’t).

1 Like

Yuri, good time to you. Once again, I wanted to thank you for dedicating so much of your time to me. It was indeed challenging for you to understand my task because I didn’t initially explain it properly. I’ll rectify that mistake and provide a comprehensive description of my task.
I have a device that reads data from the ground. I need to capture the value roughly every 3 cm of travel. The speed of my drone is approximately 1 - 1.5 meters per second. Therefore, I need to capture about 100 values per second on average. The value ranges from 1 to 9. These values are read between two coordinate points. Since the drone doesn’t always fly in a straight line, I decided to read the current coordinate, then read values from 20 points and send this package to the ground station. The process repeats. This way, on the ground station, I know the coordinates of adjacent points between which I read values from 20 points. Thus, I can calculate the coordinates of each point and obtain a key-value array, where the key is the coordinate, and the value is the reading from each of these 20 points. If some points are lost during transmission, it’s not critical for me. Next, I overlay this data on a Google map and visualize the drone’s path and the readings of my device at each point of this path. Therefore, following your advice, I limited the number of GPS requests to 5 times per second, and between the GPS requests, I poll data from my sensor 20 times. Since a coordinate cannot be represented as a float, I split it into two parts (each coordinate), and on the ground station, I concatenate them back. This is a full description of my goal.
I would appreciate your advice on whether I have chosen the right strategy.

Best regards,
Alexander