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

mavlink / MAVSDK / 16485382075

24 Jul 2025 01:13AM UTC coverage: 46.329% (+1.2%) from 45.096%
16485382075

push

github

web-flow
Merge pull request #2610 from mavlink/pr-add-libmavlike

Integrate parts of libmav into MAVSDK and add MavlinkDirect plugin

764 of 987 new or added lines in 14 files covered. (77.41%)

10 existing lines in 1 file now uncovered.

16272 of 35123 relevant lines covered (46.33%)

360.36 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
    mav::MessageSet& message_set,
54
    std::string path,
55
    int baudrate,
56
    bool flow_control,
57
    ForwardingOption forwarding_option) :
×
58
    Connection(
NEW
59
        std::move(receiver_callback),
×
NEW
60
        std::move(libmav_receiver_callback),
×
61
        message_set,
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

NEW
80
    if (!start_libmav_receiver()) {
×
NEW
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
    std::pair<bool, std::string> result;
×
267

268
    if (_serial_node.empty()) {
×
269
        LogErr() << "Dev Path unknown";
×
270
        result.first = false;
×
271
        result.second = "Dev Path unknown";
×
272
        return result;
×
273
    }
274

275
    if (_baudrate == 0) {
×
276
        result.first = false;
×
277
        result.second = "Baudrate unknown";
×
278
        return result;
×
279
    }
280

281
    uint8_t buffer[MAVLINK_MAX_PACKET_LEN];
×
282
    uint16_t buffer_len = mavlink_msg_to_send_buffer(buffer, &message);
×
283

284
    int send_len;
285
#if defined(LINUX) || defined(APPLE)
286
    send_len = static_cast<int>(write(_fd, buffer, buffer_len));
×
287
#else
288
    if (!WriteFile(_handle, buffer, buffer_len, LPDWORD(&send_len), NULL)) {
289
        std::stringstream ss;
290
        ss << "WriteFile failure: " << GET_ERROR();
291
        LogErr() << ss.str();
292
        result.first = false;
293
        result.second = ss.str();
294
        return result;
295
    }
296
#endif
297

298
    if (send_len != buffer_len) {
×
299
        result.first = false;
×
300
        result.second = "Baudrate unknown";
×
301

302
        std::stringstream ss;
×
303
        ss << "write failure: " << GET_ERROR();
×
304
        LogErr() << ss.str();
×
305
        result.first = false;
×
306
        result.second = ss.str();
×
307
        return result;
×
308
    }
×
309

310
    result.first = true;
×
311
    return result;
×
312
}
313

314
void SerialConnection::receive()
×
315
{
316
    // Enough for MTU 1500 bytes.
317
    char buffer[2048];
×
318

319
#if defined(LINUX) || defined(APPLE)
320
    struct pollfd fds[1];
×
321
    fds[0].fd = _fd;
×
322
    fds[0].events = POLLIN;
×
323
#endif
324

325
    while (!_should_exit) {
×
326
        int recv_len;
327
#if defined(LINUX) || defined(APPLE)
328
        int pollrc = poll(fds, 1, 1000);
×
329
        if (pollrc == 0 || !(fds[0].revents & POLLIN)) {
×
330
            continue;
×
331
        } else if (pollrc == -1) {
×
332
            LogErr() << "read poll failure: " << GET_ERROR();
×
333
        }
334
        // We enter here if (fds[0].revents & POLLIN) == true
335
        recv_len = static_cast<int>(read(_fd, buffer, sizeof(buffer)));
×
336
        if (recv_len < -1) {
×
337
            LogErr() << "read failure: " << GET_ERROR();
×
338
        }
339
#else
340
        if (!ReadFile(_handle, buffer, sizeof(buffer), LPDWORD(&recv_len), NULL)) {
341
            LogErr() << "ReadFile failure: " << GET_ERROR();
342
            continue;
343
        }
344
#endif
345
        if (recv_len > static_cast<int>(sizeof(buffer)) || recv_len == 0) {
×
346
            continue;
×
347
        }
348
        _mavlink_receiver->set_new_datagram(buffer, recv_len);
×
349
        // Parse all mavlink messages in one data packet. Once exhausted, we'll exit while.
350
        while (_mavlink_receiver->parse_message()) {
×
351
            receive_message(_mavlink_receiver->get_last_message(), this);
×
352
        }
353
    }
354
}
×
355

356
#if defined(LINUX)
357
int SerialConnection::define_from_baudrate(int baudrate)
×
358
{
359
    switch (baudrate) {
×
360
        case 9600:
×
361
            return B9600;
×
362
        case 19200:
×
363
            return B19200;
×
364
        case 38400:
×
365
            return B38400;
×
366
        case 57600:
×
367
            return B57600;
×
368
        case 115200:
×
369
            return B115200;
×
370
        case 230400:
×
371
            return B230400;
×
372
        case 460800:
×
373
            return B460800;
×
374
        case 500000:
×
375
            return B500000;
×
376
        case 576000:
×
377
            return B576000;
×
378
        case 921600:
×
379
            return B921600;
×
380
        case 1000000:
×
381
            return B1000000;
×
382
        case 1152000:
×
383
            return B1152000;
×
384
        case 1500000:
×
385
            return B1500000;
×
386
        case 2000000:
×
387
            return B2000000;
×
388
        case 2500000:
×
389
            return B2500000;
×
390
        case 3000000:
×
391
            return B3000000;
×
392
        case 3500000:
×
393
            return B3500000;
×
394
        case 4000000:
×
395
            return B4000000;
×
396
        default: {
×
397
            LogErr() << "Unknown baudrate";
×
398
            return -1;
×
399
        }
400
    }
401
}
402
#endif
403

404
} // 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