SSブログ

「オープンソースハードウェアセミナーVol1」レポート Arduino WEBサーバー 新しいWEBサーバースケッチ [ATmarquino Arduino]

ether_shield_018.png昨日は家に帰ってからもう眠くて、眠くて。
今から公開です。

※ほんのちょっとの改造ですが、それでもオリジナルのコンテンツを追加するベースにはなるんじゃないかと思います。
もう少し動作検証して、最終的にはライブラリ化した方が良さそうですね。


ちょっとの間だけArduino WEBサーバーに接続できるようにしておきます。
多分下に写真が掲載されている時は
接続可能です。
http://hamayan.ddo.jp:8888/

ファイルシステムがある訳ではないので、
  {
    {"/",           IndexHtml, 0},
    {"/index.html", IndexHtml, 0},
    {"/index.htm",  IndexHtml, 0},
    {"/chobi_01.jpg", StaticContents, &chobi_01_jpg },
  };

の中でuriとどんどん比較しているだけです。むしろファイルシステムから引っ張ってくるより簡単だと思います。
比較uri文字列の次はユーザー関数の先頭アドレスを入れ、その次のパラメーターはユーザー関数に引き渡したいパラメーターを登録しておきます。
例えば関数StaticContentsは引数の内容を返信する関数です。

このサーバーでは”HTTP1.0”と答えているのでブラウザ側も毎回コネクションを解除する事を想定しており、最初のコネクションの返信ではhtml文章の中に含まれるイメージへのリンクを認識し、そこでもう一回コネクションを張ってイメージコンテンツの取得を行っています。
”HTTP1.1”の”KEEP ALIVE”に比べれば効率が落ちますが、だからと言って元々のArduinoのEthernetライブラリ自体が同時に複数コネクションを張れるようにはなっていない(※KEEP ALIVE中は確か30秒程の時間待ち処理が入るので、その間は別の所からの要求に答えられなくなる。)ので、”HTTP1.0”で動作する方が現実的でしょう。
/****************************************************************/
/* Web Serverで色々いじってみる計画                             */
/*                  Copyright (c) せくすぃ部長 since 2009/06/13 */
/****************************************************************/

/****************************************************************/
/* file include                                                 */
/****************************************************************/
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <Ethernet.h>
#include <avr/pgmspace.h>
#include "chobi_01.h"

/****************************************************************/
/* 色々定義                                                     */
/****************************************************************/
#define  LINE_STRING_SIZE  128

enum HTTP_METHOD_TYPE
{
  METHOD_TYPE_IS_GET = 1,
  METHOD_TYPE_IS_HEAD,
  METHOD_TYPE_IS_PUT,
  METHOD_TYPE_IS_POST,
  METHOD_TYPE_IS_DELETE,
  METHOD_TYPE_IS_LINK,
  METHOD_TYPE_IS_UNLINK,
};

/****************************************************************/
/* 大域変数領域                                                 */
/****************************************************************/
char *line;
char meth[10],uri[30],ver[20];
long access_count;

const byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
const byte ip[] = { 192, 168, 100, 177 };
const byte gateway[] = { 192, 168, 100, 1 };

/****************************************************************/
/* サーバーレスポンスの固定メッセージ                           */
/****************************************************************/
const char PROGMEM http_ver_and_server_type[] =
  "HTTP/1.0 200 OK\r\n" \
  "Server: Arduino with Ether Shield/ ver.0.2\r\n";

const char PROGMEM mime_type_txt[] =
  "Content-Type: text/html; charset=UTF-8\r\n\r\n";

const char PROGMEM mime_type_img_jpeg[] =
  "Content-Type: image/JPEG\r\n\r\n";

const char PROGMEM mime_type_img_gif[] =
  "Content-Type: image/gif\r\n\r\n";

const char PROGMEM mime_type_img_png[] =
  "Content-Type: image/png\r\n\r\n";

const char PROGMEM mime_type_img_bmp[] =
  "Content-Type: image/bmp\r\n\r\n";

/****************************************************************/
/* HTTPフレーム                                                 */
/****************************************************************/
const char PROGMEM http_head[] =
  "<html lang=\"ja\"><head>\r\n" \
  "<meta HTTP-EQUIV=\"Content-type\" CONTENT=\"text/html; charset=UTF-8\">\r\n" \
  "<title>Arduino WEB Server 0.2</title></head>\r\n" \
  "<body bgcolor=\"#ccffcc\">\r\n";

