ESP8266开发之旅 应用篇① 局域网应用 ——炫酷RGB彩灯

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/dpjcn1990/article/details/92830457

1.前言

    这一篇,博主将教大家怎么去实现一个WiFi RGB彩灯。
    先来一个博主已经实现功能的图片,如下:

image

    当然,博主也拍了运行视频,请点击 传输门

1.1 知识储备

    本篇需要用到以下知识点:

  • 运用到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解析协议数据以达到控制效果。
    设计图如下:
image

2.实验准备

  • ESP202 8266小黄板或者NodeMcu开发板或者其他8266模块;
  • Android 手机 + 博主App;
  • mega2560开发板(博主偷懒想用两个硬件串口,读者也可以用UNO);
  • RGB LED模块以及若干线;

3.实验步骤

    我们本应用需要分成两个部分的设计——Mega2560 Arduino端以及ESP8266端。需要分别往两块板子烧录代码以及连接电路。

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下载地址: wifilamp
    如果链接失效,请找博主要最新的apk。
  • app源码下载地址:github

读者安装好app之后,会看到如下的手机配置页面,请一步步设置:

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.实验总结

博主简单介绍如何基于之前讲的基础知识来做一个简单项目,希望大家巩固认识。

展开阅读全文

没有更多推荐了,返回首页