Webクライアントから送信されたデータをATP3011にしゃべらせる(I2C通信)
作成 : 2013/01/27
Webクライアントから送信されたデータをATP3011にしゃべらせる(I2C通信)
Webクライアントから送信されたテキストをLCDに表示するとプッシュスイッチを押すとATP3011が決められた言葉をしゃべる(I2C通信)を組み合わせて、Webクライアントから送信したデータをATP3011にしゃべらせます。
/********************* ArduinoをWEBサーバとして動作させ、フォームからPOSTで送信されたテキストをATP3011に送信して発声させる。 Arduino Uno http://www.arduino.cc/en/Main/arduinoBoardUno Arduino Ethernet Shield http://arduino.cc/en/Main/ArduinoEthernetShield ATP3011 http://www.a-quest.com/products/aquestalkpicolsi.html **********************/ #include <SPI.h> #include <Ethernet.h> #include <Wire.h> // ATP3011F4のI2Cアドレス(デフォルト値のまま) #define ADDR 0x2E // POSTで読み取るデータサイズを定義 #define BUFSIZE 256 // ATP3011F4に送れるのは127byte+'\r' #define MAX_BYTE 127 // WIREで1回に送れるのは32byte #define MAX_WIRE_BYTE 32 // Arduino Ethernet ShieldのMACアドレス byte mac[] = { 0x90, 0xA2, 0xDA, 0x0D, 0x99, 0x50 }; // Arduinoに設定するTCP/IP情報 byte ip[] = { 192, 168, 1, 200 }; byte gateway[] = { 192, 168, 1, 1 }; byte subnet[] = { 255, 255, 255, 0 }; EthernetServer server(80); // 最初に1回だけ実行 void setup() { // シリアル通信の転送レートを設定 Serial.begin(9600); // I2Cを初期化 Wire.begin(); // リスナを開始 Ethernet.begin(mac, ip, gateway, subnet); server.begin(); } // 繰り返し実行 void loop() { // ATP3011からのレスポンスを格納する変数 char respStr[10]; // クライアントオブジェクトを取得 EthernetClient client = server.available(); // クライアントが存在するとき、clientはtrue if (client) { Serial.println("new client"); // クライアントからPOSTメソッドで呼ばれたらtrue boolean isPOST = false; // リクエストヘッダに改行(\r)以外が含まれたらfalse boolean currentLineIsBlank = true; // リクエストヘッダの終了(\r\n\r\n)を検出したらtrue boolean reqHeaderIsEnd = false; // リクエストデータを格納 // 最大サイズはBUFSIZEで規定 char reqParam[BUFSIZE]; // リクエストデータを読むときに使用するインデックス long idx = 0; // POSTで送信されたテキストを格納 char* wordValue; // ATP3011に送信するデコード済みのテキストを格納 char sendStr[MAX_BYTE] = ""; // クライアントが存在する間、ぐるぐるまわる while (client.connected()) { // クライアントから受信するデータが存在するときはtrueを返す if (client.available()) { char c = client.read(); Serial.write(c); // (\r)\n(\r)\nのパターンがあらわれた場合、リクエストヘッダの終わりと見なしてreqHeaderIsEndをtrueにする if (c == '\n' && currentLineIsBlank) { reqHeaderIsEnd = true; } if (c == '\n') { currentLineIsBlank = true; // 行末にきたらidxをリセット idx = 0; // 行頭が"POST "ではじまっていたら、isPOSTをtrueにする if (strncmp(reqParam, "POST ", 5) == 0) { isPOST = true; } } else if (c != '\r') { currentLineIsBlank = false; // リクエストデータ1行分をreqParamに格納する // 1行のデータサイズがPOSTデータの規定最大サイズBUFSIZE-1(-1は'\0'付加を考慮した分)を超える場合は、データを破棄 if (idx < BUFSIZE - 1) { reqParam[idx] = c; idx++; } } } // クライアントから受信するデータがなくなったらループを抜ける else { break; } } Serial.println(); // POSTで呼び出された場合のデータ解析とデコード処理 if (isPOST) { // POSTデータを'&'で分割したパラメタを格納 char* splitReqParam; // POSTデータをstrtok_rでパラメタに分割するときに使用 char* reqParamPtr; // パラメタを'='で分割した左辺(パラメタ名)を格納 char* paramName; // パラメタを'='で分割した右辺(パラメタ値)を格納 char* paramValue; // パラメタをstrtok_rで名前と値に分割するときに使用 char* nameValuePtr; // reqParamに格納した文字列の後ろにNULLを追加して、文字列の終わりを示す reqParam[idx] = '\0'; // POSTデータを'&'で分割してsplitReqParamに格納 for (splitReqParam = strtok_r(reqParam, "&", &reqParamPtr); splitReqParam != NULL; splitReqParam = strtok_r(NULL, "&", &reqParamPtr)) { // パラメタsplitReqParamを'='で分割して、名前をparamName、値をparamValueに格納 // ネストしたいので、strtokの代わりにstrtok_rを使用 char* paramName = strtok_r(splitReqParam, "=", &nameValuePtr); char* paramValue = strtok_r(NULL, "=", &nameValuePtr); // パラメタ名が"word"であれば、テキストとみなしてwordValueに値を格納 if (strcmp(paramName, "word") == 0) { wordValue = paramValue; } } // POSTで送信されたデータはパーセントエンコーディングされているため、デコードする関数を呼ぶ // デコードされた文字列はsendStrに格納 // sendStr = strdup(wordValue); urlDecode(wordValue, sendStr); } // POSTで呼び出された場合、atp3011_speak関数を呼んでデータを送信し、レスポンスをrespStrに格納 if (isPOST) { atp3011_speak(sendStr, respStr); } //レスポンスの末尾は'>'になるので、">"に変換 if (respStr[strlen(respStr) -1] == '>') { int respStrLen = strlen(respStr); respStr[respStrLen - 1] = '&'; respStr[respStrLen] = 'g'; respStr[respStrLen + 1] = 't'; respStr[respStrLen + 2] = ';'; respStr[respStrLen + 3] = '\0'; } // クライアントにデータを返す client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println("Connnection: close"); client.println(); client.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); client.println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"); client.println("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"ja\" lang=\"ja\">"); client.println("<head>"); client.println("<meta http-equiv=\"content-type\" content=\"text/html\" />"); client.println("<title>"); client.println("Arduino Web Server"); client.println("</title>"); client.println("</head>"); client.println("<body>"); client.println("<form action=\"http://192.168.1.200/\" method=\"post\">"); client.println("<p>"); client.print("<input type=\"text\" name=\"word\" />"); client.println("</p>"); // ATP3011に送信したデータをクライアントに表示 client.println("<p>"); client.print("Send message:"); client.println(sendStr); client.println("</p>"); // ATP3011から受信したレスポンスをクライアントに表示 client.println("<p>"); client.print("Responce:"); client.println(respStr); client.println("</p>"); client.println("<p><input type=\"submit\" value=\"送信\" /></p>"); client.println("</body>"); client.println("</html>"); // クライアントをサーバから切断 delay(1); client.stop(); Serial.println("client disconnected"); } } /* 42 : '*' 62 : '>' 69 : 'E' */ // POSTで読み取ったデータはパーセントエンコーディングされているため、デコードを実施 // char* str : 変換する文字列を格納、 char* dstStr : 変換後の文字列を格納 boolean urlDecode(char* str, char* dstStr) { if (str != NULL) { long dstIdx = 0; for(long idx = 0; idx < strlen(str); idx++) { if (str[idx] == '%') { char tmpHex[] = { str[idx+1], str[idx+2] }; dstStr[dstIdx] = strtol(tmpHex, NULL, 16); idx += 2; } else if (str[idx] == '+') { dstStr[dstIdx] = ' '; } else { dstStr[dstIdx] = str[idx]; } dstIdx++; } dstStr[dstIdx] = '\0'; } return true; } // ATP3011にデータsendStrを送信して発声させ、レスポンスをrespStrに格納する // 関数の戻り値は、レスポンスが'E'で始まるエラー系ならfalse、それ以外はtrueを返す boolean atp3011_speak(char* sendStr, char* respStr) { // ATP3011にデータを送信する // ATP3011には127byteまで送信できるが、Wireでは32byteしか送信できないため、 // 長いデータは32byteに分割して送る for (int w_loop = 0; w_loop < 4;w_loop++) { Wire.beginTransmission(ADDR); for (int s_idx = 0;s_idx < MAX_WIRE_BYTE;s_idx++) { // データの終わり'\0'がきたらループを抜ける if(sendStr[w_loop * MAX_WIRE_BYTE + s_idx] == '\0') { break; // データの終わり'\0'以外なら送信する(Wire.endTransmissionでまとめて送信される) } else { Wire.write(sendStr[w_loop * MAX_WIRE_BYTE + s_idx]); } } Wire.endTransmission(); } // 最後に'\r'を送信して、発声を開始させる Wire.beginTransmission(ADDR); Wire.write('\r'); Wire.endTransmission(); Serial.print("ATP3011 Write message:"); Serial.println(sendStr); // ATP3011からのレスポンスを受け取る // BUSY('*')の間はループする do { // ATP3011へのポーリングは10ms間隔をあけること delay(10); // ATP3011から8byte読み取る // ATP3011からの最大レスポンス長は"Exxx>"で5byteになるはず Wire.requestFrom(ADDR,8); int r_idx=0; // ATP3011から読み取るデータがある間、ぐるぐる回る while (Wire.available()) { // ATP3011から1byte読み取る char c = Wire.read(); // 読み取ったデータがBUSY'*'または'>'だったら、データをrespStrに格納してループを抜ける if (c == '*' || c == '>') { respStr[r_idx] = c; break; // 読み取ったデータがそれ以外だったら、データをrespStrに格納する } else { respStr[r_idx] = c; r_idx++; } } // respStrの最後に'\0'を付加して文字列の終わりを示す respStr[r_idx+1] = '\0'; // レスポンスがBUSY"*"の間、ぐるぐる回る } while (!strcmp(respStr,"*")); Serial.print("ATP3011 Responce:"); Serial.println(respStr); // レスポンスがエラー"Exxx>"の場合はfalseを返す // レスポンスがエラー">"や"Vxx>の場合はtrueを返す if (respStr[0] == 'E') { return false; } else { return true; } }