0.前言

    去年(2013年)2月第一次接触yeelink平台,当时该平台已经运行了一些时间也吸引了不少极客。试想自己也将投身IoT(物联网)行业,就花了些时间研究了它。陆陆续续使用和研究了一年,大致围绕两个问题展开——1.yeelink平台如何使用,2.如何构造一个功能简单些的yeelink平台。
    【PHP学习笔记——索引博文
    本文将讨论如何构造一个简单restful架构平台(该平台有点像yeelink,不过功能比yeelink少的多),并结合树莓派实现LED的远程控制(网络控制)。构建一个RESTFul平台涉及到很多知识,通过以下链接提供一些学习资料。
    【1】Slim——简单的 PHP5 框架可用来创建 RESTful 的 Web 应用
    【2】MySQL——关系型数据库管理系统
    【3】RedBean——用 NoSQL 的语法来使用 ORM 框架
    【4】JSON——轻量级的数据交换格式
    【5】cURL——利用URL语法在命令行方式下工作的开源文件传输工具
 
    如果亲爱的读者想快速入门也可以看看我的博客文章。
    【3】JSON——【cJSON学习笔记
    【4】树莓派——【树莓派学习笔记——yeelink 远程控制LED

    【2014年3月补充】    
    【1】如果您想获得本篇博文的源代码,请点击这里CSDN代码仓库】。数据库操作使用RedBean。
    【2】整理完本篇博文之后,修改了部分API函数并在GitHub建立了代码仓库(数据库操作并没有使用RedBean),如果本博文对您有用请点击这里GitHub Clone】。
    【3】如果想更深入一些例如部署到云平台中,请访问我的京东云擎——应用,相关代码仓库请猛击这里京东代码仓库
1.REST风格API
    在HTTP协议中定义了多种动作或者方法,这些方法具有不同的含义。
    【GET 获取】【POST 创建】【PUT 更新】【DELTE 删除】
    为了更好的理解以上的方法,下面结合LED远程控制举个例子。假设在数据库中已经保存了家庭中的LED设备信息,这些设备信息包括LED编号,LED设备描述和当前状态(打开或关闭)等,例如位于客厅的LED处于打开状态。
    可通过GET方法获得某个LED设备的信息或者全部LED的信息。这些LED灯具有一个唯一的编号,例如客厅的LED灯编号为1,那么 /leds/1就是编号为1的LED设备的唯一URI(可理解为网址)。通过这样类似的方法使每个LED设备具有网址,可通过该网址访问LED。通过GET方法可获得LED设备的所有信息,这些信息可通过JSON格式描述,例如:
    [{"id":1,"description":"raspberry pi IO1","status":"off"},{"id":2,"description":"raspberry pi IO2","status":"on"}]
 
    可通过POST方法创建一个新LED,新增加的LED具体信息可使用JSON格式描述,例如:
    {"description":"add a new led","status":"off"}
 
    可通过PUT方法更新LED信息,而具体内容用JSON格式描述,例如:
    {“status”:"on"}
 
    对于LED网络控制,REST API设计如下:
    GET /leds        返回所有的LED信息
    POST /leds      增加一个LED设备
    GET /leds/id    返回编号为id的LED设备信息
    PUT /leds/id    更新编号为id的LED设备信息

2.数据库准备
 
2.1 修改mysql密码
    【PHP再学习4—— slim框架学习和使用】一文中推荐使用wampserver,该软件为集成安装包包括了PHP和MySQL,在使用mySQL之前最好修改默认密码,可参考博文【修改mysql密码(博主:幸好我是程序猿)
    (2.1或2.2操作也可使用phpMyAdmin)
2.1 新建LED设备表
    使用mysql控制台,进入mysql数据库(输入use mysql,mysql为数据库的名称——安装wampserver后的一个默认数据库)。建立一个LED设备表,该表具有编号ID、描述description、状态status 字段,主键为id且自动增长(插入该数据表时 id写写入0或者不写,id编号会自动增长)。
  1. CREATE TABLE IF NOT EXISTS `leds` (
  2. id int(11) NOT NULL AUTO_INCREMENT,
  3. description text NOT NULL,
  4. status text NOT NULL,
  5. PRIMARY KEY (id)
  6. ) DEFAULT CHARSET=utf8;
