2018年1月7日 星期日

ESP8266 Automatic Restart Router or Wi-Fi AP



有現成不需銲接電路就可以使用的模組了




網拍搜尋  esp8266 relay

底板加ESP-01 全部不到台幣200元


這有一篇軟體安裝教學,用網頁翻譯一下很好懂

http://nothans.com/measure-wi-fi-signal-levels-with-the-esp8266-and-thingspeak


第一版          第二版

與前兩個版本比較起來增加了網頁功能,可遠端強制重開被監控的設備





如果你的草稿是使用上圖中的USB設備傳送的請記得測試運作的話不要在這個設備上面運作,
否則Wi-Fi斷線後或重新連線時會發生 wdt reset 訊息 RST CAUSE:2, BOOT MODE:(1,7)







以下紅字部分依現場環境調整後上傳


/* for Arduino IDE 1.6.13
   2018-04-10 修正運作中WiFi斷線後重新連線異常部分
   2017-12-01 device x5  
   2017-07-19 for 8266Relay公版,relay off 防呆
   2017-04-24 按鈕依狀態改變顏色
   2017-04-24 add watchdog on/off
   2017-04-23 add Web control Relay
   2017-04-19 WiFi error count change to power Volt
   2017-03-20 可選擇是否啟用 ThingSpeak 紀錄狀態& log Rssi
   2016-10-03 簡化程式碼,增加被測裝置數量
   2016-09-27 功能測試完成
   for  arduinoIDE 1.6.12 支援 ADC_MODE(ADC_VCC 但檔案目錄不支援中文,否則會檢查編譯錯誤

   ** 避免使用 gpio 16 , 以免不穩或無法運作

 big5 to html code   http://www.convertstring.com/zh_TW/EncodeDecode/HtmlEncode

  定義update區域欄位
  1:  0=loss, 1=Device Active,  2= local boot
  2:  0=loss, 1=Device Active,  2= local boot
  3:  0=loss, 1=Device Active,  2= local boot
  4:  0=loss, 1=Device Active,  2= local boot
  5:  Run Level
  6:  reboot count
  7:  Power Volt
  8:  WiFi RSSI
  thingspeak Server 只傳單一區域值第一筆之後的資料沒辦法再進資料庫
  reedited for ESP8266 2016-09-17 by yulie~
  H/W ESP8266
  Network  service  check ipaddress & port
  2015-07-26 by yulie~
*/

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
MDNSResponder mdns;
ESP8266WebServer server(80);
boolean watchDog =1;    // Device ip_1     1=看門狗開啟
boolean logger =  1;    //1=ture 啟用 ThingSpeak紀錄狀態 , 0=false 停用 ThingSpeak紀錄狀態
word chkTime  =  60;    // Sec 檢測連狀態間隔時間
byte actTime  = 120;    //  次  檢測連狀態正常達該次數後發 Active 紀錄 dev0 or dev1
byte wait     =   5;    //  次  檢測連線狀態失敗達該次數後關電重啟 only for Device ip_1
word wifiFail = 600;    // Sec Wi-Fi connect fail  Reboot wait time
byte off_time =  15;    // Sec   設備斷電該秒數之後重開

const char* Ctl_username = "Admin";     //Relay控制認證帳號
const char* Ctl_password = "0487";      //Relay控制認證密碼
const char* wd_username = "admin";      //WatchDog認證帳號
const char* wd_password = "8787";       //WatchDog認證密碼
String webPage = "";                    //網頁內容(rePage)

