メニュー

マイコンで CO2 を計測し、サーバーに計測値を投げるシステムを作る

マイコンで CO2 を計測し、サーバーに計測値を投げるシステムを作る

エンジニアブログ

投稿日:2018/05/18

マイコンで CO2 を計測し、サーバーに計測値を投げるシステムを作る

こんにちは、小林さとる と申します。システムソリューション事業部でインターンシップに参加しております。 Raspberry Pi と Arduino UNO を使って、CO2 濃度を計測し、計測値を API 経由でサーバに投げるシステムを作りました。実装過程で得られた知識やコードをまとめつつ、作り方を説明した記事です。至らない点が多々あるかと思いますが、よろしくお願いいたします。

作ったもの

作ったもの

室内の CO2 濃度を1秒間に1度計測し、取得した値( ppm )をサーバーに投げるシステムです。 ppm が一定値を超えた時、段階的に警告音を鳴らします。
Arduino UNO と MG811 ( CO2 センサー) で ppm を計測し、シリアル通信で Raspberry Pi に計測値を投げます。
Raspberry Pi が受け取った値を API 経由でサーバに値を投げます。警告音は Raspberry Pi が鳴らします。 Raspberry Pi の OS には Android Things を用いました。

室内環境における CO2 濃度の基準値

ppm(二酸化炭素濃度) 概要
~300 通常の環境では起こらないほど低い。施設栽培では二酸化炭素飢餓が懸念される
360 一般大気濃度
400~600 市街地外気
700 多人数、長時間在室の場合の許容濃度
1000 ビル管理法等の許容基準濃度。これ以上は眠気が襲ってくる
1500 学校環境衛生の基準値(これ以下が望ましい)
2000 眠くなる人が多くなるなど体調の変化と空調に苦情が出てくる
2500 思考能力が明確に低下する
3000 肩こり、頭痛やめめいを感じる人が出るなど健康被害一歩前
5000 長期安全限界濃度

※ 上記の「室内環境における CO2 濃度の基準値」の表は以下の記事より引用しています。

必要なもの

  • Raspberry Pi 3 Model B
  • Micro SD カード(8 GB 以上)
  • Micro USB ケーブル
  • Micro USB 端子から 5V 2.5A 以上( 3A ならなお良し)が供給できる電源アダプタ
  • Arduino UNO
  • USB ケーブル(A-Bタイプ)
  • USB スピーカー
  • 自作 6V 電源
  • MG-811
  • ブレッドボード
  • ジャンパー線(オス~オス) × 3

最初だけ必要なもの

  • ラズパイ用のディスプレイタッチパネル
  • SD カードリーダー

ラズパイ用のディスプレイタッチパネルがない場合は、以下を用意してください。

  • HDMI ケーブル
  • モニター
  • USB キーボード
  • USB マウス

ハード構成図

ハード構成図

1. Arduino の開発環境を構築

  1. Arduino の IDE をインストールする
    Arduino の公式サイトから IDE をダウンロードします。
    ダウンロードが完了したら、以下のリンクを参考にインストールをします。

    1. Arduino software (公式サイト)
    2. Arduino IDE インストール手順
  2. IDE の起動準備
    Arduino UNO と PC を USB ケーブル( A-B タイプ)で接続し、 Arduino IDE を起動します。
  3. IDE に Arduino UNO を認識させる
    「ツール」メニューから「シリアルポート」を開きます。
    次に「 /dev/cu.usbmodem 」または、「 /dev/tty.usbmodem 」で始まる項目を選択します。「ツール」メニューから「ボード」を開き「 Arduino/Genuino Uno 」を選択します。
    画像

