该方案的初衷是只用微信来控制物联网,免去开发其他的控制端应用。
此次更改主要加上了Edison,并不是简单地让微信来控制Edison,这太简单了.......
稍微加点难度,想到了在保证控制的及时情况下,减小服务器端的资源消耗。
简单地讲,就是每个Arduino或其他物联网节点,通过Edison这个相当于路由器来发送传感器数据和获取控制命令。
控制Arduino
首先服务器上有一个数据库,数据库里面有几个记录,每个开关都有一个对应的值。
在微信中,我们对一个公众平台发送类似于“开灯”,“打开热水器”之类的命令,以“开灯”为例公众平台的后台服务器会对这个命令进行判断,如果符合预设的命令,就会进入数据库,找到这个LED对应的记录,把这个记录对应的值改为“1”(值其实是随意的)。
这里的命令发送方式包括文字消息,语音消息。
与此同时,Arduino通过wifi从edison,不断向一个服务器上的页面发送请求,请求中会包含一些诸如请求的开关ID,密码等参数,服务器核实后,就会进入数据库,找到对应的开关的记录,把对应的值“1”通过edison反馈给Arduino,Arduino收到反馈后,就会进行判断,如果是“1”,就把对应引脚上的LED点亮。如果是”0“,就把它熄灭。
Arduino的这个过程是不断的进行的,但由于网络和性能问题,通常会有几秒钟的延迟。
实例:
首先是服务器和微信端,
服务器端选择新浪云,毕竟这是不买VPS的一种比较好的方案,如果有自己的服务器,那么看了代码就懂了,也就不用看服务器端的部署了:)。
http://sae.sina.com.cn/,注册并登录,应该有免费云豆吧。
在管理页面选择创建新应用。
数据可以这么填,有些空会影响后面的过程
进入该应用的管理页面,并选择左边的代码管理
编辑代码
编辑器
添加文件
粘贴下面的代码,注意修改代码,有些值后面会看到
<?php if ($_GET['data'] && ($_GET['token'] == "doubleq")) {//可以改token,这相当于密码,在Arduino端改成相应的值即可
$con = mysql_connect(SAE_MYSQL_HOST_M.':'.SAE_MYSQL_PORT,SAE_MYSQL_USER,SAE_MYSQL_PASS);
$data = $_GET['data'];
mysql_select_db("app_ulink42", $con);//要改成相应的数据库名
$result = mysql_query("SELECT * FROM switch");
while($arr = mysql_fetch_array($result)){//找到需要的数据的记录,并读出状态值
if ($arr['ID'] == 1) {
$state = $arr['state'];
}
}
$dati = date("h:i:sa");//获取时间
$sql ="UPDATE sensor SET timestamp='$dati',data = '$data'
WHERE ID = '1'";//更新相应的传感器的值
if(!mysql_query($sql,$con)){
die('Error: ' . mysql_error());//如果出错,显示错误
}
mysql_close($con);
echo "{".$state."}";//返回状态值,加“{”是为了帮助Arduino确定数据的位置
}else{
echo "Permission Denied";//请求中没有type或data或token或token错误时,显示Permission Denied
}
?>
同样地,把index.php改掉
<?php
//错误日志
function echo_server_log($log){
file_put_contents("log.txt", $log, FILE_APPEND);
}
//定义TOKEN
define ( "TOKEN", "ulink" );
//验证微信公众平台签名
function checkSignature() {
$signature = $_GET ['signature'];
$nonce = $_GET ['nonce'];
$timestamp = $_GET ['timestamp'];
$tmpArr = array ($nonce, $timestamp, TOKEN );
sort ( $tmpArr );
$tmpStr = implode ( $tmpArr );
$tmpStr = sha1 ( $tmpStr );
if ($tmpStr == $signature) {
return true;
}else{
return false;
}
}
if(false == checkSignature()) {
exit(0);
}
//接入时验证接口
$echostr = $_GET ['echostr'];
if($echostr) {
echo $echostr;
exit(0);
}
//获取POST数据
function getPostData() {
$data = $GLOBALS['HTTP_RAW_POST_DATA'];
return $data;
}
$PostData = getPostData();
//验错
if(!$PostData){
echo_server_log("wrong input! PostData is NULL");
echo "wrong input!";
exit(0);
}
//装入XML
$xmlObj = simplexml_load_string($PostData, 'SimpleXMLElement', LIBXML_NOCDATA);
//验错
if(!$xmlObj) {
echo_server_log("wrong input! xmlObj is NULL\n");
echo "wrong input!";
exit(0);
}
//准备XML
$fromUserName = $xmlObj->FromUserName;
$toUserName = $xmlObj->ToUserName;
$msgType = $xmlObj->MsgType;
if($msgType == 'voice') {//判断是否为语音
$content = $xmlObj->Recognition;
}elseif($msgType == 'text'){
$content = $xmlObj->Content;
}else{
$retMsg = '只支持文本和语音消息';
}
if (strstr($content, "温度")) {
$con = mysql_connect(SAE_MYSQL_HOST_M.':'.SAE_MYSQL_PORT,SAE_MYSQL_USER,SAE_MYSQL_PASS);
mysql_select_db("app_ulink42", $con);//修改数据库名
$result = mysql_query("SELECT * FROM sensor");
while($arr = mysql_fetch_array($result)){
if ($arr['ID'] == 1) {
$tempr = $arr['data'];
}
}
mysql_close($con);
$retMsg = "报告大王:"."\n"."主人房间的室温为".$tempr."℃,感谢您对主人的关心";
}else if (strstr($content, "开灯")) {
$con = mysql_connect(SAE_MYSQL_HOST_M.':'.SAE_MYSQL_PORT,SAE_MYSQL_USER,SAE_MYSQL_PASS);
$dati = date("h:i:sa");
mysql_select_db("app_ulink42", $con);//修改数据库名
$sql ="UPDATE switch SET timestamp='$dati',state = '1'
WHERE ID = '1'";//修改开关状态值
if(!mysql_query($sql,$con)){
die('Error: ' . mysql_error());
}else{
mysql_close($con);
$retMsg = "好的主人";
}
}else if (strstr($content, "关灯")) {
$con = mysql_connect(SAE_MYSQL_HOST_M.':'.SAE_MYSQL_PORT,SAE_MYSQL_USER,SAE_MYSQL_PASS);
$dati = date("h:i:sa");
mysql_select_db("app_ulink42", $con);//修改数据库名
$sql ="UPDATE switch SET timestamp='$dati',state = '0'
WHERE ID = '1'";//修改开关状态值
if(!mysql_query($sql,$con)){
die('Error: ' . mysql_error());
}else{
mysql_close($con);
$retMsg = "好的主人";
}
}else{
$retMsg = "暂时不支持该命令";
}
//装备XML
$retTmp = "<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[%s]]></Content>
<FuncFlag>0</FuncFlag>
</xml>";
$resultStr = sprintf($retTmp, $fromUserName, $toUserName, time(), $retMsg);
//反馈到微信服务器
echo $resultStr;
?>
回到应用管理页面,选择左边的MySQL,单击初始化
管理MySQL
新建数据表
参数如下
同样的方法,建立一个名字为sensor字段数为3的数据表
参数如下
分别插入一条记录
然后进入http://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index,申请测试号,
填写相应配置
这时候可以用手机端进行测试了,可以发送包含”温度“,”关灯“,”开灯“的文字消息,或者语音消息,都会有相应的反馈。
在Edison主要使用node.js,来接受终端的请求和发起对服务器的请求。
在随便一个位置添加以下代码
//httpserverrequestget.js
var http =require('http');
var http2 = require('http');
var url = require('url');
var querystring = require("querystring");
var path1 = '/downup.php?token=doubleq&data=';
var sensordata = '123';
var pathful;
var options;
var resdata;
http.createServer(function(req,res){
res.writeHead(200,{'Content-Type':'text/html'});
var objectUrl = url.parse(req.url);
var objectQuery = querystring.parse(objectUrl.query);
var gething = objectQuery['data'];
pathful = path1+gething;
options = {
host: '1.ulink42.sinaapp.com',
path: pathful,
method: 'GET',
headers: {
'Accept': 'text/html'
}
};
var req = http2.request(options, function(res2) {
res2.setEncoding('utf8');
res2.on('data', function(data) {
console.log(data);
resdata = data;
});
});
req.end();
res.end(resdata);
}).listen(8080);
我测试的物联网终端是DFRobot出的WIDO
用IDE烧写以下的代码
#include <Adafruit_CC3000.h>
#include <ccspi.h>
#include <SPI.h>
#include <string.h>
#include "utility/debug.h"
#define WiDo_IRQ 7
#define WiDo_VBAT 5
#define WiDo_CS 10
Adafruit_CC3000 WiDo = Adafruit_CC3000(WiDo_CS, WiDo_IRQ, WiDo_VBAT,
SPI_CLOCK_DIVIDER);
#define WLAN_SSID "yourwifi"
#define WLAN_PASS "yourpswd"
#define WLAN_SECURITY WLAN_SEC_WPA2
int value = 0;
char tem[5];
char state;
char c;
#define TIMEOUT_MS 20000
void setup(){
Serial.begin(9600);
pinMode(2, OUTPUT);
Serial.println(F("Hello, Wido!\n"));
Serial.println(F("\nInitialising the CC3000 ..."));
if (!WiDo.begin())
{
Serial.println(F("Unable to initialise the CC3000! Check your wiring?"));
while(1);
}
if (!WiDo.connectToAP(WLAN_SSID,WLAN_PASS,WLAN_SECURITY)) {
Serial.println(F("Failed!"));
while(1);
}
Serial.println(F("Connected!"));
Serial.println(F("Request DHCP"));
while (!WiDo.checkDHCP())
{
delay(100);
}
}
void loop(){
static Adafruit_CC3000_Client client;
if(client.connected()){
if(state == '0'){
digitalWrite(2, LOW);
}else if(state == '1'){
digitalWrite(2, HIGH);
}
value = analogRead(0);
value= int(value*0.0048828125*100); //把读取到的val转换为温度数值,系数一:0.00488125=5/1024,0~5V对应模拟口读数1~1024,系数二:100=1000/10,1000是毫伏与伏的转换;10是每10毫伏对应一度温升。
itoa(value,tem,10);
Serial.println(tem);
client.fastrprint("GET /?data=");
client.fastrprint(tem);
client.fastrprint(" HTTP/1.1\r\nHost: 192.168.11.132\r\nUser-Agent: arduino-ethernet\r\nConnection: close");
Serial.println("geted");
client.fastrprint(F("\r\n"));
client.fastrprint(F("\r\n"));
Serial.println();
unsigned long lastRead = millis();
while (client.connected() && (millis() - lastRead < TIMEOUT_MS)) {
while(client.available()) {
c = client.read();
Serial.print(c);
if (c == '{'){
state = client.read();
Serial.print(state);
}
}
}
client.close();
}else{
uint32_t ip = WiDo.IP2U32(192,168,11,132);
client = WiDo.connectTCP(ip,8080);
Serial.println("Connecting IoT Server...");
}
delay(5000);
}
其中发起请求的ip是Edison的地址,务必使他们在同一局域网内,可在Edison的命令行中输入ifconfig来进行查看。
在这个例子中,WIDO收集LM35的温度值,上传到Edison,Edison收到请求后,将数据上传至新浪云上的服务器,并获得控制LED的命令,返回给WIDO,WIDO再对LED进行控制。