Firmware OTA and remote control on NXP FRDM-RW612
This article shows how to add over-the-air (OTA) firmware updates and remote
device control to an NXP FRDM-RW612 board using the Mongoose library and
mDash, a device management cloud. You enable mDash with
three lines in mongoose_config.h, push new firmware with one curl command,
and call your own C functions on the device through a REST API. Nothing here
is RW612-specific: the same steps work on STM32, ESP32, NXP i.MX RT, RP2350
and other microcontrollers supported by Mongoose.
Why bother with a device management backend
Once you have more than a handful of devices in the field, you end up solving the same three problems again and again:
- Remote firmware update - ship a fix without anyone touching the device
- Remote device control - read state, flip a pin, change a setting
- Health monitoring - know when a device crashed, and why
You can build all of that yourself. You'd need a secure connection from device to cloud, an authentication scheme, a transport for firmware chunks, an RPC layer, and a server side to tie it all together. Or you can use mDash, which is designed to sit next to Mongoose and does exactly this. It is available as a managed service at https://mdash.net or as a private on-premises installation.
mDash is not meant to replace your product's own cloud. It takes care of the boring fleet plumbing, and your backend talks to it over a REST API.
This article covers OTA and remote control. Health monitoring will get its own video.
What you need
- NXP FRDM-RW612 board, connected to your network with an Ethernet cable
arm-none-eabi-gcctoolchain andmake- The Mongoose repository
- A free account on https://mdash.net
Step 1 - build and flash the minimal example
The RW612 example lives in
tutorials/nxp/frdm-rw612.
There are two projects: mcuxpresso and minimal. I'm using minimal, which
is a plain Makefile project with no IDE involved. Everything below works the
same in the MCUXpresso project.
cd mongoose/tutorials/nxp/frdm-rw612/minimal
make flash
On the first run, the Makefile pulls the CMSIS headers for ARM and RW612, then builds and flashes the firmware. Open a serial monitor before you flash (115200 baud) to see the boot log. The board brings up Ethernet and gets an IP address over DHCP.
The firmware runs a tiny web server, so you can check it right away:
curl http://BOARD_IP/
Hi from Mongoose, tick 12345
curl http://BOARD_IP/api/tick
{"tick":12346}
That's the whole starting point: a board on the network with Mongoose running. Now let's hook it up to mDash.
Step 2 - connect the board to mDash
Log in to https://mdash.net and click "Add device". A new device shows up in the list. Click the lock icon next to it to copy the device password to your clipboard.
Then open mongoose_config.h. The example already has the mDash lines there,
just disabled. Turn them on and paste the password:
#define MG_ENABLE_MDASH 1
#define MG_MDASH_KEY "..." // Paste the device password here
#define MG_FIRMWARE_VERSION "1.0.0"
#define MG_TLS MG_TLS_BUILTIN
MG_TLS_BUILTIN is Mongoose's own TLS implementation, so you don't need
mbedTLS or wolfSSL to talk to the cloud securely. MG_FIRMWARE_VERSION is
reported to mDash as fw_version, which helps you see whether an update
actually landed.
Rebuild and reflash:
make flash
In the serial log you'll see the device connect to mDash and exchange a few messages with it (more on those below). In the mDash UI the device turns green - it's online.
Tip: keep the device password out of version control. It's a credential, not a config option.
Step 3 - OTA firmware update
To have something visible to update, I changed the LED blink interval in
main.c from 100 ms to 1 second:
static void blink_task(void) {
static uint64_t blink_timer = 0;
if (hal_timer_expired(&blink_timer, 1000, hal_get_tick())) {
hal_gpio_toggle(LED1);
}
}
This time, don't flash it. Just build:
make build
That produces firmware.bin. You have two ways to push it to the device.
Option 1: Web UI. In the mDash device list, click the download button on
your device and pick firmware.bin. The serial log shows the update going
through. The device reboots, comes back online, and the LED now blinks once a
second.
Option 2: command line. Get an API key from https://mdash.net/#/keys and run:
curl -su :MDASH_API_KEY -F file=@firmware.bin \
https://mdash.net/api/v2/devices/DEVICE_ID/ota
The CLI option is the one you'll actually use day to day, because you can drop it into a CI pipeline or a script that updates a group of devices.
What happens on the device during OTA
There is no special OTA client in your firmware. mDash drives the update with plain RPC calls that the Mongoose mDash connector registers for you:
OTA.Begin- takes the image size, prepares flashOTA.Write- takes a base64-encoded chunk, decodes it and writes it to flashOTA.End- finalises the update so the new image boots
On the RW612 those calls end up in Mongoose's RW612 flash driver
(MG_OTA MG_OTA_RW612 in mongoose_config.h), which writes to external
FlexSPI NOR flash. If you want the details of the flash layout and the
staging + copy approach, see
NXP RW612 OTA firmware update.
Step 4 - how remote control works
The device keeps a secure WebSocket connection to mDash. Over that link they exchange JSON-RPC frames. When the board connects, mDash asks it for basic info. In the serial log it looks roughly like this.
The cloud sends a request:
{"id": 1, "method": "Sys.GetInfo", "params": {}}
The device answers with a matching id:
{"id": 1, "result": {"fw_version": "1.0.0", "uptime": 3, "reboot_reason": "...", "arch": "...", "report": {"valid": false}}}
That's the whole protocol: the request has an id, a method and optional
params, and the response has the same id plus either a result or an
error.
The useful part is that mDash bridges this to HTTP REST. Any RPC function on the device can be called with an ordinary HTTP request:
https://mdash.net/api/v2/devices/DEVICE_ID/rpc/METHOD_NAME
mDash turns the HTTP request into a JSON-RPC frame, sends it down the WebSocket, waits for the reply, and returns the result as the HTTP response. Your device never needs to accept an inbound connection, so NAT and firewalls are not a problem.
Try it:
curl -su :MDASH_API_KEY https://mdash.net/api/v2/devices/DEVICE_ID/rpc/Sys.GetInfo
curl -su :MDASH_API_KEY https://mdash.net/api/v2/devices/DEVICE_ID/rpc/RPC.List
RPC.List returns every function registered on the device. Out of the box
you get the built-in ones from the mDash connector: RPC.List,
Sys.GetInfo, OTA.Begin, OTA.Write and OTA.End.
Step 5 - add your own RPC function
Built-ins are fine, but the point is to expose your own functionality. Let's
add Sys.Pin, a function that reads or writes any GPIO pin:
{"pin": N}- read pin N and return its value{"pin": N, "val": V}- write V to pin N- anything else - return an error
Add this handler to main.c:
static void mg_rpc_sys_pin(struct mg_rpc_req *r) {
int pin = (int) mg_json_get_long(r->frame, "$.params.pin", -1);
int val = (int) mg_json_get_long(r->frame, "$.params.val", -1);
if (pin >= 0 && val >= 0) {
hal_gpio_write(pin, val); // Both pin and val are set: write pin
mg_rpc_ok(r, "true");
} else if (pin >= 0) {
mg_rpc_ok(r, "%d", hal_gpio_read(pin)); // Only pin is set: read pin
} else {
mg_rpc_err(r, 500, "%m", MG_ESC("set pin and val"));
}
}
And register it next to the built-ins, after mg_mgr_init():
mg_rpc_add(&mgr.rpcs, mg_str("Sys.Pin"), mg_rpc_sys_pin, NULL);
mg_json_get_long() pulls integers out of the request params and falls back
to -1 when a field is missing, which is how the handler tells the three cases
apart. mg_rpc_ok() and mg_rpc_err() build the response frame for you.
Build and push it over the air - no cable needed this time:
make build
curl -su :MDASH_API_KEY -F file=@firmware.bin \
https://mdash.net/api/v2/devices/DEVICE_ID/ota
Call RPC.List again and Sys.Pin is in the list.
Playing with the RGB LED
Call it with no params first, to check the error path:
curl -su :MDASH_API_KEY https://mdash.net/api/v2/devices/DEVICE_ID/rpc/Sys.Pin
You get an RPC error with the message set pin and val, exactly as written
in the handler.
The FRDM-RW612 has an RGB LED wired to three pins, defined in main.c:
#define LED1 PIN(0, 12) // Green
#define LED2 PIN(0, 0) // Blue
#define LED3 PIN(0, 1) // Red
So pin 0 is blue, pin 1 is red and pin 12 is green. To avoid fighting with
the blink task, I commented out the hal_gpio_toggle(LED1) line and
reflashed. Now read the blue pin:
curl -su :MDASH_API_KEY https://mdash.net/api/v2/devices/DEVICE_ID/rpc/Sys.Pin -d '{"pin": 0}'
It returns 1, and the LED is off. That's not a bug: the LEDs on this board
are active-low, so 1 means off and 0 means on. Turn blue on:
curl -su :MDASH_API_KEY https://mdash.net/api/v2/devices/DEVICE_ID/rpc/Sys.Pin -d '{"pin": 0, "val": 0}'
Set pins 0, 1 and 12 all to 0 and you get white.
Blinking an LED from a curl command is a toy, of course. But the same pattern works for anything you'd want to do remotely: read a sensor, change a config value, trigger a relay, dump a log. Write a handler, register it, push via OTA, and it's callable from your backend over REST.
FAQ
Does this work on boards other than the RW612?
Yes. The mDash connector is part of Mongoose and doesn't depend on the chip.
Anything Mongoose runs on with networking and an OTA driver works the same
way: STM32, ESP32, NXP i.MX RT, RP2350 and so on. Only the board-specific
hal.c and the MG_OTA setting change.
Do I need MCUXpresso or an RTOS?
No. The minimal example is a bare-metal Makefile project. Mongoose has its
own TCP/IP stack and TLS, so there's no lwIP or mbedTLS to integrate either.
How does the device authenticate to mDash?
Each device has its own password (MG_MDASH_KEY). The device opens a TLS
WebSocket connection to mDash and sends the password as a bearer token.
The connection is outbound only, so the device needs no open ports.
Can my own backend call functions on the device?
Yes, that's what the REST bridge is for. Your server calls
/api/v2/devices/DEVICE_ID/rpc/METHOD with an API key and gets the device's
JSON-RPC result back as the HTTP response.
Can I run mDash on my own servers? Yes. Besides the managed service at https://mdash.net, mDash is available as a private on-premises installation.
Links
- mDash documentation
- JSON-RPC API reference
- NXP RW612 OTA firmware update - flash layout and OTA internals on RW612
- FRDM-RW612 example on GitHub