From dd96be526be9366d992d610c410257e001cf749c Mon Sep 17 00:00:00 2001 From: Timo Lappalainen Date: Fri, 2 Feb 2024 12:47:05 +0200 Subject: [PATCH] Documentation update --- Documents/src/2_libRef.md | 95 ++++----- Documents/src/7_glossary.md | 2 +- Documents/src/changes.md | 4 + src/NMEA2000.h | 370 +++++++++++++++++++++++------------- 4 files changed, 286 insertions(+), 185 deletions(-) diff --git a/Documents/src/2_libRef.md b/Documents/src/2_libRef.md index d2cae358..e99954c9 100644 --- a/Documents/src/2_libRef.md +++ b/Documents/src/2_libRef.md @@ -437,75 +437,57 @@ Library core has all functionalities to communicate with NMEA2000 bus and N2kMes Below is short description of member functions, which hopefully gives you better knowledge why something has been used on samples. -#### Device modes +#### Device modes {#secDeviceModes} -NMEA2000 defines that all devices should act as node on N2k bus. But if you are only reading messages -on bus, why to tell anybody. So I have defined different modes how tNMEA2000 class behaves. +For historical reasons library offers several message handling modes. One idea was that CAN controller specific drivers could drop some data handling, but it is not implemented on any of them. Practically only modes +- N2km_ListenAndNode for real bus devices. +- N2km_ListenAndSend for message pass trough devices. -##### tNMEA2000::N2km_ListenOnly +would be enough. -This is default mode. The device simply listens data from bus and forwards it to the forward -stream. Look example ActisenseListener, you need only 20 line for making device to read data -on N2k bus. -Also if you like to make a device, which displays some data on bus on e.g. TFT display, you can -use this mode. There is simple example DataDisplay.ino for that. +"Listen" in mode definition refers to message forwarding. Mode definitions without "Listen" passes forward functionality. On the other hand forwarding can be enabled or disabled with tNMEA2000::EnableForward(), which then makes other modes less necessary. Also mode can not be changed after open, but forward can be enabled or disabled at any time - this makes listen modes be more usefull. And last since drivers does not limit messages by mode message handlers will be called anyway allowing you to control messages in your code and even build own "message forwarding". -##### tNMEA2000::N2km_NodeOnly +Although there should be no reason to use other than N2km_ListenAndNode or N2km_ListenAndSend modes, below is table of different modes effect. + - Tx = messages can be sent + - Rx = messages can be received + - Dev = acts as active bus device. Has full functionality like address claiming, heartbeat, response to requests. + - Fen = Message forwarding - tNMEA2000::EnableForward() does have effect + - S = Setting tNMEA2000::SetForwardSystemMessages does have effect. -In this mode device will only send data to the N2k bus. I also automatically informs itself to other devices on the bus and does required operations automatically. The device does not send as default anything to any forward stream. Use this mode for device, which simply e.g. reads data from analog or digital input or NMEA0183 bus and sends it to the N2k bus. Look example TemperatureMonitor.ino. + (x) means that feature is not limited by core library, but could have limit on "driver". -##### tNMEA2000::N2km_ListenAndNode +| Mode | Tx | Rx | Dev | Fen | S | +|--------------------|:---:|:---:|:---:|:---:|:---:| +| N2km_NodeOnly | x | x | x | | | +| N2km_ListenAndNode | x | x | x | x | x | +| N2km_ListenAndSend | x | x | | x | | +| N2km_ListenOnly | | x | | x | x | +| N2km_SendOnly | x | (x) | | | | -In this mode device works fully to both directions. It also forwards all data from bus to -the forward stream, which you can define with function \ref tNMEA2000::SetForwardStream. +NMEA2000 requires that all devices should act as active device (also called node) on the NMEA2000 bus. Total electrical bus load can be then calculated and there will not be any hidden devices. If you take care that bus load will not be exceeded, it should not be risky to make listen only devices for you own boat. -##### tNMEA2000::N2km_SendOnly +You can set your device mode on setup with function tNMEA2000::SetMode(). See also tNMEA2000::tN2kMode . -In this mode it is like tNMEA2000::N2km_NodeOnly, but it does not do automatic address -claiming and does not forward any messages from N2k bus to stream. So this is useful, -if you e.g. have some control pair like autopilot keypad/”control unit” and you want -to fool that keypad sends something to the “control unit”. Then you also need to know -also source addresses of keypad and “control unit”. -I have used this mode e.g. with example ActisenseSender and my NMEA Simulator found on - . +#### Message forwarding {#secMessageforwarding} -##### tNMEA2000::N2km_ListenAndSend +Message forwarding offers simple way to forward messages to defined stream in Actisense format. I build this functionality inside library to easily analyze bus messages even device does have its own functionality like temperature sensor. Now when I have mostly used ESP32 with WiFi, I can enable forwarding on the fly to UDP stream for any device and use it as analyzer. -This is like the tNMEA2000::N2km_SendOnly mode, but it also forwards messages from N2k -bus to the stream. In this mode one can have invisible gateway device between computer -and N2k bus. This mode can be used e.g. if one has a PC application, which is capable to -read and send messages in Actisense format to serial port. -I have used this mode with example TeensyActisenseListenerSender and Actisense NMEA Reader -and NMEA Simulator. +In any listen mode, the device will read all messages from N2k bus and forwards them to the defined forward stream. For forwarding you just define a forward stream - e.g., serial port, UDP stream - with function \ref tNMEA2000::SetForwardStream and enable forward with \ref tNMEA2000::EnableForward(true). Naturally you also need to open a stream first e.g. with Serial.begin(115200); -#### Message forwarding {#secMessageforwarding} +Messages will be forwarded as default in Actisense format. This format is supported by at least some PC chart plotter applications. You can show Actisence format messages with “NMEA Reader”, which I refer time to. Best program for bus data analyzing is my NMEA Simulator . For data visualizer you can use e.g., OpenSkipper, on which you can tailor your own displays. -Normally on N2k bus a device either shows data from bus (MFD devices) or sends data -to the bus (wind, GPS, temperature etc.). With this library you can also get messages -forwarded to the stream. In listen mode, the device will read all messages from N2k bus -and forwards them to the ForwardStream. For forwarding you have to define a -forward stream with function \ref tNMEA2000::SetForwardStream. Of course you also need to -open a stream first e.g. with Serial.begin(115200); -Messages will be forwarded as default in Actisense format. This is supported by at least -some PC chart plotter applications. With default format Actisence “NMEA Reader”, which -we used on sample, you can show message data. A better visualizer is OpenSkipper, on which -you can tailor your own displays. - -\note As default own messages send with tNMEA2000::SendMsg will be forwarded even when your -device has been set to node only mode. This may disturb your developing, if -you e.g. want to write own clear text messages within your code. You can disable that by either: +If message forwarding is enabled, own messages send with tNMEA2000::SendMsg will be forwarded as default even when your device has been set to node only mode. If this disturbs your developing, if you e.g. want to write own clear text messages within your code, you can disable that by either: - \ref tNMEA2000::EnableForward(false) to disable forwarding totally -- \ref tNMEA2000::SetForwardOwnMessages(false) to disable own messages. But then, if your device - is in listen mode, it will still forward messages from bus. -- \ref tNMEA2000::SetForwardOnlyKnownMessages(true) to define that only known messages will be - forwarded. The known messages are system messages and listed single frame or fast packet - messages. - \sa - - \ref tNMEA2000::SetSingleFrameMessages - - \ref tNMEA2000::SetFastPacketMessages - - \ref tNMEA2000::ExtendSingleFrameMessages - - \ref tNMEA2000::ExtendFastPacketMessages. +- \ref tNMEA2000::SetForwardOwnMessages(false) to disable own message forwarding. In listen mode messages from bus will be still forwarded. + +\sa + - \ref secDeviceModes + - \ref tNMEA2000::SetForwardStream + - \ref tNMEA2000::EnableForward + - \ref tNMEA2000::SetForwardOwnMessages + - \ref tNMEA2000::SetForwardOnlyKnownMessages + - \ref tNMEA2000::SetForwardSystemMessages. #### Debug mode {#descDebugMode} @@ -543,6 +525,11 @@ Fast loop requirement means that you are not allowed to use any delay on your lo A practice has shown that random 10-50 ms delay is acceptable. In average loop time should be less than 2 ms. Also it is important that if you can have up 50 ms random delay, you may get in burst up to 90 frames (=1800 frames/s *0.05 s) during that time. This means that if your receive frame buffer is smaller, your device may loose some critical system messages. In small boat this amount is a bit theory, but anyway there are a lot of large messages just from GPS system so that they may occur time to time at same time. So it is better to prepare your device work in nearly any condition. +\sa + - \ref tNMEA2000::ParseMessages + - \ref tNMEA2000::SetN2kCANReceiveFrameBufSize() + - \ref tNMEA2000::SetN2kCANSendFrameBufSize() + ##### Some timing examples If you use DallasTemperature library as default you may block loop up to 700 ms. By using it "asynchronously", delays are smaller and may be acceptable. Best solution would be to use some kind of hardware based library like esp32-owb for ESP32. diff --git a/Documents/src/7_glossary.md b/Documents/src/7_glossary.md index f523b1dd..653ad875 100644 --- a/Documents/src/7_glossary.md +++ b/Documents/src/7_glossary.md @@ -120,7 +120,7 @@ I have advertised library to be certification ready. There are some functionalit - Save device source address and restore it on startup by using saved value on tNMEA2000::SetMode(). Track possible changes by using function tNMEA2000::ReadResetAddressChanged(). - Save device and system instances and restore them on startup with function tNMEA2000::SetDeviceInformationInstances().Track possible changes by using function tNMEA2000::ReadResetDeviceInformationChanged(). - Write ISO request handler to respond requests for all PGNs you are transmitting. Register handler with tNMEA2000::SetISORqstHandler(). -- Inherit tN2kGroupFunctionHandler and write handler, which responds all messages your device is transmitting. You can write own handler for all your messages or write common handler. See N2kGroupFunction.h. Note that if you accept message period or offset change, you should also save them and restore them on startup. Also if you handle PGN data instance change, you need to save that too. +- Inherit tN2kGroupFunctionHandler and write handler, which responds all messages your device is transmitting. You can write own handler for all your messages or write common handler. See N2kGroupFunction.h. Note that if you accept message period or offset change, you should also save them and restore them on startup. Also if you handle PGN data instance change, you need to save that too. Register handler with tNMEA2000::AddGroupFunctionHandler(). - Save both installation descriptions and restore them on startup with function tNMEA2000::SetConfigurationInformation(). Track possible changes by using function tNMEA2000::ReadResetInstallationDescriptionChanged(). For commercial certified devices you need also (prices at 2024 are for members): diff --git a/Documents/src/changes.md b/Documents/src/changes.md index f3064fe4..8a49042e 100644 --- a/Documents/src/changes.md +++ b/Documents/src/changes.md @@ -1,6 +1,10 @@ # Changes to the Library {#changes} \tableofcontents +## 02.02.2024 + +- Document update. Updates on document sources and code sources. + ## 22.01.2024 - Fixed compiler error in case #define N2K_NO_HEARTBEAT_SUPPORT 1 has been used. diff --git a/src/NMEA2000.h b/src/NMEA2000.h index e0537be2..33d3fcc4 100644 --- a/src/NMEA2000.h +++ b/src/NMEA2000.h @@ -25,14 +25,19 @@ /*************************************************************************//** * \file NMEA2000.h - * \brief This file contains the class tNMEA2000, which is consists the main + * \brief This file contains the class tNMEA2000, which consists the main * functionality of the library * - * With NMEA2000 device class you can send, read and forward messages to - * NMEA2000 bus. As default library creates a system, which acts like Actisense - * NGT NMEA2000->PC interface forwarding all messages from bus to PC. By - * hanging mode to N2km_NodeOnly, one can make e.g. temperature source - * device to NMEA2000 bus. + * With tNMEA2000 class you can easily communicate with NMEA2000 bus. + * Library can be used for any kind of NMEA2000 bus device needs from + * bus traffic listener to complex MFD system. + * + * As default library simply reads all messages from bus and forwards + * them to defined forward stream (e.g., Serial) in Actisense format. + * Using N2km_ListenAndNode mode, one can make NMEA2000 compatible devices and + * even NMEA2000 certified devices by implementing all \ref secRefNMEA2000Certification + * requirements. Simple example is bus device, which provides some sensor + * like temperature, battery or engine information to the bus to be shown or MFD. * * For detailed description see \ref tNMEA2000. * @@ -104,30 +109,30 @@ /************************************************************************//** * \class tNMEA2000 - * \brief NMEA2000 device class definition. + * \brief tNMEA2000 device class definition. * \ingroup group_core * - * With NMEA2000 device class you can send, read and forward messages to - * NMEA2000 bus. As default library creates a system, which acts like Actisense - * NGT NMEA2000->PC interface forwarding all messages from bus to PC. By - * hanging mode to N2km_NodeOnly, one can make e.g. temperature source - * device to NMEA2000 bus. + * With tNMEA2000 class you can easily communicate with NMEA2000 bus. + * Library can be used for any kind of NMEA2000 bus device needs from + * bus traffic listener to complex MFD system. * - * \note Each device on NMEA2000 bus should have own address on range 0-253. + * As default library simply reads all messages from bus and forwards + * them to defined forward stream (e.g., Serial) in Actisense format. + * Using N2km_ListenAndNode mode, one can make NMEA2000 compatible devices and + * even NMEA2000 certified devices by implementing all \ref secRefNMEA2000Certification + * requirements. Simple example is bus device, which provides some sensor + * like temperature, battery or engine information to the bus to be shown or MFD. * - * This class uses J1939 automatic \ref secRefTermAddressClaiming "address claiming" - * (or dynamic addressing). So that you can start your device with some address set by method - * \ref SetMode(). It is also important to set your device - * \ref secRefTermNAME with method \ref tNMEA2000::SetDeviceInformation() so that - * it would be unique. + * Class can be used just for reading bus data. More common is to use it as bus device + * (also called node). For bus device mode you provide required basic information + * about device to class, which then takes care of informing other devices on the bus in + * standard way. For other message handling class provides callback methods and method + * to send messages. * - * If you do not set "name" to unique, you devices changes address on start - * randomly. In principle they should still work fine. - * It is also good idea to save device address to the EEPROM. In this way if - * you connect two of your devices to the bus, they will do the automatic - * address claiming. If later save address to the EEPROM and use that on next - * start, they does not need to change address anymore. See also method - * \ref tNMEA2000::ReadResetAddressChanged(). + * Each device on NMEA2000 bus will have own source address on range 0-251. Class takes + * care of \ref secRefTermAddressClaiming "address claiming" (also called dynamic addressing) + * without any developer interaction. + * Class also provides \ref descMultiDeviceSupport "multi device support", if that is necessary. */ class tNMEA2000 { @@ -668,32 +673,40 @@ class tNMEA2000 /************************************************************************//** * \enum tN2kMode - * \brief System mode. Meaning how the device will behave on the + * \brief System mode defines how the device will behave on the * NMEA2000 bus + * + * \sa + * - \ref secDeviceModes + * - \ref tNMEA2000::SetMode() */ typedef enum { - /** Default mode. Listen bus and forwards messages to default - * port in Actisense format. You can not send any data to the bus. + /** Default mode. Listen bus and if message forwarding has been + * enabled forwards messages to defined stream in Actisense format. + * You can not send any data to the bus. + * See example DataDisplay.ino. */ N2km_ListenOnly, - /** This is for devices, which only sends data to the bus e.g. + /** In this mode device acts as active bus device like display, * RPM or temperature monitor. Remember to set right device - * information first. + * information first. Messages will not be forwarded. + * Look example TemperatureMonitor.ino. */ N2km_NodeOnly, - /** In this mode, device can be e.g. temperature monitor and - * as N2km_ListenOnly. + /** Mode is as N2km_NodeOnly plus you can control + * \ref secMessageforwarding "message forwarding". */ N2km_ListenAndNode, - /** Only for message sending. Device will not inform itself + /** Only for message sending. Device will not inform itself * to the bus. Messages will not be forwarded to the stream. * By setting message handler, you can still read messages * and handle them by yourself. */ N2km_SendOnly, - /** Listen bus and forwards messages to default port in + /** Listen bus and forwards messages to given forward stream in * Actisense format. Messages can be send. Device will not - * inform itself to the bus. + * inform itself to the bus. This is mode is usable e.g., with + * NMEA Simulator . */ N2km_ListenAndSend } tN2kMode; @@ -994,34 +1007,56 @@ class tNMEA2000 }; protected: - /** \brief Buffer for received messages */ + /** \brief Buffer for receiving messages + * \sa + * - \ref MaxN2kCANMsgs + * - \ref tNMEA2000::SetN2kCANMsgBufSize() + */ tN2kCANMsg *N2kCANMsgBuf; - /** \brief Max number CAN messages that can go to the buffer - * \ref N2kCANMsgBuf */ + /** \brief Size of N2kCANMsgBuf receiving message buffer + * \sa + * - \ref N2kCANMsgBuf + * - \ref tNMEA2000::SetN2kCANMsgBufSize() + */ uint8_t MaxN2kCANMsgs; - /** \brief Buffer for send out CAN messages + /** \brief Buffer for library send out CAN frames * * CANSendFrameBuf is library internal buffer for frames waiting for sending. If - * CanSendFrame() can not send frame, frame will be stored to this buffer and its - * sending will be tried again on next ParseMessages() call. + * CanSendFrame() can not send or buffer frame to driver buffer, frame will be + * stored to this buffer and its sending will be tried again on next SendMsg() or + * ParseMessages() call. + * + * Inherited driver class can split size of MaxCANSendFrames to driver buffer + * and library buffer. Inherited class can even disable library buffer. * - * If inherited class "driver" has own send buffer, size of CANSendFrameBuf can be - * set to minimum 2 on inherited \ref InitCANFrameBuffers(). + * \ref InitCANFrameBuffers(). */ tCANSendFrame *CANSendFrameBuf; - /** \brief Max number of send out CAN messages that can go to the buffer - * \ref CANSendFrameBuf - * \sa \ref InitCANFrameBuffers()*/ + /** \brief Size of CANSendFrameBuf or before initialization requested + * total frame buffering size. + * + * Member has two function. On setup program can request + * \ref tNMEA2000::SetN2kCANSendFrameBufSize, which will be saved to this member. + * Inherited tNMEA2000::InitCANFrameBuffers can split size to driver buffer and + * library buffer. + * + * \sa + * - \ref tNMEA2000::SetN2kCANSendFrameBufSize() + * - \ref CANSendFrameBuf + * - \ref InitCANFrameBuffers() + */ uint16_t MaxCANSendFrames; - /** \brief Next read index for the CAN message Send buffer + /** \brief Next read index for the library CAN send frame buffer. */ uint16_t CANSendFrameBufferWrite; - /** \brief Next write index for the CAN message Send buffer + /** \brief Next write index for the library CAN send frame buffer. */ uint16_t CANSendFrameBufferRead; /** \brief Max number received CAN messages that can go to the buffer - * \sa \ref InitCANFrameBuffers() + * \sa + * - \ref tNMEA2000::SetN2kCANReceiveFrameBufSize() + * - \ref InitCANFrameBuffers() */ uint16_t MaxCANReceiveFrames; @@ -1046,11 +1081,10 @@ class tNMEA2000 protected: /*********************************************************************//** - * \brief Send a CAN Frame + * \brief Abstract class for sending a CAN Frame * - * This Virtual function will be overridden by a derived class for - * specific interfaces according to the hardware which is used. - * Currently there are own classes like NMEA2000_Teensyx, NMEA2000_teensy, + * Driver writer must override this function for specific CAN interface. + * Currently there are driver classes like NMEA2000_Teensyx, NMEA2000_teensy, * NMEA2000_esp32, NMEA2000_due, NMEA2000_mcp, NMEA2000_avr, NMEA2000_mbed * and NMEA2000_socketCAN. * @@ -1067,28 +1101,25 @@ class tNMEA2000 virtual bool CANSendFrame(unsigned long id, unsigned char len, const unsigned char *buf, bool wait_sent=true)=0; /*********************************************************************//** - * \brief Open the CAN Interface + * \brief Abstract class for initializing and opening CAN interface. * - * This Virtual function will be overridden by a derived class for - * specific interfaces according to the hardware which is used. - * Currently there are own classes like NMEA2000_Teensyx, NMEA2000_teensy, + * Driver writer must override this function for specific CAN interface. + * Currently there are driver classes like NMEA2000_Teensyx, NMEA2000_teensy, * NMEA2000_esp32, NMEA2000_due, NMEA2000_mcp, NMEA2000_avr, NMEA2000_mbed * and NMEA2000_socketCAN. * * \sa \ref secHWlib * - * \retval true Success - * \retval false currently prevent accidental by second instance. - * Maybe possible in future. + * \retval true Initialize and open success + * \retval false Initialize or open failed. */ virtual bool CANOpen()=0; /*********************************************************************//** - * \brief Get a CAN Frame + * \brief Abstract class for reading frame from driver class. * - * This Virtual function will be overridden by a derived class for - * specific interfaces according to the hardware which is used. - * Currently there are own classes like NMEA2000_Teensyx, NMEA2000_teensy, + * Driver writer must override this function for specific CAN interface. + * Currently there are driver classes like NMEA2000_Teensyx, NMEA2000_teensy, * NMEA2000_esp32, NMEA2000_due, NMEA2000_mcp, NMEA2000_avr, NMEA2000_mbed * and NMEA2000_socketCAN. * @@ -1103,9 +1134,23 @@ class tNMEA2000 * \brief Initialize CAN Frame buffers * * This will be called on \ref tNMEA2000::Open() before any other - * initialization. Inherit this, if buffers can be set for the driver - * and you want to change size of library send frame buffer size. See e.g. - * NMEA2000_teensy.cpp. + * initialization. + * + * Driver writer must inherit this for low level driver class and create + * driver internal send and receive buffers according requested + * MaxCANSendFrames and MaxCANReceiveFrames sizes. Driver class can + * handle completely send and receive buffers or after buffers has been + * initialized set MaxCANSendFrames for size of library buffer and call + * tNMEA2000::InitCANFrameBuffers(); + * + * Inherited function can also override requested MaxCANSendFrames + * and MaxCANReceiveFrames in case they are set e.g., too small or too large. + * + * See e.g., tNMEA2000_Teensyx::InitCANFrameBuffers() on NMEA2000_teensyx.cpp. + * + * \sa + * - \ref tNMEA2000::SetN2kCANSendFrameBufSize() + * - \ref tNMEA2000::SetN2kCANReceiveFrameBufSize() */ virtual void InitCANFrameBuffers(); #if defined(DEBUG_NMEA2000_ISR) @@ -1137,7 +1182,7 @@ class tNMEA2000 bool SendFrame(unsigned long id, unsigned char len, const unsigned char *buf, bool wait_sent=true); /*********************************************************************//** - * \brief Get the Next Free CAN Frame from \ref CANSendFrameBuf + * \brief Get the Next Free CAN Frame from \ref CANSendFrameBuf * \return tCANSendFrame* */ tCANSendFrame *GetNextFreeCANSendFrame(); @@ -1284,8 +1329,11 @@ class tNMEA2000 * If the node is not \ref N2km_SendOnly or \ref N2km_ListenAndSend this * function chooses the correct handler for the given system message. * - * \sa \ref HandleISORequest, \ref HandleISOAddressClaim, - * \ref HandleCommandedAddress, \ref HandleCommandedAddress + * \sa + * - \ref HandleISORequest + * - \ref HandleISOAddressClaim + * - \ref HandleCommandedAddress + * - \ref HandleCommandedAddress * * \param MsgIndex Message Index on \ref N2kCANMsgBuf * \retval true message was handled @@ -1456,7 +1504,10 @@ class tNMEA2000 /*********************************************************************//** * \brief Is message forwarding enabled - * \sa \ref ForwardMode, \ref N2kMode + * \sa + * - \ref ForwardMode + * - \ref N2kMode + * * Checks if forwarding i enabled and the node is not \ref N2km_SendOnly * * \retval true @@ -1538,7 +1589,7 @@ class tNMEA2000 bool IsValidDevice(int iDev) const { return (iDev>=0 && iDev