【Raspberry Pi】温湿度を計測するサーバーの作成方法

この記事ではラズパイと接続した温湿度センサー(BME280)を使って、温湿度を公開するサーバーを構築します。

ちなみに下記順で見ると、BME280の回路接続方法、温湿度サーバーの作成方法やクライアントアプリの作成方法といった、一通りのことがわかりますので、時間があれば最初からどうぞ。

目次

温湿度センサーのサーバー構築

温湿度の取得は出来ているので、今回はそのコードをサーバー化するだけになります。サーバー化すると聞くと難しく感じるかもしれませんが、意外と簡単にできます。

作成したコードは下記に格納しているので、解説と合わせて見ると分かりやすいかもしれません。

このリポジトリ内の"example/server/single_connection/single_server.c"が今回解説するコードにあたります。

プログラムの解説

サーバー化するに当たって便利なツールがあります。それはソケットAPIというものです。このAPIを使うと指定したポートで待ち受けて(つまりサーバー)、クライアントからの要求を受け取るというプログラムが簡単に作成できます。

コードのすべてを事細かに解説すると長くなるので、ソケットAPIを使ったサーバープログラムの要点を解説します。

ソケットを使ったサーバープログラムの流れ

ソケットAPIを使ってサーバープログラムを作成する場合、下記順序でAPIを呼び出します。

  1. socket()
  2. bind()
  3. listen()
  4. accept()
  5. recv()/send()
  6. close()

それぞれの関数の内容を簡単に説明します。

ソケットAPIの解説
socket()

ソケットの生成を行います。
「今からソケット使うからリソースちょうだい!」とお願いしている感じ。

bind()

生成したソケットにアドレスやポートといった情報を割り当てます。
「生成したソケットはこのアドレスとポートで使うよ!」と宣言している感じ。

listen()

生成したソケットを使って接続準備を行います。また、受け付けるキューの数などもセットします。
「このソケットを使って接続状態にしてね!」とお願いしてる感じ。

accept()

接続しているソケットに接続要求が入ってくるのを待ちます。そのため、クライアントから接続要求が来るまでプログラムはここで待ち続けます(つまり、プログラムはここで止まる)。
「接続要求が来るまで待っててね!でも接続したら教えてよ!」とお願いしている感じ。

recv()/send()

これは名前の通りで、接続したクライアントと受信/送信を行います。接続を切るまでrecv()/send()を使って双方向に情報通信できますよ。

close()

使わなくなったソケットを破棄します。
「もう使わなくなったからリソースを解放しておいて!」とお願いしている感じ。

実際にサンプルコードを見ると、この流れに沿ってプログラムを作成していることが分かると思います。(見やすくするためサンプルコードからエラー処理などを少し省いています)

    servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP

    memset(&servSockAddr, 0, sizeof(servSockAddr));
    servSockAddr.sin_family      = AF_INET;
    servSockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servSockAddr.sin_port        = htons(SERVER_PORT);

    bind(servSock, (struct sockaddr *) &servSockAddr, sizeof(servSockAddr))

    listen(servSock, QUEUELIMIT)

    clitLen = sizeof(clitSockAddr);
    clitSock = accept(servSock, (struct sockaddr *)&clitSockAddr, &clitLen)

    while(1)
    {
        memset(buf, 0, sizeof(BUFSIZE));
        if ((recvMsgSize = recv(clitSock, buf, BUFSIZE, 0)) < 0)
        {
            fprintf(stderr, "Failed recv().\n");
            break;
        }
        else if(recvMsgSize == 0)
        {
            fprintf(stderr, "connection closed by foreign host.\n");
            break;
        }

        get_temp(&comp_data) == 0
        memset(buf, 0, sizeof(BUFSIZE));
        sprintf(buf, "%0.2lf deg C, %0.2lf hPa, %0.2lf%%\n", comp_data.temperature, comp_data.pressure * 0.01, comp_data.humidity);

        if ((sendMsgSize = send(clitSock, buf, strlen(buf), 0)) < 0)
        {
            fprintf(stderr, "Failed send().\n");
            break;
        }
        else if(sendMsgSize == 0)
        {
            fprintf(stderr, "connection closed by foreign host.\n");
            break;
        }
    }

LABEL_END:
    close(servSock);

プログラムの動作確認

まずはBME280の操作プログラムと、サーバー化したサンプルコードをダウンロードします。

git clone https://github.com/radical-kei/BME280.git

コードの取得ができたら、下記コマンドを実行してディレクトリを移動します。

cd BME280/example/server/single_connection

ディレクトリ移動が済んだら、下記コマンドを実行して実行ファイルを生成します。(CmakeLists.txtは以前に紹介したクロスコンパイラ環境でコンパイルするようセットしています。コンパイル出来ない場合は自身の環境に合わせてコンパイラをCmakeLists.txtにセットしてください)

mkdir build
cd build
cmake ..
make

"temp_single_server"という実行体が生成されるので、ラズパイに転送してください。転送が終わったらラズパイのターミナルを開いて"temp_signal_service"を実行します。実行するとこんな感じで待ち状態に入ります。

pi@pizero:~ $ ./temp_single_server 
▋

あとは他のPCからnetcatコマンドを使って、実際に温湿度が取得出来るか確認します。WSL上のUbuntuやMacのターミナルを開いて下記コマンドを実行します。

nc <ラズパイのホスト名 or IPアドレス> <ポート番号>

サンプルコードはポート番号"50000"を開いているので"50000"としてください。

下記のように動作するはずです。

左側がMacのターミナルで、右側がラズパイのターミナルです。Macのターミナルでreturnを入力するたびに温湿度が取得できています。

最後にMacのターミナルでControlCで抜けると、サーバー側も終了しています。

まとめ

とても簡単にプログラムをサーバー化することができました。しかし、まだまだ課題はあります。

  • サーバーのコネクションが1回きり
  • 1台しかサーバーにコネクションできない
    など

次回は複数のクライアントからアクセスされても、それぞれに温湿度の情報が渡せるように改良します。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次