NodeMCU入门

NodeMcu环境搭建

  1. 写入固件,相当于安装系统,工具地址,同时需要一个固件,编译自己的固件,刷入固件时GPIO0引脚需要拉低,相反正常运行时需要拉高或浮动,USB连接可以通过声明DTR来拉低GPIO0,并通过声明RTS来重置你的电路板, 正常的开发板只需要重启后按住FLASH键即可,更新时勾选Erase flash清除所有数据
  2. 安装java环境
  3. 下载编译工具ESPlorer,用来编写程序,正常情况下DTR和RTS按钮为灰色
  4. api文档地址

ESPlorer使用说明

打开esplorer软件右侧上方选择端口,电机open按钮, 按下板子上RST按钮重启,窗口会显示具体版本信息,表示链接成功

左侧为操作区,右侧主要为信息返回区,显示串口返回的相关信息

 

  1. 点亮自带的发光二极管 http://blog.csdn.net/leytton/article/details/51650082
  2. 驱动pwm http://www.jianshu.com/p/8863316267a0
  3. gpio引脚 http://blog.csdn.net/leytton/article/details/51646624
gpio.mode(0,gpio.OUTPUT)  
gpio.write(0, gpio.LOW)

由于NodeMCU在启动时会执行”init.lua” 文件,一旦这个文件中存在漏洞,NodeMCU可能会不断地重启。幸运地是,Lua是异步执行的,在重启的同时,执行”file.remove(‘init.lua’)”命令即可删除”init.lua” 文件,下次启动时就不会执行这个文件了。修复漏洞后,再将”init.lua” 文件上传执行就行了。

引脚说明

有些参数选择引脚时需要使用pin参数,这个时候需要对照下对看pin对应的GPIO口是多少,比如设置PWM时pin 2参数对应GPIO4

使用注意

如果使用了TTL相关IO口输出会导致TTL传输数据显示异常, 需要留意,如IO7~IO10还有IO4,如果不需要显示数据则可忽略

打造属于自己的AI智能家居控制中心(通用改装方案)

前言

一直想将家里的一些设备打造成懒人专用的智能控制方式,所以工作之余经常留意智能家居和AI的发展,发现最近国内掀起一股智能音箱的热潮,主要还是起源于亚马逊发布ECHO,据报道ECHO的平均使用率还是挺高的,但是好像大部分都还是听歌和设置闹钟….. 我更加关注的还是智能音箱的远场语音识别技术,通过语音交互控制各种设备, 既然有这么多用户,应该做的很不错了,ECHO在国内水土不服,所以开始寻找国内比较好的产品ing

选择智能音箱

发现目前国内主流的产品主要有:

  1. 叮咚音响
  2. 小米音箱
  3. 天猫精灵
  4. 喜马拉雅“小雅”音箱

对比各类评测后选择了叮咚音箱,主要是当时加了小米和叮咚的开发群, 发现只有叮咚的开放平台是已经可以申请,并且有一些开发者,另外一个原因是叮咚音箱是和讯飞合作推出的,有讯飞背书应该不会太烂吧…..花了399购入叮咚TOP:

开工

首先定一个开发方案,画出通信控制流程图

根据流程制定工作清单:

  1. 选择合适的控制模块,继电器,供电电源,控制系统一般都是低压供电
  2. ESP8266刷入NodeMCU编写控制程序
  3. 服务器环境搭建,安装PHP Swoole服务,开启HTTPS和WebSocket通信
  4. 制作简单的控制中心页面(HTTP Page),用于调试和手机控制
  5. 接入智能音箱完成ABNF语法编写
  6. 实现离线终端配置(这个是后来加的,防止WIFI密码变更需要再修改程序)
  7. 实现简单的安全访问控制
  8. 测试安装

一.选择硬件控制模块

首先想到的是由ESP8266的GPIO口直接控制继电器,然后控制客厅灯的开关,延迟开关,亮度调节等,那么需要的零件如下,一个电源模块(3.3V)和一个继电器模块,加起来不到20元的价格

但是这两个模块体积都比较大(相对开关盒子而言),集成度不高,先做为备选,再继续寻找集成度更高的方案,然后想到了很久之前花49买的小米的智能插座,话说买了后就没用过了,直接拆开分析后发现可以改造,自带5V供电和继电器, 就选这个了!如果空间稍微大点可以选择前面的方案

拆开后的小米智能插座

旁边的小板就是他的WiFi无线控制板,我们要做的就是替换掉他,通过分析发现电路板上有几个测试点标识有 RELAY EN, USB EN,WiFi控制板上还有个3.3V测试点,RELAY EN就是继电器使能, 只需要给他高电平或者低电平就能控制继电器的开合了,然后ESP8266正好可以从3.3V取电.

二.ESP8266刷入NodeMCU固件,编写客户端程序

刷入固件可以参考这个链接.写的比较详细

