2016年7月4日 星期一

使用 WeeESP8266 函式庫重作 NTP 對時實驗

在完成 WeeESP8266 函式庫的伺服器實驗後, 我想應該放個應用進去, 看看編譯後記憶體耗掉多少, 會不會因為吃記憶體導致程式不正常. 於是我選定 NTP 對時實驗當作負載試試看, 參考去年 11 月的實驗記錄 :

# 使用 ITEAD 的 WeeESP8266 函式庫進行網路對時

我把其中的 ntpupdate() 函式複製到以 WeeESP8266 函式庫為基礎的 wifi 設定架構中, 完整程式如下 :

#include <SoftwareSerial.h>
#include "ESP8266.h"
//-----application include & def listed HERE-----
//NTP 伺服器 IP 82.209.243.241, 91.226.136.136, 192.5.41.40
#define HOST_NAME   "91.226.136.136"
#define HOST_PORT   123  //NTP 埠號

//system use please do not edit
SoftwareSerial esp8266(7,8); //(D7:RX, D8:TX)
ESP8266 wifi(esp8266); //create wifi obj
const int SW_PIN=4; //Pin to switch configuration or working mode
int mode; //store current mode(LOW=configuration, HIGH=working)

void setup() {
  //system use please do not edit
  Serial.begin(9600);
  wifi.restart();  //reset ESP8266
  Serial.print(F("Firmware version ... "));
  Serial.println(wifi.getVersion());  
  pinMode(SW_PIN, INPUT);
  mode=digitalRead(SW_PIN);
  boolean ok=true;
  if (mode==LOW) { //setup mode : for wifi configuration
    ok &= wifi.setOprToStationSoftAP();    
    ok &= wifi.enableMUX();
    ok &= wifi.startTCPServer(80);
    if (ok) {Serial.println(F("Setup mode ... OK"));}
    }
  else { //working mode : for running application
    ok &= wifi.setOprToStation();
    if (ok) {Serial.println(F("Working mode ... OK"));}
    }
  delay(5000);
  Serial.print(F("IP ... "));
  Serial.println(wifi.getLocalIP());
  //-----application setup codes listed HERE-----
  }

void loop() {
  if (mode==LOW) {setupWifi();}
  else { //-----application loop codes listed bellow-----
    //if (esp8266.available()) {Serial.write(esp8266.read());}
    //if (Serial.available()) {esp8266.write(Serial.read());}
    ntpupdate();  //呼叫 NTP 對時函數
    delay(10000);
    }
  }

void setupWifi() {
  uint8_t buffer[256]={0}; //init buffer
  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
    String path="";
    for (uint32_t i=0; i<len; i++) {
      if (!path.endsWith(" HTTP/")) {path.concat((char)buffer[i]);}
      else {break;}
      }
    path=path.substring(path.indexOf("/") + 1, path.indexOf(" HTTP/"));
    Serial.println(path); //eg:update/?ssid=EDIMAX-tony&pwd=1234567890      
    if (path.startsWith("update")) { //update ssid & pwd
      String ssid=path.substring(path.indexOf("?") + 6, path.indexOf("&"));
      String pwd=path.substring(path.indexOf("&") + 5);
      Serial.println(ssid);
      Serial.println(pwd);
      if (wifi.joinAP(ssid, pwd)) {
        uint8_t data[]="<html>Wifi setup OK!</html>";
        wifi.send(mux_id, data, sizeof(data));
        }
      else {
        uint8_t data[]="<html>Wifi setup failed!</html>";
        wifi.send(mux_id, data, sizeof(data));
        }        
      }
    else { //show setup page
      uint8_t data[]="<html><form method=get action='/update/'>"
      "SSID <input name=ssid type=text><br>"
      "PWD <input name=pwd type=text> "
      "<input type=submit value=Connect></form></html>";
      wifi.send(mux_id, data, sizeof(data));    
      }
    //Serial.println(wifi.releaseTCP(mux_id));
    }    
   Serial.println(wifi.getIPStatus().c_str());  
   }

