This document introduces an early implementation of the Node-RED runtime that runs on resource-constrained microcontrollers (MCUs).

Overview

Node-RED MCU Edition

Copyright 2022, Moddable Tech, Inc. All rights reserved.
Peter Hoddie
Updated June 25, 2022

Introduction

This document introduces an early implementation of the Node-RED runtime that runs on resource-constrained microcontrollers (MCUs). Node-RED is a popular visual environment that describes itself as "a programming tool for wiring together hardware devices, APIs and online services in new and interesting ways."

Node-RED is built on Node.js and, consequently, runs where Node.js does: desktop computers and single-board computers like the Raspberry Pi. Because of the dependency on Node.js, Node-RED cannot run where Node cannot, notably the low-cost MCUs found in many IoT products and popular in the maker community.

These MCUs are able to run the same JavaScript language used by Node-RED thanks to the XS JavaScript engine in the Moddable SDK. However, these MCUs have much less RAM, much less CPU power, and an RTOS instead of a Linux-based OS. As a result, they require a very different implementation. A typical target microcontroller is the ESP32, running FreeRTOS with about 280 KB of free RAM and a 160 MHz CPU clock.

This early implementation works by converting the JSON descriptions output by Node-RED to objects that are compatible with MCUs. The implementation uses standard JavaScript running in the Moddable SDK runtime. The ECMA-419 standard, the ECMAScript® Embedded Systems API Specification, is used for I/O such as access to pin hardware and networking.

This early work is intended to evaluate whether it is feasible to support Node-RED on MCUs. To achieve that goal, the focus is on a breadth rather than depth. Node-RED has a wealth of features that will take considerable time to implement well. This proof-of-concept effort has the following characteristics:

  • Implements fundamental structures
  • Supports core nodes that help to explore overall architecture
  • Requires no changes to Node-RED
  • Not overly concerned with efficiency

Efficiency is essential for resource constrained device, but it is not a priority yet. For the current goal of determining if it is realistic to support Node-RED on MCUs, the projects just need to run, not run optimally. Optimization can be done after the fundamentals are working.

Results

This initial effort successfully runs useful, if small, Node-RED projects on MCUs. They run reliably and perform well. They have been tested on ESP32 and ESP8266 MCUs. The ESP8266 is quite constrained, running at 80 MHz with only about 45 KB of RAM. Both are able to run flows that connect physical buttons and LEDs to the cloud using MQTT. The following sections provide details on what has been implemented.

This effort was helped greatly by Node-RED's small, well-designed core architecture. That simplicity minimizes what needed to be implemented to execute the nodes and flows. Node-RED achieves its richness through the addition of nodes on top of its core architecture. This minimal and modular approach is well suited to MCUs which are designed to be capable enough to do useful work, not to be a scalable, general-purpose computing device.

This effort was also made easier by both Node-RED and the Moddable SDK being built on JavaScript engines that provide standard, modern JavaScript (V8 and XS, respectively). This is essential because the Node-RED runtime is itself implemented in JavaScript and makes JavaScript accessible to developers through the Function node. Additionally, the capabilities defined by ECMA-419 are often a direct match for the runtime needs of Node-RED, making implementation of embedded I/O capabilities remarkably straightforward.

Based on these early results, it seems possible to provide a valuable implementation of Node-RED for MCUs. This would make developing software for these devices accessible to more developers thanks to Node-RED's goal of providing "low-code programming." It would also allow developers with Node-RED experience a path to extend their reach to widely-used MCUs.

Ways to Help

Based on this investigation, bringing Node-RED to MCUs seems both possible and desirable. It is not a small effort, however. It will require expertise in embedded software development, ECMA-419, the Moddable SDK, the Node-RED runtime, and developing flows with Node-RED. Here are some ways you might help:

  • Help spread the world.
  • Give it a try. Share what you find – what works and what doesn't.
  • Help implement some of the incomplete features of the initial node suite
  • Implement additional notes
  • Help move the Node-RED Embedded Edition APIs to better match those in Node-RED
  • Implement Node-RED nodes for features of the hardware using ECMA-419
  • Implement Node-RED nodes for feature for the Moddable SDK
  • Help improve the documentation
  • Help improve the workflow for developers to deploy and debug flows

Please open an issue or submit a pull request on this repository on GitHub. You can also reach out to @phoddie and @moddabletech on Twitter or chat in real time on our Gitter page.

Running Flows on an MCU

Node-RED MCU Edition is a Moddable SDK project. It is built and run just like any Moddable SDK project. Flows run on ESP8266 and ESP32 MCUs, and in the Moddable SDK simulator on macOS computers.

Of course, the Node-RED flows must be added to the project. The JSON version of the flows is stored in the nodes.js source file. There are two part to moving a Node-RED project to the Node-RED MCU Edition project.

