Blinker项目实例:Nodemcu驱动ws2812
一、 实验目的和要求
(1) 计并开发一个网络应用软件系统 二、 实验内容和原理 实验内容: 本实验的目的是设计并开发一个物联网彩灯系统,用于智能化的居家氛围布置、照明。系统的操控方式为手机APP控制,通过MQTT远程实现。完成的功能有自由控制颜色、亮度,既可以所有的LED灯一起进行操作,也可以对单独LED等进行操作;一键彩灯,按下按钮,即可按照设定好的氛围光亮起彩灯,营造独特的氛围。本实验对氛围光只设定一个模式作为例子,具体应用时完全可以添加更多的模式。另外,彩灯不仅使用与氛围布置,还兼容学习场合,一键可以调整至护眼模式。
开发语言: C语言(Arduino平台)
二、 架构设计
1、硬件部分
硬件包括一块Nodemcu开发板、一个WS2812灯带、一台智能手机。
(1)nodemcu开发板。NodeMCU,是一个开源的物联网平台,使用esp8266作为核心 它使用Lua脚本语言编程,同时也可以使用Arduino进行编程。硬件配置主要有16个GPIO引脚,可实现1个模拟I/O和11个数字I/O。通讯接口有一个USB口、一个串口和一个Wifi通讯模块。本实验主要用到GPIO引脚来驱动WS2812,使用Wifi模块连接网络与服务器、手机进行通讯。
(2) WS2812。WS2812B是一个集控制电路与发光电路于一体的智能外控LED光源。其外型与一个5050LED灯珠相同,每个元件即为一个像素点。像素点内部包含了智能数字接口数据锁存信号整形放大驱动电路,还包含有高精度的内部振荡器和12V高压可编程定电流控制部分,有效保证了像素点光的颜色高度一致。数据协议采用单线归零码的通讯方式,像素点在上电复位以后,DIN端接受从控制器传输过来的数据,首先送过来的24bit数据被第一个像素点提取后,送到像素点内部的数据锁存器,剩余的数据经过内部整形处理电路整形放大后通过DO端口开始转发输出给下一个级联的像素点,每经过一个像素点的传输,信号减少24bit。像素点采用自动整形转发技术,使得该像素点的级联个数不受信号传送的限制,仅仅受限信号传输速度要求。LED具有低电压驱动,环保节能,亮度高,散射角度大,一致性好,超低功率,超长寿命等优点。将控制电路集成于LED上面,电路变得更加简单,体积小,安装更加简便。
2、 软件部分
blinker是一套跨硬件、跨平台的物联网解决方案,提供APP端、设备端、服务器端支持,使用公有云服务进行数据传输存储。可用于智能家居、数据监测等领域,可以帮助用户更好更快地搭建物联网项目。Blinker由服务器端、app端、设备端组成,可以部署到几乎所有物联网平台,使用WiFi、MQTT等方式接入,服务器端可以部署到阿里云、腾讯云、OneNET、百度云、AWS、google cloud等平台,通过界面布局器,DIY用户可自己拖拽布局设备控制界面,自由打造专属的物联网设备。
3、 系统连接
(1)Nodemcu和WS2812的连接方式如下:
以上红色箭头分别是D7、GND和VCC,其中Nodemcu的Vcc和ws2812的Vcc相连,Nodemcu的GND和ws2812的GND相连,Nodemcu的D7和ws2812的S(信号线)相连。
需要注意的是,ws2812所有的灯全亮电流超过500mA,无法通过电脑进行供电,需要外接电源。因此ws2812其实有两根VCC和两根GND。这两根线是短路的,具体连接的时候一根GND连接Nodemcu的GND,另一根GND连接5V直流电源的GND。但是VCC不能两根都连,一根VCC连接5V直流电源,另外一根VCC悬空。另外,信号线和Nodemcu的D7相连即可。(1)Nodemcu和手机APP的通信。本实验没有直接搭建服务器,而是采用了blinker解决方案的服务器。Nodemcu需要处于一个有wifi的局域网环境下,通过wifi接入Internet。通讯使用MQTT方式,即便用户离开了现场也能够进行操控。Nodemcu将秘钥发送到blinker服务器并建立连接,手机APP通过开关按键发送秘钥到服务器同样建立连接。服务器识别到Nodemcu和客户端同时发来了秘钥后,开始在两者之间传递命令协议。每个命令的发出都需要两步,一是客户端向Nodemcu发送指令,二是Nodemcu返回一个确认信息,客户端接收到之后确认操作完成。
三、主要仪器设备
1、一块Nodemcu开发板
2、一个WS2812灯条
3、Blinker手机app
四、设计要点
(1)下载正确的库文件。本实验对ws2812的操作与对单个LED不同,点亮单个基色LED编写好PWM程序就可以调节亮度,不同亮度的三基色LED叠在一起就能呈现不同的颜色。但是ws2812是成线性排列的,所有的LED公用同一个根信号线,因此不能对LED进行单独操作。此处需要用到ws2812的库函数对其进行操作。 在github中搜索blinker library找到资源并下载,将下载好的blinker库解压到 我的电脑>文档>Arduino>libraries 文件夹中
(2)获得IoT设备秘钥。秘钥相当于一个设备的ID,服务器要找到特定的IoT设备,必须依赖该秘钥。打开blinker app,点击右上角的‘+’号,选择Arduino,获得秘钥并复制。
(3)代码中的IoT秘钥、wifi名称、wifi秘钥必须正确输入,否则设备无法与服务器连接。
(4)组件名称要和代码对应。如LEDNum对应Number_1,LED_R对应Number_2.
2、自由控制(整体)模式:按下自由模式,按钮高亮,点击任一颜色,灯颜色改变,手机上显示对应RGB值。图中可以看出选中绿色RGB是[0,255,52]
3、七彩模式:按下七彩模式,按钮高亮,ws2812直接显示一个绚烂的彩色灯条。
4、逐个调节模式。按下逐个调节模式按钮,按钮高亮。这时可以按顺序依次调节每一个灯珠的RGB值,当从第一个到最后一个都调整过以后,又重新回到第一个,循环进行。实验中为了显示每个灯珠都可以自由调节,特意相邻的灯珠选择了反差明显的颜色。
5、护眼照明模式。按下护眼照明模式按钮,按钮高亮。一键调整至护眼模式,此时是柔和的黄光。
以上结果达到预期要求。另外需要说明的是,本实验对灯光效果只设定一个模式作为例子,具体应用时完全可以添加更多的模式。
五、实验注意
1、本实验主要的代码内容是硬件设备Nodemcu上的,编写硬件代码比较麻烦的是参数的选取(关系到硬件是否能正产工作)。实验时一开始时将ws2812的144个灯珠全部点亮,带来了严重的问题,电脑突然掉电。重启后Windows系统提示USB口电流过大,无法继续使用。幸好内部电路没有破坏性损坏,冷却下来之后仍然能继续工作,否则我的电脑就会损失一个USB口。
2、明确电脑无法直接给ws2812供电之后,又找了一个220V转5V的电源适配器来给ws2812供电(先接在面包板上再借ws2812)。这样一来144个LED灯珠都能正常工作。然而偶然触摸面包板温度非常高,迅速拔掉电源后发现面包板已经严重融化。可见144个灯珠同时亮起电流过大。
3、最终才确定只点亮20个LED灯珠,防止再次出现意外。
六、附实验代码(C语言)
六、附实验代码(C语言)#define BLINKER_WIFI#include
<Blinker.h>#include<math.h>
char auth[] = "692310ffgrt";//IoT秘钥
char ssid[] = "ONEPLUS";//wifi名称
char pswd[] = "55555555"; //wifi秘钥
bool tab[5] = { false };
bool modestate[4] = { false }; //工作模式选项卡
bool AutoBit= false; //判断手动模式是否有效
bool SingleAdjust= false;//判断逐个调节是否有效
int LEDserial=0;//
int Offset=20; //灯珠整体移动若干单位
#include #ifdef __AVR__ #include #endif #if defined (__AVR_ATtiny85__) if (F_CPU == 16000000) clock_prescale_set(clock_div_1);#endif #define PIN 13#define NUMPIXELS 20#define RGB_1 "RGBKey"#define Tab_1 "tab-mode"#define Number_1 "LEDNum"#define Number_2 "LED_R"#define Number_3 "LED_G"#define Number_4 "LED_B" BlinkerRGB WS2812(RGB_1);BlinkerTab Tab1(Tab_1);//定义数据结构
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
BlinkerNumber Number1 (Number_1);
BlinkerNumber Number2 (Number_2);
BlinkerNumber Number3 (Number_3);
BlinkerNumber Number4 (Number_4); //*************************************************************//选项卡触发函数
void tab1_callback(uint8_t tab_set){ //BLINKER_LOG("get tab set: ", tab_set);
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
switch (tab_set) {
case BLINKER_CMD_TAB_0 :
tab[0] = true; //BLINKER_LOG("tab 0 set");
AutoBit=!AutoBit;//手动位取反
break;
case BLINKER_CMD_TAB_1 :
tab[1] = true; //BLINKER_LOG("tab 1 set");
Mode1();//开启Mode1,灯光模式1
break;
case BLINKER_CMD_TAB_2 :
tab[2] = true; //BLINKER_LOG("tab 2 set");
SingleAdjust=!SingleAdjust;//开启Mode2,灯光模式2 break;
case BLINKER_CMD_TAB_3 :
tab[3] = true; //BLINKER_LOG("tab 3 set");
Mode3();//开启Mode3,流水灯模式
break;
case BLINKER_CMD_TAB_4 :
tab[4] = true; //BLINKER_LOG("tab 4 set");
break;
default:
break; }}
void tab1_feedback(){
for(uint8_t num = 0; num < 5; num++) {
if (tab[num]) {
Tab1.tab(num);
tab[num] = false; } }
Tab1.print();} //*******************************************************************//自由模式整体调节/逐个调节服务函数
void ws2812_callback(uint8_t r_value, uint8_t g_value, uint8_t b_value, uint8_t bright_value){
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
if (AutoBit==true){ digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
Mode0(r_value,g_value,b_value,bright_value); return NULL; }
else if(SingleAdjust==true){ digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
Mode2(r_value,g_value,b_value,bright_value); }}
//********************************************************************//自由控制(整体调节)Mode0
void Mode0(uint8_t r_value, uint8_t g_value, uint8_t b_value, uint8_t bright_value){
pixels.setBrightness(bright_value);
for(int i = 0; i < NUMPIXELS; i++){
pixels.setPixelColor(i, r_value, g_value, b_value); }
pixels.show();
Number2.print(r_value);
Number3.print(g_value);
Number4.print(b_value);}
//********************************************************************//七色模式Mode1
void Mode1(){
int r=0; int g=0; int b=0; int i=0; int full=NUMPIXELS;
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
for(int i=0;i<NUMPIXELS;i++){ if(i<full/3){ r=255; g=ceil(255*3*i/full); b=0; }else if(i<full/2){ r=ceil(750-i*(250*6/full)); g=255; b=0; }else if(i<full*2/3){ r=0; g=255; b=ceil(i*(250*6/full)-750); }else if(i<full*5/6){ r=0; g=ceil(1250-i*(250*6/full)); b=255; }else{ r=ceil(150*i*(6/full)-750); g=0; b=255; } pixels.setPixelColor(i, pixels.Color(r,g,b)); } pixels.show(); }//********************************************************************//逐个调节Mode2
void Mode2(uint8_t r_value, uint8_t g_value, uint8_t b_value, uint8_t bright_value){ pixels.setBrightness(bright_value); pixels.setPixelColor(LEDserial, r_value, g_value, b_value); pixels.show(); LEDserial=LEDserial+1; LEDserial= LEDserial % NUMPIXELS; Number2.print(r_value); Number3.print(g_value); Number4.print(b_value); }//*******************************************************************//护眼照明模式 黄光Mode3
void Mode3(){ int bright_value=200; int r_value=255; int g_value=215; int b_value=0; pixels.setBrightness(bright_value); for(int i = 0; i < NUMPIXELS; i++){ pixels.setPixelColor(i, r_value, g_value, b_value); } pixels.show(); Number2.print(r_value); Number3.print(g_value); Number4.print(b_value);}//*******************************************************************//显示数值
void dataRead(const String & data){ uint32_t BlinkerTime = millis(); Number1.print(BlinkerTime/1000);} //*********************************************************************
void setup(){ Serial.begin(115200); BLINKER_DEBUG.stream(Serial); Blinker.attachData(dataRead); pinMode(LED_BUILTIN, OUTPUT);//引脚设置为输出模式
digitalWrite(LED_BUILTIN, LOW); Blinker.begin(auth, ssid, pswd); pixels.begin();//开启pixel对ws2812输出
WS2812.attach(ws2812_callback);
Tab1.attach(tab1_callback,
tab1_feedback);
}
void loop(){ Blinker.run();}