ESP8266開發之旅 應用篇① 局域網應用 ——炫酷RGB彩燈

1.前言

    這一篇,博主將教你們怎麼去實現一個WiFi RGB彩燈。
    先來一個博主已經實現功能的圖片,以下:html

image

    固然,博主也拍了運行視頻,請點擊 傳輸門android

1.1 知識儲備

    本篇須要用到如下知識點:git

  • 運用到ArduinoJson庫,github傳送門,請讀者自行下載該庫放到Arduino安裝目錄(這裏直接使用,博主後面計劃會詳細講解該庫,敬請期待);
  • 運用到TCP Server服務,請參考 ESP8266開發之旅 網絡篇⑦ TCP Server & TCP Client
  • 運用到STA模式,請參考 ESP8266開發之旅 網絡篇④ Station——ESP8266WiFiSTA庫的使用
  • 運用到一鍵配網功能,請參考 ESP8266開發之旅 網絡篇⑧ SmartConfig——一鍵配網

1.2 設計原理

    這裏的局域網,博主理解爲手機、ESP8266均鏈接同一個路由wifi,而後8266是做爲服務端,手機做爲客戶端,手機再經過wifi給8266發送數據,8266接收到數據再把數據分發給Arduino,而後Arduino解析協議數據以達到控制效果。
    設計圖以下:
imagegithub

2.實驗準備

  • ESP202 8266小黃板或者NodeMcu開發板或者其餘8266模塊;
  • Android 手機 + 博主App;
  • mega2560開發板(博主偷懶想用兩個硬件串口,讀者也能夠用UNO);
  • RGB LED模塊以及若干線;

3.實驗步驟

    咱們本應用須要分紅兩個部分的設計——Mega2560 Arduino端以及ESP8266端。須要分別往兩塊板子燒錄代碼以及鏈接電路。json

3.1 8266端代碼

    往8266燒入如下代碼:服務器

/**
* 日期:2019/02/18
* 功能:wifi lamp 8266端
*       加入SmartConfig功能
* 做者:單片機菜鳥
**/
#include <ESP8266WiFi.h>

#define MAX_SRV_CLIENTS 3   //最大同時聯接數,即你想要接入的設備數量,8266tcpserver只能接入五個,哎
#define LED 2
#define DEBUG //是否開啓debug功能

#ifdef DEBUG
#define DebugPrintln(message)    Serial.println(message)
#else
#define DebugPrintln(message)
#endif

#ifdef DEBUG
#define DebugPrint(message)    Serial.print(message)
#else
#define DebugPrint(message)
#endif
  
const unsigned long BAUD_RATE = 115200;// serial connection speed
  
WiFiServer server(8266);//你要的端口號,隨意修改,範圍0-65535
WiFiClient serverClients[MAX_SRV_CLIENTS];
int flag = HIGH;//默認當前滅燈
  
/**
* @Desc 初始化操做
*/
void setup() {
  Serial.begin(BAUD_RATE);
  pinMode(LED,OUTPUT);
  digitalWrite(LED, HIGH);

  if(!autoConfig()){
    smartConfig();
    DebugPrint("Connecting to WiFi");//寫幾句提示,哈哈
    while (WiFi.status() != WL_CONNECTED) {
    //這個函數是wifi鏈接狀態,返回wifi連接狀態
       delay(500);
       DebugPrint(".");
    }
  }
  
  delay(1000);
  digitalWrite(LED, LOW);
  DebugPrintln("IP address: ");
  DebugPrintln(WiFi.localIP());//WiFi.localIP()返回8266得到的ip地址
  server.begin();
  server.setNoDelay(true);  //加上後才正常些
  //使能軟件看門狗的觸發間隔
  ESP.wdtEnable(5000);
}
  
/**
* @Desc  主函數
*/
void loop() {
  uint8_t index;
  if (server.hasClient()){
        for (index = 0; index < MAX_SRV_CLIENTS; index++){
            if (!serverClients[index] || !serverClients[index].connected()){
                if (serverClients[index]) serverClients[index].stop();//未聯接,就釋放
                serverClients[index] = server.available();//分配新的
                continue;
            }
        }
        //8266tcpserver只能接入五個  超出的須要釋放
        WiFiClient serverClient = server.available();
        if (serverClient){
          serverClient.stop();
        }
  }
  
  for (index = 0; index < MAX_SRV_CLIENTS; index++){
        if (serverClients[index] && serverClients[index].connected()){
            //處理客戶端發過來的數據
            if (serverClients[index].available()){
                while (serverClients[index].available()) 
                    //把數據發送給mega
                    Serial.write(serverClients[index].read());
            }
        }
   }

   if(Serial.available()>0){
      char ch = Serial.read();
      //收到ardunio發過來的進入smartconfig模式的命令
      if(ch == '1'){
        smartConfig();
        delay(1000);
        digitalWrite(LED, LOW);
        DebugPrintln("IP address: ");
        DebugPrintln(WiFi.localIP());//WiFi.localIP()返回8266得到的ip地址
      }
   }

   //喂狗
   ESP.wdtFeed();
}

