2015年11月27日 星期五

Step By Step系列---開始玩ESP8266-12 with Arduino IDE(第4篇-WiFi電燈控制)

這次要來分享WiFi電燈控制, 下圖的OLED在番外篇已經有介紹過了, 就不再多說了, 整個基本是先把ESP8266操作在AP mode, 連上它輸入自己家裡的SSID和password就可以連上了, 這之把整個過程用OLED的表示出來


下圖是開機時會先試著連線, 若10秒內無法連線則顯示time out, 並將操作模式切換成AP mode

 ESP8266在AP mode有固定的IP位址, 為192.168.4.1

 找到你自己設定AP的SSID名稱, 我這裡是設定成MoonAP, 點連線

連線中

連上後, 在網址列輸入192.168.4.1, 會看到下面畫面, 它會先掃瞄週遭可用的無線網路, 找到你想連線的無線網路, 輸入在SSID和PASSWORD按儲存

畫面顯示儲存成功, 即可重新開機


 重新開機後會試著連線你剛輸入的SSID和PASSWORD, 若連上會顯示WiFi Connected.

接下來會顯示你連上的IP Address, 同時啟動mDNS服務

如果你的NB和ESP8266在同個AP下, 就可以輸入你自己設定的網域名稱 ,我這裡是設定成ESP8266.local , 即可連上

連上畫面如下圖, 這是把ESP8266操作在web server模式下, 網頁的內容也是先寫在ESP8266中, 畫面中有三個按鈕, 一個是打開電燈, 一個關閉電燈和清除無線網路的試定
 若點擊打開電燈, 則網頁會顯示LED已經turn on

若點擊關閉電燈, 則會顯示LED已經turn off

這是實際操作LED燈點亮

這是實際操作LED燈關閉

 這是Relay的接線部份, NO=normal open(常開) , COM=共用端點

Relay另一邊為DC+=5V , DC-=GND , IN=Relay控制訊號

 整個電路接線如下圖

實際動作情況


完整程式碼如下
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <EEPROM.h>
#include <ESP8266mDNS.h>
#include <Wire.h>
#include "font.h"
#include <SPI.h>
#include <SD.h>

const char* host = "esp8266";
const char* ssid = "MoonAP";
const char* passphrase = "12345678";
String content;
String st;
int statusCode;
File uploadFile;

#define offset 0x00    // SDD1306                      // offset=0 for SSD1306 controller
#define OLED_address  0x3c                             // all the OLED's I have seen have this address


// Create an instance of the server
// specify the port to listen on as an argument
ESP8266WebServer server(80);

void setup() {
  pinMode(16, OUTPUT);
  digitalWrite(16, LOW);

  pinMode(14, OUTPUT);
  digitalWrite(14, HIGH);

  Wire.begin(5,4);                                // SDA = 4 , SCL = 5

  init_OLED();                                  
  reset_display();    
  Serial.begin(115200);
  WiFi.mode(WIFI_AP);
  EEPROM.begin(512);
  Serial.println();
  Serial.println();
  sendStrXY("Jason's Lab",2,1);
  sendStrXY("Bug",6,3);
  sendStrXY("Orient Studio",1,5);
  delay(3000);
  reset_display();
  Serial.println("The program startup...");

  // read eeprom for ssid and password
  Serial.println("Reading EEPROM SSID (MAX=32 BYTE)");
  String essid;
  for (int i = 0; i < 32; ++i)
    {    
      essid += char(EEPROM.read(i));
    }
  Serial.print("SSID NAME : ");
  Serial.println(essid);
  char charBuf[32];
  essid.toCharArray(charBuf, 32);
  delay(1000);
  sendStrXY("SSID :" ,0,1);  sendStrXY(charBuf,7,1);
  delay(1000);
  sendStrXY("log in Wifi",0,2);
  delay(1000);
  Serial.println("Reading EEPROM PASSWORD (MAX=64 BYTE)");
  String epassword = "";
  for (int i = 32; i < 96; ++i)
    {    
      epassword += char(EEPROM.read(i));
    }
  Serial.print("PASSWORD NAME : ");
  Serial.println(epassword);

  Serial.print("essid.length = ");
  Serial.println(essid.length());

  WiFi.begin(essid.c_str(), epassword.c_str());

  if (testWifi()) {
    launchWeb(0);
    return;
    }
  SetupToAPMode(); //沒連上時執行這個
}

/** 用server.send簡單回應client訊息
void handleRoot() {
  digitalWrite(led, 1);
  server.send(200, "text/plain", "hello from esp8266!");
  digitalWrite(led, 0);
}
*/

void loop() {
  server.handleClient();
  }

