兼容主流智能音箱 智能双开DIY[奔IV程序猿]-Arduino中文社区 - Powered by Discuz!

Arduino中文社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 4721|回复: 13

[分享] 兼容主流智能音箱 智能双开DIY[奔IV程序猿]

[复制链接]
发表于 2020-4-13 15:30 | 显示全部楼层 |阅读模式
本帖最后由 shark526 于 2020-4-13 21:56 编辑


文章G点:

1. 物料成本不超20块,这可能是目前能找到的最便宜的DIY方案了吧。

2. 体积较小,可以塞入86开关面板内。

3. app+音箱+物理开关控制,有状态反馈。


前言叨逼叨

这两三年,随着智能家居的普及率越来越高,与之相关的产品价格也越来越低,百元内即可拥有一台智能音箱,听歌听节目控制家电,功能一应俱全,各商家无非是想以此为敲门砖,让各位消费者继而为他们家的产品全家桶继续买单。买买买当然无可厚非,但作为一个DIYer,总想自己折腾一番,即使最终加上时间成本可能比买个现成产品还要高,但这个乐子是钱买不到的。懂得人自然懂,无需太多解释。

当前这个方案组合,我看到网上也有不少人做了相同的事,我这篇就着力在他们没讲到的点来写写,力图让各位有心人能看到一些未曾考虑过的方面,发掘各自的乐趣。

废话就这么多,进入正题。


材料清单

以下物料是很多年前买的,现在价格应该只有少没得多了吧。想当初想让DIY模块接入主流智能音箱是多么困难,ESP8266接入home assistant,home assistant再暴露接口给第三方平台,第三方平台再暗渡陈仓般的把你的设备交给智能音箱平台。然鹅,效果并不好。

现在有更好的第三方帮我们超度DIY模块,后面步骤里再说。先看清单:


ESP8266-01s * 1

Screenshot_2020-04-10-11-15-44-847_手机淘宝.png

ESP8266-01 relay 继电器模块 *1

WechatIMG8.png

5V稳压模块

WechatIMG5.png

TTL烧写器如果有就不用买了,不限于下图这种对应插座的,普通的也可以,针脚插对了就行。

WechatIMG6.png


步骤

1. Blinker

链接我就不放了, 搜索下Blinker, 下载其app, 按步骤注册个账号

屏幕快照 2020-04-10 9.27.27 PM.png

添加设备, 给个你喜欢的名称,我这里叫"厨房灯".

WechatIMG3.jpeg

然后添加一个按键, 按键设置如下图


WechatIMG4.jpeg

注:按键样式随便选, 但数据键名一定要是"btn-power", 按键类型是"开关按键". 因为这两个设置是和程序挂钩的, 要和程序里的名字对应起来.


自此, APP端就设置完毕了. 后续你可以在这里用手机控制开关.配置了上图中的语音设置的话, 也可以打开APP发语音控制. 具体大家慢慢研究, 这里不做详解.



2. Arduino 及代码

我一直是用的arduino IDE做开发的, 虽然这个IDE非常难用, 但看在根正苗红的份上, 代码量不大的情况下就凑合用了.  

关于如何在Arduino IDE里安装ESP8266的库, 网上教程一大堆, 我就不再造轮子了.

代码我是基于文档中小米小爱的插座示例来修改的, 大家到这里按步骤,应该很容易上手.

屏幕快照 2020-04-10 9.38.50 PM.png

我的最终代码放在了这里 https://github.com/shark526/blinker-esp01-switch, 欢迎取阅.


3. 烧录程序

这里有必要提一下我踩过的一个坑, 可能有不熟悉ESP8266-01s的同学, 烧录这个板子, 并不是直接插上USB点击上传即可, 这个板子要短接GPIO0 和 GND再上电,才能进入烧录模式. 而正常工作模式又需要将其断开.

esp-01-pinouts-510x266.jpg

所以为了方便, 我在TTL板子焊接了个开关上去.


5e907a4c3c2258725.jpg_e680.jpg


4. 组装