The first is exporting the project from Node-RED.

  1. Open the Node-RED project
  2. From the Node-RED menu (top-left corner), select Export
  3. On the Export tab, select "all flows"
  4. Select Clipboard (not Local)
  5. Select JSON (not Export nodes)
  6. Select "Copy to Clipboard"

The JSON version of the flows is now on the clipboard. The second step is adding this JSON to the Moddable SDK project:

  1. Open the nodes.js file in the Node-RED MCU Edition project
  2. Delete the current JSON data assigned to the nodes variable (take care not to delete the export statement at the end of the file)
  3. Paste in the Node-RED JSON data so it is assigned to nodes

Build and run the Moddable SDK project as usual for the target device.

This process is quick and easy for early exploration. Of course, there are many ways it could be streamlined to improve the developer experience.

Structure

The Node-RED runtime executes the nodes and flows. This runtime architecture determines how nodes interact with each other. It also is a key factor in how efficiently the execution uses the limited RAM and CPU power available.

This is a summary of what is implemented in the Node-RED for MCUs runtime:

  • Nodes (details below)
    • Disabled nodes are ignored
  • Flows
    • Multiple
    • Configuration
    • Disabled flows are ignored
    • Sub-flows
  • Links
  • Context
    • Global
    • Flow
    • Node
  • Outputs
    • Single node output
    • Output connects to multiple inputs
    • Multiple node output
  • Messages
    • Shallow copy on send
    • Synchronous send
    • Deep copy on send
    • Asynchronous send
  • Instantiation
    • Node-RED exported JSON transferred unchanged to device
    • Parsed on device
    • Instances of classes created from JSON
    • 1:1 map from JSON type to class (maybe not always optimal)

Nodes

This section lists the supported nodes. The implemented features are checked.

Comment

  • Supported

Debug

  • Console output is displayed in the xsbug log pane
  • Sidebar output is displayed in the xsbug message pane
  • Display of selected property or complete message
  • Output to node status (maybe meaningless on device)

Function

  • "On Start", "On Message" and "On Stop" handlers
  • Access to Node context, flow context, and global context
  • Report uncaught exceptions to Catch nodes
  • Import modules (Setup)
  • When "On Start" returns Promise, queue received messages until ready
  • Does not wrap setTimeout (and friends) to automatically clear on stop

Inject

  • Injects multiple properties
  • Property values Boolean, timestamp, JSON, number, and string
  • "Inject once after"
  • "Repeat after"
  • Interval between times
  • At a specific time
  • Property values flow., global., buffer, expression, environment variable

Link Call

  • Implemented
  • Timeout

Link In

  • Implemented

Link Out

  • "Send to all connected link nodes"
  • "Return to calling link node"

Catch

  • "Catch errors from all nodes"
  • "Catch errors from selected nodes"
  • "Ignore errors handled by other Catch nodes"

Status

  • "Report status from all nodes"
  • "Report status from selected nodes"

GPIO In

Implemented using "rpi-gpio in" node

  • Select GPIO pin
  • Pull-up and pull-down resistor options
  • Debounce
  • Read initial state of pin on deploy/restart

GPIO Out

  • Select GPIO pin
  • Digital and PWM output modes
  • Initialize initial pin state option

Implemented using "rpi-gpio out" node with ECMA-419 Digital and PWM classes.

MQTT Broker

  • Broker URL and port number
  • Client ID (provides default if not specified)
  • Keep alive (defaults to 60 seconds)
  • Clean session flag (defaults to true)
  • User name and password credentials
  • Birth, close, will topics
  • Protocol version (always 3.1.1)
  • TLS (always insecure)
  • Auto-connect (connects once at start-up)
  • Session expiry
  • QoS 1 and 2
  • Fragmented read and write

Implemented using ECMA-419 MQTT Client draft.

MQTT In

  • Subscribe to topic with QoS 0
  • Payload formats: UTF-8, buffer, JSON, and Base64
  • Wildcards in topic
  • Dynamic subscription
  • Payload format: auto-detect

MQTT Out

  • Data formats: number, string, object, buffer
  • Retain flag

Range

  • Scale property value
  • Round to integer
  • Select property to map
  • Scale and limit
  • Scale and wrap

Change

  • Delete property
  • Move property
  • Set property value
  • Property values Boolean, timestamp, JSON, number, and string
  • Replace within property value
  • flow. and global. targets
  • Property values buffer, expression, environment variable

Switch

  • Multiple rules
  • "checking all rules"
  • "stopping after first match"
  • ==, !=, <, <=, >, >=, is between, is true, is false, is null, is not null, is of type, otherwise
  • flow., global., expression, env variable
  • is of type: buffer
  • has key, contains, matches regexp, is empty, is not empty, sequence rules, JSONata exp

Future Work

This prototype is a breadth-first effort to implement all the steps required to execute meaningful Node-RED flows on a resource-constrained microcontroller. For compatibility and completeness, a great deal of work remains. That work requires many different kinds of experience and expertise. Evolving this early proof-of-concept to a generally useful implementation will require contributions from many motivated individuals.