///*  //*****for my home NEW *****------------------------------------------------------------------
const byte Relay_1  = 0;                          // 使用第0腳控制繼電器 
const byte LED      = 2;                          // 使用第0腳控LED 顯示ip_1 狀態是否在線
const byte D1_pin   = 0;                          //網頁控制的遙控開關腳位 #1
const byte D2_pin   = 2;                          //網頁控制的遙控開關腳位 #2
const char* ssid = "WiFi_SSID";                        // 連線名稱SSID
const char* password = "WiFi_PASSWORD";                        // 連線密碼
char ip_1[] = "www.google.com"; word port_1 = 80; // 被檢查ip位址 & port
char ip_2[] = "192.168.1.31";   word port_2 = 80; // 被檢查ip位址 & port
char ip_3[] = "192.168.1.81";   word port_3 = 23; // 被檢查ip位址 & port
char ip_4[] = "192.168.1.51";   word port_4 = 23; // 被檢查ip位址 & port
char ip_5[] = "192.168.1.61";   word port_5 = 80; // 被檢查ip位址 & port
const char* Host = "ESP8266_Monitor";           //設備名稱
const char* html = "<h1>Device view</h1>";      //網頁標題
const char* D1_sta_1 = "Router ON";             // digitalRead = 1   敘述文字
const char* D1_sta_0 = "Router OFF";            // digitalRead = 0   敘述文字
const char* D2_sta_1 = "  LED  ON";             // digitalRead = 1   敘述文字
const char* D2_sta_0 = "  LED  OFF";            // digitalRead = 0   敘述文字
const String GET = "GET /update?key=XXXXXXXXXXXXX"; //--------- for 網路設備重開記錄
const char* thingspeakID = "-----------";
///*
// Mac address should be different for each device in your LAN
byte arduino_mac[] = { 0x00, 0x09, 0x37, 0x16, 0x89, 0x40 };
IPAddress device_ip  (192, 168,   1,  5);
IPAddress dns_ip     (168,  95, 192,  1);
IPAddress gateway_ip (192, 168,   1, 254);
IPAddress subnet_mask(255, 255, 255,   0);
//*/
//*/ //------------------------------------------------------------------

char serverIP[] = "api.thingspeak.com"; word serverPort = 80;
unsigned long t, u, v, w = 0;
byte actCoun, devFail, status_1, status_1a, status_2, status_2a, status_3, status_3a, status_4, status_4a, status_5, status_5a;;;
word WiFi_fail, reboot_count;
int rssi;
byte old_watchDog;

void setup() {
  t, u, v, w = millis();
  Serial.begin(115200);
  WiFi.hostname(Host); //no set default ESP_XXXXXX (MAC) 
 // WiFi.config(device_ip, gateway_ip, subnet_mask); //disable to  DHCP
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  /*
 if (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("WiFi Connect Failed! Rebooting...10Sec...");
    delay(20000);
    ESP.restart();
  }
*/   if (mdns.begin("esp8266", WiFi.localIP())) {
    Serial.println("MDNS responder started");
  }

  pinMode(Relay_1, OUTPUT); pinMode(LED, OUTPUT);
  // preparing GPIOs
  pinMode(D1_pin, OUTPUT);  digitalWrite(D1_pin, LOW);
  pinMode(D2_pin, OUTPUT);  digitalWrite(D2_pin, LOW);

  for (byte i = 0; i < 6; i++) {
    digitalWrite(LED, !HIGH); delay(500); digitalWrite(LED, !LOW); delay(500); //LED閃5下
  }

  if (logger == 1) updata(2, 2, 2, 2, 2, 0, 9);                 //開機信息

  digitalWrite(LED, chkdev(ip_1, port_1));      //LED 顯示裝置"ip_1"是否連線
//  rePage();

 /* //不認證
  server.on("/", []() { server.send(200, "text/gtml", webPage); });  //無認證
  server.on("/D1_On",  []() {digitalWrite(D1_pin, LOW);  delay(100);    rePage();    server.send(200, "text/html", webPage);  });
  server.on("/D1_Off", []() {digitalWrite(D1_pin, HIGH); delay(100);    rePage();    server.send(200, "text/html", webPage);  });
  server.on("/D2_On",  []() {digitalWrite(D2_pin, LOW);  delay(100);    rePage();    server.send(200, "text/html", webPage);  });
  server.on("/D2_Off", []() {digitalWrite(D2_pin, HIGH); delay(100);    rePage();    server.send(200, "text/html", webPage);  });
 */
  // /*
 //認證不過狀態不會變
  //server.on("/", []() { if (!server.authenticate(www_username, www_password))return server.requestAuthentication(); server.send(200, "text/html", webPage);  });
  server.on("/", []() {rePage(); server.send(200, "text/html", webPage); });
  server.on("/wd_on",  []() { if (!server.authenticate(wd_username, wd_password))return server.requestAuthentication(); watchDog = 1; rePage(); server.send(200, "text/html", webPage); });
  server.on("/wd_off", []() { if (!server.authenticate(wd_username, wd_password))return server.requestAuthentication(); watchDog = 0; rePage(); server.send(200, "text/html", webPage); });
  server.on("/D1_On",  []() { if (!server.authenticate(Ctl_username, Ctl_password))return server.requestAuthentication(); digitalWrite(D1_pin, !HIGH); rePage(); server.send(200, "text/html", webPage); });
  server.on("/D1_Off", []() { if (!server.authenticate(Ctl_username, Ctl_password))return server.requestAuthentication(); digitalWrite(D1_pin, !LOW ); rePage(); server.send(200, "text/html", webPage); });
  server.on("/D2_On",  []() { digitalWrite(D2_pin, HIGH); rePage(); server.send(200, "text/html", webPage); });
  server.on("/D2_Off", []() { digitalWrite(D2_pin, LOW ); rePage(); server.send(200, "text/html", webPage); });
   server.on("/reboot",  []() { if (!server.authenticate(Ctl_username, Ctl_password))return server.requestAuthentication(); rebootPower_1(); rePage(); server.send(200, "text/html", webPage); });
  //*/
  server.begin();  Serial.println("HTTP server started");
  server.begin();
  Serial.print("Open http://");
  Serial.print(WiFi.localIP());
  Serial.println("/ in your browser to see it working");
}