【小提示】
    【1】选择数据库           use <databs_name>;
    【2】查看表的结构        desc <table_name>;
    【3】删除表                 drop table <table_name>;
    【4】id的数据类型为int(11),千万别以为int的长度为11位,int(11)只是一种int的表达方式。
 
2.2 插入设备内容
    可在MySQL控制台输入以下内容,插入两条数据:
  1. INSERT INTO leds (id, description , status) VALUES  (1, 'raspberry pi pcf8574-IO1','on');
  2. INSERT INTO leds (id, description , status) VALUES  (2, 'raspberry pi pcf8574-IO2','off');
3.GET方法获得所有LED信息
    使用GET方法或的所有LED状态——GET /leds。
    返回LED状态,使用JSON数据包描述。
    【代码片段】
  1. <?php
  2. require 'rb.php';
  3. require 'Slim/Slim.php';
  4. \Slim\Slim::registerAutoloader();
  5. // 初始化数据库连接
  6. R::setup('mysql:host=localhost;dbname=mysql','root','<your password>');
  7. R::freeze(true);
  8. $app = new \Slim\Slim();
  9. // GET /leds
  10. $app->get('/leds', function () use ($app) {
  11. // 查找所有设备
  12. $led_array = R::getAll('select * from leds');
  13. $app->response()->header('Content-Type', 'application/json');
  14. // 按照JSON格式输出
  15. echo json_encode( $led_array , JSON_NUMERIC_CHECK);
  16. });
  17. $app->run();
  18. ?>
    【代码解释】
    【1】 require 'rb.php';  载入redbean,请把rb.php放在www根目录。
    【2】R::setup('mysql:host=localhost;dbname=mysql','root','<your password>');R::freeze(true);载入数据库,填入数据库的名称和密码。
    【3】$led_array = R::getAll('select * from leds'); 查询数据库获得LED数据包的所有内容,getAll返回一个索引数组。
    【4】 echo json_encode( $led_array , JSON_NUMERIC_CHECK); 请注意mysql的整形转到PHP时将变为string,如果没有JSON_NUMERIC_CHECK选项,那可能会获得{“id”:"1",....},这肯定不是你所愿意看到的。
    
    【简单测试】
    可通过浏览器,cURL工具,浏览器HTTP插件进行测试。
    
图1 使用浏览器获得所有LED信息
4. GET方法获得单个LED信息
    【代码片段】
  1. // GET /leds/:id
  2. $app->get('/leds/:id', function ($id) use ($app) {
  3. try {
  4. // 查询数据库,只返回status状态
  5. $led_single = R::getRow('select status from leds where id = :id',array(':id'=>$id));
  6. if ($led_single) {
  7. $app->response()->header('Content-Type', 'application/json');
  8. // 按照JSON格式输出
  9. echo json_encode( $led_single, JSON_NUMERIC_CHECK);
  10. }
  11. else {
  12. $app->response()->status(404);
  13. }
  14. }
  15. catch (ResourceNotFoundException $e) {
  16. $app->response()->status(404);
  17. }
  18. catch (Exception $e) {
  19. $app->response()->status(400);
  20. $app->response()->header('X-Status-Reason', $e->getMessage());
  21. }
  22. });
    【代码解释】
    【1】$app->get('/leds/:id', function ($id) use ($app) id作为参数,可以输入数字1或2等。
    【2】$led_single = R::getRow('select status from leds where id = :id',array(':id'=>$id));
        select status from leds where id = :id 为SQL查询语句,和一般的SQL语句不同的是出现:id,array(':id'=>$id)该语句实现了SQL语句中的:id和输入参数id的绑定关系。在这里只查询status内容,其他内容忽略。
    【3】echo json_encode( $led_single, JSON_NUMERIC_CHECK); JSON格式输出,请主意使用JSON_NUMERIC_CHECK选项。
 
    【简单测试】
    使用curl工具测试,在windows 控制台中输入以下命令:
    
图2 使用cURL工具获得单个LED信息
 
5. PUT更新单个LED信息
    【代码片段】
  1. $app->put('/leds/:id', function ($id) use ($app) {
  2. try {
  3. // 获得HTTP请求中的JSON数据包
  4. $request = $app->request();
  5. $body = $request->getBody();
  6. $input = json_decode($body);
  7. // 查找编号为ID的记录
  8. $led = R::findOne('leds', 'id=?', array($id));
  9. // 重新修改status状态,并保存
  10. if ($led) {
  11. $led->status = (string)$input->status;
  12. R::store($led);
  13. } else {
  14. throw new ResourceNotFoundException();
  15. }
  16. } catch (ResourceNotFoundException $e) {
  17. $app->response()->status(404);
  18. } catch (Exception $e) {
  19. $app->response()->status(400);
  20. $app->response()->header('X-Status-Reason', $e->getMessage());
  21. }
  22. });
    【代码分析】
    【1】获得HTTP请求中的内容并进行解码,json_decode总是返回一个PHP对象而不是数组,所有后面对于input的操作需要使用->符号。
        $request = $app->request();
        $body = $request->getBody();
        $input = json_decode($body); 
    【2】$led = R::findOne('leds', 'id=?', array($id));  R::findOne总是返回一个对象,后面的操作需要使用->符号。
    【3】$led->status = (string)$input->status; 修改status。
    【4】R::store($led); 重新存储led信息。
 
    【简单测试】
    使用cURL工具测试,请求的内容为{"status":"off"},请求的方法为PUT。在windows控制台下输入以下命令:
    curl -i --request PUT  --data "{\"status\":\"on\"}"  http://localhost/leds/1
    注意:1)由于该HTTP负载并没有返回值,所有curl指令中加入-i选项,意为显示HTTP响应首部。
              2)PUT方法必须大写。    
