NMEA 2000 Shield

timolappalainen:
E.g. Garmin GMI 20 has definable pages for 4 values on each page. B&G vulcan has also configurable data values. So yes, MFDs should have them othervice they are not MFD - just SFD (Some Function Display).

That I understand, but humidity and engine room temp, are those actual values existing in the n2k protocol?

nmea protocol has a few PGNs relevant for “temperatures”
PGN 130310 environmental
PGN 130311 environmental
(cant remember which of above has “air temp” , “humidity” its assumed this is outside humidity,
these are the PGNs typically used by a weather station/wind instrument)
and
PGN130312 extended temperature
PGN 130316 extended temperature
across above, they support a number of different temperature types (“cabin”, “engine room”, “refrigerator”
“freezer”, various “tanks”, “air duct”, “under bolts” ... and so on)

as timo says, it then becomes dependant on the display used (MFD, dedicated data display etc)
as to what PGNs it will support, and how it displays them (& any level of customisation ... eg renaming the way its displayed)

In my case Im interested in “engine” room data ..I dont have a weather station/wind sensor
so i will be using “humidity” to actually display the engine room humidity, and likewise “air temp”
for engine room temp - as my MFD (raymarine) doesnt support PGN instance for “engine room temp”
(later on .. I might build a small dedicated display and use timos arduino libraries to display a bunch PGNs that are not supported by my MFD)
from what i can see - nmea protocol only supports one humidity instance. but it certainly
supports multiple temp instances.

google the following
nmea technical bulletin PGN130316 feb 2015

.. it lists all the various source codes that corelate to the different temp types

If you are going to use my library, you will find possible values also in N2kMessages.h in tN2kTempSource enum definition.

Note also that 130316 is rather new PGN and old MFDs does not support it. You may need to use obsolete 130312 for temperature.

thx and noted,
a further check on my (very new) raymarine axiom - with the very latest lighthouse sw,
shows that it doesnt support either!! 130312 or 130316 (well at least according to the manual - which
ive already found one other PGN omission ... )
so at present i will likely have to fudge “engine room temp” to “air temp” PGN instance/source.
.. and hope that raymarine will add the additional PGNs over time .. ...
... its really making me think twice about a dedicated display (your library with a teensy and an OLED display) of the various PGNs that
are not supported! (eg exhaust gas temps , engine room temps, ...) .. anothet “to do list project”

It is very strange MFD, if it does not support 130312. And if it is new, it should read 130316, since 130312 and 130311 are obsolete. But maybe this is Raymarine logic.

How about 130311?

What do you mean with "air temp" PGN instance/source? PGN 130310, which is also obsolete.

Have you checked KBox: KBox | Hackaday.io

the (very latest - last month) Raymarine Lighthouse 3 sw lists support for only
PGN 130310 & 13011 ... 312, 316 not listed .. and yes agree it should support the new ones .. but doesnt appear to ... Im trying to get some clarity from them .
seems they are struggling with the feature set in the new LH3 (android? based OS)

To fudge a engine room temp - i was going to encode this as “air temp” (because i dont have a
environmental sensor on. my network)
... and hope that raymarine add support for 130316 in the future....

I looked at k-box - however my boat has multiple 24V battery banks ... and its analog inputs
are shown as “up to 23V ... way to low for a nominal 24V bank)
I also already have 2 NMEA0183 convertors on my 2k network,
likewise i have a autopilot with its own heading rate sensor.
..so im struggling with the value that k-box adds for me ?
I do like the inertial unit for pitch/roll, and barometer.
I dont see temp/humidity listed? which im also interested in.
so i will have another look at it ... perhaps its expandable ..

hi timo.
not sure where to put this question, its partly related to the arduino & nmea2k library,
but if I should put it elsewhere or start a new thread pls advise.

I want to capture a "log" from nmea2k data to be able to post process and then display
a selection of data to be able to plot fuel economy vs a range or parameters that
typically impact consumption. (eg eng RPM, eng load %, eng fuel rate, pitch and roll, true wind speed and direction,
fuel tank level, water tank level..)

