Amazon Service
Alexa 簡介
Alexa 是 Amazon 底下的智慧語音服務,它能聆聽使用者的語句,並且以語音回答。
底下是Amazon的Alexa影片介紹: https://www.youtube.com/watch?v=UOEIH2l9z7c
Amazon也推出實體產品像是Amazon Echo智慧音箱,裡面搭載Alexa服務: https://www.youtube.com/watch?v=FQn6aFQwBQU
語音服務的使用情境裡,使用者不用再 “拿起” 或 “掀開” 3C產品,第一步通常是用關鍵字喚醒產品,像是說出 “Alexa”, 接著可以說出一些問題或命令。像是今天天氣如何,播放音樂,設定一小時後的鬧鐘。
Alexa同時也支援Alexa Skill,讓3rd party的個人或公司參與開發。像是可以下載說故事的Alexa Skill,Alexa就擁有說故事的能力。
Alexa Skill裡面也包含智慧家庭的服務,支援常見的智慧家庭的產品像是智慧燈泡、智慧開關、智慧門鎖等等……。
這個範例裡,會介紹如何讓Ameba也支援Alexa的服務,讓使用者可以對Ameba詢問今天有什麼新聞,或是控制支援AWS IoT的產品,像是之前的範例裡示範過如何用Alexa經由AWS IoT開關Ameba上的燈泡。
準備事項
Alexa服務包含了 AVS (Alexa Voice Service) 以及 ASK(Alexa Skill Kit),要讓Ameba支援Alexa最主要需要設定與支援AVS。另外Ameba的語音處理需要一塊支援I2S的Audio codec、一個喇叭。
1. Ameba * 1
2. ALC5680 *1
3. Button *1
4. Speaker *1
- 設定 Alexa Voice Service
第一次使用可以參考官方文件做設定
https://developer.amazon.com/public/solutions/alexa/alexa-voice-service/getting-started-with-the-alexa-voice-service#register
它分成4個步驟:
- Step 1: Register Your Product
這部份主要是帳號設定 - Step 2: Prototype
這部份我們會在Ameba上面寫程式支援Alexa,到這一步已經可以做一些基本的控制 - Step 3: Design and Build with AVS
這部份則是細節控制,像是設定麥克風數量、裝置預設的距離等等。這個範例裡我們不會執行到這步 - Step 4: Launch Your Product
這部份是關於如何將產品上市。這個範例裡我們不會執行到這步
- 註冊成為Amazon開發者
第一步裡,要先成為Amazon 開發者,第一次設定會先填寫註冊表單
https://developer.amazon.com/registration/profile.html
填完之後,點選表單後面的 “Save and Continue”
接著會出現同意書,閱讀完之後如果同意請點選 “Accept and Continue”
接著會詢問App內購與廣告的資訊,這部份可以之後再更改,我們先跳過,點選 “Save and Continue”
- 新增AVS App
填完之後會來到Amazon開發者的管理後台
我們點選 “Alexa Voice Service”
之後點擊”Get Started”開始Arduino Alexa的設定流程
- Product Information
這個頁面裡要填寫deivce相關的資訊:
1. Product Name: 這裡的資訊應該會與我們在註冊成為開發者的一樣
2. Product Type ID: 只能使用字母, 數字, 與底線。這裡面的內容會在範例裡用到
3. Product Type: 選擇Alexa-Enabled Device
其他欄位及選項如下列圖片所示,結束後按NEXT
- Security Profile
接著要設定Security profile,如果之前沒有設定過,就需要新增一個,我們點選 “Create a new profile”
在 General裡有兩個欄位要填:
1. Security Profile: Security profile的名字
2. Security Profile Description: Security profile的描述
填完之後按 “Next” 即會產生Security Profile ID
接著會產生一組 Client ID 與 Client Secret,這裡面的內容會在範例裡用到,我們可以先記下來,或是之後再回來查看。
接著要填寫的是當使用者要授權Ameba使用Alexa的時候,要到哪個網址取得訊息,以及該如何將授權訊息回傳。這裡我們使用mDNS的位址:
1. Allowed Origins: https://localhost:3000
2. Allowed Return URLs: https://localhost:3000/authresponse
填完之後點分別在欄位右邊按下”ADD”,接著按下”UPDATE”
之後出現上圖的頁面代表設定大功告成。
- Enable security profile
接著我們要啟用前面設定好的 Security profile https://developer.amazon.com/lwa/sp/overview.html
選擇剛剛設定的 Security Profile,並點選 “Confirm”
接著要填寫第一次登入使用時,要顯示privacy policy的URL,可以任填。這裡我們填一樣的mDNS位置。然後點選 “Save”
到這裡就完成AVS的設定
- 申請Refresh Token
為了減少本範例在運作期間,進行申請Refresh Token所花費的耗能,我們提前在範例運作前先作申請。
Github上提供了Windows/Linux的申請工具:
https://github.com/ambiot/amb1_arduino/tree/master/misc/amazon_alexa_avs_auth_tool
在執行前得先使用openssl產生certificate
openssl genrsa -out ca.key 4096 openssl req -new -x509 -days 365 -key ca.key -out ca.crt -sha256
會產生ca.key及ca.crt兩個檔案,將這兩個檔案與申請工具放在同一個資料夾而在Linux下則需要先將Refresh Token tool申請工具編譯:
gcc -o amz_oauth_linux amz_oauth_linux.c -lssl -lcrypto
並且執行
./amz_oauth_linux
如果在Windows底下可以開啟cmd執行Refresh Token申請工具:amz_oauth_windows.exe
上圖是Refresh Token tool執行期的畫面,以下需要填入剛在Alexa Voice Service申請取得的相關欄位資訊
1. device id: 這裡請填Product ID
2. device dsn: 通常是一組流水號,目前還未有具體的使用情境,我們先任意填
3. client id: 這裡請填Client ID
4. client secret: 這裡請填client secret
5. allowed origins: 這裡請填https://localhost:3000
6. allowed return urls: 這裡請填https://localhost:3000/authresponse
7. server port: 這裡請填3000
之後會跑出如上圖標示紅框處的一段連結,將這段連結拷貝組合後貼上瀏覽器進行Refresh Token的申請,如果申請成功,頁面會顯示Refresh Token的參數,並且原來的cmd console也會顯示Refresh Token(如上圖標示藍框處)
材料準備
- Audio codec
Ameba本身有I2S的介面可以處理Audio data,這個範例裡我們使用Realtek Audio Shield做為I2S的資料來源。
Realtek Audio Shield使用的IC是ALC5680,通常Audio codec至少會使用到一組I2S與I2C。I2S是資料I/O,I2C則是初始與選項設定。底下是Realtek Audio Shield的官網介紹
http://www.realtek.com.tw/press/newsViewOne.aspx?NewsID=441
- 喇叭
這塊Audio codec上面並沒有喇叭,所以我們還需要外接喇叭。
喇叭的部份,用一般支援LINE IN的喇叭即可。
接線與範例設定
- 接線
Realtek Audio Shield可以完整與Ameba嵌合,在Realtek Audio Shield上另外還需要一個按鈕用來喚醒Ameba的Alexa服務(下圖標示紅框處)
接著我們將Ameba接上Audio Shield 與喇叭。
- 上傳程式
接著我們打開範例 “File” -> “Examples” -> “AmebaAudio” -> “alexa_basic”
Alexa服務需要使用到網路,這裡我們修改SSID & PASS成欲連上的WiFi AP SSDI & PASS
接著要填寫與AVS服務相關的資訊,說明如下:
- avs_refresh_token: 此欄位填入由refresh token申請工具取得的refresh token。
- avs_client_id: 這裡的 client id就是之前設定 AVS時取得的client id
- avs_client_secret: 這裡的 client secret 就是之前設定 AVS 時取得的 client secret
- avs_http2_host: 提供辨識語音的host,目前Amazon提供好幾組host供不同語系的使用者使用。這裡我們填美國在使用的host
填完之後就可以編譯上傳至Ameba
- 測試
在Realtek Audio Shield上的按鈕用於喚醒Ameba,我們將它按著,講話,然後放開,Ameba就會將語音資料上傳到雲端,雲端會回傳語音回應至Ameba,Ameba再將它播放。
請參考底下影片的示範:
程式碼說明
- 程式碼
- setup_alexa()
在 setup_alexa() 這個函式裡,設定了所有Alexa需要的資訊
Alexa.setAvsClientId(avs_client_id,sizeof(avs_client_id)-1);
設定Client ID,Client ID的值是使用者申請Alexa APP時取得
Alexa.setAvsClientSecret(avs_client_secret, sizeof(avs_client_secret)-1);
設定 Client Secret,Client Secret的值是使用者申請Alexa APP時取得
Alexa. setAvsRefreshToken (avs_refresh_token, sizeof(avs_refresh_token)-1 );
設定儲存Refresh token。當初次設定完成之後,會呼叫這個函式將Refresh token存在 Flash memory裡
Alexa.setAvsHttp2Host(avs_http2_host, sizeof(avs_http2_host)-1);
設定提供AVS語音服務的網站。Amazon目前提供的是基於HTTP2的語音服務。不同語系使用的網站不同
Alexa.begin();
最後啟始AVS語音服務功能
- loop()
在loop()函式裡,只要持續檢查網路狀態即可,如有異常則進行網路重連
void loop() { if (WiFi.status() != WL_CONNECTED) { while (WiFi.begin(ssid, pass) != WL_CONNECTED) { delay(1000); } Serial.println("Connected to wifi"); } delay(100); }
- Log說明
這小節針對Serial Monitor的log做個說明,底下是已經註冊過Alexa的Ameba的完整log,我們一一說明
Interface 0 IP address : 192.168.88.32Connected to wifi [HTTPD] httpd_server_thread started init mdns SGTL5000 CHIP ID:0xA011 [HTTPC] Use ciphersuite TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256 version=[HTTP/1.1] status=[200 OK] content_type=[application/json] content_lenght=1233 [14316] update access_token:Atza|IwEBIJYyUjyCv8luvwAI7TQyL-j [14333] update refresh_token:Atzr|IwEBIIW2_8KvJd9qake23w12WQT [15664] connecting... [24210] handshake done [24239] C->S HEADERS :method: GET :path: /v20160207/directives :scheme: https :authority: avs-alexa-na.amazon.com:443 accept: */* authorization: Bearer Atza|IwEBIJYyUjyCv8luvwAI [24308] C->S HEADERS :method: POST :path: /v20160207/events :scheme: https :authority: avs-alexa-na.amazon.com:443 accept: */* authorization: Bearer Atza|IwEBIJYyUjyCv8luvwAI content-type: multipart/form-data; boundary=bo [24546] CS HEADERS :method: POST :path: /v20160207/events :scheme: https :authority: avs-alexa-na.amazon.com:443 accept: */* authorization: Bearer Atza|IwEBIJYyUjyCv8luvwAI content-type: multipart/form-data; boundary=bo [33363] upload audio................................ [36861] C<-S HEADERS :status: 200 access-control-allow-origin: * x-amzn-requestid: 0ed21ffffef23a7d-000069fb-00025786-cd4862c89753e4bd-3736a86a-5 content-type: multipart/related;boundary=36697d04-4d4e-4595-b6e6-a390edd0295a;start=metadata.1494256392289;type="application/json" [37551] json: {"directive":{"header":{"namespace":"Speaker","name":"SetMute","messageId":"664d09e5-d664-4948-bbac-c70e08a1424d","dialogRequestId":"64567a10-0a67-48e6-8d66-4a936508f032"},"payload":{"mute":false}}} [37606] json: {"directive":{"header":{"namespace":"SpeechSynthesizer","name":"Speak","messageId":"fe57a3bb-2280-4d88-9345-529e51cc30f2","dialogRequestId":"64567a10-0a67-48e6-8d66-4a936508f032"},"payload":{"url":"cid:DeviceTTSRenderer_0ae4a760-fee0-407a-b8d1-e5922c967805_1092807486","format":"AUDIO_MPEG","token":"amzn1.as-ct.v1.Domain:Application:Notifications#ACRI#DeviceTTSRenderer_0ae4a760-fee0-407a-b8d1-e5922c967805"}}} [37718] download mp3....... (3096)
- 連上Alexa
接著要嘗試連上Alexa基於HTTP2提供的語音服務。從connecting到收送第一筆http資料不可以超過10秒,超過的話會被Amazon踢掉,並且要嘗試重連。
以底下為例,我們在15.664秒時嘗試連線,在24.210秒完成SSL交握,並在24.239秒送出HTTP封包,在24.546秒時收到Amazon回傳的HTTP封包
[15664] connecting... [24210] handshake done [24239] C->S HEADERS :method: GET :path: /v20160207/directives :scheme: https :authority: avs-alexa-na.amazon.com:443 accept: */* authorization: Bearer Atza|IwEBIJYyUjyCv8luvwAI [24308] C->S HEADERS :method: POST :path: /v20160207/events :scheme: https :authority: avs-alexa-na.amazon.com:443 accept: */* authorization: Bearer Atza|IwEBIJYyUjyCv8luvwAI content-type: multipart/form-data; boundary=bo [24546] C<-S HEADERS :status: 200 access-control-allow-origin: * x-amzn-requestid: 0ed21ffffef23a7d-000069fb-00025786-cd4862c89753e4bd-3736a86a-1 content-type: multipart/related; boundary=------abcde123; type=application/json [25716] C<-S HEADERS :status: 204 access-control-allow-origin: * x-amzn-requestid: 0ed21ffffef23a7d-000069fb-00025786-cd4862c89753e4bd-3736a86a-3
- 上傳與下載語音
底下的log會出現在使用者按下按鈕開使錄音,同時Ameba會將資料上傳至Amazon。每上傳一小塊資料便會印一個逗號 “.”
[33295] C->S HEADERS :method: POST :path: /v20160207/events :scheme: https :authority: avs-alexa-na.amazon.com:443 accept: */* authorization: Bearer Atza|IwEBIJYyUjyCv8luvwAI content-type: multipart/form-data; boundary=bo [33363] upload audio................................
上傳完之後,可能會等一會兒,才會收到語音回應的內容
語音內容是邊下載邊播放,每下載一小塊資料也會印一個逗號 “.”。
我們不需要等到語音完全下載完才播放。以這份log為例, “(3096)” 代表從使用者放開按鈕直到聽到語音回音的時間是3.096秒,這個時間越短代表使用者感受到的延遲越短。
[36861] C<-S HEADERS :status: 200 access-control-allow-origin: * x-amzn-requestid: 0ed21ffffef23a7d-000069fb-00025786-cd4862c89753e4bd-3736a86a-5 content-type: multipart/related;boundary=36697d04-4d4e-4595-b6e6-a390edd0295a;start=metadata.1494256392289;type="application/json" [37551] json: {"directive":{"header":{"namespace":"Speaker","name":"SetMute","messageId":"664d09e5-d664-4948-bbac-c70e08a1424d","dialogRequestId":"64567a10-0a67-48e6-8d66-4a936508f032"},"payload":{"mute":false}}} [37606] json: {"directive":{"header":{"namespace":"SpeechSynthesizer","name":"Speak","messageId":"fe57a3bb-2280-4d88-9345-529e51cc30f2","dialogRequestId":"64567a10-0a67-48e6-8d66-4a936508f032"},"payload":{"url":"cid:DeviceTTSRenderer_0ae4a760-fee0-407a-b8d1-e5922c967805_1092807486","format":"AUDIO_MPEG","token":"amzn1.as-ct.v1.Domain:Application:Notifications#ACRI#DeviceTTSRenderer_0ae4a760-fee0-407a-b8d1-e5922c967805"}}} [37718] download mp3....... (3096)