void rePage() {                                               //更新webPage內容
  webPage = html;
//頁面上第1個按鈕,依ON/OFF狀態改變按鈕背景顏色
  webPage += "<p><a href=\"/wd_on\"><button type=\"button\" style=\"background-color:";
  webPage += ((watchDog)?"#FF5050":"#DDDDDD"),                                          //依狀態改變ON按鈕背景顏色
  webPage += ";color:#000000;\">ON</button></a>&nbsp;";                                 //按鈕字型顏色
  webPage += "<a href=\"/wd_off\"><button type=\"button\" style=\"background-color:";
  webPage += ((!watchDog)?"#7FFF7F":"#DDDDDD"),                                         //依狀態改變OFF按鈕背景顏色
  webPage += ";color:#000000;\">OFF</button></a>";                                      //按鈕字型顏色
  webPage += "watchDog: ";
  webPage +=  ip_1;
  webPage += " -> ";
  webPage += ((watchDog)?"ON":"OFF"),       //  digitalRead=1 直接顯示 ON , digitalRead=0 直接顯示 OFF, 
  webPage += "</p>";
//頁面上第2個按鈕,依ON/OFF狀態改變按鈕背景顏色
  webPage += "<p><a href=\"/D1_On\"><button type=\"button\" style=\"background-color:";
  webPage += (!digitalRead( D1_pin )?"#FF5050":"#DDDDDD"),                              //依狀態改變(ON反向)按鈕背景顏色
  webPage += ";color:#000000;\">ON</button></a>&nbsp;";                                 //按鈕字型顏色
  webPage += "<a href=\"/D1_Off\"><button type=\"button\" style=\"background-color:";
  webPage += (digitalRead( D1_pin )?"#7FFF7F":"#DDDDDD"),                               //依狀態改變OFF按鈕背景顏色
  webPage += ";color:#000000;\">OFF</button></a>";                                      //按鈕字型顏色
  webPage += "  #0 Pin ";
  webPage +=  D1_pin;
  webPage += "  -> ";
  webPage += ((digitalRead( D1_pin ))?D1_sta_1:D1_sta_0),     // D1_pin=1 顯示 lampON 字串 ,D1_pind=0 顯示 lampOFF 字串( ! 為使顯示狀態反向,實際上低電位繼電器模組為On)
  webPage += "</p>";
//頁面上第3個按鈕,依ON/OFF狀態改變按鈕背景顏色
  webPage += "<p><a href=\"/D2_On\"><button type=\"button\" style=\"background-color:";
  webPage += (digitalRead( D2_pin )?"#FF5050":"#DDDDDD"),                               //依狀態改變ON按鈕背景顏色
  webPage += ";color:#000000;\">ON</button></a>&nbsp;";                                 //按鈕字型顏色
  webPage += "<a href=\"/D2_Off\"><button type=\"button\" style=\"background-color:";
  webPage += (!digitalRead( D2_pin )?"#7FFF7F":"#DDDDDD"),                              //依狀態改變(OFF反向)按鈕背景顏色
  webPage += ";color:#000000;\">OFF</button></a>";                                      //按鈕字型顏色
  webPage += "  #2 Pin ";
  webPage +=  D2_pin;
  webPage += "  -> ";
  webPage += ((digitalRead( D2_pin ))?D2_sta_1:D2_sta_0),     // digitalRead=1 顯示 lampON 字串 , digitalRead=0 顯示 lampOFF 字串
  webPage += "</p>";

  // webPage += "<p><a href=\"https://thingspeak.com/channels/101630\">View device logger</a></p>"; //加入連結及敘述
   webPage += "<p><a href=\"https://thingspeak.com/channels/";
   webPage +=thingspeakID;
   webPage +="\">View device logger</a></p>";
  // webPage += "<iframe width=\"450\" height=\"260\" style=\"border: 1px solid #cccccc;\" src=\"https://thingspeak.com/channels/101630/charts/1?bgcolor=%23ffffff&color=%23d62020&dynamic=true&results=24&title=%E5%85%A7%E9%83%A8+IP&type=line\"></iframe>";

  webPage += "<p>WiFi RSSI  ";
  webPage += WiFi.RSSI();   
  webPage += " dBm</p>";
  webPage += "<p> </p>""<p>       Power By ESP8266~</p>";                         // 純顯示文字

/*
  //頁面上按鈕,不依ON/OFF狀態改變按鈕背景顏色
  webPage += "<p><a href=\"/wd_on\"><button type=\"button\" style=\"background-color:dddddd;color:#ffffff;\">ON</button></a>&nbsp;";
  webPage += "  <a href=\"/wd_off\"><button type=\"button\" style=\"background-color:#cccccc;color:#ffffff;\">OFF</button></a>";
  webPage += " device: ";
  webPage +=  ip_1;
  webPage += " ->  ";
  webPage += ((watchDog)?"Watchdog ON":"Watchdog OFF"),       //  digitalRead=1 直接顯示 ON , digitalRead=0 直接顯示 OFF, 
  webPage += "</p>";
    
  webPage += "<p><a href=\"D1_On\"><button>ON</button></a>&nbsp;<a href=\"D1_Off\"><button>OFF</button></a>";
  webPage += "  #0 Pin ";
  webPage +=  D1_pin;
  webPage += "  -> ";
  webPage += ((digitalRead( D1_pin ))?D1_sta_1:D1_sta_0),     // D1_pin=1 顯示 lampON 字串 ,D1_pind=0 顯示 lampOFF 字串( ! 為使顯示狀態反向,實際上低電位繼電器模組為On)
  webPage += "</p>";

  webPage += "<p><a href=\"D2_On\"><button>ON</button></a>&nbsp;<a href=\"D2_Off\"><button>OFF</button></a>";
  webPage += "  #2 Pin ";
  webPage +=  D2_pin;
  webPage += "  -> ";
  webPage += ((digitalRead( D2_pin ))?D2_sta_1:D2_sta_0),     // digitalRead=1 顯示 lampON 字串 , digitalRead=0 顯示 lampOFF 字串
  webPage += "</p>";  
  */
}