2. Arduino UNO と MG811 で CO2 を計測する

  1. Arduino UNO と MG811 を接続する
    MG811 と 6V 電源、 Arduino UNO を以下の図のようにつなぎます。
    画像
  2. Arduino IDE に MG811 のライブラリを入れる
    下記 URL のプロジェクトからダウンロードか git clone を行い、「 CO2Sensor 」ディレクトリを手に入れます。

    1. GitHub solvek/CO2Sensor

    手に入れた「 CO2Sensor 」を Arduino IDE の「 libraries 」のディレクトリに格納します。 mac の場合 書類( documents )/Arduino/libraries にディレクトリが自動で生成されています。

  3. Arduino UNO にプログラムを書き込む
    下記のコードを貼り付けて、「検証(コンパイル)」「マイコンボードに書き込む」を順番に実行して完了です。「シリアルモニター」を起動すると取得した ppm の値を確認できます。

    #include "CO2Sensor.h"
    
    CO2Sensor co2Sensor(A0, 0.99, 100);
    int analyzeTimes = 0;
    
    void setup() {
    Serial.begin(9600);
    co2Sensor.calibrate();
    }
    
    void loop() {
    int ppm = co2Sensor.read();
    Serial.println(ppm);//シリアル通信で値を投げる
    delay(1000);
    analyzeTimes += 1;
      //計測値が徐々に下がり,6時間程で0になる.対策として定期的にキャリブレーションを行う.てきとうに1800回目(約30分)に1回行う.
    if(analyzeTimes >= 1800){
        co2Sensor.calibrate();
        analyzeTimes = 0;
    }
    }
    

    画像

3. Raspberry Pi に Android Things をインストールする

下記の記事を参考にして Raspberry Pi に Android Things をインストールしてください。

インストールの手順は以下の通りです。

  1. 公式サイトで Android Things のシステムイメージを作成、ダウンロードする
    公式サイト Android Things のコンソール画面を開く。(コンソール画面を開くには Google アカウントを所有している必要性があります)

    1. android things (公式サイト)

    画面を開いたら「 ADD A PRODUCT 」を押してください。開かれた画面で必要な項目を入力しシステムイメージを作成します。

    1. Product name
      適当な名前をつけます。
    2. SOM type
      「 RaspberryPi 3」を選択します。
    3. Include these Google services
      チェックをつけておきます。Google のクラウドサービスや API を使うのかどうかを聞いています。今回は使いませんが、拡張性を考慮します。
    4. OEM partition size
      拡張性を考慮して 512 MB にします。
      パーティションのサイズはあとで変更ができない要素です。あとは指示にしたがってシステムイメージをダウンロードしてください。
  2. microSD カードにシステムイメージを書き込む
    SD カードリーダーを使って microSD に img ファイルを書き込んでください。

  3. microSD カードを Raspberry Pi に挿入する

4. Android Things を WiFi に繋ぐ

Android Things は WiFi を経由して AndroidStudio(開発環境) に繋ぐので、 WiFi に接続する必要があります。
Android Things を操作するために以下の機器を Raspberry Pi に接続します。

  • ラズパイ用のディスプレイタッチパネル
  • Micro USB ケーブル
  • Micro USB 端子から 5V 2.5A 以上( 3A ならなお良し)が供給できる電源アダプタ

ラズパイ用のディスプレイタッチパネルがない場合は、以下を使用してください。

  • HDMIケーブル
  • モニター
  • USBキーボード
  • USBマウス

電源を接続すると以下のような画面が出てきます。左上のハンバーガーメニュー → Network → WiFi の順に押し WiFi の接続設定を行います。開発用の端末と同じ WiFi に接続してください。

画像

画像

接続設定が終わると画像の赤線部分に端末の IP アドレスが表示されます。これをコピーしておきます。

画像

5. Android Things の開発環境を構築

下記の記事を参考に開発環境を整えてください。

記事内のうち以下の2つができていれば問題ありません。

  1. Android Studio のインストール
  2. Android SDK を導入して adb コマンドのパスを通す

先ほど取得した IP アドレスを使って PC から Raspberry Pi に接続します。

adb connect {IPアドレス}

もしくは

adb connect android.local

で接続できます。接続すると下記の画像のようにデバッグの端末一覧に表示されます。

画像

6. Android Things のアプリを作成

以下の機能を有した Android Things のアプリを Java で作成します。

  1. Arduino UNO からシリアル通信で ppm の値を受け取る
  2. 受け取った ppm を http 通信で API に投げる
  3. ppm が基準値を超えた時に警告音を鳴らす

1. Arduino UNO からシリアル通信で ppm の値を受け取る

