NMEA2000 icon indicating copy to clipboard operation
NMEA2000 copied to clipboard

NMEA2000 on a linux box

Open haavardholm opened this issue 7 years ago • 29 comments

Hi,

I want to read, as a start position, course, speed on my boat running NMEA2000. I have a on the boat pc running Linux. Would this library be able to help reading from the NMEA2000 net ?

Do there exist a list of all sentences interpreted ? ( I might later be interested in info from the engine )

Best regards Håvard

haavardholm avatar Jul 14 '17 06:07 haavardholm

No/Yes.

Since you have Linux on PC and not on e.g. RPi, you need some device to bring data to PC. The simplest way is to buy Actisense NGT-1, which cost about 200 €. Other option is to use Maretron devices, but Actisense format is more widely supported on Open Source. For this you do not need library at all.

If you are handy you can buy e.g. Teensy + CAN (http://skpang.co.uk/catalog/teensy-canbus-breakout-board-include-teensy-32-p-1507.html). I prefere Teensy instead of original Arduino boards for NMEA 2000 devices. Then you load my library example ActisenseListener and program it to board. That system replaces Actisense NGT-1 and you get data also as Actisense format to the PC serial.

When you get data to PC Serial, you have new options. Find SignalK project on web. They should have ready code for PC linux too (there is ready example for RPi) to provide data by web-server.

If you have Windows box running, you can use Open Skipper, which I also update. With that you can define own displays for viewing data. Also you can use Open Skipper as web-server so you get all data to any browser.

Note also that it should be possible to split serial data on linux to two virtual serial ports, so that other can be read by e.g. SignalK and other by navigation sw, which supports NMEA 2000 Actisense format.

ttlappalainen avatar Jul 14 '17 08:07 ttlappalainen

Thanks for fast answer.

This may be a stupid question but I am new to the NMEA2000 protocol. Is it so that all vendors have their own sentences or are there some sort of standardization ? Eg. for position and sped (like nmea0183/RMC)

Which sentences/keys are possible to read by using open source code ?

haavardholm avatar Jul 14 '17 09:07 haavardholm

It is not stupid. There are both. Protocol has lots of standard messages, but also leaves possibility to use proprietary messages. For position there are GNSS Position Data (PGN 129029) and Lat/lon rapid (PGN 129025). For speed there are Boat speed (PGN 128259), which is normally sent by wheel logs and COG SOG rapid (PGN 129026), which is normally sent by GPS device. NMEA2000 is actually closed standard, so all open source information is more or less hacked out. Protocol is inherited from CAN J1939, which is open, so tha gives some information.

Almost all standard messages can be parsed by open source. Also some proprietary messages.

You have not defined, what you would like to do with position, course, speed? Also what are your programming skills? I still would think that SignalK is your way to go, but depends your final goal.

ttlappalainen avatar Jul 14 '17 19:07 ttlappalainen

Hello,

Here are some examples of the standard PGN: Format des PGN.txt

Regards. FX

FXVT avatar Jul 16 '17 05:07 FXVT

Better sources are is https://raw.githubusercontent.com/canboat/canboat/master/analyzer/pgn.h or PGNDefns.N2kDfn.xml file found on https://sourceforge.net/projects/openskipper/files/OpenSkipper/1.6/OpenSkipper-1.6-src_20161217.zip/download

ttlappalainen avatar Jul 16 '17 09:07 ttlappalainen

Cool ! Thanks.

FXVT avatar Jul 16 '17 12:07 FXVT

I still hope you could find either SignalK or my library usefull instead of "making just an other". In my library there is e.g. ActisenseReader.h/.cpp module, which can parse data from Actisense format to tN2kMsg. Then there are N2kMessages.h/.cpp module for parsing data. I think ActisenseReader is not platform independent, but it could be modified to be.

ttlappalainen avatar Jul 17 '17 05:07 ttlappalainen

Hi, Forgive me resurrecting an old thread, but I am also interested in running your NMEA2000 library on a Linux box (Ubuntu 20.04). The reason is to emulate a physical network for testing as I don't yet have access to the boat or the hardware; I'm planning to use an Arduino Due to read a Raymarine NMEA2000 network.

I have read the Raspberry Pi preparation notes and seems it could work more generically? ie including the NMEA2000 library and the NMEA2000_socketCAN library to build a binary.

The flow is:

  1. Python code to send NMEA2000 data to vcan0 (virtual can port).
  2. NMEA2000 library reads vcan0 and ParseMessage to a (virtual) serial port.
  3. Read serial port with ActisenseSerial.
  4. Parse and extract data with Analyzer.

Any constructive comments regarding the above would be appreciated; I may have completely misunderstood the process.

Also, thanks for providing the library.

Roger

Project487 avatar Sep 28 '20 09:09 Project487

NMEA 2000 library is a link to physical CAN. If you want to use it against serial port, you have to write for that special driver class, which has been inherited from tNMEA2000 class. It should be like other drivers - NMEA2000_mcp, NMEA2000_esp32, NMEA2000_teensy etc.

By the way have you checked my "NMEA Simulator", which you can use for sending messages. It it only for Windows, but you could run it on virtual host. It send data in Actisense format, which also Analyzer or Actisene Reader reads.

ttlappalainen avatar Sep 29 '20 03:09 ttlappalainen

Hi,

Thanks for the reply.

I'm obviously confused about how the NMEA2000 library operates. I assumed it obtains data from the canbus on the port pointed to by NMEA2000.SetCANPort(portname) (portname="vcan0" in my case) and directs output to serial with NMEA2000.SetForwardStream(&serStream). So anything in-between (ie the NMEA2000 library) could reside in an Arduino or as an executable in a Linux file.

Something like (from your site docs)

/* 
 * File:   main.cpp
 * Author: al
 *
 * Testing for CAN and RPI 
 * 
 * See: https://github.com/thomasonw/NMEA2000_socketCAN
 *
 * Created on February 12, 2017, 2:37 PM
 */

#include <cstdlib>
#include <stdio.h>
#include <iostream>
#include "NMEA2000_CAN.h"

using namespace std;

int main(void)
{
    cout << "Starting CAN watching" << endl;

    setvbuf (stdout, NULL, _IONBF, 0);                                          // No buffering on stdout, just send chars as they come.
 
    NMEA2000.SetCANPort("vcan0");

    NMEA2000.SetForwardStream(&serStream);                                      // Connect bridge function for streaming output.
    NMEA2000.SetForwardType(tNMEA2000::fwdt_Text);                              // Show in clear text (for now)
       
    if (!NMEA2000.Open()) {									// Defaults to port can0, see ref for how to use other ports
       cout << "Failed to open CAN0 port" << endl;
       return 1;
   }
    
    cout  << endl << "CAN started, going to watch it now" << endl;

     while(1) {
         NMEA2000.ParseMessages();                                               // Will send out CAN messages in open text 
    }
    
    return 0;
}

If NMEA2000 only works with a physical CAN then I see this will fail.

Any clarification greatly appreciated.

Regards

Roger

Project487 avatar Sep 29 '20 04:09 Project487

If you have already your python program that sends n2k messages to vcan - you can just read them back by piping candump, candump2analyzer and finally in the analyzer - no need to write code.

titio72 avatar Sep 29 '20 04:09 titio72

vcan is physical CAN port handled by CAN controller like MCP2515 on RPi systems.

There is no standard protocol for NMEA2000 on serial. One is Actisense, what e.g. Actisense NGT-1 device writes to serial port. Maretron has its own protocol on their devices. SeaSmart is one protocol, which is like extension over NMEA0183.

ttlappalainen avatar Sep 29 '20 07:09 ttlappalainen

Hello,

Will this work with your library ?

http://www.roalan.com/products/can-bus-adapters http://www.roalan.com/wp-content/uploads/2018/05/USB-CAN.pdf

Should do the same job as the actisence ntg-1 ?

Best regards Håvard

On 9/29/20 9:57 AM, Timo Lappalainen wrote:

vcan is physical CAN port handled by CAN controller like MCP2515 on RPi systems.

There is no standard protocol for NMEA2000 on serial. One is Actisense, what e.g. Actisense NGT-1 device writes to serial port. Maretron has its own protocol on their devices. SeaSmart is one protocol, which is like extension over NMEA0183.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/ttlappalainen/NMEA2000/issues/58#issuecomment-700520769, or unsubscribe https://github.com/notifications/unsubscribe-auth/AHGF6PSVLQN7P6KDLOANBT3SIGHMZANCNFSM4DS7S3TQ.

haavardholm avatar Sep 29 '20 08:09 haavardholm

Can not say for sure. Document says that it supports SocketCAN for Linux, so maybe it works.

It does not do the same as Actisense NGT-1. Actisense NGT-1 is bus device, which translates NMEA2000 messages to Actisense format, which can be transfered as ascii. My library communicates directly with CAN controllers. I am not specialist with Linus, but I have understood that SocketCAN gives right interface on Linux.

Byt the way. On Linux one should add small sleep to loop where NMEA2000.ParseMessages() will be called. In some example I used 100 us sleep to release time for other processes. In other case loop will eat that core time.

ttlappalainen avatar Sep 29 '20 09:09 ttlappalainen

Thanks for the input. I will have to wait for the Arduinos to arrive and test with hardware; maybe with a 'bench' can bus. The wait time might allow all this to sink in. (Bad nautical pun, sorry).

Regards

Roger

Project487 avatar Sep 29 '20 16:09 Project487

If you have already your python program that sends n2k messages to vcan - you can just read them back by piping candump, candump2analyzer and finally in the analyzer - no need to write code.

Hi,

I tried this and want to make a contribution, so here is my code (Python):

import socket
import struct

def byteC(integer):
    return divmod(integer, 0x100)

def IdC(priority, PGN, src):
    return  priority*2**26 + PGN*2**8 + src

fmt = "<IB3x8s"
sock = socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW) 
sock.bind(("vcan0",))

