DIY Smart Bulb แบบที่เราทำเองได้จาก ESP8266 และ Android App

Posted by | October 14, 2014 | Android, Arduino, Digital Culture, Embeded System | 8 Comments

ยุค Internet of Thing กำลังจะมาถึง ตอนนี้จะหันไปทางไหน ก็จะมีโฆษณา บ้านเมืองในอนาคต อุปกรณ์ต่างๆ สะดวกสบาย และอุปกรณ์จะควบคุมจากที่ไหน ก็ได้ (ถ้าอินเตอร์เน็ทเข้าถึง) ผมเลยอยากจุดประกาย ด้วยโครงการง่ายๆ อย่างเช่น Smart Bulb ,Smart Plug ,Smart Relay , Smart Outlet อยากจะเรียกอะไร ก็เรียกไปเลยนะครับ ซึ่งในโปรเจครูปแบบนี้ กำลังนิยมเป็นจำนวนเยอะมากใน Kickstarter เห็นระดมทุนเพื่อสร้างกันบ่อยๆ

สำหรับโปรเจคนี้ Wifi Light Bulb ของทางผม เป็นแบบเริ่มต้นนะครับ แค่เปิดปิดหลอดไฟ หรือ อุปกรณ์ใดๆ ผ่าน Smart phone ได้ แต่ผมขยับออกมาออกมาอีกนิด ให้เราดูสถานะการทำงานของหลอดไฟ ผ่านมือถือได้ ซึ่งตอนนี้จะควบคุมพร้อมกันหลายตัวได้ครับ โปรเจคนี้ยังไม่ได้ Smart มากนะครับ น่าจะพอจุดประกายให้ พวกเราได้ไอเดีย กับ หาแนวทางพัฒนากันต่อได้ อันนี้เป็น diagram บอกส่วนประกอบการทำงาน หัวใจหลักๆอยู่ที่บอร์ด arduino ที่เป็น web server

IMG_20141014_150501

 

โปรเจคประกอบไปด้วย 2 ส่วน

Android App

android-app

  • ใช้ App Inventor 2 ตั้งใจจะให้ต่อยอดกันง่ายๆ
  • ตัวโปรแกรมสามารถกดเปลี่ยนหมายเลข IP ได้
  • ควบคุมอุปกรณ์ ได้แค่ตัวเดียว ( ถ้าอยากเพิ่มต้องลองทำต่อเองนะ )
  • อ่านสถานะของหลอดไฟได้ ว่าตอนนี้ เปิดหรือปิดอยู่

capture-20141014-140821

ให้ดูว่า App นี้ใช้อะไรบ้าง หลักที่ต้องใช้นะครับ คือ Web นะครับ ตัว Web จะทำให้ App ยิงคำสั่งไปยังหน้า WebServer ได้ครับ แล้วภายในตัวจะมีรับ Content เพื่อเอามาตรวจสอบเป็นสถานะได้ครับ ส่วนวิธีใช้ เราดูใน code ไฟล์ .aia ที่ผมทิ้งไว้แล้วกัน โปรแกรม App Inventor ผมใช้ไม่ได้ค่อยมากครับ ใครอยากจะเพิ่มเติมให้สวย ผมฝากด้วยนะครับ

ผมลองหาดูว่า App Inventor สามารถสื่อสาร รูปแบบไหน ผ่าน tcp/ip ได้บ้าง ก็พบแค่ สามารถต่อเข้าเวปได้ ผมจึงออกแบบส่วนใน arduino server ให้ต่อสนองกับเวป หาวิธีเปิด socket อย่างเดียวไม่เจอ เอาเป็นว่าใครพึ่งลองทำ ทำแบบนี้ไปก่อนครับ แต่ถ้าใครเชี่ยวชาญแล้ว เจอวิธีอะไรที่น่าสนใจ comment บอกผมด้วย

หน้าตาของส่วน Block

capture-20141014-152106

