NMEA 2000 Shield

if ( Manufacturer != 140 ) return false; // Not Lowrance proprietary PGN

Timo,
don't forget that all of their sensors could be sold and go by either Lowrance, B&G or Simrad names. I think they have different manufacturer numbers/codes.
I found:
SIMRAD 1857;
Lowrance 140;
B&G ??; // I couldnt find one for them in the document here

http://www.nmea.org/Assets/20160116%20_%20jan%202016%20manu%20code%20and%20product%20code.pdf

My point is: these 3 manufactures need to be included in the code not just Lawrance.

B&G is 381 - on list "NMEA 2000 registration list". So change rule to:
if ( !(Manufacturer == 140 ||
Manufacturer == 1857 ||
Manufacturer == 381) ) return false; // Not Lowrance, Simrad or B&G proprietary PGN

timolappalainen:
I just tested WindMonitor with my own DUE and it works OK. I made tests with old NMEA2000_due/due_can and new version of both (just released on my GitHub few minutes ago). So I think the problem is not with library.

If you can read, but TX fails, there is some problem with your TX side. Have you checked TX pin is connected? Are terminal resistors OK? Maybe tranceiver does not work on TX?

Just to let you know that we have tested the TX side but weren't successfull. Several fora stated that this could be caused by the chip SN65HVD230. I've ordered a MCP2551 instead and will let you know later on. Thanks so far.

timolappalainen:
I just tested WindMonitor with my own DUE and it works OK. I made tests with old NMEA2000_due/due_can and new version of both (just released on my GitHub few minutes ago). So I think the problem is not with library.

If you can read, but TX fails, there is some problem with your TX side. Have you checked TX pin is connected? Are terminal resistors OK? Maybe tranceiver does not work on TX?

Just to let you know that we weren't able to get the TX side working. From other fora we learned that this could be caused by the SN65HVD230 chip. We have ordered a MCP2551 and let you know the results later on. Many thanks so far.

With DUE you need to remember to power MCP2551 with 3.3V only. I prefer MCP2562, since you can power it with 5V and provide 3.3V to pin 5 for defining IO levels.

timolappalainen:
We seem to have information for PGN 65285 for Lowrance proprietary data.
{ "Lowrance: Temperature", 65285, false, 8, 0,
{ { "Manufacturer Code", 11, RES_MANUFACTURER, false, "=140", "Lowrance" }
, { "Reserved", 2, RES_NOTUSED, false, 0, "" }
, { "Industry Code", 3, RES_LOOKUP, false, "=4", "Marine Industry" }
, { "Temperature Instance", 4, 1, false, 0, "" }
, { "Temperature Source", 4, 1, false, 0, "" }
, { "Actual Temperature", BYTES(2), RES_TEMPERATURE, false, "K", "" }
, { 0 }
}
}

So write handler for PGN 65285 and try code:
bool ParseN2kLowranceTempPGN65285(const tN2kMsg &N2kMsg, unsigned char &TempInstance, tN2kTempSource &TempSource,
double &ActualTemperature) {
ActualTemperature=N2kDoubleNA;
TempInstance=0;
TempSource=N2kts_SeaTemperature;
if (N2kMsg.PGN!=65285L) return false;
int Index=0;
uint16_t Manufacturer=(N2kMsg.Get2ByteUInt(Index) & 0x7ff);
if ( Manufacturer != 140 ) return false; // Not Lowrance proprietary PGN
uint8_t b=N2kMsg.GetByte(Index);
TempInstance=(b & 0x0f);
TempSource=(tN2kTempSource)( (b & 0xf0) >> 4 );
ActualTemperature=N2kMsg.Get2ByteUDouble(0.01,Index);

return true;
}

That's great Timo! I guess I should have looked under the covers deeper -- Will add this method to my code and test at the boat today... will let you know. My sensor is branded Lowrance with a manufacture code of 140 so we should be good.

Just to be clear, for the handler I'm adding:

...
{130314L,&Pressure},
  {130316L,&TemperatureExt},
  {65285,&LowranceTemp},
  {0,0}

That worked! However, the source is "sea" not "engine room" -- I can live with that but not sure if you want to correct that in the library if you want to officially support this sensor.

This code:

void LowranceTemp(const tN2kMsg &N2kMsg) {
    unsigned char TempInstance;
    tN2kTempSource TempSource;
    double ActualTemperature;
    
    if (ParseN2kLowranceTempPGN65285(N2kMsg,TempInstance,TempSource,ActualTemperature) ) {
        OutputStream->print("Lowrance Temperature source: "); PrintN2kEnumType(TempSource,OutputStream,false);
        PrintLabelValWithConversionCheckUnDef(", actual temperature (F): ",ActualTemperature,&KelvinToF);
    } else {
      OutputStream->print("Failed to parse PGN: ");  OutputStream->println(N2kMsg.PGN);
    }
}

