ttlappalainen / NMEA2000

NMEA2000 library for Arduino
522 stars 217 forks source link

Understanding N2K device selection #23

Open usauerbrey opened 7 years ago

usauerbrey commented 7 years ago

Hi Timo

hopefully you can help me in unstanding the behaivior of N2K devices. How selects a receiving device the "correct" source, if more than one device on the bus are sendind the same type of information. Is there a selection mechanism or so?

Best Regards Uwe

ttlappalainen commented 7 years ago

Hi,

If you have my library devices, ensure that they have different Unique number (set by SetDeviceInformation) and different Model serial code (set by SetProductInformation). Then MFD should show these as separate device so that you can define, what source to use. My Garmin GMI 20 can do that. But unfortunately this depends also MFD.

You can also try to use different instance number on different devices.

Timo

ttlappalainen commented 7 years ago

Now there is a new version, which hopefully is much better.

usauerbrey commented 7 years ago

Hi Timo

I have the following roblem. My log (wheel) gets dirty often and then it gives a STW of 0 kn. This is not a problem as such, I have my SOG an this is ok. But the True Wind is calculated by the wind instrument using the STW and I cannot set it to use SOG instead. So I would like to wirte a program for the libray device, which reads STW and SOG and if STW is 0 but SOG not, I would send a STW-information with the SOG value. But how can I make the wind instrument take my STW value and not the value of 0 from the log. The MFD (Raymarine does not give me the seection of sources) at all.

So my question is, how does the N2K standard defines which souce is used. Or is it just up to the manufacturer of a device which source it takes?

Best Regards Uwe

ttlappalainen commented 7 years ago

Sorry for delay,

I do not know is there any stndard for that. I think some devices just use PGN information. This is very problematic, if you have e.g. two heading sensor having 1 degree difference, so value will be jumping all the time. I had this case with my autopilot so I had to leave my spare sensor disconnected.

How are you devices constructed? Is your log N2k or NMEA0183 device? The point is that could you connect the log directly to your own device and make that as gateway? If the log is N2k log, then you would need to use Arduino due or add second cab bus to the system.

Timo

usauerbrey commented 7 years ago

Hi Timo

What delay, you are really fast ;-) The log device is n2k, but your proposal making a gateway sounds interesting, even so I had expected, that there is some kind of selection mechanism on the bus. But maybe there is one, but it is not really known.

Best Regards Uwe

ttlappalainen commented 7 years ago

Hi,

The selection mechnism exist. Each device has unique ID and they will get some source number on bus. So the device, which should select data should record unique ID, which you select on configuration. Then they can send ISO address claim query and see that device source number. Then they will use that information. Garmin GMI20 seem to work like that. I think I tested system by feeding cabin temperature from two sources and I could select, which one to show. If Raymarine does not offer that, then you can not do much.

So in principle you could have gateway, where all sensors are on one N2k bus and others on other bus. Then in your code you just pass all traffic directly from one bus to other except STW, which you check and replace with SOG in case of problem with log information.

I think it would be cheaper to buy a new log than spend several hours writing reliable code, but of coarse it is not so fun. Like if I convert the time I have spend with NMEA2000 library to money, I could have bought several converters and MFD:s etc.

usauerbrey commented 7 years ago

Hi Timo

concerning the fun, I absolutely agree with you. But in this case, it is not a question of buying a new log. The log is relatively new and the situation of blocking the wheel by seagrass, small mussels, or so come regularly.

Best Regards Uwe

ttlappalainen commented 7 years ago

OK. I think I need to do some development for library to check that there is no restriction to have two tNMEA2000 objects. There should not, but I have not tested. Also I need to check that all messages, which will not be handled, will be passed to gateway handler.

usauerbrey commented 7 years ago

Hi Timo

It is not urgent. First I have to get the HW. In summer I live on my boat, so it it difficut to get the HW. I think this will be a project for the winter, when I live in germany. So take your time ;-)

Best Regards Uwe

ttlappalainen commented 7 years ago

Hi,

Within 3 weeks I have to take my boat up for winter, so I can take my Arduino Due for test bed. It has two can internally, so I can make ready gw code.

Timo

usauerbrey commented 7 years ago

Hi, I am in the mediteranian, I will be sailing till mid of Nov. ;-)

fred2222 commented 7 years ago

Hi, I have Arduino due with nmea2000 working however I am not able to have two Address Claims on one Arduino program for two devices. Anybody can give me a hint how to send two Address Claims to the NMEA2000 network ?

Thanks

Fred

ttlappalainen commented 7 years ago

