今天繼續用 Arduino + ESP8266 來做 "Arduino Cookbook 錦囊妙計" 這本書 15.8 節的實驗, 它是在 15.7 節的基礎上擴展為依據要求不同的網址, 可顯示多個不同的網頁, 具體來說, 例如要求 192.168.2.114/digital/ 就顯示所有數位腳位的狀態; 而要求 192.168.2.114/analog/ 就顯示所有類比腳位的狀態. 事實上只要參考之前製作 IOT 模組時寫的 wifi 設定功能程式碼, 修改一下 15.7 節程式中的路徑處理部分就可以了, 參考 :
# 利用網頁控制 Arduino (一)
# 製作 Arduino Nano + ESP8266 物聯網模組 (三)
測試 1 :
#include <SoftwareSerial.h>
#define DEBUG true
SoftwareSerial esp8266(7,8); //(RX,TX)
String ssid="EDIMAX-tony";
String pwd="1234567890";
const int MAX_PAGE_NAME_LEN=8; //buffer size
char buffer[MAX_PAGE_NAME_LEN + 1]; //store page_name
void setup() {
Serial.begin(9600);
esp8266.begin(9600);
sendData("AT+RST\r\n",2000,DEBUG); // reset ESP8266
sendData("AT+CWMODE=1\r\n",1000,DEBUG); //configure as station
sendData("AT+CIPMUX=1\r\n",1000,DEBUG); //enable multiple connections
sendData("AT+CIPSERVER=1,80\r\n",2000,DEBUG); //turn on server 80 port
while (!connectWifi(ssid, pwd)) {
Serial.println("Connecting WiFi ... failed");
delay(2000);
}
sendData("AT+GMR\r\n",1000,DEBUG);
delay(3000); //wait for wifi connection to get local ip
sendData("AT+CIFSR\r\n",1000,DEBUG); //get ip address
}
void loop() {
if (esp8266.available()) { // check if esp8266 is sending message
if (esp8266.find("+IPD,")) {
delay(1000); //waiting for response: ?pinD2=1&pinA2=1023
int connectionId=esp8266.read()-48; //turn ASCII to number
if (esp8266.find("GET /")) { //retrieve page name (router)
memset(buffer, 0, sizeof(buffer)); //clear buffer (all set to 0)
String webpage="<html><body>";
if (esp8266.readBytesUntil('/', buffer, sizeof(buffer))) {
if (strcmp(buffer, "analog") == 0) { //show analog pins
Serial.println("analog");
webpage += "<h1>Analog Pins</h1>";
for (byte i=0; i<6; i++) {
webpage += "analog pin ";
webpage += i;
webpage += " is ";
webpage += analogRead(i);
webpage += "<br>";
}
}
else if (strcmp(buffer, "digital") == 0) {
Serial.println("digital");
webpage += "<h1>Digital Pins</h1>";
for (byte i=2; i<14; i++) {
webpage += "digital pin ";
webpage += i;
webpage += " is ";
if (digitalRead(i) == 1) {webpage += "HIGH";}
else {webpage += "LOW";}
webpage += "<br>";
}
}
else {
webpage += "<h1>Unknown Page</h1>";
webpage += "Recognized Pages are : <br>";
webpage += "192.168.x.x/analog/<br>";
webpage += "192.168.x.x/digital/<br>";
}
}
webpage += "</body></html>";
String cipSend="AT+CIPSEND=";
cipSend += connectionId;
cipSend += ",";
cipSend += webpage.length();
cipSend += "\r\n";
sendData(cipSend,1000,DEBUG);
sendData(webpage,2000,DEBUG);
sendData("AT+CIPCLOSE=" + (String)connectionId + "\r\n",3000,DEBUG);
}
}
}
}
boolean connectWifi(String ssid, String pwd) {
String res=sendData("AT+CWJAP=\"" + ssid + "\",\"" + pwd + "\"\r\n",8000,DEBUG);
res.replace("\r\n",""); //remove all line terminator
if (res.indexOf("OK") != -1) {return true;}
else {return false;}
}
String sendData(String command, const int timeout, boolean debug) {
String res="";
esp8266.print(command);
long int time=millis();
while ((time + timeout) > millis()) {
while(esp8266.available()) {res.concat((char)esp8266.read());}
}
if (debug) {Serial.print(res);}
return res;
}
因為要擷取網址根目錄下的路徑並進行比對以決定輸出何種網頁, 需要一個 字元陣列 buffer 來儲存路徑, 因為 digital 有 7 個字元, 因此這裡宣告一個長度為 8 字元的緩衝區即足夠.
如果客戶端連線 192.168.x.x/digial/ 的話, ESP8266 會回應含有 GET /digital/ HTTP1.1 標頭的訊息到緩衝區裡, 因此可用 find() 函數找到連線要求的標記 "GET /", 然後以 readBytesUntil() 找尋下一個 "/", 將之間的路徑字元串 digital 存入 buffer 裡, 這樣便能用 strcmp() 函數來比對了.
此程式編譯後記憶體耗用情形如下 :
草稿碼使用了 9,666 bytes (31%) 的程式存儲空間。最大值為 30,720 bytes。
全域變數使用了 804 bytes (39%) 動態記憶體,剩餘 1,244 bytes 的局部變數。最大值為 2,048 bytes 。
AT+RST
OK
bB�鑭b禔S��"� B�侒��餾�
[System Ready, Vendor:www.ai-thinker.com]
AT+CWMODE=1
no change
AT+CIPMUX=1
OK
AT+CIPSERVER=1,80
OK
AT+CWJAP="EDIMAX-tony1966","1234567890"
OK
AT+GMR
0018000902
OK
AT+CIFSR
192.168.2.106
OK
我們也可以把前一篇實驗裡更改 Arduino 輸出腳位狀態的功能整合進來, 增加一個 update 路徑如下 :
192.168.x.x/update/?pinD3=1&pinA2=1023
程式如下 :
測試 2 :
#include <SoftwareSerial.h>
#define DEBUG true
SoftwareSerial esp8266(7,8); //(RX,TX)
String ssid="EDIMAX-tony";
String pwd="1234567890";
const int MAX_PAGE_NAME_LEN=8; //buffer size
char buffer[MAX_PAGE_NAME_LEN + 1]; //store page_name
void setup() {
Serial.begin(9600);
esp8266.begin(9600);
sendData("AT+RST\r\n",2000,DEBUG); // reset ESP8266
sendData("AT+CWMODE=1\r\n",1000,DEBUG); //configure as station
sendData("AT+CIPMUX=1\r\n",1000,DEBUG); //enable multiple connections
sendData("AT+CIPSERVER=1,80\r\n",2000,DEBUG); //turn on server 80 port
while (!connectWifi(ssid, pwd)) {
Serial.println("Connecting WiFi ... failed");
delay(2000);
}
sendData("AT+GMR\r\n",1000,DEBUG);
delay(3000); //wait for wifi connection to get local ip
sendData("AT+CIFSR\r\n",1000,DEBUG); //get ip address
}
void loop() {
if (esp8266.available()) { // check if esp8266 is sending message
if (esp8266.find("+IPD,")) {
delay(1000); //waiting for response: ?pinD2=1&pinA2=1023
int connectionId=esp8266.read()-48; //turn ASCII to number
if (esp8266.find("GET /")) { //retrieve page name (router)
memset(buffer, 0, sizeof(buffer)); //clear buffer (all set to 0)
String webpage="<html><body>";
if (esp8266.readBytesUntil('/', buffer, sizeof(buffer))) {
if (strcmp(buffer, "analog") == 0) { //show analog pins
Serial.println("analog");
webpage += "<h1>Analog Pins</h1>";
for (byte i=0; i<6; i++) {
webpage += "analog pin ";
webpage += i;
webpage += " is ";
webpage += analogRead(i);
webpage += "<br>";
}
}
else if (strcmp(buffer, "digital") == 0) {
Serial.println("digital");
webpage += "<h1>Digital Pins</h1>";
for (byte i=2; i<14; i++) {
webpage += "digital pin ";
webpage += i;
webpage += " is ";
if (digitalRead(i) == 1) {webpage += "HIGH";}
else {webpage += "LOW";}
webpage += "<br>";
}
}
else if (strcmp(buffer, "update") == 0) {
Serial.println("update");
while (esp8266.findUntil("pin", "\r\n")) {
char type=(char)esp8266.read();
int pin=esp8266.parseInt();
int val=esp8266.parseInt();
if (type=='D') {
Serial.print("Digital pin ");
webpage += "Digital pin ";
webpage += pin;
webpage += " is updated to";
webpage += val;
pinMode(pin, OUTPUT);
digitalWrite(pin, val);
}
else if (type=='A') {
Serial.print("Analog pin ");
webpage += "Analog pin ";
webpage += pin;
webpage += " is updated to ";
webpage += val;
analogWrite(pin, val);
}
else {
Serial.println("Unexpected type:" + type);
}
Serial.print(pin);
Serial.print("=");
Serial.println(val);
webpage += "<br>";
}
}
else {
webpage += "<h1>Unknown Page</h1>";
webpage += "Recognized Pages are : <br>";
webpage += "192.168.x.x/analog/<br>";
webpage += "192.168.x.x/digital/<br>";
}
}
webpage += "</body></html>";
String cipSend="AT+CIPSEND=";
cipSend += connectionId;
cipSend += ",";
cipSend += webpage.length();
cipSend += "\r\n";
sendData(cipSend,1000,DEBUG);
sendData(webpage,2000,DEBUG);
sendData("AT+CIPCLOSE=" + (String)connectionId + "\r\n",3000,DEBUG);
}
}
}
}
boolean connectWifi(String ssid, String pwd) {
String res=sendData("AT+CWJAP=\"" + ssid + "\",\"" + pwd + "\"\r\n",8000,DEBUG);
res.replace("\r\n",""); //remove all line terminator
if (res.indexOf("OK") != -1) {return true;}
else {return false;}
}
String sendData(String command, const int timeout, boolean debug) {
String res="";
esp8266.print(command);
long int time=millis();
while ((time + timeout) > millis()) {
while(esp8266.available()) {res.concat((char)esp8266.read());}
}
if (debug) {Serial.print(res);}
return res;
}
此程式編譯後記憶體耗用情形如下 :
草稿碼使用了 11,372 bytes (37%) 的程式存儲空間。最大值為 30,720 bytes。
全域變數使用了 838 bytes (40%) 的動態記憶體,剩餘 1,210 bytes 供局部變數。最大值為 2,048 bytes 。
執行結果如下 :
在上面的實驗中, 路徑 digital 與 analog 的腳位狀態也可以用 HTML 的表格來呈現, 這樣會比較整齊美觀, 這也是 "Arduino Cookbook 錦囊妙計" 這本書 15.9 節的實驗, 其實也不過就是在輸出網頁格式上加上 TABLE 的相關元素而已, 如下列測試 3 所示 :
測試 3 :
#include <SoftwareSerial.h>
#define DEBUG true
SoftwareSerial esp8266(7,8); //(RX,TX)
String ssid="EDIMAX-tony";
String pwd="1234567890";
const int MAX_PAGE_NAME_LEN=8; //buffer size
char buffer[MAX_PAGE_NAME_LEN + 1]; //store page_name
void setup() {
Serial.begin(9600);
esp8266.begin(9600);
sendData("AT+RST\r\n",2000,DEBUG); // reset ESP8266
sendData("AT+CWMODE=1\r\n",1000,DEBUG); //configure as station
sendData("AT+CIPMUX=1\r\n",1000,DEBUG); //enable multiple connections
sendData("AT+CIPSERVER=1,80\r\n",2000,DEBUG); //turn on server 80 port
while (!connectWifi(ssid, pwd)) {
Serial.println("Connecting WiFi ... failed");
delay(2000);
}
sendData("AT+GMR\r\n",1000,DEBUG);
delay(3000); //wait for wifi connection to get local ip
sendData("AT+CIFSR\r\n",1000,DEBUG); //get ip address
}
void loop() {
if (esp8266.available()) { // check if esp8266 is sending message
if (esp8266.find("+IPD,")) {
delay(1000); //waiting for response: ?pinD2=1&pinA2=1023
int connectionId=esp8266.read()-48; //turn ASCII to number
if (esp8266.find("GET /")) { //retrieve page name (router)
memset(buffer, 0, sizeof(buffer)); //clear buffer (all set to 0)
String webpage="<html><body>";
if (esp8266.readBytesUntil('/', buffer, sizeof(buffer))) {
if (strcmp(buffer, "analog") == 0) { //show analog pins
Serial.println("analog");
webpage += "<h1>Analog Pins</h1>";
webpage += "<table border=1>";
for (byte i=0; i<6; i++) {
webpage += "<tr><td>analog pin ";
webpage += i;
webpage += "</td><td>";
webpage += analogRead(i);
webpage += "</td></tr>";
}
webpage += "</table>";
}
else if (strcmp(buffer, "digital") == 0) {
Serial.println("digital");
webpage += "<h1>Digital Pins</h1>";
webpage += "<table border=1>";
for (byte i=2; i<=9; i++) {
webpage += "<tr><td>digital pin ";
webpage += i;
webpage += "</td><td>";
if (digitalRead(i) == 1) {webpage += "HIGH";}
else {webpage += "LOW";}
webpage += "</td></tr>";
}
webpage += "</table>";
}
else if (strcmp(buffer, "update") == 0) {
Serial.println("update");
while (esp8266.findUntil("pin", "\r\n")) {
char type=(char)esp8266.read();
int pin=esp8266.parseInt();
int val=esp8266.parseInt();
if (type=='D') {
Serial.print("Digital pin ");
webpage += "Digital pin ";
webpage += pin;
webpage += " is updated to";
webpage += val;
pinMode(pin, OUTPUT);
digitalWrite(pin, val);
}
else if (type=='A') {
Serial.print("Analog pin ");
webpage += "Analog pin ";
webpage += pin;
webpage += " is updated to ";
webpage += val;
analogWrite(pin, val);
}
else {
Serial.println("Unexpected type:" + type);
}
Serial.print(pin);
Serial.print("=");
Serial.println(val);
webpage += "<br>";
}
}
else {
webpage += "<h1>Unknown Page</h1>";
webpage += "Recognized Pages are : <br>";
webpage += "192.168.x.x/analog/<br>";
webpage += "192.168.x.x/digital/<br>";
}
}
webpage += "</body></html>";
String cipSend="AT+CIPSEND=";
cipSend += connectionId;
cipSend += ",";
cipSend += webpage.length();
cipSend += "\r\n";
sendData(cipSend,1000,DEBUG);
sendData(webpage,5000,DEBUG);
sendData("AT+CIPCLOSE=" + (String)connectionId + "\r\n",3000,DEBUG);
}
}
}
}
boolean connectWifi(String ssid, String pwd) {
String res=sendData("AT+CWJAP=\"" + ssid + "\",\"" + pwd + "\"\r\n",8000,DEBUG);
res.replace("\r\n",""); //remove all line terminator
if (res.indexOf("OK") != -1) {return true;}
else {return false;}
}
String sendData(String command, const int timeout, boolean debug) {
String res="";
esp8266.print(command);
long int time=millis();
while ((time + timeout) > millis()) {
while(esp8266.available()) {res.concat((char)esp8266.read());}
}
if (debug) {Serial.print(res);}
return res;
}
此程式與上面兩個測試差別僅僅是在顯示數位與類比腳位部分添加 HTML 的表格元素而已 (藍色部分), 編譯後記憶體耗用情形如下 :
草稿碼使用了 11,458 bytes (37%) 的程式存儲空間。最大值為 30,720 bytes。
全域變數使用了 896 bytes (43%) 的動態記憶體,剩餘 1,152 bytes 供局部變數。最大值為 2,048 bytes 。
以手機連覽器連線 192.168.2.106/digital/ 結果如下 :
連線 192.168.2.106/analog/ 結果如下 :
注意, 上面程式中只顯示了 D2~D9 這 8 個數位腳狀態, 而非如測試 1 中顯示 D2~D13, 原因是若顯示到 D10 以上, 瀏覽器會等很久都沒回應, 最後輸出 "無法顯示網頁", 這應該是 HTML 的表格元素佔用了太多 SRAM 記憶體所致.
因此我參考之前所用的 F() 函數, 將字串常數放在程式記憶體 (Flash) 以節省 SRAM 記憶體的技巧來解決此問題, 參考 :
# 使用 ITEAD WeeESP8266 函式庫改寫物聯網模組 wifi 設定程式
# https://www.arduino.cc/en/Serial/Print
You can pass flash-memory based strings to Serial.print() by wrapping them with F(). For example :
Serial.print(F(“Hello World”))
修改後程式如下 :
測試 4 :
#include <SoftwareSerial.h>
#define DEBUG true
SoftwareSerial esp8266(7,8); //(RX,TX)
const String ssid="EDIMAX-tony";
const String pwd="1234567890";
const int MAX_PAGE_NAME_LEN=8; //buffer size
char buffer[MAX_PAGE_NAME_LEN + 1]; //store page_name
void setup() {
Serial.begin(9600);
esp8266.begin(9600);
sendData(F("AT+RST\r\n"),2000,DEBUG); // reset ESP8266
sendData(F("AT+CWMODE=1\r\n"),1000,DEBUG); //configure as station
sendData(F("AT+CIPMUX=1\r\n"),1000,DEBUG); //enable multiple connections
sendData(F("AT+CIPSERVER=1,80\r\n"),2000,DEBUG); //turn on server 80 port
while (!connectWifi(ssid, pwd)) {
Serial.println(F("Connecting WiFi ... failed"));
delay(2000);
}
sendData(F("AT+GMR\r\n"),1000,DEBUG);
delay(3000); //wait for wifi connection to get local ip
sendData(F("AT+CIFSR\r\n"),1000,DEBUG); //get ip address
}
void loop() {
if (esp8266.available()) { // check if esp8266 is sending message
if (esp8266.find("+IPD,")) {
delay(1000); //waiting for response: ?pinD2=1&pinA2=1023
int connectionId=esp8266.read()-48; //turn ASCII to number
if (esp8266.find("GET /")) { //retrieve page name (router)
memset(buffer, 0, sizeof(buffer)); //clear buffer (all set to 0)
String webpage=F("<html><body>");
if (esp8266.readBytesUntil('/', buffer, sizeof(buffer))) {
if (strcmp(buffer, "analog") == 0) { //show analog pins
Serial.println(F("analog"));
webpage += F("<h1>Analog Pins</h1>");
webpage += F("<table border=1>");
for (byte i=0; i<6; i++) {
webpage += F("<tr><td>analog pin ");
webpage += i;
webpage += F("</td><td>");
webpage += analogRead(i);
webpage += F("</td></tr>");
}
webpage += F("</table>");
}
else if (strcmp(buffer, "digital") == 0) {
Serial.println(F("digital"));
webpage += F("<h1>Digital Pins</h1>");
webpage += F("<table border=1>");
for (byte i=2; i<=13; i++) {
webpage += F("<tr><td>digital pin ");
webpage += i;
webpage += F("</td><td>");
if (digitalRead(i) == 1) {webpage += F("HIGH");}
else {webpage += F("LOW");}
webpage += F("</td></tr>");
}
webpage += F("</table>");
}
else if (strcmp(buffer, "update") == 0) {
Serial.println("update");
while (esp8266.findUntil("pin", "\r\n")) {
char type=(char)esp8266.read();
int pin=esp8266.parseInt();
int val=esp8266.parseInt();
if (type=='D') {
Serial.print(F("Digital pin "));
webpage += F("Digital pin ");
webpage += pin;
webpage += F(" is updated to");
webpage += val;
pinMode(pin, OUTPUT);
digitalWrite(pin, val);
}
else if (type=='A') {
Serial.print(F("Analog pin "));
webpage += F("Analog pin ");
webpage += pin;
webpage += F(" is updated to ");
webpage += val;
analogWrite(pin, val);
}
else {
Serial.print(F("Unexpected type:"));
Serial.println(type);
}
Serial.print(pin);
Serial.print("=");
Serial.println(val);
webpage += F("<br>");
}
}
else {
webpage += F("<h1>Unknown Page</h1>");
webpage += F("Recognized Pages are : <br>");
webpage += F("192.168.x.x/analog/<br>");
webpage += F("192.168.x.x/digital/<br>");
}
}
webpage += F("</body></html>");
String cipSend=F("AT+CIPSEND=");
cipSend += connectionId;
cipSend += F(",");
cipSend += webpage.length();
cipSend += F("\r\n");
sendData(cipSend,1000,DEBUG);
sendData(webpage,5000,DEBUG);
sendData("AT+CIPCLOSE=" + (String)connectionId + F("\r\n"),3000,DEBUG);
}
}
}
}
boolean connectWifi(String ssid, String pwd) {
String res=sendData("AT+CWJAP=\"" + ssid + F("\",\"") + pwd + F("\"\r\n"),8000,DEBUG);
res.replace("\r\n",""); //remove all line terminator
if (res.indexOf("OK") != -1) {return true;}
else {return false;}
}
String sendData(String command, const int timeout, boolean debug) {
String res=F("");
esp8266.print(command);
long int time=millis();
while ((time + timeout) > millis()) {
while(esp8266.available()) {res.concat((char)esp8266.read());}
}
if (debug) {Serial.print(res);}
return res;
}
此處我將所有可以使用 F() 函數的字串常數都用上了, 少部分沒有使用是因為編譯失敗的關係, 可能是資料型態轉換不合法, 就暫時不去研究了. 經過如此調整, 編譯後記憶體耗用如下 :
草稿碼使用了 11,938 bytes (38%) 的程式存儲空間。最大值為 30,720 bytes。
全域變數使用了 436 bytes (21%) 的動態記憶體,剩餘 1,612 bytes 供局部變數。最大值為 2,048 bytes 。
可見將字串常數放到程式記憶體去後, 動態記憶體耗用比已降至 21%, 以手機瀏覽器連線 192.168.2.106/digital/ 結果已能正常輸出網頁矣 :
可見只有 2K SRAM 的 Arduino 真的必須對記憶體的使用錙銖必較啊!
OK, 接下來要將上面測試 4 改用 WeeESP8266 函式褲來寫, 程式如下 :
測試 5 :
#include <SoftwareSerial.h>
#include "ESP8266.h"
SoftwareSerial esp8266(7,8); //(D7:RX, D8:TX)
ESP8266 wifi(esp8266); //create wifi object
const String ssid="EDIMAX-tony";
const String pwd="1234567890";
void setup() {
Serial.begin(9600);
wifi.restart(); //reset ESP8266
Serial.print(F("Firmware version ... "));
Serial.println(wifi.getVersion()); //
boolean ok=true; //default result ok
ok &= wifi.setOprToStation(); //set esp8266 : mode 1
ok &= wifi.enableMUX(); //enable multi-connection
ok &= wifi.startTCPServer(80); //enable sep8266 web server
if (ok) {Serial.println(F("Enable web server ... OK"));}
while (!wifi.joinAP(ssid, pwd)) {
Serial.println(F("Connecting WiFi ... failed"));
delay(2000);
}
delay(3000); //wait for wifi connection to get local ip
Serial.print(F("IP ... "));
Serial.println(wifi.getLocalIP()); //show local ip
}
void loop() {
uint8_t buffer[128]={0}; //init receiving buffer to store esp response
uint8_t mux_id; //TCP connection id (0~4)
uint32_t len=wifi.recv(&mux_id, buffer, sizeof(buffer), 2000); //get response
if (len > 0) { //data received, fetch requested path from HTTP
String path=F(""); //init path
int digitalRequests=0; //digital request counts
int analogRequests=0; //analog request counts
for (uint32_t i=0; i<len; i++) { //concat path string
if (!path.endsWith(F(" HTTP/"))) {path.concat((char)buffer[i]);}
else {break;}
} //path : "+IPD,1,248:GET /digital/ HTTP/"
path=path.substring(path.indexOf(F("/")) + 1, path.indexOf(F(" HTTP/")));
Serial.println(path); //eg : digital, analog, update/?pinD2=1
String webpage=F("<html><body>");
if (path.startsWith("analog")) {
Serial.println(F("analog"));
webpage += F("<h1>Analog Pins</h1>");
webpage += F("<table border=1>");
for (byte i=0; i<6; i++) {
webpage += F("<tr><td>analog pin ");
webpage += i;
webpage += F("</td><td>");
webpage += analogRead(i);
webpage += F("</td></tr>");
}
webpage += F("</table>");
}
else if (path.startsWith("digital")) {
Serial.println(F("digital"));
webpage += F("<h1>Digital Pins</h1>");
webpage += F("<table border=1>");
for (byte i=2; i<=10; i++) {
webpage += F("<tr><td>digital pin ");
webpage += i;
webpage += F("</td><td>");
if (digitalRead(i) == 1) {webpage += F("HIGH");}
else {webpage += F("LOW");}
webpage += F("</td></tr>");
}
webpage += F("</table>");
}
else if (path.startsWith("update")) {
path=path.substring(path.indexOf(F("?")) + 1); //pinD2=1&pinA3=1023
while (path.indexOf(F("pin")) != -1) { //parsing not finished
char type=path.charAt(3);
int pin=path.substring(4, path.indexOf(F("="))).toInt();
int val=path.substring(path.indexOf(F("=")) + 1).toInt();
if (type=='D') {
Serial.print(F("Digital pin "));
webpage += F("Digital pin ");
webpage += pin;
webpage += F(" is updated to");
webpage += val;
pinMode(pin, OUTPUT);
digitalWrite(pin, val);
}
else if (type=='A') {
Serial.print(F("Analog pin "));
webpage += F("Analog pin ");
webpage += pin;
webpage += F(" is updated to ");
webpage += val;
analogWrite(pin, val);
}
else {
Serial.print(F("Unexpected type:"));
Serial.println(type);
webpage += F("Unexpected type:");
webpage += type;
}
Serial.print(pin);
Serial.print(F("="));
Serial.println(val);
webpage += F("<br>");
if (path.indexOf(F("&")) != -1) { //multiple parameters
path=path.substring(path.indexOf(F("&")) + 1); //remove parameter
}
else {path=F("");} //terminate loop
}
}
else {
webpage += F("<h1>Unknown Page</h1>");
webpage += F("Recognized Pages are : <br>");
webpage += F("192.168.x.x/analog/<br>");
webpage += F("192.168.x.x/digital/<br>");
webpage += F("192.168.x.x/update/?pinD2=1&pinA3=1023<br>");
}
webpage += F("</body></html>");
wifi.send(mux_id, (const uint8_t*)webpage.c_str(), webpage.length());
wifi.releaseTCP(mux_id);
}
}
有了測試 4 在記憶體耗用上的教訓, 在測試 5 中我已完全將能用 F() 函數來節省的字串常數都優化了, 甚至 ssid 與 pwd 兩個也加上 const 來減少 SRAM 的使用. 編譯後記憶體耗用情形如下 :
稿碼使用了 16,030 bytes (52%) 的程式存儲空間。最大值為 30,720 bytes。
全域變數使用了 725 bytes (35%) 的動態記憶體,剩餘 1,323 bytes 供局部變數。最大值為 2,048 bytes 。
這數字看起來還 OK, 用手機瀏覽器測試也能得到與上面測試 4 一樣的結果, 除了 digital 的部分只能顯示到 D10 外. 請注意上面程式的 digital 迴圈部分, 上限只到 10, 若改為 11 將遇到上面測試 3 網頁無反應問題. 看來看去也找不到哪裡還可以優化, 只好這樣了. 這也是使用函式庫的代價.
另外, 由於字串處理使用 startsWith() 的關係, analog 與 digital 後面的 / 不加也沒關係 :
192.168.2.106/analog
192.168.2.106/digital
這是跟上面的測試不同的地方.
參考 :
# Arduino Uart 互傳字串 與 字串處理 依特定符號切割
# Arduino輸入字串切割成陣列
# [C/C++] 切割字串函數:strtok, Network mac address 分割
# How to convert string to char array in C++?
# c_str() 用法 (C/C++)