Zephyr™ JavaScript 运行时简介

S c r o l l D o w n

原文链接: https://www.zephyrproject.org/community/blog/introducing-javascript-runtime-zephyr-os
原文作者: Geoffrey Gustafson, Intel Corporation
微信公众号链接: Zephyr™ JavaScript 运行时简介

> Belem 翻译为中文供使用 Zephyr.JS 的开发者参考。

JavaScript* 是当今最广泛使用的编程语言之一,近年来,它已经从桌面 Web 浏览器起源转向使用 Node.js* 的服务器。现在它也可以用于物联网 (IoT) 的具有用于 Zephyr™ JavaScript 运行时环境的最小设备中。

ZJS 基于 JerryScript (一个轻量级 JavaScript 引擎) 和 Zephyr 实时操作系统 (RTOS)。ZJS 项目提供了使用传感器,执行器和通信的 Node.js API 和 JavaScript API 的子集,以及使用它们构建应用程序的工具。ZJS 环境易于学习,非常适合快速原型开发,特别是对于拥有 JavaScript 技能的开发人员。

ZJS 项目最初的计划是在 Arduino 101* 开发板上完美工作,Arduino 101 是一款基于 Intel® x86 的入门级 Arduino 平台,这些功能类似于 Arduino Uno*,但额外增加了低功耗 Bluetooth® 以及重力加速度计和陀螺仪。

预览

在我们了解详细信息之前,让我们先看一看为小型设备开发 JavaScript 应用程序的情形。此脚本控制一对危险敏感的太阳镜。

var sensitivityUp = 1500;
var sensitivityDown = 1200; // hysteresis to avoid flicker

var perilDetector = aio.open({device: 0, pin: pins.A0});
var sunglasses = gpio.open({pin: pins.IO4, direction: ‘out’});
var dark = false;

setInterval(function () {
var perilLevel = perilDetector.read();
if (perilLevel > sensitivityUp && !dark) {
print(‘Peril detected, enabling sunglasses’);
sunglasses.write(true);
dark = true;
}
else if (perilLevel < sensitivityDown && dark) {
print(‘Peril averted’);
sunglasses.write(false);
dark = false;
}
}, 10);

危险传感器报告一个与检测到的危险量成比例的电压。Arduino 101 将电压转换为精度为12位的数字 (范围为0至4095) 。当危险水平太高时,眼镜完全变黑,眼镜的佩戴者保持没有报警。

背景

Zephyr 项目是 Linux 基金会今年早些时候发布的一个开源的实时操作系统。它支持越来越多的板卡上的 x86,ARM* 和ARC* 处理器,如 Arduino 101,Minnowboard Turbot,NXP* FRDM-K64F 和 Arduino Due。

JerryScript 是一个开源的,轻量级的 JavaScript 引擎,为受限设备完全实现 ECMAScript 5,甚至支持具有小于 64 KB RAM 或 200 KB ROM 的设备。它已经在各种主板和操作系统上支持 x86 和 ARM 处理器。

英特尔于 2016 年年初开始合并这两种技术,有使用 Zephyr OS 为 Arduino 101 和未来的 IoT 开发板提供替代开发环境的想法。目前,Zephyr 项目应用程序是用 C 编写的,编程模型需要大量的专业知识。而 JavaScript 接口隐藏了很多这样的复杂性。我们还想探索在从小型嵌入式设备到最大型服务器以及从原型到无缝端到端应用程序的各个层面上提供 JavaScript 的优势。

生成的项目称为 Zephyr™ OS 的 Javascript 运行时 (ZJS) ,并提供了一个 JerryScript 环境,添加了 API 以暴露 IoT 硬件功能,并提供了简化开发人员体验的工具。

架构

当您构建 Zephyr OS 应用程序时,您将创建一个专用于运行单个应用程序的整体映像 (image),只使用您的应用程序实际使用的操作系统组件,这使 image 较小。

Zephyr OS 支持执行线程服务,定时器服务,内存管理,同步和数据传递服务,每个服务都可以根据是否需要服务单独包含或排除。它还为其支持的 SoC 和板卡的各种硬件特性提供驱动程序,例如通用 I/O (GPIO) 模数转换 (ADC) ,脉宽调制 (PWM) ,I2C 总线,SPI 总线,UART 和蓝牙低功耗。再次说明,这些可以以最小化图像的 ROM 和 RAM 配置文件单独包括或排除。

