NMEA 2000 Shield

I found out the problem..

It is the SN65HVD234D ! Just not working! I changed to the SN65 because it had some better values like more bandwith, lower power (not that this is prio) and it didn't needed 5V.

The SN65 worked perfectly all in lab, all devices could communicate and everything looked fine. It is a bt more expensive the SN65.

Now I have 16SN65 laying around here without any use.. What do you think what can be the reason, any idea?

The MCP2562 worked now immediately. Again thrown 2 days..

Hi all,

Some questions to those who might know.

I was reading trough Timos NMEA0183ToN2k.ino. How it is defined where/ in which serial GPS receiver is connected?

Would this be ok receiver to get GPS data to Arduino to be transmitted to NMEA2000: https://www.sparkfun.com/products/13740 ?

If I had two of those would they make a GPS compass when installed 6 m apart from each others? Like in this example: https://learn.sparkfun.com/tutorials/gps-differential-vector-pointer ?_ga=2.111597508.1910172968.1538324202-1706626596.1503061770

Is there a way to transmit roll and pitch rates in NMEA 2000? Would it be benefical to use that data to compensate mast head movement in wind measurements? Or is wind data too filtered for that? I have roll and pitch rates on one arduino and have been thinking if I want to buy wind instruments and make VMG and true wind calculations should those calculations be made at the same arduino where I have the sensor for autopilot.

Is rate of turn supported in Timos work?

What is the unit for speed in NMEA2000? m/s I suppose.

On line 67 NMEA0183_3.SetMessageStream(&Serial3);

Check N2kMessages.h SetN2kAttitude

N2kMessages.h should have in function description required unit. Normally they are SI.

If you provide wind data to the bus, it would be best that it would have all conpensations.

Heikkif: If I had two of those would they make a GPS compass when installed 6 m apart from each others? Like in this example: https://learn.sparkfun.com/tutorials/gps-differential-vector-pointer ?_ga=2.111597508.1910172968.1538324202-1706626596.1503061770

I read a bit more about that GPS pointer. I can not say for sure how accurately that would work. With 20 cm error in position on 6 m base you would get about 2 deg. error.

The one problem is that you do not have control to synchronize those receivers. Also they send position data in 1 Hz, which is very slow for making any filtering in moving boat. Real GPS compasses has antennas directly connected to the device, so they can follow satellites fast enough to also filter calculated position information. I have GPS compass with 50 cm antenna separation, but there both antennas are directly connected to the compass unit. That provides <1 deg. accuracy all the time and it sends HDT information to other systems with 10 Hz.

If you would add accurate movement sensor to the system, you could get fast movement infomation from it. Then you could probably have some filtering on both GPS position to calculate better heading. But still the problem would be that when you get position information from GSP, it is allready 1 sec. late.

Hey Timo,

I have some trouble with temperatures. In this case with the coolant temperature.

I measure the value through a voltage divider in parallel to the existing analog gauge.

My Axiom can show Fahrenheit and Celsius. So far so good. I get a value shown on the display of -271 C and according to the messages.h Kelvin is to be sent right?

I could not find out yet what the correct form is, I tried some conversion functions you predefined and they worked fine but still, I can´t figure out what is wrong here.

How do I have to map the temp, is there any definition out there which tells me the temperature range? What I mean is how do I map the 0-3.3 V. For example from -20 to 150 ?

That is simple linear conversion:

TempC = -20 + (x-0)*(150- -20)/(3.3-0); // , where x is your voltage.

For library function you have to provide values in Kelvin, so you use ...CToKelvin(TempC)...

Or better would be use SI in code inside and use conversion only for view values. Then your function would be:

TempK = 253.15 + (x-0)*(423.15-253.15)/(3.3-0);

Then you can just pass TempK to library function.

timolappalainen: That is simple linear conversion:

TempC = -20 + (x-0)*(150- -20)/(3.3-0); // , where x is your voltage.

For library function you have to provide values in Kelvin, so you use ...CToKelvin(TempC)...

Or better would be use SI in code inside and use conversion only for view values. Then your function would be:

TempK = 253.15 + (x-0)*(423.15-253.15)/(3.3-0);

Then you can just pass TempK to library function.

That looks good so far, just one thing: It results in a negative value. I get shown -21.7 C and just multiplying it by -1 doesn't work for any reason.

Can this be used for any kind of temp value like oiltemp ?

What shows -21.7 and why it should be multiplied with -1?

If your x is positive, the function returns allways >=-20 C!

Linear scaling works for any kind of sensor, which has linear scale. E.g. tank sensors have often non linear scale due to irregural tank shape. Then one has to split it several smaller ranges, where linear scaling gives reasonably good result.

timolappalainen: What shows -21.7 and why it should be multiplied with -1?

If your x is positive, the function returns allways >=-20 C!

