Skip to content

Examples

APRS Map Example

The following example receives & decodes APRS packets from APRS-IS, and serializes them as Node-RED Worldmap (RedMap) JSON, for display on a local map.

APRS Map Flow

APRS Map

How does it work?

The APRS RX Node connects to the APRS-IS network via a Websocket. Once connected, it applies an APRS-IS server-side filter command of f/SUNSET/50, which translates to:

Only send me APRS packets for stations that are within 50 km of SUNSET's last known position.

APRS RX Node Config

Received APRS Packets are serialized as JSON, and passed to a Function Node.

const pl = msg.payload;

let name = pl.from.call;

if (typeof pl.from.ssid !== "undefined" && pl.from.ssid !== null) {
    name = `${name}-${pl.from.ssid}`
}

msg.payload = {
    ...pl.data,
    "name": name,
}

The Function Node then serializes the data as Node-RED Worldmap (RedMap) JSON, and passes it to a Worldmap Node.

Worldmap Config

From there, the map is viewable in a web browser at: http://localhost:1880/aprs

Worldmap browser

Use this example

Copy & Paste the following code block into a new Flow in Node-RED:

[
  {
      "id": "debug_node",
      "type": "debug",
      "z": "12429aacd6a32a82",
      "name": "APRS Data Received",
      "active": true,
      "tosidebar": true,
      "console": false,
      "tostatus": false,
      "complete": "false",
      "statusVal": "",
      "statusType": "auto",
      "x": 820,
      "y": 220,
      "wires": []
  },
  {
      "id": "rx_node",
      "type": "aprs rx",
      "z": "12429aacd6a32a82",
      "name": "Generic APRS Connection",
      "user": "NODERE-D",
      "url": "ws://srvr.aprs-is.net:8080",
      "filter": "f/SUNSET/50",
      "x": 390,
      "y": 260,
      "wires": [
          [
              "05165a405241cdb9"
          ]
      ]
  },
  {
      "id": "b47ea626e6855dff",
      "type": "worldmap",
      "z": "12429aacd6a32a82",
      "name": "",
      "lat": "37.76",
      "lon": "-122.4975",
      "zoom": "",
      "layer": "OSMG",
      "cluster": "",
      "maxage": "",
      "usermenu": "show",
      "layers": "show",
      "panit": "false",
      "panlock": "false",
      "zoomlock": "false",
      "hiderightclick": "false",
      "coords": "none",
      "showgrid": "false",
      "allowFileDrop": "false",
      "path": "/aprs",
      "overlist": "DR,CO,RA,DN,HM",
      "maplist": "OSMG,OSMC,EsriC,EsriS,EsriT,EsriDG,UKOS",
      "mapname": "",
      "mapurl": "",
      "mapopt": "",
      "mapwms": false,
      "x": 770,
      "y": 260,
      "wires": []
  },
  {
      "id": "05165a405241cdb9",
      "type": "function",
      "z": "12429aacd6a32a82",
      "name": "APRS 2 WorldMap",
      "func": "const pl = msg.payload;\n\nlet name = pl.from.call;\n\nif (typeof pl.from.ssid !== \"undefined\" && pl.from.ssid !== null) {\n    name = `${name}-${pl.from.ssid}`\n}\n\nmsg.payload = {\n    ...pl.data,\n    \"name\": name,\n}\n\nreturn msg;",
      "outputs": 1,
      "noerr": 0,
      "initialize": "",
      "finalize": "",
      "libs": [],
      "x": 610,
      "y": 260,
      "wires": [
          [
              "debug_node",
              "b47ea626e6855dff"
          ]
      ]
  }
]

APRS Transmit Example

The following example transmits an APRS packet into the APRS-IS network.

APRS TX Example

Clicking the Inject button will send the APRS packet to the APRS-IS network.

W2GMD>APRSTO,TCPIP*:!3745.60N/12229.85W0 Hello World.

How does it work?

The Create APRS Payload function Node creates an APRS payload as JSON:

msg.payload = {
  from: "W2GMD",
  to: "APRSTO",
  lat: "37.76",
  lon: "-122.4975",
  comment: "Hello World.",
};

The aprs tx Node serializses the JSON as an APRS Packet, authenticates with the APRS-IS network, and sends the packet to APRS-IS.

APRS TX Config

The aprs tx Node uses a configuration to store APRS-IS user & pass credentials.

APRS TX Config

Use this example

Copy & Paste the following code block into a new Flow in Node-RED:

