Skip to content

Latest commit

 

History

History
969 lines (808 loc) · 37.2 KB

File metadata and controls

969 lines (808 loc) · 37.2 KB

NMEA Multiplexer


Summary of the links used in this document:

Scripts:
The scripts you will need to run the Multiplexer are tomux.sh, and mux.sh. You might need to tweak them to fit your context. Some pre-built examples are given in the RESTNavServer module, which is itself based on this Multiplexer.
Do also see the section Build, package, run, in this document.


Note: This project was tested and used at sea, in the real world.

The main method is in nmea.mux.GenericNMEAMultiplexer.

For the impatient: Get started, fast.


  • At the center of the picture, we have the NMEA-Multiplexer
    • This is also hosting an optional HTTP server, implementing REST and Web interfaces to the data.
  • Then you have Consumers (aka channels), bringing in NMEA Data
    • File channel (to replay logged data)
    • Serial channel, to read a Serial port
    • TCP, REST, MQTT, WebSockets, etc..., whatever can provide NMEA sentences
    • etc...
  • Then you possibly have Computers, calculating extra data, re-injected into the NMEA-multiplexer
    • Current and True Wind calculated with GPS data (SOG, COG)
    • Custom ones
    • etc...
  • Then you have Forwarders, spitting out NMEA data (and others, sometimes)
    • File Forwarder, to log data
    • Character-mode console
    • REST Forwarder, to feed other servers
    • TCP and others to feed other soft programs (like OpenCPN, SeaWi, etc)
    • etc...

NMEA channels management, in and out.

This is - obviously - a software multiplexer (no hardware required), that can read data from multiple sources, compute extra data (like current) if needed, and broadcast them onto (possibly) several output destinations.

For example:

  • Read one or more serial ports (GPS, boat data), write them into a log file
  • Read a serial port, read a BME280 sensor (air temperature, atmospheric pressure, relative humidity), and broadcast everything on a TCP port, for OpenCPN.
  • Read a file of logged data, and send them over TCP to OpenCPN, for replay.
  • etc...

NMEA

  • NMEA (National Marine Electronics Association) is one of the oldest IT standards, defining how sensor data should be conveyed.
  • Another Good Resource, by the author of GPSd.

Note: here is an opened question:

Some software products are using more "modern" technologies to shape the data (like GPSd, SignalK, ..., they use JSON over REST/HTTP), but what is the point? text/plain has always been a supported mime-type, suitable for NMEA Sentences...

The conveyed data are NMEA data, and to be able to communicate with other softs, their (proprietary?) format will eventually have to be converted back to NMEA... Here we chose not to convert anything. NMEA remains NMEA, which everyone understands. And everyone's happy.

NMEA Parsers are available in OpenSource, in several languages. In this project, you'll find at least one in Java, and for ES6. Plus one in Python (WiP), in the Java-TCP-Python module, called NMEASerialTest.py.

This being said... You will see in this project that there is a data-cache that can be used, at least, in the case of the REST Server (also part of this project, and heavily used in the RESTNavServer). This cache can be pinged and used from REST requests; as such, it supports a JSON format..., the requirements sound a lot like the ones SignalK is addressing.

The remaining point would be to agree on the JSON Schema to use, but there is no standard about that yet.


This NMEAMultiplexer will deal with NMEA Data, in input, as well as for output, in order to preserve - as much as possible - compatibility with other programs. Everyone understands NMEA.
The "only" exception to this rule will be the structure of the data cache, which brings us back to the note above...

This NMEAMultiplexer can

  • use many input protocols (File, Serial, TCP, UDP, WebSockets, Sensors, Computations, ...)
  • produce many outputs (File, Serial, TCP, UDP, WebSockets...)
  • and use a REST API on top of that.

At the heart of this lies a small http server (part of this project), designed to run on very small boards, like a Raspberry Pi Zero, and with possibly no Internet access.

Warning ⚠️: It is not designed to scale as an enterprise server!

