logo
最近の検索
マイコンで CO2 を計測し、サーバーに計測値を投げるシステムを作る
2022.05.06

マイコンで 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になる現象には苦労しました。ライブラリの中身をよく読み、値の変化をトレースしたのは初めての経験で、解決方法がわかった時はひたすら嬉しかったです。もう一度似たようなものを作るなら、もっといい構成が思いつくので、この知識を活かして今後も電子工作に挑戦してみたいです。トップゲートの業務のおかげで新しく熱中できるものが見つけられました。マイコンは楽しいです。貴重な機会を設けていただいた会社と社員の皆様には感謝しきれません。重ねてになりますが、ありがとうございました。



弊社トップゲートでは、Google Cloud (GCP) 利用料3%OFFや支払代行手数料無料、請求書払い可能などGoogle Cloud (GCP)をお得に便利に利用できます。さらに専門的な知見を活かし、

など幅広くあなたのビジネスを加速させるためにサポートをワンストップで対応することが可能です。

Google Workspace(旧G Suite)に関しても、実績に裏付けられた技術力やさまざまな導入支援実績があります。あなたの状況に最適な利用方法の提案から運用のサポートまでのあなたに寄り添ったサポートを実現します!

Google Cloud (GCP)、またはGoogle Workspace(旧G Suite)の導入をご検討をされている方はお気軽にお問い合わせください。

お問合せはこちら


メール登録者数3万件!TOPGATE MAGAZINE大好評配信中!
Google Cloud(GCP)、Google Workspace(旧G Suite) 、TOPGATEの最新情報が満載!

メルマガ登録はこちら

ライター

TOPGATE 編集部

Related Article !

View all

クラウドの主流である SaaS とは何か?仕組みやメリットまでわかりやすく解説!

データセンターとは何か?クラウドとの違いや使い分け方法を解説!

データセンターとは何か?クラウドとの違いや使い分け方法を解説!

ビッグデータとは何か?クラウドによるデータ活用事例を紹介!

ビッグデータとは何か?7業種のクラウドによるデータ活用事例をご紹介!

アジャイル開発とウォーターフォール開発との違いとは?自社に合った開発手法の選び方まで徹底解説!

アジャイル開発とウォーターフォール開発との違いとは?自社に合った開発手法の選び方まで徹底解説!

SIer(エスアイヤー)とは何か?業務内容、種類、年収まで徹底解説!

SIer(エスアイヤー)とは何か?業務内容、種類、年収まで徹底解説!

エンジニアってどんな職種?仕事内容、種類、年収を徹底解説!

エンジニアってどんな職種?仕事内容、種類、年収を徹底解説!

Google Cloudの新DBMS、AlloyDB for PostgreSQLを触ってみた Vol.6 (最終回)

Pulumi を Google Cloud で使ってみた

Google Cloudの新DBMS、AlloyDB for PostgreSQLを触ってみた Vol.5

Google Cloudの新DBMS、AlloyDB for PostgreSQLを触ってみた Vol.4

VPN接続の最小構成を例に: アーキテクチャ設計図の効果的な作り方

2023年7月新登場!Google Cloud SQLの最上位エディション「Enterprise Plus」を触ってみた

クラウドファーストとは?クラウド導入のメリットやコストを解説

効率的なデータ活用を実現!データマートの作り方を7ステップでご紹介!

クラウドとオンプレミスの減価償却と会計処理・税務処理について

クラウドとオンプレミスの減価償却と会計処理・税務処理について

コンテナ化とは?仮想化との違いやメリット、デメリット、ユースケースまで詳しく紹介!

クラウドコンピューティングとは何か?仕組みやメリットまで徹底解説!

クラウドコンピューティングとは何か?仕組みやメリットまで徹底解説!

テレワークをするなら知らなきゃ!【Googleのゼロトラスト】BeyondCorp」の特徴、メリットをご紹介!

テレワークをするなら知らなきゃ!【Googleのゼロトラスト】BeyondCorpの特徴、メリットをご紹介!

Google Cloudの新DBMS、AlloyDB for PostgreSQLを触ってみた Vol.3

Google Cloudの新DBMS、AlloyDB for PostgreSQLを触ってみた Vol.2

Google Cloudの新DBMS、AlloyDB for PostgreSQLを触ってみた Vol.1

データベース運用を効率化する SQL とは何か?メリットやデメリット、活用事例まで一挙に紹介!

データベース運用を効率化する SQL とは何か?メリットやデメリット、活用事例まで一挙に紹介!

失敗しないシステム/ソフトウェア開発会社の選び方!判断指標から判断基準まで一挙公開

失敗しないシステム/ソフトウェア開発会社の選び方!判断指標から判断基準まで一挙公開

藤原秀平

