Modern C++ library to communicate with NMEA2000 networks.
This library can be used with polling as follows:
int main() {
auto conn = nmea::connect("can0");
if (!conn) {
std::println("Error on connection: {}", conn.error());
return EXIT_FAILURE;
}
nmea::Listener listener(*conn);
pollfd pollfd{.fd = listener.sockfd(), .events = POLLIN, .revents = {}};
while (true) {
auto events_num = poll(&pollfd, 1, POLL_TIMEOUT);
if (events_num > 0) {
auto msg = listener.read();
if (msg) {
process_message(*msg);
} else {
std::println("Error: {}", msg.error());
}
} else if (events_num < 0) {
std::println("Poll error");
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}It provides a visitor abstraction over std::visit to process the message:
void process_message(nmea::NmeaMessage &msg) {
nmea::visit(
msg,
[](nmea::message::CogSog &m) {
// Can use the message payload as necessary, eg:
// auto speed = m.sog;
// The message inherits from the string formatter and can be used
// as below to print the following:
// COGSOG(SID=1, Reference=0, COG=1 radians, SOG=0.4 m/s)
std::println("{}", m);
},
[](nmea::message::Temperature &m) {
// auto temp = m.actual_temperature;
// auto source = m.source;
// Prints the following:
// Temperature(SID=1, Instance=1, Source=2 Actual Temperature=3.14 K, Set Temperature=0.00 K)
std::println("{}", m);
},
[](auto &) { std::println("Unhandled message type"); });
}The library can also function as a device on the bus and is able to send the supported messages
int main(int argc, const char **argv) {
if (argc != 2) {
std::println("Usage: {} <interface>", argv[0]);
return EXIT_FAILURE;
}
auto conn = nmea::connect(argv[1]);
if (!conn) {
std::println("Error on connection: {}", conn.error());
return EXIT_FAILURE;
}
nmea::DeviceName name{
.unique_number = 120,
.manufacturer_code = ManufacturerCode::ACTISENSE,
.device_instance_lower = 0,
.device_instance_upper = 0,
.device_function = device_function::ATMOSPHERIC,
.system_instance = 0,
.industry_group = IndustryCode::MARINE,
.arbitrary_address_capable = true,
};
nmea::Device device(*conn);
auto result = device.claim(name).get();
if (!result) {
std::println("Failed to claim address: {}", result.error());
return EXIT_FAILURE;
}
nmea::message::CogSog cogsog{
.sid = 1,
.cog_reference = 0,
.cog = 0x1234 * 1e-4,
.sog = 0x5678 * 1e-2,
};
auto send_res = device.send(cogsog);
if (!send_res) {
std::println("Error sending COGSOG message: {}", send_res.error());
return EXIT_FAILURE;
}
nmea::message::Temperature temp{
.sid = 2,
.instance = 1,
.source = 3,
.actual_temperature = 0x1234 * 0.01,
.set_temperature = 0x5678 * 0.01,
};
send_res = device.send(temp);
if (!send_res) {
std::println("Error sending Temperature message: {}", send_res.error());
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}It is possible to test the listener and device functionality by making them communicate with each other over a virtual can interface. First, set it up with
./run.sh setupThen start up the listener
./run.sh listenerAnd finally send the messages and confirm the listener prints the formatted output
./run.sh deviceSee the examples folder or projects that utilize this library:
This is a hobby project to learn the NMEA standard, for use in the real world refer to the following libraries: