2023年12月2日 星期六

TmallGenie Voice control ESP32

Purpose:
天貓精靈(TmallGenie)+ESP32+巴法雲, 實現語音家電控制!
Tmall Genie + ESP32 + Bemfa Cloud, accomplish voice control of home appliances!




Architectures:
Fundamental:

Reference https://en.wikipedia.org/wiki/Tmall_Genie
天貓精靈是阿里巴巴人工智慧實驗室研發的智慧語音助理音箱,基於AliGenie,具有娛樂、生活、購物等多個領域的數百個功能,類似於Amazon Echo、Google Home、Apple HomePod。 天貓精靈於2017年7月5日發布,2017年8月8日正式開賣。
基本參數
產品類型 智慧音箱
揚聲器 內建4Ω揚聲器
輸出功率 3W
麥克風 支持,2個
產品功能 語音辨識控制功能,自訂喚醒詞,通話
無線 WiFi 2.4GB,藍牙4.2
其它參數
處理器 MTK MT8516
系統:AliGenie語音助理
顏色 紅色,白色
尺寸 134*65*59mm
重量 265g
供電方式 DC 12V/1A
電源性能 100-240V/50Hz
控制方式 按鍵,語音
介面 耳機介面
其它特點 內建功放:TI TAS5751M
支援智慧家庭控制、語音購物、手機儲值、叫外送、音頻音樂播放等功能,尤其是購物方面,只需要說出產品描述,然後透過聲紋驗證身份,就能直接下單
訂行程、備忘提醒、訂鬧鐘、查天氣、找手機、倒數、卡路里查詢、聽新聞、查星座運勢、查價格、查股票等功能
與普普藝術結合,波點酷設計,全屋場景體驗升級,就近換新
支援多台設備採用Multi-Room功能進行連動播放
連線方式 WiFi,藍牙
巴法云 C#/.NET SDK。支持TCP和MQTT协议接入,并提供超级简单的设备操作API。跨平台,支持Linux、Windows、OSX,支持树莓派。
Reference https://github.com/withsalt/BemfaCloud
                  https://cloud.bemfa.com/user/index.html


註冊你的帳號, 新增你的主題!

Reference the previous article link on blog:


YouTube Demo:


ESP32 Code:

#include <WiFi.h>
#include <WiFiClient.h>


//巴法云服务器地址默认即可
#define TCP_SERVER_ADDR "bemfa.com"
//服务器端口,tcp创客云端口8344
#define TCP_SERVER_PORT "8344"

//********************需要修改的部分*******************//
#define DEFAULT_STASSID  "Winson_Y52"     //WIFI名称,区分大小写,不要写错
#define DEFAULT_STAPSW   "8888888888"  //WIFI密码
String UID = "your ID";  //用户私钥,可在控制台获取,修改为自己的UID
String TOPIC =   "fan003";         //主题名字,可在控制台新建
String TOPIC1 =   "adapter001";         //主题名字,可在控制台新建
String TOPIC2 =   "led002";         //主题名字,可在控制台新建
//const int LED_Pin = D2;              //单片机引脚
//-----Relay------------------
//-----Relay------------------
#define RELAY1       32
#define RELAY2       33
#define RELAY3       25
#define RELAY4       26
#define RELAY5       5
#define RELAY6       17
#define RELAY7       16
#define RELAY8       4
//**************************************************//

//最大字节数
#define MAX_PACKETSIZE 512
//设置心跳值30s
#define KEEPALIVEATIME 30*1000

//tcp客户端相关初始化,默认即可
WiFiClient TCPclient;
String TcpClient_Buff = "";
unsigned int TcpClient_BuffIndex = 0;
unsigned long TcpClient_preTick = 0;
unsigned long preHeartTick = 0;//心跳
unsigned long preTCPStartTick = 0;//连接
bool preTCPConnected = false;

//相关函数初始化
//连接WIFI
void doWiFiTick();
void startSTA();