bool testWifi(void) {
  int c = 0;
  Serial.println("Waiting for Wifi to connect...");
  //delay 10秒讓wifi連線
  while ( c < 15 ) {
    if (WiFi.status() == WL_CONNECTED) { return true; } //return一執行就結束testwifi這個函數, 有連上就return true, 沒連上就return false
    delay(1000);
    Serial.print(WiFi.status());  
    sendStrXY(".",0+c,3);
    c++;
  }
  Serial.println("");
  sendStrXY("Connect Timeout",0,5);
  delay(1000);
  sendStrXY("Set To AP Mode",0,6);
  delay(2000);
  reset_display();
  Serial.println("Connect timed out, ESP8266 change to AP mode");
  Serial.println("");
  return false;
}

void launchWeb(int webtype) {
  Serial.println("");
  if(webtype) {
    Serial.println("Operation in AP Mode.");
  }
  else {
    delay(1000);
    sendStrXY("WiFi Connected.",0,4);
    delay(2000);
    reset_display();
    Serial.println("WiFi connected.");
  }

  Serial.print("Local IP: ");
  Serial.println(WiFi.localIP());
  Serial.print("SoftAP IP: ");
  Serial.println(WiFi.softAPIP());
  createWebServer(webtype);
  // Start the server
  server.begin();
  Serial.println("Server started");

  if(WiFi.softAPIP()[0]==0 && WiFi.softAPIP()[1]==0 && WiFi.softAPIP()[2]==0 && WiFi.softAPIP()[3]==0) {  
    sendStrXY("Local IP:       ",0,0);
    char LocalIP[16];
    sprintf(LocalIP, "%d.%d.%d.%d", WiFi.localIP()[0], WiFi.localIP()[1], WiFi.localIP()[2], WiFi.localIP()[3]);
    delay(1000);
    sendStrXY(LocalIP,0,1);
    }
  else {  
    sendStrXY("SoftAP IP:     ",0,0);
    char SoftAP[16];
    sprintf(SoftAP, "%d.%d.%d.%d", WiFi.softAPIP()[0], WiFi.softAPIP()[1], WiFi.softAPIP()[2], WiFi.softAPIP()[3]);
    delay(1000);
    sendStrXY(SoftAP,0,1);
    delay(1000);
    sendStrXY("Now, You can con",0,3);
    delay(1000);
    sendStrXY("nect to 192.168.",0,4);
    delay(1000);
    sendStrXY("4.1 to set your",0,5);
    delay(1000);
    sendStrXY("AP network",0,6);
  }



  if (MDNS.begin(host)) {
    MDNS.addService("http", "tcp", 80);
    Serial.println("MDNS responder started");
    delay(1000);
    sendStrXY("mDNS Started...",0,2);
    delay(1000);
    sendStrXY("Now, You can con",0,4);
    delay(1000);
    sendStrXY("nect to http://",0,5);
    delay(1000);
    sendStrXY("esp8266.local",0,6);
    Serial.print("You can now connect to http://");
    Serial.print(host);
    Serial.println(".local");
 
  }

}

void SetupToAPMode(void) {
  ScanNetwork();
  WiFi.softAP(ssid, passphrase, 6,0);
  Serial.println("Soft AP Starting...");
  launchWeb(1);
  Serial.println("The End.");
}

