DIY一个具有远程控制功能的智能家居原型系统-Arduino中文社区 - Powered by Discuz!

Arduino中文社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 9594|回复: 7

DIY一个具有远程控制功能的智能家居原型系统

[复制链接]
发表于 2016-9-16 10:54 | 显示全部楼层 |阅读模式
本帖最后由 gongqingkui 于 2016-9-16 10:58 编辑

smarthome 0.1是一个智能家居系统的雏形。主要实现局域网、因特网远程控制家里的电器;下一步要实现根据传感器数据控制家电功能;远景要实现根据人工智能自己控制电器功能;现在初步实现第一个功能。长得大概是这个样子。
系统框图如下。
forum.php?mod=image&aid=23085&size=300x300&key=2f17df961d093351&nocache=yes&type=fixnone.gif
远程控制主要实现方式:局域网或者手机登录架设在cubieboard2上的网页界面,点击开关、输入数字、输入文字提交之后,系统将数据交给后台python程序;由python程序提交给串口通信。串口数据发送到连接在cubieboard2上的arduino模块。arduino模块解析串口数据,控制相应的LED,伺服器和LCD显示器。
forum.php?mod=image&aid=23086&size=300x300&key=a72c26a956f86921&nocache=yes&type=fixnone.gif
系统运行视频在这里。http://v.youku.com/v_show/id_XMTcyNjk1MjY3Mg==.html
cubieboard2未出镜,他是系统主核心,完成web服务器架设,与arduino串口通信的功能,同时肩负arduino模块供电。
程序代码:https://github.com/gongqingkui/smarthome
启动服务器pythonweb_manual.py 1234
访问相应服务器http://ip:1234就可以看到控制界面如图。

forum.php?mod=image&aid=23087&size=300x300&key=39d8b37addc00920&nocache=yes&type=fixnone.gif
提交相应动作后,挂在Arduino上的各种设备会做出反应。
以后的文章会逐步复述这个小系统是怎么搭建起来的。

frame.JPG
614469364146100279.jpg
web.PNG
 楼主| 发表于 2016-9-16 11:02 | 显示全部楼层
本帖最后由 gongqingkui 于 2016-9-22 23:09 编辑

这个系统可以识别三种指令:二值型、整数型和字符串型。
LED开关、闪烁显然是二值型的指令控制的。命令首字母为b,为b0或者b1,标示二值型的关和开。这里以控制Arduino板子上的LED为例:只用一句话digitalWrite(13, HIGH);或者digitalWrite(13,LOW);。这个LED的引脚接在13脚上,所以控制它高低电平就可以实现闪烁或者亮灭的功能。同理,如果不接Arduino板上的LED而另外接诸如继电器之类的元件,就可以实现控制外围高压电路的功能。
数值型指令发送一个整数,命令首字母为d,为dIntNUM。这里用伺服器作为实例。伺服器根据收到的0-180的数据进行角度调整。语句也很简单。引入并且实例化Servo:
#include <Servo.h>
Servo servo1;
在setup()函数中servo1.attach(10);设定伺服器1的数据引脚接到10脚上。
在程序loop中进行接收和处理指令,之后发送角度到伺服器1:servo1.write(ans);
字符型的指令发送一条字符串给Arduino主机,命令首字母为s,为sString。这里用1602液晶屏做例子,把收到的字符串显示到液晶屏上。语句基本是:
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
初始化setup时:lcd.begin(16,2);在随便输出点什么看看:lcd.print("SmartHome");。接收到相关字符串后,先设定显示位置lcd.setCursor(0,0);然后输出数据lcd.print(data + 1);
以上,实现了接收指令后解析,发送到相应的设备或者控制外围硬件做出反应的功能。下面说明如何给Arduino发送指令。
 楼主| 发表于 2016-9-16 11:03 | 显示全部楼层
本帖最后由 gongqingkui 于 2016-10-18 10:39 编辑

串口
这个小系统Arduino的串口接收消息并解析、控制硬件。接收的三种消息分别用不同字母开头,Arduino判断首字母并解析出相应数据,发送控制命令给硬件。
在setup中打开和初始化串口。  Serial.begin(9600);
  Serial.flush();
在loop中接收串口发来的消息,存进buffer中,并调用splitString函数处理数据,再调用setLED完成对硬件发送控制指令的操作。
在测试时,可以使用Arduino自带的串口监视器,自己手动发送以b、d、s开头的测试信号。下面再连到Cubieboard上去。
cubieboard串口链接
在这个小系统的设计中,Arduino负责硬件控制,接受解析指令。而另外一台Cubieboard2负责构建IOT服务器。这个服务器现在主要完成web接口和发送指令给Arduino的功能,未来还要完成自动控制和智能控制的功能。之所以不直接用Arduino做中心,也是考虑为未来自动和智能控制的留空间。
首先Cubieboard最简单先要和Arduino用串口连起来。插上USB转串口线之后,进到/dev/中ls下,可以看到以ttyUSB开头的设备,例如ttyUSB0,就表示串口线的名字。然后把TX和RX脚连到Arduino的0和1脚上。
用minicom软件测试下连上Arduino没有。首先下载安装sudo apt-get install minicom然后再配置下sudo minicom –s主要是把串口设置中的名字修改为USB串口线名字,关闭硬件流控制功能。然后就可以打开minicom测试了。直接输入相应的控制指令并且回车,就会将指令发送到Arduino中,如果有错的话,可以试下把串口线rx和tx对调。
 楼主| 发表于 2016-9-16 11:04 | 显示全部楼层
