这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 板卡试用 » erabbit《智能照明管理》开发手记

共3条 1/1 1 跳转至

erabbit《智能照明管理》开发手记

菜鸟
2016-10-06 17:24:59     打赏

一 智能照明管理系统概述 背景概述

深圳市酷图软件开发有限公司成立于2013年,专注设备智能控制与远程管理解决方案,面向照明、安防、家电等领域,为各类电子产品开发无线连网模块、手机APP及后台管理功能。
基于过往的项目经验、客户需求和行业趋势,我们规划了一套智能照明管理系统,目标是将家庭、写字楼、酒店等各类区域的照明系统连接到云端,实现集中管理、自动控制,以达到提升照明效果、提高管理效率、降低能源消耗的目标。

系统架构

  • 首先我们需要一个网关,通过有线或无线方式与灯具连接起来,然后将网关连接到云平台。
  • 我们在云平台上实现设备连接、消息传递,数据处理与存储。
  • 我们针对不同的应用场景开发手机APP、PC客户端与Web页面,实现对系统的控制和管理。
    • PC客户端——用于较大规模的系统,安装在监控中心的PC上,由管理人员对系统进行监控。
    • 手机客户端——用于家庭;用于酒店、写字楼等环境,由用户对自己所在区域的照明进行控制。
    • Web页面——通过浏览器对照明系统进行监控(替代PC客户端),查看报表,管理登录用户等。
网关

网关硬件采用Dragonboard 410c开发板。此开发板集成了四核ARM® Cortex® A53处理器,单核频率可达1.2GHz,通信方面支持WiFi、蓝牙和GPS,并提供了多种扩展接口连接外设。这保障了网关可以应对规模较大的照明系统,并提供多样化的控制方式。
网关软件采用专为物联网应用设计的Ubuntu Core操作系统。此系统最大的特点是采用Snappy包的方式管理应用,应用程序和其依赖的运行库打包在一起,便于快速布署和更新;此外,应用独立运行于各自的沙箱环境中,可以有效地实现数据隔离,保障系统的安全性。

云平台

云平台选择AWS。AWS的IoT服务采用MQTT协议实现与设备的安全连接和消息传递,并在此基础上提供了设备影子和规则引擎——设备影子用于保存和同步设备状态,规则引擎用于与文件存储、数据库、推送通知、Lambda函数等其他云服务的整合。这令我们可以专注照明的业务逻辑,实现快速开发。

开发技术

系统各部分的程序代码统一采用JavaScript语言编写,JavaScript无需编译,同时拥有丰富的第三方库(npm),可以有效提升开发效率。同时JavaScript的事件驱动特性非常适合于物联网应用。
本项目开放源代码,代码托管于开源中国码云平台:http://git.oschina.net/erabbit/OpenIoT


二 在Dragonboard 410c上安装Ubuntu Core

材料清单