Hi, I am sorry I di not understood your question? Normally address claiming will be done automatically by library. As in examples in setup you only need to define your node type and optional default address: NMEA2000.SetMode(tNMEA2000::N2km_NodeOnly,22); Then in your loop you call periodically: NMEA2000.ParseMessages(); With those library will automatically respond to ISO Address requests and do address claiming if necessary.

Or are you thinking about having two devices - e.g. temperature monitor and wind monitor with different device information - on one same program? If yes, you currently can not. I have option for that, it is still under construction, since it is very rarely needed.

fred2222 commented 7 years ago

Hi Timo,

Thank you for the very fast reply ! Yes I want to have two devices : speed sensor and engine tilt on one same program. I want to have two devices in one Arduino due program that is connected to the n2k network. Currently those two devices are on the network and work OK but I can only claim one address. Sorry for the confusion. Newbie here with little programming knowledge.

Fred

ttlappalainen commented 7 years ago

Hi,

Still question is that do you actually need two different device address? On the bus sight it is only important that each device providing any type of data has unic ID, created from device information. Then they would work right on bus. Other devices as far as I have experienced are not much interested about device class and device function codes, which in principle defines the type of device. So even you define your device as class 60 (Navigation) and function 135 (Depth/Speed), you can still send tilt information and MFD:s should show both. So currently having two devices on same code is more just informative.

I'll do the mutidevice code readey on some day, but you have to wait some weeks for that.

fred2222 commented 7 years ago

Hi Timo,

The Garmin GPSMap 740 does not see the device if the Address Claim is not sent once. Then it cannot be configured. The data is on the bus. The Arduino program always does the Address Claim for one device automatic so the Garmin only sees this one and the other cannot be configured. Both devices are sending the device class and function codes when I look with NMEA reader on the bus, There are 2 Tilt devices for 2 engines and they need to be configured to the engines. No problem if it takes several weeks. The holiday season is here..... Thanks for looking at it.

Fred

ttlappalainen commented 7 years ago

I think you only mean that address claiming should be resent. Normally devices should request ISO address claim from other devices. We have noticed that some devices expect that device will send it periodically. So try this: void SendIsoAddressClaim() { static unsigned long LastSent=millis();

if ( LastSent+2000<millis() ) { LastSent=millist(); NMEA2000.SendIsoAddressClaim(); } } ... void loop() { ... NMEA2000.ParseMessages(); SendIsoAddressClaim(); }

fred2222 commented 7 years ago

Hi Timo,

Thanks for the tip to resend the Address Claiming on a regular basis. This is not what my problem is. However it does solve the common problem if some devices need a regular claiming. ( for those that want to test it, just remove the "t" in millist !) My problem only needs Address Claiming once for each device at the start of each device. I found however a quick and dirty solution if I send the Address Claiming for the second device later. I need to clean up my code and then it should be OK. Your solution would also help to delay the Address Claiming at the start of each device. This often is needed if devices do switch on before the bus is active. So thank you for the tip. I hope others can use it too. I intend to look at the bus in short time to look at the traffic as I still need to understand what devices are sending. Wish you a good holiday season !

Fred

ttlappalainen commented 7 years ago

In principle it should not matter when bus becomes active. Lets say you turn on one device, so it will do address claiming and start to send data to the bus. Then you turn on other and it does same. If the addresses are same, then one of those devices may reclaim its address. Next you turn on MFD. MFD should send ISO Address request for PGN 60928 (=ISO Address claim) with broadcast to find out what devices are on the bus. Other devices would again respond to this with their information. Normally MFD would then send ISO request for 126992 (Product information) for each device one by one. So now it knows alldevices on the bus. OK, then you turn on new device. It will again send address claiming. Now again MFD should then see this and query product information for new device. Also at least my GMI 20 MFD sends address claiming priodically (e.g. 20 sec) to check that which devices are still available on the bus. If some device does not respond, it drops it out from the list. So some devices does not do not send periodical request. Instead they expect others to send address claim in every 2-5 sec.

fred2222 commented 7 years ago

You are right. But I noticed the Garmin GPSMAP740 lost two engine devices on the bus and did not reclaim them even after a long period. Those devices have fuel consumption information and so the Garmin did not update. They are removed from the NMEA2000 list of devices. There must be a protocol problem. This is one of the problems I would like to see when watching the bus traffic.

Thanks Timo your expertise is of great help.

Fred

ttlappalainen commented 7 years ago

Sorry but I am still curious about your construction. So do you have those two engine devices on same Arduino? And speed also? So are we actually talking about 2 engine instances + speed, which can be send from one N2k bus device?

fred2222 commented 7 years ago