canId_wind = IdC(2, 130306, 36) | socket.CAN_EFF_FLAG           #Wind data
sid = 168
w_speed = 0.85                                                  #m/s
w_angle = 1.5                                                   #radians
w_ref = 3                                                       #0, 1, 2, 3, 4                                                 

byte5 = sid
byte3, byte4 = byteC(int(w_speed*100)) 
byte1, byte2 = byteC(int(w_angle*10000))
byte0 = int.from_bytes(b'\xf8','big') | w_ref

n2kdata = (bytes.fromhex( hex(byte5)[2:].zfill(2) + hex(byte4)[2:].zfill(2) + hex(byte3)[2:].zfill(2) + hex(byte2)[2:].zfill(2) + hex(byte1)[2:].zfill(2) + hex(byte0)[2:].zfill(2)) )

data = struct.pack(fmt, canId_wind, 8, n2kdata)
sock.send(data)

canId_head = IdC(2, 127250, 36) | socket.CAN_EFF_FLAG           #Vessel heading
sid = 168
v_heading = 2                                                   #radians
v_deviation = 0.01                                              #radians
v_variation = 0.02                                              #radians
v_ref = 1                                                       #0, 1, 2, 3

byte7 = sid
byte5, byte6 = byteC(int(v_heading*10000)) 
byte3, byte4 = byteC(int(v_deviation*10000))
byte1, byte2 = byteC(int(v_variation*10000))
byte0 = int.from_bytes(b'\xfc','big') | v_ref

