ttlappalainen / NMEA2000

NMEA2000 library for Arduino
542 stars 229 forks source link

question: is it possible to only get data from one GNSS device on an N2K network? #426

Open caspartfg opened 2 months ago

caspartfg commented 2 months ago

Sorry if this is a dumb question.

First, the library is awesome has been absolutely brilliant for me building applications on our boat. -THAKNK YOU-
There is one minor problem I come up against and I've not been able to find the solution.

We have multiple GPS devices on our N2K network that write to the GNSS PGN (AIS, 2 x Chart Plotters, Starlink). Their antenna are in different places on the boat, (up to 7 meters away from each other). This means I can see my fix jumping around.

My question, how would I filter PGN responses by device ID so I can use just the one source? I'm using this function from the DataDisplay example:

(ParseN2kGNSS(N2kMsg,SID,DaysSince1970,SecondsSinceMidnight, lat,lon,Altitude, GNSStype,GNSSmethod, nSatellites,HDOP,PDOP,GeoidalSeparation, nReferenceStations,ReferenceStationType,ReferenceSationID, AgeOfCorrection) )

I couldn't see how to link this back to a device name or other UUID? I've looked for other functions too, but couldn't see the option there either.

FYI - in SignalK I can see the data split by device, so i'm assuming there is something in there somewhere I can use, so I suspect it's user error on my part.

Thanks so much in advance

Casp.

ttlappalainen commented 2 months ago

You need to use tN2kDeviceList. It tracks devices on the bus so that you can request source address with NAME, id:s or product from it. Normally source is static soon after start and stays same, if you do not add new devices. To check if there has been changes for sources, you can use tN2kDeviceList::ReadResetIsListUpdated. If that returns true, you need to check your interest device source from tN2kDeviceList. There is example DeviceAnalyzer how to use tN2kDeviceList.

caspartfg commented 2 months ago

Thank you so much - I really appreciate it and will take a look now!...

caspartfg commented 2 months ago

Timo, sorry, I know this isn't user support, but I can't figure out how I get from using the output of tN2kDeviceList through to applying some sort of NAME filter when getting my PGNs: NMEA2000.SetMsgHandler(HandleNMEA2000Msg). Again, apologies, i'm not a particularly strong developer. I've read through the documentation, but if you could point me in the right direction as I may have missed something. Again, thank you so much for your help and advice. I'll keep hacking away at this... Casp.

ttlappalainen commented 2 months ago

It is easy - at least for programmer who has designed the code.

tN2kDeviceList collects automatically list of all devices on the bus. Since it uses requests to get device information, it does it fast - you have full list of devices in few seconds after device has started.

// We need some mode variables
tN2kDeviceList *pN2kDeviceList;
uint8_t GPSSource=254;  // use at beginning null address
uint16_t MyGPSManufacturerCode=229; // You need to find your GPS manufacturer code. Here Garmin code.
uint16_t MyGPSUniqueID=7777; // You need to find your GPS unique ID e.g., by using mu NMEA Simulator

void HandleNMEA2000Msg(const tN2kMsg &N2kMsg);

// Setup as  normally + add device list
void setup() {
...
  NMEA2000.SetMsgHandler(HandleNMEA2000Msg);
  pN2kDeviceList = new tN2kDeviceList(&NMEA2000);
...
}

void HandleDevicesChanges() {
  if ( pN2kDeviceList->ReadResetIsListUpdated() ) {
    // Search source with manufacturer code and unique number. If we are strict this  does not define unique
    // device since same manufacturer may have both GPS and e.g., termperature sensor with same MyGPSUniqueID.
    // In real world it would be winning the lottery to have that case in single boat.
    // The strict search would be to usedevice NMEA2000 NAME and FindDeviceByName
    // see https://ttlappalainen.github.io/NMEA2000/md_7_glossary.html
    const tNMEA2000::tDevice *GPS=pN2kDeviceList->FindDeviceByIDs(MyGPSManufacturerCode,MyGPSUniqueID);
    if ( GPS!=0 ) {
      GPSSource=GPS->GetSource()
    } else {
      GPSSource=254; // We use null address in case device does not exist any more.
    }
  }
}

void HandleGNSS(const tN2kMsg &N2kMsg) {
  if ( N2kMsg.Source==GPSSource ) {
    // Do what you like with filtered message.
    ...
  }
}

void HandleNMEA2000Msg(const tN2kMsg &N2kMsg) {
  // You can handle message as you like. See also DataDisplay2.ino method
  switch ( N2kMsg.PGN ) {
    case 129029L: //GNSS
      HandleGNSS(N2kMsg);
      break;  
  }
}

void loop() {
  NMEA2000.ParseMessages();
  HandleDevicesChanges();
  ...
}

I remind again that you must keep you loop and handlers fast and non blocking to avoid problems with loss off messages or delayed responces see https://ttlappalainen.github.io/NMEA2000/pg_lib_ref.html#descRunningLibrary.

Code above does not handle any spare GPS. For that you have to add variables for all GPS, currentGPS and also save time for received GPS messages. Then if currentGPS has not received message within timeout, you switch currentGPS to next in priority which messagesa has been received within timeout.

caspartfg commented 2 months ago

that's awesome - it's working perfectly - thank you so much for your help!!!

On Wed, 4 Sept 2024 at 23:47, Timo Lappalainen @.***> wrote:

It is easy - at least for programmer who has designed the code.

tN2kDeviceList collects automatically list of all devices on the bus. Since it uses requests to get device information, it does it fast - you have full list of devices in few seconds after device has started.

// We need some mode variables tN2kDeviceList *pN2kDeviceList; uint8_t GPSSource=254; // use at beginning null address uint16_t MyGPSManufacturerCode=229; // You need to find your GPS manufacturer code. Here Garmin code. uint16_t MyGPSUniqueID=7777; // You need to find your GPS unique ID e.g., by using mu NMEA Simulator

void HandleNMEA2000Msg(const tN2kMsg &N2kMsg);

// Setup as normally + add device list void setup() { ... NMEA2000.SetMsgHandler(HandleNMEA2000Msg); pN2kDeviceList = new tN2kDeviceList(&NMEA2000); ... }

void HandleDevicesChanges() { if ( pN2kDeviceList->ReadResetIsListUpdated() ) { // Search source with manufacturer code and unique number. If we are strict this does not define unique // device since same manufacturer may have both GPS and e.g., termperature sensor with same MyGPSUniqueID. // In real world it would be winning the lottery to have that case in single boat. // The strict search would be to usedevice NMEA2000 NAME and FindDeviceByName // see https://ttlappalainen.github.io/NMEA2000/md_7_glossary.html const tNMEA2000::tDevice *GPS=pN2kDeviceList->FindDeviceByIDs(MyGPSManufacturerCode,MyGPSUniqueID); if ( GPS!=0 ) { GPSSource=GPS->GetSource() } else { GPSSource=254; // We use null address in case device does not exist any more. } } }

void HandleGNSS(const tN2kMsg &N2kMsg) { if ( N2kMsg.Source==GPSSource ) { // Do what you like with filtered message. ... } }

void HandleNMEA2000Msg(const tN2kMsg &N2kMsg) { // You can handle message as you like. See also DataDisplay2.ino method switch ( N2kMsg.PGN ) { case 129029L: //GNSS HandleGNSS(N2kMsg); break; } }

void loop() { NMEA2000.ParseMessages(); HandleDevicesChanges(); ... }

I remind again that you must keep you loop and handlers fast and non blocking to avoid problems with loss off messages or delayed responces see https://ttlappalainen.github.io/NMEA2000/pg_lib_ref.html#descRunningLibrary .

Code above does not handle any spare GPS. For that you have to add variables for all GPS, currentGPS and also save time for received GPS messages. Then if currentGPS has not received message within timeout, you switch currentGPS to next in priority which messagesa has been received within timeout.

— Reply to this email directly, view it on GitHub https://github.com/ttlappalainen/NMEA2000/issues/426#issuecomment-2330538245, or unsubscribe https://github.com/notifications/unsubscribe-auth/AD534ASFCFUDPXW4T6M2XKLZU7H4NAVCNFSM6AAAAABNUW3EN6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDGMZQGUZTQMRUGU . You are receiving this because you authored the thread.Message ID: @.***>