NMEA 2000 Shield

Thanks. It is really nice to see that people has started to use library for real devices. Maybe we could in future have simple and cheap NMEA 2000 devices available.

I personally prefer small sized simple devices spreaded around. It is rather hard to organize centralize cabling to one point for everything. With the central cabling there is also problem with noise specially with analog signals. So instead use idea of NMEA 2000 bus so that you would have bus cable through the boat and then e.g. one "sensor device" on each tank or battery. E.g. Maretron already have small tank sensor.

Also I like more simple header connector as used in DeviceNET (see on Farnell codes 1717025 and 1717013) instead of expensive NMEA 2000 connector, which also takes a lot of space. I have made several split boxes with those for price of one "real" connector. My 5 connector splitbox requires only about 45x100x40 box.

Then if you are sailor, you may think that 10 small Teensy will take too much power. For slow data like tank and battery sensors they could measure value e.g. every minute and send it to the bus and then goto low power sleep. To arrange that one needs one master, which collects data and forwards it to bus every second or two to keep MFD happy. When I have time, I'll make sample for that.

Hopefully you got ideas of my opinions.

And I'll be happy, If I will get one board. I could replace my "Alba Combi" with it. You can send me private post for further information.

bwz0:
Thank you! Will your library also work with the Teensy 3.6 and the "Dual CAN-Bus adapter for Teensy 3.5, 3.6" https://www.tindie.com/products/Fusion/dual-can-bus-adapter-for-teensy-35-36/ ?

It may require new class instead of old NMEA2000_Teensy. I'll try to study it.

timolappalainen:
It may require new class instead of old NMEA2000_Teensy. I'll try to study it.

I ordered it, so I'll try to let you know when I get around to try it in a few weeks time.

Hi Timo,
I have one NSS9 Evo3 Simrad MFD and i just got from Ebay AC42 simrad autopilot computer that i interfaced to the MFD. I added Heading and Rudder Angle output from the Teensy prototype board and at first it all appears good. I can read the heading and the rudder angle on the MFD. then I tried to commission the autopilot and as a part to the there is a sequence that one needs to perform to set the hard over to hard over limits. With my little cheat board with potentiometer on one of the analog inputs i could perfectly simulate rudder movement. the MFD shows me the rudder going to port and then to Starboard, then I center it as the calibration routine for the AP requires, but unfortunately the rudder calibration never completes, therefore I cant continue with the AP commissioning. Im sure you are wondering why i am asking you that, as you probably don't have the same AP and the chances are you don't know what the issue is, but I wanted to pick your brain on rudder PGN.
I did some digging and i found out that the MFD that controls the AP could be seeing the rudder angle but the PGN overall is not what it expects to see, and that's why the rudder calibration fails.
The way I understand your code, for rudder angle in N2kMessages.h you have:

// Rudder
// Input:
// - RudderPosition         Current rudder postion in radians.
// - Instance               Rudder instance. 
// - RudderDirectionOrder   See tN2kRudderDirectionOrder. Direction, where rudder should be turned.
// - AngleOrder             In radians angle where rudder should be turned.
// Output:
//  - N2kMsg                NMEA2000 message ready to be send.
void SetN2kPGN127245(tN2kMsg &N2kMsg, double RudderPosition, unsigned char Instance=0, 
                     tN2kRudderDirectionOrder RudderDirectionOrder=N2kRDO_NoDirectioOrder, double AngleOrder=N2kDoubleNA);

inline void SetN2kRudder(tN2kMsg &N2kMsg, double RudderPosition, unsigned char Instance=0, 
                     tN2kRudderDirectionOrder RudderDirectionOrder=N2kRDO_NoDirectioOrder, double AngleOrder=N2kDoubleNA) {
  SetN2kPGN127245(N2kMsg,RudderPosition,Instance,RudderDirectionOrder,Angle Order);
}

so my conclusion was to write something like:

void SendN2KRudder() {
#define rudderUpdatePeriod 100
  static unsigned long RudderUpdated = millis();
  tN2kMsg N2kMsg;

  if ( RudderUpdated + rudderUpdatePeriod < millis() ) {
    RudderUpdated = millis();


    SetN2kRudder(N2kMsg, (avg2 / 2 - 280) / 3.14 / 18), 0); //avg2 is just function to read the pot and some random math to get my reading convert to rad angle that represents -40 to +40 deg


    NMEA2000.SendMsg(N2kMsg);
    delay (10);

  }
}

as I mentioned earlier it kind of works but can't complete the rudder calibration sequence. I called simrad tech support and they ask me to reset the AP settings and the MFD setting which i did later on, but it did not help.

then I start doubting the validity of the PGN I was outputting and experimented with passing more to the function (as you see fro my code I am passing only the rudder angle in rad and the instance number). once i added more then these two parameters, I got en error compiling. Regardless I continued digging and found something that really got me to write you this message. In your code the first parameter to output is the rudder angle followed by the instance number, etc, ets,
I did find the string for PGN127245 here:
http://www.nmea.org/Assets/july%202010%20nmea2000_v1-301_app_b_pgn_field_list.pdf

and at least to me looks that it has different order the definition in your code:

PGN 127245 Field Field Description
Rudder order command in direction or angle with current rudder angle reading.
1 Rudder Instance
2 Direction Order
3 Reserved Bits
4 Angle Order
5 Position
6 Reserved Bits

Do you have an example outputting
PGN 127245, while passing all parameters such as RudderDirectionOrder and RudderDirectionOrder?
just in case here is the link to the AP manual http://www.simrad-yachting.com/Root/Installation%20Manual/SimradYachting/English/AC12_AC42_IM_EN_20222568_E_w.pdf

First do not compare order of c++ function parameter list and the order of PGN definition. The point is that since you normally do not need all parameters, why you should have them in that order on function? If you want to compare, you have to goto look into the function code. But I think you do not need to, since I have tested rudder PGN against Actisense NMEA Reader as I allways do with new PGNs. Also, if data would be wrong, you would not see any rudder angle on your MFD.

Then about calibration. There are two possibilities:

  1. AP expects also direction order (and order angle) parameter set right.
  2. AP expects specific Simrad rudder sensor and tries to calibrate that with proprietary command, which we do not have any information.

Case 1 you can test, if you calculate from changes right value for the direction order and put some value for direction angle.

Case 2 is complicate, since you should do reverse engineering with full Simrad system. Can be time consuming.

It is also possible that AP uses standard Group Function commands to set direction order fields for you Teensy. If you have latest library version, it will automatically respond for that command with "no support". But it may be that AP is not either happy for that. You can listen data with some device - even with your Teensy by using mode N2km_ListenAndNode and enable forwarding. Then you look does AP send any 126208 PGN:s (Group Function PGN) to your device.

Offset you can easily program to your Teensy, but can you manually set hard limits. If you can, then you could forget automatic calibration.

Hi Timo,
thanks for the reply.
I did spend all night again looking at the possible solution
I noticed that the the new library required me to change

const tProductInformation BatteryMonitorProductInformation PROGMEM={
                                       1300,                        // N2kVersion
                                       192,                         // Manufacturer's product code
                                       "SeaWitch_Monitor",    // Manufacturer's Model ID
                                       "1.0.0.13 (2016-09-19)",     // Manufacturer's Software version code
                                       "1.0.0.0 (2015-08-03)",      // Manufacturer's Model version
                                       "12340002",                  // Manufacturer's Model serial code
                                       1,                           // SertificationLevel
                                       1                            // LoadEquivalency
                                      };

to

const tNMEA2000::tProductInformation BatteryMonitorProductInformation PROGMEM={
                                       1300,                        // N2kVersion
                                       192,                         // Manufacturer's product code
                                       "SeaWitch_Monitor",    // Manufacturer's Model ID
                                       "1.0.0.13 (2016-09-19)",     // Manufacturer's Software version code
                                       "1.0.0.0 (2015-08-03)",      // Manufacturer's Model version
                                       "12340002",                  // Manufacturer's Model serial code
                                       1,                           // SertificationLevel
                                       1                            // LoadEquivalency
                                      };