[
  {
    "id": "inject_node",
    "type": "inject",
    "z": "db9a2e13b58675d9",
    "name": "",
    "props": [],
    "repeat": "",
    "crontab": "",
    "once": false,
    "onceDelay": 0.1,
    "topic": "",
    "x": 310,
    "y": 200,
    "wires": [["func_node"]]
  },
  {
    "id": "func_node",
    "type": "function",
    "z": "db9a2e13b58675d9",
    "name": "Create APRS Payload",
    "func": "// APRS TX Node uses WorldMap serialized data:\nmsg.payload = {\n    'from': 'taco',\n    'lat': '1.2',\n    'lon': '2.3',\n    'comment': 'test comment'\n}\n\nreturn msg;",
    "outputs": 1,
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 480,
    "y": 200,
    "wires": [["aprs_tx_node"]]
  },
  {
    "id": "comment_node",
    "type": "comment",
    "z": "db9a2e13b58675d9",
    "name": "APRS TX Example",
    "info": "This example creates a test WorldMap payload, serializses it as an APRS packet, and sends it to the APRS-IS backbone.",
    "x": 350,
    "y": 160,
    "wires": []
  },
  {
    "id": "aprs_tx_node",
    "type": "aprs tx",
    "z": "db9a2e13b58675d9",
    "name": "",
    "aprsConfig": "config_node",
    "from": "",
    "to": "",
    "via": "",
    "server": "rotate.aprs2.net",
    "port": "14580",
    "x": 650,
    "y": 200,
    "wires": []
  },
  {
    "id": "config_node",
    "type": "aprs config",
    "filter": "",
    "user": "User",
    "name": "",
    "pass": "Pass"
  }
]

APRS Receive Example

APRS RX example Flow

Received APRS Frames are parsed using aprs-parser and output as msg.payload JSON. When used with the Debug Node, you can view deserialized APRS data in the Debug pane:

APRS RX debug

Use this example

Copy & Paste the following code block into a new Flow in Node-RED:

[
  {
    "id": "debug_node",
    "type": "debug",
    "z": "0f5a88599bcf4911",
    "name": "APRS Data Received",
    "active": true,
    "tosidebar": true,
    "console": false,
    "tostatus": false,
    "complete": "false",
    "statusVal": "",
    "statusType": "auto",
    "x": 320,
    "y": 300,
    "wires": []
  },
  {
    "id": "rx_node",
    "type": "aprs rx",
    "z": "0f5a88599bcf4911",
    "name": "Generic APRS Connection",
    "user": "NODERE-D",
    "url": "ws://srvr.aprs-is.net:8080",
    "filter": "f/SUNSET/50",
    "x": 160,
    "y": 300,
    "wires": [["debug_node"]]
  }
]

CWOP Weather Example

The following example transmits an CWOP weather report packet into the CWOP network.

APRS CWOP

Clicking the Inject button will send the CWOP packet to the CWOP network.

The Create CWOP Payload function Node creates an CWOP payload as JSON:

msg.payload = {
  from: { call: "CALL", ssid: "6" },
  data: {
    latitude: 51.8355,
    longitude: 19.228,
    extension: { courseDeg: 239, speedMPerS: 1.543333332 },
    weather: {
      windGust: 4.4704,
      temperature: 5.555555555555555,
      rain1h: 0,
      rain24h: 2.794,
      rainSinceMidnight: 2.794,
      pressure: 996.9,
      humidity: 83,
      luminosity: 0,
    },
    comment: "Node-RED WX Station",
    timestamp: "2021-01-23T18:21:00.000Z",
  },
};

Use this example

Copy & Paste the following code block into a new Flow in Node-RED:

[
  {
    "id": "22940198689bb3eb",
    "type": "function",
    "z": "6b8486ad24e16e73",
    "name": "Create CWOP Payload",
    "func": "// APRS TX Node uses WorldMap serialized data:\nmsg.payload = {\n    from: { call: \"CALL\", ssid: \"6\" },\n    data: {\n        latitude: 51.8355,\n        longitude: 19.228,\n        extension: { courseDeg: 239, speedMPerS: 1.543333332 },\n        weather: {\n        windGust: 4.4704,\n        temperature: 5.555555555555555,\n        rain1h: 0,\n        rain24h: 2.794,\n        rainSinceMidnight: 2.794,\n        pressure: 996.9,\n        humidity: 83,\n        luminosity: 0,\n    },\n    comment: \"Node-RED WX Station\",\n    timestamp: \"2021-01-23T18:21:00.000Z\",\n    },\n};\n\nreturn msg;",
    "outputs": 1,
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 440,
    "y": 360,
    "wires": [["0a1400452910d040"]]
  },
  {
    "id": "b9806f2747e797cb",
    "type": "inject",
    "z": "6b8486ad24e16e73",
    "name": "",
    "props": [],
    "repeat": "",
    "crontab": "",
    "once": false,
    "onceDelay": 0.1,
    "topic": "",
    "x": 270,
    "y": 360,
    "wires": [["22940198689bb3eb"]]
  },
  {
    "id": "027326635c11803e",
    "type": "comment",
    "z": "6b8486ad24e16e73",
    "name": "APRS CWOP Example",
    "info": "This example creates a test weather payload, serializses it as an APRS packet, and sends it to the CWOP backbone.",
    "x": 320,
    "y": 320,
    "wires": []
  },
  {
    "id": "0a1400452910d040",
    "type": "aprs tx",
    "z": "6b8486ad24e16e73",
    "name": "",
    "aprsConfig": "config_node",
    "from": "",
    "to": "",
    "via": "",
    "server": "rotate.aprs2.net",
    "port": "14580",
    "x": 620,
    "y": 360,
    "wires": []
  },
  {
    "id": "config_node",
    "type": "aprs config",
    "filter": "",
    "user": "W2GMD",
    "name": "W2GMD",
    "pass": "10141"
  }
]