图3 使用cURL更新单个LED状态
图4 编号为1的LED状态发生改变
 
6.树莓派 实现LED网络控制
    亲爱的朋友,如果您还不熟悉树莓派的话,可以参考:
    通过树莓派实现网络控制的方法也非常简单,树莓派不停的向服务器(在局域网中,IP为192.168.1.100)发送GET请求,服务器查询数据库以JSON格式返回LED信息,树莓派根据JSON数据包的内容控制LED灯,on为点亮,off为熄灭。
    【1】树莓派发送HTTP请求    GET /leds/1
    【2】服务器返回HTTP响应    {“status”:"off"}或{“status”:"on"}
    【3】树莓派根据status控制LED设备
 
    【代码片段】
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import requests
  4. import smbus
  5. import RPi.GPIO as GPIO
  6. import time
  7. # 打开 /dev/i2c-1
  8. bus = smbus.SMBus(1)
  9. # 设备URI
  10. apiurl = 'http://192.168.1.100/leds/1'
  11. while True:
  12. #发送请求
  13. r = requests.get(apiurl)
  14. # 打印内容
  15. print(r.text)
  16. # 响应转换内容为字典形式
  17. # 转换为字典类型 请注意 2.7.4版本使用r.json()
  18. led = r.json
  19. # {'value':'xx'} on打开状态,off关闭状态
  20. if led['status'] == 'on':
  21. print("led on")
  22. bus.write_byte( 0x20 , 1 )
  23. else:
  24. print("led off")
  25. bus.write_byte( 0x20 , 0 )
  26. # 延时5S
  27. time.sleep(5)
 
    【代码测试】
    由于没有做前端,所有只能通过cURL指令改变LED的status。改变数据库中LED的status之后,树莓派上扩展板的真实LED状态便会发生变化。前端控制页面请期待后续博文。
图5 测试结果LED状态发生改变
 
7.其他遐想
    本文只是想阐述REST框架的创建和使用,树莓派的使用并不是本文的重点(树莓派让我扩展了知识面)。除了树莓派之外还可以使用其他设备“享用”这个REST服务,例如
    arduino平台——入门简单,加上ENC28J60可替代本文树莓派的功能。
    STM32平台——【Yeelink平台使用——远程控制 RT Thread + LwIP+ STM32】简单修改该博客中的代码便可实现树莓派远程控制一样的功能,但是使用STM32平台需要更多的嵌入式方面的知识。
 
8.参考资料
【3】   yeelink API文档
 
9.关于我自己
    本人是一名嵌入式工程师,专注于物联网领域。虽然是一名嵌入式工程师,但是由于物联网的多领域交叉性,不得不让自己多学一些WEB方面的知识。对于工程师来说学习新的知识绝对是有必要的,工程师没过过去只有去创造
    Email:xukai19871105@126.com