烧录成功后,就可以与继电器模块合体测试了. 我这里为了方便缩小体积, 把电源模块也粘在了继电器旁边.

线序说明:

    a. 其中电源模块输入端棕色和蓝色接220v交流电

    b. 电源模块输出端黑色和红色分别代表GND和5V+, 接入继电器对应供电输入.

    c. 白线和黄线则为继电器输出端, 控制通断, 接灯线开关或者插座开关.

    d. 背后两根细的黑线为物理开关输入信号, 连入的是GPIO2 和GND, 可以接86面板开关.



5e90807a9da454468.jpg_e680.jpg

5e90807a30f0c1160.jpg_e680.jpg 5e90807a3329c6224.jpg_e680.jpg

5. 测试




部署

东西做好了, 要实用在生活中, 并不是放桌面上玩玩算了. 我这个是要放在86开关下的.

1. 取电

单火取电...是不可能的,各位这里就先哭吧。装修的时候电工师傅也没问开关盒子里要不要零线。因为请的是野路子电工,能正常用电就谢天谢地了,钱没到位,要啥自行车啊。目前单火取电模块我还没有找到可以用来DIY的, 相信不久会出来, 后面有机会再讨论.


那难道我们搞得这个DIY模块,只能做智能插座用了?"非也非也"

屏幕快照 2020-04-10 9.00.56 PM.png

还是有骚操作的,就看你是否技高人胆大.


a. 自己布零线

我可不是跑火车,做过了才敢给各位分享。

道理比较简单, 如下图: 灯口是一根火线(红)和一根零线(蓝), 火线由开关控制通断. 我们要做的是把灯口的零线引入开关面板位置(绿色线),给模块供电.

屏幕快照 2020-04-11 11.58.07 AM.png


两种方法,一种是买穿线器: 在线管里再穿一根线来引入零线,阿宝随便搜一下,显示的价格请不要太天真,可能只是个小配件的价格(这点我恨死了这个价格机制,会让你找东西很累,外面列表显示的价格基本都是假的)

5e909b05402e69655.jpg_e680.jpg


看演示视频好像很好用的样子,各种弯角都能穿过。然而就我分析下来,在已经布了线的管子里想要穿这么条东西,恐怕会很难。所以放弃了,用另一种方式。


另一种是多买点电线,用一根灯线拉取两根灯线。也就是说, 用开关控制的那条火线作为牵引, 同时拉入另外两条线, 达到增加布线目的. 需要注意的是, 拉线连接处要尽量处理平滑, 以方便这两条线在线管里顺滑.

5e91441736b114365.png_e680.jpg

注意这里只是做个示例, 建议布入的线颜色还是按照规范来, 以免以后维护搞不清楚.


b. get lucky

为什么说lucky呢, 如果你当前的开关面板刚好和我一样的情况, 那么上面的布线问题就不用考虑啦.

我现在的情况是, 一个三开开关面板, 其中有个镜前灯是没用的, 所以牺牲这个灯, 用它的开关灯线作来走零线.


2 接线

新建项目.png
结尾

还是那句老话:感谢各位朋友点进来观看,真心希望各位身体健康, 抽空做个DIY,疫情中寻一丝快乐和安宁.

 楼主| 发表于 2020-4-15 17:07 | 显示全部楼层

本帖代码去这里下 https://github.com/shark526/blinker-esp01-switch
发表于 2020-4-13 23:37 | 显示全部楼层
本帖最后由 admin1314 于 2020-4-18 15:35 编辑

直接开关闭合,去灯那边接线也行,就算无网络,依然可以用面板开关控制灯;也可增加无线模块用遥控或86面板控制。
IO2跟GND可用于外接无线模块

#define BLINKER_WIFI                    //官方wifi协议库
#define BLINKER_APCONFIG
#define BLINKER_ALIGENIE_LIGHT              //天猫灯类库
#define BLINKER_PRINT Serial            //串口协议库
#include <Blinker.h>                    //官方库

char auth[] = "238f568aa325";           //设备key号


