SignalK / signalk-server

An implementation of a Signal K central server for boats.
http://signalk.org
Apache License 2.0
307 stars 152 forks source link

sending NMEA2000 via signalk-send-nmea2000 #1723

Closed Bein85 closed 5 months ago

Bein85 commented 5 months ago

I am stuck, I keep encountering this error message at Server Logs within SignalK:

Apr 19 20:06:51 TypeError: Cannot read properties of undefined (reading 'length') at CanbusStream.sendPGN (/usr/local/lib/node_modules/signalk-server/node_modules/@canboat/canboatjs/lib/canbus.js:265:19) at /usr/local/lib/node_modules/signalk-server/node_modules/@canboat/canboatjs/lib/canbus.js:87:12 at safeApply (/usr/local/lib/node_modules/signalk-server/lib/events.js:54:17) at safeEmit (/usr/local/lib/node_modules/signalk-server/lib/events.js:125:17) at emitWithEmitterId (/usr/local/lib/node_modules/signalk-server/lib/events.js:138:16) at Object.emit (/usr/local/lib/node_modules/signalk-server/lib/events.js:161:28) at send._inputCallback (/home/bein/.signalk/node_modules/@signalk/node-red-embedded/signalk-send-nmea2000.js:26:13) at /home/bein/.signalk/node_modules/@node-red/runtime/lib/nodes/Node.js:214:26 at Object.trigger (/home/bein/.signalk/node_modules/@node-red/util/lib/hooks.js:166:13) at Node._emitInput (/home/bein/.signalk/node_modules/@node-red/runtime/lib/nodes/Node.js:206:11) at Node.emit (/home/bein/.signalk/node_modules/@node-red/runtime/lib/nodes/Node.js:190:25) at Node.receive (/home/bein/.signalk/node_modules/@node-red/runtime/lib/nodes/Node.js:499:10) at deliverMessageToDestination (/home/bein/.signalk/node_modules/@node-red/runtime/lib/flows/Flow.js:800:40) at Immediate.<anonymous> (/home/bein/.signalk/node_modules/@node-red/runtime/lib/flows/Flow.js:816:21) at process.processImmediate (node:internal/timers:476:21) 

In my Node-Red function i have this code:

// Input object
var data = msg.payload;

// Convert string values to numbers if they are numbers
for (var key in data) {
    if (data.hasOwnProperty(key)) {
        // Check if the value is a valid number
        if (!isNaN(parseFloat(data[key]))) {
            data[key] = parseFloat(data[key]); // Convert to floating-point number
        }
    }
}

// Define mappings for Canboat message keys
var canboatMappings = {
    "BB_Oljetrykk": { "pgn": 127489, "engine": 1, "parameter": "Oil pressure" },
    "BB_Vanntemp": { "pgn": 127489, "engine": 1, "parameter": "Temperature" },
    "SB_Oljetrykk": { "pgn": 127489, "engine": 2, "parameter": "Oil pressure" },
    "SB_Vanntemp": { "pgn": 127489, "engine": 2, "parameter": "Temperature" },
    "BB_Turbotrykk": { "pgn": 127488, "engine": 1, "parameter": "Boost Pressure" },
    "SB_Turbotrykk": { "pgn": 127488, "engine": 2, "parameter": "Boost Pressure" },
    "BB_Turtall": { "pgn": 127488, "engine": 1, "parameter": "Speed" },
    "SB_Turtall": { "pgn": 127488, "engine": 2, "parameter": "Speed" }
};

// Create an array to hold NMEA 2000 messages
var nmea2000Messages = [];

// Iterate through the data and create NMEA 2000 messages
for (var key in data) {
    if (data.hasOwnProperty(key)) {
        var mapping = canboatMappings[key];
        if (mapping) {
            var value = data[key];
            // Check if the parameter needs conversion
            if (key.includes("trykk")) {
                // Convert from BAR to Pascal
                value *= 100000; // 1 BAR = 100,000 Pascal
            } else if (key.includes("temp")) {
                // Convert from Celsius to Kelvin
                value += 273.15; // Celsius to Kelvin conversion formula
            }
            // Construct the NMEA 2000 message using SetN2kPGN127489
            var message = {
                "pgn": mapping.pgn,
                "src": 34, // Assuming a specific source ID (replace with the appropriate value)
                "dst": 255,
                "fields": [
                    { "name": "EngineInstance", "value": mapping.engine },
                    { "name": "Parameter", "value": mapping.parameter },
                    { "name": "Value", "value": value.toFixed(2) } // Limit to 2 decimals
                ]
            };
            nmea2000Messages.push(message);
        }
    }
}

// Output the NMEA 2000 messages array
msg.payload = nmea2000Messages;
return msg; "

Output of the function looks like this:

[{"pgn":127489,"src":34,"dst":255,"fields":[{"name":"EngineInstance","value":1},{"name":"Parameter","value":"Oil pressure"},{"name":"Value","value":"0.00"}]},{"pgn":127489,"src":34,"dst":255,"fields":[{"name":"EngineInstance","value":1},{"name":"Parameter","value":"Temperature"},{"name":"Value","value":"284.65"}]},{"pgn":127488,"src":34,"dst":255,"fields":[{"name":"EngineInstance","value":1},{"name":"Parameter","value":"Boost Pressure"},{"name":"Value","value":"0.00"}]},{"pgn":127489,"src":34,"dst":255,"fields":[{"name":"EngineInstance","value":2},{"name":"Parameter","value":"Oil pressure"},{"name":"Value","value":"-1000.00"}]},{"pgn":127489,"src":34,"dst":255,"fields":[{"name":"EngineInstance","value":2},{"name":"Parameter","value":"Temperature"},{"name":"Value","value":"284.85"}]},{"pgn":127488,"src":34,"dst":255,"fields":[{"name":"EngineInstance","value":2},{"name":"Parameter","value":"Boost Pressure"},{"name":"Value","value":"0.00"}]},{"pgn":127488,"src":34,"dst":255,"fields":[{"name":"EngineInstance","value":1},{"name":"Parameter","value":"Speed"},{"name":"Value","value":"0.00"}]},{"pgn":127488,"src":34,"dst":255,"fields":[{"name":"EngineInstance","value":2},{"name":"Parameter","value":"Speed"},{"name":"Value","value":"0.00"}]}]
sbender9 commented 5 months ago

Your json is not correct. "fields" is not an array like that. It's an object.

sbender9 commented 5 months ago

Example here https://github.com/canboat/canboatjs/blob/master/test/pgns/127488.js

sbender9 commented 5 months ago

And why not put this stuff in signalk and use the sk-to-nmea2000 plugin?

Bein85 commented 5 months ago

good question, i have just started playing with SignalK and NMEA2000 signals. I have a Siemens PLC reading the sensor data from the engines, and use Node-Red to read from the PLC and brodcast data to my mobile. I wanted to send the same engine data to Raymarine Axiom plotter via PiCAN-m. since Node-Red was there, and a plugin was available i thought that was the easy solution. (i use the Node-Red plugin within SingalK)

How do i move it over to signalk?

sbender9 commented 5 months ago

Use send-path-value or send-delta and use the standard sk paths. Make sure use get the sk units right.

https://signalk.org/specification/1.7.0/doc/vesselsBranch.html

Bein85 commented 5 months ago

Thank you so much for the tips, i now use send-path-value and send-delta and let sk handle the data.