项目介绍
实物图
项目名称
- 基于ESP32+ASRPRO的智能家居助手设计与实现
项目背景
随着科技的不断进步,物联网(IoT)技术正在迅速改变我们的生活和工作方式。为了利用这一趋势,我开发了"基于ESP32+ASRPRO的智能家居助手设计与实现",旨在通过连接设备和数据,实现更智能化、更方便的生活。
项目目标
"基于ESP32+ASRPRO的智能家居助手设计与实现"的主要目标是:
- 实现设备远程监测与管理
- 全天候数据监测
- 增强用户设备控制体验
项目架构
设备层:
使用ESP32作为主控制器,ASRPRO作为语音识别设备,DHT11作为温湿度数据检测设备
网络层:
- 设备与服务器通过MQTT、WiFi连接
- 设备与设备之间通过WiFi、蓝牙(可选)连接
数据层:
- 数据主要通过主控制器进行处理并上传阿里云服务器
应用层:
[描述最终用户如何与系统交互,如手机应用、网页界面等]
- 使用1.8寸TFT屏幕显示数据以及交互信息
- 使用阿里云物联网平台开发的程序可在手机、网页、电脑端进行设备控制及数据查看
- 可通过阿里云服务器对数据进行监控、修改
- 本地通过语音控制其他设备以及获取其他设备信息
主要功能
-
数据监测: 通过板载DHT11传感器实时采集温湿度数据,并在显示屏显示。
-
远程控制: 用户可以通过手机或电脑远程控制设备。
-
语音控制: 通过ASRPRO模块用户可以在本地通过语音控制设备。
-
警报和通知: 在检测到异常情况时,板载蜂鸣器会自动发送警报通知用户。
-
功能拓展:分别预留ESP32、ASRPRO的IO*2并用2.0mm间距3Pin接口引出用于功能拓展
技术实现
通信协议:
采用的通信协议:
- MQTT :设备与服务器间的数据传输
- HTTP/HTTPS :实时网络时间获取
- UDP:各设备之前的数据传输
硬件:
模块
- 主控制器:ESP32
- 语音识别模块:ASRPRO
- 温湿度传感器:DHT11
- 报警器:12095蜂鸣器
- 数据显示器:1.8寸TFT屏幕
- 状态指示器:LED灯若干
外壳设计
PCB设计
PCB设计使用嘉立创EDA完成
原理图
PCB
3D模型预览
软件:
- 开发语言和框架:基于Arduino IDE(ESP32)以及天问 BLOCK(ASRPRO)使用C/C++进行开发
- 控制程序基于阿里云物联网平台进行开发
控制程序-控制界面
控制程序-数据监测界面
ESP32代码
- 长代码警告!(后续会进行封装)
#include <WiFi.h>
#include <WiFiUdp.h>
#include <DHT.h> //引入DHT库使用DHT11
#include <AliyunIoTSDK.h>// 引入阿里云 IoT SDK
//引入显示屏库
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include "TFT_Image.h" // 包含图片数组头文件
#include <NTPClient.h> //获取网络时间
// 中国的NTP服务器地址,可以选择多个
const char* ntpServer = "cn.pool.ntp.org";
const long gmtOffset_sec = 8 * 3600; // 中国标准时间(UTC+8)
const int daylightOffset_sec = 0; // 无夏令时偏移
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, ntpServer, gmtOffset_sec, 60000); // 更新时间间隔为60000ms
int currentHour =0;
int currentMinute = 0;
int currentSecond = 0;
char timeArr[] = "TIME:23-38-30";
#define TIME_COLOUR ST7735_YELLOW
#define TFT_CS 14 // 请替换为您的CS引脚号
#define TFT_DC 27 // 请替换为您的DC引脚号
#define TFT_RST 5 // 请替换为您的RST引脚号
//#define TFT_SCK 18 // 请替换为您的SCK引脚号
//#define TFT_MOSI 23 // 请替换为您的MOSI引脚号
//已经默认配置
//#define COMPUTER_PIN 0 //连接继电器的引脚
#define DHTPIN 25
#define DHTTYPE DHT11 // 定义传感器类型
DHT dht(DHTPIN, DHTTYPE);
char HumiStr[6] = "78";
char TempStr[6] = "34.8";
// 图片尺寸
#define IMAGE_WIDTH 60
#define IMAGE_HEIGHT 60
extern const unsigned char gImage_humi[];
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);//创建显示屏对象并初始化
hw_timer_t * timer = NULL; // 创建一个定时器对象
static WiFiClient espClient;
// 设置 wifi 信息
#define WIFI_SSID "Redmi_DC21"
#define WIFI_PASSWD "88888888"
//UDP变量定义
WiFiUDP udp;
WiFiUDP discoveryUdp;
const int udpPort = 5088;
const int discoveryPort = 5089; // 用于动态发现的UDP端口
#define MAX_CONNECT_COUNT 10 //预期的最大连接设备数
//char incomingPacket[255];//UDP接收数据数组
struct DeviceInfo{//存储其他设备name及ip
String name;
IPAddress ip;
};
DeviceInfo devices[MAX_CONNECT_COUNT];
int deviceCount = 0; //已经连接设备数
#define SEND_DELAY_TIME 200 //单位ms
// 设置产品和设备的信息,从阿里云设备信息里查看
#define PRODUCT_KEY "xxxxxxxxxxx"
#define DEVICE_NAME "xxxxxxxxx"
#define DEVICE_SECRET "xxxxxxxxxxxxxxxxxxxxx"
#define REGION_ID "cn-shanghai"
//定义使用的颜色
#define BACKGROUND_COLOUR ST7735_BLACK //背景颜色
#define RECT1_COLOUR ST7735_RED
#define TEXT1_COLOUR ST7735_GREEN
#define LINE1_COLOUR ST7735_RED
#define TEXT2_COLOUR ST7735_BLUE
#define RECT2_COLOUR ST7735_RED
#define TEXT3_COLOUR ST7735_GREEN
#define TEXT4_COLOUR ST7735_BLUE
#define DATA_COLOUR ST7735_WHITE
#define DATANAME_COLOUR ST7735_WHITE
#define SHOWDELAY_TIME 500 //字幕滚动速度
#define MAX_TEXT_COUNT 30 //输入文本最大字符数
#define MAX_SHOW_COUNT 12 //最大同时显示字符数
#define MAX_SHOW_STRING 6 //最大同时刷新字符串数量
#define SHOW_MODE_SEND 1 //向外发送的消息
#define SHOW_MODE_RECIVED 2//从外部收到的消息
#define SHOW_MODE_MYSELF 3 //内部消息
int textShowX[MAX_SHOW_STRING] = {0};
int textShowY[MAX_SHOW_STRING] = {0};
int showCount[MAX_SHOW_STRING] = {0};//实际显示数量
int maxCount[MAX_SHOW_STRING] = {0};//需要显示数量
char textShowString[MAX_SHOW_STRING][MAX_SHOW_COUNT + 1] = {"null"} ;//实际显示的字符串
char textMaxString[MAX_SHOW_STRING][MAX_TEXT_COUNT] = {"null"};//需要显示的字符串
int showFlag[MAX_SHOW_STRING] = {0};//显示刷新标志位
float humidity = 0;//温湿度
float temperature = 0;
bool ComputerControl = 1;//电脑控制
bool FanControl = 0;//风扇控制
int FanPwm = 50;//风扇调速
int FanRunTimeHour = 3;//风扇定时-时
int FanRunTimeMin = 30;//定时-分
bool Led1Control = 0;//灯光控制
int LedLuminance = 50;//亮度控制
int LightValue = 100;//光照检测
int SmokeDetection = 100;//烟雾检测
bool AmbientLightControl = 0;//氛围灯控制
int AmbientLightRed = 100;//氛围灯颜色控制
int AmbientLightGreen = 100;
int AmbientLightBlue = 100;
int AmbientLightLuminance = 50;//氛围灯亮度
int AmbientLightCount = 30;
// 定时器相关变量
unsigned long previousMillis = 0;
const unsigned long interval = 100; // 每100毫秒
unsigned long previousMillisUpdate = 0;
const unsigned long updateInterval = 1000; // 每1秒
unsigned long previousMillisData = 0;
const unsigned long dataInterval = 10000; // 每10秒
void displayImage(int16_t x, int16_t y, const unsigned char *image, uint16_t width, uint16_t height) {
// 遍历图片数组,逐像素显示图片
for (uint16_t row = 0; row < height; row++) {
for (uint16_t col = 0; col < width; col++) {
uint32_t pixelColor = pgm_read_byte(image + (row * width + col));
tft.drawPixel(x + col, y + row, pixelColor);
}
}
}
void messageShowUI(const int x,const int y,char text1[],char text2[],char text3[],char text4[]){
const int RectX = x;//基准坐标 (矩形左上角)
const int RectY = y;//基准坐标
const int RectWight = 160;//宽160
const int RectHight = 14;//高14
const int Rect1x = RectX;//矩形框1x坐标
const int Rect1y = RectY;//矩形框1y坐标
const int Rect1w = RectWight - RectX;//矩形框1宽度
const int Rect1h = RectHight;//矩形框1高度
//tft.drawRect(Rect1x,Rect1y,Rect1w,Rect1h,RECT1_COLOUR);//绘制矩形框1 外部包围
const int Text1x = Rect1x +3;//Text1x坐标
const int Text1y = Rect1y +3;//Text1y坐标
const int Text1C = 4;//Text1字符数量
char Text1String[Text1C + 1];//Text1字符数组
strncpy(Text1String, text1,Text1C);//从text1拷贝Text1C个字符到Text1String
Text1String[Text1C] = '\0'; // 添加字符串结束符
const int Text1Size = 1;
const int Line1x = Text1x + Text1C * 6 + 3;//字符大小为6*8
const int Line1y = RectY;
const int Line1h = RectHight;
const int Text2x = Line1x + 3;//Text2x坐标
const int Text2y = Text1y;//Text2y坐标
const int Text2C = 6 ;//Text2字符数量
const int Text2Size = 1;
char Text2String[Text2C + 1];//Text2字符数组
strncpy(Text2String, text2,Text2C);//从text2拷贝Text2C个字符到Text2String
Text2String[Text2C] = '\0'; // 添加字符串结束符
const int Rect2x = Text2x + Text2C * 6 + 1;//矩形框2x坐标
const int Rect2y = RectY;//矩形框2y坐标
const int Rect2w = 14;//矩形框2宽度
const int Rect2h = 14;//矩形框2高度
const int Text3x = Rect2x + 5;//Text3x坐标
const int Text3y = Text1y;//Text3y坐标
const int Text3C = 1;//Text3字符数量
const int Text3Size = 1;
char Text3String[Text3C + 1];//Text3字符数组
strncpy(Text3String, text3,Text3C);//从text3拷贝Text3C个字符到Text3String
Text3String[Text3C] = '\0'; // 添加字符串结束符
const int Text4x = Rect2x + Rect2w + 3;//Text4x坐标
const int Text4y = Text1y ;//Text4y坐标
const int Text4Size = 1;
const int Text4C = (159 - Text4x)/6 ;//剩余空间可显示字符数量(以0,0作为起点最多显示12个字符)字符大小6*8
char Text4String[Text4C + 1];//Text4字符数组
int Text4Length = 0;//传入需要显示的字符数量
while (text4[Text4Length] != '\0') {//动态分配的数组不能使用sizeof来计算数组大小
Text4Length++;
}
static bool flag = true;
if(flag){
tft.initR(INITR_BLACKTAB); // 使用适当的初始化参数
tft.setRotation(1); // 设置显示方向,1表示顺时针旋转90度
tft.fillScreen(BACKGROUND_COLOUR);//设置背景颜色
flag = false; //初始化完成改变标志位防止重复初始化
}
tft.drawRect(Rect1x,Rect1y,Rect1w,Rect1h,RECT1_COLOUR);//绘制矩形框1:整体框架
tft.setTextColor(TEXT1_COLOUR);//设置文字颜色
tft.setTextSize(Text1Size);//设置字体大小
tft.setCursor(Text1x,Text1y);//设置起始位置
tft.print(Text1String);//显示Text1 From
tft.drawFastVLine(Line1x,Line1y,Line1h,LINE1_COLOUR);//绘制Text1后竖线
tft.setTextColor(TEXT2_COLOUR);//设置文字颜色
tft.setTextSize(Text2Size);//设置字体大小
tft.setCursor(Text2x,Text2y);//设置起始位置
tft.fillRect(Text2x, Text2y, 6 * 6, 8, BACKGROUND_COLOUR);//绘制矩形刷新部分区域
tft.print(Text2String);//显示Text2 ASRPRO
//tft.drawFastVLine(Line2x,Line2y,Line2h,LINE2_COLOUR);//绘制Text2后竖线
tft.fillRect(Rect2x,Rect2y,Rect2w,Rect2h,RECT2_COLOUR);//绘制矩形框2:Text3包围框
tft.setTextColor(TEXT3_COLOUR);//设置文字颜色
tft.setTextSize(Text3Size);//设置字体大小
tft.setCursor(Text3x,Text3y);//设置起始位置
tft.print(Text3String);//显示Text3 R
tft.setTextWrap(false);//关闭超出范围自动换行 实现滚动字幕显示
if(Text4Length > Text4C){//需要显示的数据数量大于剩余空间可显示字符数量
//传递参数
for(int i = 0; i < MAX_SHOW_STRING ; i++){
if(showFlag[i] == 0){
showFlag[i] = 1;//标记已使用
showCount[i] = Text4C;//可显示的字符数
maxCount[i] = Text4Length;//需要显示的字符数
textShowX[i] = Text4x;
textShowY[i] = Text4y;
strcpy(textMaxString[i],text4);//传递需要显示的字符串
timerAlarmEnable(timer); // 启用定时器
//直接静态显示
tft.fillRect(Text4x, Text4y, Text4C * 6, 8, BACKGROUND_COLOUR);//绘制矩形刷新部分区域
strncpy(Text4String, text4,Text4C);//从text4拷贝Text4C个字符到Text4String
Text4String[Text4C] = '\0'; // 添加字符串结束符
tft.setTextColor(TEXT4_COLOUR);//设置文字颜色
tft.setTextSize(Text4Size);//设置字体大小
tft.setCursor(Text4x,Text4y);//设置起始位置
tft.print(Text4String);//显示Text4 Recived data!
return ;//找到可显示位置即退出循环
}
}
}
else{//直接静态显示
tft.fillRect(Text4x, Text4y, Text4C * 6, 8, BACKGROUND_COLOUR);//绘制矩形刷新部分区域
strncpy(Text4String, text4,Text4C);//从text4拷贝Text4C个字符到Text4String
Text4String[Text4C] = '\0'; // 添加字符串结束符
tft.setTextColor(TEXT4_COLOUR);//设置文字颜色
tft.setTextSize(Text4Size);//设置字体大小
tft.setCursor(Text4x,Text4y);//设置起始位置
tft.print(Text4String);//显示Text4 Recived data!
}
}
void showDataUI(int dataX,int dataY,int Icon,char dataArr[],char dataName[]){//起始点坐标x,y 显示图标 数据字符串 数据名称
int IconX = dataX;
int IconY = dataY;
int IconW = 12;
int IconH = 25;
int IconN = Icon;//0代表温度
if(IconN == 0) tft.fillRect(IconX,IconY,IconW,IconH,ST7735_GREEN);
else tft.fillRect(IconX,IconY,IconW,IconH,ST7735_BLUE);//ICON绘制
tft.fillRect(IconX + IconW + 5, IconY, 10 * 5, 16, BACKGROUND_COLOUR);//绘制矩形刷新部分区域
tft.setCursor(IconX + IconW + 5,IconY);//设置起始位置
tft.setTextColor(DATA_COLOUR);//设置文字颜色
tft.setTextSize(2);//设置字体大小
tft.print(dataArr);//显示Text4 Recived data!
if(dataX == 0) tft.print("C");
else tft.print("%");
//tft.fillRect(IconX + IconW + 1, IconY + 18, 10 * 6, 8, BACKGROUND_COLOUR);//绘制矩形刷新部分区域
tft.setCursor(IconX + IconW + 1,IconY + 18);//设置起始位置
tft.setTextColor(DATANAME_COLOUR);//设置文字颜色
tft.setTextSize(1);//设置字体大小
tft.print(dataName);//显示Humidity
}
void showLongMessage(int Mode,String Message){
static int LongMessageCount = 3;
static int showCount = 0;
int X = 0;
int Y = 0;
int MessageX = X;
int MessageY = 100 - 1 - 13 * LongMessageCount + showCount % 3 * 13;
int MessageW = 160 - X;
int MessageH = 14;
tft.drawRect(MessageX, MessageY, MessageW, MessageH, ST7735_RED);//绘制矩形框
tft.fillRect(MessageX,MessageY, 14, 14, ST7735_RED);//绘制包围矩形框
tft.setCursor(MessageX + 3,MessageY + 3);//设置起始位置
tft.setTextColor(ST7735_GREEN);//设置文字颜色
tft.setTextSize(1);//设置字体大小
if(Mode == SHOW_MODE_RECIVED) tft.print("R");//显示R
else if(Mode == SHOW_MODE_SEND) tft.print("S");//显示S
else if(Mode == SHOW_MODE_MYSELF) tft.print("M");//显示M
tft.fillRect(MessageX + 3 + 15,MessageY + 3, (160 - 3 - X - 14) / 6 * 6, 8, BACKGROUND_COLOUR);//绘制矩形刷新部分区域
tft.setCursor(MessageX + 3 + 15,MessageY + 3);//设置起始位置
tft.setTextColor(ST7735_BLUE);//设置文字颜色
tft.setTextSize(1);//设置字体大小
tft.print(Message);//显示Message
showCount += 1;
}
void ComputerControlCallBack(JsonVariant p){
//电脑控制回调函数
int ComputerControl = p["ComputerControl"];
//digitalWrite(COMPUTER_PIN,LOW);
delay(500);
//digitalWrite(COMPUTER_PIN,HIGH);
}
void LedControlCallBack(JsonVariant p){
//灯光控制回调函数
int LedControl = p["LedControl"];
Serial.println("LedControl:" + String(LedControl));
}
void DateSend(void){
// 发送一个数据到云平台,CurrentTemperature 是在设备产品中定义的物联网模型的 id
AliyunIoTSDK::send("Temperature", temperature);
delay(SEND_DELAY_TIME);
AliyunIoTSDK::send("Humility", humidity);
delay(SEND_DELAY_TIME);
AliyunIoTSDK::send("ComputerControl", ComputerControl);
delay(SEND_DELAY_TIME);
AliyunIoTSDK::send("FanControl", FanControl);
delay(SEND_DELAY_TIME);
AliyunIoTSDK::send("FanPwm", FanPwm);
delay(SEND_DELAY_TIME);
AliyunIoTSDK::send("FanRunTimeHour", FanRunTimeHour);
delay(SEND_DELAY_TIME);
AliyunIoTSDK::send("FanRunTimeMin", FanRunTimeMin);
delay(SEND_DELAY_TIME);
AliyunIoTSDK::send("LedControl", Led1Control);
delay(SEND_DELAY_TIME);
AliyunIoTSDK::send("LedLuminance", LedLuminance);
delay(SEND_DELAY_TIME);
AliyunIoTSDK::send("LightValue", LightValue);
delay(SEND_DELAY_TIME);
AliyunIoTSDK::send("SmokeDetection", SmokeDetection);
delay(SEND_DELAY_TIME);
AliyunIoTSDK::send("AmbientLightControl", AmbientLightControl);
delay(SEND_DELAY_TIME);
AliyunIoTSDK::send("AmbientLightRed", AmbientLightRed);
delay(SEND_DELAY_TIME);
AliyunIoTSDK::send("AmbientLightGreen", AmbientLightGreen);
delay(SEND_DELAY_TIME);
AliyunIoTSDK::send("AmbientLightBlue", AmbientLightBlue);
delay(SEND_DELAY_TIME);
AliyunIoTSDK::send("AmbientLightCount", AmbientLightCount);
delay(SEND_DELAY_TIME);
AliyunIoTSDK::send("AmbientLightLuminance", AmbientLightLuminance);
delay(SEND_DELAY_TIME);
}
void wifiInit(const char *ssid, const char *passphrase)
{
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, passphrase);
while (WiFi.status() != WL_CONNECTED)
{
delay(1000);
Serial.println("WiFi not Connect");
}
Serial.println("Connected to AP");
}
void serial2DataDispose(){//处理串口2接收的数据
if(Serial2.available() > 0) { // 如果串口2中有数据可用 数据格式From:Name-State
String receivedFromString2 = Serial2.readStringUntil(':');//读取一行字符串,直到换行符 ':'
String receivedNameString2 = Serial2.readStringUntil('-'); // 读取一行字符串,直到符 '-'
String receivedStateString2 = Serial2.readStringUntil('\n'); // 读取一行字符串,直到换行符 '\n'
char receivedFromArray2[receivedFromString2.length() + 1];
receivedFromString2.toCharArray(receivedFromArray2, receivedFromString2.length() + 1);
char receivedNameArray2[receivedNameString2.length() + 1];
receivedNameString2.toCharArray(receivedNameArray2, receivedNameString2.length() + 1);
char receivedStateArray2[receivedStateString2.length() + 1];
receivedStateString2.toCharArray(receivedStateArray2, receivedStateString2.length() + 1);
String receivedMessageString2 = String(receivedNameString2 + '-' + receivedStateString2);
char receivedMessageArray2[receivedMessageString2.length() + 1];
receivedMessageString2.toCharArray(receivedMessageArray2, receivedMessageString2.length() + 1);
if(receivedNameString2 == ""){//name为空 无格式字符串
messageShowUI(0,113 - 13 * 1,"From","NULL","R",receivedFromArray2);
showLongMessage(SHOW_MODE_RECIVED,String(receivedFromString2 + receivedNameString2));
}
else {//定义格式字符串
messageShowUI(0,113 - 13 * 1,"From",receivedFromArray2,"R",receivedMessageArray2);
showLongMessage(SHOW_MODE_RECIVED,String(receivedFromString2 + ':' + receivedMessageString2));
if(receivedNameArray2[0] == 'T' && receivedNameArray2[1] == 'i' && receivedNameArray2[2] == 'm'){//Time-Get
if(receivedStateArray2[0] == 'G' && receivedStateArray2[1] == 'e' && receivedStateArray2[2] == 't'){
sprintf(timeArr, "Time:%02d-%02d-%02d", currentHour, currentMinute, currentSecond);// 使用sprintf格式化时间
Serial2.printf(timeArr);//使用printf ASRPRO才能接收到数据
messageShowUI(0,113,"Goal","ASRPRO","S",timeArr);//目标 名称 发送 数据
showLongMessage(SHOW_MODE_SEND,timeArr);
}
}
else if(receivedNameArray2[0] == 'L' && receivedNameArray2[1] == 'e' && receivedNameArray2[2] == 'd'){//Led-On
if(receivedStateArray2[0] == 'O' && receivedStateArray2[1] == 'n' ){
for(int i = 0; i < deviceCount;i++){
if(strncmp(devices[i].name.c_str(),"Led",3) == 0){
udp.beginPacket(devices[i].ip, udpPort);
udp.printf("Led:On");
udp.endPacket();
messageShowUI(0,113,"Goal","Led","S","Led-On");//目标 名称 发送 数据
showLongMessage(SHOW_MODE_SEND,"Led-On");
Serial.printf("Device %d: Name = %s, IP = %s\n", i + 1, devices[i].name.c_str(), devices[i].ip.toString().c_str());
break;
}
else if(i == deviceCount - 1){//当i == deviceCount - 1 时仍未找到即不存在该设备
showLongMessage(SHOW_MODE_SEND,"No found device!");
}
}
messageShowUI(0,113,"Goal","Led","S","Led-On");//目标 名称 发送 数据
showLongMessage(SHOW_MODE_SEND,"Led-On");
}
else if(receivedStateArray2[0] == 'O' && receivedStateArray2[1] == 'f' && receivedStateArray2[2] == 'f'){//Led-Off
for(int i = 0; i < deviceCount;i++){
if(strncmp(devices[i].name.c_str(),"Led",3) == 0){
udp.beginPacket(devices[i].ip, udpPort);
udp.printf("Led:Off");
udp.endPacket();
messageShowUI(0,113,"Goal","Led","S","Led-Off");//目标 名称 发送 数据
showLongMessage(SHOW_MODE_SEND,"Led-Off");
Serial.printf("Device %d: Name = %s, IP = %s\n", i + 1, devices[i].name.c_str(), devices[i].ip.toString().c_str());
break;
}
else if(i == deviceCount - 1){//当i == deviceCount - 1 时仍未找到即不存在该设备
showLongMessage(SHOW_MODE_SEND,"No found device!");
}
}
messageShowUI(0,113,"Goal","Led","S","Led-Off");//目标 名称 发送 数据
showLongMessage(SHOW_MODE_SEND,"Led-Off");
}
}
else if(receivedNameArray2[0] == 'R' && receivedNameArray2[1] == 'e' && receivedNameArray2[2] == 'l'){//Relay-On
if(receivedStateArray2[0] == 'O' && receivedStateArray2[1] == 'n' ){
for(int i = 0; i < deviceCount;i++){
if(strncmp(devices[i].name.c_str(),"Relay",5) == 0){
udp.beginPacket(devices[i].ip, udpPort);
udp.printf("Relay:On");
udp.endPacket();
messageShowUI(0,113,"Goal","Relay","S","Relay-On");//目标 名称 发送 数据
showLongMessage(SHOW_MODE_SEND,"Relay-On");
Serial.printf("Device %d: Name = %s, IP = %s\n", i + 1, devices[i].name.c_str(), devices[i].ip.toString().c_str());
break;
}
else if(i == deviceCount - 1){//当i == deviceCount - 1 时仍未找到即不存在该设备
showLongMessage(SHOW_MODE_SEND,"No found device!");
}
}
}
else if(receivedStateArray2[0] == 'O' && receivedStateArray2[1] == 'f' && receivedStateArray2[2] == 'f'){//Relay-Off
for(int i = 0; i < deviceCount;i++){
if(strncmp(devices[i].name.c_str(),"Relay",5) == 0){
udp.beginPacket(devices[i].ip, udpPort);
udp.printf("Relay:Off");
udp.endPacket();
messageShowUI(0,113,"Goal","Relay","S","Relay-Off");//目标 名称 发送 数据
showLongMessage(SHOW_MODE_SEND,"Relay-Off");
Serial.printf("Device %d: Name = %s, IP = %s\n", i + 1, devices[i].name.c_str(), devices[i].ip.toString().c_str());
break;
}
else if(i == deviceCount - 1){//当i == deviceCount - 1 时仍未找到即不存在该设备
showLongMessage(SHOW_MODE_SEND,"No found device!");
}
}
}
else if(receivedStateArray2[0] == 'O' && receivedStateArray2[1] == 'p' && receivedStateArray2[2] == 'e'){//Relay-Open
for(int i = 0; i < deviceCount;i++){
if(strncmp(devices[i].name.c_str(),"Relay",5) == 0){
udp.beginPacket(devices[i].ip, udpPort);
udp.printf("Relay:Open");
udp.endPacket();
messageShowUI(0,113,"Goal","Relay","S","Relay-Open");//目标 名称 发送 数据
showLongMessage(SHOW_MODE_SEND,"Relay-Open");
Serial.printf("Device %d: Name = %s, IP = %s\n", i + 1, devices[i].name.c_str(), devices[i].ip.toString().c_str());
break;
}
else if(i == deviceCount - 1){//当i == deviceCount - 1 时仍未找到即不存在该设备
showLongMessage(SHOW_MODE_SEND,"No found device!");
}
}
}
else if(receivedStateArray2[0] == 'C' && receivedStateArray2[1] == 'l' && receivedStateArray2[2] == 'o'){//Relay-Close
for(int i = 0; i < deviceCount;i++){
if(strncmp(devices[i].name.c_str(),"Relay",5) == 0){
udp.beginPacket(devices[i].ip, udpPort);
udp.printf("Relay:Close");
udp.endPacket();
messageShowUI(0,113,"Goal","Relay","S","Relay-Close");//目标 名称 发送 数据
showLongMessage(SHOW_MODE_SEND,"Relay-Close");
Serial.printf("Device %d: Name = %s, IP = %s\n", i + 1, devices[i].name.c_str(), devices[i].ip.toString().c_str());
break;
}
else if(i == deviceCount - 1){//当i == deviceCount - 1 时仍未找到即不存在该设备
showLongMessage(SHOW_MODE_SEND,"No found device!");
}
}
}
}
}
}
}
void dht11DataGet(void){
static bool initFlag = false;
if(!initFlag)//未初始化
{
delay(2000); // 延迟2秒以便传感器稳定
initFlag = true;
}
humidity = dht.readHumidity(); // 读取湿度
temperature = dht.readTemperature(); // 读取温度(默认摄氏度)
if (isnan(humidity) || isnan(temperature)) {
showLongMessage(SHOW_MODE_MYSELF,"Failed to read from DHT sensor!");
return;
}
sprintf(HumiStr,"%.0f",humidity);//"%.1f"一位小数的float类型
sprintf(TempStr,"%.1f",temperature);
}
void showTimeUI(void){
char timeString[] = "23:59:59";
int timeX = 6;
int timeY = 32;
int timeSize = 3;
static int showHour = 0;
static int showMinute = 0;
static int showSecond = 0;
tft.setTextColor(TIME_COLOUR);//设置文字颜色
tft.setTextSize(timeSize);//设置字体大小
if(showHour != currentHour){
showHour = currentHour;
char showHourArr[] = "59";
sprintf(showHourArr,"%02d",showHour); //格式化%02d
tft.fillRect(timeX,timeY,6*timeSize*2,8*timeSize,ST7735_BLACK);//刷新
tft.setCursor(timeX,timeY);//设置显示位置
tft.print(showHourArr);//showHourArr
}
tft.setCursor(timeX + 6 * timeSize * 2,timeY);//设置显示位置
tft.print(":");//:
if(showMinute != currentMinute){
showMinute = currentMinute;
char showMinuteArr[] = "59";
sprintf(showMinuteArr,"%02d",showMinute); //格式化%02d
tft.fillRect(timeX + 6 * timeSize * 3,timeY,6 * timeSize * 2,8 * timeSize,ST7735_BLACK);//刷新
tft.setCursor(timeX + 6 * timeSize * 3,timeY);//设置显示位置
tft.print(showMinuteArr);//showMinuteArr
}
tft.setCursor(timeX + 6 * timeSize * 5,timeY);//设置显示位置
tft.print(":");//:
if(showSecond != currentSecond){
showSecond = currentSecond;
char showSecondArr[] = "59";
sprintf(showSecondArr,"%02d",showSecond);
tft.fillRect(timeX + 6 * timeSize * 6,timeY,6 * timeSize * 2,8 * timeSize,ST7735_BLACK);//刷新
tft.setCursor(timeX + 6 * timeSize * 6,timeY);//设置显示位置
tft.print(showSecondArr);//showSecondArr
}
else{
char showSecondArr[] = "59";
sprintf(showSecondArr,"%02d",showSecond);
tft.setCursor(timeX + 6 * timeSize * 6,timeY);//设置显示位置
tft.print(showSecondArr);//showSecondArr
}
}
void processDiscoverPacket(){//处理5089端口的发现请求
int packetSize = discoveryUdp.parsePacket();
if(packetSize){
char incomingPacket[255];
int len = discoveryUdp.read(incomingPacket,255);//读取数据
if(len > 0){
incomingPacket[len] = 0;
}
//处理发送请求
if (strcmp(incomingPacket, "DISCOVER") == 0) {
discoveryUdp.beginPacket(discoveryUdp.remoteIP(), discoveryUdp.remotePort());
discoveryUdp.printf("ESP32_IP:%s,PORT:%d", WiFi.localIP().toString().c_str(), udpPort);
discoveryUdp.endPacket();
}
}
}
void processIncomingPacket(){//处理5088端口接收的数据
int packetSize = udp.parsePacket();
if (packetSize) {
char incomingPacket[255];
int len = udp.read(incomingPacket, 255);
if (len > 0) {
incomingPacket[len] = 0;
}
//Serial.printf("Received packet of size %d from %s:%d\n", packetSize, udp.remoteIP().toString().c_str(), udp.remotePort());
//Serial.printf("Packet contents: %s\n", incomingPacket);
showLongMessage(SHOW_MODE_MYSELF,incomingPacket);
// 处理设备消息
String message = String(incomingPacket);
String deviceName = message.substring(0, message.indexOf(':'));//数据格式deviceName:stateName 例如Relay:On
String stateName = message.substring(message.indexOf(':') + 1 );
IPAddress senderIP = udp.remoteIP();
// 处理具体的设备信息
if(strcmp(stateName.c_str(),"Link") == 0){
udp.beginPacket(senderIP, udpPort);
udp.printf("OK");
udp.endPacket();
showLongMessage(SHOW_MODE_MYSELF,"Send OK!");
}
bool deviceExists = false;
for (int i = 0; i < deviceCount; i++) {
if (devices[i].name == deviceName) {
deviceExists = true;
devices[i].ip = senderIP; // 更新设备IP
break;
}
}
if (!deviceExists && deviceCount < MAX_CONNECT_COUNT) {
devices[deviceCount].name = deviceName;
devices[deviceCount].ip = senderIP;
deviceCount++;
}
Serial.println("Device List:");
for (int i = 0; i < deviceCount; i++) {
Serial.printf("Device %d: Name = %s, IP = %s\n", i + 1, devices[i].name.c_str(), devices[i].ip.toString().c_str());
}
}
}
void IRAM_ATTR onTimer() {
// 在定时器中断服务程序中更新屏幕显示
//static int timeCount = 0;
//开启滚动显示
static int i[MAX_SHOW_STRING] = {0};
for(int j = 0; j < MAX_SHOW_STRING; j++){
if(showFlag[j] == 1){//需要显示
if(i[j] <= maxCount[j] - showCount[j]){//滚动字幕显示
tft.fillRect(textShowX[j], textShowY[j], showCount[j] * 6, 8, BACKGROUND_COLOUR);//绘制矩形刷新部分区域
strncpy(textShowString[j], textMaxString[j] + i[j],showCount[j]);//从textMaxString[j]的第i[j]个字符开始,拷贝showCount[j]个字符到textShowString[j]
textShowString[j][showCount[j]] = '\0'; // 添加字符串结束符
tft.setTextColor(TEXT4_COLOUR);//设置文字颜色
tft.setTextSize(1);//设置字体大小
tft.setCursor(textShowX[j],textShowY[j]);//设置显示位置
tft.print(textShowString[j]);//显示Text4 Recived data!
i[j] += 1;
}
else{//滚动字幕显示结束 显示前showCount[j]个字符
tft.fillRect(textShowX[j], textShowY[j], showCount[j] * 6, 8, BACKGROUND_COLOUR);//绘制矩形刷新部分区域
strncpy(textShowString[j], textMaxString[j],showCount[j]);//将textMaxString[j]的前showCount[j]个字符,拷贝到textShowString[j]
textShowString[j][showCount[j]] = '\0'; // 添加字符串结束符
tft.setTextColor(TEXT4_COLOUR);//设置文字颜色
tft.setTextSize(1);//设置字体大小
tft.setCursor(textShowX[j],textShowY[j]);//设置显示位置
tft.print(textShowString[j]);//显示Text4 Recived data!
i[j] = 0;
showFlag[j] = 0;
//timerEnd(timer); // 关闭定时器
}
}
}
}
void setup() {
Serial.begin(115200);//初始化串口0用于调试
Serial2.begin(115200);//初始化串口2用于交换数据
dht.begin(); // 初始化DHT传感器
// 初始化 wifi
wifiInit(WIFI_SSID, WIFI_PASSWD);
// 启动两个UDP服务器
udp.begin(udpPort);// udpPort
discoveryUdp.begin(discoveryPort);// discoveryPort
// 初始化 iot,需传入 wifi 的 client,和设备产品信息
AliyunIoTSDK::begin(espClient, PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET, REGION_ID);
// 初始化NTP客户端
timeClient.begin();
// 配置硬件定时器
timer = timerBegin(0, 80, true); // 使用定时器0,分频器80,计数上升沿触发中断
timerAttachInterrupt(timer, &onTimer, true); // 将中断服务程序与定时器关联
timerAlarmWrite(timer, SHOWDELAY_TIME * 1000, true); // 设置定时器触发时间为SHOWDELAY_TIME*1000(单位:微秒)默认为500us*1000 = 0.5s
//timerAlarmEnable(timer); // 启用定时器
dht11DataGet();
messageShowUI(0,113 - 13 * 1,"From","ASRPRO","R","Recived data:Hello World!");
messageShowUI(0,113,"Goal","Fan","S","Send data:Hello World!");
showDataUI(0,0,0,TempStr, "Temperature");
showDataUI(80,0,1,HumiStr,"Humidity");
//showLongMessage(SHOW_MODE_MYSELF,"Hello World!");
}
void loop() {
unsigned long currentMillis = millis();//获取已运行时间
static char oldHumiStr[6] = "78";
static char oldTempStr[6] = "34.8";
// 每100毫秒处理一次数据
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
serial2DataDispose();
processIncomingPacket();//处理5088端口接收的数据
}
// 每1秒更新一次时间
if (currentMillis - previousMillisUpdate >= updateInterval) {
previousMillisUpdate = currentMillis;
timeClient.update();
unsigned long epochTime = timeClient.getEpochTime();
struct tm *ptm = gmtime((time_t *)&epochTime);
currentHour = ptm->tm_hour;
currentMinute = ptm->tm_min;
currentSecond = ptm->tm_sec;
showTimeUI();
processDiscoverPacket();//处理5089端口的发现请求
}
// 每10秒更新一次温度和湿度数据
if (currentMillis - previousMillisData >= dataInterval) {
previousMillisData = currentMillis;
dht11DataGet();
if (strcmp(oldTempStr, TempStr) != 0) {
showDataUI(0, 0, 0, TempStr, "Temperature");
strcpy(oldTempStr, TempStr);
AliyunIoTSDK::send("Temperature", temperature);//上传数据到服务器
}
if (strcmp(oldHumiStr, HumiStr) != 0) {
showDataUI(80, 0, 1, HumiStr, "Humidity");
strcpy(oldHumiStr, HumiStr);
AliyunIoTSDK::send("Humility", humidity);//上传数据到服务器
}
AliyunIoTSDK::loop(); // 你提到的保持服务器的连接,每10秒执行一次
}
}
显示部分代码可参考Arduino平台基于GFX库的滚动字幕消息显示UI设计(定时器实现刷新)
阿里云平台SDK相关代码参考使用ESP-01S与阿里云物联网平台服务器的连接与配置(温湿度检测)
ASRPRO代码
#include "asr.h"
extern "C"{ void * __dso_handle = 0 ;}
#include "setup.h"
#include "HardwareSerial.h"
#include "myLib/asr_event.h"
uint32_t snid;
void ASR_CODE();
//{speak:小蝶-清新女声,vol:4,speed:10,platform:haohaodada}
//{playid:10001,voice:欢迎使用智能家居助手,用小璐同学唤醒我。}
//{playid:10002,voice:我退下了,用小璐同学唤醒我}
/*默认循环函数
*/
void ASR_CODE(){
String Serial1_Data = "TIME:23-45-30";
String Data_Type = "0";
String Clock = "0";
String Min = "0";
switch (snid) {
case 2:
Serial1.println("ASRPRO:Led-On");
delay(10);
break;
case 3:
Serial1.println("ASRPRO:Led-Off");
delay(10);
break;
case 4:
Serial1.println("ASRPRO:Led-Up");
delay(10);
break;
case 5:
Serial1.println("ASRPRO:Led-Down");
delay(10);
break;
case 8:
Serial1.println("ASRPRO:Computer-On");
delay(10);
break;
case 9:
Serial1.println("ASRPRO:Computer-Off");
delay(10);
break;
case 10:
Serial1.println("ASRPRO:Fan-On");
delay(10);
break;
case 11:
Serial1.println("ASRPRO:Fan-Off");
delay(10);
break;
case 12:
Serial1.println("ASRPRO:Fan-Up");
delay(10);
break;
case 13:
Serial1.println("ASRPRO:Fan-Down");
delay(10);
break;
case 14:
Serial1.println("ASRPRO:Led-Up");
delay(10);
break;
case 15:
Serial1.println("ASRPRO:Led-Down");
delay(10);
break;
case 16:
Serial1.println("ASRPRO:Led-Max");
delay(10);
break;
case 17:
Serial1.println("ASRPRO:Led-Min");
delay(10);
break;
case 18:
Serial1.println("ASRPRO:Switch-On");
delay(10);
//{playid:10500,voice:开关已打开}
play_audio(10500);
break;
case 19:
Serial1.println("ASRPRO:Switch-Off");
delay(10);
//{playid:10501,voice:开关已关闭}
play_audio(10501);
break;
case 20:
Serial1.println("ASRPRO:Fan-Max");
delay(10);
break;
case 21:
Serial1.println("ASRPRO:Fan-Min");
delay(10);
break;
case 22:
Serial1.println("ASRPRO:Time-Get");
delay(500);
if(Serial1.available() > 0){
Serial1_Data = Serial1.readString();
Data_Type = String(Serial1_Data).substring(1,4);
if(String(Data_Type).indexOf("TIME",1)){
Clock = String(Serial1_Data).substring(5,7);
Min = String(Serial1_Data).substring(8,10);
//{playid:10502,voice:现在时间}
play_audio(10502);
play_num((int64_t)((String(Clock).toInt()) * 100), 1);
//{playid:10503,voice:点}
play_audio(10503);
play_num((int64_t)((String(Min).toInt()) * 100), 1);
//{playid:10504,voice:分}
play_audio(10504);
}
}
break;
case 23:
Serial1.println("ASRPRO:Sweater-Get");
delay(500);
break;
case 24:
Serial1.println("ASRPRO:Relay-On");
delay(100);
break;
case 25:
Serial1.println("ASRPRO:Relay-Off");
delay(100);
break;
}
}
void hardware_init(){
setPinFun(2,FORTH_FUNCTION);
setPinFun(3,FORTH_FUNCTION);
Serial1.begin(115200);
vTaskDelete(NULL);
}
void setup()
{
//{ID:0,keyword:"唤醒词",ASR:"小璐同学",ASRTO:"我在"}
//{ID:1,keyword:"命令词",ASR:"退下",ASRTO:"好的"}
//{ID:2,keyword:"命令词",ASR:"开灯",ASRTO:"好的,马上打开灯光"}
//{ID:3,keyword:"命令词",ASR:"关灯",ASRTO:"好的,马上关闭灯光"}
//{ID:4,keyword:"命令词",ASR:"灯亮一点",ASRTO:"好的,正在为您增加亮度"}
//{ID:14,keyword:"命令词",ASR:"亮一点",ASRTO:"好的,正在为您增加亮度"}
//{ID:5,keyword:"命令词",ASR:"灯暗一点",ASRTO:"好的,正在为您降低亮度"}
//{ID:15,keyword:"命令词",ASR:"暗一点",ASRTO:"好的,正在为您降低亮度"}
//{ID:6,keyword:"命令词",ASR:"当前温度",ASRTO:"当前温度二十七摄氏度"}
//{ID:7,keyword:"命令词",ASR:"当前湿度",ASRTO:"当前湿度百分之七十"}
//{ID:8,keyword:"命令词",ASR:"打开电脑",ASRTO:"好的,正在为您打开电脑"}
//{ID:9,keyword:"命令词",ASR:"关闭电脑",ASRTO:"好的,正在为您关闭电脑"}
//{ID:10,keyword:"命令词",ASR:"打开风扇",ASRTO:"好的,正在为您打开风扇"}
//{ID:11,keyword:"命令词",ASR:"关闭风扇",ASRTO:"好的,正在为您关闭风扇"}
//{ID:12,keyword:"命令词",ASR:"风扇快一点",ASRTO:"好的,正在增大风速"}
//{ID:13,keyword:"命令词",ASR:"风扇慢一点",ASRTO:"好的,正在减小风速"}
//{ID:16,keyword:"命令词",ASR:"亮度调到最大",ASRTO:"好的,正在调整为最大亮度"}
//{ID:17,keyword:"命令词",ASR:"亮度调到最小",ASRTO:"好的,正在调整为最小亮度"}
//{ID:18,keyword:"命令词",ASR:"打开开关",ASRTO:"好的,正在为您打开开关"}
//{ID:19,keyword:"命令词",ASR:"关闭开关",ASRTO:"好的,正在为您关闭开关"}
//{ID:20,keyword:"命令词",ASR:"风速调到最大",ASRTO:"好的,正在调整为最大风速"}
//{ID:21,keyword:"命令词",ASR:"风速调到最小",ASRTO:"好的,正在调整为最小风速"}
//{ID:22,keyword:"命令词",ASR:"几点了",ASRTO:"正在查询"}
//{ID:23,keyword:"命令词",ASR:"今天天气怎么样",ASRTO:"正在查询"}
//{ID:24,keyword:"命令词",ASR:"打开继电器",ASRTO:"好的,正在为您打开继电器"}
//{ID:25,keyword:"命令词",ASR:"关闭继电器",ASRTO:"好的,正在为您关闭继电器"}
//{playid:10084,voice:零}
//{playid:10085,voice:一}
//{playid:10086,voice:二}
//{playid:10087,voice:三}
//{playid:10088,voice:四}
//{playid:10089,voice:五}
//{playid:10090,voice:六}
//{playid:10091,voice:七}
//{playid:10092,voice:八}
//{playid:10093,voice:九}
//{playid:10094,voice:十}
//{playid:10095,voice:百}
//{playid:10096,voice:千}
//{playid:10097,voice:万}
//{playid:10098,voice:亿}
//{playid:10099,voice:负}
//{playid:10100,voice:点}
}
应用场景
智能家居:
配合其他设备实现家庭设备的智能化管理,如照明、温控等。
项目进展
已完成部分功能以及测试,包括但不限于:
- 本地语音控制
- 控制程序开发
- 手机、电脑网页端控制
- 阿里云物联网平台后台控制
- 设备间通信及控制
- 温湿度数据获取、显示以及上传
- 网络实时时间获取及显示
- 语音报时
后续进行完善及补充,后续将在立创开源社区进行软硬件开源,后续以开源社区更新为准,此博客不实时更新
点击前往立创开源社区查看:智能家居助手
演示视频
语音控制继电器以及LED灯演示
阿里云物联网平台应用演示