ラベル Raspberry Pi の投稿を表示しています。 すべての投稿を表示
ラベル Raspberry Pi の投稿を表示しています。 すべての投稿を表示

2020年8月23日日曜日

【家の自動化プロジェクト】#4 Node.jsでWEBインターフェースを作る

どうもcaketetuです。
今回は、前回製作したデバイスにアクセスするためのインターフェースをNode.jsで作ります。
より具体的にはRaspberry Pi上にWEBサーバを作り、スマホやPCのブラウザからアクセスし指示コマンドを受け付けるようにします。受信した指示コマンドをBLE信号に変換し前回製作したデバイスに飛ばせるようにします。要するに以下の経路を作ります。

webブラウザ →  <<socket.io通信>  →  webサーバ  ->  <<BLE通信>>  →  BLEデバイス



ちなみに現在のRaspberry Piの様子は…



こんな感じになってます。
3B+です。OSはちょっと古いStretch。
Amazonで安く購入できたタッチパネル付き液晶を付けている以外は特にいじっていません。いずれは音楽プレイヤー機能、キオスク機能、フォトフレーム機能を搭載したいと考えております。



PythonでBLE

bluepyでPythonからBLE信号を出せるようにします。
送信と読み込みのサンプルを貼っておきます。

ble_write.py
# -*- coding: utf-8 -*-
import time
import bluepy

HANDLE_LED = 0x002d		#ハンドル
devadr = "3C:71:BF:F0:66:26"   # アドレス

def main():
    peri = bluepy.btle.Peripheral()
    peri.connect(devadr, bluepy.btle.ADDR_TYPE_PUBLIC)
    peri.writeCharacteristic(HANDLE_LED, b'Ms' )
    time.sleep(5)
    peri.disconnect()

if __name__ == "__main__":
    main()
ble_read.py
# -*- coding: utf-8 -*-
import bluepy
import time

HANDLE_SERIAL = 0x002d		#ハンドル
devadr = "24:6F:28:9D:C6:6A"   # アドレス

def main():
    peri = bluepy.btle.Peripheral()
    peri.connect(devadr, bluepy.btle.ADDR_TYPE_PUBLIC)
    time.sleep(1)
    serialnum = peri.readCharacteristic(HANDLE_SERIAL)
    print(len(serialnum))
    print( serialnum.decode('utf-8') )
    peri.disconnect()

if __name__ == "__main__":
    main()

bluepyの簡単な使い方ですが、
まず 
peri = bluepy.btle.Peripheral()で宣言 、
peri.connect(デバイスアドレス, bluepy.btle.ADDR_TYPE_PUBLIC) 
で接続します。読み込みは 
データ = peri.readCharacteristic(ハンドル)、
送信は 
peri.writeCharacteristic(ハンドル, データ )
です。最後に
peri.disconnect()
でクローズしましょう。

デバイスのハンドル、アドレスの調べ方はここでご紹介しています。

ここで、前回作ったデバイスの通信コードですが、
//*****命令コード表****
CMD_SERVO_MOVE 0x78 //照明用サーボ動作指令
命令コード + 角度(2byte) + 動作時間(2byte)
CMD_LIGHT_LIV   0x80 //リビング照明
命令コードのみ
CMD_LIGHT_ENT 0x81 //エントランス照明
命令コードのみ
CMD_IR_SEND 0x79 //エアコン、データ書き込み
命令コード  + ボタン + 温度+ 風力+ 動作モード + ON/OFF (全1byte)
CMD_IR_POWER_OFF 0x82 //エアコンOFF
命令コードのみ
CMD_IR_REPEAT 0x83 //エアコン指令をリピート
命令コードのみ
REQ_DATA 0x50 //全データリクエスト
   命令コードのみ(読み取りで実行)

となっていました。これにあわせて作るとこんな感じになります。参考にどうぞ。
コードを書く時詰まったのがバイト操作です。WEBソケット通信の命令は文字列なのに対しBLE通信はバイト列になっているので数値を送るときはバイト単位の結合、分離が必要です。これはpythonのpack,unpackモジュールで実現できます。



Raspberry Pi上にWEBブラウザを作る

WEBブラウザの作り方はこちらに別記事でまとめました。これに上記で作成したPython
プログラムを呼び出すコードを追加することでWEBブラウザから指示を出せるようになります。現在は以下のようになってます。

app.js
	