目前に迫る! Google Cloud Next ’17 の見どころを TOPGATE エンジニアに訊いてみた

石村真吾

第二弾! Google Cloud Next ’17 の見どころを TOPGATE エンジニアに訊いてみた

新卒エンジニアが日々の学習内容を発信する「ルーキーズブログ」

新卒エンジニアが日々の学習内容を発信する「ルーキーズブログ」を始めます!

REST とは

【GCP入門編・第14回】 Cloud Functions を使ってサーバレスアーキテクチャを体験しよう!

機械学習の勉強歴が半年の初心者が、 Kaggle で銅メダルを取得した話

機械学習の勉強歴が半年の初心者が、 Kaggle で銅メダルを取得した話

Python と Twitter API でリツイートしたユーザーの情報を取得する

Python と Twitter API でリツイートしたユーザーの情報を取得する

目前に迫る!Google I/O 2018 の見どころを TOPGATE エンジニアに訊いてみた (前編)

目前に迫る!Google I/O 2018 の見どころを TOPGATE エンジニアに訊いてみた (前編)

失敗する確率を大幅に減らすために開発依頼の仕方とフェーズごとのチェックポイント

失敗する確率を大幅に減らすために開発依頼の仕方とフェーズごとのチェックポイント

ITシステム開発における自社開発と委託開発の違いと開発の流れについて一挙公開!

ITシステム開発における自社開発と委託開発の違いと開発の流れについて一挙公開!

優れた開発チームが成功の鍵!ITシステム開発のチーム編成方法と新規メンバーの調達方法とは?

優れた開発チームが成功の鍵!ITシステム開発のチーム編成方法と新規メンバーの調達方法とは?

企業のクラウド化が加速中!クラウド導入のメリットとは?

企業のクラウド化が加速中!クラウド導入のメリットとは?

ランニングコスト削減も可能?開発者が知っておきたいインフラ設計のポイント10選

ランニングコスト削減も可能?開発者が知っておきたいインフラ設計のポイント10選

【徹底解説】ウィズコロナにおけるニューノーマルとクラウド

【徹底解説】ウィズコロナにおけるニューノーマルとクラウド

次世代BIツール「Looker」の概要と導入時の注意点をご紹介!

次世代BIツール「Looker」の概要と導入時の注意点をご紹介!

政府が提唱するクラウド・バイ・デフォルト原則とは?企業における導入メリット6選

政府が提唱するクラウド・バイ・デフォルト原則とは?企業における導入メリット6選

クラウドエンジニアとは何か?仕事内容・必要スキル・資格・将来性を徹底解説!

クラウドエンジニアとは何か?仕事内容・必要スキル・資格・将来性を徹底解説!

クラウドで自社にあったカスタマイズは可能か?オンプレ利用者の悩みを解決!

デジタルトランスフォーメーション(DX)とは?概要と5つの事例をご紹介!

デジタルトランスフォーメーション(DX)とは?概要と5つの事例をご紹介!

クラウドベンダーから自社に最適な提案を引き出す!RFP(提案依頼書)の作成方法とは?

クラウドベンダーから自社に最適な提案を引き出す!RFP(提案依頼書)の作成方法とは?

クラウドネイティブ・アプリケーションとは?メリット、活用例、開発方法まで徹底解説!

クラウドネイティブ・アプリケーションとは?メリット、活用例、開発方法まで徹底解説!

【IoTとは?】ビッグデータ、クラウドとの違いや関係性まで一挙紹介

データ分析の歴史から紐解く!データウェアハウスとデータマートの違いを徹底解説

データ分析の歴史から紐解く!データウェアハウスとデータマートの違いを徹底解説

データレイクとデータウェアハウス(DWH)の違いとは?

データの定義からデータレイクとデータウェアハウス(DWH)の違いをわかりやすく解説!

データ分析基盤の一つであるデータマート概要と設計ポイントをご紹介!

データ分析基盤の一つであるデータマート概要と設計ポイントをご紹介!

クラウドアプリケーション開発とは?普及背景やメリットを解説!

クラウドアプリケーション開発とは?普及背景やメリットを解説!

マネージドサービスとフルマネージドサービスの違いとは?メリット・デメリットまで徹底解説!

マネージドサービスとフルマネージドサービスの違いとは?メリット・デメリットまで徹底解説!

ハイブリッドクラウドにした際のネットワーク構成と注意すべきポイントとは?

ハイブリッドクラウドにした際のネットワーク構成と注意すべきポイントとは?

クラウド化の社内合意を得るためには?説得するための5つのポイントをご紹介!

クラウド化の社内合意を得るためには?説得するための5つのポイントをご紹介!

クラウドインテグレーターとは何か?役割やメリット、会社の選び方まで徹底解説!

クラウドインテグレーターとは何か?役割やメリット、会社の選び方まで徹底解説!