APRS to TAK Gateway Example

The following example translates APRS packets into Cursor on Target packets for use with TAK Products. It uses the node-red-contrib-tak Nodes, which are installed separately.

TAK Flow

How does it work?

The APRS RX Node connects to the APRS-IS network via a Websocket. Once connected, it applies an APRS-IS server-side filter command of f/SUNSET/50, which translates to:

Only send me APRS packets for stations that are within 50 km of SUNSET's last known position.

APRS RX Node Config

Received APRS Packets are serialized as JSON, and passed to a Function Node.

const pl = msg.payload;

let name = pl.from.call;

if (typeof pl.from.ssid !== "undefined" && pl.from.ssid !== null) {
    name = `${name}-${pl.from.ssid}`
}

msg.payload = {
    'event': {
        '_attributes': {
            'version': '2.0',
            'time': new Date(Date.now()).toISOString(),
            'start': new Date(Date.now()).toISOString(),
            'stale': new Date(Date.now() + 500000).toISOString(),
            'type': 'a-f-G',
            'uid': name,
            'how': 'm-g',
            'access': 'Unclassified'
        },
        'point': {
            '_attributes': {
                'lat': pl.data.latitude,
                'lon': pl.data.longitude,
                'hae': "999999.0",
                'le': "999999.0",
                'ce': "999999.0",
            }
        }
    }
};

The Function Node then serializes the data as CoT (TAK) JSON, and passes it to a TAK Node. The TAK Node serializses the CoT JSON as CoT XML & Protobuf, and passes it to the UDP Out Node.

UDP Out Config

The UDP Out Node sends the data to the ATAK Mesh SA multicast address of udp://239.2.3.1:6969, which can be picked up by any TAK client on the local network.

ATAK Screenshot

Use this example

Copy & Paste the following code block into a new Flow in Node-RED:

[
  {
      "id": "rx_node",
      "type": "aprs rx",
      "z": "12429aacd6a32a82",
      "name": "Generic APRS Connection",
      "user": "NODERE-D",
      "url": "ws://srvr.aprs-is.net:8080",
      "filter": "f/SUNSET/50",
      "x": 390,
      "y": 260,
      "wires": [
          [
              "05165a405241cdb9"
          ]
      ]
  },
  {
      "id": "05165a405241cdb9",
      "type": "function",
      "z": "12429aacd6a32a82",
      "name": "APRS 2 TAK",
      "func": "const pl = msg.payload;\n\nlet name = pl.from.call;\n\nif (typeof pl.from.ssid !== \"undefined\" && pl.from.ssid !== null) {\n    name = `${name}-${pl.from.ssid}`\n}\n\nmsg.payload = {\n    'event': {\n        '_attributes': {\n            'version': '2.0',\n            'time': new Date(Date.now()).toISOString(),\n            'start': new Date(Date.now()).toISOString(),\n            'stale': new Date(Date.now() + 500000).toISOString(),\n            'type': 'a-f-G',\n            'uid': name,\n            'how': 'm-g',\n            'access': 'Unclassified'\n        },\n        'point': {\n            '_attributes': {\n                'lat': pl.data.latitude,\n                'lon': pl.data.longitude,\n                'hae': \"999999.0\",\n                'le': \"999999.0\",\n                'ce': \"999999.0\",\n            }\n        }\n    }\n};\n\n\nreturn msg;",
      "outputs": 1,
      "noerr": 0,
      "initialize": "",
      "finalize": "",
      "libs": [],
      "x": 590,
      "y": 260,
      "wires": [
          [
              "cf621b4df7174c0c"
          ]
      ]
  },
  {
      "id": "cf621b4df7174c0c",
      "type": "tak",
      "z": "12429aacd6a32a82",
      "name": "TAK",
      "x": 730,
      "y": 260,
      "wires": [
          [
              "ee26cfa99036bd92"
          ],
          [
              "969ee95c29fbd4fd"
          ],
          []
      ]
  },
  {
      "id": "ee26cfa99036bd92",
      "type": "debug",
      "z": "12429aacd6a32a82",
      "name": "TAK Data",
      "active": true,
      "tosidebar": true,
      "console": false,
      "tostatus": false,
      "complete": "payload",
      "targetType": "msg",
      "statusVal": "",
      "statusType": "auto",
      "x": 880,
      "y": 220,
      "wires": []
  },
  {
      "id": "969ee95c29fbd4fd",
      "type": "udp out",
      "z": "12429aacd6a32a82",
      "name": "",
      "addr": "239.2.3.1",
      "iface": "",
      "port": "6969",
      "ipv": "udp4",
      "outport": "",
      "base64": false,
      "multicast": "multi",
      "x": 910,
      "y": 260,
      "wires": []
  }
]