//********************************************************** */
//      モジュールオブジェクトの初期化
//********************************************************** */
var express = require('express');
var app = express();
var http = require('http').Server(app);
const io = require('socket.io')(http);          //Socket.io
const execSync = require('child_process').execSync; //外部コマンド実行(同期)

//********************************************************** */
//      グローバル変数
//********************************************************** */
//エアコン送信パラメータ
var ac_para = ['b_off', '27', 'auto', 'cooling', 'off'];

//********************************************************** */
//      EXPRESS関連
//********************************************************** */
//サーバー実装の前に、エラーハンドリングを記載します。
process.on('uncaughtException', function(err) {
    console.log(err);
  });
//テンプレートエンジンの指定s
app.set("view engine", "ejs");
//ルーティング
app.get('/', (req, res)=> { res.render('index'); });
//3000番ポートで待ち受けてサーバー開始
http.listen(3000, function () {
    console.log("Start server at port:3000...");
});

//********************************************************** */
//    SocketIOリクエスト処理
//********************************************************** */
io.sockets.on("connection", function (socket) {
    //=====照明コントロール=====
    socket.on("light", function (msg) {
        var res;
        if(msg=='living'){
            res = request_shellsync("python3 ble_light_ctrl.py servo 55 1000");
        }else if(msg=='entrance'){
            res = request_shellsync("python3 ble_light_ctrl.py servo 115 1000");
        }
        console.log(res.toString());
    });
    //=====エアコンコントロール=====
    socket.on("aircon", function (msg) {
      var res;
      var cmd = "python3 ble_aircon_ctrl.py ";
      if(msg.cmd=='send'){
          cmd += msg.btn + " ";
          cmd += msg.temp + " ";
          cmd += msg.level + " ";
          cmd += msg.select;
          res = request_shellsync(cmd);
      }else if(msg.cmd=='off'){
          cmd += "off";
          res = request_shellsync(cmd);
      }
      console.log(res.toString());
    });
    //=====画面コントロール=====
    socket.on("display", function (msg) {
      var res;
      if(msg=='on'){
          res = request_shellsync("vcgencmd display_power 1");
      }else if(msg=='off'){
          res = request_shellsync("vcgencmd display_power 0");
      }
      console.log(res.toString());
    });

    //端末データリクエスト
    socket.on("data_req", function (msg) {
        var res;
        res = request_shellsync("python3 ble_read.py");
        socket.emit("req_data", res.toString());
        console.log(res.toString());
    });
});

  //********************************************************** */
  //Shellに指示を投げる関数 同期
  //********************************************************** */
  var request_shellsync = function(command){
    var str_cmd =  command;   //コマンド列生成
    //child_processでシェルコマンドを実行
    result = execSync(str_cmd, function(error, stdout, stderr) {
      if (error !== null) {
        console.log('exec error: ' + error);    //エラーなら表示
      }
    });
    return result;  //結果を返す
  }

index.ejs
	