本帖最后由 gongqingkui 于 2016-10-18 10:41 编辑

python串口
Minicom串口测试正常之后,就可以写Python程序通过串口发送指令了。写好串口收发程序也是为了下一步web封装和与在线物联网服务连接。
发送程序serialPortModule.py比较简单:
[mw_shl_code=python,true]import serial
defserialSend(str):
    t = serial.Serial('/dev/ttyUSB1',9600)
    with t:
        n = t.write(str)
    t.close()
    return n
[/mw_shl_code]
引入serial包后,新建一个发送函数serialSend,以待发送指令作为参数。语句很简单,返回发送的指令长度。这里唯一需要注意的是ttyUSB的编号,要在/dev/里查询得到。那么接收的程序serialRead.py是这样的:
[mw_shl_code=python,true]import serial
defserialRead():
    t = serial.Serial('/dev/ttyUSB1',9600)
    print t.isOpen()
with t:
        while(1):
            s = t.readline()
            print s
#todo save to DB or web
if __name__ =='__main__':
    serialRead()
[/mw_shl_code]
程序自动接收串口发送的字符串指令,解析后可以完成转发到在线服务器或者存储到数据库的操作。
 楼主| 发表于 2016-9-16 11:05 | 显示全部楼层
本帖最后由 gongqingkui 于 2016-10-18 10:42 编辑

web封装
编写好串口发送函数后,可在命令行下进行简单测试。测试无误后,可以编写web程序,在web程序中,根据用户点击,来调用串口发送函数,进行指令的传递。Web服务器这里用的是python的web库。代码如下:
web_manual.py程序导入相应的包后,首先用render指明模板所在目录templates/,然后指定URL访问列表情况,最后根据URL中制定名字,编写相应的控制器。控制器中的GET和POST分别指代两种访问方法,用render.具体模板名来传值到相应模板。Db类和sqldb类用来访问本地sqlite3数据库。经过以上程序,在服务器中打开python web.py 1234,然后访问localhost:1234既可以访问到这个简单的网站,实现局域网内查看和控制设备的能力。
[mw_shl_code=python,true]# encoding: utf-8
import web
import os
import sqlite3
import time
import serial
from actuatorModule import execute
from web import form
# templete floder
render = web.template.render('templates/')

# url tables
urls = ('/', 'index',
        '/manualcontroller', 'manualcontroller',
        '/temperature', 'temperature',
        '/humidity', 'humidity'
        )


class index:
    def GET(self):
        return render.index()


class temperature:
    def GET(self):
        db1 = db()
        rs = db1.execute('select * from temperature order by id desc limit 20')
        return render.temperature(rs)


class humidity:
    def GET(self):
        db1 = db()
        rs = db1.execute('select * from humidity order by id desc limit 20')
        return render.humidity(rs)


class manualcontroller:

    def GET(self):
        return web.seeother('/')

    def POST(self):
        i = web.input()
        print i.switcher1, i.servor1, i.led1
        if i.servor1 != "":
            if int(i.servor1)>=0 and int(i.servor1) <=180:
                execute("d",i.servor1)
        elif i.led1 != "":   
            if i.led1 !="":
                execute("s",i.led1)
        elif i.switcher1 != "":
            if i.switcher1 == "1" or i.switcher1 == "0" :
                execute("b",i.switcher1)
        return web.seeother('/')

class db:
    def __init__(self):
        self.db = sqldb()
    def insert(self,table,values):
        return
    def delete(self,table,index):
        return
    def update(self,table,index,values):
        return
    def select(self,table,index):
        i = (table,index)
        sql = 'select * from %s where id = %d'%(table,index)
        self.db.cu.execute(sql)
        rs = self.db.cu.fetchall()
        self.db.conn.commit()
        return rs
    def execute(self,sql):
        self.db.cu.execute(sql)
        rs = self.db.cu.fetchall()
        self.db.conn.commit()
        return rs
   
   
class sqldb:

    def __init__(self):
        self.db = 'db.db'
        if os.path.exists(self.db):
            self.conn = sqlite3.connect(self.db)
            self.cu = self.conn.cursor()
        else:
            self.conn = sqlite3.connect(self.db)
            self.cu = self.conn.cursor()
            self.cu.execute(
                'create table sensors(id integer primary key autoincrement,date text,value text)')
            self.cu.execute(
                'insert into sensors values(null,\'2016-1-1 12:00:00\',\'32\')')
            self.cu.execute(
                'create table actuators(id integer primary key autoincrement,date text,value text)')
            self.cu.execute(
                'insert into actuators values(null,\'2016-1-1 12:00:02\',\'open AC ato 26\')')
            self.conn.commit()


if __name__ == '__main__':
    app = web.application(urls, globals())
    app.run()[/mw_shl_code]
采用上诉方法开启服务器之后,智能内网访问web服务,为了达到外网操作的能力,需要使用花生壳的动态IP的映射的服务。
发表于 2016-9-16 20:37 | 显示全部楼层
,棒,在网页中修改了字,在1602中就显示了对应的字
发表于 2016-9-17 08:45 | 显示全部楼层
辛苦楼主了
 楼主| 发表于 2016-10-18 10:43 | 显示全部楼层
花生棒
一般家庭使用的网络均是动态变更IP地址的,外网访问不方便。这里使用花生棒来保证外网访问内网的能力。需要在oray注册账号,并购买花生棒,绑定账号,指定内网IP和Port对外共享,之后就可以在外网使用内网服务了。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|Archiver|手机版|Arduino中文社区

GMT+8, 2024-12-1 04:05 , Processed in 0.090622 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表