C言語

ラズパイ(RaspberryPi)で、ライブラリを作成する方法

ラズパイ(RaspberryPi)で、ライブラリを作成する方法

スポンサーリンク

こんにちは!駆け出しエンジニアのまっきーです。

以前、DHT11温湿度センサーを用いて温度と湿度を測定するプログラムを作成しました。

温湿度を使用する場面はこれからいくつか出てくると思います。毎回毎回プログラムを書いてたら面倒くさくてたまらないですよね

そこで、温湿度のプログラムをすぐに使用できるようにライブラリというものを作成します。

温湿度測定のプログラムは「DHT11で温湿度測定(C言語コード解説)」に記載してあります。

ライブラリとは

main関数でプログラムが実行される際、必ずどこかに実態を持っています。普段何気なく使っているprintf,scanfなどもライブラリ関数の一つです。

これらは、標準ライブラリと呼ばれています。毎回毎回printfを一から作っていたらたまらないですよね。

このように、汎用性のある特定の機能群を他のプログラムから再利用できるようにまとめたもののことを「ライブラリ」といいます。

温湿度センサーも汎用性がありますよね。

ライブラリ

  • 静的ライブラリ
  • 共有ライブラリ

の2種類があります。

静的ライブラリ

コンパイル時にプログラムが内部にそのまま取り込んでしまう形式のライブラリです。ビルド時にプログラムに取り込まれるため、プログラム実行時にはもうすでに関数が存在している状態になるので、何も考えずにプログラムが使用できます。
半面ライブラリを丸ごと取り込むので、サイズが大きくなるというデメリットもあります。

共有ライブラリ

コンパイル時にプログラムがそのライブラリ情報だけを覚え、実行時に対象のファイルとの紐づけを行います。(ライブラリをリンクするといった呼ばれ方をします)

今回は「静的ライブラリ」を作成します

ライブラリ作成手順

使用するエディタは「Geany」を想定しています。

もちろん、他のエディタやターミナルからも実行できます。

以下、ライブラリの作成の手順です。

  1. ヘッダーファイルの作成
  2. ソースコードの作成
  3. ライブラリの作成
  4. エディタ設定

ライブラリ化したいソースコード

以前作成したコードを参考までに載せておきます。これをライブラリにしていきます。

#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#define MAXTIMINGS  85
int dht11_dat[5] = { 0, 0, 0, 0, 0 };

void read_dht11_dat(int DHTPIN)
{
  uint8_t laststate = HIGH;
  uint8_t counter   = 0;
  uint8_t j   = 0, i;
  float f; /* fahrenheit */

  dht11_dat[0] = dht11_dat[1] = dht11_dat[2] = dht11_dat[3] = dht11_dat[4] = 0;

  /* pull pin down for 18 milliseconds */
  pinMode( DHTPIN, OUTPUT );
  digitalWrite( DHTPIN, LOW );
  delay( 18 );
  /* then pull it up for 40 microseconds */
  digitalWrite( DHTPIN, HIGH );
  delayMicroseconds( 40 );
  /* prepare to read the pin */
  pinMode( DHTPIN, INPUT );

  /* detect change and read data */
  for ( i = 0; i < MAXTIMINGS; i++ )
  {
    counter = 0;
    while ( digitalRead( DHTPIN ) == laststate )
    {
      counter++;
      delayMicroseconds( 1 );
      if ( counter == 255 )
      {
        break;
      }
    }
    laststate = digitalRead( DHTPIN );

    if ( counter == 255 )
      break;

    /* ignore first 3 transitions */
    if ( (i >= 3) && (i % 2 == 0) )
    {
      /* shove each bit into the storage bytes */
      dht11_dat[j / 8] <<= 1;
      if ( counter > 30)
        dht11_dat[j / 8] |= 1;
      j++;
    }
  }

  /*
   * check we read 40 bits (8bit x 5 ) + verify checksum in the last byte
   * print it out if data is good
   */
  if ( (j >= 40) &&
       (dht11_dat[4] == ( (dht11_dat[0] + dht11_dat[1] + dht11_dat[2] + dht11_dat[3]) & 0xFF) ) )
  {
    f = dht11_dat[2] * 9. / 5. + 32;
    printf( "Humidity = %d.%d %% Temperature = %d.%d *C (%.1f *F)\n",
      dht11_dat[0], dht11_dat[1], dht11_dat[2], dht11_dat[3], f );
  }else  {
    printf( "Data not good, skip\n" );
  }
}

