Write a small JavaScript script that blinks a LED on a STM32F board by interacting with a couple of ARM mbed C HAL functions.
It should work on other targets supported by ARM mbed.
BackgroundThere are many reasons why you might want to add a scripting functionality to your embedded MCU.
This project here focuses on those want to embed a JavaScript VM into their existing C firmware, e.g in order to quickly customize the core business logic without having to reflash the device.
In this project, I'll choose V7, an embedded JavaScript VM I co-authered along with my team-mates at Cesanta.
The V7 library is distributed as just two files: v7.c and v7.h. In order to use the V7 library all you need to do is to import those two files in your project.
V7 is written in portable C, and can thus easily be used from C++. V7 is platform independent; here we'll show how to embed it in ARM mbed projects and to build it on an STM32F4 device, but it should work on other targets too.
TL;DR: show me the codeHere is the full source code:
#include "mbed.h"
#include "v7.h"
DigitalOut led_green(LED1);
enum v7_err js_set_led(struct v7 *v7, v7_val_t *res) {
led_green = !v7_get_bool(v7, v7_arg(v7, 0));
return V7_OK;
}
enum v7_err js_wait(struct v7 *v7, v7_val_t *res) {
wait(v7_get_double(v7, v7_arg(v7, 0)));
return V7_OK;
}
int main() {
struct v7 *v7 = v7_create();
v7_set_method(v7, v7_get_global(v7), "setLed", &js_set_led);
v7_set_method(v7, v7_get_global(v7), "wait", &js_wait);
v7_exec(v7,
"while(true) {" \
" setLed(false);" \
" wait(0.5); " \
" setLed(true);" \
" wait(0.5); " \
"}",
NULL);
}
You can easily clone the project in the ARM mbed online compiler or command-line tools; just open: https://developer.mbed.org/teams/Cesanta/code/DISCO-F469NI_javascript_blinker/
We'll break down this project and show how to install the required dependencies.
Before JavaScriptLet's see first how a simple blinker would look like without the scripting part:
#include "mbed.h"
DigitalOut led_green(LED1);
int main() {
while(true) {
led_green = false;
wait(0.2);
led_green = true;
wait(0.8);
}
}
You can now compile this code in the mbed online compiler or, alternatively, you can use the mbed-cli tool.
Enter JavaScriptLet's first add the V7 library. I packaged the v7.c and v7.h files in a mbed code library, so it's easier to add them to your project and to get updates.
Or, if you use the command-line tool:
mbed add http://mbed.org/teams/Cesanta/code/v7
Now, the library will be built into your firmware. But in order to use it you need to include its header:
#include "v7.h"
Then, inside your main function, you have to create an instance of the virtual machine:
struct v7 *v7 = v7_create();
This v7 variable holds the whole VM state and needs to be passed around when calling V7 API functions.
So, how do we run a script?
v7_exec(v7, "print('hello from javascript')", NULL);
The last parameter of v7_exec is used to grab the result of a JavaScript expression. We'll ignore that.
JavaScript and devicesAwesome! We managed to run some JavaScript code!
But, how can we make that JavaScript code do something useful on the device? V7 is just a generic JS VM library, it doesn't know anything about your embedded board, its API etc.
Let's first write a simple JS script that uses two simple functions we're going to export from the our device's SDK:
while(true) {
setLed(false);
wait(0.2);
setLed(true);
wait(0.8);
}
We can invoke it inline with v7_exec, or we can load it from a file on flash storage, or download it from network:
v7_exec(v7,
"while(true) {" \
" setLed(false);" \
" wait(0.2); " \
" setLed(true);" \
" wait(0.8); " \
"}",
NULL);
So, let's register a couple functions that allow us to interact with the SDK at hand:
v7_set_method(v7, v7_get_global(v7), "setLed", &js_set_led);
v7_set_method(v7, v7_get_global(v7), "wait", &js_wait);
This code registers two global JavaScript functions, setLed and wait, that can be invoked from your scripts as if they were native functions.
Their implementation is written in C. Let's see how it works by looking closely at how js_wait is implemented:
enum v7_err js_wait(struct v7 *v7, v7_val_t *res) {
wait(v7_get_double(v7, v7_arg(v7, 0)));
return V7_OK;
}
- v7_arg(v7, 0): takes the first argument passed to wait(). It returns a v7_val_t value, which represents a generic JavaScript value.
- v7_get_double(v7, val): extracts the C double floating-point value from a v7_val_t
You can read more about the V7 API here.
Comments