智能语音控制开关(可对接天猫精灵、小爱等)----保姆级教程

背景

        自用,实现使用ESP01S模块和继电器构建自定义功能的Wi-Fi控制器,后续可用于自动窗帘、自动给植物浇水,自动开关灯,自动开关风扇等

所需材料:

  1. ESP01S模块
  2. 继电器模块(这里网上卖的有个坑,需要注意,下文提及)
  3. 杜邦线(用于连接模块)
  4. USB转串口模块

步骤:

1. 准备工作:
  • 获取所需的组件和材料。
  • 安装Arduino IDE并在其中安装ESP8266支持。
  • 连接ESP01S模块和继电器模块,确保连接正确。通常网上卖的,GPIO 0用于控制继电。
  • 这里遇到的坑,需要提及下的是网上很多店家把光耦贴反了,就拿出来发货,导致继电器功能失效,要把光耦位置贴回来,跟上图原理图一致,即1脚跟R6导线一端连在一起。
2. 编写Arduino代码:
#include <ESP8266WiFi.h>   //默认,加载WIFI头文件
#include "PubSubClient.h"  //默认,加载MQTT库文件
#include <ESP8266HTTPClient.h>
#include <EEPROM.h>
#include <Ticker.h>
#include <ArduinoJson.h>
#include <AceButton.h>
#include <ESP8266httpUpdate.h>
using namespace ace_button;
WiFiClient client_bemfa_WiFiClient;
HTTPClient http_bemfa_HTTPClient;


const char* mqtt_server = "bemfa.com";  //默认,MQTT服务器
const int mqtt_server_port = 9501;      //默认,MQTT服务器


//*****可以修改的地方******//
String aptype = "002";                                  //001插座类型,002灯类型,003风扇类型,004传感器,005空调,006开关,009窗帘
String verSion = "1";                                   //1是mqtt协议,3是tcp协议,5是mqtt V2 版本,7是 tcp V2 版本
String adminID = "";                                    //默认空即可。企业id,企业用户可配置,用户会自动绑定到该企业下,获取id方法见接入文档5.17节
const int LED_Pin = 0;                                 //单片机控制的继电器引脚,或者LED引脚值,可自行修改,其他开发板,修改为自己的引脚,例如esp8266-01修改为const int LED_Pin = 0;
const int LedBlink = 2;                                //指示灯引脚,可自行修改,如果没有指示灯,建议删除指示灯相关代码
const int buttonPin = 3;                               //定义按钮引脚,可自行修改
int failCount = 3;                                      //定义失败连接次数
bool ledState = true;                                   //led 状态
String upUrl = "http://bin.bemfa.com/b/1BcZjQyYTFjMzVjYjI4NGQzZTlhNDk3Yjk0NDFiN2QwZDI=CC3857002.bin";  //OTA固件链接,请替换为自己的固件链接,如果接收到msg=update,开始执行固件升级
//**********************//


String topicMac = "";
int httpCode = 0;
String UID = "";
String TOPIC = "";
#define HOST_NAME "bemfa"
char config_flag = 0;
#define MAGIC_NUMBER 0xAA
/**
* 结构体,用于存储配网信息
*/
struct config_type {
  char stassid[32];
  char stapsw[64];
  char cuid[40];
  char ctopic[32];
  uint8_t reboot;
  uint8_t magic;
};
config_type config;

WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];
int value = 0;

//灯光函数及引脚定义
void turnOnLed();
void turnOffLed();
void doSmartconfig();
void saveConfig();
void initWiFi();
void loadConfig();
void restoreFactory();
void waitKey();