<!DOCTYPE html><html>
    <head>
        <title>Home Ctrl</title>
    </head>
    <body>
        <script src="/socket.io/socket.io.js"></script>
        <script type="text/javascript">
            var socket = io.connect('http://ip_adress:3000');
            //ライトコントロール
            function cmd_light(cmd) {
                socket.emit("light",cmd);
            }
            //エアコンコントロール
            function cmd_aircon(cmd) {
                var btn = document.getElementById('ac_btn');
                var temp = document.getElementById('ac_temp');
                var level = document.getElementById('ac_level');
                var select = document.getElementById('ac_select');
                var send_data = {
                    "cmd" : cmd,
                    "btn" : btn.value,
                    "temp" : temp.value,
                    "level" : level.value,
                    "select" : select.value,
                }
                socket.emit("aircon",send_data);
            }
            function cmd_display(cmd) {
                socket.emit("display",cmd);
            }
            //端末データリクエスト
            function cmd_request() {
                socket.emit("data_req",0);
            }
            //リクエストデータ受信時
            socket.on("req_data", function (data) {
                document.getElementById("request_data").innerHTML=data;
            });

        </script>
        <div class="title"><h1>My Home Automation</h1></div>
        <!-- 端末データ表示 -->
        <div class="function">
            <div class="fc_title">Sensor data</div>
            <div id="request_data">Info:</div>
            <button type="button" onClick="cmd_request()">Data Request</button></br>
        </div>
        <!-- 照明コントロールボタン -->
        <div class="function">
            <div class="fc_title">light switch</div>
            <button type="button" onClick="cmd_light('living')">Living</button>
            <button type="button" onClick="cmd_light('entrance')">Entrance</button>
        </div>
        <!-- エアコン操作ボタン -->
        <div class="function">
            <div class="fc_title">Air Con Controll</div>
            <select id="ac_btn">
                <option value="off">off</option>
                <option value="up">up</option>
                <option value="down">down</option>
                <option value="tistop">tistop</option>
                <option value="tiwkup">tiwkup</option>
                <option value="wind_lev">wind_lev</option>
                <option value="wind_dir">wind_dir</option>
            </select>
            <select id="ac_temp">
                <option value="19">19</option>
                <option value="20">20</option>
                <option value="21">21</option>
                <option value="22">22</option>
                <option value="23">23</option>
                <option value="24">24</option>
                <option value="25">25</option>
                <option value="26">26</option>
                <option value="27">27</option>
                <option value="28">28</option>
                <option value="29">29</option>
            </select>
            <select id="ac_level">
                <option value="auto">auto</option>
                <option value="strong">strong</option>
                <option value="nomal">nomal</option>
                <option value="week">week</option>
            </select>
            <select id="ac_select">
                <option value="cooling">cooling</option>
                <option value="dehumid">dehumid</option>
                <option value="heating">heating</option>
            </select>
            <button type="button" onClick="cmd_aircon('send')">send</button>
            <button type="button" onClick="cmd_aircon('off')">off</button>
        </div>
        <!-- 画面バックライト制御ボタン -->
        <div class="function">
            <div class="fc_title">Display Controll</div>
            <button type="button" onClick="cmd_display('on')">on</button>
            <button type="button" onClick="cmd_display('off')">off</button>
        </div>
    </body>
</html>

Node.jsで外部プログラムを呼び出すにはchild_processモジュールを使います。
const execSync = require('child_process').execSync; //外部コマンド実行(同期)

//********************************************************** */
//Shellに指示を投げる関数 同期
//********************************************************** */
var request_shellsync = function(command){
  var str_cmd =  command;   //コマンド列生成
  //child_processでシェルコマンドを実行
  result = execSync(str_cmd, function(error, stdout, stderr) {
    if (error !== null) {
      console.log('exec error: ' + error);    //エラーなら表示
    }
  });
  return result;  //結果を返す
}

res = request_shellsync("vcgencmd display_power 1");
端末で実行するようなコマンドを文字列で渡すだけなので楽です。pythonだけではなく
シェルコマンドや多言語のプログラムも同じように実行できます。
同じ手法でRaspberry Piの画面をON/OFFするコードも作っています。
実行コマンドは
vcgencmd display_power 1    //画面ON
vcgencmd display_power 0  //画面OFF
です。覚えておくといつか使う時があるかも。



サーバ起動!

ディレクトリ内でnode app.jsを打ち込みサーバを起動します。ブラウザから"http://アドレス:ポート"にアクセスするとコントローラ画面が表示されます。
動作の様子







今回は以上です。インターフェースの見た目がだいぶアレですがかなり実用的になったと思います。私も日常生活では非常に活用していて、電気を消すためいちいち立ち上がったりしなくてよいので便利になったなぁと思います。さらにIoTしてる感による近未来感が良い。

とはいえRaspberry Piでサーバーを動かしている割にはまだまだ機能がショボいので、もっとデバイスを作って機能を増やし、より快適な自動化生活を目指します。

2020年8月21日金曜日

【家の自動化プロジェクト】#3 一つ目のBLEデバイス

どうも、caketetuです。
相変わらず家の自動化プロジェクトを進めています。今回は一つ目のデバイスが完成したので
ご紹介します。



外観


前回でESP32を使ったベースに小型液晶とサブプロセッサとしてのArduino、環境センサ
を乗っけたデバイスを作りました。今回はさらに機能を足して最終的に以下機能を持った
デバイスになりました。(リンクは個別記事です。参考にどうぞ)

・照明操作(アナログサーボ) アナログサーボを動かす
・エアコン操作(赤外線LED) 赤外線でエアコンを操作する
・環境測定(環境センサ) 自動化プロジェクト#2
・画面表示(小型液晶) 自動化プロジェクト#2

全部乗せ感がすごいですね(笑)


回路設計