มาดูส่วนประกอบของ Block ใน App Intentor2 ผมจะใช้ส่วนประกอบที่เพิ่มเติม web และ Timer

  1. Web จะใช้ส่ง Request ไปยัง Server (Arduino) ซึ่งจะเหมือน server ได้รับคำสั่ง มันจะส่ง status กลับไปที่ตัว client ที่เรียกของข้อมูล เลยทำให้ App รู้สถานะ เปิด หรือ ปิดได้
  2. เพิ่มเติมจะมี timer ที่ทางผมตั้งเวลาให้ทำงานทุก 3000ms ให้เข้าไป update หน้า web ด้วย เพื่อ ที่จะได้ update สถานะหลอดไฟครับ

ผมก็ทิ้งไฟล์ App ไว้โหลดได้ตรงไหนล่ะครับ LightControlV2.aia

Arduino Mini WebServer ( Arduino + ESP8266 )

ถือเป็นการต่อยอดจากบทความที่ผ่านมา  ที่ทางเราทำเวป server ลงไปใน Arduino + ESP8266  ซึ่งในครั้งนี้ เราได้แก้ไขปัญหาเรื่องการ update ข้อมูลไม่ทันบ้าง และ เพิ่มส่วนการ check Status ระหว่างการสื่อสาร Arduino กับ ESP8266 ให้มันทำงานถูกต้องยิ่งขึ้น ซึ่งมันเลยช่วยให้เสถียรขึ้น และ ทำงานได้พร้อมกัน ไม่มีปัญหา (แต่ยังมีหลุดๆบ้าง กด reset น่าจะหายครับ)

  • ตัว Server ยังเป็น Webserver มีหน้า Page home และ Page led
  • สามารถ Get Status ระหว่าง ESP8266 กับ Arduino ได้ดียิ่งขึ้น
  • แก้ไขปัญหา เรื่องการรับส่งข้อมูล เชื่อมต่อพร้อมกัน หลายๆ client
  • ทำงานได้ทั้ง Arduino UNO ,Arduino MEGA, Arduino  Leonadro
  • ยังไม่ได้ทำเป็น Library นะ เหมาะกับคนที่อยากรู้ลองแบบเบื้องต้น ว่ามันทำงานได้อย่างไง (ถือเป็นของดีหรือนิ)

โปรแกรม Webserver ตัวใหม่ หรือ ไปโหลดได้ที่ Github

#include <SoftwareSerial.h>
//Leonardo
//Serial_ & dbgTerminal = Serial;
//HardwareSerial & espSerial = Serial1;

////UNO & M328P
#include 
SoftwareSerial dbgTerminal(10, 11); // RX, TX
HardwareSerial & espSerial = Serial;

//
////MEGA2560 
//HardwareSerial & dbgTerminal = Serial;
//HardwareSerial & espSerial = Serial1;

// set pin numbers:
const int ledPin =  13;      // the number of the LED pin
const int ESP8266_CHPD = 4;

// Variables will change:
int ledState = HIGH;             // ledState used to set the LED

#define BUFFER_SIZE 128
char buffer[BUFFER_SIZE];

void setup() { 
  pinMode(ledPin, OUTPUT);  
  //pinMode(ESP8266_CHPD, OUTPUT);
      
  dbgTerminal.begin(9600); // Serial monitor
  espSerial.begin(115200); // ESP8266
    
  //while (!dbgTerminal) {
   // ; // wait for serial port to connect. Needed for Leonardo only
  //}

  dbgTerminal.println(F("ESP8266 demo."));

  //hardReset();
  //delay(2000);
  
  clearSerialBuffer();
  
  //connect to router
  //connectWiFi("YOUR_SSID", "YOUR_PASSWORD");
  
  //test if the module is ready
  //dbgTerminal.print("AT : ");
  //dbgTerminal.println( GetResponse("AT",100) );
    
  //Change to mode 1
  //dbgTerminal.print("AT+CWMODE=1 : ");
  //dbgTerminal.println( GetResponse("AT+CWMODE=1",10) );
        
  //set the multiple connection mode
  dbgTerminal.print(F("AT+CIPMUX=1 : "));
  dbgTerminal.println( GetResponse("AT+CIPMUX=1",10) );
  
  //set the server of port 80 check "no change" or "OK"
  dbgTerminal.print(F("AT+CIPSERVER=1,8888 : "));
  dbgTerminal.println( GetResponse("AT+CIPSERVER=1,8888", 10) );
 
  //set time out
  //dbgTerminal.print("AT+CIPSTO=15 : ");
  //dbgTerminal.println( GetResponse("AT+CIPSTO=15",10) );
  
   //print the ip addr
  dbgTerminal.print(F("ip address : "));
  dbgTerminal.println( GetResponse("AT+CIFSR", 10) );
  delay(200);

   
  dbgTerminal.println();
  dbgTerminal.println(F("Start Webserver"));

  digitalWrite(ledPin,ledState);  
}