//按钮配置接口
AceButton ledButton(buttonPin);
//按键处理程序
void handleEvent(AceButton* button, uint8_t eventType,
                 uint8_t) {
  String Msgstring;
  String topicString;
  switch (eventType) {
    //当短按时
    case AceButton::kEventReleased:
      Serial.println(F("Button: Pressed"));

      ledState = !ledState;             //改变led状态
      digitalWrite(LED_Pin, ledState);  //写入状态

      if (ledState == true) {
        Serial.println("low press off");
        Msgstring = "off";
      } else {
        Serial.println("low press on");
        Msgstring = "on";
      }
      topicString = TOPIC + "/up";
      client.publish(topicString.c_str(), Msgstring.c_str());  //推送消息

      break;
      //当长按时
    case AceButton::kEventLongPressed:
      Serial.println(F("Button: Long Pressed"));

      Serial.println("Restore Factory....... ");
      config.magic = 0x00;
      config.reboot = 0;
      strcpy(config.stassid, "");
      strcpy(config.stapsw, "");
      strcpy(config.cuid, "");
      strcpy(config.ctopic, "");
      saveConfig();

      doSmartconfig();
      int num = 0;
      while (WiFi.status() != WL_CONNECTED && num < 120) {  //检查是否连接成功
        delay(500);
        num = num + 1;
        Serial.print(".");
      }
      getUid(topicMac, true);
      break;
  }
}


//当升级开始时,打印日志
void update_started() {
  Serial.println("CALLBACK:  HTTP update process started");
}

//当升级结束时,打印日志
void update_finished() {
  Serial.println("CALLBACK:  HTTP update process finished");
}

//当升级中,打印日志
void update_progress(int cur, int total) {
  Serial.printf("CALLBACK:  HTTP update process at %d of %d bytes...\n", cur, total);
  digitalWrite(LedBlink, HIGH);  //指示灯引脚
  delay(100);
  digitalWrite(LedBlink, LOW);  //指示灯引脚
  delay(100);
}

//当升级失败时,打印日志
void update_error(int err) {
  Serial.printf("CALLBACK:  HTTP update fatal error code %d\n", err);
}



/**
 * 固件升级函数
 * 在需要升级的地方,加上这个函数即可,例如setup中加的updateBin(); 
 * 原理:通过http请求获取远程固件,实现升级
 */
void updateBin() {
  Serial.println("start update");
  WiFiClient UpdateClient;

  ESPhttpUpdate.onStart(update_started);      //当升级开始时
  ESPhttpUpdate.onEnd(update_finished);       //当升级结束时
  ESPhttpUpdate.onProgress(update_progress);  //当升级中
  ESPhttpUpdate.onError(update_error);        //当升级失败时

  t_httpUpdate_return ret = ESPhttpUpdate.update(UpdateClient, upUrl);
  switch (ret) {
    case HTTP_UPDATE_FAILED:  //当升级失败
      Serial.println("[update] Update failed.");
      break;
    case HTTP_UPDATE_NO_UPDATES:  //当无升级
      Serial.println("[update] Update no Update.");
      break;
    case HTTP_UPDATE_OK:  //当升级成功
      Serial.println("[update] Update ok.");
      break;
  }
}


/**
* mqtt回调函数
*/
void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  String Mqtt_Buff = "";
  for (int i = 0; i < length; i++) {
    Mqtt_Buff += (char)payload[i];
  }
  Serial.print(Mqtt_Buff);
  Serial.println();

  // Switch on the LED if an 1 was received as first character
  if (Mqtt_Buff == "on") {  //如果接收字符on,亮灯
    turnOnLed();            //开灯函数

  } else if (Mqtt_Buff == "off") {  //如果接收字符off,亮灯
    turnOffLed();                   //关灯函数
  } else if (Mqtt_Buff == "update") {
    Serial.println("[update] Update Start......");
    updateBin();
  }
  Mqtt_Buff = "";
}

/**
* 重新连接
*/
void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(UID.c_str())) {
      Serial.println("connected");

      client.subscribe(TOPIC.c_str());  //订阅
      failCount = 0;
    } else {
      failCount = failCount + 1;
      if (failCount>2){
        delayRestart(0);
      }
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(2000);
    }
  }
}


