ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

IoT-For-Beginners 消费级项目实战:在 Wio Terminal 上实现文本转语音(Text to Speech)

IoT-For-Beginners 消费级项目实战:在 Wio Terminal 上实现文本转语音(Text to Speech) IoT-For-Beginners 消费级项目实战在 Wio Terminal 上实现文本转语音Text to Speech【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners本文基于 Azure IoT 教育项目IoT-For-Beginners的 消费级 IoTConsumer IoT 单元第 23 课——设置定时器并提供语音反馈课程目录完整讲解如何在Wio Terminal上利用 Azure Speech 服务将文本合成为语音并通过 ReSpeaker 扬声器播放。你将掌握为嵌入式设备搭建语音列表查询 文本转语音两套 Azure Functions 无服务器函数、用 librosa 解决 44.1KHz 采样率重采样问题、把合成音频流式写入 SD 卡以及用 Docker 容器将函数应用部署到云端并完成 TLS 证书迁移。读完即可在本地构建出一个能开口说话的智能定时器。文本转语音Text to Speech概述上一课中你用 Azure Speech 服务把语音Speech转换成了文本Text即语音转文本Speech to Text而本课要做的是反方向的文本转语音Text to SpeechTTS——把一段文本合成为包含语音的音频数据。整个智能定时器的交互闭环是用户对着麦克风说出set a 3 minute timer→ 语音转文本 → LUIS 语言理解提取定时时长 → 设备设置定时器 →文本转语音把timer started / Times up等提示文本合成为语音播放出来。本文聚焦最后一环文本转语音。在前一课中你已经使用过 Speech 服务 SDK 完成语音转文本而同一个 Speech 服务同样可用于把文本转回语音无需额外引入新的云服务。获取可用语音列表调用文本转语音 API 时必须显式指定使用哪个语音voice来合成因为每种语言都支持一批不同的发音人不同音色、性别、风格。每种语言支持的语音列表可以通过 Speech 服务提供的 REST 接口获取。为什么不能直接在 Wio Terminal 上获取语音列表这里正是微控制器MCU的局限所在获取文本转语音服务所支持的全部语音列表返回的是一份超过 77KB 的 JSON 文档对 Wio Terminal 来说体积过大、无法处理。撰写本文时完整列表包含215 个语音每个语音由类似下面的 JSON 文档定义{ Name: Microsoft Server Speech Text to Speech Voice (en-US, AriaNeural), DisplayName: Aria, LocalName: Aria, ShortName: en-US-AriaNeural, Gender: Female, Locale: en-US, StyleList: [ chat, customerservice, narration-professional, newscast-casual, newscast-formal, cheerful, empathetic ], SampleRateHertz: 24000, VoiceType: Neural, Status: GA }这是一份Aria语音的元数据包含多个语音风格StyleList。而真正用于文本转语音请求的其实只有它的短名称ShortNameen-US-AriaNeural。因此正确的做法不是把整份列表下载到单片机解码而是编写无服务器Serverless代码由云函数按你使用的语言把列表过滤、裁剪到极小体积Wio Terminal 再通过 HTTP 调用这个云函数获取精简后的语音列表并从列表中挑选合适的语音例如第一个。任务一创建获取语音列表的无服务器函数get-voices仓库中已提供完整参考实现functions/smart-timer-trigger/get-voices。1. 打开smart-timer-trigger函数项目并激活虚拟环境在 VS Code 中打开smart-timer-trigger项目并打开终端确认虚拟环境已激活如果没有杀掉终端重新创建。2. 在local.settings.json中配置 Speech 密钥与区域打开local.settings.json加入语音 API 密钥和区域设置仓库参考文件见 local.settings.jsonSPEECH_KEY: key, SPEECH_LOCATION: location将key替换为你的 Speech 服务资源的 API 密钥将location替换为你创建 Speech 服务资源时使用的区域如eastasia、westeurope。该文件中的Values是 Azure Functions 运行时注入的环境变量Python 代码里通过os.environ[SPEECH_KEY]/os.environ[SPEECH_LOCATION]读取。3. 新建名为get-voices的 HTTP 触发器在函数应用项目根目录下的 VS Code 终端中执行func new --name get-voices --template HTTP trigger这会在项目中创建get-voices文件夹及对应的__init__.py、function.json。4. 用以下代码替换get-voices/__init__.py的全部内容import json import os import requests import azure.functions as func def main(req: func.HttpRequest) - func.HttpResponse: location os.environ[SPEECH_LOCATION] speech_key os.environ[SPEECH_KEY] req_body req.get_json() language req_body[language] url fhttps://{location}.tts.speech.microsoft.com/cognitiveservices/voices/list headers { Ocp-Apim-Subscription-Key: speech_key } response requests.get(url, headersheaders) voices_json json.loads(response.text) voices filter(lambda x: x[Locale].lower() language.lower(), voices_json) voices map(lambda x: x[ShortName], voices) return func.HttpResponse(json.dumps(list(voices)), status_code200)这段代码向语音列表端点发起一次 HTTP GET 请求。返回的语音列表是包含所有语言语音的一大块 JSON于是代码先按请求体中传入的language用filter过滤出该语言的语音再用map提取出每个语音的ShortName最后以 JSON 数组形式返回。因为文本转语音请求只需要短名称所以只返回这一个字段把数据体积从 77KB撰写时大幅压缩——例如仅返回美国英语语音时只有408 字节。 提示你可以按需调整过滤条件只挑选你偏好的语音。5. 本地运行并用 curl 测试启动函数应用后用 curl 这类工具调用测试方式与text-to-timerHTTP 触发器相同注意把语言作为 JSON 请求体传入{ language:language }将language替换为你的语言代码例如en-GB、zh-CN或en-US。任务二从 Wio Terminal 获取语音1. 打开smart-timer工程并配置函数 URL在 VS Code 中打开smart-timer工程仓库参考工程见 code-spoken-response/wio-terminal/smart-timer。打开config.h头文件添加函数应用 URLconst char *GET_VOICES_FUNCTION_URL URL;将URL替换为你函数应用中get-voicesHTTP 触发器的 URL。它和TEXT_TO_TIMER_FUNCTION_URL基本一致只是函数名从text-to-timer换成get-voices。仓库的 config.h 中三个函数 URL 并列展示如下const char *TEXT_TO_TIMER_FUNCTION_URL http://IP_ADDRESS:7071/api/text-to-timer; const char *GET_VOICES_FUNCTION_URL http://IP_ADDRESS:7071/api/get-voices; const char *TEXT_TO_SPEECH_FUNCTION_URL http://IP_ADDRESS:7071/api/text-to-speech;2. 新建src/text_to_speech.h头文件在src文件夹下新建text_to_speech.h用于定义文本转语音类。文件顶部加入以下 include 指令#pragma once #include Arduino.h #include ArduinoJson.h #include HTTPClient.h #include Seeed_FS.h #include SD/Seeed_SD.h #include WiFiClient.h #include WiFiClientSecure.h #include config.h #include speech_to_text.h其中ArduinoJson用于构造/解析 JSONHTTPClient用于调用无服务器函数Seeed_FS.h与SD/Seeed_SD.h用于把音频写入 SD 卡WiFiClient.h/WiFiClientSecure.h用于本地 HTTP 与云端 HTTPS 两种场景仓库参考实现见 text_to_speech.h其中仅保留了config.h的包含。3. 声明TextToSpeech类与全局实例class TextToSpeech { public: private: }; TextToSpeech textToSpeech;textToSpeech作为全局实例供应用其余部分使用。4. 声明 WiFi 客户端调用函数应用需要 WiFi 客户端在类的private区添加WiFiClient _client;5. 添加所选语音字段在private区添加保存选中语音的字段String _voice;6. 添加init函数获取第一个语音在public区添加一个init函数用于获取第一个语音void init() { }7. 构造携带语言的 JSON 文档语音列表接口需要语言参数在init中构造 JSON 并序列化DynamicJsonDocument doc(1024); doc[language] LANGUAGE; String body; serializeJson(doc, body);LANGUAGE是config.h中定义的宏如en-USDynamicJsonDocument(1024)预留了 1KB 动态内存——考虑到 Wio Terminal 的 SRAM 有限文档按需分配内存是这里的关键设计。8. 创建 HTTPClient 并 POST JSONHTTPClient httpClient; httpClient.begin(_client, GET_VOICES_FUNCTION_URL); int httpResponseCode httpClient.POST(body);9. 检查响应码并取出第一个语音if (httpResponseCode 200) { String result httpClient.getString(); Serial.println(result); DynamicJsonDocument doc(1024); deserializeJson(doc, result.c_str()); JsonArray obj doc.asJsonArray(); _voice obj[0].asString(); Serial.print(Using voice ); Serial.println(_voice); } else { Serial.print(Failed to get voices - error ); Serial.println(httpResponseCode); }响应为 200成功时把返回的语音短名称 JSON 数组解析出来取第 0 项存入_voice失败则打印错误码便于排查。10. 关闭 HTTP 连接httpClient.end();11. 在main.cpp中集成打开main.cpp在文件顶部添加#include text_to_speech.h并在setup函数中、speechToText.init();调用之下初始化textToSpeech.init();仓库 main.cpp 的setup顺序是连接 WiFi → 初始化 Flashsfud_init→ 初始化麦克风mic.init()→speechToText.init()→textToSpeech.init()最后打印Ready.。12. 构建、上传并观察串口输出构建代码、上传到 Wio Terminal并通过串口监视器测试确保函数应用正在运行。你将看到函数应用返回的可用语音列表以及选中的语音--- 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: CtrlC | Menu: CtrlT | Help: CtrlT followed by CtrlH --- Connecting to WiFi.. Connected! Got access token. [en-US-JennyNeural, en-US-JennyMultilingualNeural, en-US-GuyNeural, en-US-AriaNeural, en-US-AmberNeural, en-US-AnaNeural, en-US-AshleyNeural, en-US-BrandonNeural, en-US-ChristopherNeural, en-US-CoraNeural, en-US-ElizabethNeural, en-US-EricNeural, en-US-JacobNeural, en-US-MichelleNeural, en-US-MonicaNeural, en-US-AriaRUS, en-US-BenjaminRUS, en-US-GuyRUS, en-US-ZiraRUS] Using voice en-US-JennyNeural Ready.可以看到串口输出了 19 个 en-US 语音的短名称列表设备自动选中第一个en-US-JennyNeural作为合成语音。将文本转换为语音拿到语音后就可以用它把文本合成为音频了。语音列表遇到的内存限制同样存在于语音合成环节——合成返回的音频不能直接缓存在内存中因此需要把音频写入 SD 卡再通过 ReSpeaker 播放。 本课程项目前面的课程里你用 Flash 存储器保存从麦克风采集到的语音本课改用 SD 卡是因为用 Seeed 音频库从 SD 卡播放音频更简单。采样率不匹配44.1KHz 限制还有一个必须考虑的限制Speech 服务返回的音频格式与 Wio Terminal 支持的格式不一致。与完整计算机不同微控制器的音频库对音频格式的支持非常有限。例如能在 ReSpeaker 上播放声音的Seeed Arduino Audio 库只支持 44.1KHz 采样率的音频而 Azure Speech 服务虽然能以多种格式提供音频但只用8KHz、16KHz、24KHz 和 48KHz这些采样率。这意味着音频必须被重采样到 44.1KHz——这项工作对 Wio Terminal 来说资源开销过大尤其是内存。处理这类数据变换时更好的做法是交给无服务器代码特别是数据本身就来自 Web 调用时Wio Terminal 调用一个无服务器函数把要转换的文本传过去函数内既调用 Speech 服务完成文本转语音又顺便把音频重采样到所需采样率最后把 Wio Terminal 需要的音频形式返回由设备存入 SD 卡、经 ReSpeaker 播放。这样把重采样的计算压力从单片机转移到了云端。任务三创建文本转语音的无服务器函数text-to-speech仓库参考实现见 functions/smart-timer-trigger/text-to-speech。1. 新建text-to-speechHTTP 触发器在smart-timer-trigger项目根目录的 VS Code 终端中执行func new --name text-to-speech --template HTTP trigger2. 在requirements.txt中加入 librosalibrosalibrosa随后安装依赖pip install -r requirements.txt⚠️ 如果你使用 Linux包括 Raspberry Pi OS可能需要先安装libsndfilesudo apt update sudo apt install libsndfile1-dev3. 用访问令牌替代 API 密钥与获取语音列表不同调用文本转语音 REST API 时不能直接使用 API 密钥必须先向令牌端点换取一个访问令牌Access Token再用令牌认证 TTS 请求。打开text-to-speech文件夹中的__init__.py用以下代码替换全部内容import io import os import requests import librosa import soundfile as sf import azure.functions as func location os.environ[SPEECH_LOCATION] speech_key os.environ[SPEECH_KEY] def get_access_token(): headers { Ocp-Apim-Subscription-Key: speech_key } token_endpoint fhttps://{location}.api.cognitive.microsoft.com/sts/v1.0/issuetoken response requests.post(token_endpoint, headersheaders) return str(response.text)这段代码先从设置中读取区域和语音密钥作为模块级常量然后定义get_access_token函数向区域专属的 STS 令牌端点发起 POST把 API 密钥放在Ocp-Apim-Subscription-Key头中返回响应体即访问令牌。4. 添加主处理函数SSML 合成 librosa 重采样在这段代码下方继续添加playback_format riff-48khz-16bit-mono-pcm def main(req: func.HttpRequest) - func.HttpResponse: req_body req.get_json() language req_body[language] voice req_body[voice] text req_body[text] url fhttps://{location}.tts.speech.microsoft.com/cognitiveservices/v1 headers { Authorization: Bearer get_access_token(), Content-Type: application/ssmlxml, X-Microsoft-OutputFormat: playback_format } ssml fspeak version\1.0\ xml:lang\{language}\ ssml fvoice xml:lang\{language}\ name\{voice}\ ssml text ssml /voice ssml /speak response requests.post(url, headersheaders, datassml.encode(utf-8)) raw_audio, sample_rate librosa.load(io.BytesIO(response.content), sr48000) resampled librosa.resample(raw_audio, sample_rate, 44100) output_buffer io.BytesIO() sf.write(output_buffer, resampled, 44100, PCM_16, formatwav) output_buffer.seek(0) return func.HttpResponse(output_buffer.read(), status_code200)关键点拆解playback_format riff-48khz-16bit-mono-pcm声明了期望的输出格式16 位、48KHz、单声道、RIFFWAV封装通过X-Microsoft-OutputFormat头传给 REST APIHTTP 触发器从 JSON 请求体中提取language、voice、text三个字段构造 SSML语音合成标记语言speak根节点声明语言voice name...指定发音人文本放在其中携带Authorization: Bearer token访问令牌调用 TTS REST API用librosa.load(..., sr48000)以 48KHz 载入返回的音频再通过librosa.resample重采样到44.1KHz最后用soundfilesf.write把重采样后的音频以 16 位 PCM WAV 格式写入二进制缓冲区返回。5. 本地运行并用 curl 测试本地运行函数应用或部署到云端用 curl 调用。请求体必须包含语言、语音和文本三个字段{ language: language, voice: voice, text: text }language填语言代码如en-GB、zh-CNvoice填你想用的语音短名称text填要转换为语音的文本。你可以把输出保存成文件用任意能播放 WAV 的音频播放器试听。例如用 Jenny Neural 语音把 Hello 合成为美式英语语音函数应用本地运行时curl -X GET http://localhost:7071/api/text-to-speech \ -H Content-Type: application/json \ -o hello.wav \ -d { language:en-US, voice: en-US-JennyNeural, text: Hello }这会把音频保存为当前目录下的hello.wav。任务四从 Wio Terminal 获取语音1. 配置TEXT_TO_SPEECH_FUNCTION_URL打开config.h添加函数应用 URLconst char *TEXT_TO_SPEECH_FUNCTION_URL URL;将URL替换为函数应用中text-to-speechHTTP 触发器的 URL它与TEXT_TO_TIMER_FUNCTION_URL一致仅函数名不同。2. 添加convertTextToSpeech方法打开text_to_speech.h在TextToSpeech类的public区添加void convertTextToSpeech(String text) { }3. 构造请求 JSON在方法内添加代码生成要发给函数应用的 JSONDynamicJsonDocument doc(1024); doc[language] LANGUAGE; doc[voice] _voice; doc[text] text; String body; serializeJson(doc, body);这里把语言、语音来自_voice字段和文本写入 JSON 文档并序列化为字符串。4. 调用函数应用HTTPClient httpClient; httpClient.begin(_client, TEXT_TO_SPEECH_FUNCTION_URL); int httpResponseCode httpClient.POST(body);创建 HTTPClient 并用 JSON 文档向text-to-speechHTTP 触发器发起 POST。5. 把返回的二进制音频流式写入 SD 卡调用成功时把函数应用返回的原始二进制数据流式写入 SD 卡文件if (httpResponseCode 200) { File wav_file SD.open(SPEECH.WAV, FILE_WRITE); httpClient.writeToStream(wav_file); wav_file.close(); } else { Serial.print(Failed to get speech - error ); Serial.println(httpResponseCode); }代码检查响应码200成功时把二进制数据流式写入 SD 卡根目录下的SPEECH.WAV文件。writeToStream是HTTPClient提供的流式写接口避免把整段音频读进内存——这正是应对内存限制的关键手法。6. 关闭 HTTP 连接在方法末尾httpClient.end();7. 在say函数中触发合成在main.cpp的say函数末尾添加一行把要说的文本转换为音频仓库 main.cpp 中完整实现如下void say(String text) { Serial.println(text); textToSpeech.convertTextToSpeech(text); }say是整个语音反馈的入口定时器启动时调用say(begin_message)播报timer started定时器到期时通过timer.in(total_seconds * 1000, timerExpired, ...)注册的回调timerExpired调用say(end_message)播报Times up on your ... timer。播放音频在本文对应的课程节点中通过 ReSpeaker 播放SPEECH.WAV的内容被标注为Coming soon即将推出仓库代码中尚未包含完整的播放实现。可以参考课程说明中用 Seeed 音频库从 SD 卡播放音频更容易的提示在后续课程内容中补充播放环节。将函数应用部署到云端为什么需要 Docker 容器本地运行时librosa这个 Pip 包在 Linux 上有一个默认不会安装的依赖库即libsndfile必须在函数应用运行前先安装。而函数应用是无服务器的——没有你可以自己管理的服务器自然也就无法预先安装这个库。解决方案是用 Docker 容器部署函数应用这个容器由云平台按需部署每当需要为函数应用启动一个新实例时例如需求超过可用资源时或函数应用长时间未用被关闭后再被唤起时都会基于你的自定义镜像拉起从而保证libsndfile等依赖始终就绪。创建函数应用并通过 Docker 部署的完整步骤可参考 Microsoft Docs 中使用 Linux 自定义容器创建函数的文档。部署后把 Wio Terminal 代码切换到 HTTPS部署完成后函数应用会提供 HTTPS 地址Wio Terminal 代码需要做以下迁移1. 把 Azure Functions 证书加入config.hconst char *FUNCTIONS_CERTIFICATE -----BEGIN CERTIFICATE-----\r\n MIIFWjCCBEKgAwIBAgIQDxSWXyAgaZlP1ceseIlB4jANBgkqhkiG9w0BAQsFADBa\r\n MQswCQYDVQQGEwJJRTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJl\r\n clRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTIw\r\n MDcyMTIzMDAwMFoXDTI0MTAwODA3MDAwMFowTzELMAkGA1UEBhMCVVMxHjAcBgNV\r\n BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEgMB4GA1UEAxMXTWljcm9zb2Z0IFJT\r\n QSBUTFMgQ0EgMDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqYnfP\r\n mmOyBoTzkDb0mfMUUavqlQo7Rgb9EUEf/lsGWMk4bgj8T0RIzTqk970eouKVuL5R\r\n IMW/snBjXXgMQ8ApzWRJCZbar879BV8rKpHoAW4uGJssnNABf2n17j9TiFy6BWy\r\n IhVnFILyLNKW2M3zK9gheiWa2uACKhuvgCca5Vw/OQYErEdG7LBEzFnMzTmJcli\r\n W1iCdXby/vI/OxbfqkKD4zJtm45DJvC9DhhpzqvLMiK5uo/aXSJYSqhoIEpz\r\n rErHwuAlKuHFtEjSeeku8eR3Z5ND9BSqc6JtLqb0bjOHPm5dSRrgt4nnil75bj\r\n c9j3lWXpBb9PXP9Sp/nPCKnTQmZwHGjUnqlO9ebAVQD47ZisFonnDAmjrZNVqEX\r\n F3p7laEHrFMxttYuD81BdOzxAbL9Rb/8MeFGQjE2Qx65qgVfhHRsYuuD9dUw/3w\r\n ZAhq05yO6nk07AM9cAbNtRoEcdZcLCHfMDcbkXKNs5DJncCqXAN6LhXVERCw/us\r\n G2MmCMLSIx9/kwt8bwhUmitOXc6fpT7SmFvRAtvxg84wUkg4Y/Gx0j0z6StSeN\r\n 0EJz150jaHG6WV4HUqaWTb98Tm90IgXAU4AW2GBOlzFPiU5IY9jteXC2Q6yC/Zp\r\n TL1LAcnL3Qa/OgLrHN0wiw1KFGD51WRPQ0Sh7QIDAQABo4IBJTCCASEwHQYDVR0O\r\n BBYEFLV2DDARzseSQk1Mx1wsyKkM6AtkMB8GA1UdIwQYMBaAFOWdWTCCR1jMrPoI\r\n VDaGezq1BE3wMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYI\r\n KwYBBQUHAwIwEgYDVR0TAQH/BAgwBgEB/wIBADA0BggrBgEFBQcBAQQoMCYwJAYI\r\n KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTA6BgNVHR8EMzAxMCg\r\n LaArhilodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vT21uaXJvb3QyMDI1LmNybDAq\r\n BgNVHSAEIzAhMAgGBmeBDAECATAIBgZngQwBAgIwCwYJKwYBBAGCNyoBMA0GCSqG\r\n SIb3DQEBCwUAA4IBAQCfK76SZ1vae4qt6PdTQUO7bYNFUHR5hXcA2D59CJWnEj5\r\n na7aKzyowKvQupW4yMH9fGNxtsh6iJswRqOOfZYC4/giBO/gNsBvwr8uDW7t1nYo\r\n DYGHPpvnpxCM2mYfQFHq576/TmeYu1RZY29C4w8xYBlkAA8mDJfRhMCmehk7cN5F\r\n JtyWRj2cZj/hOoI45TYDBChXpOlLZKIYiG1giY16vhCRi6zmPzEwvtk156N6cGS\r\n Vm44jTQ/rs1sa0JSYjzUaYngoFdZC4OfxnIkQvUIA4TOFmPzNPEFdjcZsgbeEz4T\r\n cGHTBPK4R28F44qIMCtHRV55VMX53ev6P3hRddJb\r\n -----END CERTIFICATE-----\r\n;这是用于验证 Azure Functions 服务端证书的 CA 证书链是 TLS 握手验证所必需的公钥材料。Wio Terminal 的 mbedTLS 栈需要把根证书以 PEM 格式内嵌到固件中才能建立可信的 HTTPS 连接。2. 将WiFiClient.h包含全部改为WiFiClientSecure.h3. 把所有WiFiClient字段改为WiFiClientSecure4. 在每个含WiFiClientSecure字段的类中添加构造函数并设置证书_client.setCACert(FUNCTIONS_CERTIFICATE);在构造函数里调用setCACert把内嵌的 CA 证书绑定到安全客户端上此后HTTPClient便可通过该安全客户端访问 HTTPS 的云端函数应用。这组证书迁移操作适用于TextToSpeech、SpeechToText等所有需要调用云端函数的类。 本地开发时函数应用走http://明文地址用普通WiFiClient即可部署到云端后必须切换为WiFiClientSecure并携带证书。仓库 config.h 中还保留了本地调用 Speech 服务令牌端点所需的TOKEN_CERTIFICATE与SPEECH_CERTIFICATE供对比参考。小结与参考实现本文完整复现了 Wio Terminal 文本转语音的整条链路语音列表裁剪用get-voices无服务器函数把 77KB 的全量语音列表按语言过滤成几百字节的短名称数组get-voices/init.py云端合成与重采样用text-to-speech无服务器函数申请访问令牌、构造 SSML、调用 TTS REST API再用 librosa 把 48KHz 音频重采样为 44.1KHz WAVtext-to-speech/init.py设备端播放准备Wio Terminal 通过TextToSpeech类获取语音、合成并把音频流式写入 SD 卡SPEECH.WAVtext_to_speech.h云端部署用 Docker 容器解决librosa的libsndfile依赖并把 Wio Terminal 切换到 HTTPS WiFiClientSecure CA 证书。工程依赖platformio.ini主要包括Seeed Arduino FS、Seeed Arduino rpcWiFi、Seeed_Arduino_mbedtls、ArduinoJson 6.17.3、arduino-timer 2.3.0等库整体在 PlatformIO Arduino 框架下构建板型seeed_wio_terminal。想动手复现的读者可以直接对照 code-spoken-response 目录中的函数端与设备端代码结合本文步骤逐步搭建自己的会说话的智能定时器。【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表