非同期通信の手法として XmlHttpRequest の代替として FetchAPI が使われるそうです。 最終更新日:2023/2/6
AjaxWebserver機能で実現するにはXmlHttpRequestを使用するExampleが紹介されており、それが正だと信じていました。しかし、iOSで動かないことが判ったため、別の方法論の調査を始めたところ FetchAPI なるモノがあることが判りました。調査します。
https://werner.rothschopf.net/202003_arduino_webserver_post_en.htmの記事はArduinoで構築するWebserberについて書かれているのですが、GETを使うとパラメータを開示してしまうので POST を使いましょう と謳っています。そしてデータ読出しについてJavascriptを使った非同期通信をしている様ですが これが FETCHAPIなのかどうかが判断出来ません。
提供されたスケッチをPico+W5500で動く様に移植は出来ましたので、掲載しておきます。このスケッチだとiOSでも動作しました。しかしm文字列処理が重いようでキビキビ動くというような感じではないです。
/* * 2023年2月6日 T.Wanibe * このコードはFetchAPIのダンプルを検索していてヒットしたモノ Pico+W5500で動作する様に移植した * https://werner.rothschopf.net/202003_arduino_webserver_post_en.htm * のドキュメントを参照のこと * 最大1044480バイトのフラッシュメモリのうち、スケッチが66592バイト(6%)を使っています。 * 最大262144バイトのRAMのうち、グローバル変数が7928バイト(3%)を使っていて、ローカル変数で254216バイト使うことができます。 */ /* ******************************************************************* A simple Arduino Ethernet web server. Fetch/API This version: Arduino String クラスの読み取りパラメータ (GET および POST) を使用しない 定義されていないページに注意する ループをきちんと処理する favicon.ico を保持する Streamlib Fetch API を使用して SRAM を保存する description: - en: https://werner.rothschopf.net/202003_arduino_webserver_post_en.htm - de: https://werner.rothschopf.net/202001_arduino_webserver_post.htm credits: - by noiasca 2020-11-24 17468/511 * *************************************************************** */ #include <SPI.h> #include <Ethernet3.h> //<Ethernet.h>から変更しています。 #include <StreamLib.h> // download from library manager #define BAUDRATE 115200 #define NICReset 18 #define NICInit 19 #define SPI_SCK 2 #define SPI_TX 3 #define SPI_RX 4 #define SPI_CS 5 byte mac[] = {0x00,0x08,0xDC,0x54,0x4D,0xDD}; //WIZNET byte ip[] = {192, 168, 0, 208}; EthernetServer server(80); // Port 80 is HTTP port byte ledPin[] = {16,17,20,21,22,26,27,28}; // define the LED pins. Don't use 0 (RX), 1(TX), 10 (SS ETH), 11 (MOSI), 12(MISO), 13(CLK). Be carefull with 4(SS SD). byte analogPin[] = {26, 27, 28}; // some analog pins to read. /* void printHardwareInfo(){ if (Ethernet.hardwareStatus() == EthernetNoHardware) { Serial.println(F("Ethernet shield was not found")); }else if (Ethernet.hardwareStatus() == EthernetW5100) { Serial.println(F("W5100 detected")); }else{ if (Ethernet.hardwareStatus() == EthernetW5200) { Serial.println(F("W5200 detected")); }else if (Ethernet.hardwareStatus() == EthernetW5500) { Serial.println(F("W5500 detected")); } if (Ethernet.linkStatus() == Unknown) { // cable connection Serial.println(F("Link status detection is only available with W5200 and W5500")); }else if (Ethernet.linkStatus() == LinkON) { Serial.println(F("Link status: On")); }else if (Ethernet.linkStatus() == LinkOFF) { Serial.println(F("Link status: Off")); } } } */ //------------------ void setup(){ Serial.begin(BAUDRATE); Serial.println(F("\nsimple webserver")); for (int i = 0 ;i<8; i++) { pinMode(ledPin[i], OUTPUT); // Set the digital pins ready to write to } SPI.setSCK(SPI_SCK); SPI.setRX(SPI_RX); SPI.setTX(SPI_TX); SPI.setCS(SPI_CS); SPI.begin(); //Ethernet3で使用可能なAPI pinMode(SPI_CS, OUTPUT); //NIC_CS出力設定 Ethernet.setCsPin(SPI_CS); //NIC_CSアサイン Ethernet.setRstPin(NICReset); //NIC_RSTアサイン //NICリセット処理 pinMode(NICReset, OUTPUT); digitalWrite(NICReset, LOW); delay(10); //10msecパルス幅で初期化 digitalWrite(NICReset, HIGH); Ethernet.begin(mac, ip); // Start the Ethernet shield: Serial.print(F("server available at http://")); Serial.println(Ethernet.localIP()); //printHardwareInfo(); server.begin(); // Start the webserver: } //--------------- void loop(){ // listen for incoming clients checkForClient(); // put your other main code here, to run repeatedly. avoid any delay() } |
/* ***************************************************************** * Webserver - the basic functions * ***************************************************************** */ //------------------- void checkForClient(){ EthernetClient client = server.available(); if (client) { Serial.println(F("\n[server] client connected")); uint8_t i = 0; // index / current read position const uint16_t buffersize = 100; // size of read buffer (reads a complete line) (if larger than 255, modify i also! const uint16_t smallbuffersize = 30; // a smaller buffer for results char lineBuffer[buffersize] {'\0'}; // buffer for incomming data char method[8]; // largest one 7+1. HTTP request methods in RFC7231 + RFC5789: GET HEAD POST PUT DELETE CONNECT OPTONS TRACE PATCH char uri[smallbuffersize]; // the requestet page, shorter than smallbuffersize - method char requestParameter[smallbuffersize]; // parameter appended to the URI after a ? char postParameter[smallbuffersize]; // parameter transmitted in the body / by POST enum class Status {REQUEST, CONTENT_LENGTH, EMPTY_LINE, BODY}; Status status = Status::REQUEST; const size_t MESSAGE_BUFFER_SIZE = 64; // size of buffer for StreamLib char buffer[MESSAGE_BUFFER_SIZE]; // a buffer for the StreamLib BufferedPrint message(client, buffer, sizeof(buffer)); // the StreamLib object to replace client print while (client.connected()) { while (client.available()) { char c = client.read(); Serial.print(c); // Debug print received characters to Serial monitor if ( c == '\n' ){ if (status == Status::REQUEST){ // read the first line //Serial.print(F("lineBuffer="));Serial.println(lineBuffer); // now split the input char *ptr; ptr = strtok(lineBuffer, " "); // strtok willdestroy the newRequest strlcpy(method, ptr, smallbuffersize); Serial.print(F("method=")); Serial.println(method); ptr = strtok(NULL, " "); strlcpy(uri, ptr, smallbuffersize); // enthalt noch evtl. parameter if (strchr(uri, '?') != NULL){ ptr = strtok(uri, "?"); // split URI from parameters strcpy(uri, ptr); ptr = strtok(NULL, " "); strcpy(requestParameter, ptr); Serial.print(F("requestParameter=")); Serial.println(requestParameter); } Serial.print(F("uri=")); Serial.println(uri); status = Status::EMPTY_LINE; // jump to next status }else if (status == Status::CONTENT_LENGTH){ // MISSING check for Content-Length status = Status::EMPTY_LINE; }else if (status > Status::REQUEST && i < 2){ // check if we have an empty line status = Status::BODY; }else if (status == Status::BODY){ strlcpy(postParameter, lineBuffer, smallbuffersize); break; // we have received one line payload and break out } i = 0; strcpy(lineBuffer, ""); }else{ if (i < buffersize){ lineBuffer[i] = c; i++; lineBuffer[i] = '\0'; } // MISSING wenn status 3 und content-length --> abbrechen. } } if (status == Status::BODY){ // status 3 could end without linefeed, therefore we takeover here also strlcpy(postParameter, lineBuffer, smallbuffersize); } Serial.print(F("postParameter=")); Serial.println(postParameter); // Simple evaluation of postParameter from body // Post data looks like pinD2=On //文字列処理に注意 if ( strncmp( postParameter, "pinD", 4) == 0 ) { byte index1 = postParameter[4] - 0x30; // Convert ascii to int byte index2 = postParameter[5] - 0x30; byte index = index1 * 10 + index2; Serial.println(index); if ( strncmp( postParameter + 6, "=On", 3) == 0 ) { digitalWrite(index, HIGH); }else if ( strncmp( postParameter + 6, "=Off", 4) == 0 ) { digitalWrite(index, LOW); } } // send back a response if (!strcmp(uri, "/")) // the homepage sendPage(message); else if (!strcmp(uri, "/1.htm")) sendPage1(message); else if (!strcmp(uri, "/1.js")) sendJs(message); // javascript calling JSON request else if (!strcmp(uri, "/1.json")) // send data as json sendJson(message); else if (!strcmp(uri, "/favicon.ico")) // a favicon //sendFavicon(client); send204(message); // if you don't have a favicon, send a defined empty response else // if the page is unknown, send HTTP response code 404 send404(message); message.flush(); client.stop(); } } } //---------------------- void sendPage(BufferedPrint &message){ // Serial.println("[server] 200 response send"); message.println(F("HTTP/1.0 200 OK\r\n" // \r\n header fields are terminated by a carriage return (CR) and line feed (LF) character sequence. "Content-Type: text/html\r\n" // The media type of the body of the request (used with POST and PUT requests). "\r\n" // a blank line to split HTTP header and HTTP body "<!doctype html>\n" // the HTTP page itself "<html lang='en'>\n" "<head>\n" "\t<meta charset='utf-8'>\n" "\t<meta name='viewport' content='width=device-width'>\n" "\t<title>Webserver as pin controller</title>\n" "</head>\n" "<body style='font-family:Helvetica, sans-serif'>\n" // a minimum style to avoid serifs "<h1>Webserver as pin controller</h1>\n" "<p>Buttons turn pins on or off</p>\n" "<form method='post' action='/' name='pins'>")); for (auto &pin : ledPin){ //client.print(F("<p>")); message.print(pin); message.print(F(" <input name='pinD")); message.print(pin); message.print(F("' type='submit' value='On'>")); message.print(F("<input name='pinD")); message.print(pin); message.print(F("' type='submit' value='Off'>")); if (digitalRead(pin)) message.print(F(" active")); message.print(F( "<br>\n")); } message.print (F("</form>\n")); message.print (F("</body>\n</html>")); message.flush(); } //-------------------- void send404(BufferedPrint &message){ // Serial.println("[server] response 404 file not found"); message.println(F("HTTP/1.0 404 Not Found\r\n" "Content-Type: text/plain\r\n" // we will send a simple plain text without html tags "\r\n" "File Fot Found")); message.flush(); } //-------------------- void send204(BufferedPrint &message){ // Serial.println("[server] response 204 no content"); message.println(F("HTTP/1.0 204 no content\r\n")); // no content message.flush(); } //-------------------- void sendFavicon(EthernetClient &client){ client.println("HTTP/1.0 204 no content\r\n"); } |
/* ***************************************************************** * Webserver - page update with Fetch/API * ***************************************************************** */ //------------------- void sendPage1(BufferedPrint &message){ // Serial.println("[server] 200 response send"); message.print(F("HTTP/1.0 200 OK\r\n" // \r\n header Fields are terminated by a carriage return (CR) and line feed (LF) character sequence. "Content-Type: text/html\r\n" // The media type of the body of the request (used with POST and PUT requests). "\r\n" // a blank line to split HTTP header and HTTP body "<!doctype html>\n" // the HTTP page itself "<html lang='en'>\n" "<head>\n" "\t<meta charset='utf-8'>\n" "\t<meta name='viewport' content='width=device-width'>\n" "\t<script src='/1.js'></script>\n" "\t<title>Webserver as pin controller</title>\n" "</head>\n" "<body style='font-family:Helvetica, sans-serif'>\n" // a minimum style to avoid serifs "<main>\n" "<h1>Arduino Webserver - Fetch Example</h1>\n")); message.print(F("<h2>Uptime</h2>\n" "<p>Webserver running since <span id='ss'>")); message.print(millis() / 1000); message.print(F("</span> seconds</p>\n")); message.print(F("<p><span id='hour'></span> <span id='min'></span> <span id='sec'></span></p>\n")); message.print(F("<h2>Analog Pins</h2>\n")); message.print(F("<p>")); for (size_t i; i < sizeof(analogPin); i++){ message.print(F("Analog pin ")); message.print(analogPin[i]); message.print(F(" = <span id='analog")); message.print(i); message.print(F("'>")); message.print(analogRead(analogPin[i])); message.print(F("</span><br>\n")); } message.print (F("</p>\n")); message.print(F("<h2>Digital Pins</h2>\n")); message.print(F("<p>")); for (size_t i; i < sizeof(ledPin); i++){ message.print(F("Digital pin ")); message.print(ledPin[i]); message.print(F(" = <span id='digital")); message.print(i); message.print(F("'>")); message.print(digitalRead(ledPin[i])); message.print(F("</span><br>\n")); } message.print (F("</p>\n")); message.print (F("</main>\n</body>\n</html>")); message.flush(); } //------------------- void sendJs(BufferedPrint &message){ message.print(F("HTTP/1.0 200\r\n" "Content-Type: application/javascript\r\n" "Cache-Control: public, max-age=2678400\r\n" "\r\n")); message.print(F("const url ='1.json';\n" "function renew(){\n" " document.getElementById('ss').style.color = 'blue'\n" " fetch(url)\n" // Call the fetch function passing the url of the API as a parameter " .then(response => {return response.json();})\n" " .then(jo => {\n" " document.getElementById('sec').innerHTML = Math.floor(jo['ss'] % 60); \n" " for (var i in jo)\n" " {if (document.getElementById(i)) document.getElementById(i).innerHTML = jo[i];}\n" // add other fields here (e.g. the delivered JSON name doesn't fit to the html id // finally, update the runtime " if (jo['ss'] > 60) { document.getElementById('min').innerHTML = Math.floor(jo['ss'] / 60 % 60);}\n" " if (jo['ss'] > 3600) {document.getElementById('hour').innerHTML = Math.floor(jo['ss'] / 3600 % 24);}\n" " if (jo['ss'] > 86400) {document.getElementById('day').innerHTML = Math.floor(jo['ss'] / 86400 % 7);}\n" " if (jo['ss'] > 604800) {document.getElementById('week').innerHTML = Math.floor(jo['ss'] / 604800 % 52);}\n" " document.getElementById('ss').style.color = 'black';\n" " })\n" " .catch(function() {\n" " document.getElementById('ss').style.color = 'red';\n" " });\n" "}\n" "document.addEventListener('DOMContentLoaded', renew, setInterval(renew, ")); message.print(F("5000")); // the update interval in milliseconds message.print(F("));")); message.flush(); } //------------------- void sendJson(BufferedPrint &message){ //void getJson(EthernetClient &client) // send the data as JSON to the web browser message.print(F("HTTP/1.0 200\r\n" "Content-Type: application/json\r\n" "\r\n" "{\"ss\":")); message.print(millis() / 1000); // timestamp in seconds for (size_t i = 0; i < sizeof(ledPin); i++){ message.print(F(",\"digital")); message.print(i); message.print(F("\":")); message.print(digitalRead(ledPin[i])); } for (size_t i = 0; i < sizeof(analogPin); i++){ message.print(F(",\"analog")); message.print(i); message.print(F("\":")); message.print(analogRead(analogPin[i])); } message.println(F("}")); message.flush(); } |
免責事項
本ソフトウエアは、あなたに対して何も保証しません。本ソフトウエアの関係者(他の利用者も含む)は、あなたに対して一切責任を負いません。
あなたが、本ソフトウエアを利用(コンパイル後の再利用など全てを含む)する場合は、自己責任で行う必要があります。本ソフトウエアの著作権はToolsBoxに帰属します。
本ソフトウエアをご利用の結果生じた損害について、ToolsBoxは一切責任を負いません。
ToolsBoxはコンテンツとして提供する全ての文章、画像等について、内容の合法性・正確性・安全性等、において最善の注意をし、作成していますが、保証するものではありません。
ToolsBoxはリンクをしている外部サイトについては、何ら保証しません。
ToolsBoxは事前の予告無く、本ソフトウエアの開発・提供を中止する可能性があります。
商標・登録商標
Microsoft、Windows、WindowsNTは米国Microsoft Corporationの米国およびその他の国における登録商標です。
Windows Vista、Windows XPは、米国Microsoft Corporation.の商品名称です。
LabVIEW、National Instruments、NI、ni.comはNational Instrumentsの登録商標です。
I2Cは、NXP Semiconductors社の登録商標です。
その他の企業名ならびに製品名は、それぞれの会社の商標もしくは登録商標です。
すべての商標および登録商標は、それぞれの所有者に帰属します。