/**
* 按键中断,仅在配网失败时使用
*/
static unsigned long buttonLastMillis = 0;  //时间戳,用于计算防抖
void IRAM_ATTR checkSwitch() {
  unsigned long newMillis = millis();  //获取当前时间戳

  if (newMillis - buttonLastMillis > 30) {  //检测短按,是否大于30ms
    Serial.println("low press !!!!!!!!");
    ledState = !ledState;             //改变led状态
    digitalWrite(LED_Pin, ledState);  //写入状态
  }
  buttonLastMillis = newMillis;  //重新计算防抖动
}

void getUid(String mac, bool reConfig) {
  if (strcmp(config.cuid, "") == 0 || reConfig) {

    http_bemfa_HTTPClient.begin(client_bemfa_WiFiClient, "http://api.bemfa.com/api/device/v1/airkiss/?topic=" + mac + "&version=" + verSion + "&ad=" + adminID);
    httpCode = http_bemfa_HTTPClient.GET();
    if (httpCode > 0) {
      String payload = http_bemfa_HTTPClient.getString();
      //json数据解析
      StaticJsonDocument<200> doc;
      DeserializationError error = deserializeJson(doc, payload);
      if (error) {
        Serial.print(F("deserializeJson() failed: "));
        Serial.println(error.c_str());
      }
      String code = doc["code"];
      if (code == "5723200") {
        String docUID = doc["uid"];
        strcpy(config.cuid, docUID.c_str());
        strcpy(config.ctopic, mac.c_str());
        saveConfig();
      } else {
        Serial.println(" config ERROR.........");
      }

      http_bemfa_HTTPClient.end();
    }
  }
  if (strcmp(config.ctopic, "") == 0) {
    TOPIC = mac;
  } else {
    TOPIC = config.ctopic;
  }
  UID = config.cuid;
}

/**
* 存储配网信息
*/
void saveConfig() {
  int rand_key;
  uint8_t mac[6];
  WiFi.macAddress(mac);
  config.reboot = 0;
  EEPROM.begin(256);
  uint8_t* p = (uint8_t*)(&config);
  for (int i = 0; i < sizeof(config); i++) {
    EEPROM.write(i, *(p + i));
  }
  EEPROM.commit();
}
Ticker delayTimer;
void delayRestart(float t) {
  delayTimer.attach(t, []() {
    ESP.restart();
  });
}


/**
* airkiss配网
*/
void doSmartconfig() {
  Serial.print(" Smartconfig begin,Waiting for WeChat Config.....");

  WiFi.disconnect();
  WiFi.mode(WIFI_STA);
  WiFi.stopSmartConfig();
  WiFi.beginSmartConfig();
  int cnt = 0;


  while (!WiFi.smartConfigDone()) {

    digitalWrite(LedBlink, HIGH);  //指示灯引脚
    delay(150);
    digitalWrite(LedBlink, LOW);  //指示灯引脚
    delay(150);
    if (WiFi.smartConfigDone()) {
      strcpy(config.stassid, WiFi.SSID().c_str());
      strcpy(config.stapsw, WiFi.psk().c_str());
      config.magic = 0xAA;
      saveConfig();
    }

    cnt++;
    if (cnt >= 600) {
      delayRestart(0);
    }
  }
  digitalWrite(LedBlink, LOW);  //指示灯引脚
  Serial.println("Smartconfig ok");
}

