APモードでの起動

本チュートリアルでは、iSDIOコマンドを使って、FlashAirの無線LANをAPモードで起動してみます。


概要

APモードの起動には、iSDIOで規定されているEstablishコマンドを使用します。

Establishコマンドの発行が完了した後、 FlashAirが実際に無線LAN APとして動作開始するまでには、 数秒~20秒程度の時間がかかります。 動作開始を確認するためには、ステータスレジスタを利用します。

チュートリアル3で ステータスレジスタの読み出し方を解説しましたが、 全部のステータスは必要ありませんので、 本チュートリアルでは、コマンド発行後の処理ステータスを確認する方法だけを切り出してみます。

また、Establishコマンドが正しく動作するためには、 FlashAirの無線LANが未接続状態になっていなければなりません。 そこで、このチュートリアルでは、無線LANを切断するためのDisconnectコマンドも実行してみましょう。

以下、 チュートリアル3のソースコードを元に追加・変更していきます。


シーケンスIDについて

前述のとおり、コマンドは発行してから処理が完了するまで時間がかかります。 最後に発行されたコマンドを識別するために、シーケンスIDという仕組みが取り入れられています。

iSDIOのシーケンスIDは、0以上の4バイトの符号なし数値です。 コマンドを新しく発行するたびに、シーケンスIDを新しい値(通常は以前の値に1加えたもの)に更新します。

FlashAirの起動時あるいはCGIでの指示により、FlashAir自身が無線LAN状態を変更した場合も、 iSDIOコマンドが発行されてシーケンスIDが変更される可能性があることに注意が必要です。

そのため、厳密にはコマンド発行の都度最新のシーケンスIDを取得して、 新しいシーケンスIDを作成するのが正しいでしょう。 ただし、本チュートリアルシリーズでは説明の簡略化のため、起動直後のシーケンスIDを読み取り、 それ以降は自分で管理したIDのみを使います。

自分が発行した最後のシーケンスIDを覚えておくため、変数を一つ用意します。

arduino_tutorial_04.ino (一部抜粋)
    uint32_t nextSequenceId = 0;

Establishコマンドの発行

Establishは、無線LAN機能をAPモードで立ち上げるためのコマンドです。 同時に、HTTPサーバー機能とDHCPサーバー機能も立ち上がります。

詳細は、 SD Specifications Part E7 Wireless LAN Simplified Addendum Version 1.10 4.2.3 Estalibsh(ssid, networkKey, encMode) に規定されています。

コマンドデータを作るためには、下記の情報が必要です。

  • コマンドID (3)
  • シーケンスID
  • 引数個数 (3)
  • 引数
    • SSID
    • ネットワークキー (パスワード)
    • セキュリティモード
arduino_tutorial_04.ino (一部抜粋)
    boolean iSDIO_establish(uint32_t sequenceId) {
      Serial.print(F("\nEstablish command: \n"));
      memset(buffer, 0, 512);
      uint8_t* p = buffer;
      p = put_command_header(p, 1, 0);
      p = put_command_info_header(p, 0x03, sequenceId, 3);
      p = put_str_arg(p, "sdiotest");
      p = put_str_arg(p, "12345678");
      p = put_u8_arg(p, 0x06);
      put_command_header(buffer, 1, (p - buffer));
      printHex(buffer, (p - buffer));
      return card.writeExtDataPort(1, 1, 0x000, buffer) ? true : false;
    }
  • 3行目~10行目
    FlashAirに発行するコマンドのデータをArduinoのメモリ上に作っています。
  • 5行目
    ヘルパー関数を利用して、コマンドのヘッダを作っています。 最後の引数にはコマンドデータの長さ(バイト数)を入れますが、 可変長の引数があるため、引数を含んでいるため、後で計算します。
  • 6行目
    ヘルパー関数を利用して、コマンド情報のヘッダを作っています。 establishのコマンドID0x03、シーケンスIDsequenceID、引数の個数3を指定しています。
  • 7行目
    1番目の引数SSIDを書き込んでいます。 文字列引数を書き込むヘルパー関数を利用しています。
  • 8行目
    同様に、2番目の引数ネットワークキーを書き込んでいます。
  • 9行目
    3番目の引数セキュリティモードを書き込んでいます。 ここでは、WPA2-PSK and AESを表す0x06を指定しています。他の値については仕様書をご覧ください。 1バイトデータを書き込むヘルパー関数を利用しています。
  • 10行目
    全データが書き込まれ、バイト数が確定したので、コマンドヘッダを改めて書き込んでいます。
  • 11行目
    正しく作成されたかを確認する目的で、コマンドデータをダンプしています。printHex()関数についてはサンプルコードを参照ください。
  • 12行目
    FlashAirに対してデータを書き込んでいます。

コマンド処理状況の確認

コマンド処理状況を確認するには、iSDIOのコマンドレスポンスステータスを読み取ります。

コマンドレスポンスステータス (SD Specifications Part E7 iSDIO Simplifed Specification Version 1.10より抜粋)