const char PROGMEM thanks[] =
  "<h3>designed by hamayan</h3>\r\n" \
  "<img src=\"./chobi_01.jpg\" align=\"center\"><br>\r\n" \
  "アクセス有難うございます。このページはArduino+Ether Shieldで表示しています。<br>\r\n" \
  "現在はリクエストのパーサーを作成しています。<br>\r\n";

const char PROGMEM links[] =
  "hamayan blog <a href=\"http://hamayan.blog.so-net.ne.jp/\">http://hamayan.blog.so-net.ne.jp/</a><br>\r\n" \
  "chip 1 stop <a href=\"http://www.chip1stop.com/\">http://www.chip1stop.com/</a><br>\r\n" \
  "オープンソースハードウェアセミナーのページ <a href=\"http://www.chip1stop.com/knowledge/Arduino/\">http://www.chip1stop.com/knowledge/Arduino/</a><br>\r\n" \
  "Make:Japan <a href=\"http://jp.makezine.com/blog/\">http://jp.makezine.com/blog/</a><br><br>\r\n";

const char PROGMEM banner_01[] =
  "<p><a href=\"http://www.chip1stop.com/knowledge/Arduino/\">" \
  "<img src=\"http://www.chip1stop.com/img/link_Arduino.gif\" width=\"468\" height=\"60\" alt=\"Arduinoモニタープログラム参加中\" /></a><br>" \
  "<a href=\"http://www.chip1stop.com/\" title=\"電子部品・半導体の通販サイト - チップワンストップ\">電子部品・半導体の通販サイト - チップワンストップ</a></p><br><br>\r\n";

const char PROGMEM http_foot[] =
  "</body></html>\r\n\r\n";

const char PROGMEM HtmlRes400[] =
{
  "HTTP/1.0 400 Bad Request\r\n" \
  "Server: Arduino with Ether Shield/ ver.0.2\r\n" \
  "Content-Type: text/html; charset=UTF-8\r\n\r\n" \
  "<html lang=\"ja\"><head><title>400 Bad Request</title></head>\r\n" \
  "<h1>Bad Request</h1>\r\n" \
  "</body></html>\r\n\r\n"
};

const char PROGMEM HtmlRes404[] =
{
  "HTTP/1.0 404 Not Found\r\n" \
  "Server: Arduino with Ether Shield/ ver.0.2\r\n" \
  "Content-Type: text/html; charset=UTF-8\r\n\r\n" \
  "<html lang=\"ja\"><head><title>404 Not Found</title></head>\r\n" \
  "<h1>Not Found</h1>\r\n" \
  "</body></html>\r\n\r\n"
};

const char PROGMEM HtmlRes405[] =
{
  "HTTP/1.0 405 Method Not Allowed\r\n" \
  "Server: Arduino with Ether Shield/ ver.0.2\r\n" \
  "Content-Type: text/html; charset=UTF-8\r\n\r\n" \
  "<html lang=\"ja\"><head><title>405 Method Not Allowed</title></head>\r\n" \
  "<h1>Method Not Allowed</h1>\r\n" \
  "</body></html>\r\n\r\n"
};

const char PROGMEM hello[] =
  "<html><body><h1>hello world</h1><br><a href=\"./index.html\">戻る</a></body></html>";

Server server( 8888 );

/****************************************************************/
/* setup                                                        */
/****************************************************************/
void setup()
{
  Ethernet.begin( (uint8_t *)mac, (uint8_t *)ip, (uint8_t *)gateway );
  server.begin();
  Serial.begin( 38400 );
  line = (char *)malloc( LINE_STRING_SIZE );
}
/****************************************************************/
/* loop                                                         */
/****************************************************************/
void loop()
{
  Client client = server.available();

  if( client )
  {
    while( client.connected() )
    {
      char *dst = HTTPGets( client, line, LINE_STRING_SIZE );
      char *argv[ 10 ];
      int div_num = split( dst, argv, sizeof(argv) / sizeof(argv[0]) );  /*文字列分割*/
      strncpy( meth, argv[ 0 ], sizeof(meth) );
      strncpy( uri, argv[ 1 ], sizeof(uri) );
      strncpy( ver, argv[ 2 ], sizeof(ver) );
      break;
    }

    while( client.connected() )
    {
      char *dst = HTTPGets( client, line, LINE_STRING_SIZE );
      if( dst != NULL && *dst == '\0' )  /*改行のみの行を検出*/
      {
        server_response( client, meth, uri );
        break;
      }
    }

//    delay( 1 );
    client.stop();
  }
}