/**
* 初始化wifi信息,并连接路由器网络
*/
void initWiFi() {

  if (WiFi.status() != WL_CONNECTED) {
    WiFi.disconnect();                          //断开连接
    WiFi.mode(WIFI_STA);                        //STA模式
    WiFi.begin(config.stassid, config.stapsw);  //连接路由器
  }
  int num = 0;
  while (WiFi.status() != WL_CONNECTED && num < 120) {  //检查是否连接成功
    delay(500);
    num = num + 1;
    Serial.print(".");
  }
  Serial.println("wifi config ok");
}
/**
* 加载存储的信息,并检查是否进行了反复5次重启恢复出厂信息
*/
uint8_t* p = (uint8_t*)(&config);
void loadConfig() {
  uint8_t mac[6];
  WiFi.macAddress(mac);
  EEPROM.begin(256);
  for (int i = 0; i < sizeof(config); i++) {
    *(p + i) = EEPROM.read(i);
  }
  config.reboot = config.reboot + 1;
  if (config.reboot >= 4) {
    restoreFactory();
  }
  if (config.magic != 0xAA) {
    config_flag = 1;
  }
  EEPROM.begin(256);
  for (int i = 0; i < sizeof(config); i++) {
    EEPROM.write(i, *(p + i));
  }
  EEPROM.commit();
  delay(2000);
  EEPROM.begin(256);
  config.reboot = 0;
  for (int i = 0; i < sizeof(config); i++) {
    EEPROM.write(i, *(p + i));
  }
  EEPROM.commit();
  delay(2000);
}
/**
* 恢复出厂设置,清除存储的wifi信息
*/
void restoreFactory() {
  Serial.println("Restore Factory....... ");
  config.magic = 0x00;
  strcpy(config.stassid, "");
  strcpy(config.stapsw, "");
  strcpy(config.cuid, "");
  strcpy(config.ctopic, "");
  config.magic = 0x00;
  saveConfig();
  delayRestart(1);
  while (1) {
    delay(100);
  }
}
/**
* 检查是否需要airkiss配网
*/
void waitKey() {
  if (config_flag == 1) {
    doSmartconfig();
  }
}


void setup() {

  Serial.begin(115200);
  pinMode(buttonPin, INPUT_PULLUP);                                        // 设置led引脚为输入引脚
  attachInterrupt(digitalPinToInterrupt(buttonPin), checkSwitch, RISING);  //设置中断
  pinMode(LED_Pin, OUTPUT);
  digitalWrite(LED_Pin, ledState);  //写入默认状态
  pinMode(LedBlink, OUTPUT);
  digitalWrite(LedBlink, LOW);  //指示灯引脚

  Serial.println("Beginning...");

  topicMac = WiFi.macAddress().substring(8);  //取mac地址做主题用
  topicMac.replace(":", "");                  //去掉:号
  topicMac = topicMac + aptype;
  Serial.print("主题:");
  Serial.println(topicMac);
  loadConfig();
  waitKey();
  initWiFi();
  getUid(topicMac, false);
  //按键配置
  ButtonConfig* buttonConfig = ButtonConfig::getSystemButtonConfig();
  buttonConfig->setEventHandler(handleEvent);
  buttonConfig->setFeature(ButtonConfig::kFeatureLongPress);
  buttonConfig->setFeature(ButtonConfig::kFeatureRepeatPress);
  buttonConfig->setFeature(ButtonConfig::kFeatureSuppressAfterLongPress);
  buttonConfig->setLongPressDelay(5000);  //长按时间5秒

  detachInterrupt(digitalPinToInterrupt(buttonPin));  //删除外部中断
  client.setServer(mqtt_server, mqtt_server_port);
  client.setCallback(callback);

  digitalWrite(LedBlink, HIGH);  //指示灯引脚
}
void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
  ledButton.check();  //按键检查
}

//打开灯泡
void turnOnLed() {
  Serial.println("Turn ON");
  digitalWrite(LED_Pin, LOW);
  digitalWrite(LedBlink, LOW);
  delay(500);
  digitalWrite(LedBlink, HIGH);
}
//关闭灯泡
void turnOffLed() {
  Serial.println("Turn OFF");
  digitalWrite(LED_Pin, HIGH);
  digitalWrite(LedBlink, LOW);
  delay(500);
  digitalWrite(LedBlink, HIGH);
}
3. 上传代码到ESP01S模块:
  • 将ESP01S模块连接到计算机上,并选择正确的端口和开发板。
  • 编译并上传代码到ESP01S。
4. 画板调试:
  • 因为想直接接220v,所以画个板子,再买个220v交流转直流5v的模块,如下:
  • 画板

     

    焊接后模组

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值