ESP8266开发之旅 应用篇① 局域网应用 ——炫酷RGB彩灯
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;
}
}
}
整个代码的流程是这样的:
- 8266上电启动后,进入自动连接模式autoConfig(根据上一次成功连接的SSID和密码),最多尝试20s,在尝试连接的过程中,LED灯会每隔1s闪烁一下,表示正在连接状态;如果连接成功,就直接配置8266服务器模式。
- 如果上面操作失败(可能连接的热点不存在了或者修改了密码),那么会自动进入一键配置模式SmartConfig等待手机一键配置,这个过程是不限制时间的,LED会每隔0.5s闪一下,表示处在SmartConfig状态,这时大家可以去手机端开始一键配置。
这里代码功能其实就是把8266当做服务端,8266连接上路由wifi,然后监听连接进来的客户端(这里是手机)
读者安装好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彩灯的更多相关文章
- ESP8266开发之旅 网络篇⑯ 无线更新——OTA固件更新
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
- ESP8266开发之旅 进阶篇② 闲聊Arduino IDE For ESP8266烧录配置
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
- ESP8266开发之旅 基础篇① 走进ESP8266的世界
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
- ESP8266开发之旅 基础篇② 如何安装ESP8266的Arduino开发环境
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
- ESP8266开发之旅 基础篇③ ESP8266与Arduino的开发说明
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
- ESP8266开发之旅 基础篇④ ESP8266与EEPROM
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
- ESP8266开发之旅 基础篇⑥ Ticker——ESP8266定时库
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
- ESP8266开发之旅 网络篇⑦ TCP Server & TCP Client
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
- ESP8266开发之旅 网络篇⑧ SmartConfig——一键配网
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
随机推荐
- .netCore+Vue 搭建的简捷开发框架 (3)-- Services层实现
继续交作业: 上一篇作业中我们实现了 Repository仓储层的应用.并为我们的框架引入了EFCore 详见: .netCore+Vue 搭建的简捷开发框架 (2)--仓储层实现和EFCore 的使 ...
- 网站启动,报编译错误:类型“ASP.global_asax”同时存在两个文件夹的问题
CS0433: The type 'ASP.global_asax' exists in both 'c:\Windows\Microsoft.NET\Framework64\v4.0.30319\T ...
- 了解Java线程优先级,更要知道对应操作系统的优先级,不然会踩坑
Java 多线程系列第 6 篇. 这篇我们来看看 Java 线程的优先级. Java 线程优先级 Thread 类中,使用如下属性来代表优先级. private int priority; 我们可以通 ...
- CF #579 (Div. 3) D1.Remove the Substring (easy version)
D1.Remove the Substring (easy version) time limit per test2 seconds memory limit per test256 megabyt ...
- Disruptor—核心概念及体验
本文基于最新的3.4.2的版本文档进行翻译,翻译自: https://github.com/LMAX-Exchange/disruptor/wiki/Introduction https://gith ...
- JS多线程WebWorker
JS多线程WebWorker 一,介绍与需求 1.1,介绍 Web Worker可以为JavaScript创建多线程,且Web Worker 是运行在后台的 JavaScript,独立于其他脚本,不会 ...
- 虚拟现实中的Motion Sickness晕动症问题 - VIMS
虚拟现实(VR)中的晕动症 - VIMS 在玩VR的时候,很多玩家都遇到过发晕恶心等症状,这就是晕动症(Motion Sickness,以下或简称MS).MS并不是VR特有的问题.我们在坐船.坐车.坐 ...
- php一行代码获取本周一,本周日,上周一,上周日,本月一日,本月最后一日,上月一日,上月最后一日日期
<?php //本周一 echo date('Y-m-d', (time() - ((date('w') == 0 ? 7 : date('w')) - 1) * 24 * 3600)); // ...
- $(document).height 与$(window).height的区别
$(document).scrollTop() 获取垂直滚动的距离 (即当前滚动的地方的窗口顶端到整个页面顶端的距离)$(document).scrollLeft() 这是获取水平滚动条的距离 要获取 ...
- 教你制作挂件头像 | 小程序七十二变之 canvas 绘制国旗头像
昨天朋友圈被「请给我一面国旗@微信官方」刷屏,虽然知道是假的,但是从另一个角度来看,弄清楚如何实现更有趣. 1.canvas 这就不得不提到小程序中的 API canvas,H5 中也是有 canva ...