PHP再學習5——RESTFul框架 遠程控制LED

0.前言php

 

    去年(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語法在命令行方式下工做的開源文件傳輸工具
 
    若是親愛的讀者想快速入門也能夠看看個人博客文章。
    【1】Slim——【 PHP再學習4—— slim框架學習和使用
    【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編號會自動增加)。
[sql]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  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控制檯輸入如下內容,插入兩條數據:
[sql]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  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數據包描述。
    【代碼片斷】
[php]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. <?php  
  2.   
  3. require 'rb.php';  
  4. require 'Slim/Slim.php';  
  5.   
  6. \Slim\Slim::registerAutoloader();  
  7.   
  8. // 初始化數據庫鏈接  
  9. R::setup('mysql:host=localhost;dbname=mysql','root','<your password>');  
  10. R::freeze(true);  
  11.   
  12. $app = new \Slim\Slim();  
  13.   
  14. // GET /leds  
  15. $app->get('/leds', function () use ($app) {   
  16.     // 查找全部設備  
  17.     $led_array = R::getAll('select * from leds');  
  18.     $app->response()->header('Content-Type', 'application/json');  
  19.     // 按照JSON格式輸出  
  20.     echo json_encode( $led_array , JSON_NUMERIC_CHECK);  
  21. });  
  22.   
  23. $app->run();  
  24. ?>  
    【代碼解釋】
    【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信息
    【代碼片斷】
[php]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  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信息
    【代碼片斷】
[php]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  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.           
  8.         // 查找編號爲ID的記錄  
  9.         $led = R::findOne('leds', 'id=?', array($id));    
  10.           
  11.         // 從新修改status狀態,並保存  
  12.         if ($led) {        
  13.             $led->status = (string)$input->status;  
  14.             R::store($led);      
  15.         } else {  
  16.             throw new ResourceNotFoundException();      
  17.         }  
  18.     } catch (ResourceNotFoundException $e) {  
  19.         $app->response()->status(404);  
  20.     } catch (Exception $e) {  
  21.         $app->response()->status(400);  
  22.         $app->response()->header('X-Status-Reason', $e->getMessage());  
  23.     }  
  24. });  
    【代碼分析】
    【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設備
 
    【代碼片斷】
[python]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  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
相關文章
相關標籤/搜索