void loop() {
  int ch_id, packet_len;
  char *pb;  
  espSerial.readBytesUntil('\n', buffer, BUFFER_SIZE);
  
  if(strncmp(buffer, "+IPD,", 5)==0) {
    // request: +IPD,ch,len:data
    sscanf(buffer+5, "%d,%d", &ch_id, &packet_len);
    if (packet_len > 0) {
      // read serial until packet_len character received
      // start from :
      pb = buffer+5;
      while(*pb!=':') pb++;
      pb++;
      if (strncmp(pb, "GET /led", 8) == 0) {
        dbgTerminal.print(millis());
        dbgTerminal.print(" : ");
        dbgTerminal.println(buffer);
        dbgTerminal.print( "get led from ch :" );
        dbgTerminal.println(ch_id);
 
        delay(100);
        clearSerialBuffer();
        
       if (ledState == LOW)
          ledState = HIGH;
       else
          ledState = LOW;
        digitalWrite(ledPin, ledState);
        
        homepage(ch_id);

      } else if (strncmp(pb, "GET / ", 6) == 0) {
        dbgTerminal.print(millis());
        dbgTerminal.print(" : ");
        dbgTerminal.println(buffer);
        dbgTerminal.print( "get Status from ch:" );
        dbgTerminal.println(ch_id);
        
        delay(100);
        clearSerialBuffer();

        homepage(ch_id);
      }
    }
  }
  clearBuffer();
}

void homepage(int ch_id) {
  String Header;

  Header =  "HTTP/1.1 200 OK\r\n";
  Header += "Content-Type: text/html\r\n";
  Header += "Connection: close\r\n";  
  //Header += "Refresh: 5\r\n";
  
  String Content;
  Content = "D";
  Content += String(ledState);
  
  Header += "Content-Length: ";
  Header += (int)(Content.length());
  Header += "\r\n\r\n";
  
  
  espSerial.print("AT+CIPSEND=");
  espSerial.print(ch_id);
  espSerial.print(",");
  espSerial.println(Header.length()+Content.length());
  delay(10);
  
  // for debug buffer serial error
  //while (espSerial.available() >0 )  {
  //  char c = espSerial.read();
  //  dbgTerminal.write(c);
  //  if (c == '>') {
  //      espSerial.print(Header);
  //      espSerial.print(Content);
  //  }
  //}
  
  if (espSerial.find(">")) {
      espSerial.print(Header);
      espSerial.print(Content);
      delay(10);
   }
 
//  Serial1.print("AT+CIPCLOSE=");
//  Serial1.println(ch_id);


}



// Get the data from the WiFi module and send it to the debug serial port
String GetResponse(String AT_Command, int wait){
  String tmpData;
  
  espSerial.println(AT_Command);
  delay(10);
  while (espSerial.available() >0 )  {
    char c = espSerial.read();
    tmpData += c;
    
    if ( tmpData.indexOf(AT_Command) > -1 )         
      tmpData = "";
    else
      tmpData.trim();       
          
   }
   return tmpData;
}