void ntpupdate() {
  uint8_t buffer[128]={0};  //儲存 NTP 回應字元的緩衝器
  if (wifi.registerUDP(HOST_NAME, HOST_PORT)) { //註冊 UDP (CIPSTART)
    Serial.print("register udp ok\r\n");
    }
  else {Serial.print("register udp err\r\n");}
  //定義兩個程式記憶體變數 (Flash) 儲存要傳送的 UDP 資料 (0~3, 12~15)
  static const char PROGMEM timeReqA[]={227,0,6,236};  //E3,00,06,EC (前 4 Bytes)
  static const char PROGMEM timeReqB[]={49,78,49,52};  //31,4E,31,34 (索引 12~15)
  //製作要傳送給 NTP 伺服器的封包資料
  uint8_t buf[48];  //傳送與接收 UDP 封包的字元串緩衝器
  memset(buf, 0, sizeof(buf));  //全部先填入預設值 0
  //把存於 Flash 的陣列複製到字元串緩衝器 (0~3, 12~15)
  memcpy_P(buf, timeReqA, sizeof(timeReqA));
  memcpy_P(&buf[12], timeReqB, sizeof(timeReqB));
  //傳送資料給 NTP 伺服器
  wifi.send((const uint8_t*)buf, 48);
  //接收 NTP 伺服器回應至 buffer 緩衝器 (索引 43~40, timeout=1 秒)
  uint32_t len=wifi.recv(buffer, sizeof(buffer), 10000);
  if (len > 0) { //有收到 NTP 回應
    //計算 UNIX 開始到現在之毫秒數 (後面三個 byte 分別移位後做 OR 運算)
    unsigned long t=(((unsigned long)buffer[40] << 24) |
                     ((unsigned long)buffer[41] << 16) |
                     ((unsigned long)buffer[42] <<  8) |
                      (unsigned long)buffer[43]) - 2208988800UL;
    //將 CCT 時間戳記轉換為 "時:分:秒"
    Serial.print("The CCT time is ");  //輸出 CCT 時間 (UTC+8)
    if ((((t % 86400L) / 3600 + 8) % 24) < 10) {Serial.print('0');} //時 (小於 10 補 0)
    Serial.print(((t % 86400L) / 3600 + 8) % 24); //時 (一天 86400 秒, 取餘數除 3600 為時)
    Serial.print(':');
    if (((t % 3600) / 60) < 10 ) {Serial.print('0');} //分 (小於 10 補 0)
    Serial.print((t  % 3600) / 60);  //分
    Serial.print(':');
    if ( (t % 60) < 10 ) {Serial.print('0');}  //秒 (小於 10 補 0)
    Serial.println(t % 60); //秒
    }
  //登出 UDP (CIPCLOSE)
  if (wifi.unregisterUDP()) {
    Serial.print("unregister udp ");
    Serial.println(" ok");
    }
  else {
    Serial.print("unregister udp ");
    Serial.println(" err");
    }
  }

此程式編譯後記憶體耗用情形如下, 差不多才用掉一半左右, 還好啦 :

草稿碼使用了 15,758 bytes (51%) 的程式存儲空間。最大值為 30,720 bytes。
全域變數使用了 993 bytes (48%) 的動態記憶體,剩餘 1,055 bytes 供局部變數。最大值為 2,048 bytes 。

串列埠監控視窗擷取訊息如下 :

Firmware version ... 0018000902
Working mode ... OK
IP ... 192.168.43.111
register udp ok
The CCT time is 16:36:17
unregister udp  ok
register udp ok
The CCT time is 16:36:28
unregister udp  ok
register udp ok
The CCT time is 16:36:39
unregister udp  ok
register udp ok
The CCT time is 16:36:50
unregister udp  ok

看起來運作正常. 這次我改用了另外一個 NTP 伺服器 91.226.136.136, 因為 82.209.243.241 伺服器有時候沒回應. OK, Bingo!


沒有留言 :