コマンドレスポンスステータス

特に重要なのは、 _iSDIO command sequence id_と、 _Response Status_の2つです。 iSDIO command sequence idが、いま確認したいシーケンスIDと一致しているか確認します。 一致していることが確認できたら、Response Statusで処理状況を確認します。

なお、iSDIO規格では最大8つまでのコマンドを同時に発行できることになっており、 コマンドレスポンスステータスレジスタも8個ありますが、 FlashAirは1つまでの対応となっていますので、アドレス 0x440のステータスを常に読み取ります。

以下に、指定したシーケンスIDのコマンドの終了を待機する関数を作成します。

arduino_tutorial_04.ino (一部抜粋)
    boolean iSDIO_waitResponse(uint32_t sequenceId) {
      Serial.print(F("\nWaiting response "));
      uint8_t prev = 0xFF;
      for (int i = 0; i < 20; ++i) {
        memset(buffer, 0, 0x14);

        // Read command response status.
        if (!card.readExtMemory(1, 1, 0x440, 0x14, buffer)) {
          return false;
        }

        uint8_t resp = get_u8(buffer + 8);
        if (sequenceId == get_u32(buffer + 4)) {
         if (prev != resp) {
            switch (resp) {
              case 0x00:
                Serial.print(F("\n  Initial"));
                break;
              case 0x01:
                Serial.print(F("\n  Command Processing"));
                break;
              case 0x02:
                Serial.println(F("\n  Command Rejected"));
                return false;
              case 0x03:
                Serial.println(F("\n  Process Succeeded"));
                return true;
              case 0x04:
                Serial.println(F("\n  Process Terminated"));
                return false;
              default:
                Serial.print(F("\n  Process Failed "));
                Serial.println(resp, HEX);
                return false;
            }
            prev = resp;
          }
        }
        Serial.print(F("."));
        delay(1000);
      }
      return false;
    }
  • 8行目
    コマンドレスポンスステータスレジスタを読み取っています。
  • 13行目
    シーケンスIDが一致することを確認しています。
  • 15行目~35行目
    レスポンスステータスを確認してます。処理が完了すると、Process Succeededになります。
  • 39行目
    1秒ごとにこの確認を実行しています。

Disconnectコマンドの発行

Disconnectは、無線LANを切断するためのコマンドです。 同時に、HTTPサーバー機能とDHCPサーバー機能も停止します。

詳細は、 **SD Specifications Part E7 Wireless LAN Simplified Addendum Version 1.10 4.2.7 Disconnect(ssid, networkKey, encMode) ** に規定されています。

コマンドデータを作るためには、下記の情報が必要です。

  • コマンドID (7)
  • シーケンスID
  • 引数個数 (0)
  • 引数はありません。
arduino_tutorial_04.ino (一部抜粋)
    boolean iSDIO_disconnect(uint32_t sequenceId) {
      Serial.print(F("\nDisconnect command: \n"));
      memset(buffer, 0, 512);
      uint8_t* p = buffer;
      p = put_command_header(p, 1, 0);
      p = put_command_info_header(p, 0x07, sequenceId, 0);
      put_command_header(buffer, 1, (p - buffer));
      printHex(buffer, (p - buffer));
      return card.writeExtDataPort(1, 1, 0x000, buffer) ? true : false;
    }

メインプログラム

メインプログラムは、対話式のプログラムとしてみましょう。

Arduinoのシリアルターミナルで入力されたコマンド番号に従って、ステータス表示、Disconnect、Establish の各コマンドを実行します。

arduino_tutorial_04.ino (一部抜粋)
    void loop() {
      if (!iSDIO_status()) {
        Serial.println(F("\nFailed to read status."));
      }

      Serial.print(F("\n0. Show status"));
      Serial.print(F("\n1. Disconnect"));
      Serial.print(F("\n2. Establish"));
      Serial.print(F("\n\nCommand? (next sequence id = "));
      Serial.print(nextSequenceId, DEC);
      Serial.println(F(")"));

      while (Serial.available() == 0);
      char command = Serial.read();

      switch (command - '0') {
        case 0 :
          break;
        case 1 :
          if (iSDIO_disconnect(nextSequenceId) &&
              iSDIO_waitResponse(nextSequenceId)) {
            Serial.println(F("\nSuccess."));
          } else {
            Serial.print(F("\nFailed or waiting. errorCode="));
            Serial.println(card.errorCode(), HEX);
          }
          nextSequenceId++;
          break;
        case 2 :
          if (iSDIO_establish(nextSequenceId) &&
              iSDIO_waitResponse(nextSequenceId)) {
            Serial.println(F("\nSuccess."));
          } else {
            Serial.print(F("\nFailed or waiting. errorCode="));
            Serial.println(card.errorCode(), HEX);
          }
          nextSequenceId++;
          break;
        default :
          Serial.println(F("\nUnknown command."));
          break;
      }
    }
  • 2行目
    ステータス表示です。
  • 6行目~11行目
    実行できるコマンドおよび次のシーケンスIDを表示しています。
  • 13行目~14行目
    番号の入力を待っています。
  • 16行目~42行目
    入力された番号に従ってコマンドを実行します。