/****************************************************************/
/* 一行取得                                                     */
/* HTTPプロトコルでは行末はCR、LFであると言う前提がある         */
/****************************************************************/
static char *HTTPGets( Client client, char *dst, int size )
{
  int loop;
  char c,*ptr,*limit;

  ptr = dst;
  limit = dst + size;

  for( loop = 10; loop > 0; loop-- )
  {
    if( ptr == limit ) return (char *)0;  /*上限の確認*/

    if( client.available() )
    {
      loop = 10;  /*タイムアウト延長*/

      c = client.read();  /*一文字取得*/

      if( c == '\r' )  /*CRはNULLに変換*/
      {
        *ptr++ = '\0';
      }
      else if( c == '\n' )  /*LFは終端文字*/
      {
        *ptr = '\0';
        return dst;
      }
      else  /*文字の取得*/
      {
        *ptr++ = c;
      }
    }
    else
    {
      delay( 100 );  /*残りのデータが遅れて来る可能性があるので、ここで待ちを入れる*/
    }
  }

  return (char *)0;
}

/****************************************************************/
/* 文字列分割ユーティリティ                                     */
/****************************************************************/
static int split( char *str , char *argv[], int sz )
{
  int argc = 0;

  while( *str != '\0' && argc < sz )
  {
    if( isgraph( *str ) != 0 )
    {
      argv[ argc++ ] = str;
      while( *str != '\0' && isgraph( *str ) != 0 ) str++;
    }
    else *str++ = '\0';
  }

  return argc;
}

/****************************************************************/
/* サーバー側の応答処理                                         */
/****************************************************************/
void server_response( Client client, char *method, char *uri )
{
  int meth;
  static const struct COMMAND_LIST
  {
    void ( PROGMEM *fn )( Client client, char *uri );
    char PROGMEM *meth_name;
  } list[] =
  {
    { Method_GET, "GET" },
    { Method_HEAD, "HEAD" },
    { Method_PUT, "PUT" },
    { Method_POST, "POST" },
    { Method_DELETE, "DELETE" },
    { Method_LINK, "LINK" },
    { Method_UNLINK, "UNLINK" },
  };

  for( meth = 0; meth < sizeof( list ) / sizeof( list[0] ); meth++ )
  {
    /*メソッド文字列の比較を行い、該当するメソッドが見つかれば実行する*/
    if( strncmp( method, list[ meth ].meth_name, strlen( list[ meth ].meth_name ) ) == 0 )
    {
      list[ meth ].fn( client, uri );
      break;
    }
  }
  if( meth == sizeof( list ) / sizeof( list[0] ) )
  {
    Bad_Request( client );
  }
}

/****************************************************************/
/* METHOD GET                                                   */
/****************************************************************/
static void Method_GET( Client client, char *uri )
{
  int req;
  struct URL_LIST
  {
    char PROGMEM *name;
    void ( PROGMEM *fn )( Client client, char *uri, const FILE_PROPERTIES *pro );
    const FILE_PROPERTIES *property;
  } PROGMEM list[] =
  {
    {"/",           IndexHtml, 0},
    {"/index.html", IndexHtml, 0},
    {"/index.htm",  IndexHtml, 0},
    {"/chobi_01.jpg", StaticContents, &chobi_01_jpg },
  };

  for( req = 0; req < sizeof( list ) / sizeof( list[0] ); req++ )
  {
    /*uriの比較を行い、該当するuriが見つかれば実行する*/
    if( strcmp( uri, list[ req ].name ) == 0 )
    {
      list[ req ].fn( client, uri, list[ req ].property );
      break;
    }
  }
  if( req == sizeof( list ) / sizeof( list[0] ) )
  {
    /*要求されたuriが見付からなかった時*/
    client.write_P( HtmlRes404, sizeof( HtmlRes404 ) - 1 );
    return;
  }
}

