JavaScript for embedded devices
October 07, 2014
Story
Programmers creating software for embedded systems are not strangers to scripting. They choose scripts because they are the fastest way to solve probl...
Programmers creating software for embedded systems are not strangers to scripting. They choose scripts because they are the fastest way to solve problems. Scripts are used to automate builds and run validation tests. Interpreted languages – a common definition of what makes a programming language a scripting language – such as Forth have been used for decades to reduce code’s memory footprint while improving maintainability in a portable way.
From toy to tool
JavaScript first emerged in 1995 as the programming language for web pages. Created by Brendan Eich by applying C-like syntax to prototype-based inheritance, JavaScript wasn’t perceived as a viable tool for creating commercial software for any environment. That perception slowly changed. Behind the scenes, JavaScript was made a standard through ECMA International, and is now on its 6th major revision.
The insightful work of Douglas Crockford at Yahoo created the data interchange format JSON (JavaScript Standard Object Notation) as a strict subset of JavaScript. JSON has quickly replaced XML as the preferred data format to communicate between web clients and servers, due to its simplicity and ease of use, particularly from JavaScript software.
In 2009 Ryan Dahl introduced node.js, a surprisingly efficient way to build web servers in JavaScript using Google’s lightning fast V8 JavaScript engine. node.js has seen rapid adoption, bringing to life a strong ecosystem of developers and a vast collection of open source modules.
Tim O’Reilly, founder of O’Reilly Media, famously commented on the shift of JavaScript from a toy for amateurs to a tool for professionals: “Learning JavaScript used to mean you weren’t a serious software developer. Today, not learning Javascript means the same thing.”
JavaScript is now widely used in web pages, web servers, and mobile applications. But not yet in embedded systems. That is poised to change.
Familiar syntax
The majority of embedded programming today is done in C. Unlike many scripting languages that require C programmers to learn a different syntax and style of programming, the syntax of JavaScript is very similar to C. Experienced embedded developers may need only a single afternoon with the language before they can read, understand, and successfully modify JavaScript code.
Of course, effectively using any programming language requires study. Many JavaScript books focus on building web pages, but David Flanagan’s excellent JavaScript: The Definitive Guide introduces the language independent of the web. Crockford’s influential JavaScript: The Good Parts is an opinionated, pragmatic style guide for experienced JavaScript programmers.
But scripts are slow
Embedded developers are vigilant about performance. Scripting languages are inherently slower than C. As a result, the first impulse of any good embedded developer is to be extremely skeptical of any scripting language. However, reliable ways to deliver great performance are available:
- Leverage built-in functions and objects. The JavaScript language has sophisticated built-in objects that support arrays, JSON, regular expressions and other string operations. The implementations of those are usually well optimized in the JavaScript engine. Take the time to learn about these to avoid duplicating their functionality in your scripts.
- Code carefully. Because scripts are slower than native code, well-optimized code is critical where performance matters. The dynamic nature of JavaScript means the JavaScript engine often cannot optimize code as effectively as a C compiler.
- Don’t try to do everything in script. Native code is faster, so use it for computationally complex functions. No language is right for all situations, so choose the best language for the job rather than trying to be “100 percent pure.” Every JavaScript engine provides a way to call native code from scripts. If unsure about performance, first implement in JavaScript because it is easier to do. Afterward, profile the system. If there is a performance bottleneck, re-implement the function in C.
What about a JIT?
A common solution for improving the performance of interpreted code is a just-in-time compiler (JIT). A JIT compiles script code to native code on the target device. Use of a JIT is common in JavaScript engines in modern web browsers. However, using a JIT on an embedded device is problematic because the JIT itself requires additional code space, because the native code consumes memory in addition to the original script code, and because running the JIT takes CPU cycles.
An embedded device has one significant advantage over a web browser: While a browser must be able to run any of billions of web pages efficiently, an embedded device needs to run only its built-in software efficiently. This gives the embedded developer the option to directly code performance-critical scripts in C instead of relying on a JIT.
A promising alternative to using C is emerging in asm.js, a project of Mozilla. asm.js defines a strict subset of JavaScript that is considerably easier to translate to native code or C source code. asm.js takes advantage of the syntax similarities between C and JavaScript to make this translation straightforward. An embedded developer can write performance-critical code in the asm.js JavaScript subset, and compile it to native code as part of the build process, instead of relying on a JIT. The initial development work on asm.js shows performance at about half of an optimal native implementation, with additional developments expected to reduce that gap.
Memory
Memory space is another key concern of embedded developers. JavaScript has both pros and cons. One obvious downside is the JavaScript engine used to execute the scripts, which is a non-trivial size – from tens of kilobytes to many megabytes depending on the engine. Because JavaScript uses dynamic typing, variables have some additional memory overhead. As a worst-case example, a Boolean value may use 16 bytes.
On the positive side, JavaScript uses a garbage collector, eliminating the need to explicitly free memory. With this simplification, the embedded developer gains time to focus on other aspects of development. Additionally, by automatically freeing unused memory, the garbage collector eliminates many slow, small memory leaks that lead to long term instabilities that can be extremely challenging to isolate and fix. This alone makes it important for embedded systems that must operate reliably for months or years. Furthermore, there are scenarios where studies show a garbage collector has lower CPU requirements than explicitly freeing memory.
One risk with a garbage collector, especially in constrained memory environments, is thrashing – the garbage collector running so often that it adversely impacts system performance. This problem can be avoided through a JavaScript programming style that largely limits creation of new objects to major state changes in the embedded device or application. This approach keeps memory usage largely stable, so the garbage collector doesn’t need to run often.
User scripting
An embedded device using JavaScript can choose to enable scripting by end-users. Because JavaScript is a managed execution environment, end-user scriptability can be secure by safely sandboxing scripts. The customization enabled by allowing consumers, hobbyists, and professional programmers to script devices will create new opportunities, much as the app revolution did on smartphones.
Embedded JavaScript today
Embedded devices are no longer islands. They connect with each other, with web services in the cloud, web browsers, and with mobile phones and tablets. JavaScript is the only language that can be used to develop the software for all of them, and the data they exchange using JSON.
The potential of JavaScript on embedded devices to simplify the development effort for embedded programmers – and deliver more reliable, more customizable products for customers – is significant. By adopting JavaScript, creators of embedded devices will be able to tap into the innovation of the many active JavaScript programmers.
As an industry, we are just at the cusp of JavaScript becoming a reality in embedded devices. Fortunately, there are already three development systems available where developers can begin working with embedded JavaScript:
- Espruino is a single chip development board, billed as JavaScript for Microcontrollers. It runs the open source Espruino JavaScript engine, which supports a subset of the JavaScript standard.
- Tessel brings integrated Wi-Fi to a JavaScript-focused microcontroller solution. Tessel provides a node.js compatible API, easing adoption for node.js developers. Tessel translates JavaScript to Lua (another scripting language) to execute. The translation implements a subset of the JavaScript standard.
- Kinoma Create from Marvell Semiconductor is a “JavaScript powered Internet of Things construction kit” running Marvell Semiconductor’s XS JavaScript engine, which supports the full JavaScript 5th Edition standard.
Each of these development systems are a good tool to start JavaScript development on embedded devices. Though JavaScript was first introduced nearly 20 years ago, it is just beginning to be used in embedded computing. There will undoubtedly be many exciting ways this will further evolve. By getting started now, you can help shape how JavaScript will be used in future embedded systems.
Marvell Semiconductor
www.marvell.com
www.kinoma.com
Twitter: @marvellsemi
Facebook: www.facebook.com/pages/Marvell-Semiconductor/96462647125
Google+: plus.google.com/111540192483002553954
YouTube: www.youtube.com/user/marvellmedia