2019/01/12

Voice control with Google home and Arduino (3/3)

Continue from Part 2

This is the part 3 of the "Voice control with Arduino and Google Home Mini" tutorial. This section primarily explain how the program on ESP8226 worked.



See the demo below:



Here are the URL links about this tutorial, totaling 3 sections:

Source code

Please accquire source code from Github:
https://github.com/stonez56/GoogleMini_NodeMCU_IFTTT_Adafruit

Section 1

This section of code is basically define relay pin numbers and status variables. See the comment in code that pin 13 is actually NodeMCU pin D7. You may see the NodeMCU ping mapping from this article.


// Relay settings
#define relay2Pin 13 //NodeMCU pin D7
#define relay1Pin 15 //NodeMCU pin D8
int relay1Status = 0; //switch of the relay; either 0=off or 1=on
int relay2Status = 0; //switch of the relay; either 0=off or 1=on

Section 2

This section is the basic settings, from Wi-Fi SSID and password to your Adafruit.IO user names and AIO_KEY that I reminded you to make copy is needed here. Exchange all the information below with your own for this program to work.
There are 4 items needs to be changed with your data:

  • WLAN_SSID
  • WLAN_PASS
  • AIO_USERNAME
  • AIO_KEY
/***************************************************
  Adafruit MQTT Library ESP8266 Adafruit IO SSL/TLS example
  Must use the latest version of ESP8266 Arduino from:
    https://github.com/esp8266/Arduino
  Works great with Adafruit's Huzzah ESP board & Feather
  ----> https://www.adafruit.com/product/2471
  ----> https://www.adafruit.com/products/2821
  Adafruit invests time and resources providing this open source code,
  please support Adafruit and open-source hardware by purchasing
  products from Adafruit!
  Written by Tony DiCola for Adafruit Industries.
  SSL/TLS additions by Todd Treece for Adafruit Industries.
  MIT license, all text above must be included in any redistribution
 *****************************************/
#include 
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"

/************* WiFi Access Point *****************/

#define WLAN_SSID       "Your_HOME_SSID"
#define WLAN_PASS       "Your_HOME_SSID_PASSWORD"

/************* Adafruit.io Setup *****************/

#define AIO_SERVER      "io.adafruit.com"
#define AIO_SERVERPORT  8883  // 8883 for MQTTS sercure, 1883 for non-Secure
#define AIO_USERNAME    "stonez56"
#define AIO_KEY         "4nnxxnnxxnnxxn88223"

Section 3

In Line 62, the code set up a subscription at Adafruit MQTT service Light 1 to get real time updated value.  "/feeds/light-1" is the subscription name and it must be the same with the one you defined at Adafruit "Feeds".

In Line 63, it's reserved for future program extension to control RGB color light. 

In Line 51, it's a MQTT publish feature that allows you to upload message from ESP8226 to Adafruit.IO MQTT server.  You might need it for other projects. 

/************ Global State (you don't need to change this!) ******************/

// WiFiFlientSecure for SSL/TLS support
WiFiClientSecure client;

//WiFiClient for non-secure
//WiFiClient client;

// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details.
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);

// io.adafruit.com SHA1 fingerprint
const char* fingerprint = "AD 4B 64 B3 67 40 B5 FC 0E 51 9B BD 25 E9 7F 88 B6 2A A3 5B";

/****************************** Feeds ***************************************/

// Setup a feed called 'test' for publishing.
// Notice MQTT paths for AIO follow the form: /feeds/
//Adafruit_MQTT_Publish light_color = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/light-color");
//Adafruit_MQTT_Publish light_1 = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/light-1");

/*************************** Sketch Code ************************************/
// Bug workaround for Arduino 1.6.6, it seems to need a function declaration
// for some reason (only affects ESP8266, likely an arduino-builder bug).
void MQTT_connect();
void verifyFingerprint();

//set up a feed called 'light_1' / 'light_color' for subscribing to changes
Adafruit_MQTT_Subscribe light_1 = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/light-1");
Adafruit_MQTT_Subscribe light_color = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/light-color");

Section 4

This part is Arduino setup function. Basically, this setup Arduino serial monitor output and connect ESP8226 to your Wi-Fi network, as well as subscribe to MQTT services. 