bool ParseN2kLowranceTempPGN65285(const tN2kMsg &N2kMsg, unsigned char &TempInstance, tN2kTempSource &TempSource,
                     double &ActualTemperature) {
  ActualTemperature=N2kDoubleNA;
  TempInstance=0;
  TempSource=N2kts_SeaTemperature;
  if (N2kMsg.PGN!=65285L) return false;
  int Index=0;
  uint16_t Manufacturer=(N2kMsg.Get2ByteUInt(Index) & 0x7ff);
  if ( Manufacturer != 140 ) return false; // Not Lowrance proprietary PGN
  uint8_t b=N2kMsg.GetByte(Index);
  TempInstance=(b & 0x0f);
  TempSource=(tN2kTempSource)( (b & 0xf0) >> 4 );
  ActualTemperature=N2kMsg.Get2ByteUDouble(0.01,Index);
 
  return true;
}

Produces:

Lowrance Temperature source: sea, actual temperature (F): 88.88

Are you sure it is returning everything right and my code was right? Did you test output with different temp source - does it change? If the enum tN2kTempSource does not match to Lowrance, we need to define own:

enum tN2kLowranceTempSource {
                            N2kLts_EngineRoomTemperature=0
                            };

const char* tN2kLowranceTempSourceStrs[] = { "engine room" };

and solve what enum values are.

timolappalainen:
Are you sure it is returning everything right and my code was right? Did you test output with different temp source - does it change? If the enum tN2kTempSource does not match to Lowrance, we need to define own:

enum tN2kLowranceTempSource {

N2kLts_EngineRoomTemperature=0
                           };

const char* tN2kLowranceTempSourceStrs[] = { "engine room" };



and solve what enum values are.

I didn't try changing the TempSource -- but the TempInstance returned should have been 3 (as shown on my Lowrance HDS) which is "Engine Room", not 0 and Sea Temp as passed in the handler.

bool ParseN2kLowranceTempPGN65285(const tN2kMsg &N2kMsg, unsigned char &TempInstance, tN2kTempSource &TempSource,
                     double &ActualTemperature) {
  ActualTemperature=N2kDoubleNA;
  >>>> TempInstance=0;
  >>>> TempSource=N2kts_SeaTemperature;
  if (N2kMsg.PGN!=65285L) return false;
  int Index=0;
  uint16_t Manufacturer=(N2kMsg.Get2ByteUInt(Index) & 0x7ff);
  if ( Manufacturer != 140 ) return false; // Not Lowrance proprietary PGN
  uint8_t b=N2kMsg.GetByte(Index);
  >>>> TempInstance=(b & 0x0f);
  >>>> TempSource=(tN2kTempSource)( (b & 0xf0) >> 4 );
  ActualTemperature=N2kMsg.Get2ByteUDouble(0.01,Index);
 
  return true;
}

But the code you provided didn't seem to get that from the message data?

The Lowrance PGN I used in the handler was "65285", not "65285L" -- not sure if that matters?

...
 {130316L,&TemperatureExt},
 {65285,&LowranceTemp},

Interestingly, the Actual Temperature was correct value.

Change your printing code to:

OutputStream->print("Lowrance Temperature (");
OutputStream->print(TempInstance);
OutputStream->print(") source: ");
OutputStream->print((unsigned char)TempSource);

and try with different sources (engine, water, cabin) and report the results.

L is integer literal and defines value to be treated as long. Actually for PGNs should have been UL

timolappalainen:
Change your printing code to:

OutputStream->print("Lowrance Temperature (");

OutputStream->print(TempInstance);
OutputStream->print(") source: ");
OutputStream->print((unsigned char)TempSource);



and try with different sources (engine, water, cabin) and report the results.

L is integer literal and defines value to be treated as long. Actually for PGNs should have been UL

So it looks like the TempSource is the issue -- the TempInstance is correct:

DEBUG:: Lowrance Temperature (Instance: 3 source: 0) // 3 is engine rm
DEBUG:: Lowrance Temperature (Instance: 4 source: 0) // 4 is cabin

Please do not quote everything.

Change code to:
TempSource=(tN2kTempSource)(b & 0x0f);
TempInstance=( (b & 0xf0) >> 4 );

Hi Timo and all,