回路はこんな感じです。赤外線LEDとサーボはAruduinoを介して接続しているので
ちょっとごちゃついています。ESP32のBLEを使うと割込みが不安定になることが
あったのでこのような構成にしました。

照度センサが標準でついているのは寝るときに画面を消すためです。<<意外と重要>>





ハード設計


画像中央のコアユニット部です。タミヤのユニバーサルプレートに合わせて
設計することで他の工作にも応用が利くように工夫しました。


画像左側のユニットにくっついているのはエアコン操作用の赤外線照射
モジュールです。LEDを三個直列につなげて照射範囲を広くしています。
結構遠くでも反応してくれます(5mぐらい?)
2自由度あるので調整も割と簡単です。


画像上部に乗っているのは照明操作用のサーボモータです。一軸で二つの照明を操作
出来ます。既製品のスイッチに左右のネジでクランプして固定します。
既存のモノの面積がとても小さいので、ちゃんと固定するのに苦労しました。



ソフト

上記の個別記事で紹介したサンプルプログラムをこちらで作ったBLEデバイスのプロトタイプにぶち込んで作りました。参考までにすべて載せておきます。

BLE_Unit_Firmware1.0.c      ESP32のメインプログラム
code_list.h                        通信コードヘッダ
Arduino_sub_prossor1.0.c    Aruduino 側プログラム
IR_Ctrl.h                          エアコン操作ヘッダ 

通信プロトコルはESP32側の136行目から始まる関数とcode_list.hに定義されており、
BLEデータの先頭バイトで識別します。
現在、以下のように定義されています。

//*****命令コード表****
CMD_SERVO_MOVE   0x78   //照明用サーボ動作指令  
        命令コード +  角度(2byte) + 動作時間(2byte)
CMD_LIGHT_LIV      0x80   //リビング照明
        命令コードのみ
CMD_LIGHT_ENT       0x81   //エントランス照明
        命令コードのみ
CMD_IR_SEND           0x79   //エアコン、データ書き込み
        命令コード  + ボタン + 温度+ 風力+ 動作モード + ON/OFF (全1byte)
CMD_IR_POWER_OFF 0x82   //エアコンOFF
        命令コードのみ
CMD_IR_REPEAT        0x83   //エアコン指令をリピート
        命令コードのみ
REQ_DATA                 0x50   //全データリクエスト
   命令コードのみ(読み取りで実行)

現在、通信エラーはめったに発生していませんが、チェックサムなどを用いた
エラーチェックをしたほうがいいかもしれませんね。

また、パラメータ設定指令などを作ってパラメータを保存できるようにするのも課題です。



今回は以上です。若干ゴツゴツした見た目が意外とお気に入りです。
次回はRaspberry piにWEBサーバを作りそこから操作できるようにしたいと思います。











2020年8月20日木曜日

【Node.js】webサーバを作る

どうもcaketetuです。
相変わらず家の自動化プロジェクトを進めております。今回はIoTで大活躍するNode.jsを
使ってRaspberry Pi上にWEBサーバーを作ります。テンプレートエンジンにEJS、フレームワークとしてExpressを使用しますが、ジェネレータは使用せずに作ります。

Node.jsの導入は省略。

Expressではファイルやデータの場所が決まっているようです。適当な場所にディレクトリを作り、以下のようにファイルを配置します。

Home---views---index.ejs     ←WEBページ(HTML)
          |
            ---app.js ←プログラム本体(javascript)

ディレクトリ内にいる状態で以下コマンドでExpressをインストールします。
$ npm init
$ npm install express --save

とりあえずWEBページを表示するプログラムは以下の通りです。

app.js
//モジュールオブジェクトの初期化
var express = require('express');       
var app = express();
var http = require('http').Server(app);

//サーバー実装の前に、エラーハンドリングを記載
process.on('uncaughtException', function(err) {
    console.log(err);
  });

//テンプレートエンジンの指定
app.set("view engine", "ejs");

//ルーティング
app.get('/', (req, res)=> { res.render('index'); });

//3000番ポートで待ち受けてサーバー開始
http.listen(3000, function () {
    console.log("Start server at port:3000...");
});
index.ejs
<!DOCTYPE html><html>
    <head>
        <title>Home Ctrl</title>
    </head>
    <body>
        <h1>My Home Automation</h1s>
    </body>
</html>

アプリの実行は以下コマンドです。
$ node app

http://IPアドレス:ポート でWEBブラウザからアクセスできます。


