ESP 8266+ TTS 实现对讲机语音播报 时间 和信息

#include <SoftwareSerial.h>

#include <ESP8266WiFi.h>

#include <NTPClient.h>

#include <WiFiUdp.h>

#include "UTF8ToGB2312.h"

//需要改造对讲机耳机连接至 TTS 音响接口

#define SERIAL2_TX 12 //接TTS RX

#define SERIAL2_RX 13 //接TTS TX

#define TTS_MAX_DATA_LENGTH 1024 // 最大支持4K字节

#define CONTROL_PIN 14 // 定义控制引脚为14 接对讲机 PPT

// WiFi设置

const char* ssid = "你的wifi名称";

const char* password = "你的wifi密码";

// NTP时间服务器设置

WiFiUDP ntpUDP;

NTPClient timeClient(ntpUDP, "pool.ntp.org", 28800, 60000); // 北京时间:UTC+8 (28800秒)

EspSoftwareSerial::UART Serial2;

//UTF8ToGB2312 GB; // 实例化UTF8转GB2312转换器

// 枚举类型表示TTS工作状态

enum class TTSState {

IDLE = 0,

BUSY = 1,

ERROR = 2

};

int lastMinute = -1; // 记录上一次检查的分钟数

// 定义播放时间范围(7:00-21:00)

const int START_HOUR = 7;

const int END_HOUR = 21;

// 检查当前时间是否在允许的播放范围内

bool isWithinPlayTime() {

int currentHour = timeClient.getHours();

return (currentHour >= START_HOUR && currentHour <= END_HOUR);

}

void setup() {

digitalWrite(CONTROL_PIN, LOW);

Serial2.begin(115200, SWSERIAL_8N1, SERIAL2_RX, SERIAL2_TX, false);

if (!Serial2) {

Serial.println("Invalid EspSoftwareSerial pin configuration, check config");

while (1) delay(1000); // 保持错误状态

}

Serial.begin(115200);

// 连接WiFi

Serial.println("Connecting to WiFi");

WiFi.begin(ssid, password);

while (WiFi.status() != WL_CONNECTED) {

delay(500);

Serial.print(".");

}

Serial.println("\nWiFi connected");

// 初始化NTP客户端

timeClient.begin();

timeClient.update();

delay(3000); // 减少CPU占用

speech("系统初始化成功,当前时间同步完成");

}

void loop() {

// 更新时间

timeClient.update();

// 获取当前时间的分钟数

int currentMinute = timeClient.getMinutes();

int currentHour = timeClient.getHours();

String fenzhong = "";

// // 构建报时消息

// 检查是否为整点或半点,并且与上次检查的分钟数不同

// 检查是否在允许的播放时间段内

if (isWithinPlayTime()) {

if ((currentMinute == 0 || currentMinute == 30) && currentMinute != lastMinute) {

lastMinute = currentMinute;

// 配置控制引脚

pinMode(CONTROL_PIN, OUTPUT);

digitalWrite(CONTROL_PIN, HIGH); // 将引脚14设置为高电平

Serial.println("Control pin 14 set to HIGH");

if(currentMinute == 0){

fenzhong = "整";

}else{

fenzhong = String(currentMinute)+"分";

}

// 添加固定播报内容

String broadcastMessage = "s6m0ring_3现在时刻w0n2"+String(currentHour)+"点"+fenzhong+"w0这里是亳州市业余无线电中继台w0n1439.750下差-5亚音n188.5w0世界药都w0中国亳州w0欢迎您";

// 播报整点/半点消息

speech(broadcastMessage);

// 播报完成后将控制引脚设为低电平

digitalWrite(CONTROL_PIN, LOW);

Serial.println("Control pin 14 set to LOW after speech");

}

}

else{

Serial.print("当前时间 ");

Serial.print(currentHour);

Serial.print(":");

Serial.print(currentMinute);

Serial.println(" 不在播放范围内,跳过语音播报");

}

// 其他任务处理...

delay(3000); // 减少CPU占用

}