The operations on the Serial port(s) require libRxTx. This is included in the gradle dependencies. To be able to use it outside gradle, run (on Linux/Debian/Raspberry Pi):

 sudo apt-get install librxtx-java

See how this is used and referred to in mux.sh (the launching script).


For the impatient: go here to get started now. The others will keep reading.


Includes

  • NMEA Strings Parser
  • AIS Strings Parser (WiP)
  • NMEA Strings generator
  • Serial port Reader / Writer
  • TCP Reader / Writer
  • REST Reader / Writer
  • UDP Reader / Writer
  • WebSockets client (read/write)
  • ... and more.

Also includes

  • A tiny (but amazing) HTTP REST server, for admin and external access (through REST services) purpose.

Javadoc

Javadoc can be generated by using

 ../../gradlew javadoc

The generated files will be in build/docs/javadoc. Open the index.html in your browser.

Some definitions

At the center of the picture, there is the Multiplexer (aka MUX).

The MUX reads from channels and writes to forwarders.

A channel is an NMEA data provider, a forwarder is an NMEA data producer, it can be used as input from another Multiplexer.

Just to insist: A channel is an NMEA data provider. This means that it produces (pure) NMEA Sentences!! This is pure NMEA. Nothing fancy like a JSON Object or whatever object representing the same data in another format. NMEA is already a well known and defined format.

In addition, we can have sensors. A sensor is reading data from a transducer, and produces NMEA sentences read by the Mux. In other words, a sensor is talking to the Multiplexer, and can be seen as a channel.

Also, a computer is using NMEA data collected by the Multiplexer to produce other NMEA data that will be broadcasted by the forwarders. For example, True Wind computed with Apparent Wind data and the GPS data.

Note: to compute the required data, we have a cache, where the data required by the computers are pushed. This cache is initialized before starting the computers, with parameters contained in the properties file used at startup.

This cache is necessary to perform damping and smoothing operations - among others.

Finally, we have transformers, that transform NMEA data into another (proprietary) format, and then behave like a a regular forwarder to provide them to whoever is interested. A transformer is also a forwarder. See below examples of transformers.

Examples

Channels (aka Consumers):

  • Serial reads NMEA data from a Serial Port
  • TCP reads NMEA data from a TCP server
  • WebSocket reads NMEA data from a WebSocket server (this is a WebSocket client)
  • File reads NMEA data from a log file
  • etc

Forwarders:

  • Serial writes NMEA data to a Serial Port (not implemented yet)
  • TCP writes NMEA data to a TCP port
  • WebSocket writes NMEA data to a WebSocket server (this is also a WebSocket client)
  • File write NMEA data to a log file
  • Small Screens, oled, eInk, etc (WiP)
  • etc

Sensors:

  • BME280 reads raw data from a BME280 sensor (Temperature, Pressure, Humidity), and produces XDR and MDA NMEA Sentences.
  • BMP180 reads raw data from a BMP180 sensor (Temperature, Pressure), and produces XDR and MDA NMEA Sentences.
  • HTU21D-F reads raw data from a HTU21D-F sensor (Temperature, Humidity), and produces XDR NMEA Sentences.
  • LSM303 reads raw data from a LSM303 sensor (3D magnetometer and accelerometer), and produces XDR NMEA Sentences for pitch and roll, HDM for heading.
  • HMC5883L reads raw data from a HMC5883L sensor (3D magnetometer), and produces XDR NMEA Sentences for pitch and roll, HDM for heading.
  • ... and the list is not closed. I2C Wiring

Note about Sensors: As opposed to what it used to be in the previous version, we kept the code that reads the sensors' data in Python. The values will be reached through the TCP channel. This was done to avoid dependencies on Java frameworks like PI4J or diozero. We've experienced some frustration recently, when PI4J-v1 was deprecated (after WiringPi's deprecation). Its V2 - and diozero as well) have restrictions on the Java SDK. It requires JDK 11, that cannot run on a Raspberry Pi Zero. This may be fixed later, but leaving the sensor code in Python (usually written by the sensors' providers) cuts us some slack, as those already working drivers do not need to be re-written in Java. This presented some redundant aspect...