boolean hardReset() {
  String tmpData;
  
  digitalWrite(ESP8266_CHPD,LOW);
  delay(100);
  digitalWrite(ESP8266_CHPD,HIGH);
  delay(1000);
    
  while ( espSerial.available() > 0 ) {
    char c = espSerial.read();
    tmpData +=c;
    espSerial.write(c);
    if ( tmpData.indexOf("Ready") > -1 ) {
      //Serial.println("Ready");
        clearBuffer();
        return 1;
    } 
  }
}

void clearSerialBuffer(void) {
       while ( espSerial.available() > 0 ) {
         espSerial.read();
       }
}

void clearBuffer(void) {
       for (int i =0;i<BUFFER_SIZE;i++ ) {
         buffer[i]=0;
       }
}
         
boolean connectWiFi(String NetworkSSID,String NetworkPASS) {
  String cmd = "AT+CWJAP=\"";
  cmd += NetworkSSID;
  cmd += "\",\"";
  cmd += NetworkPASS;
  cmd += "\"";
  
  dbgTerminal.println(cmd); 
  dbgTerminal.println(GetResponse(cmd,10));
}


About chang

ชื่อ “ช้าง” ส่วนมากเขาจะเรียกว่า “พี่ช้าง” แล้ว มีความสนใจทางเทคโนโลยีทางคอมพิวเตอร์ อิเล็กทรอนิกส์ และ หุ่นยนต์ เป็นทั้งนักคิด นักประดิษฐ์ ชอบทดลองเล่น จนเดี่ยวนี้รู้สึกว่าจะเล่นมากกว่ามืออาชีพไปสักแล้ว

8 Comments

  • Khälid Öz Deddüäng says:

    ขอโทษนะครับ รบกวนถามนิดนึงครับ ตัวนี้สมาร์ทโฟนต้องต่อกับ wifi อย่างเดียวหลอครับ ใช้ 3G แทนได้มั้ยครับ

  • worawoot says:

    พี่ครับใช้code ตามนี้ได้เลยหรือเปล่าครับ
    วงจรคือตามนี้ใช้ไหมครับ
    uno to esp8266
    rx(10) tx
    tx(11) rx
    3.3v vcc
    gnd gnd
    4 ch_pd
    code ต้องแก้ตรง
    boolean connectWiFi(String NetworkSSID,String NetworkPASS)
    เป็นชื่อ wifiของเรา และ pass ของเราใช่ไหมครับ
    ผมทำตามนี้ถูกเปล่าครับ
    ผมกำลังหัดเล่นครับ

    • korakit says:

      ไม่ทราบว่าพี่ใช้ได้หรือยังครับ ถ้าได้แล้วช่วยแนะนำหน่อยนะครับ พอดีผมทำโปรเจ็คส่งอาจารย์อะครับ ขอบคุณมากครับ

  • korakit says:

    ขอบคุณมากครับที่แบ่งปันความรู้ แต่ผมขอโค้ดที่พี่ช้างใช้เลยได้ไหม๋ครับ ของผมลองแล้ว ไม่ขึ้นอะครับ

  • Петя Васечкин says:

    ขออภัยใน Google แปลของฉัน ผมชอบคลิปวิดีโอของคุณผมต้องการที่จะเก็บมัน แต่ฉันสามเณร. ((

    นี่คือโปรแกรมเฉพาะสำหรับ Arduino หรือไม่ ซอฟท์แว esp8266 ไม่?

  • Warat Keawpang says:

    พี่ครับ ถ้าหากผมใช้ nodeMCUสามารถใช้โค๊ตนี้ได้รึป่าวครับ

  • Thanachai B says:

    โหลดไฟล์แอบไม่ได้คับ

  • TOTeGuard says:

    รบกวนสอบถามหากพอร์ต80ถูกปิดกั้น http://ย้ายไปพอร์ตอื่นแล้ว ใช้ไม่ได้ ต้องทำไงครับ

Leave a Reply to Петя Васечкин Cancel Reply

Your email address will not be published.