スポンサー広告
※ご注意:古い情報が含まれる場合があります

本サイトの記事には、過去の技術情報や設定手順が含まれています。現在の環境では動作しない可能性もありますので、ご利用の際はご注意ください。

Arduino CC3000 WiFi シールドでHTTP POST

Arduino CC3000 WiFi シールドでHTTP POST通信を試してみます。以下はArduino UNOに接続した状態です。

wifishld1

Arduino CC3000 WiFi シールドの概要、導入については以下のページで書きました。
Arduino CC3000 WiFi シールドを使ってみた
上記の記事では、製品概要、ライブラリの準備から提供されているサンプルプログラムでHTTP GETでのWEBページ取得を確認しました。

今回は続きになる内容ですが、このArduino CC3000 WiFi シールドでHTTP POSTを使ってArduinoからのWebへのデータ送信をテスト、確認します。

HTTP POST通信の確認
Arduinoでの通信の前にまずは簡単にですが、HTTP POST通信をFirefoxアドオンで確認します。ここでは、以下のアドオンを使いました。同様のツールは他にもいろいろとあると思います。またもちろん、htmlのフォームを作成してPOSTすることも出来ます。
HTTP Resource Test

先にサーバ側の準備をします。ここでは以下のPHPスクリプトを用意しました。単純にPOSTされたデータをパラメータ単位で表示するだけです。

<?php 
    foreach($_POST as $key => $val){
    	print('{' . $key . '} ' . ' => ' . '{' . $val . '} ');
    	print("\n");
    }
?>

このサーバサイドのスクリプトにFirefoxアドインからPOSTします。アドインがインストール出来ているとして、firefoxのツールメニューから、HTTP Resource Testを起動します。以下のような画面になります。
0001

まずは、URIを入力して、MethodをPOSTに変更します。
0005

そうすると、Requestの入力が可能になるのでここでは適当にパラメータを入力します。以下では、prm1=abc&prm2=xyz&prm3=999と入力しました。
0006

Headersのタブを選択してヘッダを追加します。AddボタンでContent-Typeを選択して、application/x-www-form-urlencodedを選択(初期値)します。
0007

これで、Submitを実行します。そうすると以下のように結果が表示されました。確かにRequetで設定したパラメータがサーバサイドで取得出来ました。Bodyの部分がサーバサイドのPHPでの表示結果です。
0009

ArduinoからのHTTP POST
前項でFirefoxのアドオンからPOSTしたことを次は本題のArduinoから実行します。ここではサンプルスケッチのWebClientを使って、GETの部分をPOSTに変更しました。以下がそのスケッチです。

/****************************************************************
WebClient.ino
CC3000 WebClient Test
Shawn Hymel @ SparkFun Electronics
March 1, 2014

GitHub - sparkfun/SFE_CC3000_Library: Arduino library for the TI CC3000 WiFi module
Arduino library for the TI CC3000 WiFi module. Contribute to sparkfun/SFE_CC3000_Library development by creating an acco...
Manually connects to a WiFi network and performs an HTTP GET request on a web page. Prints the contents of the page to the serial console. The security mode is defined by one of the following: WLAN_SEC_UNSEC, WLAN_SEC_WEP, WLAN_SEC_WPA, WLAN_SEC_WPA2 Hardware Connections: Uno Pin CC3000 Board Function +5V VCC or +5V 5V GND GND GND 2 INT Interrupt 7 EN WiFi Enable 10 CS SPI Chip Select 11 MOSI SPI MOSI 12 MISO SPI MISO 13 SCK SPI Clock Resources: Include SPI.h, SFE_CC3000.h, and SFE_CC3000_Client.h Development environment specifics: Written in Arduino 1.0.5 Tested with Arduino UNO R3 This code is beerware; if you see me (or any other SparkFun employee) at the local, and you've found our code helpful, please buy us a round! Distributed as-is; no warranty is given. 2015/12/10 Goro Nishida @ Independence Systems Yokohama For HTTP POST Test. ****************************************************************/ #include <SPI.h> #include <SFE_CC3000.h> #include <SFE_CC3000_Client.h> // Pins #define CC3000_INT 2 // Needs to be an interrupt pin (D2/D3) #define CC3000_EN 7 // Can be any digital pin #define CC3000_CS 10 // Preferred is pin 10 on Uno // Connection info data lengths #define IP_ADDR_LEN 4 // Length of IP address in bytes // Constants char ap_ssid[] = "SSID"; // SSID of network char ap_password[] = "PASSWORD"; // Password of network unsigned int ap_security = WLAN_SEC_WPA2; // Security of network unsigned int timeout = 30000; // Milliseconds char server[] = "yourdomain.com"; // Remote host site // Global Variables SFE_CC3000 wifi = SFE_CC3000(CC3000_INT, CC3000_EN, CC3000_CS); SFE_CC3000_Client client = SFE_CC3000_Client(wifi); void setup() { ConnectionInfo connection_info; int i; // Initialize Serial port Serial.begin(115200); Serial.println(); Serial.println("---------------------------"); Serial.println("SparkFun CC3000 - WebClient"); Serial.println("---------------------------"); // Initialize CC3000 (configure SPI communications) if ( wifi.init() ) { Serial.println("CC3000 initialization complete"); } else { Serial.println("Something went wrong during CC3000 init!"); } // Connect using DHCP Serial.print("Connecting to SSID: "); Serial.println(ap_ssid); if (!wifi.connect(ap_ssid, ap_security, ap_password, timeout)) { Serial.println("Error: Could not connect to AP"); } // Gather connection details and print IP address if ( !wifi.getConnectionInfo(connection_info) ) { Serial.println("Error: Could not obtain connection details"); } else { Serial.print("IP Address: "); for (i = 0; i < IP_ADDR_LEN; i++) { Serial.print(connection_info.ip_address[i]); if ( i < IP_ADDR_LEN - 1 ) { Serial.print("."); } } Serial.println(); } // Make a TCP connection to remote host Serial.print("Performing HTTP POST of: "); Serial.println(server); if ( !client.connect(server, 80) ) { Serial.println("Error: Could not make a TCP connection"); } // Make a HTTP POST request client.println("POST /postdisplay.php HTTP/1.1"); client.print("Host: "); client.println(server); // Content-Type client.println("Content-Type: application/x-www-form-urlencoded"); //Body part (url encoded variables) String body = String("prm1=abc&prm2=xyz&prm3=efg"); //数値から文字列への変換のテスト //HTTP POSTには関係ないです int num = 987; String numstr = String(num); body = body + numstr; // Content-Length client.print("Content-Length: "); //no need CR LF client.println(body.length()); // Close field client.println("Connection: close"); client.println(); //body client.println(body); Serial.println("setup finished."); } void loop() { // If there are incoming bytes, print them if ( client.available() ) { char c = client.read(); Serial.print(c); } // If the server has disconnected, stop the client and wifi if ( !client.connected() ) { //Serial.println(); // Close socket if ( !client.close() ) { Serial.println("Error: Could not close socket"); } // Disconnect WiFi if ( !wifi.disconnect() ) { Serial.println("Error: Could not disconnect from network"); } // Do nothing Serial.println("Finished WebClient test"); while (true) { delay(1000); } } }