// 发送命令到TTS模块

bool sendTTSCommand(const uint8_t* command, uint16_t length) {

if (!command || length == 0 || length > TTS_MAX_DATA_LENGTH) {

Serial.println("Error: Invalid command data");

return false;

}

for (uint16_t i = 0; i < length; i++) {

Serial2.write(commandi);

}

return true;

}

// 查询TTS合成工作状态

TTSState getTTSState() {

const uint8_t cmd\[\] = {0xFD, 0x00, 0x01, 0x21};

sendTTSCommand(cmd, sizeof(cmd));

// 等待响应,增加超时机制

unsigned long startTime = millis();

while (Serial2.available() < 1 && (millis() - startTime) < 1000) {

delay(10); // 减少延时,提高响应速度

}

if (Serial2.available() >= 1) {

byte response = Serial2.read();

if (response == 0x4E) return TTSState::BUSY;

if (response == 0x4F) return TTSState::IDLE;

}

return TTSState::ERROR;

}

// 语音合成函数

bool speech(const String& text) {

// 等待TTS空闲

TTSState state;

while ((state = getTTSState()) != TTSState::IDLE) {

Serial.print("TTS status: ");

Serial.println(static_cast<int>(state));

delay(1000); // 避免过于频繁的查询

}

// 转换UTF8到GB2312

String gb2312Text = GB.get(text);

if (gb2312Text.isEmpty()) {

Serial.println("Error: Text conversion failed");

return false;

}

// 计算消息长度

uint16_t textLength = gb2312Text.length();

if (textLength == 0 || textLength > (TTS_MAX_DATA_LENGTH - 6)) {

Serial.println("Error: Text length is invalid");

return false;

}

// 构建完整命令

uint8_t commandTTS_MAX_DATA_LENGTH;

command0 = 0xFD; // 帧头

// 数据长度(命令+参数+数据)

uint16_t dataLength = textLength + 2;

command1 = (dataLength >> 8) & 0xFF;

command2 = dataLength & 0xFF;

command3 = 0x01; // 命令字节:文本合成

command4 = 0x01; // 参数:合成模式

// 复制文本数据

memcpy(&command5, gb2312Text.c_str(), textLength);

// 计算校验和

uint8_t checksum = command0;

for (uint16_t i = 1; i < textLength + 5; i++) {

checksum ^= commandi;

}

commandtextLength + 5 = checksum;

// 发送命令

bool result = sendTTSCommand(command, textLength + 6);

delay(13600); // 等待合成开始,根据实际情况调整

return result;

}

相关推荐
ch3nyuyu10 小时前
socket套接字
开发语言·php
源图客10 小时前
境外电商 - 龙虾智能体-综合选品推荐报告
开发语言·javascript·ecmascript
是苏浙10 小时前
Java实现链表1
java·开发语言
Jinkxs11 小时前
Rust 性能优化全流程:从 flamegraph 定位瓶颈到 unsafe 与 SIMD 加速,响应快 2 倍
开发语言·性能优化·rust
尘中远11 小时前
Qt高性能绘图库QIm——实现二维三维科学绘图
开发语言·qt·信息可视化
雨辰AI11 小时前
从零搭建大模型本地运行环境|Python+CUDA 基础配置避坑大全
大数据·开发语言·人工智能·python·ai·ai编程·ai写作
DogDaoDao11 小时前
【第 05 篇】Python的字典与集合
开发语言·python·集合·字典
兰令水11 小时前
leecodecode【单调栈】【2026.6.12打卡-java版本】
java·开发语言·算法
leagsoft_100312 小时前
零信任选型五刀法——零信任怎么选?五个问题,五条红线
开发语言·php
AI人工智能+电脑小能手12 小时前
【大白话说Java面试题 第112题】【并发篇】第12题:AQS 中节点的入队时机有哪些?
java·开发语言·面试