n2kdata = (bytes.fromhex( hex(byte7)[2:].zfill(2) + hex(byte6)[2:].zfill(2) + hex(byte5)[2:].zfill(2) + hex(byte4)[2:].zfill(2) + hex(byte3)[2:].zfill(2) + hex(byte2)[2:].zfill(2) + hex(byte1)[2:].zfill(2) + hex(byte0)[2:].zfill(2)) )

data = struct.pack(fmt, canId_head, 8, n2kdata)
sock.send(data)

It works by executing the code from one terminal and candump vcan0 | candump2analyzer | analyzer -json in another terminal.

The string building is a bit inelegant, and I'm interested in wind direction and vessel heading only, to help with sail choice. More work would be needed for multi-frame data I think and to process the data to a more usable form.

Any improvements appreciated.

Regards

Roger

Project487 avatar Oct 01 '20 13:10 Project487

Hi,

I am currently trying to compile/install the NMEA2000 library on my linux-box (linux mint, but should be similar to raspberrry pi). I want to include the library in a c/c++ program.

As I understand, I have to install the following     https://github.com/ttlappalainen/NMEA2000_socketCAN     https://github.com/ttlappalainen/NMEA2000

I have downloaded the library, but there is no Makefile.

I also have problems trying to call g++.

Any tips on how to install ?

Best regards Håvard

haavardholm avatar Oct 06 '20 20:10 haavardholm

Yes, you need both libraries. I have not made makefiles, since I have used all projects with GUI. On RPi I used CodeBlock like on example NMEA2000ToNMEA0183.

