2016年7月12日 星期二

★ 製作 Arduino Nano + ESP8266 物聯網模組 (四) : 完結篇

昨天在看柯博文寫的 "Arduino 互動設計專題與實戰" 這本書時, 發現有一些感測器是吃 3.3V 的, 這讓我想起前陣子焊好的兩塊新版 IOT 模組有個缺失, 即竟然沒有把 AMS1117 的 3.3V 電源接出來! 遇到需要 3.3V 的感測器時, 明明板子上就有 3.3V 電源卻沒拉出來, 還得另外準備, 這不是令人感到洩氣嗎?

我拿出上一版的佈線圖, 仔細端詳後, 發現模式切換開關上面還有空位可以擠進 2*1 的針腳座, 但佈線需做局部更動, 正面還需加焊一條跳線將 3.3V 引出來. 參考 :

# 製作 Arduino Nano + ESP8266 物聯網模組 (三)

修改後的佈線圖如下 :

正面

反面

圖例 : 橘色為正面跳線 (使用細包覆線), 藍色為反面焊接線, 綠色為電阻兩端金屬線. AMS1117 是焊在反面.

我把已經在使用的一塊板子拿來修改, 加焊一個 2*1 針腳與一條跳線, 測試 OK. 接下來我想是不是要把 GPIO0 接出來, 看看此模組能不能取代 USB-TTL 轉換線, 直接燒錄 ESP8266 的韌體 (GPIO0 要接地). 不過要先測試看看是否可行, 如果可以的話, 還可以再放一個 2*1 針腳來控制 GPIO0 的準位, 工作模式為 HIGH (3.3V), 燒錄模式為 LOW.

2016-07-12 補充 :

晚上進行了 IOT 模組是否能取代 USB-TTL 轉換線的實驗, 我使用了最早製作的 ESP8266 轉換板來模擬 IOT 模組, 並將其 GPIO0 接地. 然後拆封了一顆新的 ESP-01 模組來測試, 其韌體版本比較新, 不是最穩的 0.9.2 版 (是 0.9.5 版), 其預設速率是 115200. 我把 IOT 模組設在工作模式, 並且上傳了簡單的軟體串列埠送收程式 (loop 中) :

    if (esp8266.available()) {Serial.write(esp8266.read());}
    //send char to ESP8266 if got char from serial monitor window
    if (Serial.available()) {esp8266.write(Serial.read());}  

結果是失敗, 不論用 Arduino IDE 序列埠監控視窗還是 Realterm 在任何速率都沒反應, 直接用韌體更新程式也顯示 connection failed. 我想可能是因為 Arduino 串列埠速率固定是 9600 之故. 所以透過軟體序列埠轉送燒錄訊息至 ESP8266 的想法是錯誤的, 上面的佈線圖就是此模組的最終更新了, 不需要再把 GPIO0 接出來了.

另外記一下我在九乘九買到的富士 A108 三秒膠 (特價 19 元), 免得忘記是哪一款 :


這款封裝設計得不錯, 放了一陣子還是很容易打開, 不像之前買的 10 元傳統三秒膠, 使用前必須用美工刀削掉管嘴尖端, 放了幾天就無法旋開蓋子 (久沒用就整個乾掉囉), 逼得我只好暴力破壞, 但這麼一來整管都必須用掉, 但實際上我只要用一點點, 換句話說整條都報廢了. 這個富士的就不會這樣, 雖然貴一點也值得.

三秒膠在焊接前固定元件蠻好用的, 但是要注意不要擠太多, 用一點點可以稍微固定即可, 擠太多若滲入元件內部就不妙了, 特別是滑動開關與排母, 三秒膠若進到裡面, 滑動開關就固化無法滑動, 排母就插不進去了.

2016-08-13 補充 :

為了優化記憶體耗用情形, 我把 IOT 模組的程式架構用 F() 函數將字串常數放到 Flash, 最新程式如下 :

#include <SoftwareSerial.h>
#define DEBUG true
//-----application include & def listed HERE-----


//system use please do not edit
SoftwareSerial esp8266(7,8); //(RX,TX)
const int SW_PIN=4; //Pin to switch configuration or working mode
const int MAX_PAGE_NAME_LEN=48;  //buffer size
char buffer[MAX_PAGE_NAME_LEN + 1]; //store page_name/ssid/pwd
int mode; //store current mode(LOW=wifi setup, HIGH=working)

//-----application global variables listed HERE-----


void setup() {
  //system use please do not edit
  Serial.begin(9600);
  esp8266.begin(9600);
  sendData(F("AT+RST\r\n"),2000,DEBUG); // reset ESP8266
  pinMode(SW_PIN, INPUT);
  mode=digitalRead(SW_PIN);
  sendData(F("AT+GMR\r\n"),1000,DEBUG);
  if (mode==LOW) { //setup mode : for wifi configuration
    sendData(F("AT+CWMODE=3\r\n"),1000,DEBUG); //configure as access point
    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  
    }
  else {  //working mode : for running application
    sendData(F("AT+CWMODE=1\r\n"),1000,DEBUG); //configure as a station
    delay(3000); //wait for wifi connection to get local ip
    }
  sendData(F("AT+CIFSR\r\n"),1000,DEBUG); //get ip address
  //-----application setup codes listed HERE-----


  }