これだけではまだ何もできませんのでSocket.IOでブラウザ経由でRaspberry Piに指示を
出せるようにしてみます。
$ npm install socket.io --save

index.ejs
<!DOCTYPE html><html>
    <head>
        <title>Home Ctrl</title>
    </head>
    <body>
        <script src="/socket.io/socket.io.js"></script>
        <script type="text/javascript">
            var socket = io.connect('http://アドレス:3000');
            function send_msg(cmd) {
                socket.emit("light", cmd);
            }
        </script>
        <h1>My Home Automation</h1>
        <button type="button" onClick="send_msg('living')">Living</button>
        <button type="button" onClick="send_msg('entrance')">Entrance</button>
    </body>
</html>
app.js
//モジュールオブジェクトの初期化
var express = require('express');       
var app = express();
var http = require('http').Server(app);
const io = require('socket.io')(http);          //Socket.io

//サーバー実装の前に、エラーハンドリングを記載します。
process.on('uncaughtException', function(err) {
    console.log(err);
  });

//テンプレートエンジンの指定s
app.set("view engine", "ejs");

//ルーティング
app.get('/', (req, res)=> { res.render('index'); });

//3000番ポートで待ち受けてサーバー開始
http.listen(3000, function () {
    console.log("Start server at port:3000...");
});

//********************************************************** */
//    SocketIOリクエスト処理
//********************************************************** */
io.sockets.on("connection", function (socket) {
    //=====照明コントロール=====
    socket.on("light", function (msg) {
        console.log(msg);
    });
});

socket.emitでデータ送信socket.onでデータ受信します。これでボタンをブラウザの
ボタンを押してRaspberry pi に指示を出したり、Raspberry pi からセンサデータ
を受け取ったりできるようになります。



今回は以上です。ウェブブラウザベースで作ることでパソコンからもスマホからも同じようにアクセスできるようになります。



2020年7月29日水曜日

【ESP32】 BLEでRaspberry Piと通信する

どうもcaketetuです。
最近暇つぶしにFalloutというゲームを買ってみたら、かなりハマってしまって
猛烈な勢いで時間を消費しています。短期間にゲームをやりこむと燃え尽きて
すぐ飽きてしまうのでほどほどにして現実でもクラフトしましょう。
今回はホームオートメーションの核となるBLEでRaspberryPiと通信してみます。



こちらにBLEの基礎について詳しい解説がありました。実際はライブラリに隠れているので
そこまで意識することはありませんがザっと読んでおくと大変勉強になります。

・BLEプログラムのプロトタイプを作る

ちょっと勉強して理解したつもりになったところでArduinoIDEのサンプルからBLEデバイスの
プロトタイプを作ってみました。

//==========================================================
//    Include Header
//==========================================================
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLE2902.h>

//*****ヘッダ都度部******

//*********************

//==========================================================
//    Macro Definition
//==========================================================
#define SERVER_NAME "ESP32_BLE_TEST"     // サーバー名 各自設定する
//====シグナル定義====
//#define SIGNAL_ERROR      'E'         // (異常発生:Error)
//#define SIGNAL_REQUEST    'r'         // デバイス状態取得要求
//====UUID設定====
#define SERVICE_UUID               "28b0883b-7ec3-4b46-8f64-8559ae036e4e"      // サービスのUUID
#define CHARACTERISTIC_UUID_Notify "2049779d-88a9-403a-9c59-c7df79e1dd7c"      // NotifyUUID
#define CHARACTERISTIC_UUID_RX     "9348db8a-7c61-4c6e-b12d-643625993b84"      // 受信用UUID
#define CHARACTERISTIC_UUID_TX     "e303cfe5-b463-4b82-bbc3-e0789945b499"      // 送信用UUID

//*****マクロ都度部******

//*********************

//==========================================================
//    Function Prototype
//==========================================================
void BLE_Setup(void);

//****関数プロトタイプ都度部*****

//****************************

//==========================================================
//    Global Variable
//==========================================================
BLECharacteristic *pCharacteristicTX;   // 送信用キャラクタリスティック
BLECharacteristic *pCharacteristicRX;   // 受信用キャラクタリスティック
BLECharacteristic *pCharacteristicDD;   // データ送信要求キャラクタリスティック
BLEServer *pServer = NULL;
bool deviceConnected = false;           // デバイスの接続状態
bool bInAlarm  = false;                 // デバイス異常判定
bool oldDeviceConnected = false;        //

