A new approach to embedded scripting and developing for IoT with mJS



In my previous article, I talked about IoT (Internet of Things) and connecting physical objects (“things”) to the internet. I’ve discussed how Mongoose OS, an open source operating system for IoT, makes programming microcontrollers in JavaScript easy for both newbies and professional developers.

You may have wondered why is it JavaScript and how a JavaScript engine fits into the limited memory on a microcontroller. There are many projects out there that aim to put scripting on microcontrollers, and that includes other JavaScript projects, including Duktape, Espruino, Jerryscript, MuJS, and V7. There are also other scripting languages, such as  MicroPython and Lua.

One common thing these projects share is an attempt to implement the entire language specification together with the complete standard library. There are pros and cons. The pros are obvious, but what are the cons?

The cons

First, none of the popular scripting languages have been designed for the embedded environment in the first place. They drag along some obscure constructs that take precious space but have very little practical usage in the embedded context.

Second, to export a hardware-specific functionality into the scripting environment (such as a certain sensor API or a certain LCD display API) you’ll need to write a glue code. And you’ll need to maintain that glue code, which takes up precious space and increases overall complexity.

To address these issues a new JavaScript engine was introduced.

mJS

mJS is a part of Mongoose OS and takes a radically different approach:

  1. It does not implement the entire language, but a limited subset.
  2. It has no standard library.
  3. It has no glue code.

These features make mJS fit into ~25K of flash space and less than 1K of RAM. That is hard to beat. But how can you develop without a standard library and without any glue code? The mJS’s answer is by having an ability to call C SDK (Software Developers Kit) functions directly.

How to call C SDK functions directly

Foreign Function Interface (FFI) has the ability to load and call C functions directly. To do these steps, mJS has to know two things: an address of the C function and a signature of the C function. Then it marshals JavaScript arguments into C values, puts them to where the ABI (Application Binary Interface) demands—for example, on the CPU stack—and jumps to the function’s address. Practically, it looks like this:

let f = ffi('int gpio_write(int, int)');   f(2, 1);   

This snippet loads a C SDK function gpio_write(int pin, int value) and calls it, setting GPIO pin 2 to the high-voltage level. That’s all. Need other functionality from the SDK or a third-party library? Just load it on demand. You can even do things like:

let malloc = ffi('void *malloc(int)');   let mem = malloc(10);

Not saying that you should, but you can. Also, you can marshal C callbacks:

let Timer = {     set: ffi('void timer(int, void (*)(int, userdata), userdata)') };  Timer.set(100, function(time) {     print('Time now: ', time); }, true);

Do you need an embedding API? No, you don’t. You don’t need any glue code either. And also you don’t need a standard library.

See how mJS is used in our Mongoose OS example firmware. And you can use it with any C/C++ software, such as with specific firmware. For a more in-depth introduction, see the mJS GitHub repo for the embedding example.

Ask questions on our developer forum or send us a message.



Source link

,

Leave a Reply