具体的项目,我免费分享在我的项目里,供大家参考学习:
(1条消息) 基于ESP32的语音智能台灯-智能家居文档类资源-CSDN文库
具体的效果图如下,大家可以看一看,我找了些3D打印剩下的盒子来进行拼装工作。效果很不错,不过没有画PCB,卖相并不讨喜。
所用到的技术除PWM调光外,和我之前所制作的ESP32语音舵机(闹钟盒子)基本相差不大。不过网页会更好看一些。
(2条消息) ESP32的网络定时舵机控制,廉价语音定时开关箱,OLED实时显示时间-智能家居文档类资源-CSDN文库
首先看下成品图:
接下来讲将具体的一个代码和用到的方法。
首先是使用的库:
#include <WiFi.h>
#include <HTTPClient.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "SPIFFS.h"
#include "ESPAsyncWebServer.h"
#include "AsyncTCP.h"
#include "ArduinoJson.h"
//人工语音合成库
#include "SoundData.h"
#include "XT_DAC_Audio.h"
// 引入驱动OLED0.91所需的库
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
//舵机使用的不是直接的舵机库
#include <Arduino.h>
#include "time.h"
#include <soc/rtc_cntl_reg.h>
我们使用了FreeRTOS系统,所以需要进行CPU分配
#define pro_cpu 0
#define app_cpu 1
同时进行0.91屏的初始定义和引脚定义
#define SCREEN_WIDTH 128 // 设置OLED宽度,单位:像素
#define SCREEN_HEIGHT 32 // 设置OLED高度,单位:像素
//宏定义部分引脚
#define buzzer 15 //蜂鸣器
#define dismiss_button 13 //关闭按钮
//cpu分配
#define OLED_RESET 4 //声明RST的引脚
#define Yuyin_IO 25
接下来是一些初始变量
//人工语音函数
XT_Wav_Class ForceWithYou(rawData); //引言的XT库中对FORCE的处理
XT_DAC_Audio_Class DacAudio(Yuyin_IO,0); //设置引脚
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);//ssd设置规范化
//设置类型变量声明
const char *ssid = "ccc2021"; //你的网络名称
const char *password = "cyh1217552389"; //你的网络密码
const char *ntpServer = "pool.ntp.org"; //ntpserver的类型
const long gmtOffset_sec = 8 * 3600; //声明变量
const int daylightOffset_sec = 0;
//html输入文本
const char* TEXT_INPUT2 = "HTML_INT_INPUT2";// Integer type input
//PWM灯光设置
const int output = 12;// Connected to D2 Pin of ESP32
String sliderValue = "0";//initial Slider value
// setting PWM properties
const int freq = 5000;
const int ledChannel = 0;
const int resolution = 8;
const char* PARAM_INPUT = "value";
//定时器程序的相关定义,这里主要是声明的意思
static hw_timer_t* sync_timer_handler = NULL; //固定声明一个定时器
//全局标志位的定义,主要是初始化和相关声明
//闹钟参数
char present_time[6];//存储当前的小时和分钟,当缓冲区
String _date, _time, _date_formatted;//读取数据的字符串
uint32_t DemoCounter=0;
char timeHour[10];
String inputMessage;
String inputParam;
String alarm_time;
String myString;
这里不做过多的赘述
使用异步服务器:
AsyncWebServer server(80);//将WEB服务器的端口pnning到80,就是服务器初始化的标准
定时器声明:
//定时器详情
static const uint16_t timer_divider = 80;//所有的定时器频率
static uint64_t alarm_timer_exact_count;//实际的闹钟计
接下来看看重要的相关函数定义:
首先是我们的HTML端的一个书写:
// HTML web page界面的设置
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
<meta name="viewport" charset="utf-8" content="width=device-width, initial-scale=1">
<title>ESP32 智能 闹钟</title>
<style>
html {font-family: Arial; display: inline-block; text-align: center;}
h2 {font-size: 2.3rem;}
p {font-size: 1.9rem;}
body {max-width: 400px; margin:0px auto; padding-bottom: 25px;}
.slider { -webkit-appearance: none; margin: 14px; width: 360px; height: 25px; background: #FFD65C;
outline: none; -webkit-transition: .2s; transition: opacity .2s;}
.slider::-webkit-slider-thumb {-webkit-appearance: none; appearance: none; width: 35px; height: 35px; background: #003249; cursor: pointer;}
.slider::-moz-range-thumb { width: 35px; height: 35px; background: #003249; cursor: pointer; }
</style>
</head>
<body>
<h2><u>ESP32 智能 闹钟</u></h2>
<p>PWM VALUE = <span id="textSliderValue">%SLIDERVALUE%</span></p>
<p><input type="range" onchange="updateSliderPWM(this)" id="pwmSlider" min="0" max="255" value="%SLIDERVALUE%" step="1" class="slider"></p>
<script>
function updateSliderPWM(element)
{
var sliderValue = document.getElementById("pwmSlider").value;
document.getElementById("textSliderValue").innerHTML = sliderValue;
console.log(sliderValue);
var xhr = new XMLHttpRequest();
xhr.open("GET", "/slider?value="+sliderValue, true);
xhr.send();
}
</script>
<div style="display: block; margin: 0 auto; width: 50%; background: #ccc;">
<form action="/get">
<br>
<h2>输入闹钟( XX:XX:XX ) : </h2>
<input type="text" name="HTML_INT_INPUT2" id = "alarm_inset">
<input type="submit" value="Submit">
</form><br>
<br>
<button onclick="showUname()" align="center">显示上一次设定时间</button>
<div id="uname-show"></div>
<script>
function showUname(){
document.getElementById("uname-show").innerHTML = "<h1>" + document.getElementById("alarm_inset").value + "</h1>";
}
</script>
<br>
</div>
</body>
</html>
)rawliteral";
void notFound(AsyncWebServerRequest *request) {
request->send(404, "text/plain", "Not found");
}
void web_server_task_function(void* paramters)
{
Serial.println("Setting up the HTTP server");//串口打印设置HTTP服务器
// Send web page with input fields to client
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/html", index_html, processor);
Serial.println("HTTP START");
});
// Send a GET request to <ESP_IP>/get?input1=<inputMessage>
server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) {
String inputMessage;
String inputParam;
// GET input2 value on <ESP_IP>/get?input2=<inputMessage>
if (request->hasParam(TEXT_INPUT2)) {
inputMessage = request->getParam(TEXT_INPUT2)->value();
inputParam = TEXT_INPUT2;
alarm_time = inputMessage;
Serial.println("alarm clock : ");
}
else{
inputMessage = "No Input Text sent on ESP32";
inputParam = "none";
}
Serial.println(inputMessage);
request->send(200, "text/html", "HTTP GET request sent to your ESP on input field ("
+ inputParam + ") with value: " + inputMessage +
"<br><a href=\"/\">Return to Home Page</a>");
});
server.on("/slider", HTTP_GET, [] (AsyncWebServerRequest *request) {
String inputMessage;
// GET input1 value on <ESP_IP>/slider?value=<inputMessage>
if (request->hasParam(PARAM_INPUT)) {
inputMessage = request->getParam(PARAM_INPUT)->value();
sliderValue = inputMessage;
ledcWrite(ledChannel, sliderValue.toInt());
}
else
{
inputMessage = "No message was sent";
}
Serial.println(inputMessage);
request->send(200, "text/plain", "OK");
});
server.begin();//starting the server
Serial.println("HTTP server setup completed");
vTaskDelete(NULL);
}
其次最重要的就是实时闹钟这部分,我们获取了时间字符串,然后很简单的进行了一个输入字符串的比对,就完成了定闹钟的操作:
void printLocalTime()
{
struct tm timeinfo;
if (!getLocalTime(&timeinfo))
{
display.println("Failed to obtain time");
return;
}
display.println(&timeinfo, "%F"); // 格式化输出
display.println(&timeinfo, "%T"); // 格式化输出
display.println(&timeinfo, "%A"); // 格式化输出
}
void OLED_Function(void *pvParameters){
for(;;){
vTaskDelay(1000);
//清除屏幕
display.clearDisplay();
//设置光标位置
display.setCursor(0, 0);
printLocalTime();
display.display();
Serial.println("DISPLAY ONCE AGAIN!");
}
}
void clock_task_function(void* parameters)
{
Serial.println("clock function start!");
while(1)
{
struct tm timeinfo;
if (!getLocalTime(&timeinfo))
{
Serial.println("Failed to obtain time");
return;
}
Serial.println("start print");
strftime(timeHour, 10, "%T", &timeinfo);
myString = String(timeHour);
Serial.println(myString);
Serial.println("TIME WRITE success!");
vTaskDelay(1000/portTICK_PERIOD_MS);//这里设置的是无任务时间
}
}
void alarm_task_function(void* parameters)
{
Serial.println("alarm function start!");
while(1)
{
Serial.println("alarm is setting!");
vTaskDelay(1000/portTICK_PERIOD_MS);
if(alarm_time == myString)//这里使用了memcmp函数,这个函数是将两个内存的字节进行比较,当时间校正一致时,才能发生循环
{
Serial.println("Time Right!");
while(1)//循环
{
for(int i=0; i<2; i++)//蜂鸣器循环叫3轮
{
digitalWrite(buzzer, HIGH);
vTaskDelay(500/portTICK_PERIOD_MS);
digitalWrite(buzzer, LOW);
vTaskDelay(500/portTICK_PERIOD_MS);
Serial.println("alarm is sound loundly");
YuyinBB();
}
if(!digitalRead(dismiss_button))//如果按了关闭按钮,两个标志位变False,并且串口打印闹钟关闭提醒
{
Serial.println("clock is close!");
break;
}
vTaskDelay(1000/portTICK_PERIOD_MS);//经典延时
}
}
}
}
最后就是FREERTOS的任务分配:
xTaskCreatePinnedToCore(clock_task_function, "clock_task", 2048, NULL, 1, &clock_task_handler, pro_cpu);//clock task
xTaskCreatePinnedToCore(alarm_task_function, "alarm_task", 2048, NULL, 1, &alarm_task_handler, app_cpu);//alarm task
xTaskCreatePinnedToCore(web_server_task_function, "server_task", 6000, NULL, 1, &web_server_task_handler, pro_cpu);//web server tasks
xTaskCreatePinnedToCore(OLED_Function, "oled_Task", 10000, NULL, 1, NULL, app_cpu);
//NTPClient初始化
其中需要去提及的就是廉价语音的一个实现。这个我推荐直接参考我之前的文章
(1条消息) ESP32的智能网络定时舵机和语音控制+OLED实时时间显示_Reedsway在重庆的博客-CSDN博客_esp32 语音控制
这里就不再赘述。