void loop() {

  
if(millis() - w >=500 && watchDog !=old_watchDog){
  w=millis();
  Serial.print("watchDog =");
  Serial.println(watchDog);
  old_watchDog=watchDog;
  if(digitalRead(Relay_1)==HIGH)rebootPower_1(); // Relay N.C connect (device power down) 2017-07-19 add 防呆
}

  server.handleClient();

//=============== 2018-04-10 add ===========================================================
    if (millis() - t >= 30000 && WiFi.status() != WL_CONNECTED ) {          // 連接到指定的 WiFi SSID 如果連線AP 失敗時執行這一段程式
      t = millis();
      Serial.println("WiFi Connect Failed! wait...30Sec..ReConnect.");
      for (byte i = 0; i < 6; i++) {digitalWrite(LED, HIGH); delay(100); digitalWrite(LED, LOW); delay(100);} //LED閃5下
      delay(30000);
      WiFi_fail++;
      WiFi_fail = constrain ( WiFi_fail, 0, 65534);   //  限制該值在0~100 之間
      Serial.println(WiFi_fail);
       if (WiFi.status() != WL_CONNECTED ){ 
        Serial.println("Esp8266 WiFi ReConnect... ");
        WiFi.begin(ssid, password);
        //ESP.restart();
       } 
       else{
        Serial.println("-------------------------------------->>ESP8266  Wi-Fi Link UP.....  ");
       // if(WiFi_fail != 0 )ESP.restart();
       }
       
      if (WiFi_fail >= wifiFail) {                    //connect Wifi fail over 3min Run
        rebootPower_1();
        WiFi_fail = 0;
      }
    }
//=============== 2018-04-10 add end===========================================================


  if (millis() - u > (chkTime * 1000)) {     //30000 mS on chk
    u = millis();
    actCoun++;
    Serial.println(" ");
    status_1 =  chkdev(ip_1, port_1); //回傳"0"為斷線, 回傳為"1"連線
    status_2 =  chkdev(ip_2, port_2);
    status_3 =  chkdev(ip_3, port_3);
    status_4 =  chkdev(ip_4, port_4);
    status_5 =  chkdev(ip_5, port_5);
    digitalWrite(LED, status_1);      //顯示裝置"ip_1"是否連線


    //任何裝置狀態改變時上傳
    if (status_1 !=  status_1a || status_2 !=  status_2a || status_3 !=  status_3a  || status_4 != status_4a  || status_5 != status_5a) {    //任何裝置狀態改變
      Serial.println(" Detect status change......");
      status_1a = status_1; status_2a = status_2; status_3a = status_3; status_4a = status_4; status_5a = status_5;                     //狀態更新,做為下一次狀態比較用
      if ( status_1 == 1 && logger == 1)updata(status_1, status_2, status_3, status_4, status_5, reboot_count, 6); //上傳
    }

    if (status_1 == 1) devFail = 0;           //斷線計數歸零
    else {
      devFail++;                              //裝置斷線計數,給重開設備判斷用
      actCoun = 0;
    }

    // device 0 or device 1  每60次傳送一次存活信息
    if (actCoun >= actTime && logger == 1 )updata(status_1, status_2, status_3, status_4, status_5, reboot_count,4);

    //裝置矢聯達指定次數則啓動繼電器關機重開
    if (devFail >= wait) {
      if(watchDog == 1)rebootPower_1();     //watchDog 開啟才執行
      devFail = 0;
      WiFi_fail = 0;
    }
    
    //showCount();  //for debug
    //reboot_count != 0 or WiFi_fail != 0 and connect OK send infomation...
    if ((reboot_count != 0 || WiFi_fail != 0) && status_4 == 1 && logger == 1) updata(status_1, status_2, status_3, status_4, status_5, reboot_count,0);
  }
}