/**
* 自動鏈接20s 超過以後自動進入SmartConfig模式
*/
bool autoConfig(){
  WiFi.mode(WIFI_AP_STA);     //設置esp8266 工做模式
  WiFi.begin();
  delay(2000);//剛啓動模塊的話 延時穩定一下
  DebugPrintln("AutoConfiging ......");
  for(int index=0;index<20;index++){
    int wstatus = WiFi.status();
    if (wstatus == WL_CONNECTED){
      DebugPrintln("AutoConfig Success");
      DebugPrint("SSID:");
      DebugPrintln(WiFi.SSID().c_str());
      DebugPrint("PSW:");
      DebugPrintln(WiFi.psk().c_str());
      return true;
    }else{
      DebugPrint(".");
      delay(1000);
      flag = !flag;
      digitalWrite(LED, flag);
    } 
  }
  DebugPrintln("AutoConfig Faild!");
  return false;
}

/**
* 開啓SmartConfig功能
*/
void smartConfig()
{
  WiFi.mode(WIFI_STA);
  delay(2000);
  DebugPrintln("Wait for Smartconfig");
  // 等待配網
  WiFi.beginSmartConfig();
  while (1){
    DebugPrint(".");
    delay(500);
    flag = !flag;
    digitalWrite(LED, flag);
    
    if (WiFi.smartConfigDone()){
      //smartconfig配置完畢
      DebugPrintln("SmartConfig Success");
      DebugPrint("SSID:");
      DebugPrintln(WiFi.SSID().c_str());
      DebugPrint("PSW:");
      DebugPrintln(WiFi.psk().c_str());
      WiFi.mode(WIFI_AP_STA);     //設置esp8266 工做模式
      WiFi.setAutoConnect(true);  // 設置自動鏈接
      break;
    }
  }
}

    整個代碼的流程是這樣的:網絡

  1. 8266上電啓動後,進入自動鏈接模式autoConfig(根據上一次成功鏈接的SSID和密碼),最多嘗試20s,在嘗試鏈接的過程當中,LED燈會每隔1s閃爍一下,表示正在鏈接狀態;若是鏈接成功,就直接配置8266服務器模式。
  2. 若是上面操做失敗(可能鏈接的熱點不存在了或者修改了密碼),那麼會自動進入一鍵配置模式SmartConfig等待手機一鍵配置,這個過程是不限制時間的,LED會每隔0.5s閃一下,表示處在SmartConfig狀態,這時你們能夠去手機端開始一鍵配置。

    這裏代碼功能其實就是把8266當作服務端,8266鏈接上路由wifi,而後監聽鏈接進來的客戶端(這裏是手機)app

  • app下載地址: wifilamp
    若是連接失效,請找博主要最新的apk。
  • app源碼下載地址:github

讀者安裝好app以後,會看到以下的手機配置頁面,請一步步設置:tcp

image

image

image

    若是提示失敗,通常都是由於你的8266模塊並無進入到SmartConfig模式,能夠嘗試重啓一下。再者就是,smartconfig不保證配網成功率100%;函數

3.2 mega端代碼

    燒寫如下代碼到mega2560板子,代碼以下:

/**
* 日期:2019/02/18
* 功能:wifi lamp arduino端
* 做者:單片機菜鳥
**/
#include <SoftwareSerial.h>
#include <ArduinoJson.h>
  
const unsigned long BAUD_RATE = 115200;                   // serial connection speed
const size_t MAX_CONTENT_SIZE = 50; 
const size_t t_bright=1,t_color=2,t_frequency=3,t_switch=4;
 
//#define UNO      //uncomment this line when you use it with UNO board
#define MEGA    //uncomment this line when you use it with MEGA board
 
#ifdef UNO
 SoftwareSerial mySerial(10,11);
#endif
  
#ifdef UNO
#define WifiSerial  Serial
#define MyDebugSerial mySerial
#endif
   
#ifdef MEGA
#define WifiSerial Serial1
#define MyDebugSerial Serial
#endif  
 