Linear scaling works for any kind of sensor, which has linear scale. E.g. tank sensors have often non linear scale due to irregural tank shape. Then one has to split it several smaller ranges, where linear scaling gives reasonably good result.

The MFD showed a temp of -21.7 C. The voltage is currently 1.151 V on the analog in. But now the engine is almost warm (~55 C) In code I use the following:

double EngineCoolantTemp = 253.15 + (analogRead(coolantTempPin)-0)*(423.15-253.15)/(3.3-0);

and pass the EngineCoolantTemp through return back. In SendN2kMessagesDynamic I call the function by EngineCoolantTempRead() inside EngineCoolantTempRead() with all the others which have todo with Engine parameter dynamic.

analogRead(coolantTempPin) does not return voltage. It return integer value depending of your analog resesolution. So e.g. with 10 bit resolution it will return value 0 - 1023 and for 12 bit resolution 0 - 4095. The resolution depends of device you are using and in some devices you can control it with analogReadResolution() function.

Then you need to scale value from analogRead with range it is using. That depends of analog reference voltage. E.g. on Teensy you can select internal analog reference with analogReference(0) and then it will use 3.3 V ref. In that case you you convert 10 bit analog pin data to voltage with function analogRead(coolantTempPin)/1023*3.3

So check which resolution and reference you have to get right conversion to voltage.

timolappalainen: analogRead(coolantTempPin) does not return voltage. It return integer value depending of your analog resesolution. So e.g. with 10 bit resolution it will return value 0 - 1023 and for 12 bit resolution 0 - 4095. The resolution depends of device you are using and in some devices you can control it with analogReadResolution() function.

Then you need to scale value from analogRead with range it is using. That depends of analog reference voltage. E.g. on Teensy you can select internal analog reference with analogReference(0) and then it will use 3.3 V ref. In that case you you convert 10 bit analog pin data to voltage with function analogRead(coolantTempPin)/1023*3.3

So check which resolution and reference you have to get the right conversion to voltage.

That was actually my initial question. How to map the 0-1023 to the coolant temp value. This you wrote here I know about (would be very difficult without i guess)

The point I need to know here is what to do with the 0-1023? Usually, there is a range it can be mapped to like I do with the oil pressure. There I use the custom map function (you showed me once I tried my luck with the rudder position) to 0 - 10000 pascals.

In the example of rudder position, the drive I have and the MFD Axiom shows -50 - 0 + 50 degrees. So I map here the 0-1023 like this rudderposition=(analogRead(rudderPositionPin), 49, 1023, -50, 50).

So in case of temperature, I don't know the range I can map it on. I thought there are somewhere definitions or standards which tells me what to use.

Hi Timo,

Thanks for the prompt replies. Question of GPS compass has been discussed here:

https://forum.arduino.cc/index.php?topic=366167.0

So necessarily it does not work that way. Was not mentioned what kind of base was used. Anyhow I want to have a GPS to the bus so I will get some receivers. This could be what I will buy:

https://www.banggood.com/Radiolink-M8N-GPS-Module-UBX-M8030-for-Naze32-APM-CC3D-SP-F3-Naze32-Flip32-PX4-Flight-Controller-p-1051291.html?rmmds=search

It has freq. of 10 Hz.

I have rate of turn measured with a quite good gyro at autopilot. I could feed that to bus and use it on another Arduino to improve dynamics of filtered heading from calculations the same way that I did with roll and pitch calculated from accel signals.

Would it be all wrong if arduino would read GPS-position of the other sensor from bus and other would be connected to serial?

Please do not quote everything as it is, since it does not make sense, since same can be read on previous post.

Do you mean dMap function example I wrote on post #351? map is just a simple function for making linear scaling I wrote. Arduino map uses long values and dMap doubles.

Anyway in principle you 0-1023 maps finally to your temperature values -20 - 150 C. You can also use direct mapping, if you know that it is right: tempK=dMap(analogRead(coolantTempPin),0,1023,253.15,423.15); This is naturally fastest way to calculate it, since you do mapping only once. But now, if your reference changes e.g. by providing more accurate external 2.5V reference, you have to remember that then 0-1023 values are within 0 - 2.5 V, which means that 1023 does not map to 423.15 K anymore. So if you know your sensor mapping in voltages and you make it as commonly usable function, you need two mapping.

...
const double VRef=3.3;
const double AnalogMax=1023;
...
double ReadAnalogVoltage(int pin) {
  return dMap(analogRead(pin),0,AnalogMax,0,VRef);
}
...
double SensorMxyzToKelvin(double Vin) {
  return dMap(Vin,0,3.3,253.15,423.15);
}
...
double ReadTemp(coolantTempPin) {
  return SensorMxyzToKelvin(ReadAnalogVoltage());
}
...

This makes your code a bit more abstract, understantable and movable, but also unfortunately also slower depending of you processor. Teensy works rather well with double, but Mega lags much.

