作者 | Sebastián Peyrott
责编 | 苏宓
微信公众号ID | csdn_iot
如果你想入门学习如何在一个在小型 IoT 设备(例如微控制器平台)上运行 JavaScript,那么这篇文章就是为你量身定制的。
JavaScript 不仅是 Web 时代的通用语言,现在还延伸到了令人难以置信的其它地方。它是一个对初学者友好的语言(初始版本在 10 天内构建完成),现在已经无处不在:服务器、工作站、数据库,甚至还包括物联网设备。
不过,由于某些微控制器固有的低端特性,使得初学者使用 JavaScript 变得有些困难。在这篇文章中,我们将会先走马观花般地看看在小型设备上运行 JavaSript 有哪些选择,然后将会挑选其中一个并让它运行在某个 JavaScript 引擎上。现在,我们一起来探索 JavaScript 的旅行吧!
1
简介
我始终坚信,某些事即使有实现的可能,也并不意味着它就应当被实现。我认为这也同样适用于 JavaScript。抛开 JavaScript 自身的缺陷,我认为它是一个相当合适的编程语言。不过,这不意味着它适用于所有的地方。因此,我先事先阐明我的观点:为自己的工作挑选编程语言不仅仅是一个纯粹的选择或尝试。编程语言仅仅是一个工具,工具在被设计时就有特定的使用场景和应用程序。
例如,你也可以拿一个锤子来订螺丝钉(而不是钉子),其结果可能会不尽如意,甚至可能干了坏事儿。这同样适用于编程语言。JavaScript 是一个动态的、允许用户在各个版本间快速迭代的、可以完成小型自动化任务和脚本的语言(你可能在其它网站上经常看到这段话),也是一个初学者和熟悉 Java 语法的人均容易上手的语言。
请先记住这个观点,我们会在下一节中对其进行阐释,现在只需要记住:将编程语言当做一个工具,并“为合适的工作挑选合适的工具”。
如果你不熟悉微控制器,你可以把它理解成一个非常老的系统:RAM 容量很小,CPU 速度非常慢,“磁盘”容量也非常小。当然,微控制器的的尺寸也各不相同,大一些的微控制器可以跑更大的系统,不过它们的目标基本是相同的:微控制器通常被设计用于提供专用的编程功能,且保持成本低廉、尺寸小巧。更大的系统,例如你的智能手机中所使用的系统,功能更加强大,但是却更加昂贵。
当我们在 IoT 热潮中看到有越来越多的微控制器时请不要那么吃惊,因为它们一方面比大型系统更加便宜,另一方面由于越来越成熟的技术使其也变得更加强大和复杂。因此,现在某些价格低廉的产品(例如千篇一律的物联网水壶)也具有强大的编程能力。
依然对微控制器没什么概念?我们现在来直观感受一下。在本文中,我们是使用的微控制器是一个 32 位的 120MHz 的 CPU,其 RAM 是 128KB,Flash 存储空间是 1MB。更让人哭笑不得的是,这已经是相当强大的微控制器了,有许多 8 位的微控制器,它们只有 0.375KB 的 ROM,16 字节的 RAM!这些微控制器主要用于超低功耗、超低成本的应用中。我们在本文中需要做的事(运行 JavaScript 引擎)需要更多强大的功能。
需要说明的是,时至今日,微控制器的定义已经有些模糊。在上世纪九十年代,微控制器通常指的是集成了少量 RAM 和 ROM 的小型嵌入式 CPU。如今,大家谈论的都是片上系统(SoC)。片上系统是用于描述集成了 CPU、RAM、ROM 甚至一些外设(例如 GPS 接收器)的高度耦合的硬件设计的另一种方式。我们在本文中将交替使用这两种术语。
2
为什么选择 JavaScript?
当提到嵌入式开发时,你通常会想到如何在硬件层面下手。例如,如果你需要读取某种传感器,你会将传感器的值用某个公式进行转换,然后将其结果显示在某个地方。你会选择带有模数转换器(ADC)且能驱动显示设备的微控制器。对于所有满足你功能的微控制器,越小价格越便宜。当然,越小的控制器,你在编程的时候受到的限制就越多。
对于某些非常小的设备,你可能需要使用汇编语言;如果你随后打算切换到其它平台上去,则会加大程序移植的难度。通常,你需要挑选一个满足你的需求的较小的、但不是最小的平台。你应当考虑到将来的扩展性或者新的需求,因此通常需要在硬件层预留一些空间。
当你使用稍大的微控制器时,一些大门正在为你开启。第一道大门通常是 C 语言。使用 C 代替汇编,主要有两点好处:可移植性和易用性(包括可读性和易于维护性)。C 语言被设计为”可移植的汇编语言“,是大多数小型微控制器(除极小的微控制器之外)的理想编程语言。当大小和功能继续增加的时候,更多大门开始打开:C++、操作系统(FreeRTOS, Nuttx 等),甚至包括以太网或者WiFi连接。
伴随着这些选择,可以使用的编程语言越来越多。某些编程语言,由于它们自身你的语法特性,不能被提前编译成机器代码。JavaScript 就是其中之一。那么,我们首先需要问自己的是”JavaScript 引擎能运行在我们的平台上吗?“。答案是:能!今后将在随后看到。
与汇编、C、C++ 相比,更上层语言最大的优势在于它们的安全性。在 macOS 上面,一点细微(off-by-one)的错误通常都会导致段错误;在微控制器上面,它甚至会造成整个系统级崩溃。在微控制器上,程序的调试也非常受限,所以任何可以有助于编写安全代码的方法都是非常有帮助的。更上层的语言在这一方面的好处非常明显。
再来看看在微控制器中以这种方式运行代码的另一个好处。通常,微控制器会从 ROM 中读取代码,而 ROM 中的数据是通过特殊的方法写入的,因此改变它的值非常不方便。这限制了代码更新或者程序不同版本之间的快速迭代。解释器可以读取 RAM 或者 ROM 上的程序,使无需烧写 flash 就能更新代码成为可能。
当然,这需要你自己权衡利弊:系统重启后 RAM 的数据不能恢复,因此程序需要每次重新加载;由于 RAM 大小受限,因此程序也必须非常小。上层语言富有表现力的特性在这方面具有很多的帮助:只需要几行简单的代码,就能表达复杂的行为和逻辑。
这些好处在大多数高级、解释型或实时编译的语言间均存在,但是 JavaScript 自身还有一些优势。首先,JavaScript 有大量的社区以及大量的库。虽然由于缺少系统级别的支持(主要是 Node.js 或者浏览器)大量的库不能工作在微控制器上,但是依然有一部分库是可以的,并且非常有用。
由于语法的相似性,C 和 C++ 程序员可以很快速地学会 JavaScript,因此习惯嵌入式编程的开发者在阅读 JavaScript 代码库的时候只会遇到少量的麻烦。此外,回忆一下前面的简介,我们提到 JavaScript 被设计用于为通用自动化任务和快速重复任务编写小脚本。微控制器通常也用于这种场景中 —— 为自动化任务或数据上报任务编写小片段与硬件设备交互!
总结一下我们考虑使用 JavaScript 或者其它流行解释型语言为微控制器编程的主要原因:
上层,安全:没有 off-by-one 崩溃;更容易处理错误、复杂的数据类型和类型转换;更具有表线性。
快速迭代:可以从 RAM 中加载脚本,不需要每次测试时都烧写。可以远程更新。
大量的社区和库(大多数需要调整后才能运行到嵌入式平台上):bundler 和 minifier 可用于确保代码最小化。
C、C++ 以及 Java 相似的语法。
3
缺点
当然,选择 JavaScript 也有一些缺点。首先,JavaScript 在设计时就未考虑运行在小型、内存受限的设备上。因此,语言本身的构建器会消耗大量内存。大多数 JavaScript 解释器都至少需要 150KB 的 ROM 和 32KB 的 RAM。如果你的设备的资源比这还小,那还是继续使用 C/C++ 吧,或者使用为小型系统而设计的脚本语言,例如 Lua。
你可能还会发现,让 JavaScript 引擎保持在一个合理的尺寸会限制某些高级的语言特性,我们将在后续的例子中讨论它。
最后,即使使用了 JavaScript 或者其它解释语言,你也逃不掉 C 或者 C++。除非你使用的是一个你所挑选的 JavaScript 引擎所完全支持的平台,否则将平台与 JavaScript 集成在一起需要手工进行调节。
在下决定之前,需要仔细权衡优势和劣势。在这里,我们这样做是只为了好玩。那么,我们需要在微控制器上使用 JavaScript 吗?YES!看看它能带我们遨游到哪里吧!
4
选择
我们将简单看看在硬件方面和软件方面有哪些选择。对于硬件方面,我们主要看两个选择:近期非常流行的 ESP8266 以及 Particle Photon。这二者都相当强劲,且均支持 WiFi 连接。太酷了!而且它们还非常便宜:ESP8266 价格低至 7 美金(注:淘宝 18 元就能买到了!);Particle Photon 当前也只需要 19 美金。当然,如果你计划使用大量设备,价格甚至更低。
还有一些其它的选择,例如更新的 ESP32、32 位的 PIC、Espruino、使用 ARM CPU 的 ST 开发板,甚至包括 Arduino,例如基于 Intel Curie 的 Arduino 101。说明一下,大多数 Arduino 开发板都不够强大,无法运行任何 JavaScript 引擎,而