ZJS 通过简单的静态分析传入的 JavaScript 代码建立在 Zephyr OS 的可配置性之上。如果使用 GPIO 接口,将构建并包括所需的驱动程序和相应的 ZJS API 代码,否则它不会构建。如果您使用每个可能的驱动程序,生成的 image 将对于小型设备而言将会太大。例如,添加蓝牙低功耗支持 (包括 Zephyr 操作系统驱动程序和 ZJS API 层) 目前使用额外的 7.2 KB RAM 和 56 KB ROM。但是,如果您在开发时注意 image 大小,并在必要时限制包括其他依赖项,则可以平衡这些约束。

目前,ZJS 为 GPIO,ADC,PWM,I2C,BLE 提供 API,并为带 RGB 背光的 Grove LCD 面板 (I2C 器件) 提供简化的接口。它还支持 setInterval / setTimeout,Promises 的简化实现以及 Node.js 缓冲区和事件 API 的子集。此外,ZJS 包含了对 IoT 互操作性的开放式连接基础 (OCF) 规范的支持,使用 iotivity 节点模块在 Node.js 中提供相同的 JavaScript API。我们正在努力支持串行外设接口 (SPI) 总线,W3C 通用传感器 API 和其他 Node.js API。

JavaScript Runtime for Zephyr™ OS (ZJS) Architecture on Arduino 101

图 2: Zephyr™ 系统 JavaScript 运行时 (ZJS) 架构 (Arduino 101)

Arduino 101 内的 Intel® Quark™ SE 处理器有两个 MCU 内核:一个采用 x86 架构,另一个采用 ARC 架构,C 程序员必须直接处理以便充分利用硬件需要使用两个内核。幸运的是,ZJS 可以从同一个 JavaScript 上下文使用所有的硬件功能。ZJS 运行时在必要时无缝地与 ARC 核心上运行的支持 ZJS 的 image 通信。

Arduino 101 包括 80 KB 的 RAM 和 384 KB 的闪存。默认情况下,闪存分区以将 144 KB 分配给 x86 应用程序,152 KB 分配给 ARC 应用程序。ZJS 在 ARC 端需要最少的代码,因此对于为 x86 应用程序腾出更多空间重新分区设备是有益的。我们已经为已发布的 Arduino 固件开发了一个补丁,允许您从 144 KB x86 分区迁移到 256 KB 分区,然后再次迁移。这提供了足够的空间来运行更大的 ZJS 应用程序。此外,我们将提供二进制 flashpack,使更改或恢复您的分区更简单。

优势和权衡

将 JavaScript 引入小型嵌入式设备有下面几个好处:

* 很多人都知道 JavaScript,这使得 ZJS 环境开发对更多的人来说更容易。
* Javascript 能够在不同环境(桌面,云,移动和 IoT 设备)之间重复使用代码。
* 我们获得了在这些系统之间使用通用数据交换格式 JSON 的潜力,这降低了封送处理模板的开销。JavaScript 还打开了在浏览器中更容易模拟设备行为的可能性。
* 正如任何解释性语言 (包括 JavaScript) 的典型,从做出改变到看到行动变化的开发周期是快速的。不可否认,最初我们的系统是丢失这个优势的,因为你需要编译 Zephyr 操作系统与您的应用程序,并将其闪存到设备。但我们可以使用 ashell 功能来回收一些这样的好处,这是一个不安全的开发者模式,允许您与设备交互并上传新的 JavaScript 而不闪烁。我们计划在此基础上提供一个基于浏览器的 IDE,这可能使我们能够使用模拟器在浏览器中完全开发。

当然,在小型嵌入式设备中使用 JavaScript 是有所消耗的,本地代码通常会更快更小。这个问题在受限设备上更加尖锐,因为 JavaScript 引擎的开销消耗了系统 RAM 和 ROM 的大部分。这会限制您为应用程序留下的资源。在撰写本文时,最小的 HelloWorld.js 示例需要大约 133KB 的 ROM。较大的 WebBluetoothDemo.js 示例需要大约 199KB 的 ROM 和 42KB 的 RAM。为了比较,相同 WebBluetooth 样本的 C 实现需要大约 65KB 的 ROM 和 18KB 的 RAM。(RAM 差异包括为 JavaScript 分配的大堆,这不是所有都在使用)