我们将要下载Ubuntu Core的镜像文件,烧写到SD卡上,然后放到Dragonboard上启动并进行初始化设置。除Dragonboard外,还需要准备以下物品:

  • 电脑(用于烧写SD卡,Windows/Linux/Mac都可以,本文以Macbook为例)
  • Micro SD卡和读卡器(MicroSD卡没有特殊要求,我们使用的是SanDisk 4GB,Class4型号的)
  • USB接口的键盘
  • HDMI视频线及显示器
  • 可访问Internet的WiFi连接 注册Ubuntu One账号 最新的Ubuntu Core操作系统默认已不再提供本地登录账号,需要到Ubuntu网站上注册一个Ubuntu One账号(访问各种Ubuntu服务的统一账号)并上传公钥文件。待系统安装好以后,使用私钥文件以SSH方式登录。公钥和私钥对可以在电脑上用ssh-keygen命令生成。  下载镜像文件 到这里下载最新的Ubuntu Core 16镜像: http://releases.ubuntu.com/ubuntu-core/16/
    其中包含了适用于Raspberry Pi等多种硬件的文件,我们选择ubuntu-core-16-dragonboard-410c.img.xz
    下载完成以后检查一下文件的MD5:
    $ md5 ~/Downloads/ubuntu-core-16-dragonboard-410c.img.xz 
    MD5 (/Users/Tom/Downloads/ubuntu-core-16-dragonboard-410c.img.xz) = 2aa8f5b404826818e2de63e947b0bae7
    将以上的值与文件http://releases.ubuntu.com/ubuntu-core/16/MD5SUMS中的值对比一下,确认一致,以免网络问题导致下载的文件损坏。 烧写SD卡
  • 将Micro SD卡放进读卡器,插到电脑USB口上,然后使用diskUtil命令查看读卡器对应的设备,此处是/dev/disk2:

    $ diskutil list
     /dev/disk0 (internal, physical):
        #: TYPE NAME SIZE IDENTIFIER
        0: GUID_partition_scheme *251.0 GB disk0
        1: EFI EFI 209.7 MB disk0s1
        2: Apple_CoreStorage Macintosh HD 250.1 GB disk0s2
        3: Apple_Boot Recovery HD 650.0 MB disk0s3
    
     /dev/disk1 (internal, virtual):
        #: TYPE NAME SIZE IDENTIFIER
        0: Macintosh HD +249.8 GB disk1
                                      Logical Volume on disk0s2
                                      36C243A9-C568-4875-B987-5026D4C20FE7
                                      Unencrypted
    
     /dev/disk2 (external, physical):
        #: TYPE NAME SIZE IDENTIFIER
        0: FDisk_partition_scheme *4.0 GB disk2
        1: DOS_FAT_32 NO NAME 4.0 GB disk2s1
  • 卸载磁盘
    $ diskutil unmountDisk /dev/disk2
     Unmount of all volumes on disk2 was successful
  • 烧写镜像

    $ xzcat ~/Downloads/ubuntu-core-16-dragonboard-410c.img.xz | sudo dd of=/dev/disk2 bs=32m
     0+10366 records in
     0+10366 records out
     679297024 bytes transferred in 153.468353 secs (4426300 bytes/sec)
    • 注意32后面的单位是小写的m
    • 烧录过程中命令行会卡住几分钟,不显示进度信息

      完成后再查看一下磁盘列表:

      $ diskutil list
      /dev/disk0 (internal, physical):
       #: TYPE NAME SIZE IDENTIFIER
       0: GUID_partition_scheme *251.0 GB disk0
       1: EFI EFI 209.7 MB disk0s1
       2: Apple_CoreStorage Macintosh HD 250.1 GB disk0s2
       3: Apple_Boot Recovery HD 650.0 MB disk0s3
      
      /dev/disk1 (internal, virtual):
       #: TYPE NAME SIZE IDENTIFIER
       0: Macintosh HD +249.8 GB disk1
                                     Logical Volume on disk0s2
                                     36C243A9-C568-4875-B987-5026D4C20FE7
                                     Unencrypted
      
      /dev/disk2 (external, physical):
       #: TYPE NAME SIZE IDENTIFIER
       0: GUID_partition_scheme *4.0 GB disk2
       1: DEA0BA2C-CBDD-4805-B4F9-F428251C3E98 1.0 MB disk2s1
       2: 098DF793-D712-413D-9D4E-89D711772228 1.0 MB disk2s2
       3: A053AA7F-40B8-4B1C-BA08-2F68AC71A4F4 1.0 MB disk2s3
       4: E1A6A689-0C8D-4CC6-B4E8-55A4320FBD8A 1.0 MB disk2s4
       5: 303E6AC3-AF15-4C54-9E9B-D9A8FBECF401 1.0 MB disk2s5
       6: 400FFDCD-22E0-47E7-9A23-F16ED9382388 2.1 MB disk2s6
       7: 20117F86-E985-4357-B9EE-374BC1D8487D 1.0 MB disk2s7
       8: Microsoft Basic Data system-boot 134.2 MB disk2s8
       9: Linux Filesystem 535.6 MB disk2s9
  • 最后,再次将磁盘从电脑上卸载,取下读卡器,拿出SD卡。 启动和初始化
  • 将烧写好的SD卡装入Drgonboard 410c的卡槽,将板背面的拨码开关S6拨到0110(第2、3位为ON,其他位OFF),连接键盘、显示器,然后接通电源。
  • 按照提示输入WiFi的SSID和密码,以连接网络。
  • 输入注册Ubuntu One账号的Email地址,完成初始化,记录下屏幕上显示的IP地址。
    SSH登录 在电脑上确认能够ping通Dragonboard的IP地址,然后使用SSH命令登录,用-i参数指定私钥文件。