ttlappalainen avatar Oct 07 '20 02:10 ttlappalainen

Byt the way. On Linux one should add small sleep to loop where NMEA2000.ParseMessages() will be called. In some example I used 100 us sleep to release time for other processes. In other case loop will eat that core time.

Better add small timeout (e.g. 1s) to socket select or have a look at https://github.com/linux-can/can-utils/blob/master/candump.c socket handling with epoll.

orca-hydromancer avatar Dec 19 '20 21:12 orca-hydromancer

I've tested epoll and for my target ARM system it performs ~ 20% better than select.

Additional performance boost suggestion for Linux or other systems that have spare memory is to use hash maps for handlers, instead of loops, e.g.: std::unordered_map<Pgn_t, decltype(&Class::SomePGNHandlerMethod)> Handlers and then use unordered_map::contains or unordered_map::find to check if there is handler for given PGN and this->*Handlers[N2kMsg.PGN])(N2kMsg) to invoke that handler.

orca-hydromancer avatar Dec 28 '20 04:12 orca-hydromancer

Hi,

I'm still confused about my NMEA 2000 set up.

I'm on Linux Mint 20.

I can send NMEA2000 frames generated by a Python script to vcan0 and read the candump vcan0 output in another terminal. This works fine in the virtual world and I can process the candump to my satisfaction.

Now I want to try the real world with an actual can device.

I have an Arduino Due connected to a MCP 2551 (and I have tried an SV65HVD234) to my 120 ohm terminated NMEA bus on a breadboard. This Due is plugged into a USB port on the Linux machine. The Due is running ActisenseListener.ino.

Secondly, another DUE with WindMonitor.ino running, is also on the breadboard NMEA bus.

I expected to be able to raise can0, ie the Linux machine sees the DUE as a can device. I used $ sudo link set dev can0 type can $ sudo link set up can0

but Cannot find device "can0"

I then hope to candump can0 and process the data.

I have also tried setting up the USB port as a serial can slcan0. I have 2 x MCP2551 boards and 2 x SV65HVD234 boards and have used all permutations of Dues and can boards, without success.

Should I be expecting the Due will present as a can device? Is my hardware defective or have I completely mis-understood the set up?

I would be grateful of any advice.

Regards

RH

Project487 avatar Mar 17 '21 08:03 Project487

Do you have physical can tranceiver e.g. PICAN2 or home made with MCP2515 device on RPi? As far as I understoor you have one DUE as standalonw wind monitor and other DUE with ActisenseListener on USB port. Then you simply has N2k data converted to Actisense format on normal serial port - not on can. If you want to read that data on RPi, you need to open serial port and read data from it with tActisenseReader to tN2kMsg and then you can parse message with functions on N2kMessages module.

ttlappalainen avatar Mar 18 '21 05:03 ttlappalainen

Thanks for the reply; it seems I am mistaken in my understanding of the process.

Can you elaborate on your last sentence please?

If you want to read that data on RPi, you need to open serial port and read data from it with tActisenseReader to tN2kMsg and then you can parse message with functions on N2kMessages module

My Linux machine is x86-64 rather than RPi; I want to process data using can-utils on my Linux box. I have seen numerous automotive examples of a USB-to-Can device between the can-bus and the serial (USB) port of the Linux machine. The USB-to-Can device appears at can0 and hence data can easily be processed. I had erroneously thought, as NMEA 2000 and the automotive can protocol are similar, the operation would be the same and the Arduino Due would appear as a can device.

Thanks again.

Project487 avatar Mar 18 '21 12:03 Project487

There are different USB-CAN modules. Some modules simply shows up as serial port and convert CAN data to ascii like 18EAFFFE 00 EE 00 FF FF FF FF FF Where first is ID and then 8 byte data. I do not know what kind of converter you have. Anyway that does not help you much. If you just read and send CAN messages, you have to handle all, what my library does. It takes care of address claiming, fast packet frame collection, fast packet frame sending, requests for system messages etc. can-utils does not do those things.

If you want to use library directly on Linux, you should do same as with other platforms. You inherit tNMEA2000 class as e.g. tNMEA2000_socketCAN and write required methods to handle low level frame receiving and sending. There is one important thing to take care. On sending fast packet messages, frames must be sent in order. CAN controllers often has several "mailboxes" and CAN drivers simply uses next free one for sending. This causes that frames can be sent in mixed order and other devices will not accept them. That is also reason why there is fixed low level libraries e.g. for DUE, MCP and Teensy (FlexCAN).