//*****グローバル変数都度部******

//****************************

//----------------------------------------------------------
//    接続・切断時コールバック関数
//----------------------------------------------------------
class funcServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
      deviceConnected = true;
    }
    void onDisconnect(BLEServer* pServer) {
      deviceConnected = false;
    }
};

//----------------------------------------------------------
//    シグナル受信時のコールバック
//----------------------------------------------------------
class funcReceiveCallback: public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic *pCharacteristicRX) {
      // シリアルモニタに表示する
      std::string rxValue = pCharacteristicRX->getValue();
      if (rxValue.length() > 0) {
        //受信したデータを全部表示
        Serial.print("RECV_DATA: ");
        for (int i = 0; i < rxValue.length(); i++) Serial.print(rxValue[i]);
        Serial.println();

        //*******************受信時処理の都度部**********************************

        
        //*******************************************************
      }
    }
    void onRead(BLECharacteristic *pCharacteristicRX) {
      
      //*******************受信時処理の都度部**********************************
      char send_str[] = "Hello world";
      pCharacteristicRX->setValue(send_str);
      Serial.println("BLE:Data Request");
      //*******************************************************
    }
};

//----------------------------------------------------------
//    BLEセットアップ関数
//----------------------------------------------------------
void BLE_Setup(void) {
  BLEDevice::init(SERVER_NAME);   // 初期化処理を行ってBLEデバイスを初期化する
  // Serverオブジェクトを作成してコールバックを設定する
  pServer = BLEDevice::createServer();
  pServer->setCallbacks(new funcServerCallbacks());
  // Serviceオブジェクトを作成して準備処理を実行する
  BLEService *pService = pServer->createService(SERVICE_UUID);

  // Notify用のキャラクタリスティックを作成する
  pCharacteristicTX = pService->createCharacteristic(
                        CHARACTERISTIC_UUID_TX,
                        BLECharacteristic::PROPERTY_NOTIFY
                      );
  pCharacteristicTX->addDescriptor(new BLE2902());

  // 受信用キャラクタリスティックを作成してシグナル受信時のコールバックを設定する
  pCharacteristicRX = pService->createCharacteristic(
                        CHARACTERISTIC_UUID_RX,
                        BLECharacteristic::PROPERTY_WRITE
                      );
  pCharacteristicRX->setCallbacks(new funcReceiveCallback());

  // サービスを開始して、SERVICE_UUIDでアドバタイジングを開始する
  pService->start();
  BLEAdvertising *pAdvertising = pServer->getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->start();
}

//----------------------------------------------------------
//    Set Up Function
//----------------------------------------------------------
void setup() {
  Serial.begin(115200);  //シリアル設定 
  BLE_Setup();           //BLEセットアップ
}

//----------------------------------------------------------
//    Main Loop Function
//----------------------------------------------------------
void loop() {
  // notifyしたいときはここに書く
  if (deviceConnected) {}
  
  //接続段時の処理
  if (!deviceConnected && oldDeviceConnected) {
    delay(500); // give the bluetooth stack the chance to get things ready
    pServer->startAdvertising(); // restart advertising
    //Serial.println("start advertising");
    oldDeviceConnected = deviceConnected;
  }
  //接続開始時の処理
  if (deviceConnected && !oldDeviceConnected) {
    oldDeviceConnected = deviceConnected;   // do stuff here on connecting
  }
  delay(100);
}

「都度部」と書かれたところに固有の処理を書くことで簡単にBLEデバイスを作れるように
しました。

具体的にはセントラルからデータが送信されると68行目のコールバック関数
が呼ばれ、データはrxValue[]に入っているのでこの値にその下の都度部に処理を書きます。

また、セントラルから読み出し指示があった時は84行目のonRead関数が呼ばれるので
pCharacteristicRX->setValue()にデータをセットすれば送信することができます。

本来は機能ごとにUUIDを振るのが正解っぽいですが送信データの先頭で処理を
場合分けすればいいかなと思ったのでUUIDは一つだけ使っています。



このプログラムでは単体テストできるようにセントラルから来たデータを
シリアルで書き出す処理、読み取り指示があった時は指定文字列を送信するコード
になっています。次にRaspberri Piから接続してデータをやり取りしてみます。


・Raspberry piから接続する

作ったデバイスにRaspberry Piから接続してみます。今回はテストなので
gattoolというソフトを使って直接値を見てみます。
まずはアドレスを調べます。