ステータス表示を loop()関数に含めましたので、 setup()関数からは削除しましょう。

arduino_tutorial_04.ino (一部抜粋)
    void setup() {
      // Initialize UART for message print.
      Serial.begin(9600);
      while (!Serial) {
        ;
      }

      // Initialize SD card.
      Serial.print(F("\nInitializing SD card..."));  
      if (card.init(SPI_HALF_SPEED, chipSelectPin)) {
        Serial.print(F("OK"));
      } else {
        Serial.print(F("NG"));
        abort();
      }

      // Read the previous sequence ID.
      if (card.readExtMemory(1, 1, 0x420, 0x34, buffer)) {
        if (buffer[0x20] == 0x01) {
          nextSequenceId = get_u32(buffer + 0x24);
          iSDIO_waitResponse(nextSequenceId);
          nextSequenceId++;
        } else {
          nextSequenceId = 0; 
        }
      } else {
        Serial.println(F("\nFailed to read status."));
        nextSequenceId = 0; 
      }
    }
  • 17行目~29行目
    最後に使われたシーケンスIDを読み取っています。 さらに、直前のコマンドが実行中の場合は完了を待機しています。

実行結果

実行すると、例えば次のようになります。

    Initializing SD card...OK
    Wait for response 
      Process Succeeded

    Read iSDIO Status Register
     == iSDIO Status Registers == 
     [0400h] Command Write Status: 
    ... (snip) ...
     [0440h] Command Response Status #1: id = 3, sequence id = 1, status = Process Succeeded
    ... (snip) ...
     [0506h] WLAN: No Scan, No WPS, Group Client, AP, Infrastructure, No Connection, 
    ... (snip) ...
     [0550h] IP Address: 192.168.0.1
    ... (snip) ...

    0. Show status
    1. Disconnect
    2. Establish

    Command? (next sequence id = 2)

起動後の状態と、コマンドオプションを表示します。 この例では、起動時にFlashAirがAPモードで起動したことを表しています。

コマンドは、Arduino IDEのシリアルターミナル上部にあるボックスに入力します。

1、Enterと入力してみましょう。

Disconnect command: 

00: 01010000180000000000000000000700
01: 0700000000000000

Wait for response 
  Command Processing.
  Process Succeeded

Success.

Read iSDIO Status Register
... (snip) ...
 [0440h] Command Response Status #1: id = 7, sequence id = 2, status = Process Succeeded
... (snip) ...
 [0506h] WLAN: No Scan, No WPS, Group Client, STA, Initial, No Connection, 
... (snip) ...
 [0550h] IP Address: 0.0.0.0
... (snip) ...

Command? (next sequence id = 3)

14行目が、DisconnectのコマンドID 7、指定したシーケンスID、ステータス Process Succeededになっていることから、 Disconnectコマンドが発行され、完了したことがわかります。 また、WLANステータスが STAでIP Addressの 0.0.0.0であることから、無線LANが切断されていると判断できます。

3行目~4行目は、コマンドデータです。うまく動かないときは内容を確認してみてください。

引き続き、APモードの起動を行ってみましょう。

シリアルターミナルのボックスに、 2、Enterと入力します。

Establish command: 

00: 01010000380000000000000000000300
01: 0800000003000000080000007364696F
02: 74657374080000003132333435363738
03: 0100000006000000

Wait for response 
  Command Processing.....
  Process Succeeded

Success.

Read iSDIO Status Register
... (snip) ...
 [0440h] Command Response Status #1: id = 3, sequence id = 3, status = Process Succeeded
... (snip) ...
 [0506h] WLAN: No Scan, No WPS, Group Client, AP, Infrastructure, No Connection, 
 [0508h] SSID: sdiotest
 [0528h] Encryption Mode: WPA2-PSK and AES
 [0529h] Signal Strength: 0
 [052Ah] Channel: 11
 [0530h] MAC Address: E8E0B758A7FB
 [0540h] ID: 
 [0550h] IP Address: 192.168.0.1
 [0554h] Subnet Mask: 255.255.255.0
 [0558h] Default Gateway: 192.168.0.1
 [055Ch] Preferred DNS Server: 192.168.0.1
 [0560h] Alternate DNS Server: 0.0.0.0
... (snip) ...

EstablishのコマンドID 3が実行されています。(16行目)

WLANステータスが APおよびIP Addressが 192.168.0.1となっていることから、 FlashAirがAPモードで立ち上がっていることが確認できます。


サンプルコード

arduino_tutorial_04.zip (24KB)

本チュートリアルのサンプルコードはGPLv3および二条項BSDライセンスで提供されています。 詳細はダウンロードした各ファイルを参照してください。