using teensy listener/sender - with actisense reader and EBL reader I can display n2k data,
and capture logs in "ËBL format"
but i need to get data into excel or similar to be able to do some post process computations and
then graph data. EBL reader doesnt have any options to save out to excel or other formats?

Ive had a look at "openskipper" - which i note can ingest the data from actisense listener
in real time..
however im struggling with understanding how/if it could achieve what im trying to do?

is there a simpler way to get decoded nmea2k data from activelistener into "logged" excel format?
(i prefer excel because i need to write some formulas for computing average fuel rate, economy,
and then do some graphing) ... and I understand excel (but not xml . cs etc)

I have same problem. I have logged everything for 2-3 years with OpenSkipper. In main window with right click you can start Track. Unfortunately this log format is OpenSkipper specific - timestamp+N2k and I have not yet wrote a parser for that. I am going do, but when? I use OpenSkipper for logging, since I have it anyway open for providing data as web server.

One option would be to make own logger. Take a look in to the example DataDisplay2. You could clone that and format output to .csv and then just log terminal data. If you need quick temporary solution, this could be the fastest one.

Currently thread is OK, since it partially belongs to shield.

Hello!

I need to convert the RPM signal from gasoline to diesel.
The story:
I had last year a V8 gasoline engine in my boat but this year I have dropped that engine out and have put a 6 cylinder diesel engine.

Also, I bought a completely new set of instruments last year but for gasoline engines. (RPM)
I can choose on the RPM meter from 4 to 12 cylinder. With the V8 I had 8 pulses per RPM.
With the diesel engine, the puls is coming from the flywheel and give 1 puls per RPM like I know.
So now my plan is to convert the 1 puls up to 4 pulses per RPM so I get shown the correct RPM on the gasoline RPM meter.

Since Teensy or Arduino just can handle 3 V / 5 V max it have to go through a transistor as well I guess.

The same Teensy shell deliver the RPM sentence to the MFD as well I thought (EngineSpeed Rapid).

Can somebody help here with a code example for the pulse conversion?

Thank you!

I am not sure did I understood your problem. If you just want to measure some input frequency (RPM) check https://www.pjrc.com/teensy/td_libs_FreqCount.html

Hi Timo,

I have a question regarding SetN2kEngineDynamicParam. I am building a device to read the FuelRate based on impulses per ml by fuel flow.

If I want to send the result now do I have to send now the hole SetN2kEngineDynamicParam block or can I just update the FuelRate?

How does the code line looks like to just send the FuelRate then?

The other values are coming from a different device (or shell come from a different device in the future).

Another question:

I want to use some code to calculate the flow rate which uses an external interrupt. Will this affect the library for example during sending the messages? I could imagine that this affects the rapi data for example..

Regards,
Thomas

You just set other information to NA
SetN2kEngineDynamicParam(N2kMsg,0,N2kDoubleNA,N2kDoubleNA,N2kDoubleNA,N2kDoubleNA,FuelRate,N2kDoubleNA);
NMEA2000.SendMsg(N2kMsg);

Interrupts should be short, so it should not effect too much to other routines. Anyway I prefer to use external or internal counters for calculations. I made my own fuel monitor with external counters with latches and even with that getting good results were tricky. You will also need dampers on fuel line. See e.g. FloScan Instrument Co. Inc.

ok, thank you Timo.

regarding pulsation damper. I have a water absorber inside the fuel line. The sensor shall be mounted right after the tank and before the absorber. Do you think I still need this damper? The absorber holds approx half a liter of diesel and the fuel line is made of gummy with 12mm diameter.

If your pump is pusation pump, you will need it. If it is rotation pump (very rare), it may work without.

It does not need to be those, but idea is important. They must be between engine and sensor on both input and return lines.

Hello,

may a stupid question, but kind of datatype I have to use for FluidType in:
SetN2kFluidLevel(N2kMsg, Instance, FluidType, Level, Capacity);