三 连接AWS IoT服务

管理控制台

要使用AWS的IoT服务连接和管理设备,需要先登录AWS管理控制台,创建设备对象、生成证书和设置访问策略。其中证书需要下载到本地,放到设备中,连接时需要。

设备连接

AWS IoT服务使用MQTT协议连接设备与云端,MQTT基于发布/订阅模型,是目前IoT领域设备与云端通信的主流消息协议。
AWS已经将MQTT客户端封装进了设备SDK当中,在设备侧引入aws-iot-device-sdk,配置好证书、设备ID及IoT服务所在的区域即可实现连接。
sdk提供了多种开发语言的版本,我们使用的是JavaScript:

var awsIot = require('aws-iot-device-sdk');

var thingShadows = awsIot.thingShadow({
   keyPath: path + '/certs/private.pem.key',
  certPath: path + '/certs/certificate.pem.crt',
    caPath: path + '/certs/root-CA.crt',
  clientId: gateway.id,
    region: 'ap-northeast-1',
});

var thingShadowConnected = false;
var clientTokenUpdate;

thingShadows.on('connect', function() {
    thingShadows.register(gateway.id);
    thingShadowConnected = true;
    setTimeout(gateway.reportState, 5000);
});
设备影子

AWS IoT在MQTT的基础上定义了“设备影子”服务,用于管理设备状态。“设备影子”的内容实际上就是一个JSON文档,其具体字段由开发者自己定义,系统跟踪字段值的变化。在本应用中,灯的开关状态就是通过设备影子管理的,主要流程如下:

  1. 当网关检测到灯状态变化后,即发布report消息;
  2. 云端收到report消息,即更新设备影子中灯的当前状态;
  3. 用户通过Web页面设置灯的状态,发布desired消息;
  4. 云端收到desired消息,通过对比其与当前状态的差别,发布delta消息;
  5. 网关收到delta消息,根据消息内容设置灯的状态。 ``` ... reportState: function() {
    var curState = new Object();
     for(var light of gateway.lights) {
         curState[light.id] = {
             power: light.power
         };
     }
     var state = {
         "state": {
             "reported": curState
         }
     };
     clientTokenUpdate = thingShadows.update(gateway.id, state);
     if (clientTokenUpdate)
         console.log('updated shadow: ' + JSON.stringify(state));
     else
         console.log('update shadow failed, operation still in progress');
    } ...

thingShadows.on('delta', function(thingName, stateObject) { console.log(received delta on ${thingName}: ${JSON.stringify(stateObject)}); var deltaState = stateObject.state; for(var lightId in deltaState) { var lightState = deltaState[lightId]; lightState.id = lightId; gateway.onCommand(commands.light.power, lightState); } }); ```

规则引擎

AWS云上有丰富的计算、存储、网络资源,IoT服务通过规则引擎与其他各项服务建立联系。
我们在规则引擎当中,以类似SQL的语法定义规则,筛选设备消息。当符合指定条件的消息到达后,系统就就会将其插入数据库、发送通知或触发Lambda函数的执行。