【知らないとマズイ】2025年の崖とは?DXの推進にはクラウド化が必要不可欠!

【知らないとマズイ】2025年の崖とは?DXの推進にはクラウド化が必要不可欠!

【片山さんまだ】オンプレミス、クラウド開発における違いとそれぞれの特徴とは?

オンプレミス、クラウド開発における違いとそれぞれの特徴とは?

【実例つき】クラウド移行で失敗する原因と解決策を紹介

【実例つき】クラウド移行で失敗する原因と解決策を紹介

クラウド移行は費用対効果が重要!ROIで効果を見える化しよう!

クラウド移行は費用対効果が重要!ROIで効果を見える化しよう!

【知って納得!】クラウドの高額請求を避けるための5つの確認項目とは?

【知って納得!】クラウドの高額請求を避けるための5つの確認項目とは?

【あなたは知っている?】AI(人工知能)の仕組み、作り方、活用事例まで徹底解説!

エンジニア教育における課題と効果を出すために大切なポイントとは?

データの活用で生産性向上!「BIツール」と「DWH」や「ETL」との違いとは?

【徹底解説!】人工知能(AI)の機械学習と深層学習の違いとは?

【徹底解説!】人工知能(AI)の機械学習と深層学習の違いとは?

レンタルサーバーとクラウドの違いとは?あらゆる観点から徹底比較!

レンタルサーバーとクラウドの違いとは?あらゆる観点から徹底比較!

【万が一に備えよう】クラウドの高額請求が届いたときの対処法とは?

【万が一に備えよう】クラウドの高額請求が届いたときの対処法とは?

クラウドCoEとは?社内のクラウド推進に必要な考え方を理解しよう!

クラウド導入を成功させるための鍵!クラウドアーキテクトを徹底解説!

クラウド導入を成功させるための鍵!クラウドアーキテクトを徹底解説!

クラウドサービス安全利用には理解必須!情報セキュリティマネジメントガイドラインとは?

クラウドサービス安全利用には理解必須!情報セキュリティマネジメントガイドラインとは?

【会社のセキュリティを強化しよう!】専用線と VPN の違いとは?

専用線と VPN の違いとは?違いを理解して会社のセキュリティを強化しよう!

機械学習の仕組みとは?学習方法や活用事例まで徹底解説!

機械学習の仕組みとは?学習方法や活用事例まで徹底解説!

オンプレよりも安全?クラウドがBCP対策に選ばれる理由とは

オンプレよりも安全?クラウドがBCP対策に選ばれる理由とは

「 Society 5.0」とは何か?新しい社会を支える IT 技術を一挙にご紹介!

「 Society 5.0」とは何か?新しい社会を支える IT 技術を一挙にご紹介!

Cloud IoT Core を使用してセンサー情報を Cloud Storage にストリーミングしてみた!

Cloud IoT Core を使用してセンサー情報を Cloud Storage にストリーミングしてみた!

Flutter とは何か?メリット、デメリット、採用しているプロダクト(アプリ)まで一挙にご紹介!

Flutter とは何か?メリット、デメリット、採用しているプロダクト(アプリ)まで一挙にご紹介!

BI ツールとは何か?メリット、デメリット、活用事例まで、一挙に紹介!

BI ツールとは何か?メリット、デメリット、活用事例まで、一挙に紹介!

負荷分散の重要性とは?ロードバランサーのメリット、デメリット、選び方を徹底解説!

負荷分散の重要性とは?ロードバランサーのメリット、デメリット、選び方を徹底解説!

移行コストがボトルネック?コストを抑えながらオンプレからクラウドに DWH を移行する方法とは?

移行コストがボトルネック?コストを抑えながらオンプレからクラウドに DWH を移行する方法とは?

データウェアハウス( DWH )とデータベースとの違いとは?5つのポイントを理解して最適なサービスを選択しよう!

データウェアハウス( DWH )とデータベースとの違いとは?5つのポイントを理解して最適なサービスを選択しよう!

テレワーク導入には必須!テレワークセキュリティガイドラインとは何か?

【古いシステムからの脱却を!】レガシーシステムが抱える5つの課題とは?

【古いシステムからの脱却を!】レガシーシステムが抱える5つの課題とは?

効率的なデータ活用を実現!分析したデータを有効活用するためのテクニックを4ステップで紹介

効率的なデータ活用を実現!分析したデータを有効活用するためのテクニックを4ステップで紹介

システム担当者必見!オンプレミスからクラウドデータベースへの移行で注意すべき11のポイントとは?

システム担当者必見!オンプレミスからクラウドデータベースへの移行で注意すべき11のポイントとは?

政府が提唱する「デジタル・ガバメント実行計画」とは?民間企業への影響まで徹底解説!

