This forum uses cookies
This forum makes use of cookies to store your login information if you are registered, and your last visit if you are not. Cookies are small text documents stored on your computer; the cookies set by this forum can only be used on this website and pose no security risk. Cookies on this forum also track the specific topics you have read and when you last read them. Please confirm whether you accept or reject these cookies being set.

A cookie will be stored in your browser regardless of choice to prevent you being asked this question again. You will be able to change your cookie settings at any time using the link in the footer.

Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Pypilot control ideas
#1
Just finished little project for pypilot control

i have RPI with openplotter on nav station and wifi tablet on helm.
But heavy rain and bad weather is not the time for trying to play with touchscreen display Smile
For bad weather i decided to make waterproof controls for cabin top instruments panel under sprayhood.
As enclosure i have used old non working Autohelm depth instrument display with new 2004A LCD and arduino nano. Later i added RF Remote Control.
Going to paint new buttons labels... Those controls have only four buttons, so i combined short, long presses and with simultaneous pressing of two buttons for different kind of actions.

On cabin top i already have depth and wind instruments, so for new instrument i added gps speed display mode and time display mode (just for fun)

Can't wait for next season to test it!  

   

Here is little video:
https://www.youtube.com/watch?v=2-mZD6RBbHg

do not judge strictly, i am not any kind of electronics professional but I really fell in love with all those openplotter stuff Smile

Just  for anyone who seeking for same solution and i giving an idea to not get rid of old instruments Smile

PS. just found this post: http://forum.openmarine.net/showthread.p...1#pid12301 with another solution that it is really beautiful, reliable and convenient . I am very like it
Reply
#2
This is an excellent idea. It is customary for weddings in America that a bride should wear "Something old, something new, something borrowed, something blue."  I like to retain in my boat a little of its history.

I use this shell of a Datamarine component as my main chassis for Openplotter. A benefit being that it already fits the big hole near my nav station.

[Image: i_fqVQWeaAD_O-8YbY2UbB9NNIqJcNPTVTPdWT4n...Fgkw=w2400]
Reply
#3
Would you mind sharing your code and more about how it is wired up?
Reply
#4
How did you manage the RF remote? My solution uses an arduino to sample the receiver and communicate via spi to the raspberry.
Reply
#5
(2020-02-20, 07:38 PM)seandepagnier Wrote: How did you manage the RF remote?   My solution uses an arduino to sample the receiver and communicate via spi to the raspberry.

I'm using RXB6 connected to arduino as RF received 
My arduino connected to RPI with usb and it's virtual serial port i assigned to kplex in "both" direction mode (because i wanted arduino to receive and parse some NMEA from RPI).
Then i little cheated and made new NMEA sentence intended to manage pypilot that arduino will send to kplex
For example:

$APXXX,ap.enabled,true*05
or 
$APXXX,ap.heading_command,323*0A
and etc...  

So all i did on RPI side is added new node-red flow to receive this NMEA, parse and send to pypilot signalk server
[Image: 158225731844159854.jpg]
with function code
Code:
var th = msg.payload.replace("\$APXXX,","").slice(0, -5).split(",");

msg.payload = '{\"name\": \"'+th[0]+'\", \"method\": \"set\", \"value\": '+th[1]+'}\n'
return msg;


I guess maybe this is not the right way to do such thing... I think that i'm able to just parse serial from arduino in node-red and forward commands to pypilot signalk server (without using kplex).
But in future i hope to replace arduino nano with arduino mega and use it to multiplex NMEA from various instruments and send it to RPI

(2020-02-19, 07:56 PM)Saqqara Wrote: Would you mind sharing your code and more about how it is wired up?

I will try to draw the wirings and describe things... Smile
Reply
#6
(2020-02-21, 05:16 AM)beholder77 Wrote:
(2020-02-20, 07:38 PM)seandepagnier Wrote: How did you manage the RF remote?   My solution uses an arduino to sample the receiver and communicate via spi to the raspberry.

I'm using RXB6 connected to arduino as RF received 
My arduino connected to RPI with usb and it's virtual serial port i assigned to kplex in "both" direction mode (because i wanted arduino to receive and parse some NMEA from RPI).
Then i little cheated and made new NMEA sentence intended to manage pypilot that arduino will send to kplex
For example:

$APXXX,ap.enabled,true*05
or 
$APXXX,ap.heading_command,323*0A
and etc...  

So all i did on RPI side is added new node-red flow to receive this NMEA, parse and send to pypilot signalk server
[Image: 158225731844159854.jpg]
with function code
Code:
var th = msg.payload.replace("\$APXXX,","").slice(0, -5).split(",");

msg.payload = '{\"name\": \"'+th[0]+'\", \"method\": \"set\", \"value\": '+th[1]+'}\n'
return msg;


I guess maybe this is not the right way to do such thing... I think that i'm able to just parse serial from arduino in node-red and forward commands to pypilot signalk server (without using kplex).
But in future i hope to replace arduino nano with arduino mega and use it to multiplex NMEA from various instruments and send it to RPI

(2020-02-19, 07:56 PM)Saqqara Wrote: Would you mind sharing your code and more about how it is wired up?

I will try to draw the wirings and describe things... Smile

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.
Reply
#7
Have any one changed UP, DOWN and MENU button to encoder with button?
I did not understand where the GPIO pin is read in. i can found where pins are defined on GPIO.py.

Would like to connect Encoder to spare pins on PI GPIO26, 21, 20.

Is there some variable that key state is read in that i can manipulate with encoder script?

Hope some can give some hints.
Reply
#8
(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

.zip   sketch_nov11b.zip (Size: 90.73 KB / Downloads: 219)

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
    }
]


[Image: 158255641864669890.jpg]
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
Reply
#9
It is unfortunately infeasible for pypilot to use the signalk format strictly and directly.

Instead a pypilot-signalk-node plugin is needed but not developed yet. using node-red will be poor performance and difficult to configure but still interesting to try.

Please correct me if I am wrong but:

1) signalk lacks the ability to be routed. Clients do not register data and produce it according to subscriptions, instead plugins on the server handle this role. This means it's not feasible or efficient to route data through several signalk servers. All data is produced at the fastest rate regardless of if any clients are subscripbed so it is not efficient to produce costly data. All of the important plugin logic must be implemented in the server as plugins rather than signalk clients which unfortunately means it is not possible to distribute plugins for the server on remote hosts, and also the plugin logic must use javascript at least writing signalk-node-server plugins in other languages is unknown. Because so much logic is implemented in plugins rather than clients, it is unlikely alternative signalk server implementations will emerge.

2) redundant data -- the signalk format is not concise making it less efficient and extra logic in code to process. This does not prevent integration as a translation can be used but it is better for pypilot specific services to not be forced to deal directly with this format to ensure the best performance.

3) listing -- no way to get directory listing without using http requests. This complicates simple implementations.

4) security -- I use a WPA wifi network to prevent others from controlling the boat. websockets and ssl encryption are not needed and complicate as well as increase latency. better to use ssh tunneling instead of websockets if this type of security is required.

5) poor autopilot interface -- signalk defines a few autopilot keys which are redundant and require signalk-node-server to perform autopilot logic to update absolute fields from relative ones since both are defined unfortunately. The interface misses a lot of what pypilot implements and seems to be intended to control nmea2000 autopilots. For this reason, any signalk autopilot control will be much inferior to just using the direct pypilot control options.

The data format of pypilot does not have these issues but lacks a lot of other flexibility in signalk that is not useful for pypilot.
Reply
#10
Hello Sean,
I just have to thank you for all the amazing work you have done on pypilot and opencpn, super cool. I intend to buy hardware from you shortly.
Also beholder77 thanks for posting your work.
The use case that brought me to start looking at the work beholder77 is doing was my desire to build a simple system with one rpi inside running opencpn + pypilot that is in a dry secure location away from magnetic interference and a simple interface with a screen and buttons at the helm.
I was thinking of using your original spi and gpio setup for this but i feel that the spi interface for the screen is not robust enough for ~5 meters of cable in a noisy marine environment, plus it would require a loom with many conductors.
What would you suggest for a control interface for his kind of simple setup?
Keith
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)