四 打包和布署Snap应用

在开发板上测试

网关程序使用JavaScript开发,由Node.js解释和运行,前期是在PC上开发的,打包之前需要先移植到开发板上。
由于JavaScript/Node.js本身是跨平台的,所以网关代码基本不需要修改,在开发板上安装好Node.js环境即可运行。
Ubuntu Core作为一个“Snappy Only”的系统,并不能像其他系统一样随意安装程序,而是需要先安装一个名为“classic”的snap,然后在这个snap提供的环境中安装Node.js。

$ sudo snap install classic
$ sudo classic
(classic)$ sudo apt-get install nodejs
打包

当在classic环境中测试没有问题后,就需要使用Snapcraft工具将网关程序和Node.js环境打成Snap包,关键是编写snapcraft.yaml文件:

name: lighting-gw
version: "0.1.0"
summary: Connect local lighing devices to cloud
description: A smartlighting gateway connected to AWS IoT
confinement: devmode

apps:
    lighting-gw:
        command: bin/lighting-gw
        plugs: [serial-port, network, home]

parts:
    lighting-gw:
        plugin: nodejs
        source: .
        node-engine: 6.5.0

此文件定义了应用代码和可执行程序的路径,所需的系统资源(串口、网络),以及Node.js的版本。编写好以后,执行snapcraft命令即可生成“.snap”文件。
每个“.snap”中都可以包含自己的Node.js/Python/JRE版本,相互之间不影响。

安装

安装Snap应用前需要先退出classic模式,然后使用snap命令安装:

sudo snap install --devmode --force-dangerous lighting-gw_0.1.0_arm64_api.snap

也可以将“.snap”文件复制到其他运行Ubuntu Core的Dragonboard上,然后使用以上命令进行安装,在目标板上并不需要安装classic和Node.js,因为snap包中已经包含了其运行所需的一切。

文件访问

Snap运行时的文件系统是只读的,因此安装目录下的文件无法修改,应用的数据要放在专门的数据路径下,此路径可以在Snap环境中使用“SNAP_DATA”环境变量获取。

var path = process.env.SNAP_DATA;


五 使用手机APP进行本地控制

 HTTP Restful API

为了在不连接云平台的情况下仍能在局域网中控制设备,网关上集成了HTTP服务,提供API接口供手机APP调用。
HTTP API采用express框架:

var app = express();
var router = express.Router();

router.route('/lights')
    .get(function(req, res) {
        var devices = new Array();
        for(var light of host.lights) {
            var device = new Object();
            device.id = light.id;
            device.uid = light.uid;
            device.power = light.power
            devices.push(device);
        }
        res.status(200).json(devices);
      });

app.use('/api', router);
Android APP
public class MainActivity extends Activity {
...
    @Override
    public void onLightPowerChanged(boolean isOn) {
        String curLights = curLights();
        if(curLights != null)
            Light.setLightPower(curLights, isOn ? Light.LIGHT_POWER_ON : Light.LIGHT_POWER_OFF, apiHandler);
    }
class Light {
...
    static String serverIp = "192.168.0.100";

    static String getUrl(String suffix) {
        return "http://" + serverIp + ":9000/api" + suffix;
    }

    public static void setLightPower(String lightId, int power, Handler handler) {
        String url = getUrl("/command/light/power");
        HttpRequestThread thread = new HttpRequestThread(url, handler, MSG_LIGHT_POWER);
        thread.addParam("id", lightId);
        thread.addParam("operation", (power == LIGHT_POWER_ON) ? "on" : "off");
        thread.start();
    }





关键词: IoT     照明     Dragonboard 410c    

专家
2016-10-09 10:02:21     打赏
2楼

楼主板子已经到了? 建议使用 react native进行跨平台开发。


专家
2016-10-18 13:56:44     打赏
3楼
看起来好高大上的样子

共3条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]