ESP-32 OTA updates via node-red-mcu?

Now that I finally have a working ESP-32 mcu connected to my pool controller, I have to disconnect it and bring it inside every time I want to make the slightest changes to the flow.

I found this statement from Peter earlier this year -- and I'm just wondering if this is something I'll be able to use in my project.

Yes. Check this out...

Sweet... I've added the missing nodes and imported the example flows. So is this "no httpserver!" a build problem or user error?


Looks like maybe I need to set up an http in node on the /ota/firmware endpoint?

I was trying to push the .bin file contents through MQTT and pass it directly to the ota-update node, but I can't tell if that's the way it should work or not:

I'm convinced, @phoddie can answer this better than I do...

When using push mode with the OTA node, there must be an HTTP In node in the flow. The OTA node adds a route to that for the firmware upload. Let me know how that goes. If it works for you, I'll update the docs and example. Alternatively, it may be possible to have the nodered2mcu tool automatically add the required dependency when it sees an OTA node configured for push mode (requires some investigation -- but that would be the most developer friendly solution).

FWIW – I believe that originally it wasn't necessary to add the HTTP In explicitly because the module was always included in the build. That was removed as a default to minimize the code size.

Thanks for the clarification... I think I was confused by the presence of an input port, which I assumed would accept a payload buffer containing the the new binary. Is there any technical reason why that could not work (other than it's not implemented that way ;*) ?

Reading through it more thoroughly I think the pull mode would be better for me anyway, so I am pursuing that.

Well, I'm hitting a dead-end using the "pull" mode, too...

# Break: mcu.deliver: cannot coerce undefined to object!
  #0: mcu.deliver /home/srickus/develop/node-red-mcu-plugin/node-red-mcu/nodered.js:138
  #1: (host)
XS abort: unhandled exception
# Break: C: xsDebugger!
  #0: mcu.deliver /home/srickus/develop/node-red-mcu-plugin/node-red-mcu/nodered.js:138
  #1: (host)

The code throwing the exception is this function:

    129                 static deliver() {
    130                         Timer.schedule(msgQueue.timer);
    132                         let current = msgQueue.first;
    133                         msgQueue.first = msgQueue.last = undefined;
    134                         while (current) {
    135                                 const next = current._next, target = current._target;
    136                                 delete current._next;
    137                                 delete current._target;
    138                                 target.receive(current, target.makeDone(current));
    139                                 current = next;
    140                         }
    141                 }

Has anyone seen this? The error seems to indicate that target is undefined.

This exception happens in the msg processing queue - deep in the core of node-red-mcu. That's logic a node usually isn't able to affect; very interesting though.
Can you share a flow that triggers this exception?

There is a technical reason: memory. There's a good chance that the firmware binary won't fit in the free memory (our physical memory!) of the ESP32. The OTA node writes the firmware image to flash as it arrives, to keep the RAM requirement to a minimum.

1 Like

Can you share the steps to reproduce this? Two pieces are important:

  1. The flow you are using on the MCU
  2. The flow or curl command or whatever you are using to push the firmware update.


So while I was trying to pare down my flows to something that you could use to debug, I think I may have found the issue -- it seems I overlooked the critical input msg.url parameter:

Instead, I was passing the url in the payload :*(

I find the editor for the OTA update node to be rather confusing -- if the one node has two different operating modes with different inputs, I think it needs to be more explicit as to which is being used... similar to the way the json node has a choice of "actions" with options for each (and a selector for which field to use for the input value):


Although I'm not seeing the mcu errors anymore, the ota update is not calling back, so I have more digging to do. I'll be back later!

Ok, I thought I was making progress... but there is something missing in my deployed binary. Notice how the ota-update node in "push" mode is not finding the http server on the device:

I'm seeing that same thing when trying to use the node in "pull" mode -- I added a console.log output to the ota-update.js module,

 56                 const url = new URL(msg.url);
 57                 if ("http:" !== url.protocol)
 58                         return void done("http only");
 59                 const options = {
 60               ,
 61                         host: url.hostname
 62                 };
 63                 if (url.port)
 64                         options.port = parseInt(url.port);
 65 console.log(
 66                 this.#client = new;

The xsbug-log output shows that object is undefined:

# starting xsbug
#xsbug-log listening on port 5002. ^C to exit.
#xsbug-log connected to "main"
Wi-Fi connected to "S&J-Poolhouse"
IP address
<info>{"payload":0,"topic":"WROVER/GPIO/18","_msgid":"dcb636228a4f0edf","source":{"id":"e9bc691dd336e8e5","type":"debug","name":"RPi-4 debug"}}
# Break: OTAUpdateNode.prototype.onMessage: cannot coerce undefined to object!
  #0: OTAUpdateNode.prototype.onMessage /home/srickus/develop/node-red-mcu-plugin/node-red-mcu/nodes/ota-update/ota-update.js:66
  #1: Node.prototype.receive /home/srickus/develop/node-red-mcu-plugin/node-red-mcu/nodered.js:383
  #2: mcu.deliver /home/srickus/develop/node-red-mcu-plugin/node-red-mcu/nodered.js:138
  #3: (host)
XS abort: unhandled exception

Any idea why my build is not including the network http module?

Can someone explain to me how the device object is defined/initialized?
(I guess I'm asking either @phoddie or @ralphwetzel) ...

The device global specified by ECMA-419 as the host provider instance. It is created by including the $(MODDABLE)/modules/io/manifest.json manifest in a project. It can be extended by other manifests during the build. For example, the HTTP client adds itself to device.

Any idea why my build is not including the network http module?

Yes, it was originally included as part of the core of Node-RED MCU. At some point, the core was minimized to shrink binary sizes and speed install. Unfortunately, the OTA node wasn't updated. I did that yesterday If you grab the latest Node-RED MCU repository, both the HTTP client and HTTP server are now included by the OTA node. It should again work normally.

1 Like

I've pulled the latest code with the new included modules, but it seems to make no difference:

# Break: OTAUpdateNode.prototype.onMessage: cannot coerce undefined to object!
  #0: OTAUpdateNode.prototype.onMessage /home/srickus/develop/node-red-mcu-plugin/node-red-mcu/nodes/ota-update/ota-update.js:66
  #1: Node.prototype.receive /home/srickus/develop/node-red-mcu-plugin/node-red-mcu/nodered.js:383
  #2: mcu.deliver /home/srickus/develop/node-red-mcu-plugin/node-red-mcu/nodered.js:138
  #3: (host)
XS abort: unhandled exception

The line #66 is trying to create a new -- but in other parts of the code I see references to could this be a typo?

I'm still trying to wrap my head around how all of these technologies are working together -- can you show me where in the code this module extension is being executed?

I didn't commit the change until I had independent confirmation that the change was working. So, it can work. My guess is that you aren't actually running it. That might be my mistake. I pushed to the Node-RED MCU repo but I didn't update the module on npm used from the Node-RED Library. Can you confirm that you installed the OTA module from the Node-RED Library? If not, how did you install it? Thanks.

Update: version 0.8.7 of the ota-update module is live with the manifest change.

I have cloned the node-red-mcu project to my local disk, and then npm installed it using the local file path. So node-red should be using it directly, and whenever git pull retrieves a new file I just restart node-red.

I did compare the newly pulled version of the manifest to my stashed version to see what you had to change:

[Mon 05:46:31 PM]
srickus@ shrpi4: ~/develop/node-red-mcu
$ git status
On branch main
Your branch is behind 'origin/main' by 1 commit, and can be fast-forwarded.
  (use "git pull" to update your local branch)

Untracked files:
  (use "git add <file>..." to include in what will be committed)

nothing added to commit but untracked files present (use "git add" to track)

[Mon 05:46:44 PM]
srickus@ shrpi4: ~/develop/node-red-mcu
$ git pull
Updating d445b50..efd9062
 nodes/ota-update/manifest.json | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

[Mon 09:39:56 PM]
srickus@ shrpi4: ~/develop/node-red-mcu
$ git diff stash@{0}
diff --git a/nodes/ota-update/manifest.json b/nodes/ota-update/manifest.json
index 3cebfcb..7018f37 100644
--- a/nodes/ota-update/manifest.json
+++ b/nodes/ota-update/manifest.json
@@ -1,7 +1,8 @@
        "include": [
-               "$(MODDABLE)/modules/network/http/manifest.json"
+               "$(MODDABLE)/examples/io/tcp/httpclient/manifest_httpclient.json",
+               "../network/httpserver/manifest.json"
        "modules": {
                "*": "./ota-update"

So I'm fairly sure I'm using the new code... and I ran a clean build to make sure my new binary was up to date.

Ok, I've rebuilt the npm modules, and am now using the newly updated ota-update@0.8.7 -- and now the build is a missing manifest file: build

>> IDF_PYTHON_ENV_PATH: /home/srickus/develop/esp32/esp-idf/tools/python_env/idf5.1_py3.10_env
>> mcconfig -d -x localhost:5004 -l -m -p esp32
# warning: -l deprecated. use -dl instead.!
### Error: '../network/httpserver/manifest.json': manifest not found!

I guess that path is different under the npm repo than under node-red-mcu/nodes?

Oh.... if you are running from the library that relative path can't resolve. Try replacing the line with "$(NODEREDMCU)/nodes/network/httpserver/manifest.json". That should always work.

FYI – I've committed the change to use the absolute $(NODEREDMCU)... path instead of the relative path. That is now in ota-update@0.8.8 and also in the Node-RED MCU repo.

1 Like