//該條語句用於使能DEBUG輸出信息,屏蔽掉就不會輸出debug調試信息
#define DEBUG
//該條語句用於使能是共陰RGB  屏蔽掉就是共陽RGB
//#define COMMON_GND
 
#ifdef DEBUG
#define DBGLN(message)    MyDebugSerial.println(message)
#else
#define DBGLN(message)
#endif
 
#ifdef UNO 
#define PIN_RED 3 //red 引腳
#define PIN_GREEN 5 //green 引腳
#define PIN_BLUE 6 //blue 引腳
#define PIN_ENABLE 9  //使能引腳 pwm控制亮度
#define PIN_KEY 7// 按鍵
#else
#define PIN_RED 2
#define PIN_GREEN 3
#define PIN_BLUE 4
#define PIN_ENABLE 5
#define PIN_KEY 6  
#endif 
 
int red = 0,green = 0,blue = 0;
int type = 4;//當前模式 1亮度 2顏色 3呼吸 4開關
int frequency = 1;//頻率
int switch_status = 1;//關閉 or 開啓
int bright = 1;//亮度
 
char response[MAX_CONTENT_SIZE];
int fadeValue = 0;//當前亮度
bool isAdd = true;//是不是從暗到亮
 
// 定義記錄按鍵當前狀態的變量
int state_btn;
// 定義記錄按鍵最近一次狀態變化的變量,並初始化狀態爲LOW。
int lastButtonState = LOW;
// 定義記錄最近一次抖動的時間變量,並初始化時間爲0毫秒。
long lastDebounceTime = 0;
// 定義延遲抖動的時間變量
long debouncdDelay = 60;
  
/**
* @Desc 初始化操做
*/
void setup() {
  pinMode(PIN_RED, OUTPUT);
  pinMode(PIN_GREEN, OUTPUT);
  pinMode(PIN_BLUE, OUTPUT);
  pinMode(PIN_ENABLE, OUTPUT);
  pinMode(PIN_KEY,INPUT);
  
  WifiSerial.begin(BAUD_RATE);
  #ifdef DEBUG
    #ifdef UNO
      MyDebugSerial.begin(9600);//軟串口9600穩定
    #else
      MyDebugSerial.begin(BAUD_RATE);
    #endif
  #endif
  DBGLN("Arduino Init End");
}
  
/**
* @Desc  主函數
*/
void loop() {
 
  if(WifiSerial.available()>0){
    clrEsp8266ResponseBuffer();
    int data_size = ReceiveMessage(response, sizeof(response));
    if(data_size>0){
      //開始解析數據
      parseData(response);
    }
  }
 
  if(type == t_frequency){
    //呼吸燈效果
    breatheRGB(frequency);
  }
  checkButton();
}
 
/**
* 讀取串口緩衝區裏面的數據
*/
int ReceiveMessage(char* content, size_t maxSize){
  //不用 readBytes 由於比較耗時
  size_t length = WifiSerial.readBytesUntil('}',content, maxSize);
  content[length] = '}';
  content[++length] = 0;
  DBGLN(content);
  return length;
}
 
/**
     * @Desc 解析json
     * 有三種
     * 1.亮度控制頁面(0 暗 1正常 2亮)
     * {
     *     "t": 1,
     *     "bb": 2
     * }
     * 2.顏色控制頁面
     * {
     *     "t": 2,
     *     "cr": 154,
     *     "cg": 147,
     *     "cb": 255
     * }
     * 3.呼吸燈控制頁面(0 慢呼吸 1正常 2快)
     * {
     *    "t": 3,
     *    "gf": 1
     * }
     * 4.開關控制(0關閉 1開啓)
     * {
     *    "t": 4,
     *    "ss": 1
     * } 
     **/
bool parseData(char* content) {
//    -- 根據咱們須要解析的數據來計算JSON緩衝區最佳大小
//   若是你使用StaticJsonBuffer時才須要
//    const size_t BUFFER_SIZE = 1024;
//   在堆棧上分配一個臨時內存池
//    StaticJsonBuffer<BUFFER_SIZE> jsonBuffer;
//    -- 若是堆棧的內存池太大,使用 DynamicJsonBuffer jsonBuffer 代替
  DynamicJsonBuffer jsonBuffer;
   
  JsonObject& root = jsonBuffer.parseObject(content);
   
  if (!root.success()) {
    Serial.println("JSON parsing failed!");
    return false;
  }
    
  type = root["t"];
  switch(type){
    case t_bright:
         bright = root["bb"];
         brightRGB(bright);
         break;
    case t_color:
         red = root["cr"];
         green = root["cg"];
         blue = root["cb"];
         colorRGB(red,green,blue);
         break;
    case t_frequency:
         frequency = root["gf"];
         break;
    case t_switch:
         switch_status = root["ss"];
         bool enable = switch_status == 1;
         switchRGB(enable);
         break;     
  }
  return true;
}
 