The compatibility goal should be to provide the same behaviors as much as possible so that existing Node-RED developers can apply their knowledge and experience to embedded MCUs without encountered confusing and unnecessary differences. The goal is not to provide all the features of Node-RED, as some are impractical or impossible on the target class of devices.

Transformation

In this prototype, the nodes and flows exported by Node-RED are converted from JSON to JavaScript instances on the embedded device. This is a relatively heavy operation, involving several passes over the project structure and evaluating JavaScript code. Almost all of this work could be done as a build step on the development computer prior to deploy. Done well, this would free up additional RAM for the project, allow projects to start running faster, and detect use of unsupported Node-RED features earlier. This work should be done sooner than later, as it will change the class definition for the nodes.

  • Create JavaScript tool to convert Node-RED JSON to JavaScript source code
  • Integrate embedded conversion into Deploy feature of Node-RED

Runtime

  • Align runtime behavior and APIs with Node-RED as much as practical. This would benefit from assistance from developers familiar with Node-RED.
  • Messages sent between nodes should be sent asynchronously to avoid JavaScript stack overflows on long chains of nodes (what does Node-RED do here?)
  • Implement support to instantiate nodes from a Mod. This would allow updated flows to be installed on embedded devices in seconds.

Nodes

Possible future work on built-in nodes:

  • Common nodes. The Complete node appears to require Node-RED runtime behaviors beyond what this exploration now implements. It should be implemented sooner to ensure that the object design can support all the fundamental behaviors required.
  • Function nodes. The Delay, Trigger, and Filter nodes appear to be essential. For the most part they should be straightforward to implement, though some of the behaviors are non-trivial. Exec and Template may not make sense.
  • Network nodes. The HTTP Request, WebSocket (Client), TCP, and UDP nodes should be possible to implement using ECMA-419 in the same way MQTT has been implemented.
  • Sequence nodes. The Split, Join, Sort, and Batch nodes should be possible to support. Like the Function nodes, some are quite sophisticated.
  • Parser. CSV and JSON should be possible to support, but the others (HTML, YAML, XML) are likely impractical.
  • Storage The Read File and Write File nodes can be supported. Watch probably cannot.

The built-in nodes are useful for compatibility with the standard Node-RED behaviors. Additional nodes should be added to support embedded features. For example, a Preferences node, an ECMA-419 Sensor node, a Display node, etc.

Challenging Dependencies

Several nodes use JSONata, a query language for JSON. This looks like a substantial effort to support and is perhaps impractical on a constrained embedded device. Fortunately, it seems like the Function object can do the same, just less conveniently.

The Template node uses mustache.js for powerful string substitution. Like JSONata, this could be impractical to support on embedded. A small subset is probably straightforward to support, if that would be useful.

Thank You

This exploration was motivated by an extended conversation with Nick O'Leary who patiently explained Node-RED to me at OpenJS World 2022. That clear and patient discussion gave me grounding to begin this effort.