int main( void )
{
  printf( "Raspberry Pi wiringPi DHT11 Temperature test program\n" );

  if ( wiringPiSetup() == -1 )
    exit( 1 );

  while ( 1 )
  {
    read_dht11_dat(15);
    delay( 2000 ); 
  }

  return(0);
}

ヘッダーファイルの作成

以下、ファイルの保存先は全て同じディレクトリにしてください。

以下のソースコードのファイル名を 「dht11.h」として保存します。

//温湿度読み込み関数
extern void read_dht11_dat(int DHTPIN);

「extern宣言」は複数ファイルによるシステム構成において、グローバル変数を共有するための仕組みです。

ソースコードの作成

ファイル名を「dht11.c」として以下のコードを保存します。

基本的にはmain関数がないだけで、元のファイルと同じですが一つだけ加えます。

#include "dht11.h"

include文に作成したヘッダーファイルを追記します。

<>と””の記述は、ファイルを参照する深さが変わります。

  • 「<>」デフォルトのディレクトリ(usr/include)から参照
  • 「""」ソースコードの置かれているカレントディレクトリから参照
#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "dht11.h"
#define MAXTIMINGS  85
 

int dht11_dat[5] = { 0, 0, 0, 0, 0 };
void read_dht11_dat(int DHTPIN)
{
  uint8_t laststate = HIGH;
  uint8_t counter   = 0;
  uint8_t j   = 0, i;
  float f; /* fahrenheit */

  dht11_dat[0] = dht11_dat[1] = dht11_dat[2] = dht11_dat[3] = dht11_dat[4] = 0;

  /* pull pin down for 18 milliseconds */
  pinMode( DHTPIN, OUTPUT );
  digitalWrite( DHTPIN, LOW );
  delay( 18 );
  /* then pull it up for 40 microseconds */
  digitalWrite( DHTPIN, HIGH );
  delayMicroseconds( 40 );
  /* prepare to read the pin */
  pinMode( DHTPIN, INPUT );

  /* detect change and read data */
  for ( i = 0; i < MAXTIMINGS; i++ )
  {
    counter = 0;
    while ( digitalRead( DHTPIN ) == laststate )
    {
      counter++;
      delayMicroseconds( 1 );
      if ( counter == 255 )
      {
        break;
      }
    }
    laststate = digitalRead( DHTPIN );

    if ( counter == 255 )
      break;

    /* ignore first 3 transitions */
    if ( (i >= 3) && (i % 2 == 0) )
    {
      /* shove each bit into the storage bytes */
      dht11_dat[j / 8] <<= 1;
      if ( counter > 30
       )
        dht11_dat[j / 8] |= 1;
      j++;
    }
  }

  /*
   * check we read 40 bits (8bit x 5 ) + verify checksum in the last byte
   * print it out if data is good
   */
  if ( (j >= 40) &&
       (dht11_dat[4] == ( (dht11_dat[0] + dht11_dat[1] + dht11_dat[2] + dht11_dat[3]) & 0xFF) ) )
  {
    f = dht11_dat[2] * 9. / 5. + 32;
    printf( "Humidity = %d.%d %% Temperature = %d.%d *C (%.1f *F)\n",
      dht11_dat[0], dht11_dat[1], dht11_dat[2], dht11_dat[3], f );
  }else  {
    printf( "Data not good, skip\n" );
  }
}

ライブラリの作成

「dht11.h」「dht11.c」の二つのファイルを作成したら、準備は終わりです。

Geanyで、「dht11.c」をコンパイルし、オブジェクトファイル「dht11.o」を作成します。

Geanyでの操作手順

ターミナルで以下のように入力します。

$ ar rcs libdht11.a dht11.o

使用したコマンドの説明

ar書庫(アーカイブ)ファイルを作成するコマンド
rアーカイブにメンバを追加
cアーカイブの作成
sシンボルテーブルを作成