Serial.println(ppm);//シリアル通信で値を投げる
上記のシリアル通信で送られてきた ppm の値を受け取る Android アプリを Android Studio で作ります。

  1. Android のプロジェクトを作成
    1. NEW project を選択し、 Application name を入力する
      私は「 CO2sensor 」と入力しました。

    2. Target Android Device の項目で 「 Android Things 」を選択

      画像

    3. Add an Activity to Things の項目で「 Android Things Empty Activity 」を選択

      画像

    4. Activity Name はそのままで Finish

  2. ライブラリのダウンロード
    USB 接続した Arduino を Android に認識させて、シリアル通信を行う Google 公式のライブラリを導入します。下記 URL のページ内にある「 usb-serial-for-android-v010.jar 」のリンクから jar ファイルをダウンロードします。

    1. GitHub mik3y/usb-serial-for-android/releases
  3. ライブラリをディレクトリに入れる
    手に入れた「 usb-serial-for-android-v010.jar 」ファイルを Android プロジェクト内の「 app/libs 」ディレクトリに格納します。

    画像

  4. ライブラリを gradle で導入
    「 build.gradle( Module.app ) 」の「 dependencies 」に下記のように追記して、 Sync Now してください。

    dependencies {
      compile fileTree(include:['usb-serial-for-android-v010.jar'], dir: 'libs')
    }
    
  5. Android が USB 接続されたときに通知を受ける
    USB デバイス(今回は Arduino UNO )が Android デバイスに接続されたときの通知を許可するため「 AndroidManifest.xml 」の「 internt-filter 」に以下のように追記してください。

    <activity launchMode="singleTop"...>...
    <intent-filter>
      <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/>
    </intent-filter>
    
    <meta-data
        android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
        android:resource="@xml/device_filter"/>
    </activity>
    
  6. USB で通信するデバイスを Arduino UNO に制限する
    Arduino UNO が接続されているときにのみ通知を受け、他のすべての USB デバイスを無視したいので、通信する端末を Arduino UNO に制限します。「 res/xml/device_filter.xml 」を作成し、以下のように記述します。

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
      <usb-device vendor-id="9025"/>
    </resources>
    

    “9025”が Arduino UNO の ベンダー ID です。10進数で表現されています。 下記の adb コマンドで Android に USB 接続されているベンダー ID を確認することができます。

    $ adb shell dmes
    New USB device found, idVendor=2341, idProduct=0001
    New USB device strings: Mfr=1, Product=2, SerialNumber=220
    Product: Arduino Uno
    Manufacturer: Arduino
    

    注意点としてこちらの「 idVendor=2341 」は16進数で表現されています。

  7. シリアル通信で送られてきた値を受け取るコードを書く

    public class MainActivity extends Activity {
      UsbManager usbManager;
      UsbSerialDriver usb;
      TextView altitudeStatusText;
      String ppm = "1";
    
      public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
    
          altitudeStatusText = findViewById(R.id.ppmStatusView);
    
          usbManager = getSystemService(UsbManager.class);
          usb = UsbSerialProber.acquire(usbManager);
    
          if (usb != null) {
              try{
                  usb.open();
                  usb.setBaudRate(9600);
                  // シリアル通信を読むスレッドを起動
                  StartReadPpm();
              }
              catch(IOException e){
                  e.printStackTrace();
              }
          }
      }
      public void StartReadPpm() {
          new Thread(new Runnable(){
              public void run(){
                  try{
                      while(true){
                          byte buf[] = new byte[256];
                          int num = usb.read(buf, buf.length);
                          if(num > 0) {
                              ppm = new String(buf, 0, num).trim();
                          }
                          //Arduinoから受信した値は欠けていることがある.3桁以上ならほぼ正常であるのでその値のみを扱う.
                          if(ppm.length() > 2) {
                              //Arduinoから受信した値をlogcat出力
                              Log.v("arduino", ppm);
                          }
                          Thread.sleep(1000);
                      }
                  }
                  catch(IOException e){
                      e.printStackTrace();
                  }
                  catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
          }).start();
      }
    }
    