在 C 应用程序可能在中断处理程序中处理简单的情况下,性能差异也将非常明显。要在 JavaScript 中执行这个逻辑,我们首先要在任务上下文中处理一个事件 (Zephyr OS 等同于进程上下文),使上下文切换,然后对 JavaScript 引擎进行回调,最后得到再次调用本地 API 以产生效果。

还存在 JavaScript 在实时性能方面减少确定性的问题。例如,垃圾收集可以在正常的活动脉冲中引入停顿。因此,JavaScript 可能不适合具有严格实时要求的应用程序,但它非常适合于通用应用程序。然而,随着项目的成熟,我们可能能够减轻这些类型的问题。

详细示例

现在让我们仔细看看一些使用 ZJS 的示例代码,我们可以使用它来实现微型热气球的高度控制器的一部分。我们将使用模拟传感器测量高度,并使用 PWM 引脚控制到燃烧器的气体流量。然后我们将为了其他航空旅行者的安全闪烁一个 LED 灯。最后,我们将添加一个用于快速着陆的中止开关和一个通风口控制。

Hot air balloon

图: 热气球
by Rona Proudfoot is licensed under Creative Commons Attribution Share-Alike 2.0.

首先,我们使用 require() 函数来使可选的 API 模块可用。这是 Node.js 提供的简化版本,目前只允许您从内置到 ZJS 系统中的现有组件中选择。此代码基于早期版本的 API,我们预计这些 API 很快就会发生重大变化。

现在我们有我们需要的 API 模块和 Arduino 101 的命名引脚快捷方式,我们将设置我们使用的引脚。

Arduino 101 在引脚 3,5,6 和 9 上支持 PWM。注意,vent 是 GPIO 输出,而 abort 是 GPIO 输入。 通过使用上升沿输入配置中止引脚,可以在 Zephyr OS 中启用中断。

现在让我们将气球上的 LED 配置为每四秒闪烁一次:

接下来,让我们编写主反馈循环,瞄准所需的高度,假设一个理想的高度计传感器报告海拔高度为 12 位精度,涵盖从海平面以下 1000 米到海拔 9000 米的范围。 我们将每 50 毫秒读取一次,并根据需要进行调整。

var totalError = 0;
var lastError = 0;

timerID = setInterval(function () {
var raw = altimeter.read();

// convert raw reading to meters
var altitude = raw / 4095.0 * 10000 – 1000;

// implement simple PID controller
var error = targetAltitude – altitude;
totalError += error;

// carefully tuned PID constants for our device
var proportional = 0.042 * error;
var integral = 0.00042 * totalError;
var derivative = 0.0042 * (error – lastError);
lastError = error;

// limit pulse to range of valve
var pulse = proportional + integral + derivative;
if (pulse 20000)
pulse = 20000;
gasValve.setPulseWidth(pulse);
}, 50); // every 50ms

最后,如果我们检测到我们失去了气体或从无线电控制器收到中止命令,让我们快速着陆:

// stop the loop (smooth landing left as an exercise for the reader)
clearInterval(timerId);
}

当我们设置 onchange 函数时,我们在内部设置一个回调函数来处理来自中止开关引脚的上升沿中断。当该中断来时,我们存储来自引脚的更新值,并在主循环中从任务上下文中发出要处理的回调。当主循环运行并处理该回调时,可以安全地调用 JavaScript 引擎并使用新值更新此 onchange 函数 (尽管我们不必在此特定 JavaScript 回调中查看 event.value)。

如果你有 JavaScript 的经验,这段代码应该很容易理解,你可以看到能够以这种直接的方式使用真实世界的传感器和执行器的力量!

参与

我们现在只是在 GitHub 上打开我们的存储库,它还处于初始开发阶段。我们邀请您使用我们的代码,并欢迎您加入我们,以确定项目的未来。我们有很多方面等待贡献,如新的 API,新的平台支持,改进的调试,图像大小优化,软件更新或任何你可以想到的。

该项目托管在 http://github.com/01org/zephyr.js ,开发讨论在 Freenode 的 #zjs 频道上进行。

发表评论

电子邮件地址不会被公开。 必填项已用*标注