• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mavlink / MAVSDK / 16952411343

14 Aug 2025 12:13AM UTC coverage: 46.441% (+0.3%) from 46.171%
16952411343

push

github

web-flow
Merge pull request #2637 from mavlink/pr-forward-unknown-messages

Enable forwarding unknown messages

140 of 174 new or added lines in 6 files covered. (80.46%)

12 existing lines in 2 files now uncovered.

16258 of 35008 relevant lines covered (46.44%)

368.39 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/mavsdk/core/serial_connection.cpp
1
#include "serial_connection.h"
2
#include "log.h"
3

4
#if defined(APPLE) || defined(LINUX)
5
#include <unistd.h>
6
#include <fcntl.h>
7
#include <termios.h>
8
#include <poll.h>
9

10
#include <utility>
11
#endif
12

13
#include <sstream>
14

15
namespace mavsdk {
16

17
#ifndef WINDOWS
18
#define GET_ERROR() strerror(errno)
19
#else
20
#define GET_ERROR() GetLastErrorStdStr()
21
// Taken from:
22
// https://coolcowstudio.wordpress.com/2012/10/19/getlasterror-as-stdstring/
23
std::string GetLastErrorStdStr()
24
{
25
    DWORD error = GetLastError();
26
    if (error) {
27
        LPVOID lpMsgBuf;
28
        DWORD bufLen = FormatMessage(
29
            FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
30
                FORMAT_MESSAGE_IGNORE_INSERTS,
31
            NULL,
32
            error,
33
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
34
            (LPTSTR)&lpMsgBuf,
35
            0,
36
            NULL);
37
        if (bufLen) {
38
            LPCSTR lpMsgStr = (LPCSTR)lpMsgBuf;
39
            std::string result(lpMsgStr, lpMsgStr + bufLen);
40

41
            LocalFree(lpMsgBuf);
42

43
            return result;
44
        }
45
    }
46
    return std::string();
47
}
48
#endif
49

50
SerialConnection::SerialConnection(
×
51
    Connection::ReceiverCallback receiver_callback,
52
    Connection::LibmavReceiverCallback libmav_receiver_callback,
53
    MavsdkImpl& mavsdk_impl,
54
    std::string path,
55
    int baudrate,
56
    bool flow_control,
57
    ForwardingOption forwarding_option) :
×
58
    Connection(
59
        std::move(receiver_callback),
×
60
        std::move(libmav_receiver_callback),
×
61
        mavsdk_impl,
62
        forwarding_option),
63
    _serial_node(std::move(path)),
×
64
    _baudrate(baudrate),
×
65
    _flow_control(flow_control)
×
66
{}
×
67

68
SerialConnection::~SerialConnection()
×
69
{
70
    // If no one explicitly called stop before, we should at least do it.
71
    stop();
×
72
}
×
73

74
ConnectionResult SerialConnection::start()
×
75
{
76
    if (!start_mavlink_receiver()) {
×
77
        return ConnectionResult::ConnectionsExhausted;
×
78
    }
79

80
    if (!start_libmav_receiver()) {
×
81
        return ConnectionResult::ConnectionsExhausted;
×
82
    }
83

84
    ConnectionResult ret = setup_port();
×
85
    if (ret != ConnectionResult::Success) {
×
86
        return ret;
×
87
    }
88

89
    start_recv_thread();
×
90

91
    return ConnectionResult::Success;
×
92
}
93

94
ConnectionResult SerialConnection::setup_port()
×
95
{
96
#if defined(LINUX) || defined(APPLE)
97
    // open() hangs on macOS or Linux devices(e.g. pocket beagle) unless you give it O_NONBLOCK
98
    _fd = open(_serial_node.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
×
99
    if (_fd == -1) {
×
100
        LogErr() << "open failed: " << GET_ERROR();
×
101
        return ConnectionResult::ConnectionError;
×
102
    }
103
    // We need to clear the O_NONBLOCK again because we can block while reading
104
    // as we do it in a separate thread.
105
    if (fcntl(_fd, F_SETFL, 0) == -1) {
×
106
        LogErr() << "fcntl failed: " << GET_ERROR();
×
107
        return ConnectionResult::ConnectionError;
×
108
    }
109
#elif defined(WINDOWS)
110
    // Required for COM ports > 9.
111
    const auto full_serial_path = "\\\\.\\" + _serial_node;
112

113
    _handle = CreateFile(
114
        full_serial_path.c_str(),
115
        GENERIC_READ | GENERIC_WRITE,
116
        0, // exclusive-access
117
        NULL, //  default security attributes
118
        OPEN_EXISTING,
119
        0, //  not overlapped I/O
120
        NULL); //  hTemplate must be NULL for comm devices
121

122
    if (_handle == INVALID_HANDLE_VALUE) {
123
        LogErr() << "CreateFile failed with: " << GET_ERROR();
124
        return ConnectionResult::ConnectionError;
125
    }
126
#endif
127

128
#if defined(LINUX) || defined(APPLE)
129
    struct termios tc;
130
    bzero(&tc, sizeof(tc));
×
131

132
    if (tcgetattr(_fd, &tc) != 0) {
×
133
        LogErr() << "tcgetattr failed: " << GET_ERROR();
×
134
        close(_fd);
×
135
        return ConnectionResult::ConnectionError;
×
136
    }
137
#endif
138

139
#if defined(LINUX) || defined(APPLE)
140
    tc.c_iflag &= ~(IGNBRK | BRKINT | ICRNL | INLCR | PARMRK | INPCK | ISTRIP | IXON);
×
141
    tc.c_oflag &= ~(OCRNL | ONLCR | ONLRET | ONOCR | OFILL | OPOST);
×
142
    tc.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG | TOSTOP);
×
143
    tc.c_cflag &= ~(CSIZE | PARENB | CRTSCTS);
×
144
    tc.c_cflag |= CS8;
×
145

146
    tc.c_cc[VMIN] = 0; // We are ok with 0 bytes.
×
147
    tc.c_cc[VTIME] = 10; // Timeout after 1 second.
×
148

149
    if (_flow_control) {
×
150
        tc.c_cflag |= CRTSCTS;
×
151
    }
152
#endif
153

154
#if defined(LINUX) || defined(APPLE)
155
    tc.c_cflag |= CLOCAL; // Without this a write() blocks indefinitely.
×
156

157
#if defined(LINUX)
158
    const int baudrate_or_define = define_from_baudrate(_baudrate);
×
159
#elif defined(APPLE)
160
    const int baudrate_or_define = _baudrate;
161
#endif
162

163
    if (baudrate_or_define == -1) {
×
164
        return ConnectionResult::BaudrateUnknown;
×
165
    }
166

167
    if (cfsetispeed(&tc, baudrate_or_define) != 0) {
×
168
        LogErr() << "cfsetispeed failed: " << GET_ERROR();
×
169
        close(_fd);
×
170
        return ConnectionResult::ConnectionError;
×
171
    }
172

173
    if (cfsetospeed(&tc, baudrate_or_define) != 0) {
×
174
        LogErr() << "cfsetospeed failed: " << GET_ERROR();
×
175
        close(_fd);
×
176
        return ConnectionResult::ConnectionError;
×
177
    }
178

179
    if (tcsetattr(_fd, TCSANOW, &tc) != 0) {
×
180
        LogErr() << "tcsetattr failed: " << GET_ERROR();
×
181
        close(_fd);
×
182
        return ConnectionResult::ConnectionError;
×
183
    }
184
#endif
185

186
#if defined(WINDOWS)
187
    DCB dcb;
188
    SecureZeroMemory(&dcb, sizeof(DCB));
189
    dcb.DCBlength = sizeof(DCB);
190

191
    if (!GetCommState(_handle, &dcb)) {
192
        LogErr() << "GetCommState failed with error: " << GET_ERROR();
193
        return ConnectionResult::ConnectionError;
194
    }
195

196
    dcb.BaudRate = _baudrate;
197
    dcb.ByteSize = 8;
198
    dcb.Parity = NOPARITY;
199
    dcb.StopBits = ONESTOPBIT;
200
    if (_flow_control) {
201
        dcb.fOutxCtsFlow = TRUE;
202
        dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
203
        dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
204
    } else {
205
        dcb.fDtrControl = DTR_CONTROL_DISABLE;
206
        dcb.fRtsControl = RTS_CONTROL_DISABLE;
207
    }
208
    dcb.fOutX = FALSE;
209
    dcb.fInX = FALSE;
210
    dcb.fBinary = TRUE;
211
    dcb.fNull = FALSE;
212
    dcb.fDsrSensitivity = FALSE;
213

214
    if (!SetCommState(_handle, &dcb)) {
215
        LogErr() << "SetCommState failed with error: " << GET_ERROR();
216
        return ConnectionResult::ConnectionError;
217
    }
218

219
    COMMTIMEOUTS timeout = {0, 0, 0, 0, 0};
220
    timeout.ReadIntervalTimeout = 1;
221
    timeout.ReadTotalTimeoutConstant = 1;
222
    timeout.ReadTotalTimeoutMultiplier = 1;
223
    timeout.WriteTotalTimeoutConstant = 1;
224
    timeout.WriteTotalTimeoutMultiplier = 1;
225
    SetCommTimeouts(_handle, &timeout);
226

227
    if (!SetCommTimeouts(_handle, &timeout)) {
228
        LogErr() << "SetCommTimeouts failed with error: " << GET_ERROR();
229
        return ConnectionResult::ConnectionError;
230
    }
231

232
#endif
233

234
    return ConnectionResult::Success;
×
235
}
236

237
void SerialConnection::start_recv_thread()
×
238
{
239
    _recv_thread = std::make_unique<std::thread>(&SerialConnection::receive, this);
×
240
}
×
241

242
ConnectionResult SerialConnection::stop()
×
243
{
244
    _should_exit = true;
×
245

246
    if (_recv_thread) {
×
247
        _recv_thread->join();
×
248
        _recv_thread.reset();
×
249
    }
250

251
#if defined(LINUX) || defined(APPLE)
252
    close(_fd);
×
253
#elif defined(WINDOWS)
254
    CloseHandle(_handle);
255
#endif
256

257
    // We need to stop this after stopping the receive thread, otherwise
258
    // it can happen that we interfere with the parsing of a message.
259
    stop_mavlink_receiver();
×
260

261
    return ConnectionResult::Success;
×
262
}
263

264
std::pair<bool, std::string> SerialConnection::send_message(const mavlink_message_t& message)
×
265
{
266
    // Convert message to raw bytes and use common send path
267
    uint8_t buffer[MAVLINK_MAX_PACKET_LEN];
268
    uint16_t buffer_len = mavlink_msg_to_send_buffer(buffer, &message);
×
269

NEW
270
    return send_raw_bytes(reinterpret_cast<const char*>(buffer), buffer_len);
×
271
}
272

273
void SerialConnection::receive()
×
274
{
275
    // Enough for MTU 1500 bytes.
276
    char buffer[2048];
277

278
#if defined(LINUX) || defined(APPLE)
279
    struct pollfd fds[1];
280
    fds[0].fd = _fd;
×
281
    fds[0].events = POLLIN;
×
282
#endif
283

284
    while (!_should_exit) {
×
285
        int recv_len;
286
#if defined(LINUX) || defined(APPLE)
287
        int pollrc = poll(fds, 1, 1000);
×
288
        if (pollrc == 0 || !(fds[0].revents & POLLIN)) {
×
289
            continue;
×
290
        } else if (pollrc == -1) {
×
291
            LogErr() << "read poll failure: " << GET_ERROR();
×
292
        }
293
        // We enter here if (fds[0].revents & POLLIN) == true
294
        recv_len = static_cast<int>(read(_fd, buffer, sizeof(buffer)));
×
295
        if (recv_len < -1) {
×
296
            LogErr() << "read failure: " << GET_ERROR();
×
297
        }
298
#else
299
        if (!ReadFile(_handle, buffer, sizeof(buffer), LPDWORD(&recv_len), NULL)) {
300
            LogErr() << "ReadFile failure: " << GET_ERROR();
301
            continue;
302
        }
303
#endif
304
        if (recv_len > static_cast<int>(sizeof(buffer)) || recv_len == 0) {
×
305
            continue;
×
306
        }
307
        _mavlink_receiver->set_new_datagram(buffer, recv_len);
×
308
        // Parse all mavlink messages in one data packet. Once exhausted, we'll exit while.
309
        while (_mavlink_receiver->parse_message()) {
×
310
            receive_message(_mavlink_receiver->get_last_message(), this);
×
311
        }
312

313
        // Also parse with libmav if available
314
        if (_libmav_receiver) {
×
315
            _libmav_receiver->set_new_datagram(buffer, recv_len);
×
316

317
            while (_libmav_receiver->parse_message()) {
×
318
                receive_libmav_message(_libmav_receiver->get_last_message(), this);
×
319
            }
320
        }
321
    }
322
}
×
323

324
#if defined(LINUX)
325
int SerialConnection::define_from_baudrate(int baudrate)
×
326
{
327
    switch (baudrate) {
×
328
        case 9600:
×
329
            return B9600;
×
330
        case 19200:
×
331
            return B19200;
×
332
        case 38400:
×
333
            return B38400;
×
334
        case 57600:
×
335
            return B57600;
×
336
        case 115200:
×
337
            return B115200;
×
338
        case 230400:
×
339
            return B230400;
×
340
        case 460800:
×
341
            return B460800;
×
342
        case 500000:
×
343
            return B500000;
×
344
        case 576000:
×
345
            return B576000;
×
346
        case 921600:
×
347
            return B921600;
×
348
        case 1000000:
×
349
            return B1000000;
×
350
        case 1152000:
×
351
            return B1152000;
×
352
        case 1500000:
×
353
            return B1500000;
×
354
        case 2000000:
×
355
            return B2000000;
×
356
        case 2500000:
×
357
            return B2500000;
×
358
        case 3000000:
×
359
            return B3000000;
×
360
        case 3500000:
×
361
            return B3500000;
×
362
        case 4000000:
×
363
            return B4000000;
×
364
        default: {
×
365
            LogErr() << "Unknown baudrate";
×
366
            return -1;
×
367
        }
368
    }
369
}
370
#endif
371

NEW
372
std::pair<bool, std::string> SerialConnection::send_raw_bytes(const char* bytes, size_t length)
×
373
{
NEW
374
    std::pair<bool, std::string> result;
×
375

NEW
376
    if (_serial_node.empty()) {
×
NEW
377
        LogErr() << "Dev Path unknown";
×
NEW
378
        result.first = false;
×
NEW
379
        result.second = "Dev Path unknown";
×
NEW
380
        return result;
×
381
    }
382

NEW
383
    if (_baudrate == 0) {
×
NEW
384
        result.first = false;
×
NEW
385
        result.second = "Baudrate unknown";
×
NEW
386
        return result;
×
387
    }
388

389
    int send_len;
390
#if defined(LINUX) || defined(APPLE)
NEW
391
    send_len = static_cast<int>(write(_fd, bytes, length));
×
392
#else
393
    if (!WriteFile(_handle, bytes, static_cast<DWORD>(length), LPDWORD(&send_len), NULL)) {
394
        std::stringstream ss;
395
        ss << "WriteFile failure: " << GET_ERROR();
396
        LogErr() << ss.str();
397
        result.first = false;
398
        result.second = ss.str();
399
        return result;
400
    }
401
#endif
402

NEW
403
    if (send_len != static_cast<int>(length)) {
×
NEW
404
        std::stringstream ss;
×
NEW
405
        ss << "write failure: " << GET_ERROR();
×
NEW
406
        LogErr() << ss.str();
×
NEW
407
        result.first = false;
×
NEW
408
        result.second = ss.str();
×
NEW
409
        return result;
×
NEW
410
    }
×
411

NEW
412
    result.first = true;
×
NEW
413
    return result;
×
414
}
415

416
} // namespace mavsdk
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc