跳至主要内容

設定計時器 - Wio Terminal

在本課程的這部分,你將調用你的無伺服器代碼來理解語音,並根據結果在你的 Wio Terminal 上設置計時器。

設定計時器

從語音轉文字調用返回的文本需要發送到你的無伺服器代碼中,由 LUIS 處理,並返回計時器的秒數。這個秒數可以用來設置計時器。

微控制器在 Arduino 中本身不支持多線程,所以沒有像在 Python 或其他高級語言中那樣的標準計時器類。相反,你可以使用計時器庫,這些庫通過在 loop 函數中測量經過的時間,並在時間到時調用函數來工作。

任務 - 將文本發送到無伺服器函數

  1. 如果尚未打開,請在 VS Code 中打開 smart-timer 項目。

  2. 打開 config.h 頭文件,並添加你的函數應用的 URL:

    const char *TEXT_TO_TIMER_FUNCTION_URL = "<URL>";

    <URL> 替換為你在上一課的最後一步中獲得的函數應用的 URL,指向運行函數應用的本地機器的 IP 地址。

  3. src 文件夾中創建一個名為 language_understanding.h 的新文件。這將用於定義一個類,將識別的語音發送到你的函數應用,並使用 LUIS 轉換為秒數。

  4. 在此文件的頂部添加以下內容:

    #pragma once

    #include <Arduino.h>
    #include <ArduinoJson.h>
    #include <HTTPClient.h>
    #include <WiFiClient.h>

    #include "config.h"

    這包括了一些需要的頭文件。

  5. 定義一個名為 LanguageUnderstanding 的類,並聲明這個類的一個實例:

    class LanguageUnderstanding
    {
    public:
    private:
    };

    LanguageUnderstanding languageUnderstanding;
  6. 要調用你的函數應用,你需要聲明一個 WiFi 客戶端。將以下內容添加到類的 private 部分:

    WiFiClient _client;
  7. public 部分,聲明一個名為 GetTimerDuration 的方法來調用函數應用:

    int GetTimerDuration(String text)
    {
    }
  8. GetTimerDuration 方法中,添加以下代碼來構建要發送到函數應用的 JSON:

    DynamicJsonDocument doc(1024);
    doc["text"] = text;

    String body;
    serializeJson(doc, body);

    這將傳遞給 GetTimerDuration 方法的文本轉換為以下 JSON:

    {
    "text" : "<text>"
    }

    其中 <text> 是傳遞給函數的文本。

  9. 在此下面,添加以下代碼來調用函數應用:

    HTTPClient httpClient;
    httpClient.begin(_client, TEXT_TO_TIMER_FUNCTION_URL);

    int httpResponseCode = httpClient.POST(body);

    這會向函數應用發出一個 POST 請求,傳遞 JSON 主體並獲取響應代碼。

  10. 在此下面添加以下代碼:

    int seconds = 0;
    if (httpResponseCode == 200)
    {
    String result = httpClient.getString();
    Serial.println(result);

    DynamicJsonDocument doc(1024);
    deserializeJson(doc, result.c_str());

    JsonObject obj = doc.as<JsonObject>();
    seconds = obj["seconds"].as<int>();
    }
    else
    {
    Serial.print("Failed to understand text - error ");
    Serial.println(httpResponseCode);
    }

    這段代碼檢查響應代碼。如果是 200(成功),則從響應主體中檢索計時器的秒數。否則,將錯誤發送到串行監視器,並將秒數設置為 0。

  11. 在此方法的末尾添加以下代碼來關閉 HTTP 連接並返回秒數:

    httpClient.end();

    return seconds;
  12. main.cpp 文件中,包含這個新的頭文件:

    #include "speech_to_text.h"
  13. processAudio 函數的末尾,調用 GetTimerDuration 方法來獲取計時器的持續時間:

    int total_seconds = languageUnderstanding.GetTimerDuration(text);

    這將從調用 SpeechToText 類的文本轉換為計時器的秒數。

任務 - 設置計時器

這個秒數可以用來設置計時器。

  1. platformio.ini 文件中添加以下庫依賴項來添加一個設置計時器的庫:

    contrem/arduino-timer @ 2.3.0
  2. main.cpp 文件中添加這個庫的包含指令:

    #include <arduino-timer.h>
  3. processAudio 函數上方,添加以下代碼:

    auto timer = timer_create_default();

    這段代碼聲明了一個名為 timer 的計時器。

  4. 在此下面添加以下代碼:

    void say(String text)
    {
    Serial.print("Saying ");
    Serial.println(text);
    }

    這個 say 函數最終會將文本轉換為語音,但現在它只會將傳遞的文本寫入串行監視器。

  5. say 函數下面,添加以下代碼:

    bool timerExpired(void *announcement)
    {
    say((char *)announcement);
    return false;
    }

    這是一個回調函數,當計時器到期時會被調用。它會在計時器到期時傳遞一個消息。計時器可以重複,這可以通過這個回調的返回值來控制 - 這裡返回 false,告訴計時器不要再次運行。

  6. processAudio 函數的末尾添加以下代碼:

    if (total_seconds == 0)
    {
    return;
    }

    int minutes = total_seconds / 60;
    int seconds = total_seconds % 60;

    這段代碼檢查總秒數,如果是 0,則從函數調用中返回,這樣就不會設置計時器。然後將總秒數轉換為分鐘和秒。

  7. 在此代碼下面,添加以下代碼來創建一個在計時器開始時要說的消息:

    String begin_message;
    if (minutes > 0)
    {
    begin_message += minutes;
    begin_message += " minute ";
    }
    if (seconds > 0)
    {
    begin_message += seconds;
    begin_message += " second ";
    }

    begin_message += "timer started.";
  8. 在此下面,添加類似的代碼來創建一個在計時器到期時要說的消息:

    String end_message("Times up on your ");
    if (minutes > 0)
    {
    end_message += minutes;
    end_message += " minute ";
    }
    if (seconds > 0)
    {
    end_message += seconds;
    end_message += " second ";
    }

    end_message += "timer.";
  9. 在此之後,說出計時器開始的消息:

    say(begin_message);
  10. 在此函數的末尾,啟動計時器:

    timer.in(total_seconds * 1000, timerExpired, (void *)(end_message.c_str()));

    這會觸發計時器。計時器是以毫秒為單位設置的,所以總秒數乘以 1,000 轉換為毫秒。timerExpired 函數作為回調傳遞,並將 end_message 作為參數傳遞給回調。這個回調只接受 void * 參數,所以字符串被適當地轉換。

  11. 最後,計時器需要 tick,這是在 loop 函數中完成的。在 loop 函數的末尾添加以下代碼:

    timer.tick();
  12. 構建這個代碼,將其上傳到你的 Wio Terminal,並通過串行監視器進行測試。當你在串行監視器中看到 Ready 時,按下 C 按鈕(左側最靠近電源開關的那個),然後說話。將捕獲 4 秒的音頻,轉換為文本,然後發送到你的函數應用,並設置計時器。確保你的函數應用在本地運行。

    你將看到計時器開始和結束的時間。

    --- Available filters and text transformations: colorize, debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time
    --- More details at http://bit.ly/pio-monitor-filters
    --- Miniterm on /dev/cu.usbmodem1101 9600,8,N,1 ---
    --- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
    Connecting to WiFi..
    Connected!
    Got access token.
    Ready.
    Starting recording...
    Finished recording
    Sending speech...
    Speech sent!
    {"RecognitionStatus":"Success","DisplayText":"Set a 2 minute and 27 second timer.","Offset":4700000,"Duration":35300000}
    Set a 2 minute and 27 second timer.
    {"seconds": 147}
    2 minute 27 second timer started.
    Times up on your 2 minute 27 second timer.

💁 你可以在 code-timer/wio-terminal 文件夾中找到這段代碼。

😀 你的計時器程序成功了!