and

const tNMEA2000::tProgmemConfigurationInformation BatteryMonitorConfigurationInformation PROGMEM={
                                       "SeaNav, john.doe@unknown.com", // Manufacturer information
                                       "First attempt", // Installation description1
                                       "No real information send to bus" // Installation description2
                                      };

to

const char BatteryMonitorManufacturerInformation [] PROGMEM = "John Doe, john.doe@unknown.com"; 
const char BatteryMonitorInstallationDescription1 [] PROGMEM = "Just for sample"; 
const char BatteryMonitorInstallationDescription2 [] PROGMEM = "No real information send to bus";

in order to compile properly. I'm not saying that's wrong :slight_smile: I'm just beginner in programing and i have no idea most of the time why I'm making changes. I just compare the new examples to my code and try to make it work :slight_smile:

Once it start working I also noticed that the device name is not showing up anymore on my MFD but rather show "???". I bet this is a result of my attempt to get my code running with the new library and the changes I did above.

IMG_4999.JPG

Then I noticed something else - the "rudder angle" is shown as (Simrad) group, while the "commanded rudder direction" is shown as (Global) group. All the rest of the devices on the bus are part of the Simrad group. I have no idea why "commanded rudder direction" is shown as (Global) group and it can't be changed as there is no Simrad selection in the dropdown menu as there is on the "rudder angle" (see the first picture).

IMG_4997.JPG

IMG_4998.JPG

Then I tried to change the instance from 000 to 001 and the device will not take it. I don't think this was something I was successful of doing before upgrading the library either.

IMG_4996.JPG

Let's go back to the problem at hand. My rudder angle simulator is not being liked by Simrad AP calibration procedures so im trying to understand why and possibly fix it. I did read your email carefully and some parts I understood, but for other parts my programing experience is not enough to make the right conclusions from your email.
I did program another Teensy as listener and captured the bus during my attempt to calibrate the rudder angle on the AP from the MFD. Then I searched for PGN 126208 but it wasn't there. I have attached the capture to this post. Just for clarification, I have the following devices on the N2K bus: Simrad NSS EVO3 MFD, Teensy rudder angle simulator, Simrad AC42 AP computer and another Teensy that is setup with the listener only code. I see more devices showing up in the log file, but I believe that the MFD separates the chart-ploter, the depth-sounder, the AP controller, etc.etc.

One think I would really like if you can help me with is what is the syntax of the parameters of the rudder message. I could make it compile only with passing 2 parameters. if I attempted to pass more than 2 i got errors compiling (from looking at your code i think they should be 4). Very frustrating as my abilities to understand the code are not that great. can you tell me what parameters I pass to the rudder function, what is the syntax and what each one does?

I see "SetN2kPGN127245(N2kMsg,RudderPosition,Instance,RudderDirectionOrder,AngleOrder)" in your library
and I tried many different combinations in my code, but again it was only successful with 2 parameters
e.i.

SetN2kRudder(N2kMsg,((avg2-512)/3.1415926535897932384626433832795/180),1); 
    //avg2 is just function to read the pot and some random math to get my reading convert to rad angle that represents -40 to +40 deg

Thanks in advance for spending time to look at my problems.

PS
I just received email that they are parting the big production PCB boards and my PCBs will be send in the mail to me in 48hrs! How exciting! I will post some pic immediately when I get them.

Atopilot calibration capture.txt (250 KB)

Have you noticed that on the library main page, if you scroll down there is "Changes" section, where specially compitibility changes has been notified. Time to time I unfortunately find something, which is not good for some reason, so I have to change library compatibility. So please read "Changes" section allways carefully.

I also prefer not to copy from examples totally as they are. So. e.g. change name BatteryMonitorProductInformation for e.g. SeaSwitchRudderAngleProductInformation. Also change contents like versions, dates, etc. to match your code. Also remember to define LoadEquivalency to right value. This is n on formula n * 0.050 mA, which tells how much power your device takes from the bus.