pi@raspberrypi:~ $ sudo hcitool lescan
LE Scan ...
3C:71:BF:F1:E6:7E (unknown)
3C:71:BF:F1:E6:7E ESP32_BLE_TEST
41:DD:FB:51:61:AB (unknown)
41:DD:FB:51:61:AB (unknown)

アドレスがわかったらCtrl+Cで止めてしまいましょう。私の場合3C:71:BF:F1:E6:7E
であることがわかります。ESP32側のプログラムと一致しますね。
gatttool -b アドレス -I でデバイスを指定しconnectで接続します。

pi@raspberrypi:~ $ gatttool -b 3C:71:BF:F1:E6:7E -I
[3C:71:BF:F1:E6:7E][LE]> connect
Attempting to connect to 3C:71:BF:F1:E6:7E
Connection successful

characteristicsでキャラクタリスティックを見つけましょう。

[3C:71:BF:F1:E6:7E][LE]> characteristics
handle: 0x0002, char properties: 0x20, char value handle: 0x0003, uuid: 00002a05-0000-1000-8000-00805f9b34fb
handle: 0x0015, char properties: 0x02, char value handle: 0x0016, uuid: 00002a00-0000-1000-8000-00805f9b34fb
handle: 0x0017, char properties: 0x02, char value handle: 0x0018, uuid: 00002a01-0000-1000-8000-00805f9b34fb
handle: 0x0019, char properties: 0x02, char value handle: 0x001a, uuid: 00002aa6-0000-1000-8000-00805f9b34fb
handle: 0x0029, char properties: 0x10, char value handle: 0x002a, uuid: e303cfe5-b463-4b82-bbc3-e0789945b499
handle: 0x002c, char properties: 0x08, char value handle: 0x002d, uuid: 9348db8a-7c61-4c6e-b12d-643625993b84

今回はCHARACTERISTIC_UUID_RXにアクセスしたいのでハンドルは0x002dになります。
このハンドルを使って通信をしてみましょう。char-read-hnd ハンドル で読み取り、
char-write-cmdでデータを送信します。

[3C:71:BF:F1:E6:7E][LE]> char-read-hnd 0x002d
Characteristic value/descriptor: 48 65 6c 6c 6f 20 77 6f 72 6c 64
[3C:71:BF:F1:E6:7E][LE]> char-write-cmd 0x002d 30313233



ArduinoIEDのシリアルモニタを見るとなんかでています。Raspberry Pi側は
16進数になっているのでわかりずらいですが、アスキーコードに直すとちゃんと
データがやり取りできていることがわかります。



これで無事通信できていることがわかりました。まだBLEについて理解できていない
部分があるので使い方が間違っているかもしれませんが、データを送受信できるように
なったのでとりあえず動かせるようになります。今回作ったプロトタイププログラム
に色々追加してホームオートメーション用BLEネットワークを構築していきたいと
思います。

参考にさせて頂いたサイト

2017年8月13日日曜日

Rasoberry Pi minidlnaを使ってPS4からアクセス

どうもcake tetuです.

先日PS4を購入しました.
PS4のメディアプレーヤーがなかなか使いやすそうだったので
Raspbery piにminidlnaをインストールしメディアサーバーにしてみます.

実は我が家には一応ファイルサーバーがあります.



画像からわかる通り初代Raspberry piを使用しています.
そのため,速度はかなり遅いです.
まあ自宅内でファイルの受け渡しぐらいしか使用してないので
あまり気にしませんが.

こいつにminidlnaをインストールします.
コチラを参考にさせて頂きました.

とりあえずテストということで公開フォルダはHDD上ではなくは
/home/pi上にしました.

PS4からアクセス!!



キターー!! 



適当な動画を再生してみます.
PS4のメディアプレーヤーはコントローラ―で操作しやすく,
とても扱いやすいです.これはなかなか便利.
そして,データベースを勝手に作ってくれるので,探す手間が省けます.
データベースは60sで更新になっていますが,サービスの再起動

$sudo service minidlna restart

で更新してくれる模様.


関係ない話ですが,Raspberry piでファイルサーバーは省エネで良いです.
しかし,LANが100base-TなのとUSB2.0しかないのでとても遅い.
本当は小型PCなんかでサーバを作りたいですが,金が…
金が貯まるまで頑張れ初代RaspPi.