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

mavlink / MAVSDK / 16247112396

13 Jul 2025 08:18AM UTC coverage: 46.221% (+1.0%) from 45.212%
16247112396

Pull #2610

github

web-flow
Merge a15009557 into 6c112e71f
Pull Request #2610: Integrate parts of libmav into MAVSDK and add MavlinkDirect plugin

727 of 980 new or added lines in 15 files covered. (74.18%)

27 existing lines in 5 files now uncovered.

16217 of 35086 relevant lines covered (46.22%)

148063.0 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
    std::string path,
53
    int baudrate,
54
    bool flow_control,
55
    ForwardingOption forwarding_option) :
×
56
    Connection(std::move(receiver_callback), forwarding_option),
×
57
    _serial_node(std::move(path)),
×
58
    _baudrate(baudrate),
×
59
    _flow_control(flow_control)
×
60
{}
×
61

NEW
62
SerialConnection::SerialConnection(
×
63
    Connection::ReceiverCallback receiver_callback,
64
    Connection::LibmavReceiverCallback libmav_receiver_callback,
65
    std::string path,
66
    int baudrate,
67
    bool flow_control,
NEW
68
    ForwardingOption forwarding_option) :
×
69
    Connection(
NEW
70
        std::move(receiver_callback), std::move(libmav_receiver_callback), forwarding_option),
×
NEW
71
    _serial_node(std::move(path)),
×
NEW
72
    _baudrate(baudrate),
×
NEW
73
    _flow_control(flow_control)
×
NEW
74
{}
×
75

UNCOV
76
SerialConnection::~SerialConnection()
×
77
{
78
    // If no one explicitly called stop before, we should at least do it.
79
    stop();
×
80
}
×
81

82
ConnectionResult SerialConnection::start()
×
83
{
84
    if (!start_mavlink_receiver()) {
×
85
        return ConnectionResult::ConnectionsExhausted;
×
86
    }
87

88
    ConnectionResult ret = setup_port();
×
89
    if (ret != ConnectionResult::Success) {
×
90
        return ret;
×
91
    }
92

93
    start_recv_thread();
×
94

95
    return ConnectionResult::Success;
×
96
}
97

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

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

126
    if (_handle == INVALID_HANDLE_VALUE) {
127
        LogErr() << "CreateFile failed with: " << GET_ERROR();
128
        return ConnectionResult::ConnectionError;
129
    }
130
#endif
131

132
#if defined(LINUX) || defined(APPLE)
133
    struct termios tc;
×
134
    bzero(&tc, sizeof(tc));
×
135

136
    if (tcgetattr(_fd, &tc) != 0) {
×
137
        LogErr() << "tcgetattr failed: " << GET_ERROR();
×
138
        close(_fd);
×
139
        return ConnectionResult::ConnectionError;
×
140
    }
141
#endif
142

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

150
    tc.c_cc[VMIN] = 0; // We are ok with 0 bytes.
×
151
    tc.c_cc[VTIME] = 10; // Timeout after 1 second.
×
152

153
    if (_flow_control) {
×
154
        tc.c_cflag |= CRTSCTS;
×
155
    }
156
#endif
157

158
#if defined(LINUX) || defined(APPLE)
159
    tc.c_cflag |= CLOCAL; // Without this a write() blocks indefinitely.
×
160

161
#if defined(LINUX)
162
    const int baudrate_or_define = define_from_baudrate(_baudrate);
×
163
#elif defined(APPLE)
164
    const int baudrate_or_define = _baudrate;
165
#endif
166

167
    if (baudrate_or_define == -1) {
×
168
        return ConnectionResult::BaudrateUnknown;
×
169
    }
170

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

177
    if (cfsetospeed(&tc, baudrate_or_define) != 0) {
×
178
        LogErr() << "cfsetospeed failed: " << GET_ERROR();
×
179
        close(_fd);
×
180
        return ConnectionResult::ConnectionError;
×
181
    }
182