/**
* 控制燈亮度
*/
void brightRGB(int bright){
  int level = bright%3;
  int bright_level;
  switch(level){
    case 0://暗  50
      bright_level = 50;
      break;
    case 1://正常 100
      bright_level = 100;
      break;
    case 2://亮  200
      bright_level = 200;
      break;
  }
    #ifdef COMMON_GND
     //共地
     analogWrite(PIN_ENABLE,bright_level);
    #else
     analogWrite(PIN_ENABLE,255-bright_level);
    #endif
}
 
/**
* 控制RGB顏色
*/
void colorRGB(int red, int green, int blue){
  #ifdef COMMON_GND
     analogWrite(PIN_RED,constrain(red,0,255));
     analogWrite(PIN_GREEN,constrain(green,0,255));
     analogWrite(PIN_BLUE,constrain(blue,0,255));
  #else
     analogWrite(PIN_RED,constrain(255-red,0,255));
     analogWrite(PIN_GREEN,constrain(255-green,0,255));
     analogWrite(PIN_BLUE,constrain(255-blue,0,255));
  #endif
}
 
/**
* 控制亮滅
*/
void switchRGB(bool enable){
  if(enable){
    //打開
    #ifdef COMMON_GND
     //共地
     analogWrite(PIN_ENABLE,255);
    #else
     analogWrite(PIN_ENABLE,0);
    #endif
  }else{
    //關閉
    #ifdef COMMON_GND
     //共地
     analogWrite(PIN_ENABLE,0);
    #else
     analogWrite(PIN_ENABLE,255);
    #endif
  }
}
 
/**
* 呼吸燈
*/
void breatheRGB(int frequency){
  int level = frequency%3;
  int f_level;
  switch(level){
    case 0://慢  50
      f_level = 3;
      break;
    case 1://正常 100
      f_level = 10;
      break;
    case 2://快  200
      f_level = 20;
      break;
  }
  if(isAdd){
    //遞增方向
     fadeValue +=f_level;
     if(fadeValue>=255){
       fadeValue = 255;
       isAdd =false;
     }
  }else{
    //遞減方向
    fadeValue -=f_level;
     if(fadeValue<=0){
       fadeValue = 0;
       isAdd =true;
     }
  }
  analogWrite(PIN_ENABLE,fadeValue);
  delay(20);
}
 
/**
* 檢查按鍵功能
*/
void checkButton(){
  int buttonState = digitalRead(PIN_KEY);//讀取當前按鍵狀態
  if(buttonState != lastButtonState){
     //若是按鍵發生了變化  則從新設置最近一次抖動的時間
     //方法millis()能夠獲取當前時間,單位統一爲毫秒。
     lastDebounceTime = millis();  
  }
   
  // 判斷按鍵按下狀態時間間隔是否大於延遲抖動的時間長度。
  if(millis()-lastDebounceTime>debouncdDelay){
    // 判斷當前的按鍵狀態是否和以前有所變化
    if(buttonState != state_btn){
       // 若是發生了變化,
       // 則更新按鍵狀態變量。
       state_btn = buttonState;
       if(state_btn == HIGH){
        //再次確認是否真的按下了按鍵
         DBGLN("smartconfig");
         WifiSerial.write('1');
       }
    }
  }
  // 更新按鍵最近一次狀態變化的變量
  lastButtonState = buttonState;
}
 
void clrEsp8266ResponseBuffer(void){
    memset(response, 0, MAX_CONTENT_SIZE);      //清空
}

代碼解釋:
    這裏咱們用到了一個按鍵,按下按鍵就給8266發個命令進入SmartConfig模式(Arduino和8266經過串口1通訊)。

加上讀者已經配置成功Smartconfig了,那麼咱們鏈接好電路以後就能夠進行控制操做了。請看app控制頁面:

image

image

image

    接下來,若是有使能Debug的話,應該會打印如下信息:

image

注意

  • 最後下載到板子的時候,最好把調試信息去掉,會影響到解析速度。註釋掉 #define DEBUG
  • 在上面的app源碼中,博主已經將smartconfig的代碼抽取成一個android module,懂android開發的同窗能夠自行取來用。

4.實驗總結

博主簡單介紹如何基於以前講的基礎知識來作一個簡單項目,但願你們鞏固認識。

相關文章
相關標籤/搜索