void setup() {
  Serial.begin(115200);
  delay(10);

  pinMode(relay1Pin, OUTPUT);
  pinMode(relay2Pin, OUTPUT);

  Serial.println(F("Home MQTT Light Control System"));

  // Connect to WiFi access point.
  Serial.println(); Serial.println();
  Serial.print("Connecting to ");
  Serial.println(WLAN_SSID);

  delay(1000);

  WiFi.begin(WLAN_SSID, WLAN_PASS);
  delay(2000);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();

  Serial.println("WiFi connected");
  Serial.print("IP address: "); Serial.println(WiFi.localIP());

  // check the fingerprint of io.adafruit.com's SSL cert
  verifyFingerprint();

  //Subscribe to Adafruit Feed!
  mqtt.subscribe(&light_1);
  mqtt.subscribe(&light_color);

}

Section 5

The Arduino loop keeps checking with Adafruit server to see if there were new messages. If there were new messages, then further check the subscription name. If the subscription name is equal to light-1, then check the light-1 feed_lastread content. If feed_lastread is ON or OFF then, the program call switchRelay function to either turn on light or turn off light. 

This part also check the further project extension for color-light status.

uint32_t x = 0;

void loop() {

  String feed_lastread;
  // Ensure the connection to the MQTT server is alive (this will make the first
  // connection and automatically reconnect when disconnected).  See the MQTT_connect
  // function definition further below.
  MQTT_connect();


  Adafruit_MQTT_Subscribe *subscription;
  while ((subscription = mqtt.readSubscription(5000))) {


    if (subscription == &light_1) {

      Serial.print(F("Light-1:"));
      Serial.println((char *)light_1.lastread);
      feed_lastread = (char *)light_1.lastread;
      feed_lastread.trim(); //
      // Serial.println("Light 1:" + feed_lastread); //for verifying the varialble
      // *** NOTICE: adafruit.io publishes the data as strings, not numbers!!!
      if (feed_lastread == "ON") {
        switchRelay(2, 1);
      }
      if (feed_lastread == "OFF") {
        switchRelay(2, 0);
      }
    }


    if (subscription == &light_color) {
      Serial.print(F("Color-Light:"));
      Serial.println((char *)light_color.lastread);
      feed_lastread = (char *)light_color.lastread;
      feed_lastread.trim(); //
      // Serial.println("Color Light:" + feed_lastread); //for verifying the variable
      // *** NOTICE: adafruit.io publishes the data as strings, not numbers!!!
      if (feed_lastread == "ON") {
        switchRelay(1, 1);
      } else if (feed_lastread == "OFF") {
        switchRelay(1, 0);
      } else {
        colorLightFunction(feed_lastread.toInt());
      }
    }
  }

Section 5

This is the SwitchRealy function, it either turn off or turn on the relay module and print out the current status on Arduino serial monitor. 

/* This turn on/off relay switch

*/
void switchRelay(int relay, int stat) {

  if (relay == 2) { //this is light-1; 
    if (stat == 0) {
      digitalWrite(relay2Pin, LOW);
      Serial.println(F("Relay off"));
    }
    if (stat == 1) {
      digitalWrite(relay2Pin, HIGH);
      Serial.println(F("Relay on"));
    }
  }
  if (relay == 1) { //this is color-light; 
    if (stat == 0) {
      digitalWrite(relay1Pin, LOW);
      Serial.println(F("Relay off"));
    }
    if (stat == 1) {
      digitalWrite(relay1Pin, HIGH);
      Serial.println(F("Relay on"));
    }
  }
}

Section 6

This part is all directly from Adafruit sample code. Just keep them here.

void verifyFingerprint() {

  const char* host = AIO_SERVER;

  Serial.print("Connecting to ");
  Serial.println(host);

  if (! client.connect(host, AIO_SERVERPORT)) {
    Serial.println("Connection failed. Halting execution.");
    while (1);
  }

  //  if (client.verify(fingerprint, host)) {
  //    Serial.println("Connection secure.");
  //  } else {
  //    Serial.println("Connection insecure! Halting execution.");
  //    while(1);
  //  }

}

// Function to connect and reconnect as necessary to the MQTT server.
// Should be called in the loop function and it will take care if connecting.
void MQTT_connect() {
  int8_t ret;

  // Stop if already connected.
  if (mqtt.connected()) {
    return;
  }

  Serial.print("Connecting to MQTT... ");

  uint8_t retries = 30;
  while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
    Serial.println(mqtt.connectErrorString(ret));
    Serial.println("Retrying MQTT connection in 5 seconds...");
    mqtt.disconnect();
    delay(5000);  // wait 5 seconds
    retries--;
    if (retries == 0) {
      // basically die and wait for WDT to reset me
      while (1);
    }
  }

  Serial.println("MQTT Connected!");
}

The end.


中文版程式說明:

這是 "Arduino + Google Home Mini 語音控制電燈 ” 教學的第3部分。
本次主要說明程式大概運作方式。