I get the hole time whatever I tried the message error: invalid conversion from 'WHATEVER' to 'tN2kFluidType' [-fpermissive]

You must use tN2kFluidType type define in N2kMessages.h. E.g.

SetN2kFluidLevel(N2kMsg, Instance, N2kft_Water, Level, Capacity);

or

tN2kFluidType FluidType=N2kft_Water;
SetN2kFluidLevel(N2kMsg, Instance, FluidType, Level, Capacity);

Hey Timo,

got it! I just forgot that. Had a break of some month.

Another question, RapidEngineData how often do this need to be send? Is once or twice a second enough? When I think about RPM, for example, it needs to be updated a lot to get a smooth result?

Hi,

I just got the sketch for fuel rate and fuel tank finished but can not test it.

Do anybody have a couple of minutes to look through if this would work? I don´t mean to fix everything or show every the best way. Since I am not that genius in arduino programming I would already be happy if that will work before I start to optimize everything.

EDIT
" I just found some things,I would need smoothing for the tank volume as thoose are moving a lot when the boat is rolling and so on!"

Thanks a lot!

// Get tankvolume (pin 20 Teensy3.2) and fuelrate (pin 19), calculate and send through NMEA2K CAN

#include <Arduino.h>
#include <NMEA2000.h>
#include <N2kMessages.h>
#include <NMEA2000_teensy.h>
tNMEA2000_teensy NMEA2000;

// *********************************************
// NMEA2K related setup
// *********************************************

// List here messages your device will transmit.
const unsigned long TransmitMessages[] PROGMEM = {130306L, 127505L, 0};

// *********************************************
// Variables to setup function reading fuelrate
// *********************************************

volatile int pulsCountValue; // Storage for counted pulses through interupt function
unsigned int FuelRate; // Calculated litres/hour
unsigned char flowsensor = 19; // Sensor Input
unsigned long currentTime;
unsigned long startTime;
#define FuelRateUpdatePeriod 1000


// *********************************************
// Variables to setup function reading fuel tank level
// *********************************************

unsigned char tankVolumeSens = A20;
#define FuelLevelUpdatePeriod 1000

// *********************************************
// Variables to setup sending of NMEA2K message
// *********************************************


double Instance = 0;
double Level;
int Capacity;
tN2kFluidType FluidType = N2kft_Fuel;

void setup() {
  // Set Product information
  NMEA2000.SetProductInformation("00000010", // Manufacturer's Model serial code
                                 100, // Manufacturer's product code
                                 "Fuel Information System",  // Manufacturer's Model ID
                                 "1.1.0.22 (2016-12-31)",  // Manufacturer's Software version code
                                 "1.1.0.0 (2016-12-31)" // Manufacturer's Model version
                                );
  // Set device information
  NMEA2000.SetDeviceInformation(10, // Unique number. Use e.g. Serial number.
                                150, // Device function=Atmospheric. See codes on http://www.nmea.org/Assets/20120726%20nmea%202000%20class%20&%20function%20codes%20v%202.00.pdf
                                75, // Device class=External Environment. See codes on  http://www.nmea.org/Assets/20120726%20nmea%202000%20class%20&%20function%20codes%20v%202.00.pdf
                                2046 // Just choosen free from code list on http://www.nmea.org/Assets/20121020%20nmea%202000%20registration%20list.pdf
                               );
  // Uncomment 2 rows below to see, what device will send to bus. Use e.g. OpenSkipper or Actisense NMEA Reader
  //Serial.begin(115200);
  //NMEA2000.SetForwardStream(&Serial);
  // If you want to use simple ascii monitor like Arduino Serial Monitor, uncomment next line
  //NMEA2000.SetForwardType(tNMEA2000::fwdt_Text); // Show in clear text. Leave uncommented for default Actisense format.

  // If you also want to see all traffic on the bus use N2km_ListenAndNode instead of N2km_NodeOnly below
  NMEA2000.SetMode(tNMEA2000::N2km_NodeOnly, 23);
  // NMEA2000.SetDebugMode(tNMEA2000::dm_Actisense); // Uncomment this, so you can test code without CAN bus chips on Arduino Mega
  NMEA2000.EnableForward(false);
  NMEA2000.ExtendTransmitMessages(TransmitMessages);
  NMEA2000.Open();

  // Void Setup for Fuel Rate
  pinMode(flowsensor, INPUT_PULLDOWN);
  digitalWrite(flowsensor, LOW); // Optional Internal Pull-Up

  attachInterrupt(0, pulsCounter, RISING); // Setup Interrupt

  sei(); // Enable interrupts
  currentTime = millis();
  startTime = currentTime;

  Serial.begin(9600);

  // Setup for Fuel Rate END
}

