找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3042

积分

0

好友

394

主题
发表于 3 小时前 | 查看: 0| 回复: 0

项目通过 ESP32 联网获取心知天气的实时数据,将文本信息经 Web 服务器转发至 PC 端,并调用科大讯飞 TTS 接口生成语音文件,最终由 ESP32 下载音频并通过 I2S + DAC 播放出来。

整个实现过程横跨嵌入式联网、HTTP 通信、JSON 解析、服务器文件操作以及音频播放链路,并不是单一模块的堆砌,而是一次从“数据获取”到“语音输出”的完整系统实践。本文将围绕整体设计思路、关键代码实现以及实际踩坑经验,对该项目进行一次较为完整的记录与复盘。

ESP32-S3-BOX_Lite开发板系统架构图

项目介绍

本次实现的是一个基于 ESP32-S3-BOX_Lite 开发板,使用 MicroPython 编程的天气信息语音播报系统。通过搭建一个小型文件操作服务器,并集成心知天气 API 与科大讯飞 TTS 合成服务,完成了一个从数据采集到语音输出的完整链路。

设计思路

系统设计主要分为硬件端(ESP32)和软件端(PC服务器)两部分,具体流程如下:

  1. Wi-Fi连接:ESP32 连接至 Wi-Fi 网络,获取互联网访问能力。
  2. 获取天气:ESP32 向心知天气 API 发送 HTTP 请求,获取 JSON 格式的实时天气数据。
  3. 转发数据:ESP32 将解析后的天气文本,通过 HTTP POST 请求发送给本地运行的 Web 服务器。
  4. 服务器写入:Web 服务器接收文本,并将其写入本地的 input.txt 文件。
  5. TTS转换:Web 服务器读取 input.txt 内容,调用科大讯飞 TTS API,将文本转换为音频文件。
  6. 文件托管:Web 服务器将生成的音频文件(WAV格式)存储在特定位置,并提供下载 URL。
  7. 下载音频:ESP32 向服务器发送 HTTP GET 请求,下载生成的音频文件到本地存储。
  8. 硬件连接:ESP32 通过 I2S 总线连接外部 DAC 芯片(ES8156)和功放(NS4150)。
  9. 音频播放:ESP32 从存储中读取音频数据,通过 I2S 发送给 DAC,经功放驱动扬声器播放。
  10. 循环执行:播放完毕后,等待一段时间,重新开始从步骤1执行,实现定时播报。

硬件介绍

项目核心硬件为 ESP32-S3-BOX_Lite 开发板,主控为 ESP32-S3,并集成了外部 DAC 模块(ES8156)和音频功放(NS4150)以及一个扬声器。本项目实现的功能相对简单,每20秒自动获取并播报一次天气,因此未使用板载的按键、ST7789显示屏及麦克风阵列功能。

该板卡硬件资源丰富,值得一提的是其 ADC 按键设计:三个按键通过一个 ADC 通道的电压值来区分,实现不同的按键功能,这是一个非常巧妙的设计。

ES8156音频编解码器芯片特写
NS4150音频功放芯片特写

软件流程与代码实现

1. 系统流程图

下图清晰地展示了从 ESP32 联网到最终播放语音的完整数据流与处理流程。

天气语音播报系统工作流程图

2. 主要代码片段说明

以下是各功能模块的核心代码,均已添加详细注释。在实际项目中,建议将不同功能模块化到单独的文件中。

1)连接 Wi-Fi

# WIFI配置(请替换为自己的Wi-Fi信息)
# ssid = ‘your_ssid’
# password =‘your_password’

def ConnectNet(ssid ,password):
    mynetwork=network.WLAN(network.STA_IF) # 配置为站点模式
    mynetwork.active(False) # 先关闭WiFi
    mynetwork.active(True)  # 再打开WiFi
    mynetwork.connect(ssid,password) # 连接网络,上述步骤为避免某些异常

    while True:
        if(mynetwork.isconnected()):
            break
        else :
            time.sleep(1)
    #print(mynetwork.ifconfig()) # 可打印IP地址等信息
    print(“WIFI is connect”) # 串口打印连接成功信息

