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 = "[s6][m0][ring_3]现在时刻[w0][n2]"+String(currentHour)+"点"+fenzhong+"[w0]这里是亳州市业余无线电中继台[w0][n1]439.750下差-5亚音[n1]88.5[w0]世界药都[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(command[i]);

}

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 command[TTS_MAX_DATA_LENGTH];

command[0] = 0xFD; // 帧头

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

uint16_t dataLength = textLength + 2;

command[1] = (dataLength >> 8) & 0xFF;

command[2] = dataLength & 0xFF;

command[3] = 0x01; // 命令字节:文本合成

command[4] = 0x01; // 参数:合成模式

// 复制文本数据

memcpy(&command[5], gb2312Text.c_str(), textLength);

// 计算校验和

uint8_t checksum = command[0];

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

checksum ^= command[i];

}

command[textLength + 5] = checksum;

// 发送命令

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

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

return result;

}

相关推荐
Kay_Liang9 分钟前
MySQL SQL语句精要:DDL、DML与DCL的深度探究
开发语言·数据库·sql·mysql·database
流形填表19 分钟前
AI 助力:如何批量提取 Word 表格字段并导出至 Excel
开发语言·人工智能·word·excel·办公自动化
每天吃饭的羊1 小时前
箭头函数(Arrow Functions)和普通函数(Regular Functions)
开发语言·javascript·ecmascript
寻觅~流光1 小时前
封装---统一封装处理页面标题
开发语言·前端·javascript·vue.js·typescript·前端框架·vue
geovindu1 小时前
Java: OracleHelper
java·开发语言·oracle
Q_Q19632884751 小时前
python的平安驾校管理系统
开发语言·spring boot·python·django·flask·node.js·php
遇见尚硅谷3 小时前
C语言:游戏代码分享
c语言·开发语言·算法·游戏
小刘|3 小时前
单例模式详解
java·开发语言·单例模式
超浪的晨3 小时前
Java 内部类详解:从基础到实战,掌握嵌套类、匿名类与局部类的使用技巧
java·开发语言·后端·学习·个人开发
晓13133 小时前
JavaScript加强篇——第八章 高效渲染与正则表达式
开发语言·前端·javascript