There is no standards, since sensors has different output. But the mapping range depends of sensor and you may find it from the specifig sensor data sheets.

HeikkiF,

Interesting GPS module. Have you found any data sheets? Does it take 3.3 V or 5 V in? What are cummunication pin levels - TTL? Pin order etc.

As in discussion points, you still will have unsynchronized errors in GPS. I think that can work on land as in laser pointer, since the position can be filtered a lot, when antennas are not moving.

Note also that using single GPS for heading does not give right result. GPS returns your moving direction and compass (GPS, solid state or what ever) returns your heading. Depending of conditions - specially on currents they may differ a lot. So you can not use single GPS for calibrating magnetometer data.

Note also that my example 20 cm error may be far too optimistic. It is possible that two different GPS has 1 meter or even more error in different directions. On the other hand I have not tested accuracy of those and specially two sensor around same position. You can only byu two, put them to e.g. 10 m away of each other, plot positions of them and analyze data is it usable. Simulate boat movements by moving them up and down, but keeping direction same. I am interested to hear results.

From bus you get maximum 1 Hz position, so I think that is unusable and late.

Those GPS modules seem to be not so well documented. If i buy I will try it with 3.3 V Vcc first. http://radiolink.com.cn/doce/product-detail-115.html

The max. freq. for position via bus being 1Hz is it because #define TimeUpdatePeriod 1000 or is there other limitations in NMEA2000? If not what kind of delays is there in data transmission via bus? I quess one could delay the position coming straight from GPS if time info is delayed on the bus.

Sorry small mistake. I was thinking about 1 Hz PGN129029/GNSS, but there is also 10 Hz PGN129025/LatLonRapid. But not all GPS provide PGN129025. And specially if you have old NMEA0183 GPS with converter, there is only 1 Hz PGN129029.

And if you are planning to put other GPS to the bus by yourself, then you can provide PGN129025 with 10 Hz and PGN129029 1 Hz to the bus.

PGN129025 is single frame higher priority message. One frame transfer takes about 400 us. Since bus is multimaster bus, there may be others sending data at same time and our frame transfer may be delayed. Lower source address has higher priority. So you can try to set your device source address to 0, but then depending of other devices on the bus higher priority devices may rise your source address during address claiming procedure. But in normal boat bus I would expect that extra delay would be very rare.

So if you sending lat/lon in 100 ms period, the transfer delay should be less than 1 ms, which is <1/100

Is rate of turn rapid also? N2kMessages.h nor https://www.nmea.org/content/nmea_standards/messages_pgns.asp does not mention rapid. I have been thinking of trying this GPS compass thing on another Arduino than where autopilot is and where rate of turn is measured already. It would be nice to have rate of turn via bus but 1 Hz feels slow.

I do not have NMEA 2000 documentation and if I would I could not say anything due to agreement. So all information has to be digged from public docs.

NMEA 2000 defines default rates and rate limits for PGNs. Certified systems follow these rates. If you make system for your own boat, you can use different rates. Normally fast packet messages has max. 1 Hz rate. Single frame packest are up to 200 Hz. There is also limits for min. rate. E.g. if you send temperature 130316 with 0.1 Hz, MFDs thinks that as lost data between transmits.

But anyway I digged default rates for 127250 10 Hz, 127257 1 Hz, 127251 10 Hz

So no problem to use 10 Hz for rate of turn. And as I mentioned for your own system you can use 20 Hz, if you like.

Hey Timo,

regarding the Coolant temp in case of Raymarine Axiom. I have figured out that I have to map the sensor values from -20 to 150 C in Kelvin.

double EngineCoolantTemp = dMap(analogRead(coolantTempPin), coolantTempPin_Min, coolantTempPin_Max, 253, 423);

So this was showing the correct values in C on the MFD. I guess this is the case for all other temperaturs as well.

another thing regarding RPM. I could do this now with freqMulti library. Working good so far. To get the values back to 0 when the engine is not running I overwrite the RPM value with an if..else. If rpm available do measure every 500 ms and calculate and else is if rpm lower than 200 than set rpm to 0.

Working so far. But as soon as I try to push the values into a circular buffer I do get just strange stuff. So my question is how to smooth the RPM values?

double ReadEngineRpm() {
    if (freq1.available()) {
      sum1 = sum1 + freq1.read();
      count1 = count1 + 1;
    }

    if (timeout > 500) {
      if (count1 > 0) {
        EngineSpeed = (freq1.countToFrequency(sum1 / count1)*2);
      }

      sum1 = 0;
      count1 = 0;
      timeout = 0;
    }

    if (EngineSpeed >= 200)
    {
        EngineSpeed = EngineSpeed;
    }
    else
    {
        EngineSpeed = 0;
    }
        return EngineSpeed; 

}

I am sure there are better ways to implement this right?