1.前言

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

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

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

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之后,会看到如下的手机配置页面,请一步步设置:

    如果提示失败,一般都是因为你的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控制页面:

    接下来,如果有使能Debug的话,应该会打印以下信息:

注意

  • 最后下载到板子的时候,最好把调试信息去掉,会影响到解析速度。注释掉 #define DEBUG
  • 在上面的app源码中,博主已经将smartconfig的代码抽取成一个android module,懂android开发的同学可以自行取来用。

4.实验总结

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

ESP8266开发之旅 应用篇① 局域网应用 ——炫酷RGB彩灯的更多相关文章

  1. ESP8266开发之旅 网络篇⑯ 无线更新——OTA固件更新

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  2. ESP8266开发之旅 进阶篇② 闲聊Arduino IDE For ESP8266烧录配置

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  3. ESP8266开发之旅 基础篇① 走进ESP8266的世界

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  4. ESP8266开发之旅 基础篇② 如何安装ESP8266的Arduino开发环境

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  5. ESP8266开发之旅 基础篇③ ESP8266与Arduino的开发说明

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  6. ESP8266开发之旅 基础篇④ ESP8266与EEPROM

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  7. ESP8266开发之旅 基础篇⑥ Ticker——ESP8266定时库

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  8. ESP8266开发之旅 网络篇⑦ TCP Server & TCP Client

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  9. ESP8266开发之旅 网络篇⑧ SmartConfig——一键配网

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

随机推荐

  1. C# 打印PPT幻灯片

    本文主要归纳总结了常见的几种PPT幻灯片文档打印的方法及需求.具体通过C#示例来分别阐述以下几种情况: 一.通过PresentationPrintDocument 对象来打印 使用默认打印机打印文档 ...

  2. Linux遇到的问题-记录

    Linux遇到的问题 2019-04-09以前: Linux&Win双系统下时间显示不正常的问题 一般安装了双系统(Linux+Windows)就很容易出现问题,Windows是直接取硬件时间 ...

  3. [Boost库] noncopyable——禁止拷贝的类

    1.noncopyable允许程序轻松地实现一个禁止拷贝的类,在头文件<boost/noncopyable.hpp>中   2.实现原理很简单:noncopyable的实现就是用了C++中 ...

  4. C#深入学习笔记 - 可空类型与构造函数默认参数

    在实际开发中或许可能会遇到某个属性需要提供一个默认参数,如果该参数是引用类型的话,可以通过 使用 null 来表示未知的值,但如果是int或 其他值类型的话就有点不好办了,因为如果需要一个int或fl ...

  5. 深度汉化GCompris-qt,免费的幼儿识字软件

    1 需求 因为有个小孩上幼儿园了,想开始教他一些汉语拼音和基本的汉字,但通过一书本和卡片又有些枯燥乏味,于上就上网搜索一些辅助认字的应用,还购买了悟空识字APP,在用的过程中发现他设置了很严格的关卡, ...

  6. Maven 梳理 -scope属性

    在POM 4中,<dependency>中还引入了<scope>,它主要管理依赖的部署.目前<scope>可以使用5个值: * compile,缺省值,适用于所有阶 ...

  7. asp.netcore 自动挡Docker Nginx Redis(滴滴滴,自动挡)

    前言 上一章介绍了Docker通过多条命令创建启动运行Docker容器,由此可见这样一个个去创建单独的容器也是相当麻烦的,比如要在某个复杂项目中用DB.缓存.消息等等,这样我们还要去一个个再创建,为此 ...

  8. TensorFlow2.0(四):填充与复制

    .caret, .dropup > .btn > .caret { border-top-color: #000 !important; } .label { border: 1px so ...

  9. Linux kail安装及查看命令

    Linux kail安装及查看命令  apt-get update                          //更新源 apt-get install package             ...

  10. IDEA 学习笔记之 Python项目开发

    Python项目开发: 下载Python: https://www.python.org/downloads/release/python-363/ 安装Python: 配置环境变量(path): C ...