2)访问心知天气 API 并解析 JSON

# 请求天气API(请替换为自己的API Key和城市)
result1=urequests.get(‘https://api.seniverse.com/v3/weather/now.json?key=YOUR_API_KEY&location=nanjing&language=zh-Hans&unit=c’)
j1=ujson.loads(result1.text) # 解析JSON响应

# 提取所需字段
city = j1[‘results’][0][‘location’][‘name’]
weather = j1[‘results’][0][‘now’][‘text’]
temp = j1[‘results’][0][‘now’][‘temperature’]
fresh_time = j1[‘results’][0][‘last_update’]

print(city)
print(weather)
print(temp,“度”)
print(fresh_time)

3)与服务器通信:发送文本与下载音频

def send_content_to_server(content):
    url = “http://SERVER_IP:PORT/receive_content”  # 替换为你的服务器IP和端口
    content_utf8 = content.encode(‘utf-8’)
    headers = {‘Content-Type’: ‘text/plain’}
    response = urequests.post(url, data=content_utf8, headers=headers)
    print(“Content sent to server. Response status:“, response.status_code)
    response.close()

def download_wav_file(url, save_path):
    response = urequests.get(url)
    with open(save_path, ‘wb’) as file:
        file.write(response.content)

# 配置服务器音频文件URL和本地保存路径
server_wav_url = ‘http://SERVER_IP:PORT/get_wav_file’
esp32_wav_path = ‘/output.wav’

4)I2S 与 DAC 初始化配置

sck_pin = Pin(17)  # 串行时钟输出,I2S_SCLK
ws_pin = Pin(47)   # 字时钟,I2S_LRCK
sd_pin = Pin(15)   # 串行数据输出,GPIO15=I2S_DAC_SDIN
pa_pin = Pin(46, Pin.OUT)  # 创建用于控制功率放大器的Pin对象
pa_pin.value(1)  # 将引脚设置为高电平以启用放大器

print(“ADC IS OK”) # 调试信息

audio_out = I2S(0,sck=sck_pin,
            ws=ws_pin,
            sd=sd_pin,
            mode=I2S.TX,
            bits=16,
            format=I2S.MONO,
            rate=16000,
            ibuf=20000)
print(“IIS IS OK”) # 调试信息

5)主循环函数

while True:
    # 1. 获取天气
    result1=urequests.get(‘https://api.seniverse.com/v3/weather/now.json?key=YOUR_KEY&location=nanjing&language=zh-Hans&unit=c’)
    j1=ujson.loads(result1.text)
    city = j1[‘results’][0][‘location’][‘name’]
    weather = j1[‘results’][0][‘now’][‘text’]
    temp = j1[‘results’][0][‘now’][‘temperature’]
    fresh_time = j1[‘results’][0][‘last_update’]
    print(city)
    print(weather)
    print(temp,“度”)
    print(fresh_time)

    # 2. 拼接并发送文本到服务器
    content = city + “ “ + weather + “ “ + str(temp) + “度 “
    print(content)
    send_content_to_server(content)

    # 3. 从服务器下载音频文件
    download_wav_file(server_wav_url, esp32_wav_path)
    print(“download is ok”)

    # 4. 播放音频
    wavtempfile = “output.wav”
    wav = open(wavtempfile,‘rb’)
    print(‘播放音频’)
    start_time = time.ticks_us()
    buf = wav.read()
    bufhead = 44  # WAV文件头长度
    bufsize = len(buf) - bufhead
    bufcap = 4096 # 缓冲区大小
    # 计算总时长 (微秒): 数据字节数 / (采样率 * 位宽 * 通道数 / 8 / 1e6)
    all_time = bufsize / 0.032
    bunum = 1
    bufstart = bufhead
    while bufsize:
        bufend = bufcap * bunum
        if bufend > len(buf):
            bufend = len(buf)
        bufwrite = buf[bufstart:bufend]
        num_written = audio_out.write(bufwrite)
        bufsize -= len(bufwrite)
        bufstart = bufend
        bunum = bunum + 1
    # 等待音频播放完毕
    while 1:
        end_time = time.ticks_us()
        if (end_time - start_time) > all_time:
            audio_out.deinit() # 取消初始化 I2S 总线
            break
    wav.close()
    print(“OVER”) # 单次播报结束
    time.sleep(20) # 等待20秒后进入下一轮循环

6)服务器端代码 (Python on PC)
此部分代码运行在PC的PyCharm等环境中,负责接收文本、调用讯飞TTS并生成音频文件。以下为关键逻辑摘要,完整代码较长,核心是搭建一个简单的HTTP服务器并集成讯飞WebSocket TTS客户端。