スケッチの構成は元々のWebClientのままです。今回変更したのは、setup()の // Make a HTTP POST request // POSTの実行 のコメントの部分からです。前項で確認した内容と同様に、ここではArduinoのライブラリでPOSTしています。ヘッダーの送信、Content-Lengthの送信、Bodyの送信と続けています。

コメントにも書きましたが、POSTとは関係ないですが、数値を文字列に変換してBody部分に追加する処理も書いてみました。

これをArduinoから実行すると、シリアルモニタに以下のように表示されました。

---------------------------
SparkFun CC3000 - WebClient
---------------------------
CC3000 initialization complete
Connecting to SSID: SHL23_AP56
IP Address: 192.168.43.73
Performing HTTP POST of: independence-sys.net
setup finished.
HTTP/1.1 200 OK
Date: Thu, 10 Dec 2015 12:13:00 GMT
Server: Apache
Vary: User-Agent,Accept-Encoding
Connection: close
Transfer-Encoding: chunked
Content-Type: text/html

39
{prm1}  => {abc} 
{prm2}  => {xyz} 
{prm3}  => {efg987} 

0

Finished WebClient test

ここで気になったのが39と0という数字です。これも正常なレスポンスです。ポイントは、Transfer-Encoding: chunked です。この Transfer-Encoding: chunked の場合は、メッセージボディをチャンク(Chunk、大きいかたまりといった意味) 単位に分けて、チャンク毎のサイズとともにレスポンスを返します。このレスポンスの場合の39がチャンクのサイズです。0はチャンクは終了という意味です。

静的なコンテンツの場合は、Content-Length ヘッダを生成しやすいと思いますが、動的コンテンツの場合は、始めに、Content-Length を生成するのは難しいです。その場合にこの Transfer-Encoding: chunked が使えます。Transfer-Encoding: chunked を使う場合は、 Content-Length は指定しないという仕様です。実際にここでのレスポンスには Content-Length は存在しません。

このようなHTTPのレスポンスをArduinoで解析する場合、あまり複雑なレスポンスではプログラムも複雑になり、解析結果のアウトプット(表示)も難しいと思います。Arduinoではプロトタイプと考えると、サーバ側の処理へ繰り返しPOSTしていき、必要な処理をサーバ側で行うと流れになるかと思います。

URLエンコード
ここでは、”Content-Type: application/x-www-form-urlencoded” を指定しました。つまり必要な場合はURLエンコードを行ってPOSTをする必要があります。そこで以下のパラメータを試してみました。

スペースと!をURLエンコードしない場合
prm1=Hello World!
PHPからのレスポンス
{prm1} => {Hello World!}

スペースと!をURLエンコードした場合
prm1=Hello+Arduino%20World%21
PHPからのレスポンス
{prm1} => {Hello Arduino World!}

まあいずれの場合でも今回のPHPの環境では何とか取得出来るようです。それではArduinoのスケッチで、URLエンコードを行うとすると例えば以下のようにエンコード出来ます。

これをString型での関数にすると以下のようになります。

String URLEncode(const char* msg)
{
    const char *hex = "0123456789abcdef";
    String encodedMsg = "";

    while (*msg!='\0'){
        if( ('a' <= *msg && *msg <= 'z')
                || ('A' <= *msg && *msg <= 'Z')
                || ('0' <= *msg && *msg <= '9') ) {
            encodedMsg += *msg;
        } else {
            encodedMsg += '%';
            encodedMsg += hex[*msg >> 4];
            encodedMsg += hex[*msg & 15];
        }
        msg++;
    }
    return encodedMsg;
}

これをスケッチに組み込んで以下のように使います。

String body = String("prm1=") + URLEncode("Hello Arduino World!!");

これで、以下のレスポンスが取得出来ました。
{prm1} => {Hello Arduino World!!}

以上で、ArduinoからのPOSTが確認出来ました。今回は、ここまでとします。また応用的な内容等が書けたら公開したいと思います。