這個專案運作的方式:


程式碼下載:

請從Github下載程式碼:
https://github.com/stonez56/GoogleMini_NodeMCU_IFTTT_Adafruit

程式說明

1.

這段程式碼基本上是定義繼電器引腳和狀態變數。 程式碼中的註釋裡,第13腳實際上是NodeMCU的腳位D7。 NodeMCU 腳位說明的文章可以參考: NodeMCU ping mapping from this article.


// Relay settings
#define relay2Pin 13 //NodeMCU pin D7
#define relay1Pin 15 //NodeMCU pin D8
int relay1Status = 0; //switch of the relay; either 0=off or 1=on
int relay2Status = 0; //switch of the relay; either 0=off or 1=on

2.

此部分主要是基本設定,從Wi-Fi SSID和密碼及Adafruit.IO用戶名字和AIO_KEY。上篇文章已提醒要先複制 AIO_KEY。將你下列四項自己的資料填入在程式內。
  • WLAN_SSID
  • WLAN_PASS
  • AIO_USERNAME
  • AIO_KEY

/***************************************************
  Adafruit MQTT Library ESP8266 Adafruit IO SSL/TLS example
  Must use the latest version of ESP8266 Arduino from:
    https://github.com/esp8266/Arduino
  Works great with Adafruit's Huzzah ESP board & Feather
  ----> https://www.adafruit.com/product/2471
  ----> https://www.adafruit.com/products/2821
  Adafruit invests time and resources providing this open source code,
  please support Adafruit and open-source hardware by purchasing
  products from Adafruit!
  Written by Tony DiCola for Adafruit Industries.
  SSL/TLS additions by Todd Treece for Adafruit Industries.
  MIT license, all text above must be included in any redistribution
 *****************************************/
#include 
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"

/************* WiFi Access Point *****************/

#define WLAN_SSID       "Your_HOME_SSID"
#define WLAN_PASS       "Your_HOME_SSID_PASSWORD"

/************* Adafruit.io Setup *****************/

#define AIO_SERVER      "io.adafruit.com"
#define AIO_SERVERPORT  8883  // 8883 for MQTTS sercure, 1883 for non-Secure
#define AIO_USERNAME    "stonez56"
#define AIO_KEY         "4nnxxnnxxnnxxn88223"

3.

在第62行,程式碼在Adafruit MQTT服務Light 1上設定了訂閱,才得以獲得實時更新值。
"/feeds/light-1" 則是訂閱名稱,它一定必須和您在Adafruit "Feeds" 中定義的名稱相同。

在第63行,是保留用於將來的程式擴展時來控制RGB顏色燈,此教學暫時用不到。

在第51行,是用MQTT來做發佈功能,它可以該你將資料從ESP8226上傳到Adafruit.IO MQTT服務器。 雖然這裡用不到,但你可能將來會需要用到它。

/************ Global State (you don't need to change this!) ******************/

// WiFiFlientSecure for SSL/TLS support
WiFiClientSecure client;

//WiFiClient for non-secure
//WiFiClient client;

// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details.
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);

// io.adafruit.com SHA1 fingerprint
const char* fingerprint = "AD 4B 64 B3 67 40 B5 FC 0E 51 9B BD 25 E9 7F 88 B6 2A A3 5B";

/****************************** Feeds ***************************************/

// Setup a feed called 'test' for publishing.
// Notice MQTT paths for AIO follow the form: /feeds/
//Adafruit_MQTT_Publish light_color = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/light-color");
//Adafruit_MQTT_Publish light_1 = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/light-1");

/*************************** Sketch Code ************************************/
// Bug workaround for Arduino 1.6.6, it seems to need a function declaration
// for some reason (only affects ESP8266, likely an arduino-builder bug).
void MQTT_connect();
void verifyFingerprint();

//set up a feed called 'light_1' / 'light_color' for subscribing to changes
Adafruit_MQTT_Subscribe light_1 = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/light-1");
Adafruit_MQTT_Subscribe light_color = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/light-color");

4.

這部分是Arduino基本設定。 這裡設定了Arduino serial monitor出並將ESP8226連接到您的Wi-Fi網路,以及訂閱MQTT服務。 

