NMEA2000 Library  0.1
Library to handle NMEA 2000 Communication written in C++
NMEA2000.cpp
Go to the documentation of this file.
1/*
2NMEA2000.cpp
3
4Copyright (c) 2015-2024 Timo Lappalainen, Kave Oy, www.kave.fi
5
6Permission is hereby granted, free of charge, to any person obtaining a copy of
7this software and associated documentation files (the "Software"), to deal in
8the Software without restriction, including without limitation the rights to use,
9copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
10Software, and to permit persons to whom the Software is furnished to do so,
11subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
17INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
18PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
20CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
21OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22*/
23
24#include "NMEA2000.h"
25#include "N2kDef.h"
26#if !defined(N2K_NO_GROUP_FUNCTION_SUPPORT)
28#endif
29#include <string.h>
30#include <stdlib.h>
31
32#define DebugStream Serial
33
34// #define NMEA2000_FRAME_ERROR_DEBUG
35// #define NMEA2000_FRAME_IN_DEBUG
36// #define NMEA2000_FRAME_OUT_DEBUG
37// #define NMEA2000_MSG_DEBUG
38// #define NMEA2000_BUF_DEBUG
39// #define NMEA2000_DEBUG
40
41#if defined(NMEA2000_FRAME_ERROR_DEBUG)
42# define N2kFrameErrDbgStart(fmt, args...) DebugStream.print(N2kMillis()); DebugStream.print(": "); DebugStream.print (fmt , ## args)
43# define N2kFrameErrDbg(fmt, args...) DebugStream.print (fmt , ## args)
44# define N2kFrameErrDbgln(fmt, args...) DebugStream.println (fmt , ## args)
45#else
46# define N2kFrameErrDbgStart(fmt, args...)
47# define N2kFrameErrDbg(fmt, args...)
48# define N2kFrameErrDbgln(fmt, args...)
49#endif
50
51#if defined(NMEA2000_FRAME_IN_DEBUG)
52# define N2kFrameInDbgStart(fmt, args...) DebugStream.print(N2kMillis()); DebugStream.print(": "); DebugStream.print (fmt , ## args)
53# define N2kFrameInDbg(fmt, args...) DebugStream.print (fmt , ## args)
54# define N2kFrameInDbgln(fmt, args...) DebugStream.println (fmt , ## args)
55#else
56# define N2kFrameInDbgStart(fmt, args...)
57# define N2kFrameInDbg(fmt, args...)
58# define N2kFrameInDbgln(fmt, args...)
59#endif
60
61#if defined(NMEA2000_FRAME_OUT_DEBUG)
62# define N2kFrameOutDbgStart(fmt, args...) DebugStream.print(N2kMillis()); DebugStream.print(": "); DebugStream.print (fmt , ## args)
63# define N2kFrameOutDbg(fmt, args...) DebugStream.print (fmt , ## args)
64# define N2kFrameOutDbgln(fmt, args...) DebugStream.println (fmt , ## args)
65#else
66# define N2kFrameOutDbgStart(fmt, args...)
67# define N2kFrameOutDbg(fmt, args...)
68# define N2kFrameOutDbgln(fmt, args...)
69#endif
70
71#if defined(NMEA2000_MSG_DEBUG)
72# define N2kMsgDbgStart(fmt, args...) DebugStream.print(N2kMillis()); DebugStream.print(": "); DebugStream.print (fmt , ## args)
73# define N2kMsgDbg(fmt, args...) DebugStream.print (fmt , ## args)
74# define N2kMsgDbgln(fmt, args...) DebugStream.println (fmt , ## args)
75#else
76# define N2kMsgDbgStart(fmt, args...)
77# define N2kMsgDbg(fmt, args...)
78# define N2kMsgDbgln(fmt, args...)
79#endif
80
81#if defined(NMEA2000_BUF_DEBUG)
82# define DbgPrintBuf(len, buf, addln) PrintBuf(&DebugStream, len, buf, addln)
83#else
84# define DbgPrintBuf(len, buf, addln)
85#endif
86
87#if defined(NMEA2000_DEBUG)
88# define N2kDbg(fmt, args...) DebugStream.print (fmt , ## args)
89# define N2kDbgln(fmt, args...) DebugStream.println (fmt , ## args)
90#else
91# define N2kDbg(fmt, args...)
92# define N2kDbgln(fmt, args...)
93#endif
94
95// #define NMEA2000_MEMORY_TEST 1
96
97#if defined(NMEA2000_MEMORY_TEST)
98#include <MemoryFree.h>
99
100void N2kPrintFreeMemory(const char *Source) {
101 Serial.print(Source);
102 Serial.print(", free memory=");
103 Serial.println(freeMemory());
104}
105#else
106#define N2kPrintFreeMemory(a)
107#endif
108
110#define N2kAddressClaimTimeout 250
112#define MaxHeartbeatInterval 655320UL
113
115#define TP_MAX_FRAMES 5
117#define TP_CM 60416L
119#define TP_DT 60160L
121#define TP_CM_BAM 32
123#define TP_CM_RTS 16
125#define TP_CM_CTS 17
127#define TP_CM_ACK 19
129#define TP_CM_Abort 255
130
133#define TP_CM_AbortBusy 1
136#define TP_CM_AbortNoResources 2
139#define TP_CM_AbortTimeout 3
140
141/************************************************************************/
160const unsigned long DefTransmitMessages[] PROGMEM = {
161 59392L, /* ISO Acknowledgement, pri=6, period=NA */
162 59904L, /* ISO Request, pri=6, period=NA */
163#if !defined(N2K_NO_ISO_MULTI_PACKET_SUPPORT)
164 TP_DT, /* Multi packet data transfer, TP.DT, pri=6, period=NA */
165 TP_CM, /* Multi packet connection management, TP.CM, pri=6, period=NA */
166#endif
167 60928L, /* ISO Address Claim, pri=6, period=NA */
168#if !defined(N2K_NO_GROUP_FUNCTION_SUPPORT)
169 126208L, /* NMEA Request/Command/Acknowledge group function, pri=3, period=NA */
170#endif
171 126464L, /* PGN List (Transmit and Receive), pri=6, period=NA */
172#if !defined(N2K_NO_HEARTBEAT_SUPPORT)
173 126993L, /* Heartbeat, pri=7, period=60000 */
174#endif
175 126996L, /* Product information, pri=6, period=NA */
176 126998L, /* Configuration information, pri=6, period=NA */
177 0};
178
179/************************************************************************/
194const unsigned long DefReceiveMessages[] PROGMEM = {
195 59392L, /* ISO Acknowledgement, pri=6, period=NA */
196 59904L, /* ISO Request, pri=6, period=NA */
197#if !defined(N2K_NO_ISO_MULTI_PACKET_SUPPORT)
198 TP_DT, /* Multi packet data transfer, TP.DT */
199 TP_CM, /* Multi packet connection management, TP.CM */
200#endif
201 60928L, /* ISO Address Claim */
202 65240L, /* Commanded Address */
203#if !defined(N2K_NO_GROUP_FUNCTION_SUPPORT)
204 126208L, /* NMEA Request/Command/Acknowledge group function */
205#endif
206 0};
207
208/************************************************************************/
215bool IsSingleFrameSystemMessage(unsigned long PGN) {
216 switch (PGN) {
217 case 59392L: /* ISO Acknowledgement */
218 case TP_DT: /* Multi packet data transfer, TP.DT */
219 case TP_CM: /* Multi packet connection management, TP.CM */
220 case 59904L: /* ISO Request */
221 case 60928L: /* ISO Address Claim */
222 return true;
223 }
224 return false;
225}
226
227/************************************************************************/
234bool IsFastPacketSystemMessage(unsigned long PGN) {
235 switch (PGN) {
236 case 65240L: /* Commanded Address*/
237 case 126208L: /* NMEA Request/Command/Acknowledge group function */
238 return true;
239 }
240 return false;
241}
242
243/************************************************************************/
276bool IsDefaultSingleFrameMessage(unsigned long PGN) {
277 switch (PGN) {
278 case 126992L: // System date/time, pri=3, period=1000
279 case 126993L: // Heartbeat, pri=7, period=60000
280 case 127245L: // Rudder, pri=2, period=100
281 case 127250L: // Vessel Heading, pri=2, period=100
282 case 127251L: // Rate of Turn, pri=2, period=100
283 case 127257L: // Attitude, pri=3, period=1000
284 case 127488L: // Engine parameters rapid, rapid Update, pri=2, period=100
285 case 127493L: // Transmission parameters: dynamic, pri=2, period=100
286 case 127501L: // Binary status report, pri=3, period=NA
287 case 127505L: // Fluid level, pri=6, period=2500
288 case 127508L: // Battery Status, pri=6, period=1500
289 case 128259L: // Boat speed, pri=2, period=1000
290 case 128267L: // Water depth, pri=3, period=1000
291 case 129025L: // Lat/lon rapid, pri=2, period=100
292 case 129026L: // COG SOG rapid, pri=2, period=250
293 case 129283L: // Cross Track Error, pri=3, period=1000
294 case 130306L: // Wind Speed, pri=2, period=100
295 case 130310L: // Outside Environmental parameters, pri=5, period=500
296 case 130311L: // Environmental parameters, pri=5, period=500
297 case 130312L: // Temperature, pri=5, period=2000
298 case 130313L: // Humidity, pri=5, period=2000
299 case 130314L: // Pressure, pri=5, period=2000
300 case 130316L: // Temperature extended range, pri=5, period=2000
301 case 130576L: // Small Craft Status (Trim Tab position), pri=2, period=200
302 return true;
303 }
304 return false;
305}
306
307/************************************************************************/
318bool IsMandatoryFastPacketMessage(unsigned long PGN) {
319 switch (PGN) {
320 case 126464L: // PGN List (Transmit and Receive), pri=6, period=NA
321 case 126996L: // Product information, pri=6, period=NA
322 case 126998L: // Configuration information, pri=6, period=NA
323 return true;
324 }
325 return false;
326}
327
328/************************************************************************/
448bool IsDefaultFastPacketMessage(unsigned long PGN) {
449 switch (PGN) {
450 case 126983L: // Alert, pri=2, period=1000
451 case 126984L: // Alert Response, pri=2, period=NA
452 case 126985L: // Alert Text, pri=2, period=10000
453 case 126986L: // Alert Configuration, pri=2, period=NA
454 case 126987L: // Alert Threshold, pri=2, period=NA
455 case 126988L: // Alert Value, pri=2, period=10000
456 case 127233L: // Man Overboard Notification(MOB), pri=3, period=NA
457 case 127237L: // Heading/Track control, pri=2, period=250
458 case 127489L: // Engine parameters dynamic, pri=2, period=500
459 case 127490L: // Electric Drive Status (Dynamic), pri=1, period=1500
460 case 127491L: // Electric Energy Storage Status (Dynamic), pri=7, period=1500
461 case 127494L: // Electric Drive Information, pri=4, period=NA
462 case 127495L: // Electric Energy Storage Information, pri=6, period=NA
463 case 127496L: // Trip fuel consumption, vessel, pri=5, period=1000
464 case 127497L: // Trip fuel consumption, engine, pri=5, period=1000
465 case 127498L: // Engine parameters static, pri=5, period=NA
466 case 127503L: // AC Input Status, pri=6, period=1500
467 case 127504L: // AC Output Status, pri=6, period=1500
468 case 127506L: // DC Detailed status, pri=6, period=1500
469 case 127507L: // Charger status, pri=6, period=1500
470 case 127509L: // Inverter status, pri=6, period=1500
471 case 127510L: // Charger configuration status, pri=6, period=NA
472 case 127511L: // Inverter Configuration Status, pri=6, period=NA
473 case 127512L: // AGS configuration status, pri=6, period=NA
474 case 127513L: // Battery configuration status, pri=6, period=NA
475 case 127514L: // AGS Status, pri=6, period=1500
476 case 128275L: // Distance log, pri=6, period=1000
477 case 128520L: // Tracked Target Data, pri=2, period=1000
478 case 128538L: // Elevator car status, pri=6, period=100
479 case 129029L: // GNSS Position Data, pri=3, period=1000
480 case 129038L: // AIS Class A Position Report, pri=4, period=NA
481 case 129039L: // AIS Class B Position Report, pri=4, period=NA
482 case 129040L: // AIS Class B Extended Position Report, pri=4, period=NA
483 case 129041L: // AIS Aids to Navigation (AtoN) Report, pri=4, period=NA
484 case 129044L: // Datum, pri=6, period=10000
485 case 129045L: // User Datum Settings, pri=6, period=NA
486 case 129284L: // Navigation info, pri=3, period=1000
487 case 129285L: // Waypoint list, pri=3, period=NA
488 case 129301L: // Time to/from Mark, pri=3, period=1000
489 case 129302L: // Bearing and Distance between two Marks, pri=6, period=NA
490 case 129538L: // GNSS Control Status, pri=6, period=NA
491 case 129540L: // GNSS Sats in View, pri=6, period=1000
492 case 129541L: // GPS Almanac Data, pri=6, period=NA
493 case 129542L: // GNSS Pseudorange Noise Statistics, pri=6, period=1000
494 case 129545L: // GNSS RAIM Output, pri=6, period=NA
495 case 129547L: // GNSS Pseudorange Error Statistics, pri=6, period=NA
496 case 129549L: // DGNSS Corrections, pri=6, period=NA
497 case 129551L: // GNSS Differential Correction Receiver Signal, pri=6, period=NA
498 case 129556L: // GLONASS Almanac Data, pri=6, period=NA
499 case 129792L: // AIS DGNSS Broadcast Binary Message, pri=6, period=NA
500 case 129793L: // AIS UTC and Date Report, pri=7, period=NA
501 case 129794L: // AIS Class A Static data, pri=6, period=NA
502 case 129795L: // AIS Addressed Binary Message, pri=5, period=NA
503 case 129796L: // AIS Acknowledge, pri=7, period=NA
504 case 129797L: // AIS Binary Broadcast Message, pri=5, period=NA
505 case 129798L: // AIS SAR Aircraft Position Report, pri=4, period=NA
506 case 129799L: // Radio Frequency/Mode/Power, pri=3, period=NA
507 case 129800L: // AIS UTC/Date Inquiry, pri=7, period=NA
508 case 129801L: // AIS Addressed Safety Related Message, pri=5, period=NA
509 case 129802L: // AIS Safety Related Broadcast Message, pri=5, period=NA
510 case 129803L: // AIS Interrogation PGN, pri=7, period=NA
511 case 129804L: // AIS Assignment Mode Command, pri=7, period=NA
512 case 129805L: // AIS Data Link Management Message, pri=7, period=NA
513 case 129806L: // AIS Channel Management, pri=7, period=NA
514 case 129807L: // AIS Group Assignment, pri=7, period=NA
515 case 129808L: // DSC Call Information, pri=8, period=NA
516 case 129809L: // AIS Class B Static Data: Part A, pri=6, period=NA
517 case 129810L: // AIS Class B Static Data Part B, pri=6, period=NA
518 case 129811L: // AIS Single Slot Binary Message, pri=5, period=NA
519 case 129812L: // AIS Multi Slot Binary Message, pri=5, period=NA
520 case 129813L: // AIS Long-Range Broadcast Message, pri=5, period=NA
521 case 130052L: // Loran-C TD Data, pri=3, period=1000
522 case 130053L: // Loran-C Range Data, pri=3, period=1000
523 case 130054L: // Loran-C Signal Data, pri=3, period=1000
524 case 130060L: // Label, pri=7, period=NA
525 case 130061L: // Channel Source Configuration, pri=7, period=NA
526 case 130064L: // Route and WP Service - Database List, pri=7, period=NA
527 case 130065L: // Route and WP Service - Route List, pri=7, period=NA
528 case 130066L: // Route and WP Service - Route/WP-List Attributes, pri=7, period=NA
529 case 130067L: // Route and WP Service - Route - WP Name & Position, pri=7, period=NA
530 case 130068L: // Route and WP Service - Route - WP Name, pri=7, period=NA
531 case 130069L: // Route and WP Service - XTE Limit & Navigation Method, pri=7, period=NA
532 case 130070L: // Route and WP Service - WP Comment, pri=7, period=NA
533 case 130071L: // Route and WP Service - Route Comment, pri=7, period=NA
534 case 130072L: // Route and WP Service - Database Comment, pri=7, period=NA
535 case 130073L: // Route and WP Service - Radius of Turn, pri=7, period=NA
536 case 130074L: // Route and WP Service - WP List - WP Name & Position, pri=7, period=NA
537 case 130320L: // Tide Station Data, pri=6, period=1000
538 case 130321L: // Salinity Station Data, pri=6, period=1000
539 case 130322L: // Current Station Data, pri=6, period=1000
540 case 130323L: // Meteorological Station Data, pri=6, period=1000
541 case 130324L: // Moored Buoy Station Data, pri=6, period=1000
542 case 130330L: // Lighting system settings, pri=7, period=NA
543 case 130561L: // Lighting zone, pri=7, period=NA
544 case 130562L: // Lighting scene, pri=7, period=NA
545 case 130563L: // Lighting device, pri=7, period=NA
546 case 130564L: // Lighting device enumeration, pri=7, period=NA
547 case 130565L: // Lighting color sequence, pri=7, period=NA
548 case 130566L: // Lighting program, pri=7, period=NA
549 case 130567L: // Watermaker Input Setting and Status, pri=6, period=2500
550 case 130577L: // Direction Data PGN, pri=3, period=1000
551 case 130578L: // Vessel Speed Components, pri=2, period=250
552 // Entertainment PGNs
553 case 130569L: // Current File and Status, pri=6, period=500
554 case 130570L: // Library Data File, pri=6, period=NA
555 case 130571L: // Library Data Group, pri=6, period=NA
556 case 130572L: // Library Data Search, pri=6, period=NA
557 case 130573L: // Supported Source Data, pri=6, period=NA
558 case 130574L: // Supported Zone Data, pri=6, period=NA
559 case 130580L: // System Configuration Status, pri=6, period=NA
560 case 130581L: // Zone Configuration Status, pri=6, period=NA
561 case 130583L: // Available Audio EQ Presets, pri=6, period=NA
562 case 130584L: // Bluetooth Devices, pri=6, period=NA
563 case 130586L: // Zone Configuration Status, pri=6, period=NA
564 return true;
565 }
566 return false;
567}
568
569/************************************************************************/
579bool IsProprietaryFastPacketMessage(unsigned long PGN) {
580 return ( PGN==126720L ) || ( 130816L<=PGN && PGN<=131071L );
581}
582
583bool tNMEA2000::IsProprietaryMessage(unsigned long PGN) {
584 return IsProprietaryFastPacketMessage(PGN) || ( PGN==61184L ) || ( 65280L<=PGN && PGN<=65535L );
585}
586
587/************************************************************************/
603 2101, // N2kVersion
604 666, // ProductCode
605 "Arduino N2k->PC", //N2kModelID
606 "1.0.0.0", //N2kSwCode
607 "1.0.0", // N2kModelVersion
608 "00000001", // N2kModelSerialCode
609 0, // CertificationLevel
610 1 // LoadEquivalency
611 };
612
613/************************************************************************/
616const char DefManufacturerInformation [] PROGMEM = "NMEA2000 library, https://github.com/ttlappalainen/NMEA2000";
617/************************************************************************/
621/************************************************************************/
625
626//*****************************************************************************
628 memset(this,0,sizeof(tProductInformation));
629}
630
631//*****************************************************************************
633 return memcmp(this,&Other,sizeof(tProductInformation))==0;
634}
635
636//*****************************************************************************
637void tNMEA2000::ClearCharBuf(size_t MaxLen, char *buf) {
638 if ( buf==0 ) return;
639 size_t i=0;
640 for (; i<MaxLen; i++) buf[i]=0;
641}
642
643//*****************************************************************************
644void tNMEA2000::SetCharBuf(const char *str, size_t MaxLen, char *buf) {
645 if ( buf==0 || MaxLen==0 ) return; // nothing to do for 0 buffer
646 if ( str==0 ) { buf[0]=0; return; }
647 size_t i=0;
648 for (; i<MaxLen-1 && str[i]!=0; i++) buf[i]=str[i];
649 for (; i<MaxLen; i++) buf[i]=0;
650 buf[MaxLen-1]=0; // Force null termination
651}
652
653//*****************************************************************************
654void tNMEA2000::ClearSetCharBuf(const char *str, size_t MaxLen, char *buf) {
655 ClearCharBuf(MaxLen,buf);
656 if (str) SetCharBuf(str,MaxLen,buf);
657}
658
659//*****************************************************************************
661
662#if !defined(N2K_NO_GROUP_FUNCTION_SUPPORT)
664#endif
665#if !defined(N2K_NO_GROUP_FUNCTION_SUPPORT)
667#endif
669
670 for (int i=0; i<N2kMessageGroups; i++) {SingleFrameMessages[i]=0; FastPacketMessages[i]=0;}
671
672 N2kCANMsgBuf=0;
674
676 MaxCANReceiveFrames=0; // Use driver default
678
679 OnOpen=0;
680 MsgHandler=0;
681 MsgHandlers=0;
683
686 AddressChanged=false;
691 ForwardMode=0;
699 Devices=0;
700 DeviceCount=1;
701}
702
703//*****************************************************************************
704void tNMEA2000::SetDeviceCount(const uint8_t _DeviceCount) {
705 // Note that we can set this only before any initialization. Limit count to 10.
706 if ( Devices==0 && _DeviceCount>=1 && _DeviceCount<10 ) DeviceCount=_DeviceCount;
707}
708
709//*****************************************************************************
711 if ( Devices==0 ) {
712 N2kDbgln("Init devices");
714 MaxCANSendFrames*=DeviceCount; // We need bigger buffer for sending all information
715// for (int i=0; i<DeviceCount; i++) Devices[i].tDevice();
716 // We set default device information here.
719 for ( int i=0; i<DeviceCount; i++) { // Initialize all devices with some value
720 SetDeviceInformation(1+i, // 21 bit resolution, max 2097151. Each device from same manufacturer should have unique number.
721 130, // PC Gateway. See codes on https://web.archive.org/web/20190531120557/https://www.nmea.org/Assets/20120726%20nmea%202000%20class%20&%20function%20codes%20v%202.00.pdf
722 25, // Inter/Intranetwork Device. See codes on https://web.archive.org/web/20190531120557/https://www.nmea.org/Assets/20120726%20nmea%202000%20class%20&%20function%20codes%20v%202.00.pdf
723 2046, // Maximum 2046. See the list of codes on https://web.archive.org/web/20190529161431/http://www.nmea.org/Assets/20121020%20nmea%202000%20registration%20list.pdf
724 4, // Marine
725 i
726 );
727 if ( i>0 ) {
730 }
731 }
732 }
733}
734
735//*****************************************************************************
736void tNMEA2000::SetProductInformation(const tProductInformation *_ProductInformation, int iDev) {
737 if ( !IsValidDevice(iDev) ) return;
738 InitDevices();
739 Devices[iDev].ProductInformation=_ProductInformation;
740 if (Devices[iDev].ProductInformation==0) Devices[iDev].ProductInformation=Devices[iDev].LocalProductInformation;
741 if (Devices[iDev].ProductInformation==0) Devices[iDev].ProductInformation=&DefProductInformation;
742}
743
744//*****************************************************************************
745void tNMEA2000::SetProductInformation(const char *_ModelSerialCode,
746 unsigned short _ProductCode,
747 const char *_ModelID,
748 const char *_SwCode,
749 const char *_ModelVersion,
750 unsigned char _LoadEquivalency,
751 unsigned short _N2kVersion,
752 unsigned char _CertificationLevel,
753 int iDev) {
754 if ( !IsValidDevice(iDev) ) return;
755 InitDevices();
756 if (Devices[iDev].LocalProductInformation==0) {
758 }
760 Devices[iDev].LocalProductInformation->Set(_ModelSerialCode,_ProductCode,_ModelID,_SwCode,_ModelVersion,_LoadEquivalency,_N2kVersion,_CertificationLevel);
761}
762
763//*****************************************************************************
764void tNMEA2000::SetConfigurationInformation(const char *ManufacturerInformation,
765 const char *InstallationDescription1,
766 const char *InstallationDescription2) {
767 if ( LocalConfigurationInformationData!=0 ) free(LocalConfigurationInformationData); // This happens on second call, which is not good.
769
770 size_t ManInfoLen=(ManufacturerInformation?strlen(ManufacturerInformation)+1:0);
771#if !defined(N2K_NO_GROUP_FUNCTION_SUPPORT)
772 size_t InstDesc1Len=Max_N2kConfigurationInfoField_len;
773 size_t InstDesc2Len=Max_N2kConfigurationInfoField_len;
774#else
775 size_t InstDesc1Len=(InstallationDescription1?strlen(InstallationDescription1)+1:0);
776 size_t InstDesc2Len=(InstallationDescription2?strlen(InstallationDescription2)+1:0);
777#endif
778
782
783 size_t TotalSize=ManInfoLen+InstDesc1Len+InstDesc2Len;
784 void *mem=(TotalSize>0?malloc(TotalSize):0);
785
788
789 SetCharBuf(InstallationDescription1,InstDesc1Len,Info);
790 ConfigurationInformation.InstallationDescription1=(InstallationDescription1?Info:0);
791 Info+=InstDesc1Len;
792
793 SetCharBuf(InstallationDescription2,InstDesc2Len,Info);
794 ConfigurationInformation.InstallationDescription2=(InstallationDescription2?Info:0);
795 Info+=InstDesc2Len;
796
797 SetCharBuf(ManufacturerInformation,ManInfoLen,Info);
798 ConfigurationInformation.ManufacturerInformation=(ManufacturerInformation?Info:0);
799}
800
801//*****************************************************************************
802void tNMEA2000::SetProgmemConfigurationInformation(const char *ManufacturerInformation,
803 const char *InstallationDescription1,
804 const char *InstallationDescription2) {
805 if ( LocalConfigurationInformationData!=0 ) free(LocalConfigurationInformationData); // This happens on second call, which is not good.
808 ConfigurationInformation.InstallationDescription1=InstallationDescription1;
809 ConfigurationInformation.InstallationDescription2=InstallationDescription2;
810}
811
812//*****************************************************************************
814 if ( !IsValidDevice(iDev) ) return 0;
815 unsigned long ListPGN;
816 size_t FPTxPGNCount=0;
817
818 for (int i=0; (ListPGN=pgm_read_dword(&DefTransmitMessages[i]))!=0; i++) {
819 if ( IsFastPacketPGN(ListPGN) ) FPTxPGNCount++;
820 }
821 if (Devices[iDev].TransmitMessages!=0) {
822 for (int i=0; (ListPGN=pgm_read_dword(&Devices[iDev].TransmitMessages[i]))!=0; i++) {
823 if ( IsFastPacketPGN(ListPGN) ) FPTxPGNCount++;
824 }
825 }
826
827 return FPTxPGNCount;
828}
829
830//*****************************************************************************
831int tNMEA2000::GetSequenceCounter(unsigned long PGN, int iDev) {
832 if ( !IsValidDevice(iDev) ) return 0;
833
834 if ( Devices[iDev].PGNSequenceCounters==0 ) { // Sequence counters has not yet been initialized
835 Devices[iDev].MaxPGNSequenceCounters=GetFastPacketTxPGNCount(iDev)+1; // Reserve 1 for undefined PGNs
836 Devices[iDev].PGNSequenceCounters=new unsigned long[Devices[iDev].MaxPGNSequenceCounters];
837 for ( size_t i=0; i<Devices[iDev].MaxPGNSequenceCounters; i++ ) Devices[iDev].PGNSequenceCounters[i]=0;
838 }
839 if ( Devices[iDev].PGNSequenceCounters==0 ) return 0; // Should not be. Only in case of memory allocation problem.
840 size_t last=Devices[iDev].MaxPGNSequenceCounters-1;
841 unsigned long sc;
842 for ( size_t i=0; i<last; i++ ) {
843 if ( Devices[iDev].PGNSequenceCounters[i]==0 ) { // Empty place, use this
844 Devices[iDev].PGNSequenceCounters[i]=PGN;
845 return 0; // Start from sequence 0
846 }
847 if ( (Devices[iDev].PGNSequenceCounters[i]&0x00ffffff) == PGN ) { // Found counter, use it
848 sc=Devices[iDev].PGNSequenceCounters[i]>>24;
849 sc++; if (sc>7) sc=0; // Get next counter
850 Devices[iDev].PGNSequenceCounters[i]=PGN | (sc << 24);
851 return sc;
852 }
853 }
854 // PGN counter not found, so use common
855 sc=Devices[iDev].PGNSequenceCounters[last];
856 sc++; if (sc>7) sc=0;
857 Devices[iDev].PGNSequenceCounters[last]=sc;
858 return sc;
859}
860
861#if !defined(N2K_NO_GROUP_FUNCTION_SUPPORT)
862
863//*****************************************************************************
864void CopyProgmemString(const char *str, size_t MaxLen, char *buf) {
865 if ( buf==0 || MaxLen==0 ) return; // nothing to do for 0 buffer
866 if ( str==0 ) { buf[0]=0; return; }
867 size_t i=0;
868 char c;
869 for (; i<MaxLen-1 && (c=pgm_read_byte(&(str[i])))!=0; i++) buf[i]=c;
870 for (; i<MaxLen; i++) buf[i]=0;
871 buf[MaxLen-1]=0; // Force null termination
872}
873
874//*****************************************************************************
875bool tNMEA2000::IsTxPGN(unsigned long PGN, int iDev) {
876 if ( !IsValidDevice(iDev) ) return false;
877 unsigned long ListPGN;
878
879 for (int i=0; (ListPGN=pgm_read_dword(&DefTransmitMessages[i]))!=0; i++) {
880 if ( ListPGN==PGN ) return true;
881 }
882 if (Devices[iDev].TransmitMessages!=0) {
883 for (int i=0; (ListPGN=pgm_read_dword(&Devices[iDev].TransmitMessages[i]))!=0; i++) {
884 if ( ListPGN==PGN ) return true;
885 }
886 }
887 return false;
888}
889
890//*****************************************************************************
891const tNMEA2000::tProductInformation * tNMEA2000::GetProductInformation(int iDev, bool &IsProgMem) const {
892 if ( !IsValidDevice(iDev) ) return 0;
893 int iPIDev=iDev;
894
895 if ( Devices[iPIDev].ProductInformation==0 ) iPIDev=0; // Use first device product information
896 if ( Devices[iPIDev].ProductInformation==0 ) return 0; // Can not do anything.
897 IsProgMem = (Devices[iPIDev].ProductInformation!=Devices[iPIDev].LocalProductInformation );
898 return Devices[iPIDev].ProductInformation;
899}
900
901//*****************************************************************************
902unsigned short tNMEA2000::GetN2kVersion(int iDev) const {
903 bool IsProgMem;
904 const tProductInformation *ProductInformation=GetProductInformation(iDev,IsProgMem);
905
906 if ( ProductInformation==0 ) return 0;
907
908 if ( !IsProgMem ) {
909 return ProductInformation->N2kVersion;
910 } else {
911 return pgm_read_word(&ProductInformation->N2kVersion);
912 }
913}
914
915//*****************************************************************************
916unsigned short tNMEA2000::GetProductCode(int iDev) const {
917 bool IsProgMem;
918 const tProductInformation *ProductInformation=GetProductInformation(iDev,IsProgMem);
919
920 if ( ProductInformation==0 ) return 0;
921
922 if ( !IsProgMem ) {
923 return ProductInformation->ProductCode;
924 } else {
925 return pgm_read_word(&ProductInformation->ProductCode);
926 }
927}
928
929//*****************************************************************************
930void tNMEA2000::GetModelID(char *buf, size_t max_len, int iDev) const {
931 if ( max_len==0 ) return;
932 buf[0]=0;
933 bool IsProgMem;
934 const tProductInformation *ProductInformation=GetProductInformation(iDev,IsProgMem);
935
936 if ( ProductInformation==0 ) return;
937
938 if ( !IsProgMem ) {
939 SetCharBuf(ProductInformation->N2kModelID,max_len,buf);
940 } else {
941 CopyProgmemString(ProductInformation->N2kModelID,max_len,buf);
942 }
943}
944
945//*****************************************************************************
946void tNMEA2000::GetSwCode(char *buf, size_t max_len, int iDev) const {
947 if ( max_len==0 ) return;
948 buf[0]=0;
949 bool IsProgMem;
950 const tProductInformation *ProductInformation=GetProductInformation(iDev,IsProgMem);
951
952 if ( ProductInformation==0 ) return;
953
954 if ( !IsProgMem ) {
955 SetCharBuf(ProductInformation->N2kSwCode,max_len,buf);
956 } else {
957 CopyProgmemString(ProductInformation->N2kSwCode,max_len,buf);
958 }
959}
960
961//*****************************************************************************
962void tNMEA2000::GetModelVersion(char *buf, size_t max_len, int iDev) const {
963 if ( max_len==0 ) return;
964 buf[0]=0;
965 bool IsProgMem;
966 const tProductInformation *ProductInformation=GetProductInformation(iDev,IsProgMem);
967
968 if ( ProductInformation==0 ) return;
969
970 if ( !IsProgMem ) {
971 SetCharBuf(ProductInformation->N2kModelVersion,max_len,buf);
972 } else {
973 CopyProgmemString(ProductInformation->N2kModelVersion,max_len,buf);
974 }
975}
976
977//*****************************************************************************
978void tNMEA2000::GetModelSerialCode(char *buf, size_t max_len, int iDev) const {
979 if ( max_len==0 ) return;
980 buf[0]=0;
981 bool IsProgMem;
982 const tProductInformation *ProductInformation=GetProductInformation(iDev,IsProgMem);
983
984 if ( ProductInformation==0 ) return;
985
986 if ( !IsProgMem ) {
987 SetCharBuf(ProductInformation->N2kModelSerialCode,max_len,buf);
988 } else {
989 CopyProgmemString(ProductInformation->N2kModelSerialCode,max_len,buf);
990 }
991}
992
993//*****************************************************************************
994unsigned char tNMEA2000::GetCertificationLevel(int iDev) const {
995 bool IsProgMem;
996 const tProductInformation *ProductInformation=GetProductInformation(iDev,IsProgMem);
997
998 if ( ProductInformation==0 ) return 0;
999
1000 if ( !IsProgMem ) {
1001 return ProductInformation->CertificationLevel;
1002 } else {
1003 return pgm_read_byte(&ProductInformation->CertificationLevel);
1004 }
1005}
1006
1007//*****************************************************************************
1008unsigned char tNMEA2000::GetLoadEquivalency(int iDev) const {
1009 bool IsProgMem;
1010 const tProductInformation *ProductInformation=GetProductInformation(iDev,IsProgMem);
1011
1012 if ( ProductInformation==0 ) return 0;
1013
1014 if ( !IsProgMem ) {
1015 return ProductInformation->LoadEquivalency;
1016 } else {
1017 return pgm_read_byte(&ProductInformation->LoadEquivalency);
1018 }
1019}
1020
1021//*****************************************************************************
1033 }
1034}
1035
1036//*****************************************************************************
1037void tNMEA2000::SetInstallationDescription1(const char *InstallationDescription1) {
1039 // Get pointer to local InstallationDescription1, which is after
1041 SetCharBuf(InstallationDescription1,Max_N2kConfigurationInfoField_len,Info);
1042 ConfigurationInformation.InstallationDescription1=(InstallationDescription1?Info:0);
1044}
1045
1046//*****************************************************************************
1047void tNMEA2000::SetInstallationDescription2(const char *InstallationDescription2) {
1050 SetCharBuf(InstallationDescription2,Max_N2kConfigurationInfoField_len,Info);
1051 ConfigurationInformation.InstallationDescription2=(InstallationDescription2?Info:0);
1053}
1054
1055//*****************************************************************************
1056void tNMEA2000::GetInstallationDescription1(char *buf, size_t max_len) {
1059 } else {
1061 }
1062}
1063
1064//*****************************************************************************
1065void tNMEA2000::GetInstallationDescription2(char *buf, size_t max_len) {
1068 } else {
1070 }
1071}
1072
1073//*****************************************************************************
1074void tNMEA2000::GetManufacturerInformation(char *buf, size_t max_len) {
1077 } else {
1079 }
1080}
1081
1082//*****************************************************************************
1085
1087 return result;
1088}
1089
1090#endif
1091
1092//*****************************************************************************
1093void tNMEA2000::SetDeviceInformation(unsigned long _UniqueNumber,
1094 unsigned char _DeviceFunction,
1095 unsigned char _DeviceClass,
1096 uint16_t _ManufacturerCode,
1097 unsigned char _IndustryGroup,
1098 int iDev
1099 ) {
1100 if ( !IsValidDevice(iDev) ) return;
1101 InitDevices();
1102 if (_ManufacturerCode!=0xffff) Devices[iDev].DeviceInformation.SetManufacturerCode(_ManufacturerCode);
1103 if (_UniqueNumber!=0xffffffff) Devices[iDev].DeviceInformation.SetUniqueNumber(_UniqueNumber);
1104 if (_DeviceFunction!=0xff) Devices[iDev].DeviceInformation.SetDeviceFunction(_DeviceFunction);
1105 if (_DeviceClass!=0xff) Devices[iDev].DeviceInformation.SetDeviceClass(_DeviceClass);
1106 if (_IndustryGroup!=0xff) Devices[iDev].DeviceInformation.SetIndustryGroup(_IndustryGroup);
1107}
1108
1109//*****************************************************************************
1111 uint8_t _DeviceInstanceLower,
1112 uint8_t _DeviceInstanceUpper,
1113 uint8_t _SystemInstance,
1114 int iDev
1115 ) {
1116 if ( !IsValidDevice(iDev) ) return;
1117
1118 InitDevices();
1119 uint8_t DeviceInstance=Devices[iDev].DeviceInformation.GetDeviceInstance();
1120
1121 if (_DeviceInstanceLower!=0xff ) {
1122 DeviceInstance=( (DeviceInstance & ~0x07) | (_DeviceInstanceLower & 0x07) );
1123 }
1124 if (_DeviceInstanceUpper!=0xff ) {
1125 DeviceInstance=( (DeviceInstance & ~0xF8) | ((_DeviceInstanceUpper&0x1f)<<3) );
1126 }
1127
1128 if ( Devices[iDev].DeviceInformation.GetDeviceInstance()!=DeviceInstance) {
1129 Devices[iDev].DeviceInformation.SetDeviceInstance(DeviceInstance);
1131 }
1132
1133 if (_SystemInstance!=0xff && Devices[iDev].DeviceInformation.GetSystemInstance()!=_SystemInstance) {
1134 Devices[iDev].DeviceInformation.SetSystemInstance(_SystemInstance);
1136 }
1137
1138 // Send delayed. Had problems with some devices with too fast response.
1139 if ( IsReadyToSend() ) SendIsoAddressClaim(0xff,iDev,2);
1140}
1141
1142//*****************************************************************************
1143void tNMEA2000::SetSingleFrameMessages(const unsigned long *_SingleFrameMessages) {
1144 SingleFrameMessages[0]=_SingleFrameMessages;
1145}
1146
1147//*****************************************************************************
1148void tNMEA2000::SetFastPacketMessages(const unsigned long *_FastPacketMessages) {
1149 FastPacketMessages[0]=_FastPacketMessages;
1150}
1151
1152//*****************************************************************************
1153void tNMEA2000::ExtendSingleFrameMessages(const unsigned long *_SingleFrameMessages) {
1154 SingleFrameMessages[1]=_SingleFrameMessages;
1155}
1156
1157//*****************************************************************************
1158void tNMEA2000::ExtendFastPacketMessages(const unsigned long *_FastPacketMessages) {
1159 FastPacketMessages[1]=_FastPacketMessages;
1160}
1161
1162//*****************************************************************************
1163void tNMEA2000::ExtendTransmitMessages(const unsigned long *_Messages, int iDev) {
1164 if ( !IsValidDevice(iDev) ) return;
1165 InitDevices();
1166 Devices[iDev].TransmitMessages=_Messages;
1167}
1168
1169//*****************************************************************************
1170void tNMEA2000::ExtendReceiveMessages(const unsigned long *_Messages, int iDev) {
1171 if ( !IsValidDevice(iDev) ) return;
1172 InitDevices();
1173 Devices[iDev].ReceiveMessages=_Messages;
1174}
1175
1176//*****************************************************************************
1177void tNMEA2000::SetMode(tN2kMode _N2kMode, uint8_t _N2kSource) {
1178 InitDevices();
1179 N2kMode=_N2kMode;
1180 for (int i=0; i<DeviceCount; i++) {
1181 Devices[i].N2kSource=_N2kSource+i;
1183 }
1184 AddressChanged=false;
1185}
1186
1187//*****************************************************************************
1189 if ( CANSendFrameBuf==0 && !IsInitialized() ) {
1191 N2kDbg("Initialize frame buffer. Size: "); N2kDbg(MaxCANSendFrames); N2kDbg(", address:"); N2kDbgln((uint32_t)CANSendFrameBuf);
1194 }
1195
1196 // Receive buffer has sense only with interrupt handling. So it must be handled on inherited class.
1197}
1198
1199//*****************************************************************************
1201 if ( OpenState==os_Open ) return true;
1202
1203 if ( OpenState==os_None ) {
1205 InitDevices();
1206
1207 if ( N2kCANMsgBuf==0 ) {
1208 if ( MaxN2kCANMsgs==0 ) MaxN2kCANMsgs=5;
1210 for (int i=0; i<MaxN2kCANMsgs; i++) N2kCANMsgBuf[i].FreeMessage();
1211
1212 #if !defined(N2K_NO_GROUP_FUNCTION_SUPPORT)
1213 // On first open try add also default group function handlers
1215 AddGroupFunctionHandler(new tN2kGroupFunctionHandlerForPGN126464(this)); // Rx/Tx list handler
1216 #if !defined(N2K_NO_HEARTBEAT_SUPPORT)
1217 AddGroupFunctionHandler(new tN2kGroupFunctionHandlerForPGN126993(this)); // Heartbeat handler
1218 #endif
1219 AddGroupFunctionHandler(new tN2kGroupFunctionHandlerForPGN126996(this)); // Product information
1220 AddGroupFunctionHandler(new tN2kGroupFunctionHandlerForPGN126998(this)); // Configuration information handler
1221 AddGroupFunctionHandler(new tN2kGroupFunctionHandler(this,0)); // Default handler at last
1222 #endif
1223 }
1225 }
1226
1227 if ( OpenState==os_OpenCAN ) {
1228 if ( !OpenScheduler.IsTime() ) return false;
1229 bool Notify=( (ForwardStream!=0) && (ForwardType==tNMEA2000::fwdt_Text) );
1230 if ( (dbMode!=dm_None) || CANOpen() ) {
1233 if ( Notify ) ForwardStream->println(F("CAN device ready"));
1234 } else { // Open failed, delay next open
1235 OpenScheduler.FromNow(1000);
1236 if ( Notify ) ForwardStream->println(F("CAN device failed to open"));
1237 }
1238 return OpenState==os_WaitOpen;
1239 }
1240
1241 // There were problems with some CAN controllers start sending immediately after
1242 // Initialization so we start sending delayed.
1247 #if !defined(N2K_NO_HEARTBEAT_SUPPORT)
1248 SetHeartbeatIntervalAndOffset(DefaultHeartbeatInterval,10000); // Init default hearbeat interval and offset.
1249 #endif
1250 if ( OnOpen!=0 ) OnOpen();
1251 } else {
1252 // Read rubbish out from CAN controller
1253 unsigned long canId;
1254 unsigned char len = 0;
1255 unsigned char buf[8];
1256 while ( CANGetFrame(canId,len,buf) );
1257 }
1258
1259 // For compatibility return true, when final open is waiting.
1260 return OpenState>=os_WaitOpen;
1261}
1262
1263//*****************************************************************************
1266}
1267
1268/************************************************************************/
1277void CanIdToN2k(unsigned long id, unsigned char &prio, unsigned long &pgn, unsigned char &src, unsigned char &dst) {
1278 unsigned char CanIdPF = (unsigned char) (id >> 16);
1279 unsigned char CanIdPS = (unsigned char) (id >> 8);
1280 unsigned char CanIdDP = (unsigned char) (id >> 24) & 1;
1281
1282 src = (unsigned char) id >> 0;
1283 prio = (unsigned char) ((id >> 26) & 0x7);
1284
1285 if (CanIdPF < 240) {
1286 /* PDU1 format, the PS contains the destination address */
1287 dst = CanIdPS;
1288 pgn = (((unsigned long)CanIdDP) << 16) | (((unsigned long)CanIdPF) << 8);
1289 } else {
1290 /* PDU2 format, the destination is implied global and the PGN is extended */
1291 dst = 0xff;
1292 pgn = (((unsigned long)CanIdDP) << 16) | (((unsigned long)CanIdPF) << 8) | (unsigned long)CanIdPS;
1293 }
1294}
1295
1296/************************************************************************/
1306unsigned long N2ktoCanID(unsigned char priority, unsigned long PGN, unsigned long Source, unsigned char Destination) {
1307 unsigned char CanIdPF = (unsigned char) (PGN >> 8);
1308
1309 if (CanIdPF < 240) { // PDU1 format
1310 if ( (PGN & 0xff) != 0 ) return 0; // for PDU1 format PGN lowest byte has to be 0 for the destination.
1311 return ( ((unsigned long)(priority & 0x7))<<26 | PGN<<8 | ((unsigned long)Destination)<<8 | (unsigned long)Source);
1312 } else { // PDU2 format
1313 return ( ((unsigned long)(priority & 0x7))<<26 | PGN<<8 | (unsigned long)Source);
1314 }
1315}
1316
1317//*****************************************************************************
1319{ uint16_t temp;
1320
1321 if ( CANSendFrameBuf==0 ) return true; // This can be in case, where inherited class defines own buffering.
1322
1325 if ( CANSendFrame(CANSendFrameBuf[temp].id, CANSendFrameBuf[temp].len, CANSendFrameBuf[temp].buf, CANSendFrameBuf[temp].wait_sent) ) {
1327 N2kFrameOutDbgStart("Frame unbuffered "); N2kFrameOutDbgln(CANSendFrameBuf[temp].id);
1328 } else return false;
1329 }
1330
1331 return true;
1332}
1333
1334//*****************************************************************************
1335bool tNMEA2000::SendFrame(unsigned long id, unsigned char len, const unsigned char *buf, bool wait_sent) {
1336
1337 if ( !SendFrames() || !CANSendFrame(id,len,buf,wait_sent) ) { // If we can not sent frame immediately, add it to buffer
1339 if ( Frame==0 ) {
1340 N2kFrameOutDbgStart("Frame failed "); N2kFrameOutDbgln(id);
1341 return false;
1342 }
1343 len=N2kMin<unsigned char>(len,8);
1344 Frame->id=id;
1345 Frame->len=len;
1346 Frame->wait_sent=wait_sent;
1347 for (int i=0; i<len; i++) Frame->buf[i]=buf[i];
1348 N2kFrameOutDbgStart("Frame buffered "); N2kFrameOutDbgln(id);
1349 }
1350
1351 return true;
1352}
1353
1354#if !defined(N2K_NO_HEARTBEAT_SUPPORT)
1355//*****************************************************************************
1356void tNMEA2000::SetHeartbeatIntervalAndOffset(uint32_t interval, uint32_t offset, int iDev) {
1357 if ( interval==0xffffffff && offset==0xffff ) return; // Do not change
1358 InitDevices();
1359 for (int i=(iDev<0?0:iDev); i<DeviceCount && (iDev<0?true:i<iDev+1); i++) {
1360 if ( interval==0xffffffff ) {
1361 interval=Devices[i].HeartbeatScheduler.GetPeriod();
1362 } else if (interval==0xfffffffe) { // restore default
1363 interval=DefaultHeartbeatInterval;
1364 }
1365 if ( offset==0xffffffff ) offset=Devices[i].HeartbeatScheduler.GetOffset();
1366
1367 if ( interval==0 ) { // This is for test purposes
1369 } else {
1370 if ( interval>MaxHeartbeatInterval ) interval=MaxHeartbeatInterval;
1371 if ( interval<1000 ) interval=1000;
1372
1373 bool changed=( Devices[i].HeartbeatScheduler.GetPeriod()!=interval || Devices[i].HeartbeatScheduler.GetOffset()!=offset );
1374 if ( changed ) {
1375 Devices[i].HeartbeatScheduler.SetPeriodAndOffset(interval,offset);
1377 }
1378 }
1379 }
1380}
1381
1382//*****************************************************************************
1383void tNMEA2000::SetHeartbeatInterval(unsigned long interval, bool /*SetAsDefault*/, int iDev) {
1384 SetHeartbeatIntervalAndOffset(interval,0xffffffff,iDev);
1385}
1386
1387//*****************************************************************************
1389 if ( !IsValidDevice(iDev) ) return;
1390 tN2kMsg N2kMsg;
1391 SetHeartbeat(N2kMsg,Devices[iDev].HeartbeatScheduler.GetPeriod(),0xff);
1392 SendMsg(N2kMsg,iDev);
1393}
1394
1395//*****************************************************************************
1397 if ( !IsActiveNode() ) return;
1398
1399 for (int iDev=0; iDev<DeviceCount; iDev++) {
1400 if ( !IsAddressClaimStarted(iDev) ) {
1401 if ( force || Devices[iDev].HeartbeatScheduler.IsTime() ) {
1403 tN2kMsg N2kMsg;
1404 SetHeartbeat(N2kMsg,Devices[iDev].HeartbeatScheduler.GetPeriod(),force?0xff:Devices[iDev].HeartbeatSequence);
1405 SendMsg(N2kMsg,iDev);
1406 if ( !force ) {
1407 Devices[iDev].HeartbeatSequence++;
1408 if ( Devices[iDev].HeartbeatSequence>252 ) Devices[iDev].HeartbeatSequence=0;
1409 }
1410 }
1411 }
1412 }
1413}
1414#endif
1415
1416//*****************************************************************************
1418 if (CANSendFrameBuf==0) return 0;
1419
1420 uint16_t temp = (CANSendFrameBufferWrite + 1) % MaxCANSendFrames;
1421
1422 if (temp != CANSendFrameBufferRead) {
1425 } else {
1426 return 0;
1427 }
1428}
1429
1430//*****************************************************************************
1432 for (int i=0; i<DeviceCount; i++ ) {
1433 if ( Devices[i].HasPendingInformation ) {
1434 #if !defined(N2K_NO_ISO_MULTI_PACKET_SUPPORT)
1436 #endif
1437 if ( Devices[i].QueryPendingIsoAddressClaim() ) {
1438 SendIsoAddressClaim(0xff,i);
1440 }
1441 if ( Devices[i].QueryPendingProductInformation() ) SendProductInformation(i);
1442 if ( Devices[i].QueryPendingConfigurationInformation() ) SendConfigurationInformation(i);
1443 }
1444 }
1445}
1446
1447//*****************************************************************************
1448// Sends message to N2k bus
1449//
1450bool tNMEA2000::SendMsg(const tN2kMsg &N2kMsg, int DeviceIndex) {
1451 if ( dbMode==dm_None ) {
1452 if ( OpenState!=os_Open ) {
1453 if ( !(Open() && OpenState==os_Open) ) return false; // Can not do much
1454 }
1455 }
1456
1457 bool result=false;
1458
1459 if ( DeviceIndex>=DeviceCount) return result;
1460 N2kMsg.CheckDestination();
1461 if (DeviceIndex>=0) { N2kMsg.ForceSource(Devices[DeviceIndex].N2kSource); } else { DeviceIndex=0; }
1462
1463 if ( N2kMsg.Source>N2kMaxCanBusAddress && N2kMsg.PGN!=N2kPGNIsoAddressClaim ) return false; // CAN bus address range is 0-251. Anyway allow ISO address claim mgs.
1464
1465 unsigned long canId=N2ktoCanID(N2kMsg.Priority,N2kMsg.PGN,N2kMsg.Source, N2kMsg.Destination);
1466
1467 if ( canId==0 ) { // PGN validity - N2ktoCanID returns 0 for invalid PGN
1468// if (ForwardStream!=0 && ForwardType==tNMEA2000::fwdt_Text) { ForwardStream->print(F("Invalid PGN ")); ForwardStream->println(N2kMsg.PGN); }
1469 return false;
1470 }
1471
1472 if (N2kMode==N2km_ListenOnly) return false; // Do not send anything on listen only mode
1473
1474 if (N2kMsg.PGN==0) return false;
1475
1476 switch (dbMode) {
1477 case dm_None:
1478 N2kMsgDbgStart("Send PGN:"); N2kMsgDbgln(N2kMsg.PGN);
1479 N2kMsgDbgStart(" - can ID:"); N2kMsgDbgln(canId);
1480 if ( IsAddressClaimStarted(DeviceIndex) && N2kMsg.PGN!=N2kPGNIsoAddressClaim ) return false;
1481
1482 if (N2kMsg.DataLen<=8 && !IsFastPacket(N2kMsg) ) { // We can send single frame
1483 DbgPrintBuf(N2kMsg.DataLen, N2kMsg.Data,true);
1484 result=SendFrame(canId, N2kMsg.DataLen, N2kMsg.Data,false);
1485 if (!result && ForwardStream!=0 && ForwardType==tNMEA2000::fwdt_Text) { ForwardStream->print(F("PGN ")); ForwardStream->print(N2kMsg.PGN); ForwardStream->println(F(" send failed")); }
1486 N2kPrintFreeMemory("SendMsg, single frame");
1487 } else { // Send it as fast packet in multiple frames
1488#if !defined(N2K_NO_ISO_MULTI_PACKET_SUPPORT)
1489 if ( N2kMsg.IsTPMessage() ) {
1490 result=StartSendTPMessage(N2kMsg,DeviceIndex);
1491 } else
1492#endif
1493 {
1494 unsigned char temp[8]; // {0,0,0,0,0,0,0,0};
1495 int cur=0;
1496 int frames=(N2kMsg.DataLen>6 ? (N2kMsg.DataLen-6-1)/7+1+1 : 1 );
1497 int Order=GetSequenceCounter(N2kMsg.PGN,DeviceIndex)<<5;
1498 result=true;
1499 for (int i = 0; i<frames && result; i++) {
1500 temp[0] = i|Order; //frame counter
1501 if (i==0) {
1502 temp[1] = N2kMsg.DataLen; //total bytes in fast packet
1503 //send the first 6 bytes
1504 for (int j = 2; j<8; j++) {
1505 temp[j]=N2kMsg.Data[cur];
1506 cur++;
1507 }
1508 N2kPrintFreeMemory("SendMsg, fastpacket");
1509 } else {
1510 int j=1;
1511 //send the next 7 data bytes
1512 for (; j<8 && cur<N2kMsg.DataLen; j++) {
1513 temp[j]=N2kMsg.Data[cur];
1514 cur++;
1515 }
1516 for (; j<8; j++) {
1517 temp[j]=0xff;
1518 }
1519 }
1520
1521 DbgPrintBuf(8,temp,true);
1522 result=SendFrame(canId, 8, temp, true);
1523 if (!result && ForwardStream!=0 && ForwardType==tNMEA2000::fwdt_Text) {
1524 ForwardStream->print(F("PGN ")); ForwardStream->print(N2kMsg.PGN);
1525 ForwardStream->print(F(", frame:")); ForwardStream->print(i); ForwardStream->print(F("/")); ForwardStream->print(frames);
1526 ForwardStream->println(F(" send failed"));
1527 }
1528 }
1529 }
1530 };
1531 if ( ForwardOwnMessages() ) ForwardMessage(N2kMsg);
1532 break;
1533 case dm_ClearText:
1534 result=true;
1535 N2kMsg.Print(ForwardStream);
1536 break;
1537 case dm_Actisense:
1538 result=true;
1540 break;
1541 }
1542
1543 return result;
1544}
1545
1546//*****************************************************************************
1548 dbMode=_dbMode;
1549}
1550
1551//*****************************************************************************
1552bool tNMEA2000::IsFastPacketPGN(unsigned long PGN) {
1555 IsProprietaryFastPacketMessage(PGN) ) return true;
1556
1557 int i;
1558
1559 for (unsigned char igroup=0; (igroup<N2kMessageGroups); igroup++) {
1560 if (FastPacketMessages[igroup]!=0) {
1561 for (i=0; pgm_read_dword(&FastPacketMessages[igroup][i])!=PGN && pgm_read_dword(&FastPacketMessages[igroup][i])!=0; i++);
1562 if (pgm_read_dword(&FastPacketMessages[igroup][i])==PGN) {
1563 return true;
1564 }
1565 }
1566 }
1567
1568 return false;
1569}
1570
1571//*****************************************************************************
1573 if (N2kMsg.Priority>=0x80) return false; // Special handling for force to send message as single frame.
1574
1575 return IsFastPacketPGN(N2kMsg.PGN);
1576}
1577
1578//*****************************************************************************
1579bool tNMEA2000::CheckKnownMessage(unsigned long PGN, bool &SystemMessage, bool &FastPacket) {
1580 int i;
1581// return true;
1582 FastPacket=false;
1583 SystemMessage=false;
1584 if ( PGN==0 ) { return false; } // Unknown
1585
1586 // Check other messages
1587 if ( SingleFrameMessages[0]==0 && IsDefaultSingleFrameMessage(PGN) ) return true;
1588 if ( (FastPacket=IsMandatoryFastPacketMessage(PGN)) ) return true;
1589 if ( FastPacketMessages[0]==0 && (FastPacket=IsDefaultFastPacketMessage(PGN))==true ) return true;
1590
1591 // Check system messages
1592 if ( IsSingleFrameSystemMessage(PGN) || (FastPacket=IsFastPacketSystemMessage(PGN))==true ) {
1593 SystemMessage=true;
1594 return true;
1595 }
1596
1597 // Check user defined messages
1598 for (unsigned char igroup=0; (igroup<N2kMessageGroups); igroup++) {
1599 if (SingleFrameMessages[igroup]!=0) {
1600 for (i=0; pgm_read_dword(&SingleFrameMessages[igroup][i])!=PGN && pgm_read_dword(&SingleFrameMessages[igroup][i])!=0; i++);
1601 if (pgm_read_dword(&SingleFrameMessages[igroup][i])==PGN) return true;
1602 }
1603
1604 if (FastPacketMessages[igroup]!=0) {
1605 for (i=0; pgm_read_dword(&FastPacketMessages[igroup][i])!=PGN && pgm_read_dword(&FastPacketMessages[igroup][i])!=0; i++);
1606 if (pgm_read_dword(&FastPacketMessages[igroup][i])==PGN) {
1607 FastPacket=true;
1608 return true;
1609 }
1610 }
1611 }
1612
1613 FastPacket=IsProprietaryFastPacketMessage(PGN);
1614
1615 return false;
1616}
1617
1618/************************************************************************/
1626void CopyBufToCANMsg(tN2kCANMsg &CANMsg, unsigned char start, unsigned char len, unsigned char *buf) {
1627 for (int j=start; (j<len) & (CANMsg.CopiedLen<CANMsg.N2kMsg.MaxDataLen); j++, CANMsg.CopiedLen++) {
1628 CANMsg.N2kMsg.Data[CANMsg.CopiedLen]=buf[j];
1629 }
1630}
1631
1632//*****************************************************************************
1633#if !defined(N2K_NO_ISO_MULTI_PACKET_SUPPORT)
1634void tNMEA2000::FindFreeCANMsgIndex(unsigned long PGN, unsigned char Source, unsigned char Destination, bool TPMsg, uint8_t &MsgIndex) {
1635#else
1636void tNMEA2000::FindFreeCANMsgIndex(unsigned long PGN, unsigned char Source, unsigned char Destination, uint8_t &MsgIndex) {
1637#endif
1638 unsigned long OldestMsgTime,CurTime;
1639 int OldestIndex;
1640
1641 for (MsgIndex=0, CurTime=OldestMsgTime=N2kMillis(), OldestIndex=MaxN2kCANMsgs;
1642 MsgIndex<MaxN2kCANMsgs &&
1643 !( N2kCANMsgBuf[MsgIndex].FreeMsg ||
1644 ( N2kCANMsgBuf[MsgIndex].N2kMsg.PGN==PGN
1645 && N2kCANMsgBuf[MsgIndex].N2kMsg.Source==Source
1646 && N2kCANMsgBuf[MsgIndex].N2kMsg.Destination==Destination
1647#if !defined(N2K_NO_ISO_MULTI_PACKET_SUPPORT)
1648 && N2kCANMsgBuf[MsgIndex].N2kMsg.IsTPMessage()==TPMsg
1649#endif
1650 )
1651 );
1652 MsgIndex++) { // Find free message place
1653 if ( N2kIsTimeBefore(N2kCANMsgBuf[MsgIndex].N2kMsg.MsgTime,OldestMsgTime) ) {
1654 OldestIndex=MsgIndex;
1655 OldestMsgTime=N2kCANMsgBuf[MsgIndex].N2kMsg.MsgTime;
1656 }
1657 }
1658 if ( MsgIndex==MaxN2kCANMsgs && N2kHasElapsed(OldestMsgTime,Max_N2kMsgBuf_Time,CurTime) ) {
1659 MsgIndex=OldestIndex; // Use the old one, which has timed out
1660 N2kCANMsgBuf[MsgIndex].FreeMessage();
1661 }
1662
1663}
1664
1665#if !defined(N2K_NO_ISO_MULTI_PACKET_SUPPORT)
1666
1667//*****************************************************************************
1669 if ( !IsActiveNode() ) return false;
1670
1671 tN2kMsg N2kMsg;
1672
1673 N2kMsg.Source=Devices[iDev].N2kSource;
1674 N2kMsg.Destination=0xff;
1675 N2kMsg.SetPGN(TP_CM);
1676 N2kMsg.Priority=6;
1677 N2kMsg.AddByte(TP_CM_BAM);
1678 int nBytes=Devices[iDev].PendingTPMsg.DataLen;
1679 N2kMsg.Add2ByteUInt(nBytes);
1680 N2kMsg.AddByte(nBytes/7+(nBytes%7!=0?1:0));
1681 N2kMsg.AddByte(0xff); // Reserved;
1682 N2kMsg.Add3ByteInt(Devices[iDev].PendingTPMsg.PGN);
1683 return SendMsg(N2kMsg,iDev);
1684}
1685
1686//*****************************************************************************
1688 if ( !IsActiveNode() ) return false;
1689
1690 tN2kMsg N2kMsg;
1691 N2kMsg.Source=Devices[iDev].N2kSource;
1693 N2kMsg.SetPGN(TP_CM);
1694 N2kMsg.Priority=6;
1695 N2kMsg.AddByte(TP_CM_RTS);
1696 int nBytes=Devices[iDev].PendingTPMsg.DataLen;
1697 N2kMsg.Add2ByteUInt(nBytes);
1698 N2kMsg.AddByte(nBytes/7+(nBytes%7!=0?1:0));
1699 N2kMsg.AddByte(0xff); // Reserved;
1700 N2kMsg.Add3ByteInt(Devices[iDev].PendingTPMsg.PGN);
1701 return SendMsg(N2kMsg,iDev);
1702}
1703
1704unsigned char TPCtsPackets(unsigned char nPackets) { return tNMEA2000::N2kMax<unsigned char>(1,tNMEA2000::N2kMin<unsigned char>(nPackets,TP_MAX_FRAMES)); }
1705
1706//*****************************************************************************
1707void tNMEA2000::SendTPCM_CTS(unsigned long PGN, unsigned char Destination, int iDev, unsigned char nPackets, unsigned char NextPacketNumber) {
1708 tN2kMsg N2kMsg;
1709
1710 if ( !IsActiveNode() ) return;
1711 N2kMsg.Source=Devices[iDev].N2kSource;
1712 N2kMsg.Destination=Destination;
1713 N2kMsg.SetPGN(TP_CM);
1714 N2kMsg.Priority=6;
1715 N2kMsg.AddByte(TP_CM_CTS);
1716 N2kMsg.AddByte(TPCtsPackets(nPackets));
1717 N2kMsg.AddByte(NextPacketNumber);
1718 N2kMsg.AddByte(0xff); // Reserved;
1719 N2kMsg.AddByte(0xff); // Reserved;
1720 N2kMsg.Add3ByteInt(PGN);
1721 SendMsg(N2kMsg,iDev);
1722}
1723
1724//*****************************************************************************
1725void tNMEA2000::SendTPCM_EndAck(unsigned long PGN, unsigned char Destination, int iDev, uint16_t nBytes, unsigned char nPackets) {
1726 tN2kMsg N2kMsg;
1727
1728 if ( !IsActiveNode() ) return;
1729 N2kMsg.Source=Devices[iDev].N2kSource;
1730 N2kMsg.Destination=Destination;
1731 N2kMsg.SetPGN(TP_CM);
1732 N2kMsg.Priority=6;
1733 N2kMsg.AddByte(TP_CM_ACK);
1734 N2kMsg.Add2ByteUInt(nBytes);
1735 N2kMsg.AddByte(nPackets);
1736 N2kMsg.AddByte(0xff); // Reserved;
1737 N2kMsg.Add3ByteInt(PGN);
1738 SendMsg(N2kMsg,iDev);
1739}
1740
1741//*****************************************************************************
1742void tNMEA2000::SendTPCM_Abort(unsigned long PGN, unsigned char Destination, int iDev, unsigned char AbortCode) {
1743 tN2kMsg N2kMsg;
1744
1745 if ( !IsActiveNode() ) return;
1746 N2kMsg.Source=Devices[iDev].N2kSource;
1747 N2kMsg.Destination=Destination;
1748 N2kMsg.SetPGN(TP_CM);
1749 N2kMsg.Priority=6;
1750 N2kMsg.AddByte(TP_CM_Abort);
1751 N2kMsg.AddByte(AbortCode);
1752 N2kMsg.AddByte(0xff); // Reserved;
1753 N2kMsg.AddByte(0xff); // Reserved;
1754 N2kMsg.AddByte(0xff); // Reserved;
1755 N2kMsg.Add3ByteInt(PGN);
1756 SendMsg(N2kMsg,iDev);
1757}
1758
1759//*****************************************************************************
1760// Caller should take care of not calling this after all has been done.
1761// Use HasAllTPDTSent for checking.
1762bool tNMEA2000::SendTPDT(int iDev) {
1763 tN2kMsg N2kMsg;
1764 N2kMsg.Source=Devices[iDev].N2kSource;
1766 N2kMsg.SetPGN(TP_DT);
1767 N2kMsg.Priority=6;
1768 N2kMsg.AddByte(Devices[iDev].NextDTSequence+1);
1769 int iByteToSend=Devices[iDev].NextDTSequence*7;
1770 for ( int i=0; i<7; i++,iByteToSend++ ) {
1771 if ( iByteToSend<Devices[iDev].PendingTPMsg.DataLen ) {
1772 N2kMsg.AddByte(Devices[iDev].PendingTPMsg.Data[iByteToSend]);
1773 } else N2kMsg.AddByte(0xff);
1774 }
1775 Devices[iDev].NextDTSequence++;
1776
1777 return SendMsg(N2kMsg,iDev);
1778}
1779
1780//*****************************************************************************
1782 return ( Devices[iDev].NextDTSequence*7>=Devices[iDev].PendingTPMsg.DataLen );
1783}
1784
1785//*****************************************************************************
1786bool tNMEA2000::TestHandleTPMessage(unsigned long PGN, unsigned char Source, unsigned char Destination,
1787 unsigned char len, unsigned char *buf,
1788 uint8_t &MsgIndex) {
1789 MsgIndex=MaxN2kCANMsgs;
1790 int iDev=FindSourceDeviceIndex(Destination);
1791
1792 if ( PGN==TP_CM ) {
1793 unsigned char TP_CM_Control=buf[0];
1794 int Index=5;
1795 unsigned long TransportPGN=GetBuf3ByteUInt(Index,buf);
1796
1797 switch (TP_CM_Control) {
1798 case TP_CM_BAM:
1799 case TP_CM_RTS: {
1800 N2kMsgDbgln("Got TP Command");
1801 Index=1;
1802 uint16_t nBytes=GetBuf2ByteUInt(Index,buf);
1803 uint8_t TPMaxPackets=buf[Index++];
1804 //Index++; // reserved
1805
1806 FindFreeCANMsgIndex(TransportPGN,Source,Destination,true,MsgIndex);
1807
1808 if (MsgIndex==MaxN2kCANMsgs) { // No free msg place
1809 N2kMsgDbgStart("No free msg slot"); N2kMsgDbgln();
1810 if ( (TP_CM_Control==TP_CM_RTS) && (iDev>=0) ) { // If it was for us and not broadcast, we need to abort transport
1811 SendTPCM_Abort(TransportPGN,Source,iDev,TP_CM_AbortBusy);
1812 }
1813 } else { // Start transport
1814 N2kMsgDbgStart("Use msg slot: "); N2kMsgDbgln(MsgIndex);
1815 bool FastPacket;
1816 N2kCANMsgBuf[MsgIndex].KnownMessage=CheckKnownMessage(TransportPGN,N2kCANMsgBuf[MsgIndex].SystemMessage,FastPacket);
1817 if ( nBytes < tN2kMsg::MaxDataLen && // Currently we can handle only tN2kMsg::MaxDataLen long messages
1818 (N2kCANMsgBuf[MsgIndex].KnownMessage || !HandleOnlyKnownMessages()) ) {
1819 N2kCANMsgBuf[MsgIndex].FreeMsg=false;
1820 N2kCANMsgBuf[MsgIndex].N2kMsg.Init(7 /* Priority? */,TransportPGN,Source,Destination);
1821 N2kCANMsgBuf[MsgIndex].CopiedLen=0;
1822 N2kCANMsgBuf[MsgIndex].LastFrame=0;
1823 N2kCANMsgBuf[MsgIndex].N2kMsg.DataLen=nBytes;
1825 N2kCANMsgBuf[MsgIndex].TPMaxPackets=TPMaxPackets;
1826 if ( (TP_CM_Control==TP_CM_RTS) && (iDev>=0) ) { // If it was for us and not broadcast, we need to response
1827 SendTPCM_CTS(TransportPGN,Source,iDev,N2kCANMsgBuf[MsgIndex].TPMaxPackets,N2kCANMsgBuf[MsgIndex].LastFrame+1);
1828 N2kCANMsgBuf[MsgIndex].TPRequireCTS=TPCtsPackets(N2kCANMsgBuf[MsgIndex].TPMaxPackets);
1829 } else {
1830 N2kCANMsgBuf[MsgIndex].TPMaxPackets=0xff; // TPMaxPackets>0 indicates that it is TP message
1831 }
1832 } else { // Too long or unknown
1833 if ( (TP_CM_Control==TP_CM_RTS) && (iDev>=0) ) { // If it was for us and not broadcast, we need to response
1834 SendTPCM_Abort(TransportPGN,Source,iDev,TP_CM_AbortBusy); // Abort
1835 }
1836 }
1837 }
1838 break;
1839 }
1840 case TP_CM_CTS:
1841 if ( !IsValidDevice(iDev) ) break; // Should never fail
1842 N2kMsgDbgStart("Got TP CTS"); N2kMsgDbgln(MsgIndex);
1843 if ( IsBroadcast(Devices[iDev].PendingTPMsg.Destination) ) break; // We should not get controls for broadcast TP msg
1844 if ( Devices[iDev].PendingTPMsg.PGN!=TransportPGN ) { // Some failure on communication
1845 EndSendTPMessage(iDev); // Should we retry from beginning?
1846 break;
1847 }
1848 // Now respond with next data packets
1849 if ( buf[1]>0 ) { // Note that with 0, receiver wants to have break
1850 if ( buf[2]-1!=Devices[iDev].NextDTSequence ) { // We got sequence error
1851 EndSendTPMessage(iDev); // Should we retry from beginning?
1852 break;
1853 }
1854 uint8_t MaxTPSequences=buf[1];
1855 bool TPDTResult=true;
1856 for ( uint8_t iSeq=0; TPDTResult &&iSeq<MaxTPSequences && !HasAllTPDTSent(iDev); iSeq++ ) TPDTResult&=SendTPDT(iDev);
1857 if ( !TPDTResult ) EndSendTPMessage(iDev);
1858 }
1859 Devices[iDev].NextDTSendTime.FromNow(100); // Set timeout for next response
1860 break;
1861 case TP_CM_ACK:
1862 if ( !IsValidDevice(iDev) ) break; // Should never fail
1863 N2kMsgDbgStart("Got TP ACK"); N2kMsgDbgln(MsgIndex);
1864 if ( IsBroadcast(Devices[iDev].PendingTPMsg.Destination) ) break; // We should not get controls for broadcast TP msg
1865 EndSendTPMessage(iDev);
1866 break;
1867 case TP_CM_Abort:
1868 if ( !IsValidDevice(iDev) ) break; // Should never fail
1869 N2kMsgDbgStart("Got TP Abort"); N2kMsgDbgln(MsgIndex);
1870 if ( IsBroadcast(Devices[iDev].PendingTPMsg.Destination) ) break; // We should not get controls for broadcast TP msg
1871 EndSendTPMessage(iDev);
1872 break;
1873 default:
1874 ;
1875 }
1876 MsgIndex=MaxN2kCANMsgs; // After TP_CM, message is newer ready.
1877 return true;
1878 } else if ( PGN==TP_DT ) { // Datapacket
1879 N2kMsgDbgStart("Got TP data"); N2kMsgDbgln(MsgIndex);
1880 // So we need to find TP msg which sender and destination matches.
1881 for (MsgIndex=0;
1882 MsgIndex<MaxN2kCANMsgs &&
1883 !( !N2kCANMsgBuf[MsgIndex].FreeMsg
1884 && N2kCANMsgBuf[MsgIndex].N2kMsg.IsTPMessage()
1885 && N2kCANMsgBuf[MsgIndex].N2kMsg.Destination==Destination
1886 && N2kCANMsgBuf[MsgIndex].N2kMsg.Source==Source
1887 );
1888 MsgIndex++);
1889 // if (MsgIndex==MaxN2kCANMsgs) N2kMsgDbgln("TP data msg not found");
1890 // for (int i=1; i<len; i++) N2kMsgDbgln(buf[i]);
1891 if (MsgIndex<MaxN2kCANMsgs) { // found TP message under reception
1892 N2kMsgDbgStart("Use msg slot: "); N2kMsgDbgln(MsgIndex);
1893 if (N2kCANMsgBuf[MsgIndex].LastFrame+1 == buf[0]) { // Right packet is coming
1894 // Add packet to the message
1895 CopyBufToCANMsg(N2kCANMsgBuf[MsgIndex],1,len,buf);
1896 N2kCANMsgBuf[MsgIndex].LastFrame=buf[0];
1897 // Transport protocol is slower, so to avoid timeout, we reset message time
1899 if ( N2kCANMsgBuf[MsgIndex].CopiedLen>=N2kCANMsgBuf[MsgIndex].N2kMsg.DataLen ) { // all done
1900 N2kCANMsgBuf[MsgIndex].Ready=true;
1901 if ( N2kCANMsgBuf[MsgIndex].TPRequireCTS>0 && iDev>=0 ) { // send response
1902 SendTPCM_EndAck(N2kCANMsgBuf[MsgIndex].N2kMsg.PGN,Source,iDev,N2kCANMsgBuf[MsgIndex].N2kMsg.DataLen,N2kCANMsgBuf[MsgIndex].LastFrame);
1903 }
1904 } else {
1905 if ( N2kCANMsgBuf[MsgIndex].TPRequireCTS>0 && ((N2kCANMsgBuf[MsgIndex].LastFrame)%N2kCANMsgBuf[MsgIndex].TPRequireCTS)==0 ) { // send response
1906 SendTPCM_CTS(N2kCANMsgBuf[MsgIndex].N2kMsg.PGN,Source,iDev,N2kCANMsgBuf[MsgIndex].TPMaxPackets,N2kCANMsgBuf[MsgIndex].LastFrame+1);
1907 }
1908 }
1909 } else { // Wrong packet - either we lost packet or sender sends wrong, so free this
1910 N2kMsgDbgStart("Invalid packet: "); N2kMsgDbgln(buf[0]);
1911 if ( N2kCANMsgBuf[MsgIndex].TPRequireCTS>0 && iDev>=0 ) { // We need to abort transport
1912 SendTPCM_Abort(N2kCANMsgBuf[MsgIndex].N2kMsg.PGN,Source,iDev,TP_CM_AbortTimeout); // Abort transport
1913 }
1914 N2kCANMsgBuf[MsgIndex].FreeMessage();
1915
1916 }
1917 if ( !N2kCANMsgBuf[MsgIndex].Ready ) MsgIndex=MaxN2kCANMsgs;
1918 }
1919 return true; // We handled message
1920 }
1921
1922 return false;
1923}
1924
1925//*****************************************************************************
1926bool tNMEA2000::StartSendTPMessage(const tN2kMsg& msg, int iDev) {
1927 if ( !IsValidDevice(iDev) ) return false;
1928
1929 if ( Devices[iDev].PendingTPMsg.PGN!=0 ) return false; // No room for sending TP message
1930
1931 int result=false;
1932
1933 Devices[iDev].HasPendingInformation=true;
1934 Devices[iDev].PendingTPMsg=msg;
1935 Devices[iDev].NextDTSequence=0;
1936 Devices[iDev].NextDTSendTime.FromNow(50);
1937 if ( IsBroadcast(msg.Destination) ) { // Start with BAM
1938 result=SendTPCM_BAM(iDev);
1939 } else {
1940 result=SendTPCM_RTS(iDev);
1941 }
1942
1943 if ( !result ) EndSendTPMessage(iDev); // Currently no retry
1944
1945 return result;
1946}
1947
1948//*****************************************************************************
1950 Devices[iDev].PendingTPMsg.Clear();
1953}
1954
1955//*****************************************************************************
1957 if ( Devices[iDev].PendingTPMsg.PGN!=0 && Devices[iDev].NextDTSendTime.IsTime() ) { // Pending message and timed out
1958 if ( IsBroadcast(Devices[iDev].PendingTPMsg.Destination) ) { // For broadcast we just send next data
1959 SendTPDT(iDev);
1960 Devices[iDev].NextDTSendTime.FromNow(50);
1961 if ( HasAllTPDTSent(iDev) ) EndSendTPMessage(iDev); // All done
1962 } else { // We have not got response from receiver within timeout, so just end. Or should we retry?
1963 EndSendTPMessage(iDev);
1964 }
1965 }
1966}
1967
1968#endif
1969
1970//*****************************************************************************
1971inline bool IsFastPacketFirstFrame(unsigned char b) { return ((b & 0x1F)==0); }
1972
1973//*****************************************************************************
1974// Function handles received CAN frame and adds it to tN2kCANMsg.
1975// Returns: Index to ready tN2kCANMsg or MaxN2kCANMsgs, if we skipped the frame
1976// or message is not ready (fast packet or ISO Multi-Packet)
1977uint8_t tNMEA2000::SetN2kCANBufMsg(unsigned long canId, unsigned char len, unsigned char *buf) {
1978 unsigned char Priority;
1979 unsigned long PGN;
1980 unsigned char Source;
1981 unsigned char Destination;
1982 bool FastPacket;
1983 bool SystemMessage;
1984 bool KnownMessage;
1985 uint8_t MsgIndex=MaxN2kCANMsgs;
1986
1987 CanIdToN2k(canId,Priority,PGN,Source,Destination);
1988#if !defined(N2K_NO_ISO_MULTI_PACKET_SUPPORT)
1989 if ( !TestHandleTPMessage(PGN,Source,Destination,len,buf,MsgIndex) )
1990#endif
1991 {
1992 KnownMessage=CheckKnownMessage(PGN,SystemMessage,FastPacket);
1993 if ( KnownMessage || !HandleOnlyKnownMessages() ) {
1994 if (FastPacket && !IsFastPacketFirstFrame(buf[0]) ) { // Not first frame
1995 N2kFrameInDbgStart("New frame="); N2kFrameInDbg(PGN); N2kFrameInDbg(" frame="); N2kFrameInDbg(buf[0],HEX); N2kFrameInDbgln();
1996 // Find previous slot for this PGN
1997 for (MsgIndex=0;
1998 MsgIndex<MaxN2kCANMsgs &&
1999 !( N2kCANMsgBuf[MsgIndex].N2kMsg.PGN==PGN
2000 && N2kCANMsgBuf[MsgIndex].N2kMsg.Source==Source
2001#if !defined(N2K_NO_ISO_MULTI_PACKET_SUPPORT)
2002 && !N2kCANMsgBuf[MsgIndex].N2kMsg.IsTPMessage()
2003#endif
2004 );
2005 MsgIndex++);
2006 if (MsgIndex<MaxN2kCANMsgs) { // we found start for this message, so add data to it.
2007 N2kMsgDbgStart("Use msg slot: "); N2kMsgDbgln(MsgIndex);
2008 if (N2kCANMsgBuf[MsgIndex].LastFrame+1 == buf[0]) { // Right frame is coming
2009 N2kCANMsgBuf[MsgIndex].LastFrame=buf[0];
2010 CopyBufToCANMsg(N2kCANMsgBuf[MsgIndex],1,len,buf);
2011 } else { // We have lost frame, so free this
2012 N2kFrameErrDbgStart("Lost frame "); N2kFrameErrDbg(N2kCANMsgBuf[MsgIndex].LastFrame); N2kFrameErrDbg("/"); N2kFrameErrDbg(buf[0]);
2013 N2kFrameErrDbg(", source "); N2kFrameErrDbg(Source); N2kFrameErrDbg(" for: "); N2kFrameErrDbgln(PGN);
2014 N2kCANMsgBuf[MsgIndex].FreeMessage();
2015 MsgIndex=MaxN2kCANMsgs;
2016 }
2017 } else { // Orphan frame
2018 N2kFrameErrDbgStart("Orphan frame "); N2kFrameErrDbg(buf[0]); N2kFrameErrDbg(", source ");
2019 N2kFrameErrDbg(Source); N2kFrameErrDbg(" for: "); N2kFrameErrDbgln(PGN);
2020 }
2021 } else { // Handle first frame
2022#if !defined(N2K_NO_ISO_MULTI_PACKET_SUPPORT)
2023 FindFreeCANMsgIndex(PGN,Source,Destination,false,MsgIndex);
2024#else
2025 FindFreeCANMsgIndex(PGN,Source,Destination,MsgIndex);
2026#endif
2027 if ( MsgIndex<MaxN2kCANMsgs ) { // we found free place, so handle frame
2028 N2kMsgDbgStart("Use msg slot: "); N2kMsgDbgln(MsgIndex);
2029 N2kCANMsgBuf[MsgIndex].FreeMsg=false;
2030 N2kCANMsgBuf[MsgIndex].KnownMessage=KnownMessage;
2031 N2kCANMsgBuf[MsgIndex].SystemMessage=SystemMessage;
2032 N2kCANMsgBuf[MsgIndex].N2kMsg.Init(Priority,PGN,Source,Destination);
2033#if !defined(N2K_NO_ISO_MULTI_PACKET_SUPPORT)
2034 N2kCANMsgBuf[MsgIndex].N2kMsg.SetIsTPMessage(false);
2035#endif
2036 N2kCANMsgBuf[MsgIndex].CopiedLen=0;
2037 if (FastPacket) {
2038 CopyBufToCANMsg(N2kCANMsgBuf[MsgIndex],2,len,buf);
2039 N2kFrameInDbgStart("First frame="); N2kFrameInDbg(PGN); N2kFrameInDbgln();
2040 N2kCANMsgBuf[MsgIndex].LastFrame=buf[0];
2041 N2kCANMsgBuf[MsgIndex].N2kMsg.DataLen=buf[1];
2042 } else {
2043 CopyBufToCANMsg(N2kCANMsgBuf[MsgIndex],0,len,buf);
2044 N2kFrameInDbgStart("Single frame="); N2kFrameInDbg(PGN); N2kFrameInDbgln();
2045 N2kCANMsgBuf[MsgIndex].LastFrame=0;
2046 N2kCANMsgBuf[MsgIndex].N2kMsg.DataLen=len;
2047 }
2048 }
2049 }
2050
2051 if ( MsgIndex<MaxN2kCANMsgs ) {
2052 N2kCANMsgBuf[MsgIndex].Ready=(N2kCANMsgBuf[MsgIndex].CopiedLen>=N2kCANMsgBuf[MsgIndex].N2kMsg.DataLen);
2053 if ( !N2kCANMsgBuf[MsgIndex].Ready ) MsgIndex=MaxN2kCANMsgs; // If packet is not ready, do not return index to it
2054 }
2055 }
2056 }
2057
2058 return MsgIndex;
2059}
2060
2061//*****************************************************************************
2062 int tNMEA2000::FindSourceDeviceIndex(unsigned char Source) const {
2063 int i=DeviceCount;
2064
2065 if ( Source<=253 ) {
2066 for (i=0; i<DeviceCount && Devices[i].N2kSource!=Source; i++);
2067 }
2068
2069 return (i<DeviceCount?i:-1);
2070 }
2071
2072//*****************************************************************************
2073bool tNMEA2000::IsMySource(unsigned char Source) {
2074 return (FindSourceDeviceIndex(Source)!=-1);
2075}
2076
2077//*****************************************************************************
2079 if ( !ForwardEnabled() || ( !( ForwardOwnMessages() && IsMySource(N2kMsg.Source) ) && N2kMode==N2km_NodeOnly ) ) return;
2080
2081 switch (ForwardType) {
2082 case fwdt_Actisense:
2084 break;
2085 case fwdt_Text:
2086 N2kMsg.Print(ForwardStream);
2087 break;
2088 }
2089}
2090
2091//*****************************************************************************
2093 if ( N2kCanMsg.KnownMessage || !ForwardOnlyKnownMessages() ) ForwardMessage(N2kCanMsg.N2kMsg);
2094}
2095
2096//*****************************************************************************
2097void tNMEA2000::SendIsoAddressClaim(unsigned char Destination, int DeviceIndex, unsigned long FromNow) {
2098
2099 // Some devices (Garmin) request constantly information on network about others
2100 // 59904 ISO Request: PGN = 60928
2101 // So we need to Re-send Address claim, or they will stop detecting us
2102 if (Destination==0xff && DeviceIndex==-1) DeviceIndex=0;
2103
2104 if ( DeviceIndex<0 || DeviceIndex>=DeviceCount) return;
2105
2106 // Set pending, if we have delayed it.
2107 if ( FromNow>0 ) {
2108 Devices[DeviceIndex].SetPendingIsoAddressClaim(FromNow);
2109 return;
2110 }
2111
2112 tN2kMsg RespondMsg(Devices[DeviceIndex].N2kSource);
2113
2114 RespondMsg.Destination=Destination;
2115 SetN2kISOAddressClaim(RespondMsg,Devices[DeviceIndex].DeviceInformation.GetName());
2116 SendMsg(RespondMsg,DeviceIndex);
2117}
2118
2119template <typename T> void PROGMEM_readAnything (const T * sce, T& dest)
2120 {
2121 memcpy_P (&dest, sce, sizeof (T));
2122 }
2123
2124#define MAX_PGNS_IN_LIST 74
2125
2126//*****************************************************************************
2127#if !defined(N2K_NO_ISO_MULTI_PACKET_SUPPORT)
2128void tNMEA2000::SendTxPGNList(unsigned char Destination, int DeviceIndex, bool UseTP) {
2129#else
2130void tNMEA2000::SendTxPGNList(unsigned char Destination, int DeviceIndex) {
2131#endif
2132 if (Destination==0xff && DeviceIndex==-1) DeviceIndex=0;
2133
2134 if ( !IsValidDevice(DeviceIndex) ) return;
2135
2136 tN2kMsg RespondMsg(Devices[DeviceIndex].N2kSource);
2137 unsigned long PGN;
2138
2139 RespondMsg.Destination=Destination;
2140 RespondMsg.SetPGN(126464L);
2141 RespondMsg.Priority=6;
2142#if !defined(N2K_NO_ISO_MULTI_PACKET_SUPPORT)
2143 RespondMsg.SetIsTPMessage(UseTP);
2144#endif
2145 RespondMsg.AddByte(N2kpgnl_transmit);
2146 // First add default messages
2147 size_t PGNCount=0;
2148 for (int i=0; (PGN=pgm_read_dword(&DefTransmitMessages[i]))!=0 && PGNCount<MAX_PGNS_IN_LIST; i++, PGNCount++ ) {
2149 RespondMsg.Add3ByteInt(PGN);
2150 }
2151 if (Devices[DeviceIndex].TransmitMessages!=0) {
2152 for (int i=0; (PGN=pgm_read_dword(&Devices[DeviceIndex].TransmitMessages[i]))!=0 && PGNCount<MAX_PGNS_IN_LIST; i++, PGNCount++ ) {
2153 RespondMsg.Add3ByteInt(PGN);
2154 }
2155 }
2156 SendMsg(RespondMsg,DeviceIndex);
2157}
2158
2159//*****************************************************************************
2160#if !defined(N2K_NO_ISO_MULTI_PACKET_SUPPORT)
2161void tNMEA2000::SendRxPGNList(unsigned char Destination, int DeviceIndex, bool UseTP) {
2162#else
2163void tNMEA2000::SendRxPGNList(unsigned char Destination, int DeviceIndex) {
2164#endif
2165 if (Destination==0xff && DeviceIndex==-1) DeviceIndex=0;
2166
2167 if ( !IsValidDevice(DeviceIndex) ) return;
2168
2169 tN2kMsg RespondMsg(Devices[DeviceIndex].N2kSource);
2170 unsigned long PGN;
2171
2172 RespondMsg.Destination=Destination;
2173 RespondMsg.SetPGN(126464L);
2174 RespondMsg.Priority=6;
2175#if !defined(N2K_NO_ISO_MULTI_PACKET_SUPPORT)
2176 RespondMsg.SetIsTPMessage(UseTP);
2177#endif
2178 RespondMsg.AddByte(N2kpgnl_receive);
2179 // First add default messages
2180 size_t PGNCount=0;
2181 for (int i=0; (PGN=pgm_read_dword(&DefReceiveMessages[i]))!=0 && PGNCount<MAX_PGNS_IN_LIST; i++, PGNCount++ ) {
2182 RespondMsg.Add3ByteInt(PGN);
2183 }
2184 if (Devices[DeviceIndex].ReceiveMessages!=0) {
2185 for (int i=0; (PGN=pgm_read_dword(&Devices[DeviceIndex].ReceiveMessages[i]))!=0 && PGNCount<MAX_PGNS_IN_LIST; i++, PGNCount++ ) {
2186 RespondMsg.Add3ByteInt(PGN);
2187 }
2188 }
2189 SendMsg(RespondMsg,DeviceIndex);
2190}
2191
2192//*****************************************************************************
2193void SetN2kPGN126996Progmem(tN2kMsg &N2kMsg, const tNMEA2000::tProductInformation *ProductInformation, char *OptionalSerialCode=0) {
2194 int i;
2195
2196 N2kMsg.SetPGN(126996L);
2197 N2kMsg.Priority=6;
2198 N2kMsg.Add2ByteInt(pgm_read_word(&ProductInformation->N2kVersion));
2199 N2kMsg.Add2ByteInt(pgm_read_word(&ProductInformation->ProductCode));
2200 for (i=0; i<Max_N2kModelID_len && pgm_read_byte(&ProductInformation->N2kModelID[i]); i++ ) { N2kMsg.AddByte(pgm_read_byte(&ProductInformation->N2kModelID[i])); }
2201 for (; i<Max_N2kModelID_len; i++ ) { N2kMsg.AddByte(0xff); }
2202 for (i=0; i<Max_N2kSwCode_len && pgm_read_byte(&ProductInformation->N2kSwCode[i]); i++ ) { N2kMsg.AddByte(pgm_read_byte(&ProductInformation->N2kSwCode[i])); }
2203 for (; i<Max_N2kSwCode_len; i++ ) { N2kMsg.AddByte(0xff); }
2204 for (i=0; i<Max_N2kModelVersion_len && pgm_read_byte(&ProductInformation->N2kModelVersion[i]); i++ ) { N2kMsg.AddByte(pgm_read_byte(&ProductInformation->N2kModelVersion[i])); }
2205 for (; i<Max_N2kModelVersion_len; i++ ) { N2kMsg.AddByte(0xff); }
2206 if (OptionalSerialCode) {
2207 for (i=0; i<Max_N2kModelSerialCode_len && OptionalSerialCode[i]; i++ ) { N2kMsg.AddByte(OptionalSerialCode[i]); }
2208 } else {
2209 for (i=0; i<Max_N2kModelSerialCode_len && pgm_read_byte(&ProductInformation->N2kModelSerialCode[i]); i++ ) { N2kMsg.AddByte(pgm_read_byte(&ProductInformation->N2kModelSerialCode[i])); }
2210 }
2211 for (; i<Max_N2kModelSerialCode_len; i++ ) { N2kMsg.AddByte(0xff); }
2212 N2kMsg.AddByte(pgm_read_byte(&ProductInformation->CertificationLevel));
2213 N2kMsg.AddByte(pgm_read_byte(&ProductInformation->LoadEquivalency));
2214}
2215
2216//*****************************************************************************
2217#if !defined(N2K_NO_ISO_MULTI_PACKET_SUPPORT)
2219 return SendProductInformation(0xff,iDev,false);
2220}
2221
2222bool tNMEA2000::SendProductInformation(unsigned char Destination, int iDev, bool UseTP) {
2223#else
2224bool tNMEA2000::SendProductInformation(int iDev) {
2225#endif
2226 if ( !IsValidDevice(iDev) ) return false;
2227 tN2kMsg RespondMsg(Devices[iDev].N2kSource);
2228 int iPIDev=iDev;
2229
2230 if ( Devices[iPIDev].ProductInformation==0 ) iPIDev=0; // Use first device product information
2231 if ( Devices[iPIDev].ProductInformation==0 ) return false; // Can not do anything.
2232
2233 if ( Devices[iPIDev].ProductInformation==Devices[iPIDev].LocalProductInformation ) {
2234 SetN2kProductInformation(RespondMsg,Devices[iPIDev].ProductInformation->N2kVersion,
2242 } else {
2243 SetN2kPGN126996Progmem(RespondMsg,Devices[iPIDev].ProductInformation);
2244 }
2245#if !defined(N2K_NO_ISO_MULTI_PACKET_SUPPORT)
2246 RespondMsg.Destination=Destination;
2247 RespondMsg.SetIsTPMessage(UseTP);
2248#endif
2249 if (SendMsg(RespondMsg,iDev) ) {
2251 return true;
2252 }
2253
2255 return false;
2256}
2257
2258//*****************************************************************************
2259#if !defined(N2K_NO_ISO_MULTI_PACKET_SUPPORT)
2261 return SendConfigurationInformation(0xff,DeviceIndex,false);
2262}
2263
2264bool tNMEA2000::SendConfigurationInformation(unsigned char Destination, int DeviceIndex, bool UseTP) {
2265#else
2266bool tNMEA2000::SendConfigurationInformation(int DeviceIndex) {
2267#endif
2268 if ( !IsValidDevice(DeviceIndex) ) return false;
2269 tN2kMsg RespondMsg(Devices[DeviceIndex].N2kSource);
2270
2279 } else { // No information provided, so respond not available
2280 SetN2kPGNISOAcknowledgement(RespondMsg,1,0xff,126998L);
2281 }
2282
2283#if !defined(N2K_NO_ISO_MULTI_PACKET_SUPPORT)
2284 RespondMsg.Destination=Destination;
2285 RespondMsg.SetIsTPMessage(UseTP);
2286#endif
2287 if (SendMsg(RespondMsg,DeviceIndex) ) {
2289 return true;
2290 }
2291
2293 return false;
2294}
2295
2296//*****************************************************************************
2297void tNMEA2000::RespondISORequest(const tN2kMsg &N2kMsg, unsigned long RequestedPGN, int iDev) {
2298 if ( IsAddressClaimStarted(iDev) ) return; // We do not respond any queries during address claiming.
2299
2300 switch (RequestedPGN) {
2301 case 60928L: /*ISO Address Claim*/ // Someone is asking others to claim their addresses
2302 SendIsoAddressClaim(0xff,iDev);
2303 break;
2304 case 126464L:
2305 SendTxPGNList(N2kMsg.Source,iDev);
2306 SendRxPGNList(N2kMsg.Source,iDev);
2307 break;
2308 case 126996L: /* Product information */
2310 break;
2311 case 126998L: /* Configuration information */
2313 break;
2314 default:
2315 /* If user has established a handler */
2316 if (ISORqstHandler!=0) {
2317 /* and if it handled the request, we are done */
2318 if (ISORqstHandler(RequestedPGN,N2kMsg.Source,iDev)) {
2319 return;
2320 }
2321 }
2322
2323 tN2kMsg N2kMsgR;
2324 // No user handler, or there was one and it returned FALSE. Send NAK
2325 SetN2kPGNISOAcknowledgement(N2kMsgR,1,0xff,RequestedPGN);
2326 // Direct the response to original requester.
2327 N2kMsgR.Destination = N2kMsg.Source;
2328 SendMsg(N2kMsgR,iDev);
2329 }
2330}
2331
2332//*****************************************************************************
2334 unsigned long RequestedPGN;
2335 int iDev=FindSourceDeviceIndex(N2kMsg.Destination);
2336
2337 if ( !tNMEA2000::IsBroadcast(N2kMsg.Destination) && iDev==-1) return; // if destination is not for us, we do nothing
2338
2339 ParseN2kPGNISORequest(N2kMsg,RequestedPGN);
2340 N2kMsgDbgStart("ISO request: "); N2kMsgDbgln(RequestedPGN);
2341 if (tNMEA2000::IsBroadcast(N2kMsg.Destination)) { // broadcast -> respond from all devices
2342 for (iDev=0; iDev<DeviceCount; iDev++) RespondISORequest(N2kMsg,RequestedPGN,iDev);
2343 } else {
2344 RespondISORequest(N2kMsg,RequestedPGN,iDev);
2345 }
2346}
2347
2348#if !defined(N2K_NO_GROUP_FUNCTION_SUPPORT)
2349//*****************************************************************************
2350// Document https://web.archive.org/web/20170609033039/http://www.nmea.org/Assets/20140109%20nmea-2000-corrigendum-tc201401031%20pgn%20126208.pdf
2351// defines that systems should respond to NMEA Request/Command/Acknowledge group function PGN 126208.
2352// Here we first call callback and if that will not handle function, we use default handler.
2353void tNMEA2000::RespondGroupFunction(const tN2kMsg &N2kMsg, tN2kGroupFunctionCode GroupFunctionCode, unsigned long PGNForGroupFunction, int iDev) {
2354 // Find group function handler for PGN
2355 tN2kGroupFunctionHandler *pGroupFunctionHandler;
2356 for ( pGroupFunctionHandler=pGroupFunctionHandlers; pGroupFunctionHandler!=0; pGroupFunctionHandler=pGroupFunctionHandler->pNext) {
2357 if ( pGroupFunctionHandler->PGN==PGNForGroupFunction ) {
2358 // For matching PGN we run handler and exit always
2359 pGroupFunctionHandler->Handle(N2kMsg,GroupFunctionCode, PGNForGroupFunction,iDev);
2360 return;
2361 } else if ( pGroupFunctionHandler->PGN==0 &&
2362 pGroupFunctionHandler->Handle(N2kMsg,GroupFunctionCode, PGNForGroupFunction,iDev)
2363 ) {
2364 // If handler PGN is 0, we try handler and exit, if it does it. Default handler has PGN=0, but
2365 // it is at end of list. This allows user to add handlers, which tries to handle all PGNs.
2366 return;
2367 }
2368 }
2369}
2370
2371//*****************************************************************************
2372// Document https://web.archive.org/web/20170609033039/http://www.nmea.org/Assets/20140109%20nmea-2000-corrigendum-tc201401031%20pgn%20126208.pdf
2373// defines that systems should respond to NMEA Request/Command/Acknowledge group function PGN 126208.
2374// On the document it is not clear can request be send as broadcast, so we handle it, if we can.
2376 tN2kGroupFunctionCode GroupFunctionCode;
2377 unsigned long PGNForGroupFunction;
2378 int iDev=FindSourceDeviceIndex(N2kMsg.Destination);
2379
2380 if ( !tNMEA2000::IsBroadcast(N2kMsg.Destination) && iDev==-1) return; // if destination is not for us, we do nothing
2381
2382 if (!tN2kGroupFunctionHandler::Parse(N2kMsg,GroupFunctionCode,PGNForGroupFunction)) return;
2383 N2kMsgDbgStart("Group function: "); N2kMsgDbgln(PGNForGroupFunction);
2384 if ( tNMEA2000::IsBroadcast(N2kMsg.Destination) ) { // broadcast -> respond from all devices
2385 for (iDev=0; iDev<DeviceCount; iDev++) RespondGroupFunction(N2kMsg,GroupFunctionCode,PGNForGroupFunction,iDev);
2386 } else {
2387 RespondGroupFunction(N2kMsg,GroupFunctionCode,PGNForGroupFunction,iDev);
2388 }
2389}
2390#endif
2391
2392//*****************************************************************************
2394 if ( IsReadyToSend() ) { // Start address claim automatically
2397 ForwardStream->print(F("Start address claim for device "));
2398 ForwardStream->println(iDev);
2399 }
2400 SendIsoAddressClaim(0xff,iDev);
2402 }
2403}
2404
2405//*****************************************************************************
2407 for (int i=0; i<DeviceCount; i++) {
2408 if ( Devices[i].N2kSource==N2kNullCanBusAddress ) GetNextAddress(i,true); // On restart try address claiming from the beginning
2410 }
2411}
2412
2413//*****************************************************************************
2415 // Reset address claim after timeout
2416 bool result=Devices[iDev].AddressClaimTimer.IsEnabled();
2417 if ( result ) {
2418 if ( Devices[iDev].AddressClaimTimer.IsTime() ) {
2420 result=false;
2421 // We have claimed our address, so save end source for next possible claim run.
2423 }
2424 }
2425
2426 return result;
2427}
2428
2429//*****************************************************************************
2431 int iDev=FindSourceDeviceIndex(N2kMsg.Source);
2432 if ( N2kMsg.Source==N2kNullCanBusAddress || iDev==-1 ) return; // if the address is not same as we have, we do nothing
2433
2434 int Index=0;
2435 uint64_t CallerName=N2kMsg.GetUInt64(Index);
2436
2437 if (Devices[iDev].DeviceInformation.GetName()<CallerName) { // We can keep our address, so just reclaim it
2438 SendIsoAddressClaim(0xff,iDev);
2439 } else { // we have to try an other address
2440 if ( Devices[iDev].DeviceInformation.GetName()==CallerName && IsAddressClaimStarted(iDev) ) {
2441 // If the name is same, then the first instance will get claim and change its address.
2442 // This should not happen, if user takes care of setting unique ID for device information.
2443 // If he does not there is no problem with this class, but e.g. Garmin gets crazy.
2444 // Try to solve situation by changing our device instance.
2445 Devices[iDev].DeviceInformation.SetDeviceInstance(Devices[iDev].DeviceInformation.GetDeviceInstance()+1);
2447 } else {
2448 GetNextAddress(iDev);
2449 }
2450 StartAddressClaim(iDev);
2451 }
2452}
2453
2454//*****************************************************************************
2455void tNMEA2000::HandleCommandedAddress(uint64_t CommandedName, unsigned char NewAddress, int iDev) {
2456 if ( IsBroadcast(NewAddress) ) return;
2457 if (Devices[iDev].DeviceInformation.GetName() == CommandedName &&
2458 Devices[iDev].N2kSource!=NewAddress) { // We have been commanded to set our address
2459 Devices[iDev].N2kSource=NewAddress;
2461 StartAddressClaim(iDev);
2462 AddressChanged=true;
2463 }
2464}
2465
2466//*****************************************************************************
2468 N2kMsgDbgStart(" Commanded address:"); N2kMsgDbgln(N2kMsg.Destination);
2469
2470 if ( N2kMsg.PGN!=65240L || !N2kMsg.IsTPMessage() || N2kMsg.DataLen!=9 ) return;
2471
2472 int iDev=FindSourceDeviceIndex(N2kMsg.Destination);
2473 if ( !tNMEA2000::IsBroadcast(N2kMsg.Destination) && iDev==-1) return; // if destination is not for us, we do nothing
2474
2475 int Index=0;
2476 uint64_t CommandedName=N2kMsg.GetUInt64(Index);
2477 unsigned char NewAddress=N2kMsg.GetByte(Index);
2478 if ( NewAddress>=252 ) return;
2479
2480 if ( iDev==-1 ) {
2481 for (iDev=0; iDev<DeviceCount; iDev++) HandleCommandedAddress(CommandedName,NewAddress,iDev);
2482 } else {
2483 HandleCommandedAddress(CommandedName,NewAddress,iDev);
2484 }
2485}
2486
2487//*****************************************************************************
2488void tNMEA2000::SetN2kSource(unsigned char _iAddr, int _iDev) {
2489 if ( !IsValidDevice(_iDev) || IsInitialized() ) return;
2490 InitDevices();
2491 Devices[_iDev].N2kSource= _iAddr;
2493}
2494
2495//*****************************************************************************
2497 bool result=AddressChanged;
2498
2499 AddressChanged=false;
2500 return result;
2501}
2502
2503//*****************************************************************************
2505 bool result=DeviceInformationChanged;
2506
2508 return result;
2509}
2510
2511//*****************************************************************************
2512void tNMEA2000::GetNextAddress(int DeviceIndex, bool RestartAtEnd) {
2513 bool FoundSame;
2514 // Currently simply add address
2515 // Note that 251 is the last source. We do not send data if address is higher than that.
2516
2517 do {
2518 if ( Devices[DeviceIndex].N2kSource==N2kNullCanBusAddress ) {
2519 if ( RestartAtEnd ) {
2520 // For null address start from beginning.
2521 Devices[DeviceIndex].N2kSource=14;
2522 Devices[DeviceIndex].UpdateAddressClaimEndSource();
2523 } else return;
2524 } else if (Devices[DeviceIndex].N2kSource!=Devices[DeviceIndex].AddressClaimEndSource) {
2525 Devices[DeviceIndex].N2kSource++;
2526 // Roll to start?
2527 if ( Devices[DeviceIndex].N2kSource>N2kMaxCanBusAddress ) Devices[DeviceIndex].N2kSource=0;
2528 } else {
2529 Devices[DeviceIndex].N2kSource=N2kNullCanBusAddress; // Force null address = cannot claim address
2530 AddressChanged=true;
2531 return;
2532 }
2533 FoundSame=false;
2534 // Check that we do not have same on our list
2535 for (int i=0; i<DeviceCount && !FoundSame; i++) {
2536 if (i!=DeviceIndex) FoundSame=(Devices[DeviceIndex].N2kSource==Devices[i].N2kSource);
2537 }
2538 } while (FoundSame);
2539 AddressChanged=true;
2540}
2541
2542//*****************************************************************************
2544 bool result=false;
2545
2546 if ( N2kMode==N2km_SendOnly || N2kMode==N2km_ListenAndSend ) return result;
2547
2548 if ( N2kCANMsgBuf[MsgIndex].SystemMessage ) {
2549 if ( ForwardSystemMessages() ) ForwardMessage(N2kCANMsgBuf[MsgIndex].N2kMsg);
2550 if ( N2kMode!=N2km_ListenOnly ) { // Note that in listen only mode we will not inform us to the bus
2551 switch (N2kCANMsgBuf[MsgIndex].N2kMsg.PGN) {
2552 case 59392L: /*ISO Acknowledgement*/
2553 break;
2554 case 59904L: /*ISO Request*/
2555 HandleISORequest(N2kCANMsgBuf[MsgIndex].N2kMsg);
2556 break;
2557 case 60928L: /*ISO Address Claim*/
2558 HandleISOAddressClaim(N2kCANMsgBuf[MsgIndex].N2kMsg);
2559 break;
2560 case 65240L: /*Commanded Address*/
2561 HandleCommandedAddress(N2kCANMsgBuf[MsgIndex].N2kMsg);
2562 break;
2563#if !defined(N2K_NO_GROUP_FUNCTION_SUPPORT)
2564 case 126208L: /*NMEA Request/Command/Acknowledge group function*/
2565 HandleGroupFunction(N2kCANMsgBuf[MsgIndex].N2kMsg);
2566 break;
2567#endif
2568 }
2569 }
2570 result=true;
2571 }
2572
2573 return result;
2574}
2575
2576//*****************************************************************************
2578 unsigned long canId;
2579 unsigned char len = 0;
2580 unsigned char buf[8];
2581 uint8_t MsgIndex;
2582 static const int MaxReadFramesOnParse=20;
2583 int FramesRead=0;
2584// tN2kMsg N2kMsg;
2585
2586 if ( OpenState!=os_Open ) {
2587 if ( !(Open() && OpenState==os_Open) ) return; // Can not do much
2588 }
2589
2590 if (dbMode != dm_None) return; // No much to do here, when in Debug mode
2591
2592 SendFrames();
2594#if defined(DEBUG_NMEA2000_ISR)
2595 TestISR();
2596#endif
2597
2598 while (FramesRead<MaxReadFramesOnParse && CANGetFrame(canId,len,buf) ) { // check if data coming
2599 FramesRead++;
2600 N2kMsgDbgStart("Received frame, can ID:"); N2kMsgDbg(canId); N2kMsgDbg(" len:"); N2kMsgDbg(len); N2kMsgDbg(" data:"); DbgPrintBuf(len,buf,false); N2kMsgDbgln();
2601 MsgIndex=SetN2kCANBufMsg(canId,len,buf);
2602 if (MsgIndex<MaxN2kCANMsgs) {
2603 if ( !HandleReceivedSystemMessage(MsgIndex) ) {
2604 N2kMsgDbgStart(" - Non system message, MsgIndex: "); N2kMsgDbgln(MsgIndex);
2605 ForwardMessage(N2kCANMsgBuf[MsgIndex]);
2606 }
2607// N2kCANMsgBuf[MsgIndex].N2kMsg.Print(Serial);
2608 RunMessageHandlers(N2kCANMsgBuf[MsgIndex].N2kMsg);
2609 N2kCANMsgBuf[MsgIndex].FreeMessage();
2610 N2kMsgDbgStart(" - Free message, MsgIndex: "); N2kMsgDbg(MsgIndex); N2kMsgDbgln();
2611 }
2612 }
2613
2614#if !defined(N2K_NO_HEARTBEAT_SUPPORT)
2615 SendHeartbeat();
2616#endif
2617}
2618
2619//*****************************************************************************
2621 if ( MsgHandler!=0 ) MsgHandler(N2kMsg);
2622
2624 // Loop through all PGN handlers
2625 for ( ;MsgHandler!=0 && MsgHandler->GetPGN()==0; MsgHandler=MsgHandler->pNext) MsgHandler->HandleMsg(N2kMsg);
2626 // Loop through specific PGN handlers
2627 for ( ;MsgHandler!=0 && MsgHandler->GetPGN()<=N2kMsg.PGN; MsgHandler=MsgHandler->pNext) {
2628 if ( MsgHandler->GetPGN()==N2kMsg.PGN ) MsgHandler->HandleMsg(N2kMsg);
2629 }
2630}
2631
2632//*****************************************************************************
2633void tNMEA2000::SetOnOpen(void (*_OnOpen)()) {
2634 OnOpen=_OnOpen;
2635}
2636
2637//*****************************************************************************
2638void tNMEA2000::SetMsgHandler(void (*_MsgHandler)(const tN2kMsg &N2kMsg)) {
2639 MsgHandler=_MsgHandler;
2640}
2641
2642//*****************************************************************************
2644 if ( _MsgHandler==0 ) return;
2645
2646 if ( _MsgHandler->pNMEA2000==this ) return; // Already attached
2647
2648 DetachMsgHandler(_MsgHandler);
2649
2650 if ( MsgHandlers==0 ) {
2651 MsgHandlers=_MsgHandler;
2652 } else {
2654 if ( MsgHandler->GetPGN()>_MsgHandler->GetPGN() ) { // Add to first
2655 _MsgHandler->pNext=MsgHandler;
2656 MsgHandlers=_MsgHandler;
2657 } else {
2658 for ( ; MsgHandler->pNext!=0 && MsgHandler->pNext->GetPGN()<_MsgHandler->GetPGN(); MsgHandler=MsgHandler->pNext );
2659 _MsgHandler->pNext=MsgHandler->pNext;
2660 MsgHandler->pNext=_MsgHandler;
2661 }
2662 }
2663
2664 _MsgHandler->pNMEA2000=this;
2665}
2666
2667//*****************************************************************************
2669 if ( _MsgHandler==0 || _MsgHandler->pNMEA2000==0 ) return;
2670
2671 tMsgHandler *MsgHandler=_MsgHandler->pNMEA2000->MsgHandlers;
2672
2673 if ( MsgHandler==_MsgHandler ) { // Is this at first
2674 _MsgHandler->pNMEA2000->MsgHandlers=MsgHandler->pNext;
2675 } else {
2676 for ( ; MsgHandler!=0 && MsgHandler->pNext!=_MsgHandler; MsgHandler=MsgHandler->pNext );
2677 if ( MsgHandler!=0 ) MsgHandler->pNext=_MsgHandler->pNext;
2678 }
2679 _MsgHandler->pNext=0;
2680 _MsgHandler->pNMEA2000=0;
2681}
2682
2683//*****************************************************************************
2684void tNMEA2000::SetISORqstHandler(bool(*ISORequestHandler)(unsigned long RequestedPGN, unsigned char Requester, int DeviceIndex)) {
2685 ISORqstHandler=ISORequestHandler;
2686}
2687
2688#if !defined(N2K_NO_GROUP_FUNCTION_SUPPORT)
2689//*****************************************************************************
2691 if (pGroupFunctionHandler==0 || pGroupFunctionHandlers==0 ) return;
2692
2693 tN2kGroupFunctionHandler* pPrevGroupFunctionHandler=pGroupFunctionHandlers;
2694 // Handle, if first
2695 if ( pPrevGroupFunctionHandler==pGroupFunctionHandler ) {
2696 pGroupFunctionHandlers=pPrevGroupFunctionHandler->pNext;
2697 } else {
2698 for ( ;pPrevGroupFunctionHandler!=0 && pPrevGroupFunctionHandler->pNext!=pGroupFunctionHandler;
2699 pPrevGroupFunctionHandler = pPrevGroupFunctionHandler->pNext);
2700 if ( pPrevGroupFunctionHandler!=0 && pPrevGroupFunctionHandler->pNext==pGroupFunctionHandler ) {
2701 pPrevGroupFunctionHandler->pNext=pGroupFunctionHandler->pNext;
2702 }
2703 }
2704 pGroupFunctionHandler->pNext=0;
2705}
2706
2707//*****************************************************************************
2709 if (pGroupFunctionHandler==0) return;
2710 RemoveGroupFunctionHandler(pGroupFunctionHandler);
2711 // Add to the end on the list
2712 if ( pGroupFunctionHandlers==0 ) { // If there is none set, put it to first
2713 pGroupFunctionHandlers=pGroupFunctionHandler;
2714 } else {
2715 tN2kGroupFunctionHandler* pLastGroupFunctionHandler;
2716 // find last
2717 for (pLastGroupFunctionHandler = pGroupFunctionHandlers;
2718 pLastGroupFunctionHandler->pNext != 0 && pLastGroupFunctionHandler->pNext->PGN != 0;
2719 pLastGroupFunctionHandler = pLastGroupFunctionHandler->pNext);
2720 // Insert the new handler before the default handler if the default handler is present.
2721 if ( pLastGroupFunctionHandler->pNext != 0 && pLastGroupFunctionHandler->pNext->PGN == 0 ) {
2722 pGroupFunctionHandler->pNext = pLastGroupFunctionHandler->pNext;
2723 }
2724 // Add the new handler to the list.
2725 pLastGroupFunctionHandler->pNext = pGroupFunctionHandler;
2726 }
2727}
2728#endif
2729
2730//*****************************************************************************
2732void SetN2kPGN59392(tN2kMsg &N2kMsg, unsigned char Control, unsigned char GroupFunction, unsigned long PGN) {
2733 N2kMsg.SetPGN(59392L);
2734 N2kMsg.Priority=6;
2735
2736 N2kMsg.AddByte(Control);
2737 N2kMsg.AddByte(GroupFunction);
2738 N2kMsg.AddByte(0xff); // Reserved
2739 N2kMsg.AddByte(0xff); // Reserved
2740 N2kMsg.AddByte(0xff); // Reserved
2741 N2kMsg.Add3ByteInt(PGN);
2742}
2743
2744//*****************************************************************************
2745// ISO Address Claim
2746void SetN2kPGN60928(tN2kMsg &N2kMsg, unsigned long UniqueNumber, int ManufacturerCode,
2747 unsigned char DeviceFunction, unsigned char DeviceClass,
2748 unsigned char DeviceInstance, unsigned char SystemInstance, unsigned char IndustryGroup
2749 ) {
2751 N2kMsg.Priority=6;
2752
2753 N2kMsg.Add4ByteUInt((UniqueNumber&0x1FFFFF) | ((unsigned long)(ManufacturerCode&0x7ff))<<21);
2754 N2kMsg.AddByte(DeviceInstance);
2755 N2kMsg.AddByte(DeviceFunction);
2756 N2kMsg.AddByte((DeviceClass&0x7f)<<1);
2757 N2kMsg.AddByte( 0x80 | ((IndustryGroup&0x7)<<4) | (SystemInstance&0x0f) );
2758}
2759
2760//*****************************************************************************
2761// ISO Address Claim
2762void SetN2kPGN60928(tN2kMsg &N2kMsg, uint64_t Name) {
2764 N2kMsg.Priority=6;
2765
2766 N2kMsg.AddUInt64(Name);
2767}
2768
2769//*****************************************************************************
2770// Product Information
2771void SetN2kPGN126996(tN2kMsg &N2kMsg, unsigned int N2kVersion, unsigned int ProductCode,
2772 const char *ModelID, const char *SwCode,
2773 const char *ModelVersion, const char *ModelSerialCode,
2774 unsigned char CertificationLevel, unsigned char LoadEquivalency) {
2775
2777 N2kMsg.Priority=6;
2778 N2kMsg.Add2ByteUInt(N2kVersion);
2779 N2kMsg.Add2ByteUInt(ProductCode);
2780 N2kMsg.AddStr(ModelID, Max_N2kModelID_len);
2781 N2kMsg.AddStr(SwCode, Max_N2kSwCode_len);
2782 N2kMsg.AddStr(ModelVersion, Max_N2kModelVersion_len);
2783 N2kMsg.AddStr(ModelSerialCode, Max_N2kModelSerialCode_len);
2784 N2kMsg.AddByte(CertificationLevel);
2785 N2kMsg.AddByte(LoadEquivalency);
2786}
2787
2788bool ParseN2kPGN126996(const tN2kMsg& N2kMsg, unsigned short &N2kVersion, unsigned short &ProductCode,
2789 int ModelIDSize, char *ModelID, int SwCodeSize, char *SwCode,
2790 int ModelVersionSize, char *ModelVersion, int ModelSerialCodeSize, char *ModelSerialCode,
2791 unsigned char &CertificationLevel, unsigned char &LoadEquivalency) {
2792 if (N2kMsg.PGN!=N2kPGNProductInformation) return false;
2793
2794 int Index=0;
2795 N2kVersion=N2kMsg.Get2ByteUInt(Index);
2796 ProductCode=N2kMsg.Get2ByteUInt(Index);
2797 N2kMsg.GetStr(ModelIDSize,ModelID,Max_N2kModelID_len,0xff,Index);
2798 N2kMsg.GetStr(SwCodeSize,SwCode,Max_N2kSwCode_len,0xff,Index);
2799 N2kMsg.GetStr(ModelVersionSize,ModelVersion,Max_N2kModelVersion_len,0xff,Index);
2800 N2kMsg.GetStr(ModelSerialCodeSize,ModelSerialCode,Max_N2kModelSerialCode_len,0xff,Index);
2801 CertificationLevel=N2kMsg.GetByte(Index);
2802 LoadEquivalency=N2kMsg.GetByte(Index);
2803
2804 return true;
2805}
2806
2807//*****************************************************************************
2808size_t ProgmemStrLen(const char *str) {
2809 size_t len;
2810 if (str==0) return 0;
2811 for (len=0; pgm_read_byte(&(str[len]))!=0; len++ );
2812 return len;
2813}
2814
2815//*****************************************************************************
2816size_t StrLen(const char *str) {
2817 if (str==0) return 0;
2818 return strlen(str);
2819}
2820
2821//*****************************************************************************
2822// Configuration Information
2824 const char *ManufacturerInformation,
2825 const char *InstallationDescription1,
2826 const char *InstallationDescription2,
2827 bool UsePgm) {
2828 size_t TotalLen;
2829 size_t MaxLen=tN2kMsg::MaxDataLen-6; // Each field has 2 extra bytes
2830 size_t ManInfoLen;
2831 size_t InstDesc1Len;
2832 size_t InstDesc2Len;
2833
2834 if ( UsePgm ) {
2835 ManInfoLen=ProgmemStrLen(ManufacturerInformation);
2836 InstDesc1Len=ProgmemStrLen(InstallationDescription1);
2837 InstDesc2Len=ProgmemStrLen(InstallationDescription2);
2838 } else {
2839 ManInfoLen=StrLen(ManufacturerInformation);
2840 InstDesc1Len=StrLen(InstallationDescription1);
2841 InstDesc2Len=StrLen(InstallationDescription2);
2842 }
2843
2847
2848 TotalLen=0;
2849 if (TotalLen+ManInfoLen>MaxLen) ManInfoLen=MaxLen-TotalLen;
2850 TotalLen+=ManInfoLen;
2851 if (TotalLen+InstDesc1Len>MaxLen) InstDesc1Len=MaxLen-TotalLen;
2852 TotalLen+=InstDesc1Len;
2853 if (TotalLen+InstDesc2Len>MaxLen) InstDesc2Len=MaxLen-TotalLen;
2854 TotalLen+=InstDesc2Len;
2855
2857 N2kMsg.Priority=6;
2858 // InstallationDescription1
2859 N2kMsg.AddByte(InstDesc1Len+2);
2860 N2kMsg.AddByte(0x01);
2861 N2kMsg.AddStr(InstallationDescription1,InstDesc1Len,UsePgm);
2862
2863 // InstallationDescription2
2864 N2kMsg.AddByte(InstDesc2Len+2);
2865 N2kMsg.AddByte(0x01);
2866 N2kMsg.AddStr(InstallationDescription2,InstDesc2Len,UsePgm);
2867 // ManufacturerInformation
2868 N2kMsg.AddByte(ManInfoLen+2);
2869 N2kMsg.AddByte(0x01);
2870 N2kMsg.AddStr(ManufacturerInformation,ManInfoLen,UsePgm);
2871}
2872
2873bool ParseN2kPGN126998(const tN2kMsg& N2kMsg,
2874 size_t &ManufacturerInformationSize, char *ManufacturerInformation,
2875 size_t &InstallationDescription1Size, char *InstallationDescription1,
2876 size_t &InstallationDescription2Size, char *InstallationDescription2) {
2877 if (N2kMsg.PGN!=N2kPGNConfigurationInformation) return false;
2878
2879 int Index=0;
2880 return ( N2kMsg.GetVarStr(InstallationDescription1Size,InstallationDescription1,Index) &&
2881 N2kMsg.GetVarStr(InstallationDescription2Size,InstallationDescription2,Index) &&
2882 N2kMsg.GetVarStr(ManufacturerInformationSize,ManufacturerInformation,Index) );
2883}
2884
2885//*****************************************************************************
2886// ISO Request
2887void SetN2kPGN59904(tN2kMsg &N2kMsg, uint8_t Destination, unsigned long RequestedPGN) {
2888 N2kMsg.SetPGN(59904L);
2889 N2kMsg.Destination=Destination;
2890 N2kMsg.Priority=6;
2891 N2kMsg.Add3ByteInt(RequestedPGN);
2892}
2893
2894bool ParseN2kPGN59904(const tN2kMsg &N2kMsg, unsigned long &RequestedPGN) {
2895 int result=((N2kMsg.DataLen>=3) && (N2kMsg.DataLen<=8));
2896 RequestedPGN=0;
2897 if (result) {
2898 int Index=0;
2899 RequestedPGN=N2kMsg.Get3ByteUInt(Index);
2900 }
2901
2902 return result;
2903}
2904
2905//*****************************************************************************
2906// PGN List (Transmit and Receive)
2907void SetN2kPGN126464(tN2kMsg &N2kMsg, uint8_t Destination, tN2kPGNList tr, const unsigned long *PGNs) {
2908 unsigned long PGN;
2909
2910 N2kMsg.SetPGN(126464L);
2911 N2kMsg.Destination=Destination;
2912 N2kMsg.Priority=6;
2913 N2kMsg.AddByte(tr);
2914
2915 for (int i=0; (PGN=pgm_read_dword(&PGNs[i]))!=0; i++) {
2916 N2kMsg.Add3ByteInt(PGN);
2917 }
2918}
2919
2920#if !defined(N2K_NO_HEARTBEAT_SUPPORT)
2921//*****************************************************************************
2922// Heartbeat
2923void SetN2kPGN126993(tN2kMsg &N2kMsg, uint32_t timeInterval_ms, uint8_t sequenceCounter) {
2924 N2kMsg.SetPGN(126993L);
2925 N2kMsg.Priority=7;
2926 if ( timeInterval_ms>MaxHeartbeatInterval ) {
2927 N2kMsg.Add2ByteUInt(0xfffe); // Error
2928 } else {
2929 N2kMsg.Add2ByteUInt((uint16_t)(timeInterval_ms));
2930 }
2931 N2kMsg.AddByte(sequenceCounter);
2932 N2kMsg.AddByte(0xff); // Reserved
2933 N2kMsg.Add4ByteUInt(0xffffffff); // Reserved
2934}
2935#endif
Type definitions and utility macros used in the NMEA2000 libraries.
#define pgm_read_byte(var)
Definition: N2kDef.h:46
#define F(str)
Definition: N2kDef.h:64
#define pgm_read_dword(var)
Definition: N2kDef.h:48
#define PROGMEM
Definition: N2kDef.h:45
#define pgm_read_word(var)
Definition: N2kDef.h:47
tN2kGroupFunctionCode
FunctionCode for the group function.
The file contains default group function handler classes.
uint16_t GetBuf2ByteUInt(int &index, const unsigned char *buf)
Extracts 2 bytes out of the given buffer and converts it to an integer value.
Definition: N2kMsg.cpp:630
uint32_t GetBuf3ByteUInt(int &index, const unsigned char *buf)
Extracts 3 bytes out of the given buffer and converts it to an integer value.
Definition: N2kMsg.cpp:635
uint32_t N2kMillis()
Definition: N2kTimer.cpp:48
bool N2kHasElapsed(uint32_t Start, uint32_t Elapsed, uint32_t Now=N2kMillis())
Has time elapsed since start.
Definition: N2kTimer.h:111
bool N2kIsTimeBefore(uint32_t T1, uint32_t T2)
Comparing 2 values even after 32 bit time roll over situation.
Definition: N2kTimer.h:96
#define N2kMsgDbg(fmt, args...)
Definition: NMEA2000.cpp:77
size_t ProgmemStrLen(const char *str)
Definition: NMEA2000.cpp:2808
#define N2kFrameErrDbgln(fmt, args...)
Definition: NMEA2000.cpp:48
#define N2kFrameErrDbg(fmt, args...)
Definition: NMEA2000.cpp:47
#define N2kMsgDbgln(fmt, args...)
Definition: NMEA2000.cpp:78
#define DbgPrintBuf(len, buf, addln)
Definition: NMEA2000.cpp:84
bool IsSingleFrameSystemMessage(unsigned long PGN)
Checks if the given PGN is a Single Frame System Message.
Definition: NMEA2000.cpp:215
bool IsDefaultFastPacketMessage(unsigned long PGN)
Checks if the PGN is a Default Fast Packet Message.
Definition: NMEA2000.cpp:448
bool IsFastPacketSystemMessage(unsigned long PGN)
Checks if the given PGN is a Fast Packet System Message.
Definition: NMEA2000.cpp:234
#define MAX_PGNS_IN_LIST
Definition: NMEA2000.cpp:2124
#define N2kDbg(fmt, args...)
Definition: NMEA2000.cpp:91
bool ParseN2kPGN126996(const tN2kMsg &N2kMsg, unsigned short &N2kVersion, unsigned short &ProductCode, int ModelIDSize, char *ModelID, int SwCodeSize, char *SwCode, int ModelVersionSize, char *ModelVersion, int ModelSerialCodeSize, char *ModelSerialCode, unsigned char &CertificationLevel, unsigned char &LoadEquivalency)
Parsing the content of message PGN 126996 "Product information".
Definition: NMEA2000.cpp:2788
#define N2kFrameOutDbgStart(fmt, args...)
Definition: NMEA2000.cpp:66
void SetN2kPGN126993(tN2kMsg &N2kMsg, uint32_t timeInterval_ms, uint8_t sequenceCounter)
Setting up PGN 126993 Message "Heartbeat".
Definition: NMEA2000.cpp:2923
bool IsProprietaryFastPacketMessage(unsigned long PGN)
Checks if the PGN is a Proprietary Fast Packet Message.
Definition: NMEA2000.cpp:579
#define TP_CM_BAM
Multi packet connection management, Broadcast Announce Message.
Definition: NMEA2000.cpp:121
#define TP_MAX_FRAMES
Max frames, which can be received at time.
Definition: NMEA2000.cpp:115
const char DefInstallationDescription1[]
Default Installation Description (Field1)
Definition: NMEA2000.cpp:620
#define TP_CM_Abort
Multi packet connection management, Abort Connection.
Definition: NMEA2000.cpp:129
#define TP_CM_AbortTimeout
A timeout occurred and this is the connection abort to close the session.
Definition: NMEA2000.cpp:139
const tNMEA2000::tProductInformation DefProductInformation
Default Product Information.
Definition: NMEA2000.cpp:602
void SetN2kPGN126464(tN2kMsg &N2kMsg, uint8_t Destination, tN2kPGNList tr, const unsigned long *PGNs)
Setting up PGN 126464 Message "PGN List - Transmit PGNs group function".
Definition: NMEA2000.cpp:2907
#define TP_DT
Multi packet data transfer.
Definition: NMEA2000.cpp:119
#define N2kDbgln(fmt, args...)
Definition: NMEA2000.cpp:92
unsigned long N2ktoCanID(unsigned char priority, unsigned long PGN, unsigned long Source, unsigned char Destination)
Convert NMEA2000 values into a CAN Id.
Definition: NMEA2000.cpp:1306
bool ParseN2kPGN126998(const tN2kMsg &N2kMsg, size_t &ManufacturerInformationSize, char *ManufacturerInformation, size_t &InstallationDescription1Size, char *InstallationDescription1, size_t &InstallationDescription2Size, char *InstallationDescription2)
Parsing the content of message PGN 126998 "Configuration information".
Definition: NMEA2000.cpp:2873
#define N2kFrameInDbg(fmt, args...)
Definition: NMEA2000.cpp:57
#define MaxHeartbeatInterval
Maximum value for the ISO Heartbeat interval in ms.
Definition: NMEA2000.cpp:112
void PROGMEM_readAnything(const T *sce, T &dest)
Definition: NMEA2000.cpp:2119
void SetN2kPGN126996Progmem(tN2kMsg &N2kMsg, const tNMEA2000::tProductInformation *ProductInformation, char *OptionalSerialCode=0)
Definition: NMEA2000.cpp:2193
void CanIdToN2k(unsigned long id, unsigned char &prio, unsigned long &pgn, unsigned char &src, unsigned char &dst)
Convert a CAN Id to NMEA2000 values.
Definition: NMEA2000.cpp:1277
#define N2kFrameOutDbgln(fmt, args...)
Definition: NMEA2000.cpp:68
bool IsMandatoryFastPacketMessage(unsigned long PGN)
Checks if the PGN is a Mandatory Fast Packet Message.
Definition: NMEA2000.cpp:318
bool IsFastPacketFirstFrame(unsigned char b)
Definition: NMEA2000.cpp:1971
void SetN2kPGN59904(tN2kMsg &N2kMsg, uint8_t Destination, unsigned long RequestedPGN)
Setting up PGN 59904 Message "ISO request".
Definition: NMEA2000.cpp:2887
#define N2kMsgDbgStart(fmt, args...)
Definition: NMEA2000.cpp:76
#define N2kFrameErrDbgStart(fmt, args...)
Definition: NMEA2000.cpp:46
#define TP_CM_CTS
Multi packet connection management, Clear To Send.
Definition: NMEA2000.cpp:125
void SetN2kPGN59392(tN2kMsg &N2kMsg, unsigned char Control, unsigned char GroupFunction, unsigned long PGN)
ISO Acknowledgement.
Definition: NMEA2000.cpp:2732
#define N2kPrintFreeMemory(a)
Definition: NMEA2000.cpp:106
void SetN2kPGN126998(tN2kMsg &N2kMsg, const char *ManufacturerInformation, const char *InstallationDescription1, const char *InstallationDescription2, bool UsePgm)
Setting up PGN 126998 Message "Configuration information".
Definition: NMEA2000.cpp:2823
#define N2kFrameInDbgStart(fmt, args...)
Definition: NMEA2000.cpp:56
bool ParseN2kPGN59904(const tN2kMsg &N2kMsg, unsigned long &RequestedPGN)
Parsing the content of message PGN 59904 "ISO request".
Definition: NMEA2000.cpp:2894
void SetN2kPGN60928(tN2kMsg &N2kMsg, unsigned long UniqueNumber, int ManufacturerCode, unsigned char DeviceFunction, unsigned char DeviceClass, unsigned char DeviceInstance, unsigned char SystemInstance, unsigned char IndustryGroup)
Setting up PGN 60928 Message "ISO Address Claim".
Definition: NMEA2000.cpp:2746
const unsigned long DefTransmitMessages[]
Default list of Transmit Messages.
Definition: NMEA2000.cpp:160
#define N2kAddressClaimTimeout
Timeout value for the ISO Address Claim in ms.
Definition: NMEA2000.cpp:110
void SetN2kPGN126996(tN2kMsg &N2kMsg, unsigned int N2kVersion, unsigned int ProductCode, const char *ModelID, const char *SwCode, const char *ModelVersion, const char *ModelSerialCode, unsigned char CertificationLevel, unsigned char LoadEquivalency)
Setting up PGN 126996 Message "Product information".
Definition: NMEA2000.cpp:2771
#define N2kFrameInDbgln(fmt, args...)
Definition: NMEA2000.cpp:58
#define TP_CM_AbortBusy
Already in one or more connection managed sessions and cannot support another.
Definition: NMEA2000.cpp:133
const char DefManufacturerInformation[]
Default Manufacturer Information.
Definition: NMEA2000.cpp:616
unsigned char TPCtsPackets(unsigned char nPackets)
Definition: NMEA2000.cpp:1704
const unsigned long DefReceiveMessages[]
Default list of Received Messages.
Definition: NMEA2000.cpp:194
#define TP_CM_RTS
Multi packet connection management, Request To Send.
Definition: NMEA2000.cpp:123
void CopyProgmemString(const char *str, size_t MaxLen, char *buf)
Definition: NMEA2000.cpp:864
bool IsDefaultSingleFrameMessage(unsigned long PGN)
Checks if the given PGN is a Default Single Frame Message.
Definition: NMEA2000.cpp:276
#define TP_CM
Multi packet connection management, TP.CM.
Definition: NMEA2000.cpp:117
void CopyBufToCANMsg(tN2kCANMsg &CANMsg, unsigned char start, unsigned char len, unsigned char *buf)
Copy a Buffer to a CAN Message.
Definition: NMEA2000.cpp:1626
size_t StrLen(const char *str)
Definition: NMEA2000.cpp:2816
#define TP_CM_ACK
Multi packet connection management, End of Message Acknowledgement.
Definition: NMEA2000.cpp:127
const char DefInstallationDescription2[]
Default Installation Description (Field2)
Definition: NMEA2000.cpp:624
This file contains the class tNMEA2000, which consists the main functionality of the library.
void SetN2kProductInformation(tN2kMsg &N2kMsg, unsigned int N2kVersion, unsigned int ProductCode, const char *ModelID, const char *SwCode, const char *ModelVersion, const char *ModelSerialCode, unsigned char CertificationLevel=1, unsigned char LoadEquivalency=1)
Setting up Message "Product information" - PGN 126996.
Definition: NMEA2000.h:3107
#define Max_N2kModelSerialCode_len
Max length of SerialCode Document says for length 32 but then values has not been translated right on...
Definition: NMEA2000.h:82
#define N2kPGNIsoAddressClaim
PGN for an ISO Address Claim message.
Definition: NMEA2000.h:59
#define N2kPGNProductInformation
PGN for a Production Information message.
Definition: NMEA2000.h:61
#define Max_N2kSwCode_len
Max length of Software Code Document says for length 40 but then values has not been translated right...
Definition: NMEA2000.h:74
void SetN2kConfigurationInformation(tN2kMsg &N2kMsg, const char *ManufacturerInformation, const char *InstallationDescription1=0, const char *InstallationDescription2=0, bool UsePgm=false)
Setting up Message "Configuration information" - PGN 126998.
Definition: NMEA2000.h:3169
#define DefaultHeartbeatInterval
Interval for Heartbeat.
Definition: NMEA2000.h:824
#define N2kMessageGroups
Number of message groups.
Definition: NMEA2000.h:104
#define Max_N2kMsgBuf_Time
Message buffer time.
Definition: NMEA2000.h:102
#define Max_N2kModelVersion_len
Max length of Model Version Document says for length 24 but then values has not been translated right...
Definition: NMEA2000.h:78
#define Max_N2kConfigurationInfoField_len
Max length of Configuration Info Fields.
Definition: NMEA2000.h:99
#define N2kPGNConfigurationInformation
PGN for an Configuration Information message.
Definition: NMEA2000.h:63
void SetN2kISOAddressClaim(tN2kMsg &N2kMsg, unsigned long UniqueNumber, int ManufacturerCode, unsigned char DeviceFunction, unsigned char DeviceClass, unsigned char DeviceInstance=0, unsigned char SystemInstance=0, unsigned char IndustryGroup=4)
Setting up Message "ISO Address Claim" - PGN 60928.
Definition: NMEA2000.h:3063
#define Max_N2kModelID_len
Max length of ModelID Document says for length 33 but then values has not been translated right on de...
Definition: NMEA2000.h:70
void SetHeartbeat(tN2kMsg &N2kMsg, uint32_t timeInterval_ms, uint8_t sequenceCounter)
Setting up Message "Heartbeat" - PGN 126993.
Definition: NMEA2000.h:3313
#define N2kNullCanBusAddress
Null Address (???)
Definition: NMEA2000.h:108
void SetN2kPGNISOAcknowledgement(tN2kMsg &N2kMsg, unsigned char Control, unsigned char GroupFunction, unsigned long PGN)
Setting up Message "ISO Acknowledgement" - PGN 59392.
Definition: NMEA2000.h:3016
#define N2kMaxCanBusAddress
Max CAN Bus Address given by the library.
Definition: NMEA2000.h:106
bool ParseN2kPGNISORequest(const tN2kMsg &N2kMsg, unsigned long &RequestedPGN)
Parsing the content of a "ISO request" message - PGN 59904.
Definition: NMEA2000.h:3253
tN2kPGNList
Enumeration of types for PGN lists according to PGN 126464.
Definition: NMEA2000.h:3262
@ N2kpgnl_receive
Definition: NMEA2000.h:3262
@ N2kpgnl_transmit
Definition: NMEA2000.h:3262
const unsigned long TransmitMessages[]
size_t print(const char *str)
Print string to stream.
Definition: N2kStream.cpp:32
size_t println(const char *str)
Print string and newline to stream.
Definition: N2kStream.cpp:75
Class used internally on tNMEA2000 to handle incoming NMEA2000 messages.
Definition: N2kCANMsg.h:42
unsigned char TPMaxPackets
=0 not TP message. >0 number of packets can be received
Definition: N2kCANMsg.h:71
bool KnownMessage
Message is already known
Definition: N2kCANMsg.h:66
bool Ready
Message ready for handling?
Definition: N2kCANMsg.h:60
tN2kMsg N2kMsg
Reference to a N2kMsg Object, Output: NMEA2000 message ready to be send.
Definition: N2kCANMsg.h:58
bool SystemMessage
Message is a system message.
Definition: N2kCANMsg.h:64
void FreeMessage()
Free the message.
Definition: N2kCANMsg.h:86
unsigned char TPRequireCTS
=0 no, n=after each n frames
Definition: N2kCANMsg.h:69
unsigned char LastFrame
Last received frame sequence number on fast packets or multi packet
Definition: N2kCANMsg.h:75
bool FreeMsg
Message is free for fill up
Definition: N2kCANMsg.h:62
unsigned char CopiedLen
Length of copied bytes.
Definition: N2kCANMsg.h:77
Default Group Function Handler for PGN 126464 - "PGN List - Transmit/Receive PGNs group function".
Default Group Function Handler for PGN 126993 - "Heartbeat".
Default Group Function Handler for PGN 126996 - Product Information.
Default Group Function Handler for PGN 126998 - "Configuration Information".
Default Group Function Handler for PGN 60928 - "ISO Address Claim".
Base handler class for Group Functions.
virtual bool Handle(const tN2kMsg &N2kMsg, tN2kGroupFunctionCode GroupFunctionCode, unsigned long PGNForGroupFunction, int iDev)
Handle group function message.
static bool Parse(const tN2kMsg &N2kMsg, tN2kGroupFunctionCode &GroupFunctionCode, unsigned long &PGNForGroupFunction)
Parse group function code and requested/commanded etc. PGN from group function message.
unsigned long PGN
Parameter Group Number (PGN) of this Group Function.
This class contains all the data of an NMEA2000 message.
Definition: N2kMsg.h:656
unsigned char Data[MaxDataLen]
Byte array which carries all the data of the NMEA2000 message.
Definition: N2kMsg.h:675
void AddStr(const char *str, int len, bool UsePgm=false, unsigned char fillChar=0xff)
Add string value to the buffer The string will be added to the end (indicated by DataLen) of the byte...
Definition: N2kMsg.cpp:213
void Add2ByteUInt(uint16_t v)
Add unsigned integer value to the buffer using 2 bytes The value will be added to the end (indicated ...
Definition: N2kMsg.cpp:188
bool GetVarStr(size_t &StrBufSize, char *StrBuf, int &Index) const
Get a string out of Data This method determines the length of the string by it self,...
Definition: N2kMsg.cpp:416
void Add2ByteInt(int16_t v)
Add integer value to the buffer using 2 bytes The value will be added to the end (indicated by DataLe...
Definition: N2kMsg.cpp:183
bool IsTPMessage() const
Determine if the message is flagged as MultiPacket Message.
Definition: N2kMsg.h:699
void AddByte(unsigned char v)
Add byte value to the buffer The byte will be added to the end (indicated by DataLen) of the byte arr...
Definition: N2kMsg.cpp:208
void ForceSource(unsigned char _Source) const
Set the Source of the message.
Definition: N2kMsg.h:725
void Print(N2kStream *port, bool NoData=false) const
Print out the whole content of the N2kMsg Object.
Definition: N2kMsg.cpp:824
void AddUInt64(uint64_t v)
Add unsigned integer value to the buffer using 8 bytes The value will be added to the end (indicated ...
Definition: N2kMsg.cpp:203
uint16_t Get2ByteUInt(int &Index, uint16_t def=0xffff) const
Get an unsigned integer from 2 bytes out of Data.
Definition: N2kMsg.cpp:268
unsigned char Source
Source of the NMEA2000 message.
Definition: N2kMsg.h:669
static const int MaxDataLen
Maximum number of bytes that can be stored in the data buffer With fast packet the first frame can ha...
Definition: N2kMsg.h:663
unsigned char Priority
Priority of the NMEA2000 message.
Definition: N2kMsg.h:665
uint32_t Get3ByteUInt(int &Index, uint32_t def=0xffffffff) const
Get an unsigned integer from 3 bytes out of Data.
Definition: N2kMsg.cpp:275
void Add3ByteInt(int32_t v)
Add integer value to the buffer using 3 bytes The value will be added to the end (indicated by DataLe...
Definition: N2kMsg.cpp:193
virtual void Clear()
Clears the content of the N2kMsg object The method sets the PGN, DataLen and MsgTime to zero.
Definition: N2kMsg.cpp:94
unsigned long MsgTime
timestamp (ms since start [max 49days]) of the NMEA2000 message
Definition: N2kMsg.h:677
void Add4ByteUInt(uint32_t v)
Add unsigned integer value to the buffer using 4 bytes The value will be added to the end (indicated ...
Definition: N2kMsg.cpp:198
void CheckDestination() const
Validation check for the message destination We can send to specified destination only for PGN:s low ...
Definition: N2kMsg.h:734
void SetPGN(unsigned long _PGN)
Set the Parameter Group Number of the message *.
Definition: N2kMsg.cpp:68
unsigned char GetByte(int &Index) const
Get the value from a byte out of Data.
Definition: N2kMsg.cpp:254
uint64_t GetUInt64(int &Index, uint64_t def=0xffffffffffffffffULL) const
Get an unsigned integer from 8 bytes out of Data.
Definition: N2kMsg.cpp:289
bool GetStr(char *StrBuf, size_t Length, int &Index) const
Get a string out of Data.
Definition: N2kMsg.cpp:359
void Init(unsigned char _Priority, unsigned long _PGN, unsigned char _Source, unsigned char _Destination=0xff)
Initialisation of the N2kMsg object.
Definition: N2kMsg.cpp:75
void SetIsTPMessage(bool tp=true)
Set the MultiPacket Message status.
Definition: N2kMsg.h:692
void SendInActisenseFormat(N2kStream *port) const
Print out the whole content of the N2kMsg Object using the Actisense Format.
Definition: N2kMsg.cpp:853
int DataLen
Number of bytes already stored in tN2kMsg::Data of this message.
Definition: N2kMsg.h:673
unsigned long PGN
Parameter Group Number (PGN) of the NMEA2000 message.
Definition: N2kMsg.h:667
unsigned char Destination
Destination of the NMEA2000 message.
Definition: N2kMsg.h:671
void Disable()
Disable the Scheduler.
Definition: N2kTimer.h:306
bool IsEnabled() const
Check if the scheduler is enabled.
Definition: N2kTimer.h:324
void FromNow(uint32_t _Add)
Set Timestamp for next event relative to now.
Definition: N2kTimer.h:345
bool IsTime() const
Is it time for the next event.
Definition: N2kTimer.h:332
static void SetSyncOffset()
Set the SyncOffset of the scheduler.
Definition: N2kTimer.h:259
void SetPeriodAndOffset(uint32_t _Period, uint32_t _Offset)
Set the Period And Offset of the Scheduler.
Definition: N2kTimer.h:210
uint32_t GetOffset() const
Get the Offset of the Scheduler.
Definition: N2kTimer.h:222
uint32_t GetPeriod() const
Get the Period of the Scheduler.
Definition: N2kTimer.h:229
void UpdateNextTime()
Update the timestamp for NextTime.
Definition: N2kTimer.h:242
void Disable()
Disable Scheduler.
Definition: N2kTimer.h:157
Structure holds all the data needed for a valid CAN-Message.
Definition: NMEA2000.h:993
unsigned char buf[8]
Data payload for the CAN Message.
Definition: NMEA2000.h:1000
unsigned char len
Length of carried data of the CAN Message.
Definition: NMEA2000.h:998
bool wait_sent
Has the CAN Message to wait before sending.
Definition: NMEA2000.h:1002
unsigned long id
ID of the CAN Message.
Definition: NMEA2000.h:996
void SetDeviceInstance(unsigned char _DeviceInstance)
Set the Device Instance to the Device Information.
Definition: NMEA2000.h:388
void SetIndustryGroup(unsigned char _IndustryGroup)
Set the Industry Group to the Device Information.
Definition: NMEA2000.h:439
unsigned char GetDeviceInstance() const
Get the Device Instance from the Device Information.
Definition: NMEA2000.h:394
void SetUniqueNumber(uint32_t _UniqueNumber)
Set a unique Number to the Device Information.
Definition: NMEA2000.h:364
void SetDeviceClass(unsigned char _DeviceClass)
Set the Device Class to the Device Information.
Definition: NMEA2000.h:426
void SetDeviceFunction(unsigned char _DeviceFunction)
Set the Device Function to the Device Information.
Definition: NMEA2000.h:412
void SetManufacturerCode(uint16_t _ManufacturerCode)
Set the Manufacturer Code to the Device Information.
Definition: NMEA2000.h:376
void SetSystemInstance(unsigned char _SystemInstance)
Set the System Instance to the Device Information.
Definition: NMEA2000.h:451
This class represents an internal device.
Definition: NMEA2000.h:771
void UpdateAddressClaimEndSource()
Updates AddressClaimEndSource.
Definition: NMEA2000.h:918
bool HasPendingInformation
internal device has pending information
Definition: NMEA2000.h:812
const unsigned long * ReceiveMessages
Pointer to a buffer that holds all supported receive PGNs for this device.
Definition: NMEA2000.h:804
void ClearPendingConfigurationInformation()
Resets PendingConfigurationInformation to zero.
Definition: NMEA2000.h:903
void SetPendingProductInformation()
Set the timestamp for Pending a ProductInformation message.
Definition: NMEA2000.h:879
void SetPendingConfigurationInformation()
Set the timestamp for Pending a ConfigurationInformation message.
Definition: NMEA2000.h:901
uint8_t NextDTSequence
Next Sequence.
Definition: NMEA2000.h:820
uint8_t HeartbeatSequence
Heartbeat Sequence.
Definition: NMEA2000.h:828
tN2kScheduler NextDTSendTime
Timestamp, when next data packet can be send on TP broadcast.
Definition: NMEA2000.h:818
tProductInformation * LocalProductInformation
This holds all the local (???) Product Informations for this specific device.
Definition: NMEA2000.h:783
tN2kSyncScheduler HeartbeatScheduler
Scheduler for the heartbeat message.
Definition: NMEA2000.h:826
uint8_t N2kSource
Source address of this device on the NMEA2000 bus.
Definition: NMEA2000.h:774
size_t MaxPGNSequenceCounters
Fast packet PGNs sequence counters.
Definition: NMEA2000.h:808
const tProductInformation * ProductInformation
This holds all the Product Informations for this specific device.
Definition: NMEA2000.h:780
tN2kScheduler AddressClaimTimer
Timer value for AddressClaim.
Definition: NMEA2000.h:798
void UpdateHasPendingInformation()
Definition: NMEA2000.h:922
tN2kMsg PendingTPMsg
Pending N2k message.
Definition: NMEA2000.h:816
tDeviceInformation DeviceInformation
This holds all the Device Informations for this specific device.
Definition: NMEA2000.h:776
const unsigned long * TransmitMessages
Pointer to a buffer that holds all supported transmit PGNs for this device.
Definition: NMEA2000.h:801
void SetPendingIsoAddressClaim(unsigned long FromNow=2)
Set the timestamp for Pending an ISO Address Claim message.
Definition: NMEA2000.h:857
void ClearPendingIsoAddressClaim()
Resets PendingIsoAddressClaim to zero.
Definition: NMEA2000.h:870
unsigned long * PGNSequenceCounters
Fast packet PGNs sequence counters.
Definition: NMEA2000.h:806
void ClearPendingProductInformation()
Resets PendingProductInformation to zero.
Definition: NMEA2000.h:881
Message handler class.
Definition: NMEA2000.h:616
unsigned long GetPGN() const
Return the PGN that is handled by this message handler.
Definition: NMEA2000.h:656
bool SendTPCM_RTS(int iDev)
Send ISO Transport Protocol message RTS.
Definition: NMEA2000.cpp:1687
bool IsMySource(unsigned char Source)
Checks if the source belongs to a device on Devices.
Definition: NMEA2000.cpp:2073
void SetFastPacketMessages(const unsigned long *_FastPacketMessages)
Set the list of known Fast Packet Messages.
Definition: NMEA2000.cpp:1148
bool IsReadyToSend() const
Checks if the device is ready to start address claiming.
Definition: NMEA2000.h:1601
void SendHeartbeat(int iDev)
Send heartbeat for specific device.
Definition: NMEA2000.cpp:1388
void SendPendingInformation()
Send ISO AddressClaim, Product Information and Config Information.
Definition: NMEA2000.cpp:1431
void SetProductInformation(const char *_ModelSerialCode, unsigned short _ProductCode=0xffff, const char *_ModelID=0, const char *_SwCode=0, const char *_ModelVersion=0, unsigned char _LoadEquivalency=0xff, unsigned short _N2kVersion=0xffff, unsigned char _CertificationLevel=0xff, int iDev=0)
Set the Product Information of this device.
Definition: NMEA2000.cpp:745
bool(* ISORqstHandler)(unsigned long RequestedPGN, unsigned char Requester, int DeviceIndex)
Handler callbacks for 'ISORequest' messages.
Definition: NMEA2000.h:1079
static void ClearSetCharBuf(const char *str, size_t MaxLen, char *buf)
Setting up a clean Char Buffer.
Definition: NMEA2000.cpp:654
bool IsTxPGN(unsigned long PGN, int iDev=0)
Check if this message is a Transmit message of this device.
Definition: NMEA2000.cpp:875
size_t GetFastPacketTxPGNCount(int iDev)
Get the Fast Packet Tx PGN Count.
Definition: NMEA2000.cpp:813
int DeviceCount
Number of devices.
Definition: NMEA2000.h:977
static void ClearCharBuf(size_t MaxLen, char *buf)
Clears a char array buffer with 0s.
Definition: NMEA2000.cpp:637
void InitDevices()
Initialize all devices.
Definition: NMEA2000.cpp:710
tN2kCANMsg * N2kCANMsgBuf
Buffer for receiving messages.
Definition: NMEA2000.h:1015
void ExtendSingleFrameMessages(const unsigned long *_SingleFrameMessages)
Set the list of known Extended Single Frame Messages.
Definition: NMEA2000.cpp:1153
void EnableForward(bool v=true)
Enable message forwarding to stream.
Definition: NMEA2000.h:2900
void ParseMessages()
Parse all incoming Messages.
Definition: NMEA2000.cpp:2577
void SetISORqstHandler(bool(*ISORequestHandler)(unsigned long RequestedPGN, unsigned char Requester, int DeviceIndex))
Set the message handler for incoming ISO Requests.
Definition: NMEA2000.cpp:2684
tDebugMode dbMode
Attribute that holds the actual Debug Mode (default = md_none)
Definition: NMEA2000.h:950
void SetDeviceInformation(unsigned long _UniqueNumber, unsigned char _DeviceFunction=0xff, unsigned char _DeviceClass=0xff, uint16_t _ManufacturerCode=0xffff, unsigned char _IndustryGroup=4, int iDev=0)
Set the Device Information. See also NAME.
Definition: NMEA2000.cpp:1093
bool HasAllTPDTSent(int iDev)
Check if all data bytes of the multi packet message has been send successful.
Definition: NMEA2000.cpp:1781
void RemoveGroupFunctionHandler(tN2kGroupFunctionHandler *pGroupFunctionHandler)
Remove a message handler for incoming Group Function messages.
Definition: NMEA2000.cpp:2690
void SetForwardSystemMessages(bool v=true)
Enable System Messages for forwarding.
Definition: NMEA2000.h:2914
unsigned int ForwardMode
Actual message forward operation mode (default = all messages - also system and own)
Definition: NMEA2000.h:959
void SetSingleFrameMessages(const unsigned long *_SingleFrameMessages)
Set the list of known Single Frame Messages.
Definition: NMEA2000.cpp:1143
void SetInstallationDescription1(const char *InstallationDescription1)
Set the Installation Description 1 of this device.
Definition: NMEA2000.cpp:1037
void HandleISORequest(const tN2kMsg &N2kMsg)
Handles an ISO Request.
Definition: NMEA2000.cpp:2333
N2kStream * ForwardStream
Actual stream to be used for forward messaging.
Definition: NMEA2000.h:961
void SetMsgHandler(void(*_MsgHandler)(const tN2kMsg &N2kMsg))
Set the message handler for incoming NMEA2000 messages.
Definition: NMEA2000.cpp:2638
tN2kGroupFunctionHandler * pGroupFunctionHandlers
Pointer to Buffer for GRoup Function Handlers.
Definition: NMEA2000.h:1083
bool SendFrame(unsigned long id, unsigned char len, const unsigned char *buf, bool wait_sent=true)
Sends a single CAN frame.
Definition: NMEA2000.cpp:1335
bool StartSendTPMessage(const tN2kMsg &msg, int iDev)
Start sending an ISO-TP message.
Definition: NMEA2000.cpp:1926
void HandleGroupFunction(const tN2kMsg &N2kMsg)
Handles a Group Function.
Definition: NMEA2000.cpp:2375
bool SendConfigurationInformation(unsigned char Destination, int DeviceIndex, bool UseTP)
Send a Config Information message.
Definition: NMEA2000.cpp:2264
bool ReadResetInstallationDescriptionChanged()
Check if this device has changed its Install Description.
Definition: NMEA2000.cpp:1083
void SetForwardOwnMessages(bool v=true)
Enable Own Messages for forwarding.
Definition: NMEA2000.h:2948
bool IsInitialized()
Determines if the CAN BUS is already initialized.
Definition: NMEA2000.h:1234
unsigned char GetCertificationLevel(int iDev=0) const
Get the Certification Level of the device.
Definition: NMEA2000.cpp:994
void AttachMsgHandler(tMsgHandler *_MsgHandler)
Attach a message handler for incoming N2kMessages.
Definition: NMEA2000.cpp:2643
bool AddressChanged
Flag that the address has changed.
Definition: NMEA2000.h:970
bool CheckKnownMessage(unsigned long PGN, bool &SystemMessage, bool &FastPacket)
Check if this Message is known to the system.
Definition: NMEA2000.cpp:1579
void SetDebugMode(tDebugMode _dbMode)
Set the Debug Mode of the system.
Definition: NMEA2000.cpp:1547
bool IsValidDevice(int iDev) const
Checks if the device index on Devices is valid.
Definition: NMEA2000.h:1593
void ExtendReceiveMessages(const unsigned long *_ReceiveMessages, int iDev=0)
Extend the list of Received Messages.
Definition: NMEA2000.cpp:1170
uint8_t MaxN2kCANMsgs
Size of N2kCANMsgBuf receiving message buffer.
Definition: NMEA2000.h:1021
virtual bool CANGetFrame(unsigned long &id, unsigned char &len, unsigned char *buf)=0
Abstract class for reading frame from driver class.
static bool IsBroadcast(unsigned char Source)
Checks if the given Address is a broadcast address.
Definition: NMEA2000.h:2990
void SendIsoAddressClaim(unsigned char Destination=0xff, int DeviceIndex=0, unsigned long FromNow=0)
Send an IsoAddressClaim message.
Definition: NMEA2000.cpp:2097
bool ForwardEnabled() const
Is message forwarding enabled.
Definition: NMEA2000.h:1520
bool Open()
Open the CAN device.
Definition: NMEA2000.cpp:1200
tN2kMode
System mode defines how the device will behave on the NMEA2000 bus.
Definition: NMEA2000.h:683
@ N2km_ListenOnly
Definition: NMEA2000.h:689
@ N2km_NodeOnly
Definition: NMEA2000.h:695
@ N2km_SendOnly
Definition: NMEA2000.h:705
@ N2km_ListenAndSend
Definition: NMEA2000.h:711
void GetManufacturerInformation(char *buf, size_t max_len)
Get the Manufacturer Information of this device.
Definition: NMEA2000.cpp:1074
void SetHeartbeatInterval(unsigned long interval, bool SetAsDefault=true, int iDev=-1) __attribute__((deprecated))
Deprecated. Use function SetHeartbeatIntervalAndOffset.
Definition: NMEA2000.cpp:1383
bool ForwardSystemMessages() const
Is forwarding enabled for system messages.
Definition: NMEA2000.h:1528
@ fwdt_Text
Definition: NMEA2000.h:671
@ fwdt_Actisense
Definition: NMEA2000.h:667
void SendPendingTPMessage(int iDev)
Send pending ISO-TP Messages.
Definition: NMEA2000.cpp:1956
void SendTPCM_EndAck(unsigned long PGN, unsigned char Destination, int iDev, uint16_t nBytes, unsigned char nPackets)
Send ISO Transport Protocol message End Acknowledge.
Definition: NMEA2000.cpp:1725
tN2kScheduler OpenScheduler
Definition: NMEA2000.h:966
bool IsFastPacketPGN(unsigned long PGN)
Check if this PNG is a fast packet message.
Definition: NMEA2000.cpp:1552
void AddGroupFunctionHandler(tN2kGroupFunctionHandler *pGroupFunctionHandler)
Add a message handler for incoming Group Function messages.
Definition: NMEA2000.cpp:2708
void Restart()
Restart the device.
Definition: NMEA2000.cpp:1264
bool SendMsg(const tN2kMsg &N2kMsg, int DeviceIndex=0)
Send message to the NMEA2000 bus.
Definition: NMEA2000.cpp:1450
void ExtendTransmitMessages(const unsigned long *_TransmitMessages, int iDev=0)
Extend the list of Transmitted Messages.
Definition: NMEA2000.cpp:1163
bool SendTPDT(int iDev)
Send ISO Transport Protocol data packet.
Definition: NMEA2000.cpp:1762
virtual bool CANOpen()=0
Abstract class for initializing and opening CAN interface.
static bool IsProprietaryMessage(unsigned long PGN)
Check if the given PGN is proprietary.
Definition: NMEA2000.cpp:583
void HandleISOAddressClaim(const tN2kMsg &N2kMsg)
Handles an IsoAddressClaim.
Definition: NMEA2000.cpp:2430
tNMEA2000()
Construct a new NMEA2000 object.
Definition: NMEA2000.cpp:660
uint16_t MaxCANSendFrames
Size of CANSendFrameBuf or before initialization requested total frame buffering size.
Definition: NMEA2000.h:1049
void GetInstallationDescription1(char *buf, size_t max_len)
Get the Install Description 1 of this device.
Definition: NMEA2000.cpp:1056
@ os_Open
State Open.
Definition: NMEA2000.h:763
@ os_None
State none.
Definition: NMEA2000.h:754
@ os_OpenCAN
State Open CAN.
Definition: NMEA2000.h:757
@ os_WaitOpen
State Wait Open.
Definition: NMEA2000.h:760
uint16_t CANSendFrameBufferRead
Next write index for the library CAN send frame buffer.
Definition: NMEA2000.h:1055
void SetHeartbeatIntervalAndOffset(uint32_t interval, uint32_t offset=0, int iDev=-1)
Set the Heartbeat Interval and Offset for a device.
Definition: NMEA2000.cpp:1356
uint16_t CANSendFrameBufferWrite
Next read index for the library CAN send frame buffer.
Definition: NMEA2000.h:1052
bool SendFrames()
Sends pending all frames.
Definition: NMEA2000.cpp:1318
void SetOnOpen(void(*_OnOpen)())
Set OnOpen callback function.
Definition: NMEA2000.cpp:2633
bool IsAddressClaimStarted(int iDev)
Checks if the IsoAddressClaim is already started.
Definition: NMEA2000.cpp:2414
void ExtendFastPacketMessages(const unsigned long *_FastPacketMessages)
Set the list of known Extended Fast Packet Messages.
Definition: NMEA2000.cpp:1158
void RespondISORequest(const tN2kMsg &N2kMsg, unsigned long RequestedPGN, int iDev)
Respond to an ISO request.
Definition: NMEA2000.cpp:2297
void SendRxPGNList(unsigned char Destination, int DeviceIndex, bool UseTP=false)
Send a list with all supported Receive messages.
Definition: NMEA2000.cpp:2161
tMsgHandler * MsgHandlers
Pointer to a buffer for Message Handlers.
Definition: NMEA2000.h:963
uint8_t SetN2kCANBufMsg(unsigned long canId, unsigned char len, unsigned char *buf)
Function handles received CAN frame and adds it to tN2kCANMsg.
Definition: NMEA2000.cpp:1977
void SendTxPGNList(unsigned char Destination, int DeviceIndex, bool UseTP=false)
Send a list with all supported Transmit messages.
Definition: NMEA2000.cpp:2128
void FindFreeCANMsgIndex(unsigned long PGN, unsigned char Source, unsigned char Destination, bool TPMsg, uint8_t &MsgIndex)
Find index for free space for a message on N2kCANMsgBuf.
Definition: NMEA2000.cpp:1634
void StartAddressClaim()
Starting the ISO Address Claim for all devices.
Definition: NMEA2000.cpp:2406
tCANSendFrame * GetNextFreeCANSendFrame()
Get the Next Free CAN Frame from CANSendFrameBuf.
Definition: NMEA2000.cpp:1417
tConfigurationInformation ConfigurationInformation
Configuration Information of the device.
Definition: NMEA2000.h:983
void SetProgmemConfigurationInformation(const char *ManufacturerInformation, const char *InstallationDescription1=0, const char *InstallationDescription2=0)
Set the Configuration Information located on PROGMEM.
Definition: NMEA2000.cpp:802
void GetModelVersion(char *buf, size_t max_len, int iDev=0) const
Get the Model Version of the device.
Definition: NMEA2000.cpp:962
int GetSequenceCounter(unsigned long PGN, int iDev)
Get the Sequence Counter for the PGN.
Definition: NMEA2000.cpp:831
const tNMEA2000::tProductInformation * GetProductInformation(int iDev, bool &IsProgMem) const
Get the Product Information of the device.
Definition: NMEA2000.cpp:891
void SendTPCM_Abort(unsigned long PGN, unsigned char Destination, int iDev, unsigned char AbortCode)
Send ISO Transport Protocol message Abort.
Definition: NMEA2000.cpp:1742
void EndSendTPMessage(int iDev)
Ends sending of ISO-TP message.
Definition: NMEA2000.cpp:1949
tForwardType ForwardType
Actual message forward type (default = fwdt_Actisense)
Definition: NMEA2000.h:956
const unsigned long * SingleFrameMessages[N2kMessageGroups]
Definition: NMEA2000.h:985
bool HandleReceivedSystemMessage(int MsgIndex)
Handles a received system message.
Definition: NMEA2000.cpp:2543
tN2kMode N2kMode
Actual operation mode of this device (default = N2km_ListenOnly)
Definition: NMEA2000.h:954
tOpenState OpenState
Definition: NMEA2000.h:968
const unsigned long * FastPacketMessages[N2kMessageGroups]
Definition: NMEA2000.h:986
void(* MsgHandler)(const tN2kMsg &N2kMsg)
Handler callbacks for normal messages.
Definition: NMEA2000.h:1077
bool HandleOnlyKnownMessages() const
Is handle only known messages enabled.
Definition: NMEA2000.h:1552
char * LocalConfigurationInformationData
Pointer to a buffer for local Configuration Information.
Definition: NMEA2000.h:981
static void SetCharBuf(const char *str, size_t MaxLen, char *buf)
Setting up a Char Buffer.
Definition: NMEA2000.cpp:644
unsigned short GetN2kVersion(int iDev=0) const
Get the N2k standard version of the device.
Definition: NMEA2000.cpp:902
uint16_t MaxCANReceiveFrames
Max number received CAN messages that can go to the buffer.
Definition: NMEA2000.h:1061
int FindSourceDeviceIndex(unsigned char Source) const
Finds a device on Devices by its source address.
Definition: NMEA2000.cpp:2062
bool SendProductInformation(unsigned char Destination, int DeviceIndex, bool UseTP)
Send a Product Information message.
Definition: NMEA2000.cpp:2222
virtual void InitCANFrameBuffers()
Initialize CAN Frame buffers.
Definition: NMEA2000.cpp:1188
bool DeviceInformationChanged
Flag that the device information has changed.
Definition: NMEA2000.h:972
void SendTPCM_CTS(unsigned long PGN, unsigned char Destination, int iDev, unsigned char nPackets, unsigned char NextPacketNumber)
Send ISO Transport Protocol message CTS.
Definition: NMEA2000.cpp:1707
bool SendTPCM_BAM(int iDev)
Send ISO Transport Protocol message BAM.
Definition: NMEA2000.cpp:1668
bool IsActiveNode()
Returns if this node is active on the bus.
Definition: NMEA2000.h:1584
void GetModelSerialCode(char *buf, size_t max_len, int iDev=0) const
Get the Model Serial of the device.
Definition: NMEA2000.cpp:978
bool ReadResetAddressChanged()
Check if this device has changed its address.
Definition: NMEA2000.cpp:2496
void SetMode(tN2kMode _N2kMode, uint8_t _N2kSource=15)
Set the library mode and start source address.
Definition: NMEA2000.cpp:1177
void ForwardMessage(const tN2kMsg &N2kMsg)
Forwards a N2k message.
Definition: NMEA2000.cpp:2078
bool ForwardOnlyKnownMessages() const
Is forwarding enabled for known messages only.
Definition: NMEA2000.h:1536
virtual bool CANSendFrame(unsigned long id, unsigned char len, const unsigned char *buf, bool wait_sent=true)=0
Abstract class for sending a CAN Frame.
void SetInstallationDescription2(const char *InstallationDescription2)
Set the Installation Description 2 of this device.
Definition: NMEA2000.cpp:1047
bool TestHandleTPMessage(unsigned long PGN, unsigned char Source, unsigned char Destination, unsigned char len, unsigned char *buf, uint8_t &MsgIndex)
ISO Transport Protocol handlers for multi packet support.
Definition: NMEA2000.cpp:1786
void SetDeviceInformationInstances(uint8_t _DeviceInstanceLower=0xff, uint8_t _DeviceInstanceUpper=0xff, uint8_t _SystemInstance=0xff, int iDev=0)
Set the Device Information Instances.
Definition: NMEA2000.cpp:1110
void HandleCommandedAddress(uint64_t CommandedName, unsigned char NewAddress, int iDev)
Handles if we get commanded to set a new address.
Definition: NMEA2000.cpp:2455
bool IsFastPacket(const tN2kMsg &N2kMsg)
Check if this PNG is a fast packet message.
Definition: NMEA2000.cpp:1572
void GetInstallationDescription2(char *buf, size_t max_len)
Get the Install Description 2 of this device.
Definition: NMEA2000.cpp:1065
bool ForwardOwnMessages() const
Is forwarding enabled for own messages.
Definition: NMEA2000.h:1544
unsigned short GetProductCode(int iDev=0) const
Get the Product Code of the device.
Definition: NMEA2000.cpp:916
tDebugMode
For debugging we have some cases for SendMsg.
Definition: NMEA2000.h:718
@ dm_ClearText
Definition: NMEA2000.h:724
@ dm_Actisense
Definition: NMEA2000.h:727
bool ReadResetDeviceInformationChanged()
Check if this device has changed its DeviceInstances or SystemInstance.
Definition: NMEA2000.cpp:2504
void GetSwCode(char *buf, size_t max_len, int iDev=0) const
Get the Sw Code of the device.
Definition: NMEA2000.cpp:946
void SetN2kSource(unsigned char _iAddr, int _iDev=0)
Set source for the given device.
Definition: NMEA2000.cpp:2488
void GetModelID(char *buf, size_t max_len, int iDev=0) const
Get the ModelID of the device.
Definition: NMEA2000.cpp:930
void DetachMsgHandler(tMsgHandler *_MsgHandler)
Detach a message handler for incoming N2kMessages.
Definition: NMEA2000.cpp:2668
void CopyProgmemConfigurationInformationToLocal()
Copy Configuration Information to local memory.
Definition: NMEA2000.cpp:1022
void(* OnOpen)()
Callback function, which will be called when library start bus communication.
Definition: NMEA2000.h:1074
unsigned char GetLoadEquivalency(int iDev=0) const
Get the Load Equivalency of this device.
Definition: NMEA2000.cpp:1008
void GetNextAddress(int DeviceIndex, bool RestartAtEnd=false)
Get the next free address for the device.
Definition: NMEA2000.cpp:2512
void RespondGroupFunction(const tN2kMsg &N2kMsg, tN2kGroupFunctionCode GroupFunctionCode, unsigned long PGNForGroupFunction, int iDev)
Respond to an Group Function.
Definition: NMEA2000.cpp:2353
bool InstallationDescriptionChanged
Flag the Installation description has changed.
Definition: NMEA2000.h:1746
void SetConfigurationInformation(const char *ManufacturerInformation, const char *InstallationDescription1=0, const char *InstallationDescription2=0)
Set the Configuration Information of this device.
Definition: NMEA2000.cpp:764
tInternalDevice * Devices
Pointer to a buffer for all internal devices.
Definition: NMEA2000.h:975
void SetDeviceCount(const uint8_t _DeviceCount)
Set the count of devices library shows on bus.
Definition: NMEA2000.cpp:704
tCANSendFrame * CANSendFrameBuf
Buffer for library send out CAN frames.
Definition: NMEA2000.h:1035
void RunMessageHandlers(const tN2kMsg &N2kMsg)
Run all message handlers.
Definition: NMEA2000.cpp:2620
const char * InstallationDescription2
pointer to char array holding the installation description 2
Definition: NMEA2000.h:740
const char * InstallationDescription1
pointer to char array holding the installation description 1
Definition: NMEA2000.h:738
const char * ManufacturerInformation
pointer to char array holding the manufacturer information
Definition: NMEA2000.h:736
Structure that holds all the product information.
Definition: NMEA2000.h:216
unsigned short N2kVersion
Version of NMEA2000 Standard that is supported.
Definition: NMEA2000.h:219
unsigned short ProductCode
Product Code of the device.
Definition: NMEA2000.h:221
char N2kModelID[Max_N2kModelID_len+1]
Max length of ModelID Note that we reserve one extra char for null termination
Definition: NMEA2000.h:225
unsigned char LoadEquivalency
Load Equivalency of the device A Load Equivalence Number express the amount of current that is drawn ...
Definition: NMEA2000.h:243
unsigned char CertificationLevel
Certification level of the device.
Definition: NMEA2000.h:236
char N2kModelVersion[Max_N2kModelVersion_len+1]
Max length of Model Version Note that we reserve one extra char for null termination.
Definition: NMEA2000.h:231
bool IsSame(const tProductInformation &Other)
Compares two product information structures.
Definition: NMEA2000.cpp:632
void Clear()
Clears out all data
Definition: NMEA2000.cpp:627
char N2kSwCode[Max_N2kSwCode_len+1]
Max length of Software Code Note that we reserve one extra char for null termination
Definition: NMEA2000.h:228
char N2kModelSerialCode[Max_N2kModelSerialCode_len+1]
Max length of Serial Code Note that we reserve one extra char for null termination.
Definition: NMEA2000.h:234
void Set(const char *_ModelSerialCode, unsigned short _ProductCode=0xffff, const char *_ModelID=0, const char *_SwCode=0, const char *_ModelVersion=0, unsigned char _LoadEquivalency=0xff, unsigned short _N2kVersion=0xffff, unsigned char _CertificationLevel=0xff)
Set all the product information data of the structure.
Definition: NMEA2000.h:262