bool oState = false;
int kg=0;//继电器输出
int kg2=2;//本地轻触开关,io2与gnd接时动作
//*******新建组件对象
BlinkerButton Button1("a1");              //设置app按键的键名
BlinkerButton Button2("a2");
BlinkerButton Button3("a3");
//*******app上按下按键即会执行该函数app里按键有2种模式3钟不同样式,下面写出所有的开关状态。

void dataRead(const String & data)
{
    BLINKER_LOG("Blinker readString: ", data);

    Blinker.vibrate();
   
    uint32_t BlinkerTime = millis();
   
    Blinker.print("millis", BlinkerTime);
}

void button1_callback(const String & state)
{
  BLINKER_LOG("app操作了!: ", state);                //串口打印

  if (digitalRead(kg)==LOW) {              //如果state是on状态
    BLINKER_LOG("亮灯!");                          //串口打印   
  }
  else if (digitalRead(kg)==HIGH) {       //如果state是off状态
    BLINKER_LOG("灭灯!");                          //串口打印
  }
}

void button2_callback(const String & state)  
{
  
    digitalWrite(kg,LOW);             //如果state是on状态
    BLINKER_LOG("get button state:on", state);
    Button1.color("#0000FF");        //设置app按键是蓝色
    Button1.text("亮");
    Button1.print("On");             //反馈回按键状态是开

}
void button3_callback(const String & state)     //点灯app内控制按键触发
{
  

    digitalWrite(kg,HIGH);           //如果state是off状态
    BLINKER_LOG("get button state:off", state);
    Button1.color("#000000");       //设置app按键是黑色
    Button1.text("灭");
    Button1.print("off");       //反馈回按键状态是关

}
//*******如果天猫有对设备进行操作就执行下面
void AliGeniePowerState(const String & state)
{
    BLINKER_LOG("need set power state: ", state);             //串口打印

    if (state == BLINKER_CMD_ON){
        digitalWrite(kg,LOW);
        BlinkerAliGenie.powerState("on");
        BLINKER_LOG("亮灯!");
        Button1.color("#0000FF");                  //设置app按键是蓝色
        Button1.text("亮");
        Button1.print("on");
        BlinkerAliGenie.print();
        oState = true;
    }
    else if (state == BLINKER_CMD_OFF){
        digitalWrite(kg,HIGH);
        BlinkerAliGenie.powerState("off");
        BLINKER_LOG("灭灯!");
        Button1.color("#000000");                     //设置app按键是黑色
        Button1.text("灭");
        Button1.print("off");
        BlinkerAliGenie.print();
        oState = false;
    }
}

void AliGenieQuery(int32_t queryCode)      //caiyq52-天猫精灵控制
{
    BLINKER_LOG("AliGenie Query codes: ", queryCode);

    switch (queryCode)
    {
        case BLINKER_CMD_QUERY_ALL_NUMBER :
            BLINKER_LOG("AliGenie Query All");
            BlinkerAliGenie.powerState(oState ? "on" : "off");
            BlinkerAliGenie.print();
            break;
        case BLINKER_CMD_QUERY_POWERSTATE_NUMBER :
            BLINKER_LOG("AliGenie Query Power State");
            BlinkerAliGenie.powerState(oState ? "on" : "off");
            BlinkerAliGenie.print();
            break;
        default :
            BlinkerAliGenie.powerState(oState ? "on" : "off");
            BlinkerAliGenie.print();
            break;
    }
}
  //*******app定时向设备发送心跳包, 设备收到心跳包后会返回设备当前状态30s~60s一次