//-------------------------------------------------------------------------------void updata()
word updata(word field1, word field2, word field3, word field4, word field5, word field6, word field7) {
  rssi = WiFi.RSSI();
//  float volt = analogRead(A0) * 0.0125 ;
  // volt = (analogRead(A0) * (1.0 / 1024) * 12.83) ; // 220K + 18.6k 分壓 , (220 + 18.6)/18.6=12.83
  WiFiClient client;
  if (client.connect(serverIP, serverPort)) {
    String getStr = GET +
                    "&field1=" + String((float)field1, 0) +     // active 01
                    "&field2=" + String((float)field2, 0) +     // active 02
                    "&field3=" + String((float)field3, 0) +     // active 03
                    "&field4=" + String((float)field4, 0) +     // active 04
                    "&field5=" + String((float)field5, 0) +     // active 05
                    "&field6=" + String((float)field6, 0) +     // reboot_count
                    "&field7=" + String((float)field7, 0) +   //input Volt
                    "&field8=" + String((float)rssi, 2) +    // RSSI
                    " HTTP/1.1\r\n";;
    client.print( getStr );
    client.print( "Host: api.thingspeak.com\n" );
    client.print( "Connection: close\r\n\r\n" );
    delay(10);
    // 處理遠端伺服器回傳的訊息,程式碼可以寫在這裡!
    Serial.println(getStr);
    client.stop();
    actCoun = 0;      //如果狀態傳送就將先前的存活計數歸零
    WiFi_fail  = 0;
    reboot_count = 0;
  }
  else {                               //連線失敗執行此段程式
    Serial.print(ip_1); Serial.print(" : "); Serial.print(port_1); Serial.println(" ThingSpeak Server disconnection  ---- X  ");
  }
  client.stop();
}