void createWebServer(int webtype)
{
  if ( webtype == 1 ) {  //ap mode
    server.on("/", []() {
        IPAddress ip = WiFi.softAPIP();
        content = "<!DOCTYPE HTML><html><title>雲端電燈的無線網路設定</title><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"><p>     你好, 請輸入您家裡的無線網路設定</p>";
        content += st;
        //&nbsp;代表塞一個空白, 重覆5個就是塞5個空白    
        content += "<br>";
        content += "<form method='get' action='setting'>";
        content += "<table border=\"0\"><tr><td><label>SSID</label></td><td><input type=\"text\" placeholder=\"請輸入要連接的SSID\" name='ssid' maxlength=32 size=64></td></tr>";
        content += "<tr><td><label>PASSWORD</label></td><td><input type=\"text\" placeholder=\"請輸入要連接的密碼\" name='pass' maxlength=64 size=64></td></tr></table>";
        content += "<input type=\"button\" value=\"重新掃瞄無線網路\" onclick=\"self.location.href='/rescannetwork'\">";
        content += "&nbsp;&nbsp;&nbsp;<input type='reset' value=\"重設\">&nbsp;&nbsp;&nbsp;<input type='submit' value=\"儲存\"></form></html>";
        server.send(200, "text/html", content);  //200代表伺服器回應OK, text/html代表用html網頁類型, 不加這個會找不到網頁
      });  

    server.on("/rescannetwork", []() {      
      content = "<!DOCTYPE HTML><html><title>雲端電燈的無線網路設定</title><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"><p>重新掃瞄無線網路中...</p>";
      content += "<Meta http-equiv=\"ReFresh\" Content=\"5; URL='/'\">"; //讓網頁5秒自動跳回root page
      server.send(200, "text/html", content);
      ScanNetwork();  
      });

  server.on("/setting", []() {
    String qsid = server.arg("ssid");
    String qpass = server.arg("pass");
    if (qsid.length() > 0 && qpass.length() > 0) {
      Serial.println("clearing eeprom");
      for (int i = 0; i < 96; ++i) { EEPROM.write(i, 0); }
      Serial.println(qsid);
      Serial.println("");
      Serial.println(qpass);
      Serial.println("");
       
      Serial.println("writing eeprom ssid:");
      for (int i = 0; i < qsid.length(); ++i)
        {
        EEPROM.write(i, qsid[i]);
        Serial.print("Wrote: ");
        Serial.println(qsid[i]);
        }
      Serial.println("writing eeprom pass:");
      for (int i = 0; i < qpass.length(); ++i)
        {
        EEPROM.write(32+i, qpass[i]);
        Serial.print("Wrote: ");
        Serial.println(qpass[i]);
        }  
      EEPROM.commit(); //EEPROM.write並不會馬上寫入EEPROM, 而是要執行EEPROM.commit()才會實際的寫入EEPROM
      content = "儲存成功, 請按RESET鍵重新開機!";
      statusCode = 200;
      }
    else {        
      content = "<p>輸入錯誤!!!SSID或PASSWORD任何其中一欄不能是空白, 按上一頁重新輸入</p>";
      content += "<input type=\"button\" value=\"上一頁\" onclick=\"self.location.href='/'\"></html>";
      //content = "{\"Error\":\"404 not found\"}";
      statusCode = 404;
      Serial.println("Sending 404");  
      }  
    server.send(statusCode, "text/html", content);    
    });
  }
  else if (webtype == 0) {  //sta mode
    server.on("/", []() {
      IPAddress ip = WiFi.localIP();
       content = "<!DOCTYPE HTML><html><title>雲端電燈控制網頁</title>";
      content += "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">";
      content += "<body background=\"https://goo.gl/xc7YUc\">";
      content += "<p>你好, 請按下面的按鈕來開啟或關閉電燈</p>";
      content += "<input type=\"button\" value=\"打開電燈\" onclick=\"self.location.href='/led/on'\">";
      content += "&nbsp;&nbsp;&nbsp;<input type=\"button\" value=\"關閉電燈\" onclick=\"self.location.href='/led/off'\">";    
      content += "<br><br>";  //空一行
      content += "<p>若要清除無線網路設定, 請按下方的清除無線網路設定按鈕</p>";
      content += "<input type=\"button\" value=\"清除無線網路設定\" onclick=\"self.location.href='/cleareeprom'\"></html>";
      //server.send(200, "application/json", "{\"IP\":\"" + ipStr + "\"}");    
      server.send(200, "text/html", content);
      });
 
    server.on("/led/on", []() {
      content = "<!DOCTYPE HTML>\r\n<html>";
      content += "<p>LED已經Turn On</p>";
      content += "<Meta http-equiv=\"ReFresh\" Content=\"3; URL='/'\">";
      content += "</html>";
      server.send(200, "text/html", content);
      digitalWrite(16, HIGH);
      digitalWrite(14, LOW);
      delay(50);
      digitalWrite(14, HIGH);
      });
    server.on("/led/off", []() {
      content = "<!DOCTYPE HTML>\r\n<html>";
      content += "<p>LED已經Turn Off</p>";
      content += "<Meta http-equiv=\"ReFresh\" Content=\"3; URL='/'\">";
      content += "</html>";
      server.send(200, "text/html", content);
      digitalWrite(16, LOW);
      });
    server.on("/cleareeprom", []() {                            
      for (int i = 0; i < 96; ++i) { EEPROM.write(i, 0); }
      EEPROM.commit();
      content = "<!DOCTYPE HTML>\r\n<html>";
      content += "<p>已清除無線網路設定</p></html>";
      server.send(200, "text/html", content);
      //Serial.println("clearing eeprom");
      });
    }
}