void heartbeat()
{
  BLINKER_LOG("状态同步!");
   if (digitalRead(kg)==LOW)
    {
      Button1.print("on");
      Button1.color("#0000FF");              //设置app按键是蓝色
      Button1.text("亮");
    }
    else
    {
    Button1.print("off");
    Button1.color("#000000");              //设置app按键是黑色
    Button1.text("灭");
    }
}
///如果本地开关有动作执行下面手动模式
void sdms(){
  
if (digitalRead(kg)==HIGH&&digitalRead(kg2)==LOW) { //
    Blinker.delay(150);  //延时150ms不能太少标准按键成功率
   
    if (digitalRead(kg2)==HIGH){
        BLINKER_LOG("亮灯!");                         //串口打印
        digitalWrite(kg,LOW);
        Button1.color("#0000FF");                  //设置app按键是蓝色      
        Button1.text("亮");
        Button1.print("on");
        
        }
}
if (digitalRead(kg)==LOW&&digitalRead(kg2)==LOW) {              
    Blinker.delay(150);
    if (digitalRead(kg2)==HIGH){
        BLINKER_LOG("灭灯!");                           //串口打印
        digitalWrite(kg, HIGH);
        Button1.color("#000000");                     //设置app按键是黑色            
        Button1.text("灭");
        Button1.print("off");
        
        }
}
}
void setup()
{
  // 初始化串口
  Serial.begin(115200);
  BLINKER_DEBUG.stream(Serial);

  // 初始化有LED的IO
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
  pinMode(kg, OUTPUT);
  digitalWrite(kg, LOW);              //默认上电是开  LOW 时的是接通继电器。
  pinMode(kg2,INPUT_PULLUP);         //输入上拉
  
  // 初始化blinker
  Blinker.begin(auth);
  Blinker.attachData(dataRead);
  Button1.attach(button1_callback);
  Button2.attach(button2_callback);
  Button3.attach(button3_callback);//app上操作必须的注册回调函数关联按键名“Button1”和判断程序“button1_callback"
  BlinkerAliGenie.attachPowerState(AliGeniePowerState);              //天猫语音操作注册函数
  Blinker.attachHeartbeat(heartbeat);              //app定时向设备发送心跳包, 设备收到心跳包后会返回设备当前状态进行语音操作和app操作同步。
}

void loop() {
  sdms();//本地开关手动模式函数
  Blinker.run();
}[/mw_shl_code]
发表于 2020-4-13 23:38 | 显示全部楼层
admin1314 发表于 2020-4-13 23:37
直接开关闭合,去灯那边接线也行,就算无网络,依然可以用面板开关控制灯;也可增加无线模块用遥控或86面板 ...

  为字母o
 楼主| 发表于 2020-4-14 11:37 | 显示全部楼层
admin1314 发表于 2020-4-13 23:37
直接开关闭合,去灯那边接线也行,就算无网络,依然可以用面板开关控制灯;也可增加无线模块用遥控或86面板 ...

你的意思是直接串接在灯口位置?那样关了开关岂不是模块就断电了, 再也喊不醒了?
发表于 2020-4-14 12:30 | 显示全部楼层
shark526 发表于 2020-4-14 11:37
你的意思是直接串接在灯口位置?那样关了开关岂不是模块就断电了, 再也喊不醒了? ...

为何还要关了开关?有其它3种开关方式可以代替。
发表于 2020-4-15 16:54 | 显示全部楼层
admin1314 发表于 2020-4-13 23:37
直接开关闭合,去灯那边接线也行,就算无网络,依然可以用面板开关控制灯;也可增加无线模块用遥控或86面板 ...

不得?

QQ截图20200415165311.png
 楼主| 发表于 2020-4-15 17:11 | 显示全部楼层
admin1314 发表于 2020-4-13 23:37
直接开关闭合,去灯那边接线也行,就算无网络,依然可以用面板开关控制灯;也可增加无线模块用遥控或86面板 ...

你这个粘过来的你的代码有转码问题, 会误导新手。 如果真要推你的代码你贴个链接到你原帖好了。
发表于 2020-4-18 15:24 | 显示全部楼层

表情 为字母o            
发表于 2020-4-18 15:25 | 显示全部楼层
shark526 发表于 2020-4-15 17:11
你这个粘过来的你的代码有转码问题, 会误导新手。 如果真要推你的代码你贴个链接到你原帖好了。 ...

现在我这边网络不稳定,隔个1.2天就会自己开灯,所以不上贴。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|Archiver|手机版|Arduino中文社区

GMT+8, 2024-11-28 07:47 , Processed in 0.116180 second(s), 21 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表