服务器需要实现两个端点:

  • /receive_content (POST): 接收ESP32发来的天气文本,写入input.txt
  • /get_wav_file (GET): 提供output.wav音频文件的下载。

TTS转换部分,使用科大讯飞官方WebSocket API示例代码(需自行填入APPID、APIKey、APISecret),其工作流程为:读取input.txt -> 调用讯飞API生成PCM数据 -> 将PCM转换为WAV格式并保存为output.wav

功能展示

1. ESP32 串口输出
串口日志显示了完整的执行流程:Wi-Fi连接成功、获取到的天气信息(南京、多云、27度)、与服务器的通信状态、音频下载成功以及播放开始与结束的标识。

ESP32串口终端运行日志

2. 服务器访问日志
PC端的服务器日志显示,成功处理了来自ESP32的文本写入请求和后续的音频文件下载请求,状态码均为200。

本地Web服务器访问日志

3. 文本文件写入
服务器成功将接收到的天气文本“南京 多云 27度”写入到了本地的input.txt文件中。

input.txt文本文件内容

4. 讯飞TTS服务调用
在讯飞开放平台的服务管理界面,可以查看到TTS接口的调用记录和用量情况。

科大讯飞TTS服务用量管理界面

心得体会与踩坑记录

  1. 开发环境选择:乐鑫官方的IDF(C语言)环境功能强大,但配置过程对新手可能有一定门槛。本次因环境配置问题,临时转向了MicroPython,其上手速度快,适合原型验证。但对于追求极致性能和底层控制的场景,仍建议使用官方IDF。
  2. 系统架构设计:本项目采用“ESP32 + PC服务器”的架构,将计算密集的TTS任务卸载到服务器端,降低了嵌入式端的资源压力。这种客户端-服务器的思维在物联网项目中很常见,也让我实践了HTTP通信和简单的服务端编程。
  3. 知识整合:最大的收获在于将多个独立的技术点(网络连接、API调用、JSON解析、文件操作、音频播放)串联成了一个可工作的系统。特别是搭建和使用简易Web服务器进行数据中转的思路,为未来更复杂的物联网项目提供了参考。
  4. MicroPython的局限:虽然开发便捷,但MicroPython在硬件底层操作、内存管理以及第三方库生态上不如C/IDF灵活。例如,直接将讯飞TTS的Python SDK移植到ESP32 MicroPython中可能会遇到依赖和性能问题。
  5. 总结:这是一个很好的嵌入式全栈入门实践。它不仅仅关乎代码,更关乎系统性的设计和问题分解能力。在云栈社区也有许多开发者分享过类似的嵌入式与物联网项目,这种从传感器到云端再到执行器的完整链路,是物联网应用的典型模式。建议初学者可以从这样的项目入手,理解每个模块的作用,再逐步深入优化和扩展。



上一篇:PaddleOCR-VL-1.5 模型解析:0.9B参数量高效文档理解,支持跨页表格与多语种识别
下一篇:Spring Boot 开发中 Controller 拆分指南:以业务语义为决策核心
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-2-1 21:53 , Processed in 0.286812 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表