void ScanNetwork() {
  WiFi.disconnect();
    WiFi.mode(WIFI_STA);
    delay(100);
    int n = WiFi.scanNetworks();
    Serial.print("Scan Network Done...and ");
    if (n == 0)
      Serial.println("No Any Networks Found!");
    else
      {
      Serial.print(n);
      Serial.println(" Networks Found!");
      for (int i = 0; i < n; ++i)
        {
        // Print SSID and RSSI for each network found
        Serial.print(i + 1);
        Serial.print(": ");
        Serial.print(WiFi.SSID(i));
        Serial.print(" (");
        Serial.print(WiFi.RSSI(i));
        Serial.print(")");
        byte encryption = WiFi.encryptionType(i);
        Serial.print(" Encryption Type:");
        //Serial.println(encryption,HEX);  // TKIP (WPA) = 2 , WEP = 5 , CCMP (WPA) = 4 , NONE = 7 , AUTO = 8(WPA or WPA2)
        switch (encryption) {
          case 2: Serial.println("TKIP(WPA)");break;
          case 5: Serial.println("WEP");break;
          case 4: Serial.println("CCMP(WPA)");break;
          case 7: Serial.println("NONE");break;
          case 8: Serial.println("AUTO(WPA or WPA2)");break;
          }    
        //Serial.println((WiFi.encryptionType(i) == ENC_TYPE_NONE)?" ":"*");
        delay(100);
      }
    }
  Serial.println("");
  String k;
  st = "<ol type=\"1\" start=\"1\">";
  for (int i = 0; i < n; ++i)
    {
    // Print SSID and RSSI for each network found
    st += "<table border=\"0\"><tr><td width=\"300px\">";
    k=String(i+1);
    st += k + ". ";
    st += WiFi.SSID(i);
    st += " (";
    st += WiFi.RSSI(i);
    st += ")";
    st += "</td><td width=\"200px\">";
    //st += (WiFi.encryptionType(i) == ENC_TYPE_NONE)?" ":"*";
    byte encryption = WiFi.encryptionType(i);
    switch (encryption) {
      case 2: st += "TKIP(WPA)";break;
      case 5: st += "WEP";break;
      case 4: st +="CCMP(WPA)";break;
      case 7: st +="NONE";break;
      case 8: st +="AUTO(WPA or WPA2)";break;
      }
    //st += "</li></td></tr>";
    st += "</td></tr>";  
    }
  st += "</ol></table><br>";
  }

static void reset_display(void)
{
  displayOff();
  clear_display();
  displayOn();
}

//==========================================================//
// Turns display on.
void displayOn(void)
{
  sendcommand(0xaf);        //display on
}

//==========================================================//
// Turns display off.
void displayOff(void)
{
  sendcommand(0xae);    //display off
}

//==========================================================//
// Clears the display by sendind 0 to all the screen map.
static void clear_display(void)
{
  unsigned char i,k;
  for(k=0;k<8;k++)
  {
    setXY(0,k);  
    {
      for(i=0;i<(128 + 2 * offset);i++)     //locate all COL
      {
        SendChar(0);         //clear all COL
        //delay(10);
      }
    }
  }
}

//==========================================================//
// Inits oled and draws logo at startup
static void init_OLED(void)
{
  sendcommand(0xae);    //display off
  sendcommand(0xa6);            //Set Normal Display (default)
    // Adafruit Init sequence for 128x64 OLED module
    sendcommand(0xAE);             //DISPLAYOFF
    sendcommand(0xD5);            //SETDISPLAYCLOCKDIV
    sendcommand(0x80);            // the suggested ratio 0x80
    sendcommand(0xA8);            //SSD1306_SETMULTIPLEX
    sendcommand(0x3F);
    sendcommand(0xD3);            //SETDISPLAYOFFSET
    sendcommand(0x0);             //no offset
    sendcommand(0x40 | 0x0);      //SETSTARTLINE
    sendcommand(0x8D);            //CHARGEPUMP
    sendcommand(0x14);
    sendcommand(0x20);             //MEMORYMODE
    sendcommand(0x00);             //0x0 act like ks0108
 
    //sendcommand(0xA0 | 0x1);      //SEGREMAP   //Rotate screen 180 deg
    sendcommand(0xA0);
 
    //sendcommand(0xC8);            //COMSCANDEC  Rotate screen 180 Deg
    sendcommand(0xC0);
 
    sendcommand(0xDA);            //0xDA
    sendcommand(0x12);           //COMSCANDEC
    sendcommand(0x81);           //SETCONTRAS
    sendcommand(0xCF);           //
    sendcommand(0xd9);          //SETPRECHARGE
    sendcommand(0xF1);
    sendcommand(0xDB);        //SETVCOMDETECT              
    sendcommand(0x40);
    sendcommand(0xA4);        //DISPLAYALLON_RESUME      
    sendcommand(0xA6);        //NORMALDISPLAY          

  clear_display();
  sendcommand(0x2e);            // stop scroll
  //----------------------------REVERSE comments----------------------------//
    sendcommand(0xa0);    //seg re-map 0->127(default)
    sendcommand(0xa1);    //seg re-map 127->0
    sendcommand(0xc8);
    delay(1000);
  //----------------------------REVERSE comments----------------------------//
  // sendcommand(0xa7);  //Set Inverse Display
  // sendcommand(0xae);   //display off
  sendcommand(0x20);            //Set Memory Addressing Mode
  sendcommand(0x00);            //Set Memory Addressing Mode ab Horizontal addressing mode
  //  sendcommand(0x02);         // Set Memory Addressing Mode ab Page addressing mode(RESET)
}