政府が提唱する「デジタル・ガバメント実行計画」とは?民間企業への影響まで徹底解説!

API 活用の最前線に迫る! Apigee の3大活用パターン、国内事例、最新情報まで徹底解説!

Looker で次世代のデータ活用を実現!データの民主化における課題と解決法とは?

Looker で次世代のデータ活用を実現!データの民主化における課題と解決法とは?

図解】Google データポータルとは?機能、導入方法、使い方まで徹底解説!

【図解】Google データポータルとは?機能、導入方法、使い方まで徹底解説!

クラウドサーバーとは何か?導入時のポイントや選び方まで徹底解説!

クラウドサーバーとは何か?導入時のポイントや選び方まで徹底解説!

社内のコラボレーションを加速する Googleグループとは?概要、できること、実際の作り方まで徹底解説!

情報漏えいが起こる原因とは?過去事例や防ぐための方法まで徹底解説!

成功する DXの進め方とは?具体的な手順を9ステップでわかりやすく解説!

Chrome OS で動くビデオ会議システム? Google の最新 AI を搭載した Series One を徹底解説!

【経営者必見!】IT 化が進まないことによるリスクとは?進まない理由や推進するための方法まで徹底解説!

【経営者必見!】IT 化が進まないことによるリスクとは?進まない理由や推進するための方法まで徹底解説!

システム導入に反対する現場を説得するには?ステークホルダーマネジメントと大切な3つのポイントを徹底解説!

システム導入に反対する現場を説得するには?ステークホルダーマネジメントと大切な3つのポイントを徹底解説!

【 IT に強い人材を育てる!】「社員の IT リテラシーを向上させる3つの方法」と「 IT リテラシーが低いことによる5つのリスク」とは?

【 IT に強い人材を育てる!】「社員の IT リテラシーを向上させる3つの方法」と「 IT リテラシーが低いことによる5つのリスク」とは?

IT モダナイゼーションとは?種類、メリット、実現するためのポイントまで徹底解説!

IT モダナイゼーションとは?種類、メリット、実現するためのポイントまで徹底解説!

複数プロジェクト構成の Cloud Monitoring がより使いやすくなりました

複数プロジェクト構成の Cloud Monitoring がより使いやすくなりました

Cloud Run 2020 年のアップデートおさらい

Cloud Run 2020 年のアップデートおさらい

「 Lift & Shift 」 とは?クラウド移行の手順を5ステップで解説!

Lift & Shift とは?クラウド移行の手順を5ステップで解説!

スプレッドシートの定期作業を GAS で自動化する

スプレッドシートの定期作業を GAS で自動化する

Cloud SDK のインストールについて

Cloud SDK のインストールについて

iOS アプリと Android アプリを同時に開発!Flutter とは??

iOS アプリと Android アプリを同時に開発!Flutter とは??

Cloud SDK から VM へ安全に接続する方法

Cloud SDK から VM へ安全に接続する方法

データマネジメントとは何か?成功させるための3つのポイントと具体的な進め方を5ステップで解説!

データドリブン経営とは?実現に向けた4ステップや成功事例まで徹底解説!

Excel作業の属人化を回避する方法とは?組織全体のデータ活用が課題解決の鍵!

Cloud Spanner vs Cloud SQL

Cloud Spanner vs Cloud SQL

データ活用に欠かせないデータクレンジングとは?具体的な方法を4ステップで解説!

BYODとは?導入時のメリットとデメリットや導入時に押さえておきたいポイントを徹底解説

効率的なデータ保護を実現! DLP の概要、機能、メリット、活用事例まで一挙に紹介!

効率的なデータ保護を実現! DLP の概要、機能、メリット、活用事例まで一挙に紹介!

【実況ツイートまとめ】 Google Cloud Next '18 トップゲートエンジニアの3日目

【実況ツイートまとめ】 Google Cloud Next '18 トップゲートエンジニアの3日目

【実況ツイートまとめ】 Google Cloud Next '18 トップゲートエンジニアの2日目

【実況ツイートまとめ】 Google Cloud Next '18 トップゲートエンジニアの2日目

【実況ツイートまとめ】 Google Cloud Next '18 トップゲートエンジニアの1日目

【実況ツイートまとめ】 Google Cloud Next '18 トップゲートエンジニアの1日目

画像

GCP Live November 2014

画像

Managed VMs with Docker

画像

GAE Managed VMs誕生までの歴史を振り返る

画像

GAE ModulesをSimpleに使う

画像

Web Componentsを使ってみよう!

画像

Android Wearアプリケーション開発入門

画像

TypeScriptの型定義ファイルを共有しよう!

画像

Google Cloud Platform Live Report

画像

yeomanを用いてWeb開発を楽にする

画像

Topgate Golang勉強会 Report No.1