FlashAirを使ったセンサーデータの無線モニタリング

本チュートリアルでは、FlashAirを、センサ機器の簡便な無線化オプションとして利用する方法を解説します。


システム概要

arduino-sensor

機器構成

  • FlashAir
  • Arduino
  • SDメモリカードシールド
  • センサー
  • 電源
  • 外箱

機器例

写真の例では、下記の機材を使用しています。

  • FlashAir W-02 (容量は何でもよい)
  • Arduino Uno R3
  • Seeed Studio SD Shield v4.0
  • Seeed Studio Groveシステム 押しボタン
  • 電池ボックス (本来は7V以上が推奨されています)
  • 外箱 (アクリルボックスを丁番でつなげています)

動作の説明

動作の説明

  1. ブラウザで http://flashair/ を開く
    • HTMLテンプレート (ブラウザユーティリティとも言います) は事前に作っておく必要があります。
  2. JavaScriptで約1秒ごとにファイルを読み取り、グラフを表示
  3. SD.hを使ってセンサーデータをファイルに追記

ソースコード

Arduino側

    #include <SD.h>

    const int SOUND_SENSOR = A5;
    const int LED = 9;
    int val = 0;

    const int TIMER_COUNTER = 34286; // 2Hz. (65536 - 16MHz/256scale/2Hz)

    const uint8_t SLOT_SIZE = 2;

    File myFile;

    void setup()
    {
     // Open serial communications and wait for port to open:
      Serial.begin(9600);
       while (!Serial) {
        ; // wait for serial port to connect. Needed for Leonardo only
      }

      Serial.print("Initializing SD card...");
      if (!SD.begin(4)) {
        Serial.println("initialization failed!");
        return;
      }
      Serial.println("initialization done.");

      if (SD.exists("TEST.TXT")) {
        Serial.println("TEST.TXT exists.");
        Serial.println("Removing TEST.TXT...");
        SD.remove("TEST.TXT");
      }
      else {
        Serial.println("TEST.TXT doesn't exist.");
      }

      pinMode(LED, OUTPUT);
      pinMode(SOUND_SENSOR, INPUT);

      // initialize Timer1
      noInterrupts(); // disable all interrupts
      TCCR1A  = 0;
      TCCR1B  = 0;
      TCNT1   = TIMER_COUNTER;
      TCCR1B |= (1 << CS12);
      TIMSK1 |= (1 << TOIE1); // enable timer overflow interrupt
      interrupts(); // enable all interrupts
    }
    uint16_t counter = 0;
    uint8_t  slot = 0;
    bool     readyToSave = false;
    uint16_t value[2][SLOT_SIZE];

    ISR(TIMER1_OVF_vect)
    {
      value[slot][counter] = analogRead(SOUND_SENSOR);
      if (++counter == SLOT_SIZE) {
        readyToSave = true;
        counter = 0;
        slot ^= 1;
      }
      TCNT1 = TIMER_COUNTER; // preload timer
    }

    void save(uint8_t slotToSave) {
      myFile = SD.open("TEST.TXT", FILE_WRITE);
      if (myFile) {
        for (int i = 0; i < SLOT_SIZE - 1; i++) {
          myFile.println(value[slotToSave][i]);
        }
        myFile.println(value[slotToSave][SLOT_SIZE - 1]);
        myFile.close();
        Serial.println(".");
      } else {
        // if the file didn't open, print an error:
        Serial.println("error opening TEST.TXT");
      }
    }

    void loop()
    {
      if (readyToSave) {
        save(slot ^ 1);
        readyToSave = false;
      }
      delay(100);
    }

割り込みの使用は、押しボタン程度ならば不要ですが、、変化の速いセンサーを考えて割り込みを使っています。

HTMLテンプレート側

    <!DOCTYPE html>
    <html>
      <head>
      <meta charset="UTF-8">
      <script type="text/javascript" src="/SD_WLAN/jquery-2.1.0.min.js"></script>      
      <script type="text/javascript" src="/SD_WLAN/ccchart-min.js"></script>      
      </head>
      <body style="width: 99%; height: 99%; margin: 0 auto; background-color:#FFA;">
      <div id="drawing">
        <canvas id="graph" width="240" height="360" style="width:99%; max-width: 400px; height:99%; max-height: 600px;"></canvas>
      </div>

      <script type="text/javascript">
    function drawBars(testData) {
      var canvas = $('#graph');
      var chartdata = {

        'config': {
        'title': 'FlashAir x Sensor',
        'titleFont': '100 16px "Arial"',
        'type': 'line',
        'lineWidth': 4,
        'minY': 0,
        'maxY': 1100,
        'useVal': 'yes',
        'onlyChartWidthTitle': 'yes',
        'colorSet': ['yellow'],
        'bgGradient': {
            'direction':'vertical',
            'from':'#333',
            'to':'#000'
          },
        'width': canvas.width(),
        'height': canvas.height()
        },

        'data': [
          ['time',1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],
          ['brightness',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        ]
      };
     var values = testData.trim().split('\n');
      var MAX_BARS = 20;
      var first = Math.max(values.length - MAX_BARS, 0);
      var last  = values.length;
      for (var i = first; i < last; i++) {
        var n = i - first;
        chartdata['data'][1][1 + n] = values[i];
      }

      ccchart.init('graph', chartdata)
    }

    function repeater() {
      $.ajax({
        type: 'GET',
        url: '/TEST.TXT?TIME=' + Math.floor(Math.random() * 10000),
        datatype: 'text',
        success: function(data, dataType) {
          drawBars(data);
          setTimeout('repeater()', 800);
        },
        error: function(request, status, error) {
          setTimeout('repeater()', 800);
        },
      });
    }

    window.onload = function() {
      var data = "100\n200\n300\n400\n500\n600\n700\n800\n900\n1000\n100\n200\n300\n400\n500\n600\n700\n800\n900\n1000\n";
      drawBars(data);

      repeater();
    }
      </script>
    </body>
    </html>

以下のライブラリを使用しています。

センサーデータファイル (TEXT.TXT)

1行に1個の数値が並んでいるのみ センサーデータファイル

Arduino起動時にクリア (ファイル削除)

  • 容量が増えるとダウンロードが遅くなるため、データ量が多い・全部保存したい、などの場合はリングバッファ方式の導入など検討したほうがよいでしょう。