//==========================================================//
// Used to send commands to the display.
static void sendcommand(unsigned char com)
{
  Wire.beginTransmission(OLED_address);     //begin transmitting
  Wire.write(0x80);                          //command mode
  Wire.write(com);
  Wire.endTransmission();                    // stop transmitting
}

static void setXY(unsigned char col,unsigned char row)
{
  sendcommand(0xb0+row);                //set page address
  sendcommand(offset+(8*col&0x0f));       //set low col address
  sendcommand(0x10+((8*col>>4)&0x0f));  //set high col address
}

//==========================================================//
// Actually this sends a byte, not a char to draw in the display.
// Display's chars uses 8 byte font the small ones and 96 bytes
// for the big number font.
static void SendChar(unsigned char data)
{
  //if (interrupt && !doing_menu) return;   // Stop printing only if interrupt is call but not in button functions

  Wire.beginTransmission(OLED_address); // begin transmitting
  Wire.write(0x40);//data mode
  Wire.write(data);
  Wire.endTransmission();    // stop transmitting
}

//==========================================================//
// Prints a string in coordinates X Y, being multiples of 8.
// This means we have 16 COLS (0-15) and 8 ROWS (0-7).
static void sendStrXY( const char *string, int X, int Y)
{
  setXY(X,Y);
  unsigned char i=0;
  while(*string)
  {
    for(i=0;i<8;i++)
    {
      SendChar(pgm_read_byte(myFont[*string-0x20]+i));
    }
    *string++;
  }
}