I tend to built a depth alarm for NMEA 2000 first and later maybe something more ambitious. Have arduino due board and in the mail is coming http://copperhilltech.com/can-bus-mini-breakout-board/. I guess for NMEA-bus that 120 Ohm resistor has to picked off as bus has its own terminations. When trying to verify examples in Timos library following error is generated:

Arduino: 1.8.3 (Windows 10), Board: "Arduino Due (Programming Port)"

C:\Users\heikki\AppData\Local\Temp\arduino_build_230965\sketch\DataDisplay.ino.cpp.o: In function `__static_initialization_and_destruction_0':

C:\Users\heikki\Documents\Arduino\libraries\NMEA2000-master\src/NMEA2000_CAN.h:78: undefined reference to `tNMEA2000_due::tNMEA2000_due()'

collect2.exe: error: ld returned 1 exit status

Using library NMEA2000-master at version 1.1.141 in folder: C:\Users\heikki\Documents\Arduino\libraries\NMEA2000-master
Using library due_can-master at version 2.0.1 in folder: C:\Users\heikki\Documents\Arduino\libraries\due_can-master
exit status 1
Error compiling for board Arduino Due (Programming Port).

Left some jargon of in between the error message in order to make message shorter. What could possibly go wrong here?

Br: Heikki

Reload the NMEA2000_due (GitHub - ttlappalainen/NMEA2000_due: Inherited object for use NMEA2000 library for Arduino Due Boards) library. I can produce that error if I remove file NMEA2000_due.cpp from path libraries\NMEA2000_due-master so I expect you are missing that file.

I will try as Timo suggested. There is a file like that in the location with size of 3KB.

Then you could also try to remove completely your NMEA2000_due-master directory. Then it should give error:
In file included from xxx\Arduino\Examples\xxx\xxx:x:x

xxx\Arduino\libraries\NMEA2000/NMEA2000_CAN.h:77:26: fatal error: NMEA2000_due.h: No such file or directory

#include <NMEA2000_due.h>

If not you have NMEA2000_due.h somewhere, where it should not be.

Now examples seem to verify correctly. Since my adventure with arduino is 5 days old I uninstalled and deleted everything related to arduino and downloaded everything again. Strangely enough computer still had stuff for due board left.

Hi Timo, I'm using your library and I want to congratulate you first and foremost for the work you've done.

We come to the question ......

I joined n2k at my Lowrance Elite Ti and although I can see the device and all the data stream I can not see product information.

In their place I see blank fields, and in the selection of sources I see three question marks

I see that another user with the Lowrance HDS (page 14) had the same problem and then seems to have resolved it.

You could help me please

I'm using the latest version of your library, arduino-mega and shield can-bus V1.2

Hi, and thanks.

I did not find your reference for "Lowrance HDS" - I made just quick search with "Lowrance" and "HDS".

Have you tested that with e.g. example TemperatureMonitor and not done any memory squeezing prefered for Uno board? You could also try does it help to enable interrupt by adding line

#define N2k_CAN_INT_PIN xx

before any includes. xx is the pin where interrupt has been connected on CAN_BUS shield.

You could also enable rows on example:

  delay(5000); // add this row to have time to open serial monitor
  Serial.begin(115200);
  NMEA2000.SetForwardStream(&Serial);
  NMEA2000.SetForwardType(tNMEA2000::fwdt_Text); // Show in clear text. Leave uncommented for default Actisense format.

And record the communication and email me.

In principle MDF devices should query "Product information", when they see new device on the bus. There are two way to do that and current version of library support only PGN request. I will publish new version of library within few days after finishing tests. That support also responding to query done by "Request group function" for "Product information". Meanwhile there are two possibilities:

  1. Email me directly so I can send you a test release, which has that support.
  2. Add to loop() code to send ProductInformation in every 1-2 sec.
#define ProductInformationSendPeriod 2000
void loop() {
  static unsigned long ProductInformationSendTime=millis()+ProductInformationSendPeriod ;
    if ( ProductInformationSendTime<millis() ) {
       NMEA2000.SendProductInformation();
       ProductInformationSendTime=millis()+ProductInformationSendPeriod ;
    }
}

Even if 2. will work, I prefere to test without it with new release. And I would like to get anyway the information for the solution.

Hi Timo .....
I solved Method 2, that is, sending the ProductInformation every two seconds as recommended by you.
Anyway I attach you the log obtained on Arduino's serial monitor.