.aはライブラリファイルです。ライブラリファイルの先頭にはlibをつけます。

以上でライブラリファイルの作成は終わりです!

エディタ設定

上記の手順でビルドコマンドに「libdht11.a」を追加してください。

gcc -Wall -o"%e""%f" -lwiringPi -lpthread -g -o0 libdht11.a

これで"dht11.h"をinclude文で呼び出せばいつでも温湿度を計測できるようになりました。

実際に使ってみます!

自作ライブラリを使用したソースコード

#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "dht11.h"
#define MAXTIMINGS  85

int main( void ){
  printf( "Raspberry Pi wiringPi DHT11 Temperature test program\n" );
  if ( wiringPiSetup() == -1 )
    exit( 1 );
  while ( 1 )
  {
    read_dht11_dat(15);
    delay( 2000 ); 
  }
  return(0);
}

随分すっきりしたコードになりました!プログラムも問題なく動作しました。

おわりに

いかがでしたでしょうか?意外と少ない手順で実装できました。

LCD表示やA/Dコンバータのソースコードもライブラリとして使用すると楽に使用できそうです。

次回は、LCDと紐づけていきたいと思います!

ありがとうございました。

*追記(2020/9/19)

上記で作成したライブラリだと、温湿度のデータの受け渡しができなくなってしまうので少しコードを変更しました。以下の3点です。

  • 温湿度のデータを受け渡すため、引数にポインタを使用
  • データを取得できたか確認するために、戻り値を追加
  • ファイル名の変更

ファイル名は「dht11.c」「dht11.h」から「dht11Pi.c」「dhtPi.h」に変更しました。

以下、改善したソースコードです。変更点には日本語でコメントを入れてあります。

本記事の手順で再びライブラリ化していただけたらと思います。

#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "dht11Pi.h"//ヘッダーファイル名の変更
#define MAXTIMINGS  85
 
int read_dht11_dat(int DHTPIN,int *dht11_dat)
{
  uint8_t laststate = HIGH;
  uint8_t counter   = 0;
  uint8_t j   = 0, i;

  dht11_dat[0] = dht11_dat[1] = dht11_dat[2] = dht11_dat[3] = dht11_dat[4] = 0;

  /* pull pin down for 18 milliseconds */
  pinMode( DHTPIN, OUTPUT );
  digitalWrite( DHTPIN, LOW );
  delay( 18 );
  /* then pull it up for 40 microseconds */
  digitalWrite( DHTPIN, HIGH );
  delayMicroseconds( 40 );
  /* prepare to read the pin */
  pinMode( DHTPIN, INPUT );

  /* detect change and read data */
  for ( i = 0; i < MAXTIMINGS; i++ )
  {
    counter = 0;
    while ( digitalRead( DHTPIN ) == laststate )
    {
      counter++;
      delayMicroseconds( 1 );
      if ( counter == 255 )
      {
        break;
      }
    }
    laststate = digitalRead( DHTPIN );

    if ( counter == 255 )
      break;

    /* ignore first 3 transitions */
    if ( (i >= 3) && (i % 2 == 0) )
    {
      /* shove each bit into the storage bytes */
      dht11_dat[j / 8] <<= 1;
      if ( counter > 30
       )
        dht11_dat[j / 8] |= 1;
      j++;
    }
  }

  /*
   * check we read 40 bits (8bit x 5 ) + verify checksum in the last byte
   * print it out if data is good
   */
  if ( (j >= 40) &&
       (dht11_dat[4] == ( (dht11_dat[0] + dht11_dat[1] + dht11_dat[2] + dht11_dat[3]) & 0xFF) ) )
  {
    return 0;//データの整合性が取れた時、戻り値0を返す
  }else{
    return -1;//データの整合性が取れなかった時、戻り値-1を返す
  }
}

関数の仕様です。

read_dht11_dat
関数型int read_dht11_dat(int DHTPIN , int *dht11_dat)
引数ピン番号、温湿度データを格納する配列
戻り値0:データが正しく取得できた場合 -1:データが取得できなかった場合
機能温湿度を計測して配列に格納する

スポンサーリンク

-C言語