//------------------------------------------------------------------------------- check device
byte chkdev(char* IP, word PORT) {
  WiFiClient client; delay(200);
  if (client.connect(IP, PORT)) {    //連線成功執行此段程式
    Serial.print(IP); Serial.print(" : "); Serial.print(PORT); Serial.println(" check device  connected ");
    return 1;  //傳回值"1"
  }

  else if (client.connect(IP, PORT)) {    //第一次連線不成功執行此段程式
     Serial.print(IP); Serial.print(" : "); Serial.print(PORT); Serial.println(" check device  connected ");
    return 1;  //傳回值"1"
  }
  else {                               //兩次連線失敗執行此段程式
    Serial.print(IP); Serial.print(" : "); Serial.print(PORT); Serial.println(" check device  disconnection  ---- X  ");
    return 0;  //傳回值"0"
  }
  
}

//-------------------------------------------------------------------------------status_1, status_2
void showCount() {   //for debug
  Serial.print("status_1 : "); Serial.println(status_1);   Serial.print("status_2 : "); Serial.println(status_2);
  Serial.print("WiFi_fail : ");  Serial.println(WiFi_fail);  Serial.print("reboot_count : "); Serial.println(reboot_count);
  uint32_t getVcc = ESP.getVcc();  float voltaje = ESP.getVcc();  Serial.println("core Volt : "); Serial.print(voltaje / 1024);
  Serial.println(" V");
}

//-------------------------------------------------------------------------------void rebootPower_1()
void rebootPower_1() {
  digitalWrite(Relay_1, HIGH); // Relay N.C connect (device power down)
  Serial.print("power down~  Wait  "); Serial.print(off_time); Serial.println(" Sec ..");
  delay(off_time * 1000);          //device power down (off_time) sec
  digitalWrite(Relay_1, LOW);
  Serial.println("power up");
  reboot_count++;

}








改良火焰切圓機過程







下圖直徑62cm 處的

切線速度為30.1公分/分鐘,小車直線行進速度為22.3公分/分鐘,


設定板厚9mm,開槽角度單側20度,開槽間隙1mm 




正面說法稱為版本升級,持續進化(O) 負面說法叫思慮不周,產品沒一次到位(X) 產品就是因應需求而生 功能方面從實際工作上最耗時間的部份先行著手處理 一般火焰切割開槽用圓規最少要切三刀,直切一刀、內導角一刀、外導角一刀 而且只有直切才能用圓規輔助,一個直徑62公分的圓,圓週將近兩公尺,所以 開一個直徑62公分的圓孔至少要切將近6公尺,一次要割8個孔含 4個剖半的總 長度超過 50 公尺 所以第一代的機器主要的重點放在切圓的部分基本上只切內倒角和外倒角,每


一個孔只要切兩刀就好了 















 第一代的產品完成後 發現每次在調整內倒角和外倒角的間距和角度上面花了很多的時間 所以第二代的機器重點放在角度調整的部分,透過程式的計算直接 設定角度和間隙寬度,確保每次的開槽角度和間隙都能一致,微控制

器使用市面上很普遍的 Arduino MEGA2560




接下來這裡也遭遇了一些狀況







以上這部分運作不理想所以砍掉重練



火口角度調整機構重新做過
















在接下來的部分就是發現驅動輪軸的間隙過大

在切割過程中造成前進的速度不穩 定會間斷造成切割品質不良,所以後來驅動輪的部分改成步進馬達直接驅動







改成步進馬達直接驅動之後又發現驅動能力略微不足常常會有掉步的問題, 再加上直接驅動的情況下輪軸只有5 mm,很擔心輪軸的強度不夠所以換成 了皮帶驅動用上了16齒配上60齒的配置增加輪軸的扭力,使用上已滿足工 作需求後就沒再做修改了。