美文网首页我爱编程
NodeMcu 作为 MQTT 智能家居中的 TTS

NodeMcu 作为 MQTT 智能家居中的 TTS

作者: 梦沧海 | 来源:发表于2018-05-22 21:43 被阅读283次

    本次应用使用 NodeMcu 连接讯飞的 XFS5152CE 语音合成芯片(开发板)并且连接一个小型功放作为 MQTT TTS 设备。除了 使用这种 TTS ,对于 HomeAssistant 来说,还有许多的 TTS 服务,如百度TTS、谷歌 TTS、微软 TTS ,之所以做这么一款设备,是因为这样可以实现一个纯局域网环境下的智能家居设备,当然也许还有其他好处,此处不表。

    代码中使用的 GPIO 可通过下图进行进行查询。在此再次提醒诸位使用 Arduino IDE 为 NodeMcu 编程,NodeMcu 的引脚号与 GPIO 标号不一致。不要再出现明明程序是对的,引脚输出缺不正确这种情况了。

    NodeMCU GPIO号与引脚对应图

    使用硬件:

    • MicroUSB 母头(作为供电接口)
    • NodeMcu(MQTT 订阅者,并且进行编码转换,将转换后的字符串以指定格式发送给 TTS 芯片)
    • 科大讯飞 XFS5152CE 语音合成芯片(开发板)
    • 小型功放板(注意输入电压,如果电压不是5.0V 则需要电源模块(升压模块))
    • 4Ω 3W 喇叭两个(功放板要能推得动)
    • 3.5 mm 音频接头公头一个
    • 运行 HomeAssistant 服务设备
    • 运行 MQTT 服务设备(也可将此服务与 HomeAssistant 安装在同一设备上,或者使用其他公司提供的 MQTT 服务器)

    MQTT 设备接线图


    MQTT 设备接线图

    以下为 NodeMcu 代码:

    #include <ESP8266WiFi.h>
    #include <PubSubClient.h>
    #include <SoftwareSerial.h>
    
    #define MQTT_VERSION MQTT_VERSION_3_1_1
    #define iFlyTek_ResetPin 12
    #define iFlyTek_RDYPin 14
    #define WIFI_RX 5
    #define WIFI_TX 4
    
    // Wifi: SSID 和 密码
    const char* WIFI_SSID = "YourWifiSSID"; //改成你的 WiFi 名称
    const char* WIFI_PASSWORD = "YourWifiPassword"; //改成你的 WiFi 密码
    
    // MQTT: ID, 服务端 IP, 端口号, 用户名 和 密码
    const PROGMEM char* MQTT_CLIENT_ID = "ESP1";
    const PROGMEM char* MQTT_SERVER_IP = "192.168.31.202"; //改成你的 MQTT 服务 IP 地址
    const PROGMEM uint16_t MQTT_SERVER_PORT = 1883;  //改成你的 MQTT 服务端口号
    const PROGMEM char* MQTT_USER = "YourMQTTUserName"; //改成你的 MQTT 服务用户名
    const PROGMEM char* MQTT_PASSWORD = "YourMQTTUserPassword"; //改成你的 MQTT 服务密码
    
    // MQTT: 主题
    const char* MQTT_iFlyTek_TOPIC = "mqtt/ESP1/iFlyTek";
    
    WiFiClient wifiClient;
    PubSubClient client(wifiClient);
    SoftwareSerial MySerial(WIFI_RX, WIFI_TX);
    
    //芯片复位
    void reset_iFlyTek()
    {
      digitalWrite(iFlyTek_ResetPin, LOW);
      delay(50);
      digitalWrite(iFlyTek_ResetPin, HIGH);
    }
    
    bool get_iFlyTek_Ready()
    {
      bool isReady = digitalRead(iFlyTek_RDYPin);
      isReady = !isReady;
      return isReady;
    }
    
    //用来获取该字符在 UTF8 格式中占用的内存大小
    int get_utf8_size(const char pInput)
    {
      char c = pInput;
      if (c < 0x80) return 1;
      if (c >= 0x80 && c < 0xC0) return 0;
      if (c >= 0xC0 && c < 0xE0) return 2;
      if (c >= 0xE0 && c < 0xF0) return 3;
      return -1;
    }
    
    //获取整个字符数组以 UNICODE 编码时占用的大小
    unsigned int get_output_size(char* ch, int size)
    {
      unsigned int outSize = 0;
      char* input = ch;
      int inputPos = 0;
      while (inputPos < size)
      {
        int utf8Size = get_utf8_size(*input);
        if (utf8Size > 0)
        {
          outSize = outSize + 2;
          input = input + utf8Size;
          inputPos = inputPos + utf8Size;
        }
        else
        {
          outSize = 0;
          break;
        }
      }
      return outSize;
    }
    
    //以小端形式编码
    //将单个 UTF8 编码的字符转换成以 UNICODE 编码
    int utf8_to_unicode(const char* pInput, char *Unic)
    {
      // b1 表示UTF-8编码的pInput中的高字节, b2 表示次高字节, ...
      char b1, b2, b3;
    
      *Unic = 0x0; // 把 *Unic 初始化为全零
      int utfbytes = get_utf8_size(*pInput);
      char *pOutput = Unic;
      switch ( utfbytes )
      {
        case 1:
          *pOutput     = *pInput;
          *(pOutput + 1) = 0x00;
          utfbytes    = 1;
          break;
        case 2:
          b1 = *pInput;
          b2 = *(pInput + 1);
          if ( (b2 & 0xE0) != 0x80 )
            return 0;
          *pOutput     = (b1 << 6) + (b2 & 0x3F);
          *(pOutput + 1) = (b1 >> 2) & 0x07;
          break;
        case 3:
          b1 = *pInput;
          b2 = *(pInput + 1);
          b3 = *(pInput + 2);
          if ( ((b2 & 0xC0) != 0x80) || ((b3 & 0xC0) != 0x80) )
            return 0;
          *pOutput     = (b2 << 6) + (b3 & 0x3F);
          *(pOutput + 1) = (b1 << 4) + ((b2 >> 2) & 0x0F);
          break;
        default:
          return 0;
          break;
      }
      return utfbytes;
    }
    
    void sendString(String string)
    {
      int size = string.length();
      //Serial.println(string);
      //Serial.print("string.length();: ");
      //Serial.println(size);
      size =  size + 1;
    
      char buffer[size];
      string.toCharArray(buffer, size);
      char* input = buffer;
      size = size - 1;
      unsigned int outSize = get_output_size(buffer, size);
      //Serial.print("outSize: ");
      //Serial.println(outSize);
      if (outSize > 0)
      {
        char outCh[outSize];
        char* output = outCh;
        int inputPos = 0;
        while (inputPos < size)
        {
          int uft8Size = get_utf8_size(*input);
          if (uft8Size > 0)
          {
            utf8_to_unicode(input, output);
            output = output + 2;
            input = input + uft8Size;
            inputPos = inputPos + uft8Size;
          }
          else
          {
            break;
          }
        }
        unsigned char frameHeader = 0xFD;
        unsigned char temp = 0x00;
        unsigned int frameLength = outSize + 2;
        unsigned char dataHeader[2] = {0x01, 0x03};
        MySerial.write(frameHeader);
        //Serial.print(frameHeader, HEX);
        if (frameLength < 255)
        {
          MySerial.write(temp);
          //Serial.print(temp, HEX);
        }
        MySerial.write(frameLength);
        //Serial.print(frameLength, HEX);
        MySerial.write(dataHeader[0]);
        //Serial.print(dataHeader[0], HEX);
        MySerial.write(dataHeader[1]);
        //Serial.print(dataHeader[1], HEX);
        //Serial.print("   ");
        for (int index = 0; index < outSize ; ++index)
        {
          MySerial.write(outCh[index]);
          //Serial.print(outCh[index], HEX);
          //Serial.print(" ");
        }
        //    Serial.println(" ");
      }
    }
    
    // 接收来自 MQTT 的消息
    void callback(char* p_topic, byte* p_payload, unsigned int p_length) {
      // concat the payload into a string
      //  Serial.println("call back has called");
      String payload;
      for (uint8_t i = 0; i < p_length; i++) {
        payload.concat((char)p_payload[i]);
      }
    
      // handle message topic
      if (String(MQTT_iFlyTek_TOPIC).equals(p_topic)) {
        //    Serial.println("MQTT receive Data: ");
        //    Serial.println(payload);
        sendString (payload);
      }
    }
    
    void reconnect() {
      // Loop until we're reconnected
      while (!client.connected()) {
        //    Serial.print("INFO: Attempting MQTT connection...");
        // Attempt to connect
        if (client.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASSWORD)) {
          //      Serial.println("INFO: connected");
          delay(1);
          // Once connected, publish an announcement...
        } else {
          //      Serial.print("ERROR: failed, rc=");
          //      Serial.print(client.state());
          //      Serial.println("DEBUG: try again in 5 seconds");
          // Wait 5 seconds before retrying
          delay(5000);
        }
      }
    }
    
    void setup() {
      // init the serial
      Serial.begin(9600);
      MySerial.begin(9600);
    
      pinMode(iFlyTek_ResetPin, OUTPUT);
      pinMode(iFlyTek_RDYPin, INPUT);
    
      // init the WiFi connection
      //  Serial.println();
      //  Serial.println();
      //  Serial.print("INFO: Connecting to ");
      WiFi.mode(WIFI_STA);
      //  Serial.println(WIFI_SSID);
      WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
    
      while (WiFi.status() != WL_CONNECTED) {
        delay(1000);
        //    Serial.print("- - - | ");
      }
    
      //  Serial.println("");
      //Serial.println("INFO: WiFi connected");
      //  Serial.print("INFO: IP address: ");
      //  Serial.println(WiFi.localIP());
    
      // init the MQTT connection
      client.setServer(MQTT_SERVER_IP, MQTT_SERVER_PORT);
      client.setCallback(callback);
    
      reset_iFlyTek();
    }
    
    void loop() {
      if (!client.connected()) {
        reconnect();
        /*boolean subOK = */ client.subscribe(MQTT_iFlyTek_TOPIC);
        //Serial.print("MQTT: sub status: ");
        //Serial.println(subOK);
      }
      client.loop();
    }
    

    HomeAssistant 配置:

    input_text:
      iflytek:
        name: iFlyTek
        min: 0
        max: 5000
    
    mqtt:
    #改成你的 MQTT 服务端口号
      broker: 192.168.31.202
    #改成你的 MQTT 服务端口号
      port: 1883
    #改成你的 MQTT 服务用户名
      username: YourMQTTUserName
    #改成你的 MQTT 服务密码
      password: YourMQTTUserPassword
    
    automation: !include automations.yaml
    

    automations.yaml 文件:

    - id: iFlyTek
      alias: iFlyTekTTS
      trigger:
        platform: state
        entity_id: input_text.iflytek
      action:
        service: mqtt.publish
        data_template:
          topic: "mqtt/ESP1/iFlyTek"
          payload: "{{states.input_text.iflytek.state}}"
    

    后续我会将 Arduino 代码封装成库,方便直接调用。这个坑留着以后填。

    相关文章

      网友评论

        本文标题:NodeMcu 作为 MQTT 智能家居中的 TTS

        本文链接:https://www.haomeiwen.com/subject/nalhjftx.html