APモードでの起動

最終更新: 2014/9

本チュートリアルでは、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のコマンドID 0x03、シーケンスID sequenceID、引数の個数 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ライセンスで提供されています。 詳細はダウンロードした各ファイルを参照してください。