什么是 Blinker?
Blinker 是一套跨硬件、跨平台的物联网解决方案,提供 APP 端、设备端、服务器端支持,使用公有云服务进行数据传输存储。适用于智能家居、数据监测等场景。
- GitHub: https://github.com/blinker-iot/
- 官网: https://diandeng.tech/
- APP: 点灯科技 APP(iOS / Android)
- 许可证: MIT License
硬件平台支持
Blinker 目前支持三大主流开发板:
| 平台 | WiFi | BLE | NB-IoT | GPRS |
|---|---|---|---|---|
| ESP32 | ✅ | ✅ 内置 | ✅ SIM7020/SIM7000 | ✅ Air202 |
| ESP8266 | ✅ | ✅ 外接透传模块 | ✅ | ✅ |
| Arduino R4 | ✅ AT固件 | ✅ | - | - |
ESP32 凭借内置蓝牙和丰富的外设接口,是目前最推荐的入门选择。
通信模式
WiFi 和蓝牙不能同时启用,通过宏定义切换:
1#define WIFIMODE // 启用WiFi模式
2//#define BLEMODE // 启用蓝牙模式
3
4#if defined(WIFIMODE)
5 #define BLINKER_WIFI
6#endif
7#if defined(BLEMODE)
8 #define BLINKER_BLE
9#endif
10
11#include <Blinker.h> // 必须在通信模式宏定义之后
关键规则:#include <Blinker.h> 必须放在通信模式宏定义之后,否则编译会出错。
WiFi 模式初始化:
1Blinker.begin(auth, ssid, pswd);
BLE 模式初始化(ESP32 内置蓝牙):
1Blinker.begin();
程序结构:九大标准模块
Blinker 标准程序按以下顺序组织:
- 程序版本备注 — 记录日期和功能
- 通信模式宏定义 — WIFIMODE / BLEMODE 切换
- 包含库 —
#include <Blinker.h> - 全局变量声明 — WiFi 信息、引脚、状态变量
- 组件声明 — 必须放在回调函数前面
- 回调函数 — APP 发来数据时由
Blinker.run()调用 - 功能函数 — WiFi 掉线检测等
- setup() 初始化 — 开机后只执行一次
- loop() 主循环 — 循环执行到关机
这个结构清晰地分离了配置、声明和逻辑,是编写 Blinker 程序的基本骨架。
组件:APP 端的交互元素
Blinker 提供丰富的内置组件:
| 组件 | 类名 | 用途 |
|---|---|---|
| 按钮 | BlinkerButton |
开关、模式切换 |
| 滑块 | BlinkerSlider |
阈值、亮度、速度 |
| 数值 | BlinkerNumber |
显示传感器数据 |
| 文本 | BlinkerText |
显示字符串 |
| RGB | BlinkerRGB |
颜色控制 |
| 摇杆 | BlinkerJoystick |
方向控制 |
| 开关 | BlinkerSwitch |
内置开关 |
声明示例:
1BlinkerButton Button1("b1"); // 键名 "b1",对应APP中组件名
2BlinkerSlider Slider1("s1"); // 键名 "s1"
3BlinkerNumber NUM1("abcd"); // 键名 "abcd"
4BlinkerText Text1("1234"); // 键名 "1234"
核心规则:组件声明必须放在回调函数前面,键名必须与 APP 中的组件名完全一致。
回调函数:设备与 APP 的数据桥梁
按钮回调
1void button1_callback(const String& state)
2{
3 if (state == "tap") { b1_state = !b1_state; } // 普通按键:反转
4 else if (state == "on") { b1_state = true; } // 开关:开启
5 else if (state == "off") { b1_state = false; } // 开关:关闭
6 else if (state == "press") { b1_state = true; } // 长按:开启
7 else if (state == "pressup") { b1_state = false; } // 松开:关闭
8 Button1.print(b1_state ? "on" : "off"); // 立即反馈到APP
9}
重要:不要简化按钮的五种处理,防止 APP 设置不同出现逻辑错误。
滑块回调
1void slider1_callback(int32_t value)
2{
3 s1_value = value; // 赋值全局变量即可
4}
注意:滑块不可在回调函数里调用 print,否则拖不动。
注册回调
1void setup()
2{
3 Button1.attach(button1_callback);
4 Slider1.attach(slider1_callback);
5 Blinker.attachHeartbeat(heartbeat);
6 Blinker.attachDataStorage(dataStorage);
7}
心跳包:APP 获取设备数据的核心机制
心跳包是 APP 获取设备数据的主要方式,间隔约 30 秒一次。
1void heartbeat()
2{
3 Button1.print(b1_state ? "on" : "off"); // 按钮:回调+心跳包双保险
4 Slider1.print(s1_value); // 滑块:仅心跳包
5 NUM1.print(100); // 数值:仅心跳包
6 Text1.print("Hello Blinker"); // 文本:仅心跳包
7 Blinker.notify("页面数据已刷新"); // APP内弹窗推送
8}
数据上报位置规则
| 数据类型 | 上报位置 | 原因 |
|---|---|---|
| 按钮状态 | 回调函数 + 心跳包 | 即时反馈 + 兜底 |
| 滑块值 | 仅心跳包 | 回调里 print 会拖不动 |
| 数值/文本 | 仅心跳包 | 统一管理 |
关键:print 必须放心跳包回调或按钮回调,不能放 loop(),否则触发 MQTT NOT ALIVE OR MESSAGE LIMIT 错误。
数据存储与图表
生成传感器数据折线图表:
1void dataStorage()
2{
3 Blinker.dataStorage("data1", random(0, 120));
4}
5
6void setup()
7{
8 Blinker.attachDataStorage(dataStorage);
9}
实时数据(RT_DATA)
实时数据每秒以上一次,适用于高频刷新场景。不支持文本组件,只支持数值组件。
1void rtData()
2{
3 Blinker.sendRtData("data1", (int32_t)random(0, 120));
4 Blinker.sendRtData("data2", random(0, 120) / (float)1.5);
5 Blinker.printRtData(); // 刷新到 APP
6}
7
8void setup()
9{
10 Blinker.attachRTData(rtData);
11}
WiFi 掉线检测
WiFi 模式下必须实现掉线重启检测:
1void wifiOfflineMonitor()
2{
3 if (Blinker.connect()) {
4 offline_millis = 0;
5 offline_flag = false;
6 }
7 else if (!Blinker.connect() && millis() > 180000) {
8 offline_flag = true;
9 }
10
11 if (millis() > 180000 && offline_millis && millis() - offline_millis >= 180000) {
12 ESP.restart(); // 掉线超三分钟重启
13 }
14}
在 loop() 中调用:
1void loop()
2{
3 Blinker.run();
4 wifiOfflineMonitor();
5}
调试与日志
1void setup()
2{
3 Serial.begin(115200);
4 BLINKER_DEBUG.stream(Serial); // 调试信息输出到串口
5 BLINKER_DEBUG.debugAll(); // 开启所有调试信息
6 BLINKER_LOG("版本信息"); // 打印日志
7}
常见错误排查
| 错误 | 原因 | 解决 |
|---|---|---|
MQTT NOT ALIVE OR MESSAGE LIMIT |
print 放在 loop() 中 | 移到心跳包回调或按钮回调 |
| 按钮状态不更新 | APP 按钮类型与代码不匹配 | 保留五种状态处理 |
| 滑块拖动不灵敏 | 回调里调用了 print | 滑块 print 只放心跳包 |
| WiFi 连不上 | 非 2.4GHz / 密码错误 | 检查频段和密码 |
| 授权码错误 | 引号/空格问题 | 从点灯 APP 复制 |
ESP32 Web 配网
ESP32_Blinker_NET 是一个完整的 Web 配网示例项目,支持通过网页配置 WiFi 和 Blinker 授权码。
工作流程:
- 开机读取 EEPROM 中的 WiFi 配置
- 有配置则尝试连接,无配置则进入配网模式
- 手机连接热点
ESP32_Config,访问192.168.4.1 - 在 Web 页面选择 WiFi、输入密码和授权码
- 保存后 ESP32 重启,自动连接
核心代码:
1WIFIConnect wifiManager("ESP32_Config");
2
3void setup() {
4 Serial.begin(115200);
5 wifiManager.begin();
6 if (wifiManager.isConnected()) {
7 Blinker.begin(
8 wifiManager.getAuth().c_str(),
9 wifiManager.getSSID().c_str(), ""
10 );
11 }
12}
13
14void loop() {
15 wifiManager.loop();
16 if (wifiManager.isConnected()) Blinker.run();
17}
完整模板代码
以下是一个包含按钮、滑块、数值、文本、数据存储、心跳包、APP弹窗七大功能的完整模板:
1#define NOTE "Blinker 完整模板"
2
3#define WIFIMODE
4
5#if defined(WIFIMODE)
6 #define BLINKER_WIFI
7#endif
8
9#include <Blinker.h>
10
11char ssid[] = "WiFi名称";
12char pswd[] = "WiFi密码";
13char auth[] = "授权码";
14
15#define RELAYPIN_1 2
16bool b1_state = false;
17int s1_value = 0;
18
19BlinkerButton Button1("b1");
20BlinkerSlider Slider1("s1");
21BlinkerNumber NUM1("abcd");
22BlinkerText Text1("1234");
23
24void button1_callback(const String& state)
25{
26 if (state == "tap") { b1_state = !b1_state; }
27 else if (state == "on") { b1_state = true; }
28 else if (state == "off") { b1_state = false; }
29 else if (state == "press") { b1_state = true; }
30 else if (state == "pressup") { b1_state = false; }
31 Button1.print(b1_state ? "on" : "off");
32 digitalWrite(RELAYPIN_1, !b1_state);
33}
34
35void slider1_callback(int32_t value) { s1_value = value; }
36
37void heartbeat()
38{
39 Button1.print(b1_state ? "on" : "off");
40 Slider1.print(s1_value);
41 NUM1.print(100);
42 Text1.print("Hello Blinker");
43 Blinker.notify("页面数据已刷新");
44}
45
46void dataStorage() { Blinker.dataStorage("data1", random(0, 120)); }
47
48void setup()
49{
50 Serial.begin(115200);
51 BLINKER_DEBUG.stream(Serial);
52 BLINKER_DEBUG.debugAll();
53
54 pinMode(RELAYPIN_1, OUTPUT);
55 Blinker.begin(auth, ssid, pswd);
56
57 Button1.attach(button1_callback);
58 Slider1.attach(slider1_callback);
59 Blinker.attachDataStorage(dataStorage);
60 Blinker.attachHeartbeat(heartbeat);
61}
62
63void loop() { Blinker.run(); }
总结
Blinker 以其简洁的 API、丰富的组件和跨平台支持,极大降低了物联网开发门槛。掌握以下核心要点即可快速上手:
- 宏定义在前,头文件在后 — 通信模式决定编译路径
- 组件声明在回调前 — 否则绑定失败
- print 不放 loop() — 否则触发 MQTT 限流
- 滑块回调不 print — 否则拖不动
- 按钮保留五种状态 — 防止 APP 模式不匹配
- WiFi 掉线要检测 — 三分钟无响应自动重启
遵循这些规范,你就能用 Blinker 快速搭建出稳定可靠的物联网应用。