Comments
  • Testing Nodes

    Testing Nodes

    I have been using a simple method to test Node Red nodes to begin to identify those that are working. The method is to create a simple flow with limited nodes focused on the node on test. The first results are that when a node does not work the error has been the same. I have concentrated on some of the core nodes but when testing a DS1820 node from the palette, this produced the same error. If this can be resolved it could fix the issues with a large number of nodes and get them working. The screenshot of the error is Screenshot from 2022-06-26 08-05-15 The whole debug output text is TestedNodesDebug.txt

    opened by mtoko 15
  • MCU Builder node: Initial commit.

    MCU Builder node: Initial commit.

    This is the initial attempt to support / automate the node-red-mcu building process. This PR proposes a MCU builder node that is able to perform the building process from the node-red editor. Minimal usecase: image

    The build parameters can either be provided as a parameter in msg.mcu or defined in the node config panel:

    image

    A short description of how to install this package was added to the readme.

    No intensive testing has been performed ... thus: Expect the unexpected! :wink:

    opened by ralphwetzel 13
  • Injecting

    Injecting "too early" throws ReferenceError

    https://github.com/phoddie/node-red-mcu/blob/9c4a95f09591f1c4199ff9ca572e703bd86ce64f/nodered.js#L1384-L1391

    This function processes the messages sent from the Node-RED runtime to the MCU. It references flows, that is globalThis.flows, defined @ static build(builder).

    Whereas static build(builder) is invoked only after the MCU has performed its initialization procedure (especially after its network acquiring actions - which may take some seconds), globalThis["<xsbug:script>"] is accepting incoming data immediately after system start.

    Impatient people (like me 😉 ) who try to inject a value via the Node-RED editor too early - in the time period between system start & completion of the (network) initialization phase - may trigger a ReferenceError, as globalThis.flows is still undefined.

    Obviously this error demands to be caught. I'm yet not sure of the further actions. Possible options:

    • Silently ignore the injected command.
    • Feedback an error to the editor, stating "Flow not ready". That might confuse people, as the editor usually states "Successfully injected..."
    • Suppress the "Sucessfully injected..." message and replace it w/ something like "Flow not ready". (Not sure if this is possible though!)
    • Buffer the command and execute it when the flow is ready & running.
    • ... ?

    Any preferences?

    opened by ralphwetzel 10
  • Pico targets fail to build w/ ReferenceError

    Pico targets fail to build w/ ReferenceError

    @phoddie -- Fortunately @rei-vilo ran his tests with a Raspberry Pi Pico W. All other Pico target fail to build (at least on my system) with

    ### ReferenceError: module "embedded:io/socket/tcp" not found!
    

    despite I'm not using (for this test) any node that needs networking capabilities. I think, this is due to the fact that manifest_runtime.json, included by default, includes various other manifests that depend on the embedded socket/network code.

    Simple test case:

    image
    [
        {
            "id": "d54ee83032ee8422",
            "type": "tab",
            "label": "Flow 10",
            "disabled": false,
            "info": "",
            "env": [],
            "_mcu": {
                "mcu": true
            }
        },
        {
            "id": "4311906cded2e5d5",
            "type": "inject",
            "z": "d54ee83032ee8422",
            "name": "",
            "props": [
                {
                    "p": "payload"
                }
            ],
            "repeat": "",
            "crontab": "",
            "once": false,
            "onceDelay": "3",
            "topic": "",
            "payload": "",
            "payloadType": "date",
            "_mcu": {
                "mcu": true
            },
            "x": 360,
            "y": 360,
            "wires": [
                [
                    "359cc37ba40d4b50"
                ]
            ]
        },
        {
            "id": "359cc37ba40d4b50",
            "type": "debug",
            "z": "d54ee83032ee8422",
            "name": "Debug from MCU",
            "active": true,
            "tosidebar": true,
            "console": false,
            "tostatus": true,
            "complete": "payload",
            "targetType": "msg",
            "statusVal": "payload",
            "statusType": "auto",
            "_mcu": {
                "mcu": true
            },
            "x": 550,
            "y": 360,
            "wires": []
        }
    ]
    
    opened by ralphwetzel 9
  • LinkOut node doesn't return to LinkCall Node

    LinkOut node doesn't return to LinkCall Node

    @phoddie -- As the title says: A LinkOut node doesn't return to its LinkCall Node.

    Test case image
    [
        {
            "id": "5d96ad23f6c6c847",
            "type": "tab",
            "label": "Flow 15",
            "disabled": false,
            "info": "",
            "env": [],
            "_mcu": {
                "mcu": true
            }
        },
        {
            "id": "cc2784ee66765e42",
            "type": "group",
            "z": "5d96ad23f6c6c847",
            "name": "Come here!",
            "style": {
                "label": true
            },
            "nodes": [
                "988cf8d2c744c76e",
                "4ea66c21ba624d51",
                "300e0c35ed5e6039"
            ],
            "x": 254,
            "y": 419,
            "w": 362,
            "h": 82
        },
        {
            "id": "6ed2343e18f0a785",
            "type": "inject",
            "z": "5d96ad23f6c6c847",
            "name": "",
            "props": [
                {
                    "p": "payload"
                },
                {
                    "p": "topic",
                    "vt": "str"
                }
            ],
            "repeat": "",
            "crontab": "",
            "once": false,
            "onceDelay": 0.1,
            "topic": "",
            "payload": "",
            "payloadType": "date",
            "_mcu": {
                "mcu": true
            },
            "x": 340,
            "y": 380,
            "wires": [
                [
                    "1613d9f16b6133d2"
                ]
            ]
        },
        {
            "id": "1613d9f16b6133d2",
            "type": "link call",
            "z": "5d96ad23f6c6c847",
            "name": "",
            "links": [
                "988cf8d2c744c76e"
            ],
            "linkType": "static",
            "timeout": "30",
            "_mcu": {
                "mcu": true
            },
            "x": 510,
            "y": 380,
            "wires": [
                [
                    "1a9d7ab025d3035c"
                ]
            ]
        },
        {
            "id": "988cf8d2c744c76e",
            "type": "link in",
            "z": "5d96ad23f6c6c847",
            "g": "cc2784ee66765e42",
            "name": "Come here!",
            "links": [],
            "_mcu": {
                "mcu": true
            },
            "x": 295,
            "y": 460,
            "wires": [
                [
                    "4ea66c21ba624d51"
                ]
            ]
        },
        {
            "id": "4ea66c21ba624d51",
            "type": "change",
            "z": "5d96ad23f6c6c847",
            "g": "cc2784ee66765e42",
            "name": "payload > done",
            "rules": [
                {
                    "t": "set",
                    "p": "payload",
                    "pt": "msg",
                    "to": "done",
                    "tot": "str"
                }
            ],
            "action": "",
            "property": "",
            "from": "",
            "to": "",
            "reg": false,
            "_mcu": {
                "mcu": true
            },
            "x": 430,
            "y": 460,
            "wires": [
                [
                    "300e0c35ed5e6039"
                ]
            ]
        },
        {
            "id": "300e0c35ed5e6039",
            "type": "link out",
            "z": "5d96ad23f6c6c847",
            "g": "cc2784ee66765e42",
            "name": "link out 82",
            "mode": "return",
            "links": [],
            "_mcu": {
                "mcu": true
            },
            "x": 575,
            "y": 460,
            "wires": []
        },
        {
            "id": "1a9d7ab025d3035c",
            "type": "debug",
            "z": "5d96ad23f6c6c847",
            "name": "debug 55",
            "active": true,
            "tosidebar": true,
            "console": false,
            "tostatus": false,
            "complete": "false",
            "statusVal": "",
            "statusType": "auto",
            "_mcu": {
                "mcu": true
            },
            "x": 680,
            "y": 380,
            "wires": []
        }
    ]
    

    https://github.com/phoddie/node-red-mcu/blob/6427d0e16c32c9d32d29d117ff5c59a79d6da51c/nodered.js#L951-L980

    This is due to the fact, that if (links) { is true in line 961 for links === [] and nodered2mcu.js emits links: [], for this node.

    	[...]
    	node = nodes.next().value;	// link out - 300e0c35ed5e6039
    	node.onStart({
    		g: "cc2784ee66765e42",
    		name: "link out 82",
    		mode: "return",
    		links: [],
    	});
    	[...]
    

    I propose to switch the sequence of the if clauses - as the existence of msg._linkSource should be the trigger to return to the calling node.

    	if (Array.isArray(msg._linkSource) && (msg._linkSource.length > 0)) { [...] }
    	else if (links) { [...] }
    	else { [...] }
    
    opened by ralphwetzel 7
  • Function node external modules

    Function node external modules

    Hello! Been trying out node-red-mcu , smaller flows doesn't take up resources. One thing before i try , does node-red-mcu support using function nodes with external modules . If possible one could just use scripts for rest of the npm modules ,without the need to add each node manually. Next is trying out servers and listeners !

    Good job peter and the team at node-red

    opened by spillz-dxb 7
  • Function node: node.warn & node.error shall emit to debug tab

    Function node: node.warn & node.error shall emit to debug tab

    Hello Peter! The node-red docu states that

    The warn and error messages also get sent to the debug tab on the right side of the flow editor.

    Currently, those just trace() to the log window of xsbug:

    https://github.com/phoddie/node-red-mcu/blob/029cd1e6586435936b84ffe5c44e68ea9f4fc853/nodered.js#L336-L341

    The plugin already understands a warn or error message in the following (standard) format (replace error vs warn twice for node.warn):

          trace.left(JSON.stringify({
            "error": {
              "error": <appropriate error message>
            }
          }), this.id);
    

    I propose to add this functionality to class Node definition.

    -- R.

    opened by ralphwetzel 6
  • MQTT connection and Authorisation

    MQTT connection and Authorisation

    I used the example MQTT flow from the earlier issue as a starting point and after rebuilding the SDK after manifest_mqttclient.json was added this worked. I made a simple flow that was connected to an MQTT in and operated the built in LED on an ESP32 and sent out an MQTT out when this received a message. This flow worked, it was possible to publish a message to the broker and operate the LED Flow dowloaded to ESP32

    Flow downloaded into ESP32 connected to test Mosquitto Broker.txt

    Test flow on Node Red to operate above

    Flow to send MQTT to test Mosquitto fom Node Red.txt

    I created a Node Red flow on an instance of Node Red on the same network using an MQTT Broker node without authorisation and then used the same flow above with the broker credentials changed, also no authorisation. The ESP did not connect to the broker and the broker was tested with other publish/subscribe messages which worked. This is the flow with the broker Node Red Flow with MQTT broker node to create local MQTT without Auth.txt

    The first issue is that the ESP will not connect to another broker on the same network even without authorisation. In normal circumstances the MQTT would use user and password for auth, but if you copy flows from one NR instance to another these credentials are not copied and need to be re-entered in the copied flow config. If a flow is copied to the Node.js folder, how can the user and password be included?

    opened by mtoko 6
  • MQTT topics

    MQTT topics

    Peter --

    I tried to use the MQTT nodes - and encountered some strange effects:

    MQTT Sender (to be run on the MCU): image

    sender.json
    [
        {
            "id": "4dc6e91342cfdc95",
            "type": "tab",
            "label": "MQTT Sender",
            "disabled": false,
            "info": "",
            "env": [],
            "_mcu": {
                "mcu": true
            }
        },
        {
            "id": "7e8450f889bc77e5",
            "type": "group",
            "z": "4dc6e91342cfdc95",
            "name": "Test: Send to MQTT",
            "style": {
                "label": true
            },
            "nodes": [
                "329f102a0e2672e9",
                "f7d2c588db6cf95c",
                "23d2e1e9ec0bf70e"
            ],
            "x": 494,
            "y": 139,
            "w": 552,
            "h": 82
        },
        {
            "id": "329f102a0e2672e9",
            "type": "function",
            "z": "4dc6e91342cfdc95",
            "g": "7e8450f889bc77e5",
            "name": "prepare for MQTT",
            "func": "// msg.topic == sensor.id\n\nlet values = {\n    \"t1\": msg.data.t1,\n    \"t2\": msg.data.t2,\n    \"t3\": msg.payload\n}\n\nlet res = [];\n\nfor (let key in values) {\n    if (values[key]) {\n        let m = {\n            \"topic\": `mqtt/test/${key}`,\n            \"payload\": values[key]\n        }\n        res.push(m);\n    }\n}\n\n// send a msg per value\nreturn [res];\n",
            "outputs": 1,
            "noerr": 0,
            "initialize": "",
            "finalize": "",
            "libs": [],
            "_mcu": {
                "mcu": true
            },
            "x": 790,
            "y": 180,
            "wires": [
                [
                    "f7d2c588db6cf95c",
                    "7d8c6612a681c9cb"
                ]
            ],
            "inputLabels": [
                "Sensor values"
            ],
            "outputLabels": [
                "MQTT messages"
            ]
        },
        {
            "id": "f7d2c588db6cf95c",
            "type": "mqtt out",
            "z": "4dc6e91342cfdc95",
            "g": "7e8450f889bc77e5",
            "name": "",
            "topic": "",
            "qos": "2",
            "retain": "false",
            "respTopic": "",
            "contentType": "",
            "userProps": "",
            "correl": "",
            "expiry": "",
            "broker": "4b0b89cd78b32db9",
            "_mcu": {
                "mcu": true
            },
            "x": 970,
            "y": 180,
            "wires": []
        },
        {
            "id": "23d2e1e9ec0bf70e",
            "type": "inject",
            "z": "4dc6e91342cfdc95",
            "g": "7e8450f889bc77e5",
            "name": "",
            "props": [
                {
                    "p": "payload"
                },
                {
                    "p": "data",
                    "v": "{}",
                    "vt": "json"
                },
                {
                    "p": "data.t1",
                    "v": "2",
                    "vt": "str"
                },
                {
                    "p": "data.t2",
                    "v": "3",
                    "vt": "str"
                },
                {
                    "p": "topic",
                    "vt": "str"
                }
            ],
            "repeat": "",
            "crontab": "",
            "once": false,
            "onceDelay": 0.1,
            "topic": "01",
            "payload": "1",
            "payloadType": "num",
            "_mcu": {
                "mcu": true
            },
            "x": 590,
            "y": 180,
            "wires": [
                [
                    "329f102a0e2672e9"
                ]
            ]
        },
        {
            "id": "7d8c6612a681c9cb",
            "type": "debug",
            "z": "4dc6e91342cfdc95",
            "name": "debug 67",
            "active": true,
            "tosidebar": true,
            "console": false,
            "tostatus": false,
            "complete": "true",
            "targetType": "full",
            "statusVal": "",
            "statusType": "auto",
            "_mcu": {
                "mcu": true
            },
            "x": 1020,
            "y": 300,
            "wires": []
        },
        {
            "id": "4b0b89cd78b32db9",
            "type": "mqtt-broker",
            "name": "",
            "broker": "192.168.178.72",
            "port": "1883",
            "clientid": "",
            "autoConnect": true,
            "usetls": false,
            "protocolVersion": "4",
            "keepalive": "60",
            "cleansession": true,
            "birthTopic": "",
            "birthQos": "0",
            "birthPayload": "",
            "birthMsg": {},
            "closeTopic": "",
            "closeQos": "0",
            "closePayload": "",
            "closeMsg": {},
            "willTopic": "",
            "willQos": "0",
            "willPayload": "",
            "willMsg": {},
            "userProps": "",
            "sessionExpiry": "",
            "_mcu": {
                "mcu": false
            }
        }
    ]
    

    MQTT Receiver (to be run as standard NR flow): image

    receiver.json
    [
        {
            "id": "df3521992a1ba688",
            "type": "tab",
            "label": "MQTT Receiver",
            "disabled": false,
            "info": "",
            "env": [],
            "_mcu": {
                "mcu": false
            }
        },
        {
            "id": "e2e71ed681ee60da",
            "type": "mqtt in",
            "z": "df3521992a1ba688",
            "name": "",
            "topic": "mqtt/test/#",
            "qos": "2",
            "datatype": "auto-detect",
            "broker": "4b0b89cd78b32db9",
            "nl": false,
            "rap": true,
            "rh": 0,
            "inputs": 0,
            "_mcu": {
                "mcu": false
            },
            "x": 291.6666564941406,
            "y": 224.99998474121094,
            "wires": [
                [
                    "1f1bcb4d8b228924"
                ]
            ]
        },
        {
            "id": "1f1bcb4d8b228924",
            "type": "debug",
            "z": "df3521992a1ba688",
            "name": "debug 68",
            "active": true,
            "tosidebar": true,
            "console": false,
            "tostatus": false,
            "complete": "true",
            "targetType": "full",
            "statusVal": "",
            "statusType": "auto",
            "_mcu": {
                "mcu": false
            },
            "x": 521.6666564941406,
            "y": 224.99998474121094,
            "wires": []
        },
        {
            "id": "4b0b89cd78b32db9",
            "type": "mqtt-broker",
            "name": "",
            "broker": "192.168.178.72",
            "port": "1883",
            "clientid": "",
            "autoConnect": true,
            "usetls": false,
            "protocolVersion": "4",
            "keepalive": "60",
            "cleansession": true,
            "birthTopic": "",
            "birthQos": "0",
            "birthPayload": "",
            "birthMsg": {},
            "closeTopic": "",
            "closeQos": "0",
            "closePayload": "",
            "closeMsg": {},
            "willTopic": "",
            "willQos": "0",
            "willPayload": "",
            "willMsg": {},
            "userProps": "",
            "sessionExpiry": "",
            "_mcu": {
                "mcu": false
            }
        }
    ]
    

    👉 You need to configure the MQTT connection to adapt to your system!

    Observations:

    • When pushing the inject button for the first time, the sender emits the Debug messages, yet no data is received by the receiver.
    • When pushing the inject button for the second time, the sender throws with /Users/ralph/Projekte/Moddable/moddable/examples/io/tcp/mqttclient/mqttclient.js (121) # Exception: MQTTClient.prototype.write: cannot coerce undefined to object!.

    When both flows are run as standard NR flows, everything works as intended. Any proposals?

    My system status: Moddable @ 758b624, node-red-mcu @ 6427d0e

    opened by ralphwetzel 5
  • FunctionNode: send() throws. Simplification possible?

    FunctionNode: send() throws. Simplification possible?

    There are two quite similar implementations of send() in nodered.js:

    1. send(msg) @ class Node: https://github.com/phoddie/node-red-mcu/blob/ec7bce69735e996d788d53795b8cce68232a53ae/nodered.js#L241-L270

    2. send(msd, _msgid) @ class FunctionNode ... which quite heavily walks through msg only to set _msgid and then relays to send(msg) @ class Node: https://github.com/phoddie/node-red-mcu/blob/ec7bce69735e996d788d53795b8cce68232a53ae/nodered.js#L563-L580

    Issue: There's a bug @ line 573 that triggers Exception: FunctionNode.prototype.send: cannot coerce null to object! in case output == null. The same guard as in line 258 (if (output) ...) is necessary to fix this.

    But: Do we really need this twice? send(msg) @ class Node is almost identical to the second implementation; both handle _msgid slightly different. I haven't checked the standard Node RED sources if this difference is demanded. If it is, I would propose to give send(msg) @ class Node a second parameter _msgid & handle the whole stuff @ class Node. This should simplify code management & additionally reduce overhead.

    opened by ralphwetzel 5
  • Exception: LinkOutNode.prototype.onMessage: cannot coerce undefined to object!

    Exception: LinkOutNode.prototype.onMessage: cannot coerce undefined to object!

    The design of the (standard) LinkNodes looks a bit strange to me: Although the LinkOutNode shown in the picture definitely has no link to another (LinkIn) node, it's links property is populated. The id shown does not exist (anymore?) in any of my flows.

    image

    In nodered.js the call @ line 849 to RED.nodes.getNode(link) w/ this non-existing id returns undefined: https://github.com/phoddie/node-red-mcu/blob/7d9f7630e0bb17d00edd00ab56f32758acc66789/nodered.js#L842-L850

    Later, when a msg arrives, links[i] @ links[i].send(msg) (line 855) will be that returned undefined. Consequentially, an exception is thrown: LinkOutNode.prototype.onMessage: cannot coerce undefined to object! https://github.com/phoddie/node-red-mcu/blob/7d9f7630e0bb17d00edd00ab56f32758acc66789/nodered.js#L851-L861

    I propose to replace line 849 by a classical loop checking if the (link) id references an existing node:

     			this.#links = [];
     			for (let i=0, l=config.links.length; i<l; i++) {
     				let n = RED.nodes.getNode(config.links[i]);
     				if (n) this.#links.push(n);
     			}
    
    bug 
    opened by ralphwetzel 5
Owner
Peter Hoddie
Peter Hoddie
Attempt to replicate the visuals of late 80s / early 90s flight simulators

Retro Fligh Simulator Quick and dirty attempt to replicate the visuals of late 80s / early 90s flight simulators, using as a reference MicroProse's F-

Ruben Moreno Montoliu 70 Dec 15, 2022
RWP stands for Roblox web panel, it's a code snippet that you can run via developer console or the provided Google Chrome extension to try out early

RWP stands for Roblox web panel, it's a code snippet that you can run via developer console or the provided Google Chrome extension to try out early Roblox site features before they're officially out without any programming experience.

null 10 Nov 28, 2022
Open Horizon service container demonstrating Node-RED Object Detection

service-node-red-object-detection Open Horizon service container demonstrating Node-RED Object Detection. This is an Open Horizon configuration to dep

null 4 Dec 25, 2022
A new Node.js resource built using Gatsby.js with React.js, TypeScript, and Remark.

Nodejs.dev Nodejs.dev site built using Gatsby.js with React.js, TypeScript, SCSS, and Remark. You can find the latest Figma design protype here. ?? Ge

Node.js 2.2k Jan 5, 2023
A quick and easy to use security reconnaissance webapp tool, does OSINT, analysis and red-teaming in both passive and active mode. Written in nodeJS and Electron.

ᵔᴥᵔ RedJoust A quick and easy to use security reconnaissance webapp tool, does OSINT, analysis and red-teaming in both passive and active mode. Writte

Dave 17 Oct 31, 2022
A pleasing darker theme made mostly with complementary purple and blue colors, occasionally with a touch of pink or red.

Nebula Float away in space with a beautiful blurple oriented color theme. DISCLAIMER: ⚠️ This theme is not as high contrast as other themes. A pleasin

catgirlkelly 5 Nov 23, 2022
VS Code extension that adds a red error squiggle to every word in your code.

Dumbisense Dumbisense is a VS Code extension that adds a red error squiggle to every word in your code, with an interesting error message and dino ima

Maggie Liu 9 Sep 3, 2022
RedEye is a visual analytic tool supporting Red & Blue Team operations

RedEye Red Team C2 Log Visualization RedEye is an open-source analytic tool developed by CISA and DOE’s Pacific Northwest National Laboratory to assis

Cybersecurity and Infrastructure Security Agency 2.1k Jan 3, 2023
ParkyDB - block based, linkable and verifiable document database -- javascript reference implementation

Ancon ParkyDB A data mesh database using Web 3.0 technology Note: Requires Node v17.7.2 and up for development More about data mesh architecture Block

Ancon Protocol 6 Aug 16, 2022
Minimally viable DOM Document implementation for NativeScript.

DOMiNATIVE Minimally viable DOM Document implementation for NativeScript NOTE THIS IS STILL EXPERIMENTAL. Installation Via npm: npm install dominative

SudoMaker 33 Dec 28, 2022
MLPleaseHelp is a simple ML resource search engine.

README MLPleaseHelp is a simple ML resource search engine. How To Use You can use this search engine right now at https://jgreenemi.github.io/MLPlease

Joseph Greene 5 Jan 20, 2021
Utility for collecting resource-based policies from an AWS account

AWS resource-based policy collector This library aims to collect resource-based policies from an AWS account. NOTE: This library does not cover all AW

Will Dady 22 Dec 5, 2022
Cache Solidjs resources by key (derived from the resource source)

Solid Cached Resource (Experimental) Create a solid resource attached to a cached state by a unique key. Heavily inspired by react-query, but for soli

Yonatan 27 Dec 31, 2022
Sanity plugin for viewing resources which reference a particular resource.

@indent-oss/sanityio-referenced-by Plugin to see which documents reference a particular document referenced-by-sanityio.mov Video Alt Text: Demonstrat

Indent 16 Nov 2, 2022
PxLoader is a simple JavasScript library for creating preloaders and resource downloaders for HTML5 apps.

PxLoader is a Javascript library that helps you download images, sound files or anything else you need before you take a specific action on your site

Pixel Lab 1.1k Dec 30, 2022
JS Cloudimage 360 View - A simple, interactive resource that can be used to provide a virtual tour of your product

JS Cloudimage 360 View - A simple, interactive resource that can be used to provide a virtual tour of your product

Scaleflex 1.9k Jan 7, 2023
This tool allows you to draw up plans for facilities from Foxhole's new Inferno update. It takes power and resource needs into account to help you efficiently design your facilities.

Foxhole Facility Planner This tool allows you to draw up plans for facilities from Foxhole's new Inferno update. It takes power and resource needs int

Brandon Ray 23 Dec 23, 2022
Palaemon is an open-source developer tool for monitoring health and resource metrics of Kubernetes clusters and analyzing Out of Memory (OOMKill) errors

Palaemon ?? ?? An Electron based developer tool for Kubernetes cluster monitoring and error analysis Palaemon is a Greek, child sea-god who came to ai

OSLabs Beta 99 Dec 28, 2022
A Jest runner that runs tests directly in bare Node.js, without virtualizing the environment.

jest-light-runner A Jest runner that runs tests directly in bare Node.js, without virtualizing the environment. Comparison with the default Jest runne

Nicolò Ribaudo 193 Dec 12, 2022