下面是OLED的字形檔陣列
// Small 8x8 font
const char myFont[][8] PROGMEM = {  
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x5F,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x07,0x00,0x07,0x00,0x00,0x00},
{0x00,0x14,0x7F,0x14,0x7F,0x14,0x00,0x00},
{0x00,0x24,0x2A,0x7F,0x2A,0x12,0x00,0x00},
{0x00,0x23,0x13,0x08,0x64,0x62,0x00,0x00},
{0x00,0x36,0x49,0x55,0x22,0x50,0x00,0x00},
{0x00,0x00,0x05,0x03,0x00,0x00,0x00,0x00},
{0x00,0x1C,0x22,0x41,0x00,0x00,0x00,0x00},
{0x00,0x41,0x22,0x1C,0x00,0x00,0x00,0x00},
{0x00,0x08,0x2A,0x1C,0x2A,0x08,0x00,0x00},
{0x00,0x08,0x08,0x3E,0x08,0x08,0x00,0x00},
{0x00,0xA0,0x60,0x00,0x00,0x00,0x00,0x00},
{0x00,0x08,0x08,0x08,0x08,0x08,0x00,0x00},
{0x00,0x60,0x60,0x00,0x00,0x00,0x00,0x00},
{0x00,0x20,0x10,0x08,0x04,0x02,0x00,0x00},
{0x00,0x3E,0x51,0x49,0x45,0x3E,0x00,0x00},
{0x00,0x00,0x42,0x7F,0x40,0x00,0x00,0x00},
{0x00,0x62,0x51,0x49,0x49,0x46,0x00,0x00},
{0x00,0x22,0x41,0x49,0x49,0x36,0x00,0x00},
{0x00,0x18,0x14,0x12,0x7F,0x10,0x00,0x00},
{0x00,0x27,0x45,0x45,0x45,0x39,0x00,0x00},
{0x00,0x3C,0x4A,0x49,0x49,0x30,0x00,0x00},
{0x00,0x01,0x71,0x09,0x05,0x03,0x00,0x00},
{0x00,0x36,0x49,0x49,0x49,0x36,0x00,0x00},
{0x00,0x06,0x49,0x49,0x29,0x1E,0x00,0x00},
{0x00,0x00,0x36,0x36,0x00,0x00,0x00,0x00},
{0x00,0x00,0xAC,0x6C,0x00,0x00,0x00,0x00},
{0x00,0x08,0x14,0x22,0x41,0x00,0x00,0x00},
{0x00,0x14,0x14,0x14,0x14,0x14,0x00,0x00},
{0x00,0x41,0x22,0x14,0x08,0x00,0x00,0x00},
{0x00,0x02,0x01,0x51,0x09,0x06,0x00,0x00},
{0x00,0x32,0x49,0x79,0x41,0x3E,0x00,0x00},
{0x00,0x7E,0x09,0x09,0x09,0x7E,0x00,0x00},
{0x00,0x7F,0x49,0x49,0x49,0x36,0x00,0x00},
{0x00,0x3E,0x41,0x41,0x41,0x22,0x00,0x00},
{0x00,0x7F,0x41,0x41,0x22,0x1C,0x00,0x00},
{0x00,0x7F,0x49,0x49,0x49,0x41,0x00,0x00},
{0x00,0x7F,0x09,0x09,0x09,0x01,0x00,0x00},
{0x00,0x3E,0x41,0x41,0x51,0x72,0x00,0x00},
{0x00,0x7F,0x08,0x08,0x08,0x7F,0x00,0x00},
{0x00,0x41,0x7F,0x41,0x00,0x00,0x00,0x00},
{0x00,0x20,0x40,0x41,0x3F,0x01,0x00,0x00},
{0x00,0x7F,0x08,0x14,0x22,0x41,0x00,0x00},
{0x00,0x7F,0x40,0x40,0x40,0x40,0x00,0x00},
{0x00,0x7F,0x02,0x0C,0x02,0x7F,0x00,0x00},
{0x00,0x7F,0x04,0x08,0x10,0x7F,0x00,0x00},
{0x00,0x3E,0x41,0x41,0x41,0x3E,0x00,0x00},
{0x00,0x7F,0x09,0x09,0x09,0x06,0x00,0x00},
{0x00,0x3E,0x41,0x51,0x21,0x5E,0x00,0x00},
{0x00,0x7F,0x09,0x19,0x29,0x46,0x00,0x00},
{0x00,0x26,0x49,0x49,0x49,0x32,0x00,0x00},
{0x00,0x01,0x01,0x7F,0x01,0x01,0x00,0x00},
{0x00,0x3F,0x40,0x40,0x40,0x3F,0x00,0x00},
{0x00,0x1F,0x20,0x40,0x20,0x1F,0x00,0x00},
{0x00,0x3F,0x40,0x38,0x40,0x3F,0x00,0x00},
{0x00,0x63,0x14,0x08,0x14,0x63,0x00,0x00},
{0x00,0x03,0x04,0x78,0x04,0x03,0x00,0x00},
{0x00,0x61,0x51,0x49,0x45,0x43,0x00,0x00},
{0x00,0x7F,0x41,0x41,0x00,0x00,0x00,0x00},
{0x00,0x02,0x04,0x08,0x10,0x20,0x00,0x00},
{0x00,0x41,0x41,0x7F,0x00,0x00,0x00,0x00},
{0x00,0x04,0x02,0x01,0x02,0x04,0x00,0x00},
{0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00},
{0x00,0x01,0x02,0x04,0x00,0x00,0x00,0x00},
{0x00,0x20,0x54,0x54,0x54,0x78,0x00,0x00},
{0x00,0x7F,0x48,0x44,0x44,0x38,0x00,0x00},
{0x00,0x38,0x44,0x44,0x28,0x00,0x00,0x00},
{0x00,0x38,0x44,0x44,0x48,0x7F,0x00,0x00},
{0x00,0x38,0x54,0x54,0x54,0x18,0x00,0x00},
{0x00,0x08,0x7E,0x09,0x02,0x00,0x00,0x00},
{0x00,0x18,0xA4,0xA4,0xA4,0x7C,0x00,0x00},
{0x00,0x7F,0x08,0x04,0x04,0x78,0x00,0x00},
{0x00,0x00,0x7D,0x00,0x00,0x00,0x00,0x00},
{0x00,0x80,0x84,0x7D,0x00,0x00,0x00,0x00},
{0x00,0x7F,0x10,0x28,0x44,0x00,0x00,0x00},
{0x00,0x41,0x7F,0x40,0x00,0x00,0x00,0x00},
{0x00,0x7C,0x04,0x18,0x04,0x78,0x00,0x00},
{0x00,0x7C,0x08,0x04,0x7C,0x00,0x00,0x00},
{0x00,0x38,0x44,0x44,0x38,0x00,0x00,0x00},
{0x00,0xFC,0x24,0x24,0x18,0x00,0x00,0x00},
{0x00,0x18,0x24,0x24,0xFC,0x00,0x00,0x00},
{0x00,0x00,0x7C,0x08,0x04,0x00,0x00,0x00},
{0x00,0x48,0x54,0x54,0x24,0x00,0x00,0x00},
{0x00,0x04,0x7F,0x44,0x00,0x00,0x00,0x00},
{0x00,0x3C,0x40,0x40,0x7C,0x00,0x00,0x00},
{0x00,0x1C,0x20,0x40,0x20,0x1C,0x00,0x00},
{0x00,0x3C,0x40,0x30,0x40,0x3C,0x00,0x00},
{0x00,0x44,0x28,0x10,0x28,0x44,0x00,0x00},
{0x00,0x1C,0xA0,0xA0,0x7C,0x00,0x00,0x00},
{0x00,0x44,0x64,0x54,0x4C,0x44,0x00,0x00},
{0x00,0x08,0x36,0x41,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7F,0x00,0x00,0x00,0x00,0x00},
{0x00,0x41,0x36,0x08,0x00,0x00,0x00,0x00},
{0x00,0x02,0x01,0x01,0x02,0x01,0x00,0x00},
{0x00,0x02,0x05,0x05,0x02,0x00,0x00,0x00} 
};