2. 受け取った ppm を http 通信で API に投げる

  1. 「 StartReadPpm 」メソッドに下記のようにコードを追記する
    if(ppm.length() > 2) {
      //Arduinoから受信した値をlogcat出力
      Log.v("arduino", ppm);
      // Handlerを使用してメイン(UI)スレッドに処理を依頼する
      handler.post(new Runnable() {
          @Override
          public void run() {
              altitudeStatusText.setText(ppm + "ppm");
              int ppmInt = Integer.parseInt(ppm);
              SendPpmRequestToApi(ppmInt);
          }
      });
    }
    
  2. ppm を API に送るメソッド「 SendPpmRequestToApi 」を作る
    public void SendPpmRequestToApi(final int ppm){
      @SuppressLint("StaticFieldLeak") AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
          @Override
          protected void onPreExecute() {
          }
    
          @Override
          protected Void doInBackground(Void... params) {
              HttpURLConnection connection = null;
              URL url = null;
              //Parameters
              String deviceId = "xxxxx";
              String key = "xxxxx";
              String endPointUrl="xxxxx";
              String urlSt = endPointUrl + "/xxx/" + deviceId + "?key=" + key;
              HashMap<String, Object> jsonMap = new HashMap<>();
              jsonMap.put("co2",ppm);
    
              try {
                  //URLの作成
                  url = new URL(urlSt);
                  // 接続用HttpURLConnectionオブジェクト作成
                  connection = (HttpURLConnection) url.openConnection();
                  //接続タイムアウトを設定する.
                  connection.setConnectTimeout(100000);
                  //レスポンスデータ読み取りタイムアウトを設定する.
                  connection.setReadTimeout(100000);
                  //ヘッダーにAccept-Languageを設定する.
                  connection.setRequestProperty("Accept-Language", Locale.getDefault().toString());
                  //ヘッダーにContent-Typeを設定する
                  connection.addRequestProperty("Content-Type", "application/json; charset=UTF-8");
                  // リクエストメソッドの設定
                  connection.setRequestMethod("POST");
                  // リダイレクトを自動で許可しない設定
                  connection.setInstanceFollowRedirects(false);
                  // URL接続からデータを読み取る場合はtrue
                  connection.setDoInput(true);
                  // URL接続にデータを書き込む場合はtrue
                  connection.setDoOutput(true);
                  // 接続
                  connection.connect();
                  //リクエストボディの書き出しを行う.
                  OutputStream outputStream = connection.getOutputStream();
                  if (jsonMap.size() > 0) {
                      //JSON形式の文字列に変換する.
                      JSONObject responseJsonObject = new JSONObject(jsonMap);
                      String jsonText = responseJsonObject.toString();
                      PrintStream printStreams = new PrintStream(connection.getOutputStream());
                      printStreams.print(jsonText);
                      printStreams.close();
                  }
                  outputStream.close();
                  // レスポンスコードの取得
                  int code = connection.getResponseCode();
                  //Log.d("レスポンスコードは", code + "だよ?");
                  if(code==204){
                      String ppmStr = Integer.toString(ppm);
                      Log.d("送信成功", ppmStr);
                  }
              } catch (IOException e) {
                  e.printStackTrace();
              } finally {
                  if (connection != null) {
                      //7.コネクションを閉じる.
                      connection.disconnect();
                  }
              }
              return null;
          }
          @Override
          protected void onPostExecute(Void result) {
          }
      };
      // パラメータを渡す
      task.execute();
    }
    