Yes exactly. In fact there are 2 engine devices not on the Arduino that provide fuel and RPM and on the Arduino are just two engine Tilt devices + paddle speed sensor ( waterspeed ). I would include some temperature sensors maybe later. If it is not time critical you could include a lot more devices. As the engine fuel and RPM devices are in the back of the boat it makes no sense to include them in the Arduino as that box is near the helm The engine Tilt use the ADC's on the Arduino.

ttlappalainen commented 7 years ago

OK. But still I do not see the real requirement for address claiming for two devices. You just do periodically e.g. 100 ms: NMEA2000.SetN2kEngineParamRapid(N2kMsg,0,N2kDoubleNA,N2kDoubleNA,Engine0Tilt); NMEA2000.SendMsg(N2kMsg); NMEA2000.SetN2kEngineParamRapid(N2kMsg,1,N2kDoubleNA,N2kDoubleNA,Engine1Tilt); NMEA2000.SendMsg(N2kMsg);

and also speed with 1000 ms period. Also you may need to send ISO Address claim 2000 ms period as discusssed earlier.

MFD should be happy to see your system as one device, which just send data from different sources. As far as I know there is no limitation on NMEA2000 how much different type of data one device can send.

fred2222 commented 7 years ago

Yes, I would like to try this. But there is no need to send Address Claim periodically. Once the Garmin has seen the device it can be configured and I do not think it looses the Address like the external engine interfaces do. However full test and looking at the bus with NMEAreader I need to wait until my friend is back after Christmas. With two devices I mean not the two engine Tilt devices but two different devices like speed and engine Tilt. The two enigine Tilt devices are working OK when I can configure them after they claimed address. They do not need to be configured as separate devices. So once they have been seen by the Garmin there is no problem.

dhopf commented 2 years ago

OK. I think I need to do some development for library to check that there is no restriction to have two tNMEA2000 objects. There should not, but I have not tested. Also I need to check that all messages, which will not be handled, will be passed to gateway handler.

I thought I'd raise this ancient discussion from the depths of the Github servers instead of creating a new issue since it exactly fits my use-case:

The scenario

I'm having the following simplified starting scenario: [KUS NS-5 fluid sensor (Diesel)] <-------> [NMEA2k bus] <------> [SIMRAD Plotter]

My issue is that due to a special construction where and how the fluid sensor is attached to the tank, it always reports with some offset (e.g. tank in reality has 15% remaining while reporting 24%).

So my approach is to create a Gateway which simply looks for PGN127505 messages from the fluid sensor side on a first CAN interface, corrects the reported fluid level and sends the message on to the actual bus (out on a second CAN interface). I slightly patched your NMEA- and teensyx-libs for that to support two devices.

The setup now looks like: [KUS NS-5 fluid sensor (Diesel)] <-------> [(CAN1) MY GATEWAY DEVICE (CAN2)] <-----> [NMEA2k bus] <------> [SIMRAD Plotter] BTW: I took care of the "second bus" I now opened towards the fluid sensor (termination, power supply) - I created a custom PCB/circuitry.

The issue

Due to lack in understanding NMEA2000, I first did NOT forward any other communication between the fluid sensor and the bus, I thought I just need to manipulate the actual PGN127505 messages. The SIMRAD then reported some unknown network device with fuel level information, but couldn't reliably list it nor detect it when the SIMRAD plotters were powered off and back on. From the discussion here I now get the understanding that the Fluid sensor likely sends ISO address claims onto the bus and the SIMRAD plotters somehow either acknowledge those (or they actually don't send any reply to this, but use the information from the ISO address claim to list the fluid sensor correctly?)

The solution?

So, I'm now figuring that I need to set my Gateway device into a mode where (in addition to manipulating the PGN127505 messages) I simply forward all other messages from the fluid sensor onto the bus and - if not even more important - I have to forward messages from the bus back to the fluid sensor (maybe I could find out which ones specifically, but I can simply gateway everything I guess). For this, I now set both of my tNMEA2000_Teensyx objects to tNMEA2000::N2km_ListenAndSend and added code to forward all messages besides the one I'm manipulating.

@ttlappalainen: I would really appreciate if you could confirm or correct my understanding/approach. Also: Did you ever continue investigation in supporting instantiating multiple tNMEA2000 objects and maybe adding example code for a simple "Gateway" functionality?

Thanks & BR Daniel

ttlappalainen commented 2 years ago

First: There is no limitation to have two instances of tNMEA2000.

Second: In principle in N2km_ListenAndSend mode it may work. You should relay all messages from Simrad side to sensor side and opposite and then modify only tank level message. Instead of using #include you do:

#include <N2kMsg.h>
#include <NMEA2000.h>
#include <NMEA2000_teexnsyx.h>
#include <N2kMessages.h>
...
tNMEA2000 &NMEA2000_sensor=*(new tNMEA2000_Teensyx(tNMEA2000_Teensyx::CAN1));
tNMEA2000 &NMEA2000_bus=*(new tNMEA2000_Teensyx(tNMEA2000_Teensyx::CAN2));
...

void SensorMsgHandler(const tN2kMsg &N2kMsg);
void BusMsgHandler(const tN2kMsg &N2kMsg);

// *****************************************************************************
void setup() {
  ...
  NMEA2000_sensor.SetN2kCANMsgBufSize(10); // Should be enough
  NMEA2000_bus.SetN2kCANMsgBufSize(10);
  NMEA2000_sensor.SetN2kCANReceiveFrameBufSize(100); // probably not even necessary. I think teensyx driver had big buffers as default
  NMEA2000_bus.SetN2kCANReceiveFrameBufSize(100);
  NMEA2000_sensor.SetMode(N2km_ListenAndSend,255);
  NMEA2000_bus.SetMode(N2km_ListenAndSend,255);
  NMEA2000_sensor.SetMsgHandler(SensorMsgHandler);
  NMEA2000_bus.SetMsgHandler(BusMsgHandler);
...
}

// *****************************************************************************
void loop() {
  NMEA2000_sensor.ParseMessages();
  NMEA2000_bus.ParseMessages();
}

// *****************************************************************************
double FixFluidLevel(double FluidLevel) {
  // do something with it
  return FluidLevel;
}

// *****************************************************************************
void SensorMsgHandler(const tN2kMsg &N2kMsg) {
  double FluidLevel,Capacity;
  unsigned char Instance;
  tN2kFluidType FluidType;

  if ( ParseN2kFluidLevel(N2kMsg,Instance,FluidType,FluidLevel, Capacity) ) {
    tN2kMsg  N2kMsgFwd(N2kMsg.Source);
    N2kMsgFwd.Destination=N2kMsg.Destination;
    SetN2kFluidLevel(N2kMsgFwd,Instance,FluidType,FixFluidLevel(FluidLevel), Capacity);
    NMEA2000_bus.SendMsg(N2kMsgFwd,-1); 
  } else {
    NMEA2000_bus.SendMsg(N2kMsg,-1);
  }
}

// *****************************************************************************
void BusMsgHandler(const tN2kMsg &N2kMsg) {
  NMEA2000_sensor.SendMsg(N2kMsg,-1);
}

Have not tested above code.

It should also work with single bus.

Solution 1. Just do normal device (like on example BatteryMonitor) with your own product and device information. This should show up on Simrad with your device information. Manufacturer will be unknown, if you do not use some predefined MfgCode. Then catch fluid level from your sensor, modify it and send it as your own device fluid message.

Solution 2. Spy your sensor product and device information. Increment unique ID with one and use those information for your device. Then do the same as above. Now your own device should be visible on SIMRAD as second fluid sensor and same manufacture as other.

Actually above solutions are bit more complex, since you have to follow your fluid sensor source address so that you handle right sensor. For that you need to know your sensor NAME, which is 8 byte data provided by ISO address claim message. So you need to catch also ISO address claim message (60928) and if its data matches to your sensor NAME, you take source address and listen 127505 messages from that source. Library N2kDeviceList module does that for all devices and you can request source by name.

dhopf commented 2 years ago

Wow, that's perfect support @ttlappalainen! Thank you! I expected a sentence but not complete example code (which I also wrote myself, but it differed from your suggestion in some places, so I made sure to use your approach).

Unfortunately I will not be able to get on said boat before May timeframe.

One more question though: When testing with OpenSkipper I noticed that it already showed my fluid/tank level when I simply sent out my own (or manipulated) messages. I didn't have to do ISO address claim etc.

Is there a possibility in OpenSkipper (or another [free] simulation software) to mimic behaviour like SIMRAD or other plotters where I can list further details of the device (MfgCode, ID, NAME ...)?

ttlappalainen commented 2 years ago

OpenSkipper does not care of devices and so shows device data without knowing anything about devices..

If library device is in node mode, it does address claiming automatically and you do not need to care of that. If you do it with my gateway way, address claiming will be done by devices automatically since all messages will be transfered to both sides.

My NMEA Simulator simulates devices and you can analyze bus messages. Unfortunately free version parses only first 100 messages and message console has been limited to 5000 messages. You can find it from http://www.kave.fi/Apps/