NodeMCU固件比AT指令更加简单,并且有很多现成的功能模块可以使用,尽量用最少的代码干更多的事,NodeMCU基于lua语言,语法有点类似Node.js.

通过查找文档发现NodeMCU对MQTT协议和WebSocket还有基本的Socket都实现了简单的支持,但是Swoole对WebSocket支持更加全面,所以先用Socket尝试了下简单的控制,发现还OK, 考虑到需要一个控制页面,最终选择了WebSocket,因为Js可以直接通信,最终完成的lua代码如下:

wifi.setmode(wifi.STATIONAP)
wifi.ap.config({ssid="MyPersonalSSID", auth=wifi.OPEN})
enduser_setup.manual(true)
print("ap ip:"..wifi.ap.getip())
print("ap mac:"..wifi.ap.getmac())
print("sta mac:"..wifi.sta.getmac())

device_id=0
pin=3  -- 尽量避开TTL口
gpio.mode(pin,gpio.OUTPUT);

function explode(sep, str)
    t={}
    for w in string.gmatch(str,"([^'"..sep.."']+)") do     -- 分割字符串
        table.insert(t,w)
    end
    return t
end

enduser_setup.start(
  function()
    print("sta ip:" .. wifi.sta.getip())
    wifi.setmode(wifi.STATION)

    ws = websocket.createClient()
    ws:on("connection", function(ws)
        ws:send("device_id:"..device_id)  --send device id
        print('got ws connection')
        tmr.alarm(1, 5000, 1, function()
             ws:send(device_id.."-life...")  -- 发送心跳包
        end)
    end)
    ws:on("receive", function(_, msg, opcode)  --接收消息
      if string.len(msg)>5 then
        data=explode(':',msg);
        gpio.write(pin,tonumber(data[2]));
      end
      print('got message:', msg) -- opcode is 1 for text message, 2 for binary
    end)
    ws:on("close", function(_, status)   --关闭连接
      print('connection closed', status)
      --ws = nil -- required to lua gc the websocket client
      tmr.alarm(1, 5000, 0, function()
        ws:connect('wss://xxx.cn')
      end)
    end)
    ws:connect('wss://xxx.cn')

  end,
  function(err, str)
    print("enduser_setup: Err #" .. err .. ": " .. str)
  end
)

实现了断网自动重连,测试发现稳定性还不错,不用担心断网后需要去重启等问题

刚开始不知道有cjson模块,所以用分割字符串的方式来传输数据体,因为是简单的数据交互,所以就懒得改了,还节省空间…

三.安装PHP Swoole,搭建HTTPS和WebSocket服务

因为HTTPS加密需要使用到openssl,所以只能编译安装Swoole,编译流程如下

#先安装openssl
yum install openssl openssl-devel
#下载源码
wget http://pecl.php.net/get/swoole-1.9.21.tgz
tar zxvf swoole-1.9.21.tgz
#需要开启openssl以支持https
./configure --enable-openssl
make && make install

Swoole使用过程中遇到一些问题,首先是测试发现HTTPS页面浏览器可以正常访问,但是叮咚的服务器无法正常请求,提示

WARN swSSL_accept: SSL_do_handshake() failed.

刚开始以为是openssl版本的问题,编译最新版本后问题依旧,通过查看源码没有发现什么特别的地方,猜测是加密方式不对导致,通过一番搜索找到了修改加密方式的配置,问题解决!

'ssl_ciphers' => 'ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP',

另外一个问题就是HTTP服务和WebSocket服务底层都是继承过来的,虽然同时启动的时候需要分别设置端口,但是启用ssl加密后,都只能通过443一个端口连接,另外一个端口是无效的,控制端核心代码如下:

//websocket请求  当服务器收到来自客户端的数据帧时会回调此函数。
$this->server->on('message', function (swoole_websocket_server $server, $frame) {
    echo "device_data: {$frame->data}\n";
    $data = explode(':', $frame->data);
    if ($data[0] == 'device_id') {
        $this->device_list[$data[1]] = ['fd' => $frame->fd, 'status' => 0];
        echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
        //发送数据到前端
        if ($this->web_fd) {
            $server->push($this->web_fd, json_encode($this->device_list, true));
        }
    }
    if ($frame->data == 'WebPage') {
        $this->web_fd = $frame->fd;
    }

});

//在收到一个完整的Http请求后,会回调此函数。回调函数共有2个参数:
$this->server->on('request', function ($request, $response) {
    $response->header('Content-Type', 'text/html;charset=utf-8');
    $uri = $request->server['request_uri'];
    echo "请求地址: {$uri}\n";
    $url_exp = explode('/', $uri);
    if ($url_exp[1]) {
        $fun = $url_exp[1] . 'Action';
        if (method_exists($this, $fun)) {
            $this->$fun($request, $response);
        } else {
            $response->status(404);
            $response->end("<h1>404</h1>");
        }
    } else {
        //访问的首页
        $response->end("<h1>Hello Swoole. #" . rand(1000, 9999) . " Home Page</h1>");
    }
});