Computers:

  • True Wind computer (produces MDA, MWD data)
  • Current computer (produces VDR data).

Also look into nmea.computers.ExtraDataComputer, instantiated when tw-current is mentioned in the computers list of the mux definition.

Important: Computers require the cache to be initialized. This is set in the properties file:

 init.cache=true

Transformers:
A Transformer is specific kind of Forwarder.

  • GPSD data, like nmea.forwarders.GPSdServer (See this)
  • Custom data (like nmea.forwarders.CharacterConsoleWriter)
A word about the Current Computer

A basic approach to compute the current would be to do it by instant triangulation, figuring the Course Made Good (CMG) and comparing it with the GPS Data (Course and Speed Over Ground). A better approach turned out to compute the current over a given period of time. For example, you can perform this calculation by comparing the position you should be at with the CMG only (i.e. as if there was no current) and the one given by the GPS, over periods like 30 seconds, 1 minute, 10 minutes, etc, using a smoothing of the Boat Speed (BSP) and the CMG. The cache is designed to manage several such computations in parallel, they are discriminated by the length of their time-buffer (30 seconds, 5 minutes, etc). The accuracy of such a computations is much higher than the instant triangulation. See this article for details.

Overview

Overall Overview

Note: There is no Transformer on the picture above

Note

There is an rmi forwarder. This is a work in progress, but it works. It is feeding an RMI server that can then be accessed by an RMI client. See an example of such a client in samples.rmi.client.SampleRMIClient.

To see it at work (aka Get Started)

See the class nmea.mux.GenericNMEAMultiplexer, it uses the file nmea.mux.properties to define what to read, and what to re-broadcast it to. See it to understand its content (should be clear enough). The properties file used by the nmea.mux.GenericNMEAMultiplexer can be overridden by the System property named mux.properties, like in

-Dmux.properties=nmea.mux.gps.log.small.properties

Those settings can be modified once the mux is started, through the REST API.

To compile and build:

 $> ../../gradlew shadowJar

To run it, modify mux.sh to fit your environment, and run

 $> ./mux.sh

The script above can take the name of the properties (or yaml) file as a parameter, like in $> ./mux.sh nmea.mux.gps.log.properties. The default name for the configuration file is nmea.mux.properties, as seen in nmea.mux.GenericNMEAMultiplexer.java, where it is the default value for the system variable named mux.properties.

See the Manual for more technical details.

Build, package, run

The build is done as said above, with a

 $> ../../gradlew shadowJar

This produces a fat jar (in the build/libs folder), that can be used to run the Multiplexer.

In the modules implementing the NMEA-multiplexer, you can also package for production, using the script to.prod.sh from a terminal (you will be prompted for input). This will produce a tar.gz file, containing everything needed to run smoothly (jar-files, scripts, sample config files). This archive can be exported to wherever it is needed, and it will not require any source file, nor git repository.

To start the Multiplexer, you need to eventually run the script mux.sh. This script will refer to the right jar-file, and use the right java command to start the Multiplexer. It can also take the name of the properties (or yaml) file as a parameter. The script to.mux.sh can help you as well, as it can take care of some cleanup before running mux.sh. Those two scripts can be run in batch mode, like with nohup ./mux.sh &. If you do so, then you will need to kill the Multiplexer process to stop it. This can be achieved by running killmx.sh.

Note: During the packaging step above (to.prod.sh), the script is also archiving some web resources. The Multiplexer this page is about can be exposed through a small REST/HTTP server.
This way to expose the Multiplexer's features is indeed part of the Multiplexer, hence those Web examples. But those pages do not mean to be seen as anything but examples !!

Filtering