CAN device ready
Start address claim for device 0
Pri:6 PGN:60928 Source:22 Dest:255 Len:8 Data:1,0,C0,FF,0,84,32,C0
Pri:3 PGN:127488 Source:22 Dest:255 Len:8 Data:0,F8,43,FF,FF,7F,FF,FF
Pri:3 PGN:129026 Source:22 Dest:255 Len:8 Data:1,FC,D0,4E,A,0,FF,FF
Pri:3 PGN:127488 Source:22 Dest:255 Len:8 Data:0,F8,43,FF,FF,7F,FF,FF
Pri:3 PGN:129026 Source:22 Dest:255 Len:8 Data:1,FC,D0,4E,A,0,FF,FF
Pri:3 PGN:127488 Source:22 Dest:255 Len:8 Data:0,F8,43,FF,FF,7F,FF,FF
Pri:3 PGN:129026 Source:22 Dest:255 Len:8 Data:1,FC,D0,4E,A,0,FF,FF
Pri:3 PGN:127488 Source:22 Dest:255 Len:8 Data:0,F8,43,FF,FF,7F,FF,FF
Pri:3 PGN:129026 Source:22 Dest:255 Len:8 Data:1,FC,D0,4E,A,0,FF,FF
Pri:6 PGN:127508 Source:22 Dest:255 Len:8 Data:1,F8,4,FF,7F,FF,FF,1
Pri:6 PGN:127508 Source:22 Dest:255 Len:8 Data:0,64,5,A,0,FF,FF,0
Pri:6 PGN:130316 Source:22 Dest:255 Len:8 Data:1,1,4,74,7D,4,84,B
Pri:6 PGN:130312 Source:22 Dest:255 Len:8 Data:1,1,4,F2,72,23,73,FF
Pri:6 PGN:130311 Source:22 Dest:255 Len:8 Data:1,4,F2,72,B6,35,F6,3
Pri:6 PGN:130310 Source:22 Dest:255 Len:8 Data:1,C1,70,95,74,F6,3,FF
Pri:6 PGN:127513 Source:22 Dest:255 Len:8 Data:1,D2,1,9A,1,5F,82,61
Pri:6 PGN:127506 Source:22 Dest:255 Len:9 Data:1,1,1,56,5B,8C,5,D2,0
Pri:2 PGN:127257 Source:22 Dest:255 Len:8 Data:1,E3,FD,A3,1,AF,FA,FF
Pri:6 PGN:127489 Source:22 Dest:255 Len:26 Data:0,A0,19,B,E,C5,8A,8D,5,39,0,4C,6B,75,0,FF,FF,FF,FF,FF,1,0,0,0,7F,7F
Pri:6 PGN:127493 Source:22 Dest:255 Len:8 Data:0,FC,4C,1D,3B,D,5,FF
Pri:3 PGN:126992 Source:22 Dest:255 Len:8 Data:1,0,93,44,0,73,F4,24
Pri:6 PGN:129029 Source:22 Dest:255 Len:47 Data:1,93,44,0,73,F4,24,0,80,2,D6,50,2E,57,8,0,80,76,D2,4E,5C,1F,3,A0,37,A0,0,0,0,0,0,10,1,C,50,0,32,0,DC,5,0,0,1,F0,0,C8,0
Pri:6 PGN:129539 Source:22 Dest:255 Len:8 Data:1,6C,78,0,B0,FF,FF,7F
Pri:2 PGN:127250 Source:22 Dest:255 Len:8 Data:0,1A,9B,F4,FD,C0,3,FD
Pri:6 PGN:129038 Source:22 Dest:255 Len:27 Data:41,15,CD,5B,7,80,86,45,D0,C0,B5,BB,F,7,40,D,D0,7,FF,FF,FF,E0,93,0,0,F1,FF
Pri:6 PGN:127501 Source:22 Dest:255 Len:8 Data:2,CD,FF,FF,FF,FF,FF,FF
Pri:6 PGN:130314 Source:22 Dest:255 Len:8 Data:0,2,0,0,A0,F,0,FF
Pri:2 PGN:127245 Source:22 Dest:255 Len:8 Data:1,F9,97,FC,69,3,FF,FF
Pri:6 PGN:127501 Source:22 Dest:255 Len:8 Data:3,FF,DF,FF,FF,FF,FF,FF
Pri:3 PGN:127488 Source:22 Dest:255 Len:8 Data:0,F8,43,FF,FF,7F,FF,FF
Pri:3 PGN:129026 Source:22 Dest:255 Len:8 Data:1,FC,D0,4E,A,0,FF,FF
Pri:3 PGN:127488 Source:22 Dest:255 Len:8 Data:0,F8,43,FF,FF,7F,FF,FF
Pri:3 PGN:129026 Source:22 Dest:255 Len:8 Data:1,FC,D0,4E,A,0,FF,FF
Pri:3 PGN:127488 Source:22 Dest:255 Len:8 Data:0,F8,43,FF,FF,7F,FF,FF
Pri:3 PGN:129026 Source:22 Dest:255 Len:8 Data:1,FC,D0,4E,A,0,FF,FF
Pri:3 PGN:127488 Source:22 Dest:255 Len:8 Data:0,F8,43,FF,FF,7F,FF,FF
Pri:3 PGN:129026 Source:22 Dest:255 Len:8 Data:1,FC,D0,4E,A,0,FF,FF
Pri:3 PGN:127488 Source:22 Dest:255 Len:8 Data:0,F8,43,FF,FF,7F,FF,FF
Pri:3 PGN:129026 Source:22 Dest:255 Len:8 Data:1,FC,D0,4E,A,0,FF,FF
Pri:3 PGN:127488 Source:22 Dest:255 Len:8 Data:0,F8,43,FF,FF,7F,FF,FF
Pri:3 PGN:129026 Source:22 Dest:255 Len:8 Data:1,FC,D0,4E,A,0,FF,FF
Pri:6 PGN:127508 Source:22 Dest:255 Len:8 Data:1,F8,4,FF,7F,FF,FF,1
Pri:6 PGN:127508 Source:22 Dest:255 Len:8 Data:0,64,5,A,0,FF,FF,1
Pri:6 PGN:130316 Source:22 Dest:255 Len:8 Data:1,1,4,74,7D,4,84,B
Pri:6 PGN:130312 Source:22 Dest:255 Len:8 Data:1,1,4,F2,72,23,73,FF
Pri:6 PGN:130311 Source:22 Dest:255 Len:8 Data:1,4,F2,72,B6,35,F6,3
Pri:6 PGN:130310 Source:22 Dest:255 Len:8 Data:1,C1,70,95,74,F6,3,FF
Pri:6 PGN:127513 Source:22 Dest:255 Len:8 Data:1,D2,1,9A,1,5F,82,61
Pri:6 PGN:127506 Source:22 Dest:255 Len:9 Data:1,1,1,56,5B,8C,5,D2,0
Pri:2 PGN:127257 Source:22 Dest:255 Len:8 Data:1,E3,FD,A3,1,AF,FA,FF
Pri:6 PGN:127489 Source:22 Dest:255 Len:26 Data:0,A0,19,B,E,C5,8A,8D,5,39,0,4C,6B,75,0,FF,FF,FF,FF,FF,1,0,0,0,7F,7F
Pri:6 PGN:127493 Source:22 Dest:255 Len:8 Data:0,FC,4C,1D,3B,D,5,FF
Pri:3 PGN:126992 Source:22 Dest:255 Len:8 Data:1,0,93,44,0,73,F4,24
Pri:6 PGN:129029 Source:22 Dest:255 Len:47 Data:1,93,44,0,73,F4,24,0,80,2,D6,50,2E,57,8,0,80,76,D2,4E,5C,1F,3,A0,37,A0,0,0,0,0,0,10,1,C,50,0,32,0,DC,5,0,0,1,F0,0,C8,0
Pri:6 PGN:129539 Source:22 Dest:255 Len:8 Data:1,6C,78,0,B0,FF,FF,7F
Pri:2 PGN:127250 Source:22 Dest:255 Len:8 Data:0,1A,9B,F4,FD,C0,3,FD
Pri:6 PGN:129038 Source:22 Dest:255 Len:27 Data:41,15,CD,5B,7,80,86,45,D0,C0,B5,BB,F,7,40,D,D0,7,FF,FF,FF,E0,93,0,0,F1,FF
Pri:6 PGN:127501 Source:22 Dest:255 Len:8 Data:2,CD,FF,FF,FF,FF,FF,FF
Pri:6 PGN:130314 Source:22 Dest:255 Len:8 Data:0,2,0,0,A0,F,0,FF
Pri:2 PGN:127245 Source:22 Dest:255 Len:8 Data:1,F9,97,FC,69,3,FF,FF
Pri:6 PGN:127501 Source:22 Dest:255 Len:8 Data:3,FF,DF,FF,FF,FF,FF,FF
Pri:3 PGN:127488 Source:22 Dest:255 Len:8 Data:0,F8,43,FF,FF,7F,FF,FF
Pri:3 PGN:129026 Source:22 Dest:255 Len:8 Data:1,FC,D0,4E,A,0,FF,FF
Pri:3 PGN:127488 Source:22 Dest:255 Len:8 Data:0,F8,43,FF,FF,7F,FF,FF