/****************************************************************/
/* METHOD HEAD                                                  */
/****************************************************************/
static void Method_HEAD( Client client, char *uri )
{
  /*現在サポートしていないので、許可されていないメソッドとして返信*/
  client.write_P( HtmlRes405, sizeof( HtmlRes405 ) - 1 );
}

/****************************************************************/
/* METHOD PUT                                                   */
/****************************************************************/
static void Method_PUT( Client client, char *uri )
{
  /*現在サポートしていないので、許可されていないメソッドとして返信*/
  client.write_P( HtmlRes405, sizeof( HtmlRes405 ) - 1 );
}

/****************************************************************/
/* METHOD POST                                                  */
/****************************************************************/
static void Method_POST( Client client, char *uri )
{
  /*現在サポートしていないので、許可されていないメソッドとして返信*/
  client.write_P( HtmlRes405, sizeof( HtmlRes405 ) - 1 );
}

/****************************************************************/
/* METHOD DELETE                                                */
/****************************************************************/
static void Method_DELETE( Client client, char *uri )
{
  /*現在サポートしていないので、許可されていないメソッドとして返信*/
  client.write_P( HtmlRes405, sizeof( HtmlRes405 ) - 1 );
}

/****************************************************************/
/* METHOD LINK                                                  */
/****************************************************************/
static void Method_LINK( Client client, char *uri )
{
  /*現在サポートしていないので、許可されていないメソッドとして返信*/
  client.write_P( HtmlRes405, sizeof( HtmlRes405 ) - 1 );
}

/****************************************************************/
/* METHOD UNLINK                                                */
/****************************************************************/
static void Method_UNLINK( Client client, char *uri )
{
  /*現在サポートしていないので、許可されていないメソッドとして返信*/
  client.write_P( HtmlRes405, sizeof( HtmlRes405 ) - 1 );
}

/****************************************************************/
/* BAD REQUEST                                                  */
/****************************************************************/
static void Bad_Request( Client client )
{
  client.write_P( HtmlRes400, sizeof( HtmlRes400 ) - 1 );
}

/****************************************************************/
/* index.htmlの返信を行う                                       */
/****************************************************************/
static void IndexHtml( Client client, char *uri, const FILE_PROPERTIES *pro )
{
  //レスポンスヘッダーの返信
  client.write_P( http_ver_and_server_type, sizeof( http_ver_and_server_type ) - 1 );
  client.write_P( mime_type_txt, sizeof( mime_type_txt ) - 1 );

  //httpヘッダーの返信
  client.write_P( http_head, sizeof( http_head ) - 1 );

  //サンキューメッセージ
  client.write_P( thanks, sizeof( thanks ) - 1 );

  //リンク
  client.write_P( links, sizeof( links ) - 1 );

  //Arduinoバージョン
  sprintf( line, "Arduino ver=%d <br>\r\n", ARDUINO );
  client.write( line );

  //アクセスカウント
  sprintf( line, "COUNT=%d <br>\r\n", ++access_count );
  client.write( line );

  //バナー
  client.write_P( banner_01, sizeof( banner_01 ) - 1 );

  //Footer
  client.write_P( http_foot, sizeof( http_foot ) - 1 );
}

/****************************************************************/
/* 静的コンテンツ(FILE)の返信を行う                           */
/****************************************************************/
static void StaticContents( Client client, char *uri, const FILE_PROPERTIES *pro )
{
  //レスポンスヘッダーの返信
  switch( pro->attr )
  {
    case AddType_TXT :
  //レスポンスヘッダーの返信
      client.write_P( http_ver_and_server_type, sizeof( http_ver_and_server_type ) - 1 );
      client.write_P( mime_type_txt, sizeof( mime_type_txt ) - 1 );
      break;

    case AddType_JPEG :
      client.write_P( http_ver_and_server_type, sizeof( http_ver_and_server_type ) - 1 );
      client.write_P( mime_type_img_jpeg, sizeof( mime_type_img_jpeg ) - 1 );
      break;

    case AddType_GIF :
      client.write_P( http_ver_and_server_type, sizeof( http_ver_and_server_type ) - 1 );
      client.write_P( mime_type_img_gif, sizeof( mime_type_img_gif ) - 1 );
      break;

    case AddType_PNG :
      client.write_P( http_ver_and_server_type, sizeof( http_ver_and_server_type ) - 1 );
      client.write_P( mime_type_img_png, sizeof( mime_type_img_png ) - 1 );
      break;

    case AddType_BMP :
      client.write_P( http_ver_and_server_type, sizeof( http_ver_and_server_type ) - 1 );
      client.write_P( mime_type_img_bmp, sizeof( mime_type_img_bmp ) - 1 );
      break;

    case AddType_APPLI :
    case AddType_AUDIO :
    case AddType_VIDEO :
    default :
      return;
  }

  //静的コンテンツの返信
  client.write_P( pro->file, pro->size );
}