The Channels - aka Consumers - support sentence filtering. Filtering can be applied on NMEA Device ID, and on NMEA Sentence ID. Filters can be positive (inclusive) or negative (exclusive).

Positive filters are linked with an or, Negative ones with an and.

A (sentence) filter like "HDM", "GLL", "~RMC", "~XDR" would mean

(HDM or GLL) and (not RMC and not XDR)

Note: the filter above is meaningless, this is just an example. If the sentence has to start with HDM or GLL, there is no point in excluding RMC or XDR... 🙄. They are already implicitly excluded.

  • "HDM", "GLL" would mean "Only HDM or GLL"
  • "~RMC", "~XDR" would mean "Everything, but RMC and XDR"

It is the user's responsibility not to have contradiction in the filters, like [ "GLL", "~GLL" ], no verification is done in this area. Such a filter would just return nothing.

Note: This is just providing the possibility to negate an expression. Convenient, but limited. The best would probably be to use regular expressions (RegExp). Big drawback though: for the majority of the users, the RegExp syntax could be complex, too complex, or even scary...

About transformers

There is an example of a transformer in WebSocketProcessor.java. As you would see, it is just implementing the Forwarder interface, and this is where it fits in the picture above. A Transformer is just reworking the data before forwarding them as a regular forwarder would.

The example in WebSocketProcessor.java is transforming the NMEA Data in the format expected by a Pebble (this is a smart watch) application. See it here. Data are expected as a json object, over WebSocket. The expected data look like:

{
    "gpstime": 1290377346000,
    "gpsdatetime": 1290377346000,
    "wp": "RANGI   ",
    "d2wp": 561.6,
    "b2wp": 230,
    "xte": 3.0,
    "lat": -9.1102,
    "lng": -140.21108333333333,
    "gpssolardate": 1290343695340,
    "log": 3013.0,
    "daylog": 12.4,
    "cog": 218,
    "sog": 7.2,
    "awa": -121,
    "aws": 17.8,
    "dbt": 1.7000000476837158,
    "hdg": 220,
    "bsp": 6.6,
    "wtemp": 26.5,
    "atemp": 0.0,
    "D": 10.0,
    "d": -0.9830777902388692,
    "W": 9.01692220976113,
    "leeway": 0.0,
    "cmg": 229,
    "tws": 21.4,
    "twa": -139,
    "twd": 89,
    "cdr": 149,
    "csp": 0.29,
    "prmsl": 0.0,
    "hum": 0.0
}

The transformer reads the data from the cache and generates such an object. Then it is sent to a WebSocket server.

To run this transformer example

Start the websocket server, on a port of your choice:

 $> node wsnmea.js -port:1234

Define your transformer in the properties file:

forward.07.type=wsp
forward.07.wsuri=ws://localhost:1234/

Make sure you have configured the Pebble application as required (WebSocket URI), and you are good to go.

Application list
Start here
Press select to start
Choose the channel
Scroll...
Channel list
Choose...
Hit select
Displayed!
Display

Note: Pebble has been acquired by FitBit... There are other smart watch interfaces (like Samsung Frontier) available in the SmartWatches repo.

WebSockets

For more details and use-cases, see here.

WebSocket protocol is supported, in input, and in output. If needed, you can start your own local WebSocket server, running on nodejs. To install it (once):

 $> npm install

Then, to run it,

 $> node wsnmea.js

or

 $> npm start
An alternative, Node-RED.

There is a possibility to use Node-RED as a forwarder as well, that is a cool one. You can create a Node-RED flow that ingests data from a TCP port, and spits them out on a WebSocket one.

Node-RED

Here is the flow to import in Node-RED.

To run it
  • Start the Mux ($> ./mux.sh)
    • Make sure there is a TCP Forwarder (port 7002)
  • Start the node script wsnmea.parser.js.
 node wsnmea.parser.js
  • Start Node-RED and run the flow mentioned above.