void loop() {
  if (mode==LOW) {setupWifi();} //system use please do not edit
  else {
    //-----application loop codes listed HERE-----

 
    }
  }

void setupWifi() { //system use please do not edit
  if (esp8266.available()) { // check if the esp is sending a message
    if (esp8266.find("+IPD,")) {
      delay(1000);
      //esp8266 link response : +IPD,0,498:GET / HTTP/1.1
      //retrieve connection ID from response (0~4, after "+IPD,")
      int connectionId=esp8266.read()-48;  //from ASCII to number
      //subtract 48 because read() returns ASCII decimal value
      //and in ASCII, "0" (the first decimal number) starts at 48
      if (esp8266.find("GET /")) { //retrieve page name (router)
        memset(buffer, 0, sizeof(buffer));  //clear buffer (all set to 0)
        if (esp8266.readBytesUntil('/', buffer, sizeof(buffer))) {
          if (strcmp(buffer, "update") == 0) { //update wifi
            //"?ssid=aaa&pwd=bbb HTTP/1.1"      
            esp8266.find("?ssid="); //skip ssid token
            memset(buffer, 0, sizeof(buffer));  //clear buffer (all set to 0)
            esp8266.readBytesUntil('&', buffer, sizeof(buffer)); //retrieve ssid
            String ssid=buffer;
            esp8266.find("pwd="); //skip pwd token
            memset(buffer, 0, sizeof(buffer));  //clear buffer (all set to 0)
            esp8266.readBytesUntil(' ', buffer, sizeof(buffer)); //retrieve pwd
            String pwd=buffer;
            //configure as a station
            String res=sendData("AT+CWJAP=\"" + ssid + "\",\"" + pwd + "\"\r\n",6000,DEBUG);
            //show setup result
            String webpage=F("<html>Wifi setup ");
            if (res.indexOf("OK") != -1) {webpage += F("OK!</html>");}
            else {webpage += F("Failed!</html>");}
            String cipSend=F("AT+CIPSEND=");
            cipSend += connectionId;
            cipSend += F(",");
            cipSend += webpage.length();
            cipSend += F("\r\n");
            sendData(cipSend,1000,DEBUG);
            sendData(webpage,2000,DEBUG);      
            String closeCommand=F("AT+CIPCLOSE=");
            closeCommand += connectionId; // append connection id
            closeCommand += F("\r\n");
            sendData(closeCommand,3000,DEBUG);          
            }
          else { //show setup page
            String webpage=F("<html><form method=get action='/update/'>SSID ");
            webpage += F("<input name=ssid type=text><br>");
            String cipSend=F("AT+CIPSEND=");
            cipSend += connectionId;
            cipSend += F(",");
            cipSend += webpage.length();
            cipSend += F("\r\n");
            sendData(cipSend,1000,DEBUG);
            sendData(webpage,2000,DEBUG);
            webpage=F("PWD <input name=pwd type=text> ");
            cipSend=F("AT+CIPSEND=");
            cipSend += connectionId;
            cipSend += F(",");
            cipSend += webpage.length();
            cipSend += F("\r\n");
            sendData(cipSend,1000,DEBUG);
            sendData(webpage,2000,DEBUG);
            webpage=F("<input type=submit value=Connect></form></html>");
            cipSend = F("AT+CIPSEND=");
            cipSend += connectionId;
            cipSend += F(",");
            cipSend += webpage.length();
            cipSend += F("\r\n");
            sendData(cipSend,1000,DEBUG);
            sendData(webpage,2000,DEBUG);
            String closeCommand =F("AT+CIPCLOSE=");
            closeCommand += connectionId; // append connection id
            closeCommand += F("\r\n");
            sendData(closeCommand,3000,DEBUG);
            }
          }
        }
      }
    }
  }

String sendData(String command, const int timeout, boolean debug) {
  String response="";
  esp8266.print(command); // send the read character to the esp8266
  long int time=millis();
  while ((time+timeout) > millis()) {
    while(esp8266.available()) {
      // The esp has data so display its output to the serial window
      char c=esp8266.read(); // read the next character.
      response += c;
      }
    }
  if (debug) {Serial.print(response);}
  return response;
  }

編譯後記憶體耗用如下 :

草稿碼使用了 9,278 bytes (30%) 的程式存儲空間。最大值為 30,720 bytes。
全域變數使用了 412 bytes (20%) 的動態記憶體,剩餘 1,636 bytes 供局部變數。最大值為 2,048 bytes 。

與優化之前的 35% 相比, 全域變數節省了 15%, 真是太驚人了! 而代價僅僅是 Flash 程式從 29% 提升為 30% 而已, 非常值得. 參考 :

# 製作 Arduino Nano + ESP8266 物聯網模組 (三)

此 IOT 模組電路圖如下 :




若懶得焊接板子, 可以照上面電路圖自行用麵包板接線, 只不過 AMS1117 不是 DIP 封裝, 沒辦法插入麵包板, 比較麻煩而已.


沒有留言 :