43 則留言:

  1. 如果 無線分享器 斷線後 會自動 在連線嗎? 還是會轉成ap模式等待再次等使用者入ssid?

    回覆刪除
  2. 這是一個很好的問題, 所以立馬做了實驗, 我先讓一切都能正常動作後, 故意關掉AP 5分鐘, 我想應該是夠斷了, 5分鐘後, 我重新連上http://esp8266.local的網頁, 可正常動作, 按打開LED燈或關掉LED燈都可以正常工作, 我不知原理是什麼, 但是你的問題的答案就是沒有影嚮, 我不確定斷線5分鐘是否足夠, 這你可以自己試看看, 但就我的看法, 就算你斷線後, 也只要電源重開, 它會記住你之前輸入自己家裡AP的SSID和PASSWORD, 會再度自動重新連上的

    回覆刪除
  3. 如果不做遠端遙控 就根本不需sta mode 更不需家裡的AP(ssid,pwd)
    你現在做的東西 直接用 ap mode 做就可以了
    遙控程式直接用手機app inventor 寫 手機wifi 連上8266 AP指定的ssid
    app inventor 連上192.168.4.1 即可控制
    不用繞來繞去

    回覆刪除
    回覆
    1. 你說的很正確, 但是...我只是想練習+記錄+分享, 沒有特別要做什麼東西

      刪除
  4. 遠端遙控才需AP+STA mode, 中繼請用mqtt(架在rpi)
    這才是 IOT killer=esp8266 的玩法

    回覆刪除
    回覆
    1. 你說的對, 我學習了, thank you

      刪除
    2. 不好意思,如果是按照上述的寫法,該怎麼寫?
      我的信箱:biung52075@gmail.com

      刪除
  5. 作者已經移除這則留言。

    回覆刪除
  6. 板主請問一下,要esp8266.local有動作,還要有什麼設備嗎?輸入IP位址可正常動作,但輸入esp8266.local卻找不到網頁!!

    回覆刪除
    回覆
    1. 你是用手機還是PC來輸入IP呢?

      刪除
    2. 版主你好,我也是遇到同樣的問題,我是用PC輸入ip可以顯示網頁,但用http://esp8266.local卻無法顯示

      刪除
  7. 板主請問, 小弟沒有OLED.可否把輸出轉到Arduino ide 的 serial monitor?

    回覆刪除
    回覆
    1. 可以啊...就只是把輸出的OLED的訊息, 改用printf到serial monitor

      刪除
  8. 你好
    我使用ESP-01,但是當我使用digitalWrite(2, 1)時,LED會發出弱弱光,繼電器並沒有動作(一通電GPIO就丟出高電位給繼電器,程式設定LOW),在使用digitalWrite(2, 0)繼電器一樣沒有動作
    接線圖: GPIO -> LED -> 4.7K電阻 -> 繼電器
    但當我把電阻拿掉Serial就會一直丟亂碼出來
    想請問有沒有碰到類似的問題

    回覆刪除
  9. ESP8266應該沒有足夠的驅動能力去推動繼電器, 必須加一個電晶體提供足夠大的電流去推動繼電器, 我找了一個網址你照著做看看 http://www.electroschematics.com/8975/arduino-control-relay/

    回覆刪除
  10. 老師請問一下,接電腦,程式都能正常執行,但我只要將電腦關掉,接esp8266的UART拿走,其它接線皆不變,讓esp8266單獨執行,就會找不到AP,難道esp8266不能單獨執行嗎?,一定要接UART及跟著電腦走嗎?,蟹蟹指導

    回覆刪除
    回覆
    1. 可否把UART的訊息貼上來, 我幫你看一看

      刪除
  11. 版主你好:
    很偶然的在網路上看到你的大作,覺得很有意思,也不禁手癢想提供一個可用手機WIFI介面直接控制ESP8266輸出的程式,可是試了許久發現程式都貼不上,說什麼不能用HTML這個標記或字眼,如果你不介意的話,我想把程式寄給你由你代發可以嗎?

    回覆刪除
    回覆
    1. 這是arduino的程式, 並不是網頁的程式

      刪除
  12. 應該說是ESP8266的程式,只是使用Arduino IDE來開發!因為在程式中有使用到一些網路HTML的語法,以便回應使用者;看到版主的這篇文章,感覺挺受用的,只可惜好像只能工作在桌機筆電的PC之下,就像訪客ttm說的,要發揮ESP8266的功能還是要用手機比較實際,感覺上好像也不少朋友對用手機做遙控滿有興趣的,如果只是想用手機WIFI介面直接控制一些開關的話,這個簡單的範例程式可以讓大家參考一下;這個程式的內容,它是在Arduino IDE中開發完成的,在程式中我是把ESP8266設定成一個AP,也就是像一般的WIFI分享器一樣,他的名稱叫"Hello_IoT",而密碼則是"12345678",所以說當程式順利燒進ESP8266後,接上電源時,我們必須先用手機進行WIFI搜尋連線與配對的動作,完成這個基本的動作以後就可以用手機來測試與遙控了,這個程式的好處是不管是Android或Apple的手機都可以使用!
    至於使用的方法如果各位不想寫程式去控制他的話,只要啟動任何一款手機上的網路瀏覽器就可以,其步驟如下:
    1.先打開手機的WIFI功能,並連上"Hello_IoT"這個網點
    2.啟動任一款的網路瀏覽器APP,如IE或Chrome
    3.在這個範例程式中我只提供了ESP8266的GPIO0,GPIO2兩個接腳的控制動作,所以在網址行中輸入"http://192.168.4.1/GPIO2ON"並前往後就可以讓ESP8266的GPIO2接腳輸出High,而且你的瀏覽器也會收到相對應的回應訊息(Arduino IDE的監控視窗也可以看要一些相關的輸出訊息),如果把ON-->OFF則會輸出Low,其他的接腳就依此類推了!
    如果想控制ESP8266其他的接腳,只要比照自行修改及增加程式就可以了!希望這個範例程式能對大家有些幫助!

    回覆刪除
    回覆
    1. 謝謝你願意和大家分享, 你可以寄e-mail到nhs54798799@gmail.com
      我可以幫你代PO

      刪除
    2. 版主檔案已經寄出了,你可以先試看看,有甚麼問題再告訴我!

      刪除
  13. 版主 你好
    想請問版主是否知道請問ESP8266能設定STA+AP模式後,連線到192.168.4.1以無線的方式,也就是不使用serial port來設定ESP8266連上家中的wifi嗎?

    回覆刪除
  14. 作者已經移除這則留言。

    回覆刪除
    回覆
    1. 版主 你好 請問一下
      連上後, 在網址列輸入192.168.4.1, 會看到下面畫面, 它會先掃瞄週遭可用的無線網路, 找到你想連線的無線網路, 輸入在SSID和PASSWORD按儲存

      這個可以自由選擇WIFI連線的頁面是需要自行編寫程式,還是esp8266內件就有了?

      板主回: 需自行編寫

      刪除
  15. 嗨~板大請問一下,一隻手機最多可以連結多少個不同的SSID NodeMCU 呢? 若要一隻手機可控制5個 nodemcu 上的LED 作(同時)亮 或 漸亮漸滅的動作,這樣可行嗎?,謝謝!

    回覆刪除
    回覆
    1. 一隻手機就只能連一個SSID(等同wifi), 若要一次控制5個裝置, 你只能手機連一個SSID nodemcu, 然後經由它和arduino uno板用software serial連接, 手機發出控制訊號, nodemcu接受到訊號, 用software serial傳給uno, 再由uno同時控制5個LED, 這樣應該是可以的

      刪除
  16. 版主您好 我剛剛複製您的程式碼 驗證的時候出了問題 說是font.h 這邊出了錯
    請問是有哪裡要做更改的嗎?

    回覆刪除
    回覆
    1. 把上面的程式碼貼到arduino, 再把OLED的字形檔陣列剪下來另外存成font.h, 應該就可以了, 我剛試過沒問題

      刪除
  17. 我是用arduino d1 他有自帶esp8266
    請問我的程式碼該改成什麼樣子

    回覆刪除
    回覆
    1. 我沒用過arduino d1, 所以不清楚該改什麼

      刪除
  18. 請問板子上有EEPROM?因為程式裡有EEPROM的函式,請問EEPROM的型號?

    回覆刪除
    回覆
    1. ESP8266內部本身就有EEPROM, 只有宣告就可以使用了, 不需要額外掛個EEPROM

      刪除
  19. 你好版主我的程式編譯時出現錯誤,不知道該如何解決?

    回覆刪除
  20. 板主您好~
    冒昧問了一個比較偏題的問題,
    當走出戶外要用手機操縱時,
    我相信不是用192.168.4.1就可以操控8266,
    雖我網路架構不是很好,
    但我大概知道一定需經過轉址....,
    那麼您有相關可以參考的文章嗎?
    自己用Xamarin 寫了App,
    但也只能在同一個區網下操作。
    感謝指教~

    回覆刪除
    回覆
    1. 192.168.4.1是操作在AP狀態下, 手機連上後, 兩邊應該就能溝通, 不需要轉址吧

      刪除
    2. sorry,我忘了這點,
      那麼我人在外地手機經網際網路,怎麼連到server呢?
      真的連上server後,server又如何知道我是要連到底下的ESP8266呢?

      刪除
    3. 你要有一個固定ip, 再透過ap設定轉址到你內網的esp8266網址, 詳情可參考這一篇
      https://ithelp.ithome.com.tw/questions/10181424

      刪除
    4. 可以透過 Google FireBase

      刪除

歡迎大家來討論交流一下~~~