2020-02-24, 04:36 PM
(2020-02-21, 08:31 PM)kp.sov69 Wrote: So if i understand correctly you are able to control all the autopilot functions through the arduino serial connection sending messages back and forth through signalk? That seems like a very elegant solution for a helm based control and display interface. 32u4 based arduinos have an extra hardware uart and high speed built in usb serial.
Would love to see the code and implementation as well.
Here it is wiring diagram:
and arduino sketch is attached as well
sketch_nov11b.zip (Size: 90.73 KB / Downloads: 228)
arduino is USB connected to Raspberry Pi as /dev/ttyUSB1
Recently i have changed implementation on Raspberry Pi side. Now it is only using Node-Red dashboard in SignalK server without using Kplex (so, now it is working in Openplotter2 too)
the arduino and node-red code in the testing and debugging state... (just as proof of concept, but working at table perfectly : )
Here is is node-red flow
Code:
[
{
"id": "9a419ebb.65893",
"type": "switch",
"z": "ba70a55e.0d29a8",
"name": "Filter XXX to pypilot and another NMEA's to signalk",
"property": "payload",
"propertyType": "msg",
"rules": [
{
"t": "regex",
"v": "^(?=...*XXX)",
"vt": "str",
"case": false
},
{
"t": "regex",
"v": "^\\$",
"vt": "str",
"case": false
},
{
"t": "else"
}
],
"checkall": "true",
"repair": false,
"outputs": 3,
"x": 527,
"y": 388,
"wires": [
[
"195f2de4.b63742"
],
[
"fdd29b96.b7f2a8"
],
[]
],
"inputLabels": [
"Input"
],
"outputLabels": [
"XXX",
"Otherwise",
""
]
},
{
"id": "9f6be0e6.5389b",
"type": "tcp out",
"z": "ba70a55e.0d29a8",
"host": "localhost",
"port": "21311",
"beserver": "client",
"base64": false,
"end": false,
"name": "",
"x": 1184,
"y": 362,
"wires": []
},
{
"id": "195f2de4.b63742",
"type": "function",
"z": "ba70a55e.0d29a8",
"name": "Parse to Pypilot internal Signalk",
"func": "var th = msg.payload.replace(\"\\$APXXX,\",\"\").slice(0, -5).split(\",\");\n\nmsg.payload = '{\\\"name\\\": \\\"'+th[0]+'\\\", \\\"method\\\": \\\"set\\\", \\\"value\\\": '+th[1]+'}\\n'\nreturn msg;",
"outputs": 1,
"noerr": 0,
"x": 875,
"y": 328,
"wires": [
[
"9f6be0e6.5389b"
]
]
},
{
"id": "ebba1cdb.316b6",
"type": "tcp request",
"z": "ba70a55e.0d29a8",
"server": "localhost",
"port": "21311",
"out": "sit",
"splitc": " ",
"name": "",
"x": 350,
"y": 68,
"wires": [
[
"dcd047b9.925a68"
]
]
},
{
"id": "ebddce32.852eb",
"type": "inject",
"z": "ba70a55e.0d29a8",
"name": "",
"topic": "",
"payload": "[123,34,110,97,109,101,34,58,32,34,105,109,117,46,104,101,97,100,105,110,103,95,108,111,119,112,97,115,115,34,44,32,34,109,101,116,104,111,100,34,58,32,34,103,101,116,34,125,13,10,123,34,110,97,109,101,34,58,32,34,97,112,46,104,101,97,100,105,110,103,95,99,111,109,109,97,110,100,34,44,32,34,109,101,116,104,111,100,34,58,32,34,103,101,116,34,125,13,10,123,34,110,97,109,101,34,58,32,34,97,112,46,101,110,97,98,108,101,100,34,44,32,34,109,101,116,104,111,100,34,58,32,34,103,101,116,34,125,13,10,123,34,110,97,109,101,34,58,32,34,115,101,114,118,111,46,115,116,97,116,101,34,44,32,34,109,101,116,104,111,100,34,58,32,34,103,101,116,34,125,13,10,123,34,110,97,109,101,34,58,32,34,97,112,46,109,111,100,101,34,44,32,34,109,101,116,104,111,100,34,58,32,34,103,101,116,34,125,13,10,123,34,110,97,109,101,34,58,32,34,105,109,117,46,104,101,97,100,105,110,103,95,108,111,119,112,97,115,115,34,44,32,34,109,101,116,104,111,100,34,58,32,34,119,97,116,99,104,34,125,13,10,123,34,110,97,109,101,34,58,32,34,97,112,46,104,101,97,100,105,110,103,95,99,111,109,109,97,110,100,34,44,32,34,109,101,116,104,111,100,34,58,32,34,119,97,116,99,104,34,125,13,10,123,34,110,97,109,101,34,58,32,34,97,112,46,101,110,97,98,108,101,100,34,44,32,34,109,101,116,104,111,100,34,58,32,34,119,97,116,99,104,34,125,13,10,123,34,110,97,109,101,34,58,32,34,115,101,114,118,111,46,115,116,97,116,101,34,44,32,34,109,101,116,104,111,100,34,58,32,34,119,97,116,99,104,34,125,13,10,123,34,110,97,109,101,34,58,32,34,97,112,46,109,111,100,101,34,44,32,34,109,101,116,104,111,100,34,58,32,34,119,97,116,99,104,34,125,13,10]",
"payloadType": "bin",
"repeat": "30",
"crontab": "",
"once": true,
"onceDelay": "1",
"x": 140,
"y": 69,
"wires": [
[
"ebba1cdb.316b6"
]
]
},
{
"id": "dcd047b9.925a68",
"type": "function",
"z": "ba70a55e.0d29a8",
"name": "",
"func": "msg.payload = msg.payload.toString();\nreturn msg;\n",
"outputs": 1,
"noerr": 0,
"x": 533,
"y": 68,
"wires": [
[
"42c84588.bc3fdc"
]
]
},
{
"id": "8075876f.6fb518",
"type": "json",
"z": "ba70a55e.0d29a8",
"name": "",
"property": "payload",
"action": "obj",
"pretty": false,
"x": 1005,
"y": 68,
"wires": [
[
"51e6b603.327568"
]
]
},
{
"id": "d0d13719.18e238",
"type": "change",
"z": "ba70a55e.0d29a8",
"name": "",
"rules": [
{
"t": "change",
"p": "payload",
"pt": "msg",
"from": "name",
"fromt": "str",
"to": "id",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 835,
"y": 68,
"wires": [
[
"8075876f.6fb518"
]
]
},
{
"id": "51e6b603.327568",
"type": "function",
"z": "ba70a55e.0d29a8",
"name": "Form $APDBG NMEA",
"func": "var heading = context.get(\"heading\") || 0; \nvar heading_command = context.get(\"heading_command\") || 0; \nvar ap_enabled = context.get(\"ap_enabled\") || \"\";\nvar ap_mode = context.get(\"ap_mode\") || \"\"; \nvar servo_state = context.get(\"servo_state\") || \"\"; \n\nif (msg.payload.hasOwnProperty(\"imu.heading_lowpass\")) {\n heading = msg.payload[\"imu.heading_lowpass\"].value; \n context.set(\"heading\", heading); \n}\n\nif (msg.payload.hasOwnProperty(\"ap.heading_command\")) {\n heading_command = msg.payload[\"ap.heading_command\"].value; \n context.set(\"heading_command\", heading_command); \n}\n\nif (msg.payload.hasOwnProperty(\"ap.enabled\")) {\n if (msg.payload[\"ap.enabled\"].value===false) ap_enabled=\"False\";\n if (msg.payload[\"ap.enabled\"].value===true) ap_enabled=\"True\"; \n context.set(\"ap_enabled\", ap_enabled); \n}\n\nif (msg.payload.hasOwnProperty(\"ap.mode\")) {\n ap_mode = msg.payload[\"ap.mode\"].value; \n context.set(\"ap_mode\", ap_mode); \n}\n\nif (msg.payload.hasOwnProperty(\"servo.state\")) {\n servo_state = msg.payload[\"servo.state\"].value; \n context.set(\"servo_state\", servo_state); \n}\nvar today = new Date();\n\nfunction addZeroBefore(n) {\n return (n < 10 ? '0' : '') + n;\n}\n\n\n\nmsg.payload=\"$APDBG,\"+Math.trunc(heading)+\",M,\"+ap_enabled+\",\"+heading_command+\",\"+servo_state+\",\"+ap_mode+\",\"+addZeroBefore(today.getHours())+addZeroBefore(today.getMinutes())+\"*00\"+'\\r\\n';\nreturn msg;",
"outputs": 1,
"noerr": 0,
"x": 1195,
"y": 68,
"wires": [
[
"1cf49820.97cf98"
]
]
},
{
"id": "42c84588.bc3fdc",
"type": "split",
"z": "ba70a55e.0d29a8",
"name": "",
"splt": "\\n",
"spltType": "str",
"arraySplt": 1,
"arraySpltType": "len",
"stream": true,
"addname": "",
"x": 665,
"y": 68,
"wires": [
[
"d0d13719.18e238"
]
]
},
{
"id": "1cf49820.97cf98",
"type": "delay",
"z": "ba70a55e.0d29a8",
"name": "",
"pauseType": "rate",
"timeout": "1",
"timeoutUnits": "seconds",
"rate": "2",
"nbRateUnits": "1",
"rateUnits": "second",
"randomFirst": "1",
"randomLast": "5",
"randomUnits": "seconds",
"drop": true,
"x": 581,
"y": 150,
"wires": [
[
"441c49c4.6c2d68"
]
]
},
{
"id": "441c49c4.6c2d68",
"type": "serial out",
"z": "ba70a55e.0d29a8",
"name": "",
"serial": "e7818a0.af4be78",
"x": 885,
"y": 228,
"wires": []
},
{
"id": "b02a0a9a.5d0a18",
"type": "serial in",
"z": "ba70a55e.0d29a8",
"name": "",
"serial": "e7818a0.af4be78",
"x": 169,
"y": 389,
"wires": [
[
"9a419ebb.65893"
]
]
},
{
"id": "58ec5d5a.5bdc04",
"type": "tcp in",
"z": "ba70a55e.0d29a8",
"name": "",
"server": "client",
"host": "localhost",
"port": "10110",
"datamode": "stream",
"datatype": "utf8",
"newline": "\\n",
"topic": "",
"base64": false,
"x": 155,
"y": 228,
"wires": [
[
"27887b9f.7527d4"
]
]
},
{
"id": "27887b9f.7527d4",
"type": "switch",
"z": "ba70a55e.0d29a8",
"name": "Filter RMC NMEA only",
"property": "payload",
"propertyType": "msg",
"rules": [
{
"t": "regex",
"v": "^(?=...*RMC)",
"vt": "str",
"case": false
},
{
"t": "else"
}
],
"checkall": "true",
"repair": false,
"outputs": 2,
"x": 375,
"y": 228,
"wires": [
[
"441c49c4.6c2d68"
],
[]
],
"inputLabels": [
"Input"
],
"outputLabels": [
"OK",
""
]
},
{
"id": "fdd29b96.b7f2a8",
"type": "udp out",
"z": "ba70a55e.0d29a8",
"name": "",
"addr": "localhost",
"iface": "",
"port": "10110",
"ipv": "udp4",
"outport": "",
"base64": false,
"multicast": "false",
"x": 908,
"y": 438,
"wires": []
},
{
"id": "e7818a0.af4be78",
"type": "serial-port",
"z": "",
"serialport": "/dev/ttyUSB1",
"serialbaud": "9600",
"databits": "8",
"parity": "none",
"stopbits": "1",
"newline": "\\n",
"bin": "false",
"out": "char",
"addchar": false
}
]
The principle is to form and send to arduino fake NMEA with information from pypilot and receive fake NMEA from arduino with commands that are passing for pypilot...
Please don't judge the quality of code, i am not so familiar with node-red