Node-RED

  • The Multiplexer reads the NMEA data and forwards them on a TCP Channel.
  • Node-RED listens to the TCP port, and forwards the payloads to a WebSocket server
  • The WebSocket server transforms the data it receives into the expected format, and broadcasts them to all the connected clients (Pebbles in this case).

Once everything runs, you can reach http://localhost:9876/data/web/wsconsole.html to see the data as they come through.

REST Admin Interface

The properties files like nmea.mux.properties defines the configuration at startup.

You can remotely manage the input channels and the re-broadcasting ones through a REST interface. The soft includes a dedicated HTTP Server. The http port is driven by a property (in nmea.mux.properties). Same if you want the HTTP server to be started or not.

with.http.server=yes
http.port=9999

This HTTP Server is designed and written to run on small computers (like the Raspberry Pi Zero). It is NOT an enterprise server, and it will NOT scale as one. Think of it as a Micro server.

Examples of Web implementations (HTML, REST, etc) are gathered in another module, NMEA-multiplexer-basic.

Supported REST end-points

List of operations

A full list of the available REST services is available at

 GET /oplist

Operations List

Examples

All the end points and operations are defined in nmea.mux.GenericNMEAMultiplexer.java. See the List<Operation> named operations.

 GET /mux/serial-ports

returns a payload as:

[
  "/dev/tty.Bluetooth-Incoming-Port",
  "/dev/cu.Bluetooth-Incoming-Port"
]

The list of available serial ports.

 GET /mux/channels

returns a payload like

[
  {
    "cls": "consumers.client.SerialClient",
    "type": "serial",
    "port": "/dev/ttyUSB0",
    "br": 4800
  },
  {
    "cls": "consumers.client.BME280Client",
    "type": "bme280"
  }
]
 GET /mux/forwarders

returns a payload like

[
  {
    "cls": "forwarders.TCPWriter",
    "port": 7001,
    "type": "tcp"
  },
  {
    "cls": "forwarders.ConsoleWriter",
    "type": "console"
  }
]
 DELETE /mux/forwarders/{type}

type is one of

  • file. requires a body like
{
   "log": "./data.nmea",
   "type": "file"
}

identical to the elements returned by GET /mux/forwarders.

  • console. requires no body.
  • tcp. requires a body like
{
     "port": 7002,
     "type": "tcp"
}

identical to the elements returned by GET /mux/forwarders.

  • ws. requires a body like
{
   "wsUri": "ws://localhost:9876/",
   "type": "ws"
}

identical to the elements returned by GET /mux/forwarders.

 DELETE /mux/channels/{type}
 POST /mux/forwarders

with payloads like:

  • file. requires a body like
{
   "log": "./data.nmea",
   "type": "file"
}

identical to the elements returned by GET /mux/forwarders.

  • console. requires a body like
{
     "type": "console"
}
  • tcp. requires a body like
{
     "port": 7002,
     "type": "tcp"
}

identical to the elements returned by GET /mux/forwarders.

  • ws. requires a body like
{
   "wsUri": "ws://localhost:9876/",
   "type": "ws"
}

identical to the elements returned by GET /mux/forwarders.

 POST /mux/channels

There is a Web UI using the REST resources above, in raspberry-sailor/MUX-implementations/NMEA-multiplexer-basic.

Note: This Web UI is to be considered as an example of the way to access the resources. Nothing more. Best case scenario, a Web-UI Developer might find it cute...

On the HTTP Port, use a url like http://machine-name:9999/web/admin.html, where machine-name is the name of the machine where the multiplexer is running, and 9999 is the port defined in the properties.

Admin Web UI

And any REST client (Node.js, Postman, curl, your own code, ...) does the job.

Example with curl:

$ curl -v http://192.168.1.181:9999/mux/last-sentence
  *   Trying 192.168.1.181...
  * Connected to 192.168.1.181 (192.168.1.181) port 9999 (#0)
  > GET /mux/last-sentence HTTP/1.1
  > Host: 192.168.1.181:9999
  > User-Agent: curl/7.43.0
  > Accept: */*
  >
  < HTTP/1.1 200
  < Access-Control-Allow-Origin: *
  < Content-Length: 110
  < Content-Type: application/json
  <
  * Connection #0 to host 192.168.1.181 left intact
  {
      "last-data": "$GPGGA,171812.255,3718.7160,N,12142.3005,W,1,09,0.9,494.3,M,,,,0000*16",
      "timestamp": 1499466116699
  }
 $

With REST traffic

You can also visualize the REST traffic if needed; In (server to client) on the left, Out (client to server) on the right.

With graph

Click on the Flow value (top-right) to show/hide the graph.

Note: Don't be alarmed by the colors of the graph, they all rely on their own css class, named .graphdisplay.

The cool thing in this context is that whatever complex the rendering (what the user sees) looks like, the complexity belongs to the renderer (i.e. the client, the web browser in this case). The server is only responsible for providing the raw data the graph is based on; and typically here, providing the data is certainly not the most complex part, pinging the server does the job... As a matter of fact, a small board like a Raspberry Pi Zero can totally assume it, without noticing.

Dynamic loading

You have the possibility to dynamically load Channels, Forwarders and Computers.

To load a class, mention its name in the properties file used at startup, like for example:

#
# Dynamic loading sample
#
forward.03.class=nmea.forwarders.ProcessorSkeleton
#

This ProcessorSkeleton is part of the project, it is provided as an example you can start from to develop your own forwarders.

Note: Dynamically loaded classes can also be managed from the REST admin interface. Use the Custom label when creating them.

They will show up in the admin interface, with their id in italic. Admin Web UI Notice the line that says skeleton.

Among the forwarders available in this project some are to be dynamically loaded.

  • SSD1306Processor
  • MQTTPublisher (a cool one, for IoT)
  • Character Console
  • SQLite (log data in a SQL database)
  • etc... list keeps growing.

Character Console

Warning: for now, you cannot load more than one dynamically loaded forwarder, consumer or computer of the same class. They are identified by their class name, you cannot have more than one object with a given class name in the lists. For example, if you have an MQTTPublisher that uses a broker URL like tcp://192.168.1.136:1883, you might very well want to have another one, using a broker URL like tcp://io.adafruit.com:1883. Currently, this is not possible.

This should not be too much of a problem though, specially if you use all this on a boat, you probably do not need in Internet connection. A small LAN would do the job in this case.

Compatibility with other softwares

Whatever software understands NMEA is happy with all this. For example, OpenCPN:

OpenCPN

In the screenshot above:

  • The atmospheric pressure and air temperature come from a BME280 (~ $20)
  • The True Wind is calculated by a Computer (as well as the current, not displayed here, but available)
  • Heel and Pitch come from an LSM303 (~ $15)
  • Data are re-broadcasted on TCP

All the data can be logged, and replayed. Replayed data can be displayed just like above, in OpenCPN or any NMEA-savvy software. The screenshot above has been taken during one of those replays (that explains the disparity between air and water temperature...)

Some (good and open source) softwares

A note on the build

As you must have seen, we use Gradle here, along with the ShadowJar plugin. This plugin gathers all the dependencies, and puts them all together in a single jar file (named a fat-jar). This makes your life considerably easier, just put the jar named build/libs/NMEA-multiplexer-1.0-all.jar in your classpath (see for example in the mux.sh), and you have everything you need in there.

This fat-jar is currently (Apr-2017) a bit more than 7 Mb big. In some cases, you might want to trim the jar down, to make it smaller. If you do not need the classes that can be dynamically loaded, if you are not interested in the I2C sensors, etc, you could very well prune those projects, the resulting jar can be substantially smaller. To do so for yourself, refer to the Gradle documentation.

Open Issues

  • UDP client
    • Needs some cleanup...

Remote access

Remote access is not a problem on any Raspberry Pi, ssh does the trick. It can even be done from phone or other small devices. For example, several ssh clients are available on Android, here is a screenshot from a phone:

SSH Console

In this kind of case, it could be a good idea to have scripts with small names... See go and killmux, for examples.

The Raspberry Pi can also be an hotspot, you connect to its network to be able to access the data. there is a good tutorial for that on the Adafruit website. This one also, is worth a look.

The definition of your network is in /etc/hostapd/hostapd.conf.

scp and other such tools are also conveniently available.

This can prove to be particularly convenient when you want to log data away from a stable Internet connection (in your car, in a plane, on a boat, etc). You start the Raspberry Pi, with a hotspot generating its own network. Then you can connect on that network, and you can drive the Multiplexer application (start, stop, monitor, etc).

Along the same lines - even if it can sound a bit disproportionate - VNC works just fine, even on the Raspberry Pi Zero (see below)

VNC Viewer

This is Jessie/Pixel running on a Raspberry Pi Zero.

Note: Access point and Internet access

Instructions were moved here.

Start / Stop all forwarders

In some cases, you might want to start and stop the forwarders (doing logging for example) on demand. Like when you want to log data when driving, hiking, running, etc.

There is a possibility not to start the forwarders when the multiplexer is starting. For that, set the system variable process.on.start to false, default value is true.

In any case, the value can be set using the service

 PUT /mux/mux-process/on

or

 PUT /mux/mux-process/off

The current status can be read using

 GET /mux/mux-process

A Web UI (using the above) is also available:

  http://machine:9999/web/runner.html

Screenshots from a smart-phone:

Notice that this allows you to set the process.on.start, but also to shutdown the Multiplexer. Use it with caution.

Here is a use case:

You start from your home for a run, you want to log the data from a GPS. You start everything at home before you go, with process.on.start=false. All the stuff (Raspberry Pi Zero W, battery, GPS) is in a plastic box in your backpack, a Nalgene canteen, or something similar. You get out, and when you're about to start running, you connect to the Raspberry Pi from your cell phone, and from the UI above, you start the recording. Once done, you stop it the same way. The data have been collected only when you were running. Then you can even stop the Multiplexer, before going home to download and visualize your data file.

Demos

Follow this link.

Examples!!

They are here.

Also see a combination with the Project-Trunk:SunFlower project. This takes the position from a GPS (serial port), and the device heading from an LSM303. Then it orients a solar panel so it faces the sun. The specific forwarder that does the job is the SunFlower project.

And there is an integration with the REST Nav Server.

Case Studies

Bonus

  • A Web UI to generate the yaml files defining the Multiplexer, just open muxbuilder/text/mux-builder.html in a browser, no HTTP server needed. Generated YAML

    • A Web GUI, Drag-n-Drop based, is also available, open muxbuilder/dragndrop/mux-builder.html in a browser.
    Drag the Consumers from the left Edit each component's properties Same for Forwarders
    01 02 03
    And Computers Edit general properties And you have your YAML
    04 05 06

This yaml is generated by the GUI above, it can be edited like any other yaml:

#
# Generated on Fri Apr 02 2021 06:57:24 GMT-0700 (Pacific Daylight Time)
#
name: "GUI Mux Builder"
context:
  with.http.server: true
  http.port: 8080
  init.cache: true
  default.declination: 14.0
  deviation.file.name: dev.csv
  max.leeway: 10.0
  bsp.factor: 1.0
  aws.factor: 1.0
  hdg.offset: 0.0
  awa.offset: 0.0
  damping: 30
# 2 Channels
channels:
- type: serial
  port: /dev/ttyS80
  baudrate: 4800
  verbose: false
  reset.interval: 60000
- type: bme280
  device.prefix: RP
  verbose: false
# 2 Forwarders
forwarders:
- type: file
  timebase.filename: true
  filename.suffix: _LOG
  log.dir: logged
  split: day
  flush: true
- type: tcp
  port: 7001
# 1 Computer
computers:
- type: tw-current
  prefix: CC
  time.buffer.length: 30, 60, 600

Note: This does not work correctly on Safari, which does not support correctly the <dialog> element of HTML5.
Looks like Firefox also has issues...
Brave, Chromium, and Chrome work OK.

Log-files Utilities

Several small utilities are available to do several operations on the data log files. You can

  • merge several log files
  • shrink (remove leading and trailing lines with no speed) log files
  • turn a log-file (.nmea) into
    • CSV
    • GPX
    • KML
    • JSON

Note: scripts are available to invoke those utilities. See (for Linux-like systems):

  • log.analyzer.sh
  • log.merge.sh
  • log.shrinker.sh
  • log.to.csv.sh
  • log.to.gpx.sh
  • log.to.kml.sh
  • log.to.json.sh

  • A sample of a log file analyzer is in util.LogAnalyzer:
 $ java -cp ./build/libs/NMEA-multiplexer-1.0-all.jar util.LogAnalyzer ./sample.data/2017.06.17.nmea

  Started 17-Jun-2017 09:11:01 PDT
  Arrived 17-Jun-2017 11:42:37 PDT
  9,097 record(s) out of 36,387. Total distance: 12.221 km, in 2 hour(s) 31 minute(s) 36.0 sec(s). Avg speed:4.837 km/h
  Max Speed: 6.112 km/h
  Top-Left    :N  37°20.17' / W 121°43.62'
  Bottom-Right:N  37°18.70' / W 121°41.46'

 $
  • A sample of a kml file generator is in util.NMEAtoKML:
 $ java -cp ./build/libs/NMEA-multiplexer-1.0-all.jar util.NMEAtoKML sample.data/alcatraz.2018.may.5.nmea --title:Alcatraz --sub-title:"May 5, 2018"
Started 05-May-2018 10:32:23 PDT
Arrived 05-May-2018 15:52:31 PDT
38,426 record(s) out of 76,821. Total distance: 44.222 km, in 5 hour(s) 20 minute(s) 8.0 sec(s). Avg speed:8.288 km/h
Max Speed: 327.804 km/h
Min alt: -248.60 m, Max alt: 212.00 m, delta 460.60 m
Top-Left    :N  37°49.84' / W 122°30.66'
Bottom-Right:N  37°44.91' / W 122°24.12'

Generated file sample.data/alcatraz.2018.may.5.nmea.kml is ready.
 $

Then open the generated file in Google Map or Google Earth.

  • The same feature is also available for GPX (for navigation software like OpenCPN), use util.NMEAtoGPX, from the script log.to.gpx.sh.
  • Idem for Comma Separated Values (CSV, spreadsheet compatible), use util.NMEAtoCSV, from the script log.to.csv.sh.

Builder

As an example, there is in the NMEA-mux-WebUI module a script called to.prod.sh that shows a way to build the soft for distribution without the full git repository.

Just run

 $ ./to.prod.sh

It will:

  • Start a fresh gradle build
  • Put all the required resources into a new directory (provided at runtime)
  • Archive the directory into a tar.gz file
  • Drop the directory.

Then you can distribute the archive.

All the user has to do it to un-archive it and run the mux.sh script.


Misc and others

Extract custom data

See here

Porting this on other systems...

Windows 10

To read a Serial Port on Windows 10, with PowerShell:

Write-Host "Running PowerShell, reading Serial port"
[System.IO.Ports.SerialPort]::getportnames()
$port = new-Object System.IO.Ports.SerialPort COM1,4800,None,8,one
$port.open()
# $val = 0
# while ($val -ne 10) {
while ($true) {
  # $val++
  $port.ReadLine()
}
$port.close()

To authorize the execution of such a script:

Run once, as administrator:

 dos> powershell
 PS > get-executionpolicy
 PS > set-executionpolicy unrestricted
 PS > exit
 dos>

To execute a script in PowerShell, with redirection:

 dos> powershell ".\serial.ps1" > out.txt

See instructions for Windows 10 here