//TCP初始化连接
void doTCPClientTick();
void startTCPClient();
void sendtoTCPServer(String p);

//风扇控制函数
void turnOn();
void turnOff();



/*
  *发送数据到TCP服务器
 */
void sendtoTCPServer(String p){
 
  if (!TCPclient.connected())
  {
    Serial.println("Client is not readly");
    return;
  }
  TCPclient.print(p);
  Serial.println("[Send to TCPServer]:String");
  Serial.println(p);
}


/*
  *初始化和服务器建立连接
*/
void startTCPClient(){
  if(TCPclient.connect(TCP_SERVER_ADDR, atoi(TCP_SERVER_PORT))){
    Serial.print("\nConnected to server:");
    Serial.printf("%s:%d\r\n",TCP_SERVER_ADDR,atoi(TCP_SERVER_PORT));
   
    String tcpTemp="";  //初始化字符串
    String tcpTemp1="";  //初始化字符串
    String tcpTemp2="";  //初始化字符串
    tcpTemp = "cmd=1&uid="+UID+"&topic="+TOPIC+"\r\n"; //构建订阅指令
    sendtoTCPServer(tcpTemp); //发送订阅指令
    tcpTemp="";//清空
    tcpTemp1 = "cmd=1&uid="+UID+"&topic="+TOPIC1+"\r\n"; //构建订阅指令
    sendtoTCPServer(tcpTemp1); //发送订阅指令
    tcpTemp1="";//清空
    tcpTemp2 = "cmd=1&uid="+UID+"&topic="+TOPIC2+"\r\n"; //构建订阅指令
    sendtoTCPServer(tcpTemp2); //发送订阅指令
    tcpTemp2="";//清空

    preTCPConnected = true;
    preHeartTick = millis();
    TCPclient.setNoDelay(true);
  }
  else{
    Serial.print("Failed connected to server:");
    Serial.println(TCP_SERVER_ADDR);
    TCPclient.stop();
    preTCPConnected = false;
  }
  preTCPStartTick = millis();
}


/*
  *检查数据,发送心跳
*/
void doTCPClientTick(){
 //检查是否断开,断开后重连
   if(WiFi.status() != WL_CONNECTED) return;

  if (!TCPclient.connected()) {//断开重连

  if(preTCPConnected == true){

    preTCPConnected = false;
    preTCPStartTick = millis();
    Serial.println();
    Serial.println("TCP Client disconnected.");
    TCPclient.stop();
  }
  else if(millis() - preTCPStartTick > 1*1000)//重新连接
    startTCPClient();
  }
  else
  {
    if (TCPclient.available()) {//收数据
      char c =TCPclient.read();
      TcpClient_Buff +=c;
      TcpClient_BuffIndex++;
      TcpClient_preTick = millis();
     
      if(TcpClient_BuffIndex>=MAX_PACKETSIZE - 1){
        TcpClient_BuffIndex = MAX_PACKETSIZE-2;
        TcpClient_preTick = TcpClient_preTick - 200;
      }
      preHeartTick = millis();
    }
    if(millis() - preHeartTick >= KEEPALIVEATIME){//保持心跳
      preHeartTick = millis();
      Serial.println("--Keep alive:");
      sendtoTCPServer("cmd=0&msg=keep\r\n");
    }
  }
  if((TcpClient_Buff.length() >= 1) && (millis() - TcpClient_preTick>=200))
  {//data ready
    TCPclient.flush();
    Serial.println("Buff");
    Serial.println(TcpClient_Buff);
   
    if((TcpClient_Buff.indexOf("fan003") > 0))
    {
      if((TcpClient_Buff.indexOf("&msg=on") > 0))
      {
        turnOn();
      }
      else if((TcpClient_Buff.indexOf("&msg=off") > 0))
      {
        turnOff();
      }
    }
    //-------------------------------------------------------------
    if((TcpClient_Buff.indexOf("adapter001") > 0))
    {
      if((TcpClient_Buff.indexOf("&msg=on") > 0))
      {
        turnAdapterOn();
      }
      else if((TcpClient_Buff.indexOf("&msg=off") > 0))
      {
        turnAdapterOff();
      }
    }
    //-------------------------------------------------------------
    if((TcpClient_Buff.indexOf("led002") > 0))
    {
      if((TcpClient_Buff.indexOf("&msg=on") > 0))
      {
        turnLedOn();
      }
      else if((TcpClient_Buff.indexOf("&msg=off") > 0))
      {
        turnLedOff();
      }
    }
    /**
    if((TcpClient_Buff.indexOf("&msg=on") > 0)) {
      turnOn();
    }else if((TcpClient_Buff.indexOf("&msg=off") > 0)) {
      turnOff();
    }**/
   TcpClient_Buff="";
   TcpClient_BuffIndex = 0;
  }
}