PHP再学习5——RESTFul框架 远程控制LED的更多相关文章

  1. 树莓派学习笔记——Restful服务 采用slim php apache

    0.前言     前些时间沉迷于Restful,采用PHP+Slim+MySQL实现了一些简单的API函数.但是这些工作都是在windows中实现(采用wamp server集成安装包),但是转到li ...

  2. 玩转OneNET物联网平台之MQTT服务⑦ —— 远程控制LED(数量无限制)+ Android App控制 优化第一版

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  3. [源码解析] 深度学习分布式训练框架 horovod (18) --- kubeflow tf-operator

    [源码解析] 深度学习分布式训练框架 horovod (18) --- kubeflow tf-operator 目录 [源码解析] 深度学习分布式训练框架 horovod (18) --- kube ...

  4. Java并发包源码学习之AQS框架(四)AbstractQueuedSynchronizer源码分析

    经过前面几篇文章的铺垫,今天我们终于要看看AQS的庐山真面目了,建议第一次看AbstractQueuedSynchronizer 类源码的朋友可以先看下我前面几篇文章: <Java并发包源码学习 ...

  5. Java并发包源码学习之AQS框架(三)LockSupport和interrupt

    接着上一篇文章今天我们来介绍下LockSupport和Java中线程的中断(interrupt). 其实除了LockSupport,Java之初就有Object对象的wait和notify方法可以实现 ...

  6. RESTful框架调研

    背景 当前的开放服务平台发展趋势,是服务使用者变得多种多样,其中既有各种前端设备(台式机.手机等),又有各种后端服务器,因此必须有一个统一的机制,方便各种服务使用者和开放服务平台进行通信.为了更好的实 ...

  7. golang学习之beego框架配合easyui实现增删改查及图片上传

    golang学习之beego框架配合easyui实现增删改查及图片上传 demo目录: upload文件夹主要放置上传的头像文件,main是主文件,所有效果如下: 主页面: 具体代码: <!DO ...

  8. 最好的8个 Java RESTful 框架

    原文出处: colobu 过去的每一年,涌现出越来越多的Java框架.就像JavaScript,每个人都认为他们知道一个好的框架的功能应该是怎么样的.连我的老祖母现在也使用 一个我从来没有听说过而且可 ...

  9. 对比深度学习十大框架:TensorFlow 并非最好?

    http://www.oschina.net/news/80593/deep-learning-frameworks-a-review-before-finishing-2016 TensorFlow ...

随机推荐

  1. Codeforces Round #620 (Div. 2) A. Two Rabbits

    Being tired of participating in too many Codeforces rounds, Gildong decided to take some rest in a p ...

  2. 【转载】Linux截图工具

    如果linux安装了gnome,那么系统自带了一款截屏软件 gnome-screenshot,使用起来很方便,功能齐备,支持命令行.简单介绍如下. 功能 对屏幕,窗口,或自定义的区域进行截图. 选项 ...

  3. JAVA 开学测试

    package StudentScore; public class ScoreInformation { String stunumber; //学号 String name; //姓名 doubl ...

  4. 【SSM 】导出excel含图片

    ExprotRentUtils package com.kikyo.stat.utils; import java.awt.image.BufferedImage; import java.io.By ...

  5. 2018 最新注册码【激活码】、在线激活 pycharm 完整方法(亲测有效)【2018.06.01 重大更新!!!!】

    2018-06-01修改如下: 原来方法的第二种还是有效的,操作如下 (1)更新hosts文件(可以直接下载,然后解压缩) host文件地址 hosts文件,在windows中的地址为: 替换host ...

  6. sshpass安装以及使用

    centos7如何安装sshpass 先安装epel yum install -y epel-release yum repolist 安装完成epel之后,就可以按照sshpass了 yum ins ...

  7. 【网摘】JS 或 jQuery 获取当前页面的 URL 信息

    1.设置或获取对象指定的文件名或路径. window.location.pathname 2.设置或获取整个 URL 为字符串. window.location.href 3.设置或获取与 URL 关 ...

  8. MyBatis 入门Demo

    新建数据库my_db,新建表student_tb id为主键,不自动递增. 不必插入数据. 下载MyBatis https://github.com/mybatis/mybatis-3/release ...

  9. selenium webdriver 相关网站

    ITeye:http://shijincheng0223.iteye.com/blog/1481446 http://ztreeapi.iteye.com/blog/1750554 http://sm ...

  10. DOMContentLoaded 事件

    DOMContentLoaded 事件 字面上看,它会在dom加载完成后触发. 与window.onload事件非常相似,但有一定区别: DOMContentLoaded 事件是在文档完全加载和解析之 ...