chobi_01.hの中身
/* ------------------------------------------------------------------------ */
/* Binary data file convert to header file program.                         */
/*                                             designed by hamayan          */
/*                                             Copyright(C) hamayan         */
/*                                             since 2004 -                 */
/*                                        hamayan.contact あと 爺mail.com   */
/* ------------------------------------------------------------------------ */
#include  <avr/pgmspace.h>
#include  "mime.h"

extern const FILE_PROPERTIES chobi_01_jpg;

/* ------------------------------------------------------------------------ */
/*                                             designed by hamayan          */
/*                                             Copyright(C) hamayan         */
/*                                             since 2004 -                 */
/*                                        hamayan.contact あと 爺mail.com   */
/* ------------------------------------------------------------------------ */


mime.hの中身
/* ------------------------------------------------------------------------ */
/*  Memi Typeの定義                                                         */
/*                                             designed by hamayan          */
/*                                             Copyright(C) hamayan         */
/*                                             since 2004 -                 */
/*                                        hamayan.contact あと 爺mail.com   */
/* ------------------------------------------------------------------------ */

enum HTTP_ADD_TYPE
{
  AddHandler,     /*ハンドラ*/
  AddType_TXT,    /*テキスト*/
  AddType_JPEG,   /*JPEGイメージ*/
  AddType_GIF,    /*GIFイメージ*/
  AddType_PNG,    /*PNGイメージ*/
  AddType_BMP,    /*BMPイメージ*/
  AddType_APPLI,  /*アプリケーション*/
  AddType_AUDIO,  /*audio*/
  AddType_VIDEO,  /*video*/
};

typedef struct
{
	char	*filename;	/*ファイル名*/
	char	*timestamp;	/*タイムスタンプ*/
	int		attr;	    /*属性*/
	const unsigned char PROGMEM	*file;		/*ファイルの開始アドレス*/
	char	*author;	/*著者*/
	void	*certification;	/*認証用*/
	long	size;		/*ファイルののサイズ*/
} FILE_PROPERTIES;

/* ------------------------------------------------------------------------ */
/*                                             designed by hamayan          */
/*                                             Copyright(C) hamayan         */
/*                                             since 2004 -                 */
/*                                        hamayan.contact あと 爺mail.com   */
/* ------------------------------------------------------------------------ */


Arduinoモニタープログラム参加中
電子部品・半導体の通販サイト - チップワンストップ




マスタリングTCP/IP 入門編 第4版

マスタリングTCP/IP 入門編 第4版

  • 作者: 竹下 隆史
  • 出版社/メーカー: オーム社
  • 発売日: 2007/02/24
  • メディア: 大型本



nice!(0)  コメント(2)  トラックバック(0) 

nice! 0

コメント 2

初心者

クライアントから、フォーム上にあるラジオボタンを選択してもらい、その選択された値(または入力された値)をサーバで取得して何らかの処理(例えばLED13の点滅など)をさせたく、このコードを参考にwebサーバを構築したいのですが、chobi_01.hがなくビルドできません。何かアドバイスいただけませんでしょうか、
よろしく願いいたします。

by 初心者 (2010-05-23 12:10) 

hamayan

ヘッダーファイルの追加を行っています。
by hamayan (2010-05-23 22:26) 

コメントを書く

お名前:[必須]
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

※ブログオーナーが承認したコメントのみ表示されます。

トラックバック 0

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。