void startSTA(){
  WiFi.disconnect();
  WiFi.mode(WIFI_STA);
  WiFi.begin(DEFAULT_STASSID, DEFAULT_STAPSW);
}

/**************************************************************************
                                 WIFI
***************************************************************************/
/*
  WiFiTick
  检查是否需要初始化WiFi
  检查WiFi是否连接上,若连接成功启动TCP Client
  控制指示灯
*/
void doWiFiTick(){
  static bool startSTAFlag = false;
  static bool taskStarted = false;
  static uint32_t lastWiFiCheckTick = 0;

  if (!startSTAFlag) {
    startSTAFlag = true;
    startSTA();
    Serial.printf("Heap size:%d\r\n", ESP.getFreeHeap());
  }

  //未连接1s重连
  if ( WiFi.status() != WL_CONNECTED ) {
    if (millis() - lastWiFiCheckTick > 1000) {
      lastWiFiCheckTick = millis();
    }
  }
  //连接成功建立
  else {
    if (taskStarted == false) {
      taskStarted = true;
      Serial.print("\r\nGet IP Address: ");
      Serial.println(WiFi.localIP());
      startTCPClient();
    }
  }
}
//打開fan
void turnOn(){
  Serial.println("Turn ON");
  //digitalWrite(LED_Pin,LOW);
  digitalWrite(RELAY1,LOW);
}
//关闭灯泡
void turnOff(){
  Serial.println("Turn OFF");
    digitalWrite(RELAY1,HIGH);
}
//打开灯泡
void turnAdapterOn(){
  Serial.println("Turn1 ON");
  //digitalWrite(LED_Pin,LOW);
  digitalWrite(RELAY2,LOW);
}
//关闭灯泡
void turnAdapterOff(){
  Serial.println("Turn1 OFF");
    digitalWrite(RELAY2,HIGH);
}
//打开灯泡
void turnLedOn(){
  Serial.println("Turn2 ON");
  //digitalWrite(LED_Pin,LOW);
  digitalWrite(RELAY3,LOW);
}
//关闭灯泡
void turnLedOff(){
  Serial.println("Turn2 OFF");
    digitalWrite(RELAY3,HIGH);
}


// 初始化,相当于main 函数
void setup() {
  Serial.begin(9600);
  pinMode(RELAY1,OUTPUT);
  digitalWrite(RELAY1,HIGH);
  pinMode(RELAY2,OUTPUT);
  digitalWrite(RELAY2,HIGH);
  pinMode(RELAY3,OUTPUT);
  digitalWrite(RELAY3,HIGH);
  pinMode(RELAY4,OUTPUT);
  digitalWrite(RELAY4,HIGH);
  pinMode(RELAY5,OUTPUT);
  digitalWrite(RELAY5,HIGH);
  pinMode(RELAY6,OUTPUT);
  digitalWrite(RELAY6,HIGH);
  pinMode(RELAY7,OUTPUT);
  digitalWrite(RELAY7,HIGH);
  pinMode(RELAY8,OUTPUT);
  digitalWrite(RELAY8,HIGH);

}

//循环
void loop() {
  doWiFiTick();
  doTCPClientTick();
}





沒有留言:

張貼留言