3. ppm が基準値を超えた時に警告音を鳴らす

  1. 警告の仕様
    毎秒取得している ppm が基準値を超えた場合に警告音を鳴らします。基準値は4段階 700、1000、2000、3000 ppm です。警告音は再生が終わるまで割り込みは発生しません。警告音再生後、同じ種類もしくは基準値がより小さい警告音は5分間鳴らない仕様です。

  2. mp3 ファイルの作成
    適当な警告音の mp3 ファイル が無い場合は「 Sound of Text 」を使います。 Google 翻訳で流れる音声を MP3 で保存できる Web サイトです。ここで4種類の警告音を作成します。私はファイル名を「 [基準値] ppmAlarm.mp3 」としました。

  3. mp3 ファイルの格納
    手に入れた mp3 ファイルを Android プロジェクト内の「 app/src/main/assets/sound 」ディレクトリに格納します。
    画像

  4. MediaPlayer インターフェイスを実装する
    音源の再生に MediaPlayer を用います。 MainActivity.java に下記のようにコードを追記してください。

    public class MainActivity extends Activity implements MediaPlayer.OnCompletionListener {
    
  5. 「 StartReadPpm 」メソッドに下記のようにコードを追記する
    // Handlerを使用してメイン(UI)スレッドに処理を依頼する
    handler.post(new Runnable() {
     @Override
     public void run() {
         altitudeStatusText.setText(ppm + "ppm");
         int ppmInt = Integer.parseInt(ppm);
         SendPpmRequestToApi(ppmInt);
         if(ppmInt > 700){
             if(WhetherAnAlarmIsBeingPlayed == false) {
                 try {
                     SoundTheAlarm(ppmInt);
                 } catch (IOException e) {
                     e.printStackTrace();
                 }
             }
             if(snoozeResetCountTimeflag == false){
                 TimeSnoozeReset();
                 snoozeResetCountTimeflag = true;
             }
         }
     }
    
  6. 警告音を鳴らすメソッド「 SoundTheAlarm 」を作る
    MediaPlayer mediaPlayer = new MediaPlayer();
    boolean snoozeResetCountTimeflag = false;
    boolean snoozeFlag3000ppmAlarm = false;
    boolean snoozeFlag2000ppmAlarm = false;
    boolean snoozeFlag1000ppmAlarm = false;
    boolean snoozeFlag700ppmAlarm = false;
    boolean WhetherAnAlarmIsBeingPlayed = false;
    public void SoundTheAlarm (int ppm) throws IOException {
          String fileName = null;
          if (ppm > 3000 && snoozeFlag3000ppmAlarm == false) {
              snoozeFlag3000ppmAlarm = true;
              snoozeFlag2000ppmAlarm = true;
              snoozeFlag1000ppmAlarm = true;
              snoozeFlag700ppmAlarm = true;
              fileName = "3000ppmAlarm.mp3";
          } else if (ppm <= 3000 && ppm > 2000 && snoozeFlag2000ppmAlarm == false) {
              snoozeFlag2000ppmAlarm = true;
              snoozeFlag1000ppmAlarm = true;
              snoozeFlag700ppmAlarm = true;
              fileName = "2000ppmAlarm.mp3";
          } else if (ppm <= 2000 && ppm > 1000 && snoozeFlag1000ppmAlarm == false) {
              snoozeFlag1000ppmAlarm = true;
              snoozeFlag700ppmAlarm = true;
              fileName = "1000ppmAlarm.mp3";
          } else if (ppm <= 1000 && ppm > 700 && snoozeFlag700ppmAlarm == false) {
              snoozeFlag700ppmAlarm = true;
              fileName = "700ppmAlarm.mp3";
          }
      if(fileName != null) {
          AssetFileDescriptor afdescripter = getAssets().openFd("sound/" + fileName);
          try {
              mediaPlayer.setDataSource(afdescripter.getFileDescriptor(),
                      afdescripter.getStartOffset(), afdescripter.getLength());
          } catch (IOException e) {
              e.printStackTrace();
          }
          mediaPlayer.setOnCompletionListener(this);
          // 再生準備,再生可能状態になるまでブロック
          mediaPlayer.prepare();
          // 再生開始
          mediaPlayer.start();
          Log.d(TAG, "再生開始");
          WhetherAnAlarmIsBeingPlayed = true;//再生中
      }else{
          Log.d(TAG,"スヌーズ中");
      }
    }
    public void TimeSnoozeReset ()throws ParseException {
      final Timer timer = new Timer(false);
      TimerTask task = new TimerTask() {
          @Override
          public void run() {
              SnoozeReset();
              timer.cancel();
          }
      };
      Log.d(TAG,"同時種類のアラームは最初のアラームがなってから5分間再生できない.SnoozeResetまでのカウント開始" );
      timer.schedule(task, 300000);//300000,5分間
    }
    

7. 完成したコード

public class MainActivity extends Activity implements MediaPlayer.OnCompletionListener {
    UsbManager usbManager;
    UsbSerialDriver usb;
    TextView altitudeStatusText;
    String ppm = "1";
    MediaPlayer mediaPlayer = new MediaPlayer();

    boolean snoozeResetCountTimeflag = false;
    boolean snoozeFlag3000ppmAlarm = false;
    boolean snoozeFlag2000ppmAlarm = false;
    boolean snoozeFlag1000ppmAlarm = false;
    boolean snoozeFlag700ppmAlarm = false;
    boolean WhetherAnAlarmIsBeingPlayed = false;

    final Handler handler = new Handler();
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        altitudeStatusText = findViewById(R.id.ppmStatusView);

        usbManager = getSystemService(UsbManager.class);
        usb = UsbSerialProber.acquire(usbManager);

        if (usb != null) {
            try{
                usb.open();
                usb.setBaudRate(9600);
                // シリアル通信を読むスレッドを起動
                StartReadPpm();
            }
            catch(IOException e){
                e.printStackTrace();
            }
        }
    }
    public void StartReadPpm() {
        new Thread(new Runnable(){
            public void run(){
                try{
                    while(true){
                        byte buf[] = new byte[256];
                        int num = usb.read(buf, buf.length);
                        if(num > 0) {
                            ppm = new String(buf, 0, num).trim();
                        }
                        //Arduinoから受信した値は欠けていることがある.3桁以上ならほぼ正常であるのでその値のみを扱う.
                        if(ppm.length() > 2) {
                            //Arduinoから受信した値をlogcat出力
                            Log.v("arduino", ppm);
                            // Handlerを使用してメイン(UI)スレッドに処理を依頼する
                            handler.post(new Runnable() {
                                @Override
                                public void run() {
                                    altitudeStatusText.setText(ppm + "ppm");
                                    int ppmInt = Integer.parseInt(ppm);
                                    SendPpmRequestToApi(ppmInt);
                                    if(ppmInt > 700){
                                        if(WhetherAnAlarmIsBeingPlayed == false) {
                                            try {
                                                SoundTheAlarm(ppmInt);
                                            } catch (IOException e) {
                                                e.printStackTrace();
                                            }
                                        }
                                        if(snoozeResetCountTimeflag == false){
                                            TimeSnoozeReset();
                                            snoozeResetCountTimeflag = true;
                                        }
                                    }
                                }
                            });
                        }
                        Thread.sleep(1000);
                    }
                }
                catch(IOException e){
                    e.printStackTrace();
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
    public void SendPpmRequestToApi(final int ppm){
        @SuppressLint("StaticFieldLeak") AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
            @Override
            protected void onPreExecute() {
            }

            @Override
            protected Void doInBackground(Void... params) {
                HttpURLConnection connection = null;
                URL url = null;
                //Parameters
                String deviceId = "xxxxx";
                String key = "xxxxx";
                String endPointUrl="xxxxx";
                String urlSt = endPointUrl + "/xxxxx/" + deviceId + "?key=" + key;
                HashMap<String, Object> jsonMap = new HashMap<>();
                jsonMap.put("co2",ppm);

                try {
                    //URLの作成
                    url = new URL(urlSt);
                    // 接続用HttpURLConnectionオブジェクト作成
                    connection = (HttpURLConnection) url.openConnection();
                    //接続タイムアウトを設定する.
                    connection.setConnectTimeout(100000);
                    //レスポンスデータ読み取りタイムアウトを設定する.
                    connection.setReadTimeout(100000);
                    //ヘッダーにAccept-Languageを設定する.
                    connection.setRequestProperty("Accept-Language", Locale.getDefault().toString());
                    //ヘッダーにContent-Typeを設定する
                    connection.addRequestProperty("Content-Type", "application/json; charset=UTF-8");
                    // リクエストメソッドの設定
                    connection.setRequestMethod("POST");
                    // リダイレクトを自動で許可しない設定
                    connection.setInstanceFollowRedirects(false);
                    // URL接続からデータを読み取る場合はtrue
                    connection.setDoInput(true);
                    // URL接続にデータを書き込む場合はtrue
                    connection.setDoOutput(true);
                    // 接続
                    connection.connect();
                    //リクエストボディの書き出しを行う.
                    OutputStream outputStream = connection.getOutputStream();
                    if (jsonMap.size() > 0) {
                        //JSON形式の文字列に変換する.
                        JSONObject responseJsonObject = new JSONObject(jsonMap);
                        String jsonText = responseJsonObject.toString();
                        PrintStream printStreams = new PrintStream(connection.getOutputStream());
                        printStreams.print(jsonText);
                        printStreams.close();
                    }
                    outputStream.close();
                    // レスポンスコードの取得
                    int code = connection.getResponseCode();
                    //Log.d("レスポンスコードは", code + "だよ?");
                    if(code==204){
                        String ppmStr = Integer.toString(ppm);
                        Log.d("送信成功", ppmStr);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (connection != null) {
                        //7.コネクションを閉じる.
                        connection.disconnect();
                    }
                }
                return null;
            }
            @Override
            protected void onPostExecute(Void result) {
            }
        };
        // パラメータを渡す
        task.execute();
    }
    public void SoundTheAlarm (int ppm) throws IOException {
            String fileName = null;
            if (ppm > 3000 && snoozeFlag3000ppmAlarm == false) {
                snoozeFlag3000ppmAlarm = true;
                snoozeFlag2000ppmAlarm = true;
                snoozeFlag1000ppmAlarm = true;
                snoozeFlag700ppmAlarm = true;
                fileName = "3000ppmAlarm.mp3";
            } else if (ppm <= 3000 && ppm > 2000 && snoozeFlag2000ppmAlarm == false) {
                snoozeFlag2000ppmAlarm = true;
                snoozeFlag1000ppmAlarm = true;
                snoozeFlag700ppmAlarm = true;
                fileName = "2000ppmAlarm.mp3";
            } else if (ppm <= 2000 && ppm > 1000 && snoozeFlag1000ppmAlarm == false) {
                snoozeFlag1000ppmAlarm = true;
                snoozeFlag700ppmAlarm = true;
                fileName = "1000ppmAlarm.mp3";
            } else if (ppm <= 1000 && ppm > 700 && snoozeFlag700ppmAlarm == false) {
                snoozeFlag700ppmAlarm = true;
                fileName = "700ppmAlarm.mp3";
            }
        if(fileName != null) {
            AssetFileDescriptor afdescripter = getAssets().openFd("sound/" + fileName);
            try {
                mediaPlayer.setDataSource(afdescripter.getFileDescriptor(),
                        afdescripter.getStartOffset(), afdescripter.getLength());
            } catch (IOException e) {
                e.printStackTrace();
            }
            mediaPlayer.setOnCompletionListener(this);
            // 再生準備,再生可能状態になるまでブロック
            mediaPlayer.prepare();
            // 再生開始
            mediaPlayer.start();
            Log.d(TAG, "再生開始");
            WhetherAnAlarmIsBeingPlayed = true;//再生中
        }else{
            Log.d(TAG,"スヌーズ中");
        }
    }
    public void TimeSnoozeReset ()throws ParseException {
        final Timer timer = new Timer(false);
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                SnoozeReset();
                timer.cancel();
            }
        };
        Log.d(TAG,"同時種類のアラームは最初のアラームがなってから5分間再生できない.SnoozeResetまでのカウント開始" );
        timer.schedule(task, 300000);//300000,5分間
    }
    //最初のアラームがなってから5分後に実行されるメソッド
    public void SnoozeReset () {
        snoozeResetCountTimeflag = false;
        snoozeFlag3000ppmAlarm = false;
        snoozeFlag2000ppmAlarm = false;
        snoozeFlag1000ppmAlarm = false;
        snoozeFlag700ppmAlarm = false;
        Log.d(TAG,"SnoozeResetを実行しました" );
    }

    @Override
    public void onCompletion(MediaPlayer mp) {
        // リソースの解放
        mediaPlayer.release();
        mediaPlayer = new MediaPlayer();//初期化
        WhetherAnAlarmIsBeingPlayed = false;//再生終了
        Log.d(TAG,"再生終了" );
    }
}

Android Things にアプリを入れた後の注意点

Android Things にアプリをいれた状態だと新しい WiFi の接続設定が行えません。ラズパイに電源を入れると強制的にアプリが起動するからです。現在接続している WiFi から離れて開発を行いたい場合は、「 adb uninstall {アプリ名}」等のコマンドでアプリをアンインストールしておくか、持ち運べるモバイル WiFi やスマホのテザリング WiFi に接続しておいてください。アプリ内のアプリを終了させる機能( finishAndRemoveTask()、 finish() )を実行しても、すぐにアプリが再起動する仕様になっており、設定画面を開くことができません。

8. まとめ

ラズパイ、 Arduino 共に初めてで回路設計などの知識もなかったため、詰まる場面が多々ありましたが無事すべて解決しました。特に MG811 の取得値が下がっていき、6時間ほどで0になる現象には苦労しました。ライブラリの中身をよく読み、値の変化をトレースしたのは初めての経験で、解決方法がわかった時はひたすら嬉しかったです。もう一度似たようなものを作るなら、もっといい構成が思いつくので、この知識を活かして今後も電子工作に挑戦してみたいです。トップゲートの業務のおかげで新しく熱中できるものが見つけられました。マイコンは楽しいです。貴重な機会を設けていただいた会社と社員の皆様には感謝しきれません。重ねてになりますが、ありがとうございました。

PAGE TOP