void setup() {
  Serial.begin(115200);
  delay(10);

  pinMode(relay1Pin, OUTPUT);
  pinMode(relay2Pin, OUTPUT);

  Serial.println(F("Home MQTT Light Control System"));

  // Connect to WiFi access point.
  Serial.println(); Serial.println();
  Serial.print("Connecting to ");
  Serial.println(WLAN_SSID);

  delay(1000);

  WiFi.begin(WLAN_SSID, WLAN_PASS);
  delay(2000);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();

  Serial.println("WiFi connected");
  Serial.print("IP address: "); Serial.println(WiFi.localIP());

  // check the fingerprint of io.adafruit.com's SSL cert
  verifyFingerprint();

  //Subscribe to Adafruit Feed!
  mqtt.subscribe(&light_1);
  mqtt.subscribe(&light_color);

}

5.

Arduino loop 不斷檢查 Adafruit 服務器查看是否有新資料送過來。 如果有新資料進來,則進一步檢查訂閱名稱。 如果訂閱名稱等於light-1,則檢查light-1 feed_lastread變數內容。 如果feed_lastread為ON或OFF,則程式呼叫switchRelay副程式,來打開燈或關閉燈。

此部分也檢查顏色燈狀態,此專案暫時用不到。

uint32_t x = 0;

void loop() {

  String feed_lastread;
  // Ensure the connection to the MQTT server is alive (this will make the first
  // connection and automatically reconnect when disconnected).  See the MQTT_connect
  // function definition further below.
  MQTT_connect();


  Adafruit_MQTT_Subscribe *subscription;
  while ((subscription = mqtt.readSubscription(5000))) {


    if (subscription == &light_1) {

      Serial.print(F("Light-1:"));
      Serial.println((char *)light_1.lastread);
      feed_lastread = (char *)light_1.lastread;
      feed_lastread.trim(); //
      // Serial.println("Light 1:" + feed_lastread); //for verifying the varialble
      // *** NOTICE: adafruit.io publishes the data as strings, not numbers!!!
      if (feed_lastread == "ON") {
        switchRelay(2, 1);
      }
      if (feed_lastread == "OFF") {
        switchRelay(2, 0);
      }
    }


    if (subscription == &light_color) {
      Serial.print(F("Color-Light:"));
      Serial.println((char *)light_color.lastread);
      feed_lastread = (char *)light_color.lastread;
      feed_lastread.trim(); //
      // Serial.println("Color Light:" + feed_lastread); //for verifying the variable
      // *** NOTICE: adafruit.io publishes the data as strings, not numbers!!!
      if (feed_lastread == "ON") {
        switchRelay(1, 1);
      } else if (feed_lastread == "OFF") {
        switchRelay(1, 0);
      } else {
        colorLightFunction(feed_lastread.toInt());
      }
    }
  }

5.

這是SwitchRealy程式,它可以關閉或打開繼電器模塊,並在Arduino serial monitor 上印出當前狀態。 

/* This turn on/off relay switch

*/
void switchRelay(int relay, int stat) {

  if (relay == 2) { //this is light-1; 
    if (stat == 0) {
      digitalWrite(relay2Pin, LOW);
      Serial.println(F("Relay off"));
    }
    if (stat == 1) {
      digitalWrite(relay2Pin, HIGH);
      Serial.println(F("Relay on"));
    }
  }
  if (relay == 1) { //this is color-light; 
    if (stat == 0) {
      digitalWrite(relay1Pin, LOW);
      Serial.println(F("Relay off"));
    }
    if (stat == 1) {
      digitalWrite(relay1Pin, HIGH);
      Serial.println(F("Relay on"));
    }
  }
}

6.

這部分都直接來自Adafruit範例程式。

void verifyFingerprint() {

  const char* host = AIO_SERVER;

  Serial.print("Connecting to ");
  Serial.println(host);

  if (! client.connect(host, AIO_SERVERPORT)) {
    Serial.println("Connection failed. Halting execution.");
    while (1);
  }

  //  if (client.verify(fingerprint, host)) {
  //    Serial.println("Connection secure.");
  //  } else {
  //    Serial.println("Connection insecure! Halting execution.");
  //    while(1);
  //  }

}

// Function to connect and reconnect as necessary to the MQTT server.
// Should be called in the loop function and it will take care if connecting.
void MQTT_connect() {
  int8_t ret;

  // Stop if already connected.
  if (mqtt.connected()) {
    return;
  }

  Serial.print("Connecting to MQTT... ");

  uint8_t retries = 30;
  while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
    Serial.println(mqtt.connectErrorString(ret));
    Serial.println("Retrying MQTT connection in 5 seconds...");
    mqtt.disconnect();
    delay(5000);  // wait 5 seconds
    retries--;
    if (retries == 0) {
      // basically die and wait for WDT to reset me
      while (1);
    }
  }

  Serial.println("MQTT Connected!");
}

The end.



No comments:

Post a Comment