Create aerospace attitude indicator in dashboard?

I'm running an aerospace simulation outside node-red... and I need to visualize my vehicle's roll and pitch using an attitude indicator...

My simulation software does a crummy job with gauges, so I'm wondering if node-red can do this? I'd be happy just knowing if I'm wasting my time (complete noob here).

ai

This would be a custom gauge not covered by any node I've seen so far.

It would also be special in that it should respond simultaneously to two variables: i) vehicle roll about the length axis and ii) vehicle pitch about the "wing" axis. I can do all the graphics in any format no problem.

Instead of needles, there should be an artificial horizon that rotates side to side to visualize roll and deflects up and down to visualize pitch.

This would make it a sort of gauge that moves in two different ways simultaneously---something unusual in a gauge of any type, it seems.

Does this seem feasible? Is the template node my best bet? Will this involve SVG and HTML beside JS? Any links to unusual custom gauges/data visualizations would be great if you can share them.

Thanks!!!

1 Like

This might be something useful for you maybe

And works with node-red very well.
image

2 Likes

@hotNipi it looks like you did a flow with the gauge? If so, would you mind sharing so I can see how you did it?
Thanks

Here's the flow.

It also takes to download the scripts from that github page and to place them into your static folder. (I have created the folder "flight" for those)

image

[{"id":"438a95b9.6a9eac","type":"ui_template","z":"a05ff4cd.1ae7e8","group":"9f2da61.3353758","name":"","order":3,"width":0,"height":0,"format":"<link rel=\"stylesheet\" href=\"/flight/css/flightindicators.css\"/>\n<script src=\"/flight/js/jquery.flightindicators.js\"></script>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"global","x":760,"y":80,"wires":[[]]},{"id":"1192ffdd.27b32","type":"ui_template","z":"a05ff4cd.1ae7e8","group":"9f2da61.3353758","name":"Flight","order":1,"width":20,"height":5,"format":"<div><span id=\"attitude\"></span><span id=\"heading\"></span><span id=\"airspeed\"></span></div>\n\n<script>\n(function(scope) {\n    var options = {\n    \tsize : 200,\t\t\t\t// Sets the size in pixels of the indicator (square)\n    \troll : 0,\t\t\t\t// Roll angle in degrees for an attitude indicator\n    \tpitch : 0,\t\t\t\t// Pitch angle in degrees for an attitude indicator\n    \theading: 0,\t\t\t\t// Heading angle in degrees for an heading indicator\n    \tvario: 0,\t\t\t\t// Variometer in 1000 feets/min for the variometer indicator\n    \tairspeed: 0,\t\t\t// Air speed in knots for an air speed indicator\n    \taltitude: 0,\t\t\t// Altitude in feets for an altimeter indicator\n    \tpressure: 1000,\t\t\t// Pressure in hPa for an altimeter indicator\n    \tshowBox : true,\t\t\t// Sets if the outer squared box is visible or not (true or false)\n    \timg_directory : '/flight/img/'\t// The directory where the images are saved to\n    }\n    var atti = $.flightIndicator('#attitude', 'attitude',options);\n    var heading = $.flightIndicator('#heading', 'heading',options);\n    var speed = $.flightIndicator('#airspeed', 'airspeed',options);\n    scope.$watch('msg', function(msg) {\n        if (msg) {\n          if(msg.topic == \"roll\"){\n              atti.setRoll(msg.payload)\n          }\n          if(msg.topic == \"pitch\"){\n              atti.setPitch(msg.payload)\n          }\n          if(msg.topic == \"heading\"){\n              heading.setHeading(msg.payload)\n          }\n          if(msg.topic == \"speed\"){\n              speed.setAirSpeed(msg.payload)\n          }\n          \n        }\n  });\n})(scope);\n</script>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","x":750,"y":140,"wires":[[]]},{"id":"d43fe4a5.147468","type":"random","z":"a05ff4cd.1ae7e8","name":"","low":"-30","high":"30","inte":"true","property":"payload","x":380,"y":120,"wires":[["1192ffdd.27b32","a57f9fbf.ad588"]]},{"id":"b2242ffa.eaa9","type":"inject","z":"a05ff4cd.1ae7e8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":".8","crontab":"","once":false,"onceDelay":0.1,"topic":"roll","payload":"","payloadType":"date","x":180,"y":120,"wires":[["d43fe4a5.147468"]]},{"id":"e1ad695c.22d578","type":"inject","z":"a05ff4cd.1ae7e8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":".5","crontab":"","once":false,"onceDelay":0.1,"topic":"pitch","payload":"","payloadType":"date","x":190,"y":180,"wires":[["1a1dfc34.970234"]]},{"id":"1a1dfc34.970234","type":"random","z":"a05ff4cd.1ae7e8","name":"","low":"-2","high":"9","inte":"true","property":"payload","x":380,"y":180,"wires":[["1192ffdd.27b32"]]},{"id":"d44c2342.9b81","type":"inject","z":"a05ff4cd.1ae7e8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"5","crontab":"","once":false,"onceDelay":0.1,"topic":"speed","payload":"","payloadType":"date","x":190,"y":240,"wires":[["afc0c9a8.865138"]]},{"id":"a57f9fbf.ad588","type":"change","z":"a05ff4cd.1ae7e8","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload*-1.2","tot":"jsonata"},{"t":"set","p":"topic","pt":"msg","to":"heading","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":580,"y":80,"wires":[["1192ffdd.27b32"]]},{"id":"afc0c9a8.865138","type":"random","z":"a05ff4cd.1ae7e8","name":"","low":"98","high":"130","inte":"true","property":"payload","x":380,"y":240,"wires":[["1192ffdd.27b32"]]},{"id":"9f2da61.3353758","type":"ui_group","name":"Flight","tab":"d6d3c358.5fb46","order":1,"disp":true,"width":"20","collapse":false},{"id":"d6d3c358.5fb46","type":"ui_tab","name":"Home","icon":"dashboard","order":1,"disabled":false,"hidden":false}]
2 Likes

Thank you, I did download the files so I'll play around with this.
Again, thanks

Oh wow, that seems just the thing! So it can be done. Thanks for sharing!!

@hotNipi: Dude, those gauges are beautiful. Haven't even tried them but at a glance... yeah, you've covered the gamut and they look right. Huge thanks for sharing.

Dumb question, but how do I install from Github...? Do I just put all the contents somewhere in my .node-red folder?

post #4

OK so copy and paste the flow and download the Github scripts into a folder named static/flight should do it? Does the flow capture all the gauges or just the attitude indicator? Thanks!

All gauges in action ..

[{"id":"438a95b9.6a9eac","type":"ui_template","z":"a05ff4cd.1ae7e8","group":"9f2da61.3353758","name":"","order":3,"width":0,"height":0,"format":"<link rel=\"stylesheet\" href=\"/flight/css/flightindicators.css\"/>\n<script src=\"/flight/js/jquery.flightindicators.js\"></script>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"global","x":380,"y":60,"wires":[[]]},{"id":"1192ffdd.27b32","type":"ui_template","z":"a05ff4cd.1ae7e8","group":"9f2da61.3353758","name":"Flight","order":1,"width":"12","height":"8","format":"<div><span id=\"attitude\"></span><span id=\"heading\"></span><span id=\"airspeed\"></span></div>\n<div><span id=\"variometer\"></span><span id=\"altimeter\"></span></div>\n\n<script>\n(function(scope) {\n    var options = {\n    \tsize : 200,\t\t\t\t// Sets the size in pixels of the indicator (square)\n    \troll : 0,\t\t\t\t// Roll angle in degrees for an attitude indicator\n    \tpitch : 0,\t\t\t\t// Pitch angle in degrees for an attitude indicator\n    \theading: 0,\t\t\t\t// Heading angle in degrees for an heading indicator\n    \tvario: 0,\t\t\t\t// Variometer in 1000 feets/min for the variometer indicator\n    \tairspeed: 0,\t\t\t// Air speed in knots for an air speed indicator\n    \taltitude: 0,\t\t\t// Altitude in feets for an altimeter indicator\n    \tpressure: 1000,\t\t\t// Pressure in hPa for an altimeter indicator\n    \tshowBox : true,\t\t\t// Sets if the outer squared box is visible or not (true or false)\n    \timg_directory : '/flight/img/'\t// The directory where the images are saved to\n    }\n    var atti = $.flightIndicator('#attitude', 'attitude',options);\n    var heading = $.flightIndicator('#heading', 'heading',options);\n    var speed = $.flightIndicator('#airspeed', 'airspeed',options);\n    var vario = $.flightIndicator('#variometer', 'variometer',options);\n     var alti = $.flightIndicator('#altimeter', 'altimeter',options);\n    scope.$watch('msg', function(msg) {\n        if (msg) {\n          if(msg.topic == \"roll\"){\n              atti.setRoll(msg.payload)\n          }\n          if(msg.topic == \"pitch\"){\n              atti.setPitch(msg.payload)\n          }\n          if(msg.topic == \"heading\"){\n              heading.setHeading(msg.payload)\n          }\n          if(msg.topic == \"speed\"){\n              speed.setAirSpeed(msg.payload)\n          }\n          if(msg.topic == \"vario\"){\n              vario.setVario(msg.payload)\n          }\n          if(msg.topic == \"alti\"){\n              alti.setAltitude(msg.payload)\n          }\n          if(msg.topic == \"press\"){\n              alti.setPressure(msg.payload)\n          }\n          \n        }\n  });\n})(scope);\n</script>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","x":730,"y":160,"wires":[[]]},{"id":"d43fe4a5.147468","type":"random","z":"a05ff4cd.1ae7e8","name":"","low":"-30","high":"30","inte":"true","property":"payload","x":380,"y":120,"wires":[["1192ffdd.27b32","a57f9fbf.ad588"]]},{"id":"b2242ffa.eaa9","type":"inject","z":"a05ff4cd.1ae7e8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":".8","crontab":"","once":false,"onceDelay":0.1,"topic":"roll","payload":"","payloadType":"date","x":180,"y":120,"wires":[["d43fe4a5.147468"]]},{"id":"e1ad695c.22d578","type":"inject","z":"a05ff4cd.1ae7e8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":".5","crontab":"","once":false,"onceDelay":0.1,"topic":"pitch","payload":"","payloadType":"date","x":170,"y":160,"wires":[["1a1dfc34.970234"]]},{"id":"1a1dfc34.970234","type":"random","z":"a05ff4cd.1ae7e8","name":"","low":"-2","high":"9","inte":"true","property":"payload","x":380,"y":160,"wires":[["1192ffdd.27b32"]]},{"id":"d44c2342.9b81","type":"inject","z":"a05ff4cd.1ae7e8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"5","crontab":"","once":false,"onceDelay":0.1,"topic":"speed","payload":"","payloadType":"date","x":170,"y":200,"wires":[["afc0c9a8.865138"]]},{"id":"a57f9fbf.ad588","type":"change","z":"a05ff4cd.1ae7e8","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload*-1.2","tot":"jsonata"},{"t":"set","p":"topic","pt":"msg","to":"heading","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":560,"y":80,"wires":[["1192ffdd.27b32"]]},{"id":"afc0c9a8.865138","type":"random","z":"a05ff4cd.1ae7e8","name":"","low":"98","high":"130","inte":"true","property":"payload","x":380,"y":200,"wires":[["1192ffdd.27b32"]]},{"id":"7fd6a2a5.39055c","type":"random","z":"a05ff4cd.1ae7e8","name":"","low":"-1","high":"1","inte":"false","property":"payload","x":380,"y":240,"wires":[["1192ffdd.27b32"]]},{"id":"16ead1eb.8b688e","type":"inject","z":"a05ff4cd.1ae7e8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":".8","crontab":"","once":false,"onceDelay":0.1,"topic":"vario","payload":"","payloadType":"date","x":170,"y":240,"wires":[["7fd6a2a5.39055c"]]},{"id":"e0999e9e.04ab3","type":"random","z":"a05ff4cd.1ae7e8","name":"","low":"2000","high":"3000","inte":"false","property":"payload","x":380,"y":280,"wires":[["1192ffdd.27b32"]]},{"id":"af184996.d2e198","type":"inject","z":"a05ff4cd.1ae7e8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":".8","crontab":"","once":false,"onceDelay":0.1,"topic":"alti","payload":"","payloadType":"date","x":180,"y":280,"wires":[["e0999e9e.04ab3"]]},{"id":"60c65555.6d873c","type":"random","z":"a05ff4cd.1ae7e8","name":"","low":"1000","high":"1005","inte":"true","property":"payload","x":380,"y":320,"wires":[["1192ffdd.27b32"]]},{"id":"c300c923.cebe58","type":"inject","z":"a05ff4cd.1ae7e8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"2","crontab":"","once":false,"onceDelay":0.1,"topic":"press","payload":"","payloadType":"date","x":170,"y":320,"wires":[["60c65555.6d873c"]]},{"id":"9f2da61.3353758","type":"ui_group","name":"Flight","tab":"d6d3c358.5fb46","order":1,"disp":true,"width":"12","collapse":false},{"id":"d6d3c358.5fb46","type":"ui_tab","name":"Home","icon":"dashboard","order":1,"disabled":false,"hidden":false}]

Whenever the gauges should animate smoothly, it takes to open the flightindicator.css file and add following

.roll {
  transition: all 0.5s;
}
.pitch {
  transition: all 0.5s;
}
.vario {
  transition: all 3s;
}
.speed {
  transition: all 5s;
}
.heading{
  transition: all 1s;
}
.needle{
  transition: all 1s;
}
.needleSmall{
  transition: all 1s;
}
.pressure{
  transition: all 2s;
}

Hm... I copied your flows, but when I import into node-red, I get "unknown: random" nodes that node-red can't seem to recognize.

Am I missing a step?

I just copied your flow, pressed CTRL+I in node-red, and pasted the flow code in the clipboard tab of the Import Nodes window.

Should I have pasted the code in a template node instead, or maybe a ui_template node?

The missing node is node-red-node-random and you can install it from burger menu at right top of the page and from here use Mange Palette

Perfect! The nodes are all in. Now on to the fun part.

Is there a way to add the... gauge flows to my node-red library on the left pane? Or should I just keep them in a separate tab and copy-paste them from there whenever I need them elsewhere?

You can export the flow and store it to library and when needed, load from library

Got it!

My flight data will come in from an external flight simulation through a TCP connection (already figured out from another flow).

I suspect I'll have to replace the injector nodes with my TCP in nodes (plus function and other nodes to put the incoming data in a usable format---converting from buffer to double/integers, etc).

Does that seem right?

@hotNipi , I realize this isn't your code but as a less than competent coder may I ask what are the names of the scripts you are referring to? The ones needed to be copied into flight?

I see the flight gauges in my dashboard! This is the most excited I've been all week. Super cool.

I'm obviously missing something as my dashboard is blank, sigh.....