void loop() {

  // *****************************************************************************
  // Call the looping functions
  // *****************************************************************************

  SendN2kFuelRate();
  SendN2kFuelLevel();
  NMEA2000.ParseMessages();
}



// *****************************************************************************
// Double replacement for standard map function
// *****************************************************************************

double dMap(double x, double in_min, double in_max, double out_min, double out_max) {
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

// *****************************************************************************
// Interupt function. Count pulses from interupt.
// *****************************************************************************

void pulsCounter() {
  pulsCountValue++;
}

// *****************************************************************************
// Read and calculate fuelrate based on pulses from pin 19
// *****************************************************************************

double ReadFuelRate() {

  currentTime = millis();
  // Every 3 second, calculate and print litres/hour
  if (currentTime >= (startTime + 3000))
  {
    startTime = currentTime; // Updates startTime
    
    FuelRate = (pulsCountValue * 3.6); // (Pulse frequency x "3600/1000" 3.6 = flowrate in L/hour

    //    Serial.print(FuelRate, DEC); // Print litres/hour
    //    Serial.println(" L/hour");
  }
  return FuelRate;
}

// *****************************************************************************
// Read and map current fuel tank volume based on pin 20
// *****************************************************************************

double ReadTankVolume() {
  double Level= dMap(analogRead(tankVolumeSens), 60, 900, 0, 100);
  return Level;
}

// *****************************************************************************
// Send NMEA2K message for Fuel rate
// *****************************************************************************

void SendN2kFuelRate() {
  static unsigned long FuelRateUpdated = millis();
  tN2kMsg N2kMsg;

  if ( FuelRateUpdated + FuelRateUpdatePeriod < millis() ) {
    SetN2kEngineDynamicParam(N2kMsg, Instance, N2kDoubleNA, N2kDoubleNA, N2kDoubleNA, N2kDoubleNA, FuelRate, N2kDoubleNA);
    FuelRateUpdated = millis();
    NMEA2000.SendMsg(N2kMsg);
    pulsCountValue = 0;
  }
}

// *****************************************************************************
// Send NMEA2K message for fuel tank level
// *****************************************************************************

void SendN2kFuelLevel() {
  static unsigned long FuelLevelUpdated = millis();
  tN2kMsg N2kMsg;

   if ( FuelLevelUpdated + FuelLevelUpdatePeriod < millis() ) {
    SetN2kFluidLevel(N2kMsg, Instance, FluidType, Level, Capacity);
    FuelLevelUpdated = millis();
    NMEA2000.SendMsg(N2kMsg);
  }
}

NMEA2000 defination for RapidEngineData is 10 times/second.

If you are not sending WindSpeed PGN, you should not list it on TransmitMessages. If you are sending EngineDynamic parameters, you should list that on TransmitMessages.

Set the SetProductInformation and SetDeviceInformation right.

What kind of sensor (pulses/litre) you have and how much you expect to burn fuel in hour? As quick calculation your code accuracy can not be very good.

For analog read you can increase accuracy by increasing avaraging with analogReadAveraging(32); and/or putting readings to ring buffer and averaging that. Fuel level does not need to be very quick, so you could have e.g. 60 readings long ring buffer where you update reading every second. Then you have 1 minute averaging value.