ttlappalainen avatar Mar 18 '21 14:03 ttlappalainen

Hi,

I plan to use this device to connect the can-bus to my linux-computer : https://www.vscom.de/usb-can-plus-iso.html

I hope this device can handle both can-bus and nmea2000.

I have not started this project as time have not allowed.

Timo, I think this device is representative also for "Project487", what would be the path to be able to read/write from linux ? What will your library do and what will it not do ?

Best regards Håvard

On 18.03.2021 15:55, Timo Lappalainen wrote:

There are different USB-CAN modules. Some modules simply shows up as serial port and convert CAN data to ascii like 18EAFFFE 00 EE 00 FF FF FF FF FF Where first is ID and then 8 byte data. I do not know what kind of converter you have. Anyway that does not help you much. If you just read and send CAN messages, you have to handle all, what my library does. It takes care of address claiming, fast packet frame collection, fast packet frame sending, requests for system messages etc. can-utils does not do those things.

If you want to use library directly on Linux, you should do same as with other platforms. You inherit tNMEA2000 class as e.g. tNMEA2000_socketCAN and write required methods to handle low level frame receiving and sending. There is one important thing to take care. On sending fast packet messages, frames must be sent in order. CAN controllers often has several "mailboxes" and CAN drivers simply uses next free one for sending. This causes that frames can be sent in mixed order and other devices will not accept them. That is also reason why there is fixed low level libraries e.g. for DUE, MCP and Teensy (FlexCAN).

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/ttlappalainen/NMEA2000/issues/58#issuecomment-801994588, or unsubscribe https://github.com/notifications/unsubscribe-auth/AHGF6PSWCV3GC5REGGTOG73TEIH4ZANCNFSM4DS7S3TQ.

haavardholm avatar Mar 18 '21 15:03 haavardholm

According doc it supports SocketCan. So it may directly work by using NMEA2000_socketCAN library with NMEA2000 library. Then other examples should work. I did example NMEA2000ToNMEA0183 with NMEA2000_socketCAN for RPi with Code:Block s IDE. If you get that working, then you Linux will act as NMEA2000 bus device showing up as given information on setup. The library does a lot required NMEA2000 things background and you can concentrate for data you are interested.

If you only want to read data, then maybe Signal-K server would be easier.

ttlappalainen avatar Mar 18 '21 16:03 ttlappalainen

Thank you for you replies and patience.

I am not sure how to incorporate your library into my project but I now understand I have been using it incorrectly. My current thinking is that processed data on the Arduino has to be sent to it's serial port for the Linux machine to deal with?? Modifying your code is beyond me at the moment. (I consider myself as a reasonable Linux user with Python and Arduino C experience) As I primarily want to read the bus at present I might purchase a cheap USB/CAN 'analyzer' and see what happens. Signal-K seems very interesting for the future, but requires an expensive 'Gateway'; may be this 'analyzer' or the device mentioned by @haavardholm above would be suitable. I would still like to understand the NMEA2000 library and how it would be useful to me, but alas despite reading much of the documentation, I don't have a handle on it yet.

Once again, thanks of your support. Roger

Project487 avatar Mar 18 '21 18:03 Project487

The solution depends of your skills and goal.

  • If you want that your Linux box acts as NMEA2000 device, you should use Socket CAN and library. This requires you to make application linked with library and maybe tune NMEA2000_socketCAN library. It has been tested on RPi so it should be easy. Linux users always tells how easy it is to do everything on Linux - you only need 50 sudo commands, which you can easily google from internet and takes only few days to separate old information from valid. With this solution you have better control for bus communication, but may require more own work.
  • If you just want to read data, you can use Signal-K. There is example ArduinoGateway, which is almost like Actisense NGT-1, which data also Sgnal-K reads. You can load that example to DUE. The difference is that it does not handle any NGT-1 specific commands, which are for control data read and send ArduinoGateway simply sends and reads everything.

ttlappalainen avatar Mar 19 '21 04:03 ttlappalainen

Thanks for the info; I'll definitely look at the Arduino Gateway.

Your comments about Linux, sudo commands and two days on the internet are most apt!

Roger

Project487 avatar Mar 19 '21 11:03 Project487