四.添加HTTP控制页面

HTTP请求实现了一个最简单的路由功能

$url_exp = explode('/', $uri);
   if ($url_exp[1]) {
       $fun = $url_exp[1] . 'Action';
       if (method_exists($this, $fun)) {
           $this->$fun($request, $response);
       } else {
           $response->status(404);
           $response->end("<h1>404</h1>");
       }
   }

然后添加HTTP控制页面,控制页面通过WebSocket实现了设备在线和离线时动态显示,核心代码:

<div id="app" class="container" style="padding: 30px 0px;">
    <?php // var_dump($this->device_list);?>
    <div v-if="!list.length">
        <h2>没有设备连接!</h2>
    </div>
    <button type="button" class="btn btn-success" style="margin: 5px;" v-for="(value,key) in list"
            v-on:click="change(value)">
        设备号:{{value}},status:<span v-html="key.status"></span> <span
                v-bind:class="parseInt(key.status)?'glyphicon glyphicon-flash':''"></span></button>
</div>
<div id="output"></div>
</body>
<script>

    vm = new Vue({
        el: '#app',
        data: {'list':<?php echo json_encode($this->device_list, true)?>},
        methods: {
            change: function (value) {
                vm.list[value].status = vm.list[value].status == 1 ? 0 : 1
                $.get('/change', {'device': value, 'status': vm.list[0].status}, function (res) {
                    //alert(res)
                })
            }
        }
    })

使用了Vue.js,有设备在线或离线时通过WebSocket发送数据并替换掉list的值即可,很简单.

完整的页面如下,可以通过手机随时访问,有设备掉线时按钮消失,上线立刻显示.

五.接入智能音箱完成ABNF语法编写

购买音箱的同时申请了开放平台账号,由于叮咚是和讯飞合作, 所以语法这块用的ABNF语法,使用起来有点像正则表达式…..主要用到的几个语法如下

$_ti_ch_ 任意匹配
|  或者匹配
[] 可选匹配
() 分组匹配,起到明确界限作用
<> 重复  +(N次以上) -(N次以下)

完成后的语法类似下面的:

#ABNF 1.0 UTF-8;
business my_house;
#include "pre_word.lst"
root main;
#ABNF HEAD-END;
//业务唤醒前缀词
$pre_word = ($u_LST_pre_word);
//称谓
$call_name_must = 小(杰|姐|结|节);
//设备名称  可以使用通配符
$device_name{device_name} = $_ti_ch_<1-4>;
$switch{switch}=(开|关);
$switch_on=([打] $switch [启])|($switch [闭] [掉]);
//让小杰帮我打开电视机
$call_content = [$pre_word] (让|请) $call_name_must [帮我];
$content= [$call_content ] $switch_on $device_name;
$main{biz:my_house} = $_ti_ch_<8->$content$_ti_ch_<8->;

可以识别的语句能够匹配到很多,比如

让小杰帮我打开客厅灯
让小杰打开客厅灯
让小杰开灯
请小杰开灯
...

六,实现离线终端配置

想到wifi密码变更需要拆开重新改程序等简直不能忍,于是寻找离线配置方案,发现已经有一个现成的模块”enduser_setupstop”,可以实现没有网络时开启AP模式,直用手机连上去配置,页面也可以自定义,效果如下,完美!

七,访问控制

考虑安全问题,在路由前面加了一层简单的HTTP Basic认证, 因为是走HTTPS,所以稍微强一丁点,只要地址不暴露应该还算安全吧,哈哈,代码如下

//http基本认证
    public function authorization($request, $response)
    {
        if (!isset($request->header['authorization'])) {
            $response->header('WWW-Authenticate', 'Basic realm="My Realm"');
            $response->status(401);
            $response->end();
        } else {
            if (str_replace('Basic ', '', $request->header['authorization']) !== base64_encode("{$this->user}:{$this->pwd}")) {
                $response->end("<h1>401 unauthorized</h1>");
            }
        }
    }

 

八,测试安装

从小米的小板接3.3V和GND供电

ESP8266编写程序测试

组装完毕

先连接到台灯上测试

下一步接到客厅灯 ing, 发现一些传统布线方式导致的问题,和解决方案,然后也了解了下未来智能家居会使用到的几种通信方式,比如星形,环形,现场总线等,改完后再陆续更新上来

最后

  • 总价格,一个遗忘的小米插座+ESP8266不管用哪种方案,基本都是50元内+智能音箱399
  • 总用时,几个没有吃鸡的晚上,
  • 其他照片 :

临时做的超好用的供电电源,电压可调

临时工作台

其他测试视频:

end

转载请联系原作者vip@wwjie.cn,保留所有权利