ttlappalainen / NMEA2000

NMEA2000 library for Arduino
538 stars 226 forks source link

NMEA2000 on a linux box #58

Open haavardholm opened 7 years ago

haavardholm commented 7 years ago

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

ttlappalainen commented 7 years ago

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.

haavardholm commented 7 years ago

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 ?

ttlappalainen commented 7 years ago

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.

FXVT commented 7 years ago

Hello,

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

Regards. FX

ttlappalainen commented 7 years ago

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

FXVT commented 7 years ago

Cool ! Thanks.

ttlappalainen commented 7 years ago

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.

Project487 commented 4 years ago

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

ttlappalainen commented 4 years ago

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.

Project487 commented 4 years ago

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

titio72 commented 4 years ago

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.

ttlappalainen commented 4 years ago

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.

haavardholm commented 4 years ago

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.

ttlappalainen commented 4 years ago

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.

Project487 commented 4 years ago

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 commented 4 years ago

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

haavardholm commented 4 years ago

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

ttlappalainen commented 4 years ago

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.

orca-hydromancer commented 3 years ago

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 commented 3 years ago

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.

Project487 commented 3 years ago

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

ttlappalainen commented 3 years ago

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.

Project487 commented 3 years ago

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.

ttlappalainen commented 3 years ago

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).

haavardholm commented 3 years ago

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.

ttlappalainen commented 3 years ago

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.

Project487 commented 3 years ago

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

ttlappalainen commented 3 years ago

The solution depends of your skills and goal.

Project487 commented 3 years ago

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