在【8. ESP8266使用Mongoose-os初体验(使用bh1750传感器)】一文中,介绍过在mongoose-os中利用javascript(mjs)进行bh1750数据读取的方法。脚本语言的灵活和非编译解释执行的特点表现的很鲜明,代码少,开发便利,这也是nodeMCU和各种脚本iot平台繁荣、大行其道的原因之一。
但是作为mongoose-os这样一个iot系统而言,mjs只是为提高效率、原型开发而加入的javascript的语法糖。其核心部分依然是C实现来主导,甚至mjs中的大量接口都是通过直接ffi映射C接口而来。当然,用mjs完全可以做开发(简单),但知其然也要知其所以然,复杂的应用、新设备的支持,用C来进行开发底层,或者功能接口映射到mjs是更自然的做法。这也就是官方所说【Advanced JavaScript】开发。
举例而言,mongoose-os对传感器的支持有限,官方的只有bme280等寥寥数种,对于不支持、不常见的传感器而言,用户必须自己进行驱动的开发。
本文以bh1750光度传感器为例(主要还是简单),简单介绍mongoose-os下,
开发环境的配置,I2C驱动的开发示例,以及mjs的导出和使用。
(bh1750的I2C接口很简单,只有用mjs也足以胜任,本文目的为介绍开发步骤和注意点)
mongoose-os虽说是opensource的,但利益驱动使然,其牢牢控制住了开发中的每一个步骤。
从空工程建立、依赖库下载,编译、固件刷入,都需要用其mos工具,除下载固件外,都必须在其云端完成。
环境在linux和windows上均可运行,编译、固件写入的入口均为官方的mos程序。
(本文的环境为win+git bash+mos.exe)
空工程创建
从github上下载工程模板,并重新命名为bh1750-app
git clone https://github.com/mongoose-os-apps/empty bh1750-app
文件列表如下:
./build
./fs ->文件系统(可视为mos中看到的device Files),后面做成的mjs文件将置于此目录
./fs/index.html
./LICENSE
./mos.yml ->配置文件
./README.md
./src
./src/main.c ->C实现代码
bh1750使用I2C接口,因此需要根据bh1750的datesheet,增加数据访问接口(即驱动实现)。
另外bh1750支持1次测量、连续测量 + L分辨率和2种H分辨率等多种组合模式,
这里简便起见,驱动部分只实现了 连续 H 分辨率模式,并直接实现于src/main.c。如果实现完整的、标准的驱动,应当与main.c分离实现。
修改src/main.c文件实现驱动
src/main.c
#include "mgos.h"
#include <stdint.h>
#include <stddef.h>
#include <mgos_i2c.h>
#include <mgos_system.h>
int32_t bh1750_init() {
// 获取i2c总线实例
struct mgos_i2c* i2c = mgos_i2c_get_global();
if (NULL == i2c) {
LOG(LL_INFO, ("Could not get i2c global instance"));
return -1;
}
// 通电
int8_t cmd = 0x01;
bool ok = mgos_i2c_write(i2c, 0x23, &cmd, 1, true); // dh1750默认地址为0x23(没有寄存器)
if( !ok ) {
return -2;
}
// 设置连续 H 分辨率模式
cmd = 0x10;
ok = mgos_i2c_write(i2c, 0x23, &cmd, 1, true); // dh1750默认地址为0x23(没有寄存器)
return ok ? 0 : -3;
}
int32_t bh1750_read() {
// 获取i2c总线实例
struct mgos_i2c* i2c = mgos_i2c_get_global();
if (NULL == i2c) {
LOG(LL_INFO, ("Could not get i2c global instance"));
return -1;
}
uint16_t data = 0;
uint16_t lux = 0;
// 连续 H 分辨率的数据读取
// bh1750默认地址为0x23,没有寄存器,直接从总线上读
bool ok = mgos_i2c_read(i2c, 0x23, &data, 2, true);
if( ok ) {
// 数据为2字节,收到的第1字节为高位,第2字节为低位
// 变换后按datesheet计算要求除1.2
((uint8_t*)(&lux))[0] = ((uint8_t*)(&data))[1];
((uint8_t*)(&lux))[1] = ((uint8_t*)(&data))[0];
lux /= 1.2;
}
return ok ? lux : -2;
}
// app默认初始化,如果希望没有bh1750不启动可在此检测
enum mgos_app_init_result mgos_app_init(void) {
LOG(LL_INFO, ("Hi there"));
return MGOS_APP_INIT_SUCCESS;
}
增加fs/api_bh1750.js文件,导出mjs接口
内容如下:声明ffi接口,并导出无状态的全局对象BH1750。
如果驱动支持多种模式并且能切换模式,就需要在持有状态数据。
fs/api_bh1750.js
let BH1750 = {
init : ffi('int bh1750_init()'),
lux : ffi('int bh1750_read()'),
};
增加初始脚本文件fs/init.js,在其中使用定时器打印传感器采集的流明数据
fs/init.js
load('api_config.js');
load('api_rpc.js');
load('api_timer.js');
load('api_bh1750.js'); // 加载bh1750驱动
let ret = BH1750.init(); // bh1750初始化
print('init result:', ret);
// 连续 H 分辨率模式要求120ms以上间隔
Timer.set(1000, true, function() {
let lux = BH1750.lux(); // 获取流明数据
print('lux:', lux);
}, null);
// 注册mongoose-os的RPC服务
RPC.addHandler('Lux.Read', function(args) {
return { value: BH1750.lux() };
});
修改mos.yml文件,其为标准的YAML格式,具体格式、注意点可搜索。
author: mongoose-os
description: A Mongoose OS app skeleton
version: 1.0
libs_version: ${mos.version}
modules_version: ${mos.version}
mongoose_os_version: ${mos.version}
# Optional. List of tags for online search.
tags:
- c
# List of files / directories with C sources. No slashes at the end of dir names.
sources:
- src
# List of dirs. Files from these dirs will be copied to the device filesystem
filesystem:
- fs
#驱动的配置信息可以加入config
# config_schema:
# - ["my_app", "o", {title: "My app custom settings"}]
#默认使用i2c总线
config_schema:
- ["i2c.enable", true]
libs:
- origin: https://github.com/mongoose-os-libs/boards
- origin: https://github.com/mongoose-os-libs/ca-bundle
- origin: https://github.com/mongoose-os-libs/rpc-service-config
- origin: https://github.com/mongoose-os-libs/rpc-service-fs
- origin: https://github.com/mongoose-os-libs/rpc-uart
- origin: https://github.com/mongoose-os-libs/i2c (增加i2c库依赖)
- origin: https://github.com/mongoose-os-libs/rpc-service-i2c (增加rpc依赖,不加也可,目的是用scan接口调试)
- origin: https://github.com/mongoose-os-libs/mjs(增加mjs依赖)
# Used by the mos tool to catch mos binaries incompatible with this file format
# 貌似日期实为框架版本,不可随意变更
manifest_version: 2017-09-29
在bh1750-app目录下执行编译命令行:
e:/mos.exe --verbose build --arch esp8266
建议打开-verbose选项,可看到详细的日志,基本流程如下:
优点显而易见:一键编译,依赖处理,云端可以统一维护、调整编译环境,
缺点也是鲜明:所有代码上传,毫无秘密可言;编译细节无法得知(甚至不知道具体编译器是什么);必须联网、速度很慢、访问繁忙时无法编译。
成功后日志的最后会有如下消息:
Success, built bh1750-app/esp8266 version 1.0 (20181007-145007).
Firmware saved to build/fw.zip
打包的Zip文件内容如下:
624K 10月 7 14:50 bh1750-app.bin
128 10月 7 14:50 esp_init_data_default_v08.bin
256K 10月 7 14:50 fs.bin
1.2K 10月 7 14:50 manifest.json
2.3K 10月 7 14:50 rboot.bin
其中,bh1750-app.bin为C语言的固件,fs.bin为存放mjs(包括依赖的底层mjs)的固件,manifest为刷入配置,其余为乐鑫官方提供的初始固件。
在bh1750-app目录下执行命令行如下(无须指定串口号,这点还没有调查是如何实现的):
e:/mos.exe --verbose flash
$ e:/mos --verbose flash
Loaded bh1750-app/esp8266 version 1.0 (20181007-145007)
Using port COM3
Opening COM3 @ 115200...
Connecting to ESP8266 ROM, attempt 1 of 10...
Connected, chip: ESP8266EX
Running flasher @ 0...
Flasher is running
Flash size: 4194304, params: 0x0240 (dio,32m,40m)
Deduping...
2320 @ 0x0 -> 0
262144 @ 0x8000 -> 65536
638560 @ 0x100000 -> 474720
128 @ 0x3fc000 -> 0
Writing...
4096 @ 0x7000
65536 @ 0x11000
77824 @ 0x101000
...
200704 @ 0x163000
16384 @ 0x195000
8192 @ 0x19a000
4096 @ 0x3fb000
Wrote 548448 bytes in 32.90 seconds (130.23 KBit/sec)
Verifying...
2320 @ 0x0
4096 @ 0x7000
262144 @ 0x8000
638560 @ 0x100000
4096 @ 0x3fb000
128 @ 0x3fc000
Booting firmware...
All done!
ESP8266是安信可的NodeMCU板(ESP12系列的话,接线相同)
BH1750(GY-302) | ESP8266(NodeMCU) |
---|---|
VCC | 3V3 |
GND | GND |
SCL | D5(Pin14) |
SDA | D6(Pin12) |
Mongoose-os上的I2C的SCL和SDA的管脚是可配置的。
因为ESP8266没有硬件I2C,其I2C是软件模拟的。