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 四路继电器模块

微信图片_20250701121729

usb烧录器

建议使用这种插入式烧录器接线麻烦了

微信图片_20250701121729

📱 第一步:配置 Blinker App#

  1. 打开 Blinker App,注册并登录;
  2. 添加设备,选择点灯-独立设备:
    • 接入方式:网络接入;
  3. 创建完后,进入设备-点击设置复制 设备密钥(authKey),稍后在代码中使用;
  4. 拖入四个开关按钮 Widget,分别命名为 b1 ~ b4;
  5. 点击“保存”。

🔧 第二步:准备固件代码#

使用 Arduino IDE 编写以下程序并烧录至 ESP-01 模块:

✅ 下载串口驱动#

C341驱动

✅ 安装库#

  • 下载 Blinker 库,选择Arduino (C++)下载
  • 打开 Arduino IDE文件-首选项-设置开发板管理地址,任意一个都可以
https://arduino.me/packages/esp8266.json
https://arduino.esp8266.com/stable/package_esp8266com_index.json
  • 导入Blinker
  • 板子选择:Generic ESP8266 Module
  • img

✅ 四路继电器代码#

//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名称"; // 替换为你的WiFi
char 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; // 实体按钮切换限制的时间戳,初始化为0
void 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
}
#endif
void 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名称"; // 替换为你的WiFi
char 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 引脚串口烧录器
RXTXD
3V3VCC
IO0GDN
TXRXD
GNDGND

✅ 若使用 ESP-12F 模块,可以稳定使用 4 个 GPIO 引脚控制 4 路继电器。


🚀 第四步:烧录和测试#

  1. 使用 USB 转串口模块将 ESP-01 与电脑连接;
  2. 选择正确端口和开发板,上传代码;
  3. 插入继电器模块供电;
  4. 打开 Blinker App,控制按钮即可实现开关控制;
  5. 可选扩展:绑定小爱同学、添加定时器等。

📌 项目拓展建议#

  • ✅ 支持语音控制(点灯已支持小爱同学、天猫精灵)
  • ✅ 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://adunm.top/posts/blinker/
作者
Ylan
发布于
2025-07-01
许可协议
CC BY-NC-SA 4.0