183
    if (tcsetattr(_fd, TCSANOW, &tc) != 0) {
×
184
        LogErr() << "tcsetattr failed: " << GET_ERROR();
×
185
        close(_fd);
×
186
        return ConnectionResult::ConnectionError;
×
187
    }
188
#endif
189

190
#if defined(WINDOWS)
191
    DCB dcb;
192
    SecureZeroMemory(&dcb, sizeof(DCB));
193
    dcb.DCBlength = sizeof(DCB);
194

195
    if (!GetCommState(_handle, &dcb)) {
196
        LogErr() << "GetCommState failed with error: " << GET_ERROR();
197
        return ConnectionResult::ConnectionError;
198
    }
199

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

218
    if (!SetCommState(_handle, &dcb)) {
219
        LogErr() << "SetCommState failed with error: " << GET_ERROR();
220
        return ConnectionResult::ConnectionError;
221
    }
222

223
    COMMTIMEOUTS timeout = {0, 0, 0, 0, 0};
224
    timeout.ReadIntervalTimeout = 1;
225
    timeout.ReadTotalTimeoutConstant = 1;
226
    timeout.ReadTotalTimeoutMultiplier = 1;
227
    timeout.WriteTotalTimeoutConstant = 1;
228
    timeout.WriteTotalTimeoutMultiplier = 1;
229
    SetCommTimeouts(_handle, &timeout);
230

231
    if (!SetCommTimeouts(_handle, &timeout)) {
232
        LogErr() << "SetCommTimeouts failed with error: " << GET_ERROR();
233
        return ConnectionResult::ConnectionError;
234
    }
235

236
#endif
237

238
    return ConnectionResult::Success;
×
239
}
240

241
void SerialConnection::start_recv_thread()
×
242
{
243
    _recv_thread = std::make_unique<std::thread>(&SerialConnection::receive, this);
×
244
}
×
245

246
ConnectionResult SerialConnection::stop()
×
247
{
248
    _should_exit = true;
×
249

250
    if (_recv_thread) {
×
251
        _recv_thread->join();
×
252
        _recv_thread.reset();
×
253
    }
254

255
#if defined(LINUX) || defined(APPLE)
256
    close(_fd);
×
257
#elif defined(WINDOWS)
258
    CloseHandle(_handle);
259
#endif
260

261
    // We need to stop this after stopping the receive thread, otherwise
262
    // it can happen that we interfere with the parsing of a message.
263
    stop_mavlink_receiver();
×
264

265
    return ConnectionResult::Success;
×
266
}
267

268
std::pair<bool, std::string> SerialConnection::send_message(const mavlink_message_t& message)
×
269
{
270
    std::pair<bool, std::string> result;
×
271

272
    if (_serial_node.empty()) {
×
273
        LogErr() << "Dev Path unknown";
×
274
        result.first = false;
×
275
        result.second = "Dev Path unknown";
×
276
        return result;
×
277
    }
278

279
    if (_baudrate == 0) {
×
280
        result.first = false;
×
281
        result.second = "Baudrate unknown";
×
282
        return result;
×
283
    }
284

285
    uint8_t buffer[MAVLINK_MAX_PACKET_LEN];
×
286
    uint16_t buffer_len = mavlink_msg_to_send_buffer(buffer, &message);
×
287

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

302
    if (send_len != buffer_len) {
×
303
        result.first = false;
×
304
        result.second = "Baudrate unknown";
×
305

306
        std::stringstream ss;
×
307
        ss << "write failure: " << GET_ERROR();
×
308
        LogErr() << ss.str();
×
309
        result.first = false;
×
310
        result.second = ss.str();
×
311
        return result;
×
312
    }
×
313

314
    result.first = true;
×
315
    return result;
×
316
}
317

318
void SerialConnection::receive()
×
319
{
320
    // Enough for MTU 1500 bytes.
321
    char buffer[2048];
×
322

323
#if defined(LINUX) || defined(APPLE)
324
    struct pollfd fds[1];
×
325
    fds[0].fd = _fd;
×
326
    fds[0].events = POLLIN;
×
327
#endif
328

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

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

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