You can not change instance of "Rudder Angle"-device, because currently it requires to write specific "Command Group Function" handler for rudder PGN. Currently default handler responds that change is not supported.

You can call it e.g.:
SetN2kRudder(N2kMsg,DegToRad(RudderAngle),1,N2kRDO_MoveToStarboard,DegToRad(RudderCommandedAngle));

Note that you can use library function DegToRad() for conversion.

I'll recheck that product information should be send right to the bus. I'll inform later.

I tested product information sending and it seem to work. But I have to test it also with MFD. Your MFD should query product information from your device. I have to check could support for Comand Group Function cause that some MFD:s could query product information by using it instead of ISO request.

Hi Timo,
Thank you for your instructions. After I received your reply I managed to output completely
PGN127245 string, I think... and read the compatibility section in your library so now I see the proper name, but stiill struggling outputting rudder angle on the top of the other two with your multi device sample.
Back to the problem on hand: On the Simrad display I still see difference in the groups. The rudder angle Bellingham nags to the simrad group and the rudder commanded angle belongs to the global group. The AP rudder angle calibration still does not work. :frowning: at this point I am almost positive that maybe the AP needs some response from the Rudder angle sensor, but not too sure as I couldnt not find anything like that in my data capture during an attempt to calibrate the rudder portion of the AP dock setup.
The datasheet for the Simrad N2K rudder angle sensor refers output only PGN127245.
http://www.simrad-yachting.com/Root/Simrad-Documents/RF25_Instruction_Rev.A.pdf
So I will still try to concentrate providing the proper output from my simulated sensor. With your help I got the sintax ok, but the data I put in it is still bogus... I output proper rudder angle from -35 to 35 deg, that incontrol with potentiometer, but the rest of the data is static. I just plugged some direction and random number for commanded angle DegToRad(19)
I would like to understand little better how this works, so I do have few questions about the paremeters.
setN2kRudder(N2kMsg,DegToRad(RudderAngle),1,N2kRDO_MoveToStarboard,DegToRad(RudderCommandedAngle);

  1. What is the second parameter "1"? Is that the instance?
  2. What is the "MoveToStarboard" how and when you change it to "MoveToPort"? In your function I see 0 is unknown, 1 starboard, 2 port(if I remember correct...) so it surprised me that I needed to put in the string rather than the un. char 0, 1, or 2 which of course I was trying without success before your explanation. Do I have to create a function to compare the readings and then based on the fact that they get larger or smaller one replaces MoveToPort with MoveToStarboard and vice versa?
  3. How you calculate "RudderCommandedAngle"?
  4. Why are these parameters required? If they are what I think they are, why are they not calculated by the AP or by AP controller? Why rudder indicator should tell ap in which direction is moving? The APis the one issuing the commands to the steering to move so it should know already...
  1. It is instance value. If you would have 2 rudder sensors, they all should have different instance value.

  2. N2kRDO_MoveToStarboard is not a string. Strings will be marked on code with "...". N2kRDO_MoveToStarboard is a value of enumerated type tN2kRudderDirectionOrder. The point for enumerated types is to limit values only to allowed group and also make code readable. If that would be define as uchar, you should comment you code e.g. ...1 /* move starboard */... and also you could set value to e.g. 75, which is not allowed for that setting.

I think normally this PGN comes from AP, which uses integrated sensor. So then AP can say "Current Rudder Angle is 5, I am moving rudder To Starboard and the goal is 15" with this PGN. When you have standalone sensor, it should not use other than angle and instance values. For rest values it should give defaults as I have in function (N2kRDO_NoDirectionOrder,N2kDoubleNA). I was just guessing that may be that during calibration your AP will expect those commaned values. Can be wrong. But if you want to try, you should provide delta of angle.

  1. Partially answered on 2. Also here you can ted delta angle.

  2. Also answered on 2. Stand alone sensor should not provide these values.

What manufacturer code you have used for you Rudder sensor? If you have used 1875 (Simrad), then your AP may think that is manufactured by Simrad and may try to use their proprietary calibration system. I do not know any std sensor calibration system, so it is best to mfg code, which is not know (=registered) - e.g. 2046.

From your log I can see that probably your AP (source 0) is providing Rudder PGN with all fields as "unavailable" value.

bwz0:
I ordered it, so I'll try to let you know when I get around to try it in a few weeks time.

There is now new version of NMEA2000 library, NMEA2000_teensy library and also under my repositories developed version of FlexCAN library.

I do not have dual CAN Teensy, so I can not test library with it. But you should not need NMEA2000 with dual CAN except if you are going to write some gateway system.

Hi timo,
I tried the new code with 3 device output and it worked well. Then I tried adding 3rd device and I wasn't successful outputting 3 devices. I read the documentation and it says it is for two devices can you please confirm that it is for two devices only.

Sorry, but I am bit lost. What do you mean with 2 devices? MultiDevice support? In principle it should have only memory limitation. Or something else?

LOL! I know what the "Or something else" limitation is :slight_smile: Most likely my ability to write code.
I will sweat it little longer and then if I don't succeed I'll post my code with the attempt to add another device. This time I thought I have it right, damn! LOL :confused:

With "Something else" I meant that do you mean with 2 devices multi device support or something else. Device has so many meanings that I was not sure.

And about the multi device support - keep you code simple at the beginning and use just single device definition like in the simple example TemperatureSensor. I started multi device thinking in the very beginning, but soon noticed that other devices on bus actually does not care what you say you are. Roughly saying you can show you device as wind instrument and send only cabin temperature. Others are happy for someone sending cabin temperature.

Hi,
I did try hard for a long time and finally i discovered the "magic bullet"
Apparently I was missing something very simple

 NMEA2000.SetDeviceCount(2);

After I changed the number of devices to 3 it all worked as suppose to.
I did laugh at myself... for a long time. Still shorter time than what I spent trying to figure out why only 2 devices were showing up on the MFD.

I have a question about
NMEA2000.SetProductInformation
and
NMEA2000.SetDeviceInformation
it appears that if the second one is remed out everithing still works, or does it?

In these small devices it is not good to use new/free to extend vectors. That is why you have to first say that "I'll have 3 devices, so reserve me this big vector" with NMEA2000.SetDeviceCount(3); So there will be only one new commad to make vector and no free to cause hole to small memory.

It is best to define both. SetDeviceInformation is the most important, since it defines unique ID for you device. Specially serial number should be different. If you do not define SetDeviceInformation, my library tries to survive by changin some instance value, but your MFD probably can't handle it.

SetProductInformation you need so that your MFD will show different names. Otherwise it will show same name for both devices.

I'm very excited as the boards arrived on Friday. Here is the first look:
26907739-C07B-4A9A-98EE-2982F33C7898.JPG
Next stop is assambly and revival of the first 3 boards

Bonjour Timo,

I am still waiting for the Mega board I ordered, so I continue my test with Arduino Boards.
Anyway, parts of my latest sketch work, but not together.

There are 2 main parts:

  • Acquiring datas from the serial port of a Victron BMV700 battery Monitor: Current (A), Voltage (V) and capacity (%)
  • Sending those datas to the SeaTalkNG network (NMEA2000).
    I can send datas to a Raymarine MFD or B&G display if I write test values directly in the
    SetN2kDCBatStatus(N2kMsg,0,13.87,5.12,35.12,1) command. That is ok.

I can get data from the BMV700 and print them on the serial Monitor. So I gess this part works.
But for that, I must comment (//) all the lines who send the N2K messages, especially this command
// tN2kMsg N2kMsg;

As soon as I uncomment these lines, I only get null values for Current, Voltage and Capacity
I get :
“0.00 0.00 0.00”
, instead of, for example:
“-3.45 12.90 95.45”

     // Print the values on the Serial Monitor
     Serial.print (Current);
     Serial.print ("   ");
     Serial.print (Voltage);
     Serial.print ("   ");
     Serial.println (SOC);

During the test I have modified the structure of your Battery Monitor Example, copying the SendN2kBattery subroutine in the main loop.

Why the tN2kMsg N2kMsg; command seems to RAZ my datas ?

Regards, thanks for your great job.
François-Xavier.

// From http://www.jw5zla.com/?p=7 Interfacing the Victron BMV-700
// and Timo Lappalainen'NMEA2000 libraries

#include <Arduino.h>
#include <SoftwareSerial.h>
#include <SPI.h>
 
SoftwareSerial Victron(0,3);    // RX= Pin 0, TX= Pin 3(Not used)

char p_buffer[80];
#define P(str) (strcpy_P(p_buffer, PSTR(str)), p_buffer)

#define N2k_SPI_CS_PIN 9        // Valeur 10 pour le shield www.elecrow.com, 9 pour le Seeedunio 

#include <NMEA2000_CAN.h>       // This will automatically choose right CAN library and create suitable NMEA2000 object
#include <N2kMessages.h>
 
char c;
String V_buffer;  // Buffer to hold data from the Victron monitor
String E_buffer;  // Buffer to hold data from the ethernet shield
 
float Current;
float Voltage;
float SOC;


// List here messages your device will transmit.
const unsigned long TransmitMessages[] PROGMEM={127506L,127508L,0};

// ---  Example of using PROGMEM to hold Product ID.  However, doing this will prevent any updating of
//      these details outside of recompiling the program.
const tProductInformation BatteryMonitorProductInformation PROGMEM={
                                       1300,                        // N2kVersion
                                       100,                         // Manufacturer's product code
                                       "Moniteur Batterie Arduino", // Manufacturer's Model ID C'est la ligne qui est détectée comme appareil par le Triton
                                       "1.0.0.0 (2017-06)",         // Manufacturer's Software version code
                                       "1.0.0.0 (2017-06)",         // Manufacturer's Model version
                                       "00000001",                  // Manufacturer's Model serial code
                                       0,                           // SertificationLevel
                                       1                            // LoadEquivalency
                                      };                                      

// ---  Example of using PROGMEM to hold Configuration information.  However, doing this will prevent any updating of
//      these details outside of recompiling the program.
const char BatteryMonitorManufacturerInformation  [] PROGMEM = "FX VAN THUAN"; 
const char BatteryMonitorInstallationDescription1 [] PROGMEM = "BMV700 Victron & Arduino"; 
const char BatteryMonitorInstallationDescription2 [] PROGMEM = "Send Informations to the N2K bus"; 


 
void setup()
{
  Serial.begin(19200);
  Victron.begin(19200);


  // Set Product information
  NMEA2000.SetProductInformation(&BatteryMonitorProductInformation );
  // Set Configuration information
  NMEA2000.SetProgmemConfigurationInformation(BatteryMonitorManufacturerInformation,BatteryMonitorInstallationDescription1,BatteryMonitorInstallationDescription2);
  // Set device information
  NMEA2000.SetDeviceInformation(1,      // Unique number. Use e.g. Serial number.
                                170,    // Device function=Battery. See codes on http://www.nmea.org/Assets/20120726%20nmea%202000%20class%20&%20function%20codes%20v%202.00.pdf
                                35,     // Device class=Electrical Generation. See codes on  http://www.nmea.org/Assets/20120726%20nmea%202000%20class%20&%20function%20codes%20v%202.00.pdf
                                999     // Just choosen free from code list on http://www.nmea.org/Assets/20121020%20nmea%202000%20registration%20list.pdf                               
                               );                          
                                 
  // Uncomment 3 rows below to see, what device will send to bus                           
  // Serial.begin(115200);
   NMEA2000.SetForwardStream(&Serial);
   NMEA2000.SetForwardType(tNMEA2000::fwdt_Text);     // Show in clear text. Leave uncommented for default Actisense format.
 Serial.println ("ETAPE 1");
  // If you also want to see all traffic on the bus use N2km_ListenAndNode instead of N2km_NodeOnly below
  NMEA2000.SetMode(tNMEA2000::N2km_NodeOnly,22);
  // NMEA2000.SetDebugMode(tNMEA2000::dm_ClearText);     // Uncomment this, so you can test code without CAN bus chips on Arduino Mega
  // NMEA2000.EnableForward(false);                      // Disable all msg forwarding to USB (=Serial)
   NMEA2000.SetN2kCANMsgBufSize(2);                      // For this simple example, limit buffer size to 2, since we are only sending data
   NMEA2000.SetN2kCANSendFrameBufSize(30);               // essai suite lecture forum
  Serial.println ("ETAPE 2");
    NMEA2000.Open();
  Serial.println ("ETAPE 3");

  
}
 
void loop() {
 
  // Victron 
 
  if (Victron.available()) {
    c = Victron.read();
 
    if (V_buffer.length() <80) {
      V_buffer += c;
    }
 
    if (c == '\n') {  // New line.
 
      if (V_buffer.startsWith("I")) {
        String temp_string = V_buffer.substring(V_buffer.indexOf("\t")+1);
        double temp_int = temp_string.toInt();
        Current = (float) temp_int/1000;
      }     
 
      if (V_buffer.startsWith("V")) {
        String temp_string = V_buffer.substring(V_buffer.indexOf("\t")+1);
        int temp_int = temp_string.toInt();
        Voltage = (float) temp_int/1000;
      }     
 
      if (V_buffer.startsWith("SOC")) {
        String temp_string = V_buffer.substring(V_buffer.indexOf("\t")+1);
        int temp_int = temp_string.toInt();
        SOC = (float) temp_int/10;
      }     
      V_buffer="";
    }
   
  }
     // Print the values on the Serial Monitor
     Serial.print (Current);
     Serial.print ("   ");
     Serial.print (Voltage);
     Serial.print ("   ");
     Serial.println (SOC);


//---------------------- Section N2K 

     #define BatUpdatePeriod 1000 
     static unsigned long TempUpdated=millis();
// tN2kMsg N2kMsg;

   if ( TempUpdated+BatUpdatePeriod<millis() ) 
   {
        TempUpdated=millis();
    /*
    //SetN2kDCBatStatus(N2kMsg,0,13.87,5.12,35.12,1);     // test
    SetN2kDCBatStatus(N2kMsg,0,Voltage,Current,22.22,1);
    NMEA2000.SendMsg(N2kMsg);
    
    //SetN2kDCStatus(N2kMsg,1,0,0,56,92,38500,0.012);     // test
    SetN2kDCStatus(N2kMsg,1,0,N2kDCt_Battery,SOC,0,0,0);
    NMEA2000.SendMsg(N2kMsg);
    */
         Serial.print(millis()); Serial.println(", Battery send ready");

    
   }   // Fin de la boucle conditionnelle de temps

             
          NMEA2000.ParseMessages();
  
}

Are you are still using Uno, I think it is simple memory problem. When you have tN2kMsg N2kMsg; on loop, it will make a reservarion on stack for that object. The total size of N2kMsg is about 240 bytes. That is over 10% of Uno memory.

You can try to squeeze memory more.

  • Change NMEA2000.SetForwardStream(&Serial); -> NMEA2000.SetForwardStream(0);
  • comment NMEA2000.SetForwardType(tNMEA2000::fwdt_Text);
  • uncomment NMEA2000.EnableForward(false);
  • change NMEA2000.SetN2kCANSendFrameBufSize(30); -> NMEA2000.SetN2kCANSendFrameBufSize(20);
  • you can try even set NMEA2000.SetN2kCANMsgBufSize(1);

Make own functions for Victron and SendN2kInfo, which have own local variables and call them on loop. In this way it is sure that their local varables does not take memory from stack at same time.

Also you have buffers
char p_buffer[80];
...
String V_buffer; // Buffer to hold data from the Victron monitor
String E_buffer; // Buffer to hold data from the ethernet shield

If these are not needed globally, move them inside some function, so they will need RAM on stack, when needed and then released. I have not checked how String class works in point of memory use as global or local.

And also you have to define all options on NMEA2000_CompilerDefns.h to disable all new features to save memory.

I would say that using Uno is just banging your head to the wall!