ドラえもん
3852 字
19 分钟
点灯科技blinker控制四路继电器
📦 项目简介
本项目实现了通过 点灯科技 Blinker App,远程控制一个淘宝购买的 四路继电器模块(LC-Relay-ESP01-4R-12V)。它基于 ESP8266-01 模块,适合智能家居、遥控开关等场景。
✅ 实现功能:手机远程控制 1-4 路继电器的开关
🔀 准备材料
名称 | 说明 |
---|---|
LC-Relay-ESP01-4R-12V | 四路继电器模块,支持 ESP-01s,ESP-01 |
ESP8266-01 模块 | 带 WiFi 功能,插入继电器模块上 |
12V 直流电源 | 继电器供电,注意极性 |
安卓/iOS 手机 | 安装 Blinker App |
杜邦线/USB转串口模块,C341 | 如需刷写固件 |
LC-Relay-ESP01-4R-12V 四路继电器模块
usb烧录器
建议使用这种插入式烧录器接线麻烦了
📱 第一步:配置 Blinker App
- 打开 Blinker App,注册并登录;
- 添加设备,选择点灯-独立设备:
- 接入方式:网络接入;
- 创建完后,进入设备-点击设置复制 设备密钥(authKey),稍后在代码中使用;
- 拖入四个开关按钮 Widget,分别命名为 b1 ~ b4;
- 点击“保存”。
🔧 第二步:准备固件代码
使用 Arduino IDE 编写以下程序并烧录至 ESP-01 模块:
✅ 下载串口驱动
✅ 安装库
- 下载 Blinker 库,选择
Arduino (C++)
下载 - 打开
Arduino IDE
文件-首选项-设置开发板管理地址,任意一个都可以
https://arduino.me/packages/esp8266.jsonhttps://arduino.esp8266.com/stable/package_esp8266com_index.json
- 导入
Blinker
库 - 板子选择:
Generic ESP8266 Module
✅ 四路继电器代码
//1.默认!在#include <Blinker.h>前定义点灯库通信方式为WiFi#define BLINKER_WIFI //定义点灯库通信模式,必须加在#include <Blinker.h>前! 以保证编译器对代码进行预处理时能够包含正确的库代码组合#define BLINKER_MIOT_MULTI_OUTLET // 新增:支持米家多插座
//2.必须填写!参考说明书第五节
//没有硬件请注释掉该行#define RELAYPIN_1 0 //定义继电器1在8266芯片的引脚GPIO0,对应NodeMCU开发板D3#define RELAYPIN_2 2 //定义继电器2在8266芯片的引脚GPIO2,对应NodeMCU开发板D4#define RELAYPIN_3 4 //定义继电器3在8266芯片的引脚GPIO4,对应NodeMCU开发板D2#define RELAYPIN_4 5 //定义继电器4在8266芯片的引脚GPIO5,对应NodeMCU开发板D1//#define SELF_LOCK //焊接自复位按钮注释掉本句,焊接自锁开关取消注释// #define BUTTONPIN 3 //实体开关,定义后检测IO3(RX),低电平则调用按钮1回调翻转一次灯状态/**************************************** 预处理命令——软件库包含****************************************/#include <Blinker.h> //包含点灯库,编译器根据“宏定义的通信方式、工具中选择的开发板”来选择合适的端口定义、顶层调度函数、底层适配函数
/**************************************** 声明——全局变量****************************************///必须修改!参考说明书第四节char auth[] = "你的Blinker授权码"; // 替换为你的授权码char ssid[] = "你的WiFi名称"; // 替换为你的WiFichar pswd[] = "你的WiFi密码"; // 替换为你的密码
bool button1_state = false; // 按钮1状态bool button2_state = false; // 按钮2状态bool button3_state = false; // 按钮3状态bool button4_state = false; // 按钮4状态
bool oState[4] = { false, false, false, false }; // 新增:多路插座状态数组,4路
uint32_t hebt_time = 0; //心跳包持续强制发送变量uint32_t hebt_time_limit = 0; //心跳包发送周期限制,时间戳变量
/**************************************** 定义——功能函数
7.(1)按钮1回调函数(基于Arduino>文件>示例>鼠标滚轮往下滚>第三方库示例>Blinker>Blinker_Widgets>Blinker_Button>Botton_Wifi) 7.(2)按钮2回调函数 8.用户心跳包回调函数(基于Blinker库自带的心跳包函数,官方教程https://arduino.me/s/blinker-arduino?aid=711官方说明见https://diandeng.tech/doc/arduino-support#%E5%BF%83%E8%B7%B3%E5%8C%85heartbeat的心跳包小节) 11.主函数初始化部分setup(执行一次) 12.主函数循环执行部分loop(循环到关机)****************************************/
#if defined(RELAYPIN_1)
void button1_callback(const String& state); //按钮1回调函数声明//下面这句是类库实例化语句,类名BlinkerButton(按钮组件类),实例名Button1,数据键名“b1”,当blinker.run检测到APP发来数据包含键名“b1”就调用回调button1_callback。BlinkerButton Button1("b1", button1_callback); //用此句不需要在setup里attach回调了void button1_callback(const String& state) //按钮1回调函数定义,按下后设备开启继电器1{ if (state == "tap") { button1_state = !button1_state; //普通按键:tap反转状态 } else if (state == "on") { button1_state = true; //开关按键:on 短按开启 } else if (state == "off") { button1_state = false; //开关按键:off短按关闭 } else if (state == "press") { button1_state = true; //普通按键或开关按键:press长按开启 } else if (state == "pressup") { button1_state = false; //普通按键或开关按键:pressup松开关闭 }
Button1.print(button1_state ? "on" : "off"); //立即反馈状态到APP
digitalWrite(RELAYPIN_1, !button1_state); //IO输出到继电器 Serial.write(0xA0); //串口输出到继电器 Serial.write(0x01); Serial.write(button1_state?0x01:0x00); Serial.write(button1_state?0xA2:0xA1);
// 新增:同步oState[0]状态 oState[0] = button1_state;}#endif#if defined(RELAYPIN_2)BlinkerButton Button2("b2", button2_callback); //类库实例化语句,类名BlinkerButton按钮组件,实例名Button2,组件键名“b2”void button2_callback(const String& state) //7.(2)按钮2回调函数,按下后设备开启继电器2{ if (state == "tap") { button2_state = !button2_state; //普通按键:tap反转状态 } else if (state == "on") { button2_state = true; //开关按键:on 短按开启 } else if (state == "off") { button2_state = false; //开关按键:off短按关闭 } else if (state == "press") { button2_state = true; //普通按键或开关按键:press长按开启 } else if (state == "pressup") { button2_state = false; //普通按键或开关按键:pressup松开关闭 } Button2.print(button2_state ? "on" : "off"); //反馈状态到APP digitalWrite(RELAYPIN_2, !button2_state);//IO输出到继电器 Serial.write(0xA0); //串口输出到继电器 Serial.write(0x02); Serial.write(button2_state?0x01:0x00); Serial.write(button2_state?0xA3:0xA2);
// 新增:同步oState[1]状态 oState[1] = button2_state;}#endif#if defined(RELAYPIN_3)BlinkerButton Button3("b3", button3_callback); //类库实例化语句,类名BlinkerButton按钮组件,实例名Button3,组件键名“b3”void button3_callback(const String& state) //7.(3)按钮3回调函数,按下后设备开启继电器3{ if (state == "tap") { button3_state = !button3_state; //普通按键:tap反转状态 } else if (state == "on") { button3_state = true; //开关按键:on 短按开启 } else if (state == "off") { button3_state = false; //开关按键:off短按关闭 } else if (state == "press") { button3_state = true; //普通按键或开关按键:press长按开启 } else if (state == "pressup") { button3_state = false; //普通按键或开关按键:pressup松开关闭 } Button3.print(button3_state ? "on" : "off"); //反馈状态到APP digitalWrite(RELAYPIN_3, !button3_state);//IO输出到继电器 Serial.write(0xA0); //串口输出到继电器 Serial.write(0x03); Serial.write(button3_state?0x01:0x00); Serial.write(button3_state?0xA4:0xA3);
// 新增:同步oState[2]状态 oState[2] = button3_state;}#endif#if defined(RELAYPIN_4)BlinkerButton Button4("b4", button4_callback); //类库实例化语句,类名BlinkerButton按钮组件,实例名Button4,组件键名“b4”void button4_callback(const String& state) //7.(4)按钮4回调函数,按下后设备开启继电器4{ if (state == "tap") { button4_state = !button4_state; //普通按键:tap反转状态 } else if (state == "on") { button4_state = true; //开关按键:on 短按开启 } else if (state == "off") { button4_state = false; //开关按键:off短按关闭 } else if (state == "press") { button4_state = true; //普通按键或开关按键:press长按开启 } else if (state == "pressup") { button4_state = false; //普通按键或开关按键:pressup松开关闭 } Button4.print(button4_state ? "on" : "off"); //反馈状态到APP digitalWrite(RELAYPIN_4, !button4_state);//IO输出到继电器 Serial.write(0xA0); //串口输出到继电器 Serial.write(0x04); Serial.write(button4_state?0x01:0x00); Serial.write(button4_state?0xA5:0xA4);
// 新增:同步oState[3]状态 oState[3] = button4_state;}#endif
// 新增:米家多插座回调函数void miotPowerState(const String & state, uint8_t num){ if (num >= 4) return; // 只支持4路 BLINKER_LOG("need set outlet: ", num, ", power state: ", state);
if (state == BLINKER_CMD_ON) { oState[num] = true; } else if (state == BLINKER_CMD_OFF) { oState[num] = false; }
// 控制对应继电器 switch(num) { case 0: button1_state = oState[0]; digitalWrite(RELAYPIN_1, !oState[0]); Button1.print(oState[0] ? "on" : "off"); break; case 1: button2_state = oState[1]; digitalWrite(RELAYPIN_2, !oState[1]); Button2.print(oState[1] ? "on" : "off"); break; case 2: button3_state = oState[2]; digitalWrite(RELAYPIN_3, !oState[2]); Button3.print(oState[2] ? "on" : "off"); break; case 3: button4_state = oState[3]; digitalWrite(RELAYPIN_4, !oState[3]); Button4.print(oState[3] ? "on" : "off"); break; }
BlinkerMIOT.powerState(oState[num] ? "on" : "off", num); BlinkerMIOT.print();}
void miotQuery(int32_t queryCode, uint8_t num){ if (num >= 4) return; BLINKER_LOG("MIOT Query outlet: ", num,", codes: ", queryCode);
switch (queryCode) { case BLINKER_CMD_QUERY_ALL_NUMBER : case BLINKER_CMD_QUERY_POWERSTATE_NUMBER : default : BlinkerMIOT.powerState(oState[num] ? "on" : "off", num); BlinkerMIOT.print(); break; }}
void heartbeat() //8.用户心跳包回调函数(APP定时向设备发送心跳包请求{"get":"state"},设备默认返回{"state":"online"},若用户有自定义状态需要在收到心跳包时返回, 可声明并在setup()中注册此回调函数){#if defined(RELAYPIN_1) //当定义继电器1引脚,编译以下语句 button1_callback(""); //按钮1状态返回#endif#if defined(RELAYPIN_2) //当定义继电器2引脚,编译以下语句 button2_callback(""); //按钮2状态返回#endif#if defined(RELAYPIN_3) //当定义继电器3引脚,编译以下语句 button3_callback(""); //按钮3状态返回#endif#if defined(RELAYPIN_4) //当定义继电器4引脚,编译以下语句 button4_callback(""); //按钮4状态返回#endif}//11.实体按钮处理函数#if defined(BUTTONPIN) //若定义实体按钮则编译以下语句#define KEY_NO 0 //无按键状态,用于判断是否按下#define KEY_DOWN 1 //有按键按下状态,判断是否为抖动#define KEY_UP 2 //等待松手状态,判断是否弹起int key_state = 0;uint32_t tap_time = 0; // 实体按钮切换限制的时间戳,初始化为0void BUTTON_Read(void){#if defined(SELF_LOCK)//实体开关状态机法(自锁开关) switch (key_state) { case KEY_NO: //无按键状态,用于判断是否按下 if (!digitalRead(BUTTONPIN)) key_state = KEY_DOWN; break;
case KEY_DOWN: //有按键按下状态,判断是否为抖动 if (!digitalRead(BUTTONPIN) && millis() - tap_time > 10) //非抖动则按下 { button1_callback(button1_state ? "off" : "on"); tap_time = millis(); key_state = KEY_UP; } else key_state = KEY_NO; //抖动判定为没按下 break;
case KEY_UP: //等待松手状态,判断是否弹起 if (digitalRead(BUTTONPIN)) { button1_callback(button1_state ? "off" : "on"); key_state = KEY_NO; } break; }#else//实体按钮状态机法(自复位开关) switch (key_state) { case KEY_NO: //无按键状态,用于判断是否按下 if (!digitalRead(BUTTONPIN)) key_state = KEY_DOWN; break; case KEY_DOWN: //有按键按下状态,判断是否为抖动 if (!digitalRead(BUTTONPIN) && millis() - tap_time > 10) //非抖动则按下 { button1_callback("tap"); tap_time = millis(); key_state = KEY_UP; } else key_state = KEY_NO; //抖动判定为没按下 break; case KEY_UP: //等待松手状态,判断是否弹起 if (digitalRead(BUTTONPIN)) key_state = KEY_NO; break; } //非状态机法 //if(!digitalRead(BUTTONPIN) &&millis()-tap_time>500){button1_callback("tap");tap_time=millis();}//每500ms检测一次按钮#endif}#endifvoid setup() //13.主函数初始化部分(开机引导后第一个执行此函数){#if defined(BUTTONPIN)//当定义实体开关,编译以下语句 pinMode(BUTTONPIN, INPUT_PULLUP); //实体按钮引脚设置为上拉输入 if (!digitalRead(BUTTONPIN)) key_state = KEY_UP; else key_state = KEY_DOWN; //初始低则等高,初始高则等低#endif
Serial.begin(115200); //初始化硬件串口UART0波特率115200bps(调试时需要相应的调整Aruino>工具>串口监视器>波特率为115200) BLINKER_DEBUG.stream(Serial); //将Blinker库代码调试信息流打印到硬件串口 BLINKER_DEBUG.debugAll(); //开启所有调试信息
Blinker.begin(auth, ssid, pswd); //调用Blinker库的开始成员函数,初始化Wifi连接(参数:授权码、WiFi名、密码) Blinker.attachHeartbeat(heartbeat); //注册用户心跳包回调函数,Blinker库代码每30秒发送心跳包时会顺带执行此函数(此函数不用返回任何值)
// 新增:注册米家多插座回调 BlinkerMIOT.attachPowerState(miotPowerState); BlinkerMIOT.attachQuery(miotQuery);} uint32_t offline_millis = 0;//掉线计时 bool offline_flag = false; //掉线状态void loop() //14.主函数循环执行部分(初始化后循环执行此函数直至断电){ Blinker.run(); //运行一下Blinker库代码(库代码确认没有消息收发需求时将自动暂停,执行后方用户代码)(刚开机会先连HTTP服务器鉴权然后用鉴权信息登录MQTT服务器后才能与APP通信) if (Blinker.connect()) //调用一下Blinker库的连接检测函数,若检测到MQTT服务器连接成功则执行以下用户代码 { offline_millis = 0; offline_flag = false; //在线,清空掉线状态 //用户代码放这里:*****************************************************************************
} else if (!Blinker.connected() && millis() > 180000) //开机三分钟后,点灯库一旦连不上,就判掉线,触发一次 { offline_flag = true; //Blinker.connected()触发标志 } if (millis() > 180000 && !offline_millis && offline_flag) //开机后三分钟后,若触发,则记录第一次掉线时间,之后!offline_millis为假,不再触发 { offline_flag = false; //Blinker.connected()触发取消 offline_millis = millis(); //记录掉线时间戳后!offline_millis为假,不再触发 } if (millis() > 180000 && offline_millis && millis() - offline_millis >= 180000) //开机三分钟后,掉线时间戳不为0,出现三分钟以上断连就重启 { BLINKER_LOG_ALL(BLINKER_F("******************************************Reatart***************************************")); //串口打印重启消息
#if defined(ESP32) ESP.restart(); //ESP32 重启#elif defined(ESP8266) ESP.reset(); //ESP8266 硬件重启#endif }#if defined(BUTTONPIN) //若定义实体按钮则编译以下语句 if (millis() > 3000)BUTTON_Read();#endif
}
✅ 一路继电器代码
#define BLINKER_WIFI#define BLINKER_MIOT_OUTLET // 支持小爱同学控制开关设备
#include <Blinker.h>
char auth[] = "你的Blinker授权码"; // 替换为你的授权码char ssid[] = "你的WiFi名称"; // 替换为你的WiFichar pswd[] = "你的WiFi密码"; // 替换为你的密码
#define RELAY_PIN 0 // ESP-01 可用 GPIO0
bool relayState = false; // 当前继电器状态BlinkerButton Button1("btn-relay"); // Blinker App 中的按钮组件
// 小爱同学语音控制回调void miotPowerState(const String &state) { BLINKER_LOG("MIOT power state: ", state);
if (state == BLINKER_CMD_ON) { digitalWrite(RELAY_PIN, LOW); // 低电平触发:打开 relayState = true; BlinkerMIOT.powerState("on"); } else if (state == BLINKER_CMD_OFF) { digitalWrite(RELAY_PIN, HIGH); // 高电平:关闭 relayState = false; BlinkerMIOT.powerState("off"); }
BlinkerMIOT.print(); // 反馈状态给小爱}
// App 按钮控制回调void button1_callback(const String &state) { BLINKER_LOG("App button state: ", state);
if (state == BLINKER_CMD_ON) { digitalWrite(RELAY_PIN, LOW); relayState = true; } else if (state == BLINKER_CMD_OFF) { digitalWrite(RELAY_PIN, HIGH); relayState = false; }
Button1.print(relayState ? "on" : "off");}
void setup() { Serial.begin(115200); pinMode(RELAY_PIN, OUTPUT); digitalWrite(RELAY_PIN, HIGH); // 初始为关闭(高电平)
Blinker.begin(auth, ssid, pswd); Button1.attach(button1_callback); BlinkerMIOT.attachPowerState(miotPowerState);}
void loop() { Blinker.run();}
⚠️ 注意:ESP-01 只有 GPIO0 和 GPIO2 稳定可用,GPIO1 是串口 TX,GPIO3 是 RX,使用时需谨慎或更换 ESP-12F。
🔌 第三步:模块连接图(TTL下载器)
ESP01 引脚 | 串口烧录器 |
---|---|
RX | TXD |
3V3 | VCC |
IO0 | GDN |
TX | RXD |
GND | GND |
✅ 若使用 ESP-12F 模块,可以稳定使用 4 个 GPIO 引脚控制 4 路继电器。
🚀 第四步:烧录和测试
- 使用 USB 转串口模块将 ESP-01 与电脑连接;
- 选择正确端口和开发板,上传代码;
- 插入继电器模块供电;
- 打开 Blinker App,控制按钮即可实现开关控制;
- 可选扩展:绑定小爱同学、添加定时器等。
📌 项目拓展建议
- ✅ 支持语音控制(点灯已支持小爱同学、天猫精灵)
- ✅ MQTT/本地控制(无需云)
- ✅ 与其他智能家居平台集成(如 Home Assistant)
- ✅ 控制电灯、电扇、水泵等 220V 设备(注意隔离与安全)
📝 常见问题
Q:ESP01 只有两个 GPIO 怎么控制四路?
A:使用 IO 扩展、或更换为 ESP-12F 模块。ESP01 控制 GPIO1/TX 和 GPIO3/RX 会影响串口。
Q:继电器没反应?
A:确认供电为 12V、ESP01 正确工作、按键绑定与引脚设置无误。
Q:Blinker 无法连接?
A:检查 WiFi 名称/密码、设备密钥、是否被防火墙拦截。
✅ 总结
本项目利用淘宝购买的四路继电器模块 + ESP8266,实现了用点灯 App 控制继电器开关,成本低,效果好,适合入门 IoT 和智能家居应用。
如需完整工程文件(.ino 源码)、原理图或 3D 打印外壳设计,可留言获取~
参考资料
-
- Blinker 官方文档:https://diandeng.tech/doc
-
- Blinker GitHub 示例:https://github.com/blinker-iot/blinker-library
-
- 小爱同学[Blinker]2024最新-小爱联动4路继电器 简易开源教程:https://arduino.me/a/2852