基于Stm32和Esp8266的智能药箱设计与阿里云物联网集成
基于Stm32和Esp8266的阿里云物联网设计(智能药箱)
本系统以STM32为核心,利用温湿度传感器,光敏传感器,语音模块,物联网时钟,Wi-Fi模块Esp8266共同实现完成的。
文章目录
前言
提示:这里可以添加本文要记录的大概内容:
本系统以STM32为核心,利用温湿度传感器,光敏传感器,语音模块,物联网时钟,Wi-Fi模块Esp8266共同实现完成的。
一、硬件模块
- stm32f103c8t6单片机
- esp8266模块
- DHT11
- 光敏传感器
- ASR-PRO语音模块
- 0.96OLED
- st-link,ch340串口模块、线材、面包板灯等
二、接线
1. esp8266接线
esp8266模块—————————–stm32模块
GND引脚———————————GND引脚
3V3引脚———————————–3V3引脚
Rx引脚————————————–A9引脚(Tx)
Tx引脚————————————-A10引脚(Rx)
2. DHT11接线
DHT11模块—————————–stm32模块
GND引脚———————————GND引脚
VCC引脚———————————–3V3引脚
DATA引脚———————————-B15引脚
3. 光敏传感器接线
DHT11模块—————————–stm32模块
GND引脚———————————GND引脚
VCC引脚———————————–3V3引脚
DO引脚————————————-A0引脚
4. 语音模块接线
AS-PRO模块—————————–stm32模块
GND引脚———————————GND引脚
VCC引脚———————————–5V引脚
A1引脚————————————-A2引脚
A2引脚————————————-A3引脚
外设接线
喇叭正负对应SPK+和SPK-
麦克风正负对应MIC+和MIC-
烧录接线
AS-PRO模块—————————-CH340模块
GND引脚———————————GND引脚
VCC引脚———————————–5V引脚
PB5引脚————————————Rx引脚
PB6引脚————————————Tx引脚
具体参考链接:天问ASRPRO资料汇总
5. OLED屏幕接线
OLED模块—————————–stm32模块
GND引脚———————————GND引脚
VCC引脚———————————-3V3引脚
SCL引脚————————————8引脚
SDA引脚————————————9引脚
三、ESP8266固件烧录
参考教程链接: 开源 单片机通过ESP8266连接阿里云(1)固件烧录
1. 下载固件
固件链接: MQTT透传AT固件(固件号:1471)
2. 下载烧录软件
烧录软件链接: 烧录WiFi固件工具:ESP_DOWNLOAD_TOOL
3. 固件烧录
4. 固件测试
通过串口调试助手在波特率为115200, 发送如下指令
#设置模式
AT+CWMODE=1
#复位
AT+RST
# 连接WIFI 第一项为自己的WIFI名字 第二项为WIFI密码
AT+CWJAP="WIFI","12345678"
# 查看现在的时间
AT+CIPSNTPTIME?
AT+CIPSNTPCFG=1,8,"ntp.aliyun.com"
均能正常返回OK则说明固件烧录成功
四、串口调试助手调试ESP8266
1. 查看连接设备的MQTT参数
2. 发送MQTT用户信息
# 格式: AT+MQTTUSERCFG=0,1,"NULL","username","passwd",0,0,""
AT+MQTTUSERCFG=0,1,"NULL","ESP-xxxxxxxx","723xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",0,0,""
3. 发送MQTT ID信息
# 格式: AT+MQTTCLIENTID=0,"产品名.设备名|securemode=x\,signmethod=xxxxxxxx\,timestamp=xxxxxxxxxx|"
# 注意需要在对应位置的逗号","前面添加一个反斜杠"\"
AT+MQTTCLIENTID=0,"k0xxxxxx.ESP-01S|securemode=x\,signmethod=xxxxxx\,timestamp=xxxxxxxxxx|"
4. 发送MQTT URL信息
# 格式: AT+MQTTCONN=0,"mqttHostUrl",port,1
AT+MQTTCONN=0,"iot-xxxxxxxxxx.mqtt.iothub.aliyuncs.com",1883,1
均返回OK, 说明已经连接上阿里云, 在平台也可以看到设备上线
5. 发送数据到云端
Topic查看
# 格式 AT+MQTTPUB=0,"/sys/产品名/设备名/thing/event/property/post","{\"params\":{\"数据标签\":数据}}",1,0
AT+MQTTPUB=0,"/sys/k0txxxxxxx/ESP-01S/thing/event/property/post","{\"params\":{\"temperature\":25.0}}",1,0
四、stm32串口调试ESP8266
1. MQTT 参数
#define clientId "k0xxxxx.ESP-01S|securemode=x\\,signmethod=xxxxxxx\\,timestamp=xxxxxxxxxx|"
#define username "ESP-01S&k0xxxxxxxxxxxxxxx"
#define passwd "72xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
#define mqttHostUrl "iot-xxxxxxxxxxxxx.mqtt.iothub.aliyuncs.com"
#define port "1883"
#define post "/sys/k0xxxx/ESP-01S/thing/event/property/post"
#define WIFI_Name "WIFI"
#define WIFI_Pass "12345678"
2. MQTT 连接
注意相比于上面串口调试助手发送,在引号内(即字符串内)嵌套的 引号以及反斜杠 均需加转义字符即"表示"和\表示\
void ESP8266_Init(void)
{
// 定义一个足够大的字符数组来存储合并后的字符串
char command[140];
memset(command, 0, sizeof(command)); //清空command数组
//发送WIFI模式信息
OLED_ShowString(3,1, " ");
OLED_ShowString(3,1, "WIFI_MODE");
sprintf(command, "AT+CWMODE=1\r\n");
// Serial2_SendString(command); //发送串口2给电脑看指令情况
ESP8266_Init_Send(command, "OK1", "OR1");
//发送WIFI连接信息
OLED_ShowString(3,1, " ");
OLED_ShowString(3,1, "WIFI_CONN");
sprintf(command, "AT+CWJAP=\"%s\",\"%s\"\r\n", WIFI_Name, WIFI_Pass);
// Serial2_SendString(command); //发送串口2给电脑看指令情况
ESP8266_Init_Send(command, "OK2", "OR2");
//发送MQTT连接用户信息
OLED_ShowString(3,1, " ");
OLED_ShowString(3,1, "MQTT_USER");
sprintf(command, "AT+MQTTUSERCFG=0,1,\"NULL\",\"%s\"\,\"%s\"\,0,0,\"\"\r\n",username,passwd);
// Serial2_SendString(command); //发送串口2给电脑看指令情况
ESP8266_Init_Send(command, "OK3", "OR3");
//发送MQTT连接ID信息
OLED_ShowString(3,1, " ");
OLED_ShowString(3,1, "MQTT_ID");
sprintf(command, "AT+MQTTCLIENTID=0,\"%s\"\r\n",clientId);
// Serial2_SendString(command); //发送串口2给电脑看指令情况
ESP8266_Init_Send(command, "OK4", "OR4");
//发送MQTT连接URL信息
OLED_ShowString(3,1, " ");
OLED_ShowString(3,1, "MQTT_URL");
sprintf(command, "AT+MQTTCONN=0,\"%s\",%s,1\r\n",mqttHostUrl,port);
// Serial2_SendString(command); //发送串口2给电脑看指令情况
ESP8266_Init_Send(command, "OK5", "OR5");
//获取连接的时间服务器
OLED_ShowString(3,1, " ");
OLED_ShowString(3,1, "TIME_CONN");
sprintf(command, "AT+CIPSNTPCFG=1,8,\"ntp.aliyun.com\"\r\n");
// Serial2_SendString(command); //发送串口2给电脑看指令情况
ESP8266_Init_Send(command, "OK6", "OR6");
OLED_ShowString(3,1, " ");
OLED_ShowString(3,1, "TIME_GET");
Delay_s(1);
}
3. MQTT 发送
void ESP8266_Send(char *property1, float Data1, char *property2, u8 Data2, char *property3, u8 Data3, char *property4, u8 Data4, char *property5, u8 Data5, char *property6, u8 Data6)
{
// 定义一个足够大的字符数组来存储合并后的字符串
char command[260];
memset(command, 0, sizeof(command));
// 定义消息发送格式
//sprintf(command, "AT+MQTTPUB=0,\"%s\",\"{\\\"params\\\":{\\\"%s\\\":%d.%d}}\",1,0\r\n", post, property, Data_int, Data_decimal); //第四个参数设置为0表示不要响应
sprintf(command, "AT+MQTTPUB=0,\"%s\",\"{\\\"params\\\":{\\\"%s\\\":%.1f\\, \\\"%s\\\":%d\\, \\\"%s\\\":%d\\, \\\"%s\\\":%d\\, \\\"%s\\\":%d\\, \\\"%s\\\":%d\\}}\",0,0\r\n", post, property1, Data1, property2, Data2, property3, Data3, property4, Data4, property5, Data5, property6, Data6); //第四个参数设置为0表示不要响应
// 发送消息
Serial_SendString(command);
// Serial2_SendString(command); //发送串口2给电脑看指令情况
// 释放内存空间
free(command);
// 在OLED上显示OK_S
// OLED_ShowString(1,8,"OK_S");
}
4. MQTT 接收
u8 ESP8266_Received(u8 *hour_set, u8 *minute_set, u8 *temp_set, u8 *humi_set, u8 *hour_set2, u8 *minute_set2)
// void ESP8266_Received(u8 *hour_set, u8 *minute_set, u8 *second_set)
{
char String[250];
memset(String, 0, sizeof(String)); //清空String数组
Serial_ReceiveString(String);
// Serial2_SendString(String); //发送串口2给电脑看指令情况
//解析出属性名对应的值 例如解析出LEDSwitch的值 {"LEDSwitch":0},
//查找PRO在String中的位置 例如查找字符串 "LEDSwitch" 在String中的位置, 返回指向第一次出现的位置的指针 即字符串第一个字符引号的位置
char *params_start = strstr(String, "\"params\":{");
if (params_start != NULL) {
char *hour_start = strstr(params_start, "\"hour_set\":");
if (hour_start != NULL) {
int hour;
sscanf(hour_start, "\"hour_set\":%d", &hour);
*hour_set = hour;
}
char *minute_start = strstr(params_start, "\"minute_set\":");
if (minute_start != NULL) {
int minute;
sscanf(minute_start, "\"minute_set\":%d", &minute);
*minute_set = minute;
}
char *temp_set_start = strstr(params_start, "\"temp_set\":");
if (temp_set_start != NULL) {
int temp;
sscanf(temp_set_start, "\"temp_set\":%d", &temp);
*temp_set = temp;
}
char *humi_set_start = strstr(params_start, "\"humi_set\":");
if (humi_set_start != NULL) {
int humi;
sscanf(humi_set_start, "\"humi_set\":%d", &humi);
*humi_set = humi;
}
char *hour_start2 = strstr(params_start, "\"hour_set2\":");
if (hour_start2 != NULL) {
int hour2;
sscanf(hour_start2, "\"hour_set2\":%d", &hour2);
*hour_set2 = hour2;
}
char *minute_start2 = strstr(params_start, "\"minute_set2\":");
if (minute_start2 != NULL) {
int minute2;
sscanf(minute_start2, "\"minute_set2\":%d", &minute2);
*minute_set2 = minute2;
}
return 1;
}
return 0;
}
5. 完整源码
- Esp8266.h
#ifndef __ESP8266_H
#define __ESP8266_H
/*MQTT 连接参数*/
#define clientId "k0xxxxx.ESP-01S|securemode=x\\,signmethod=xxxxxxx\\,timestamp=xxxxxxxxxx|"
#define username "ESP-01S&k0xxxxxxxxxxxxxxx"
#define passwd "72xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
#define mqttHostUrl "iot-xxxxxxxxxxxxx.mqtt.iothub.aliyuncs.com"
#define port "1883"
#define post "/sys/k0xxxx/ESP-01S/thing/event/property/post"
#define WIFI_Name "WIFI"
#define WIFI_Pass "12345678"
void ESP8266_Init_Send(char *command, char *OK, char *OR); //初始化发送函数
void ESP8266_Init(void); //初始化函数
void ESP8266_Time_Get(u8 *hour, u8 *minute, u8 *second); //获取时间函数
u8 Serial_ReceiveString_Flag(void); //串口接收标志位
// void ESP8266_Send(char *property1, float Data1, char *property2, float Data2, char *property3, u8 Data3, char *property4, u8 Data4, char *property5, u8 Data5, char *property6, u8 Data6, char *property7, u8 Data7, char *property8, u8 Data8); //发送函数
void ESP8266_Send(char *property1, float Data1, char *property2, u8 Data2, char *property3, u8 Data3, char *property4, u8 Data4, char *property5, u8 Data5, char *property6, u8 Data6);
u8 ESP8266_Received(u8 *hour_set, u8 *minute_set, u8 *temp_set, u8 *humi_set, u8 *hour_set2, u8 *minute_set2); //接收函数
// void ESP8266_Received(u8 *hour_set, u8 *minute_set, u8 *second_set);
#endif
- Esp8266.c
#include "stm32f10x.h" // Device header
#include "Serial.h"
#include "Esp8266.h"
#include "Delay.h"
#include "OLED.h"
#include <string.h>
/*************************
* 函数名称:ESP8266_Init
* 函数功能:初始化ESP8266
* 输入参数:无
* 输出参数:无
* 调用示例:ESP8266_Init();
**************************/
void ESP8266_Init(void)
{
// 定义一个足够大的字符数组来存储合并后的字符串
char command[140];
memset(command, 0, sizeof(command)); //清空command数组
//发送WIFI模式信息
OLED_ShowString(3,1, " ");
OLED_ShowString(3,1, "WIFI_MODE");
sprintf(command, "AT+CWMODE=1\r\n");
// Serial2_SendString(command); //发送串口2给电脑看指令情况
ESP8266_Init_Send(command, "OK1", "OR1");
//发送WIFI连接信息
OLED_ShowString(3,1, " ");
OLED_ShowString(3,1, "WIFI_CONN");
sprintf(command, "AT+CWJAP=\"%s\",\"%s\"\r\n", WIFI_Name, WIFI_Pass);
// Serial2_SendString(command); //发送串口2给电脑看指令情况
ESP8266_Init_Send(command, "OK2", "OR2");
//发送MQTT连接用户信息
OLED_ShowString(3,1, " ");
OLED_ShowString(3,1, "MQTT_USER");
sprintf(command, "AT+MQTTUSERCFG=0,1,\"NULL\",\"%s\"\,\"%s\"\,0,0,\"\"\r\n",username,passwd);
// Serial2_SendString(command); //发送串口2给电脑看指令情况
ESP8266_Init_Send(command, "OK3", "OR3");
//发送MQTT连接ID信息
OLED_ShowString(3,1, " ");
OLED_ShowString(3,1, "MQTT_ID");
sprintf(command, "AT+MQTTCLIENTID=0,\"%s\"\r\n",clientId);
// Serial2_SendString(command); //发送串口2给电脑看指令情况
ESP8266_Init_Send(command, "OK4", "OR4");
//发送MQTT连接URL信息
OLED_ShowString(3,1, " ");
OLED_ShowString(3,1, "MQTT_URL");
sprintf(command, "AT+MQTTCONN=0,\"%s\",%s,1\r\n",mqttHostUrl,port);
// Serial2_SendString(command); //发送串口2给电脑看指令情况
ESP8266_Init_Send(command, "OK5", "OR5");
//获取连接的时间服务器
OLED_ShowString(3,1, " ");
OLED_ShowString(3,1, "TIME_CONN");
sprintf(command, "AT+CIPSNTPCFG=1,8,\"ntp.aliyun.com\"\r\n");
// Serial2_SendString(command); //发送串口2给电脑看指令情况
ESP8266_Init_Send(command, "OK6", "OR6");
OLED_ShowString(3,1, " ");
OLED_ShowString(3,1, "TIME_GET");
Delay_s(1);
}
/*************************
* 函数名称:ESP8266_Time_Get
* 函数功能:ESP8266的获取网络时间
* 输入参数:u8 *hour, u8 *minute, u8 *second 时分秒
* 输出参数:无
* 调用示例:ESP8266_Time_Get(&hour, &minute, &second);
**************************/
void ESP8266_Time_Get(u8 *hour, u8 *minute, u8 *second)
{
//定义一个足够大的字符数组来存储合并后的字符串
char command[140];
memset(command, 0, sizeof(command)); //清空command数组
//获取时间
sprintf(command, "AT+CIPSNTPTIME?\r\n");
Serial_SendString(command); //发送串口1发给ESP8266
// Serial2_SendString(command); //发送串口2给电脑看指令情况
//解析返回的时间
//定义一个足够大的字符数组来存储接收到的字符串
char String[170];
memset(String, 0, sizeof(String)); //清空String数组
//定义一个计数器
u8 i = 0;
while(1)
{
//等待串口接收到数据
while(Serial_GetRxFlag() == 0);
//将接收到的数据存储到String数组中
String[i] = Serial_GetRxData();
//计数器加1
i++;
//判断接收到的数据是否为OK
if(String[i-1] == 'K' && String[i-2] == 'O')
{
//数组末端加上换行符
String[i] = '\r';
String[i+1] = '\n';
// Serial2_SendString(String); //发送串口2给电脑看指令情况
char *time_start = strstr(String, ":");
if (time_start == NULL) {
// 如果没有找到冒号,解析失败,返回
OLED_ShowString(3,11,"OR7");
break;
}
char *time_second = strstr(time_start+1, ":");
if (time_second == NULL) {
// 如果没有找到冒号,解析失败,返回
OLED_ShowString(3,11,"OR7");
break;
}
// Serial2_SendNumber(*time_second, 2);
//解析出时间
char hour_str[2];
char minute_str[2];
char second_str[2];
memset(hour_str, 0, sizeof(hour_str));
memset(minute_str, 0, sizeof(minute_str));
memset(second_str, 0, sizeof(second_str));
strncpy(hour_str, time_second-2, 2);
strncpy(minute_str, time_second+1, 2);
strncpy(second_str, time_second+4, 2);
// Serial2_SendString(hour_str);
// Serial2_SendString(":");
// Serial2_SendString(minute_str);
// Serial2_SendString(":");
// Serial2_SendString(second_str);
*hour = atoi(hour_str);
*minute = atoi(minute_str);
*second = atoi(second_str);
// Serial2_SendNumber(*hour, 2);
// Serial2_SendString(":");
// Serial2_SendNumber(*minute, 2);
// Serial2_SendString(":");
// Serial2_SendNumber(*second, 2);
// Serial2_SendString("\r\n");
// OLED_ShowNum(4,1,12, 2);
// OLED_ShowString(4,3,":");
// OLED_ShowNum(4,4,*minute, 2);
// OLED_ShowString(4,6,":");
// OLED_ShowNum(4,7,*second, 2);
//在OLED上显示OK
OLED_ShowString(3,11,"OK7");
//返回1
// return 1;
break;
}
//判断接收到的数据是否为RO
else if(String[i-1] == 'R' && String[i-2] == 'O')
{
//数组末端加上换行符
String[i] = '\r';
String[i+1] = '\n';
// Serial2_SendString(String); //发送串口2给电脑看指令情况
//返回0
// return 0;
break;
}
}
Delay_ms(1000);
}
/*************************
* 函数名称:Serial_ReceiveString_Flag
* 函数功能:对串口接收到的数据进行处理
* 输入参数:无
* 输出参数:u8 1/0 1表示接收到OK,0表示接收到RO
* 调用示例:Serial_ReceiveString_Flag();
**************************/
u8 Serial_ReceiveString_Flag(void)
{
//定义一个足够大的字符数组来存储接收到的字符串
char String[170];
memset(String, 0, sizeof(String)); //清空String数组
//定义一个计数器
u8 i = 0;
while(1)
{
//等待串口接收到数据
while(Serial_GetRxFlag() == 0);
//将接收到的数据存储到String数组中
String[i] = Serial_GetRxData();
//计数器加1
i++;
//判断接收到的数据是否为OK
if(String[i-1] == 'K' && String[i-2] == 'O')
{
//数组末端加上换行符
String[i] = '\r';
String[i+1] = '\n';
// Serial2_SendString(String); //发送串口2给电脑看指令情况
//返回1
return 1;
}
//判断接收到的数据是否为RO
else if(String[i-1] == 'R' && String[i-2] == 'O')
{
//数组末端加上换行符
String[i] = '\r';
String[i+1] = '\n';
// Serial2_SendString(String); //发送串口2给电脑看指令情况
//返回0
return 0;
}
}
}
/*************************
* 函数名称:ESP8266_Init_Send
* 函数功能:初始化ESP8266
* 输入参数:char *command, char *OK, char *OR 指令,OK,OR
* 输出参数:无
* 调用示例:ESP8266_Init_Send(command, "OK1", "OR1");
**************************/
void ESP8266_Init_Send(char *command, char *OK, char *OR)
{
//用于判断是否接收到OK的标志位
u8 OK_OR_flag = 0;
//发送指令
Serial_SendString(command);
//检测接收的数据是否为OK,将接收标志位赋值给OK_OR_flag
OK_OR_flag = Serial_ReceiveString_Flag();
//判定接收的最后两个字符是不是OK,如果是
if (OK_OR_flag == 1)
{
//在OLED上显示OK
OLED_ShowString(3,11, OK);
}
//如果不是,再发送1次指令
else
{
//再发送一次指令
Delay_ms(1000);
Serial_SendString(command);
//再次检测接收的数据是否为OK,将接收标志位赋值给OK_OR_flag
OK_OR_flag = Serial_ReceiveString_Flag();
if (OK_OR_flag == 1)
{
//在OLED上显示OK
OLED_ShowString(3,11, OK);
}
else
{
//在OLED上显示OR
OLED_ShowString(3,11, OR);
}
}
Delay_ms(1000);
}
/*************************
* 函数名称:ESP8266_Send
* 函数功能:发送数据到云端
* 输入参数:char *property1, float Data1, char *property2, float Data2, char *property3, u8 Data3, char *property4, u8 Data4, char *property5, u8 Data5, char *property6, u8 Data6, char *property7, u8 Data7, char *property8, u8 Data8
* 输出参数:无
* 调用示例:ESP8266_Send("temperature", 25.5, "humidity", 50.5, "light", 1, "hour_set", 12, "minute_set", 30, "second_set", 0, "temp_set", 30, "humi_set", 50);
**************************/
// void ESP8266_Send(char *property1, float Data1, char *property2, float Data2, char *property3, u8 Data3, char *property4, u8 Data4, char *property5, u8 Data5, char *property6, u8 Data6, char *property7, u8 Data7, char *property8, u8 Data8)
// {
// // 定义一个足够大的字符数组来存储合并后的字符串
// char command[270];
// memset(command, 0, sizeof(command));
// // 定义消息发送格式
// //sprintf(command, "AT+MQTTPUB=0,\"%s\",\"{\\\"params\\\":{\\\"%s\\\":%d.%d}}\",1,0\r\n", post, property, Data_int, Data_decimal); //第四个参数设置为0表示不要响应
// sprintf(command, "AT+MQTTPUB=0,\"%s\",\"{\\\"params\\\":{\\\"%s\\\":%.1f\\, \\\"%s\\\":%.1f\\, \\\"%s\\\":%d\\, \\\"%s\\\":%d\\, \\\"%s\\\":%d\\, \\\"%s\\\":%d\\, \\\"%s\\\":%d\\, \\\"%s\\\":%d\\}}\",0,0\r\n", post, property1, Data1, property2, Data2, property3, Data3, property4, Data4, property5, Data5, property6, Data6, property7, Data7, property8, Data8); //第四个参数设置为0表示不要响应
// // 发送消息
// Serial_SendString(command);
// Serial2_SendString(command); //发送串口2给电脑看指令情况
// // 在OLED上显示OK_S
// // OLED_ShowString(1,8,"OK_S");
// }
void ESP8266_Send(char *property1, float Data1, char *property2, u8 Data2, char *property3, u8 Data3, char *property4, u8 Data4, char *property5, u8 Data5, char *property6, u8 Data6)
{
// 定义一个足够大的字符数组来存储合并后的字符串
char command[260];
memset(command, 0, sizeof(command));
// 定义消息发送格式
//sprintf(command, "AT+MQTTPUB=0,\"%s\",\"{\\\"params\\\":{\\\"%s\\\":%d.%d}}\",1,0\r\n", post, property, Data_int, Data_decimal); //第四个参数设置为0表示不要响应
sprintf(command, "AT+MQTTPUB=0,\"%s\",\"{\\\"params\\\":{\\\"%s\\\":%.1f\\, \\\"%s\\\":%d\\, \\\"%s\\\":%d\\, \\\"%s\\\":%d\\, \\\"%s\\\":%d\\, \\\"%s\\\":%d\\}}\",0,0\r\n", post, property1, Data1, property2, Data2, property3, Data3, property4, Data4, property5, Data5, property6, Data6); //第四个参数设置为0表示不要响应
// 发送消息
Serial_SendString(command);
// Serial2_SendString(command); //发送串口2给电脑看指令情况
// 释放内存空间
free(command);
// 在OLED上显示OK_S
// OLED_ShowString(1,8,"OK_S");
}
/*************************
* 函数名称:ESP8266_Received
* 函数功能:接收云端消息 并解析数据格式
* 输入参数:char *PRO 属性名
* 输出参数:无
* 调用示例:ESP8266_Received("temperature");
* 数据格式示例: "/sys/k0twlspmAZo/ESP-01S/thing/service/property/set",99,{"method":"thing.service.property.set","id":"159665729","params":{"LEDSwitch":0},"version":"1.0.0"} 解析出LEDSwitch的值
* **************************/
// u8 ESP8266_Received(char *PRO, u8 *value)
// {
// //判断串口1中断标志位是否被设置
// // if(Serial_GetRxFlag() == 1)
// // {
// char String[180];
// memset(String, 0, sizeof(String)); //清空String数组
// Serial_ReceiveString(String);
// Serial2_SendString(String); //发送串口2给电脑看指令情况
// //解析出属性名对应的值 例如解析出LEDSwitch的值 {"LEDSwitch":0},
// //查找PRO在String中的位置 例如查找字符串 "LEDSwitch" 在String中的位置, 返回指向第一次出现的位置的指针 即字符串第一个字符引号的位置
// char *p = strstr(String, PRO);
// //如果找到了
// if(p != NULL)
// {
// //获取字符串"LEDSwitch"的长度
// u8 len = strlen(PRO);
// // Serial2_SendNumber(len, 2);
// // Serial2_SendString("\r\n");
// //查找第一个}在String中的位置
// char *q = strstr(String, "}");
// if(q == NULL)
// {
// return 0;
// }
// //定义一个足够大的字符数组来存储解析出的数据
// char Data[20];
// memset(Data, 0, sizeof(Data)); //清空Data数组 没有这个 第一次会有bug
// // Serial2_SendString(Data);
// // Serial2_SendString("\r\n");
// //将"LEDSwitch":与"}"的之间的值存储到Data数组中
// strncpy(Data, p+len+2, q-p-len-2); // strncpy(目标数组, 源数组, 复制的长度)
// // strncpy(Data, p+len+2, 1); // strncpy(目标数组, 源数组, 复制的长度)
// //清空OLED第四行显示
// // OLED_ShowString(4,1, " ");
// // //将Data数组中的数据字符在OLED上显示
// // OLED_ShowString(4,1, Data);
// // Serial2_SendString(Data);
// // Serial2_SendString("\r\n");
// //将Data数组中的数据转化为整数
// *value = atoi(Data);
// //清楚所有的字符数组
// memset(String, 0, sizeof(String));
// memset(Data, 0, sizeof(Data));
// return 1;
// // memset(Data, 0, sizeof(Data));
// }
// return 0;
// // }
// }
/********************
* 函数名称:ESP8266_Received
* 函数功能:接收云端消息 并解析数据格式
* 输入参数:u8 *hour, u8 *minute, u8 *second 时分秒 u8 *temp_set, u8 *humi_set 温度湿度阈值
* 输出参数:无
* 调用示例:ESP8266_Received(&hour, &minute, &temp_set, &humi_set);
*********************/
u8 ESP8266_Received(u8 *hour_set, u8 *minute_set, u8 *temp_set, u8 *humi_set, u8 *hour_set2, u8 *minute_set2)
// void ESP8266_Received(u8 *hour_set, u8 *minute_set, u8 *second_set)
{
char String[250];
memset(String, 0, sizeof(String)); //清空String数组
Serial_ReceiveString(String);
// Serial2_SendString(String); //发送串口2给电脑看指令情况
//解析出属性名对应的值 例如解析出LEDSwitch的值 {"LEDSwitch":0},
//查找PRO在String中的位置 例如查找字符串 "LEDSwitch" 在String中的位置, 返回指向第一次出现的位置的指针 即字符串第一个字符引号的位置
char *params_start = strstr(String, "\"params\":{");
if (params_start != NULL) {
char *hour_start = strstr(params_start, "\"hour_set\":");
if (hour_start != NULL) {
int hour;
sscanf(hour_start, "\"hour_set\":%d", &hour);
*hour_set = hour;
}
char *minute_start = strstr(params_start, "\"minute_set\":");
if (minute_start != NULL) {
int minute;
sscanf(minute_start, "\"minute_set\":%d", &minute);
*minute_set = minute;
}
char *temp_set_start = strstr(params_start, "\"temp_set\":");
if (temp_set_start != NULL) {
int temp;
sscanf(temp_set_start, "\"temp_set\":%d", &temp);
*temp_set = temp;
}
char *humi_set_start = strstr(params_start, "\"humi_set\":");
if (humi_set_start != NULL) {
int humi;
sscanf(humi_set_start, "\"humi_set\":%d", &humi);
*humi_set = humi;
}
char *hour_start2 = strstr(params_start, "\"hour_set2\":");
if (hour_start2 != NULL) {
int hour2;
sscanf(hour_start2, "\"hour_set2\":%d", &hour2);
*hour_set2 = hour2;
}
char *minute_start2 = strstr(params_start, "\"minute_set2\":");
if (minute_start2 != NULL) {
int minute2;
sscanf(minute_start2, "\"minute_set2\":%d", &minute2);
*minute_set2 = minute2;
}
return 1;
}
return 0;
}
五、传感器数据读取
部分代码参考链接 江协科技STM32入门教程-2023版 细致讲解 中文字幕
1. DHT11(单总线)
- DHT11.h
#ifndef __DHT11_H
#define __DHT11_H
// 定义DHT11的数据引脚
#define DHT11_RCC RCC_APB2Periph_GPIOB
#define DHT11_IO GPIOB
#define DHT11_PIN GPIO_Pin_15
// 函数声明
u8 DHT11_Init(void); // 初始化DHT11
u8 DHT11_Read_Data(float *temp, float *humi); // 读取DHT11数据
// u8 DHT11_Read_Data(u8 *temp, u8 *humi); // 读取DHT11数据
#endif
- DHT11.c
#include "stm32f10x.h" // Device header
#include "Delay.h" // 时间延时函数
#include "DHT11.h" // DHT11头文件
#include <string.h>
static void GPIO_SETOUT(void); // 定义一个静态函数GPIO_SETOUT 作用是设置为输出模式
static void DHT11_Rst(void); // 定义一个静态函数DHT11_Rst 作用是主机发送开始信号
static void GPIO_SETIN(void); // 定义一个静态函数GPIO_SETIN 作用是设置为输入模式
static u8 DHT11_Check(void); // 定义一个静态函数DHT11_Check 作用是检测DHT11的存在
/******************
* 函数名称:DHT11_SETOUT
* 函数功能:设置为输出模式
* 输入参数:无
* 输出参数:无
* 调用示例:DHT11_SETOUT();
*******************/
static void GPIO_SETOUT(void)
{
GPIO_InitTypeDef GPIO_InitStructure; // 定义一个GPIO_InitTypeDef类型的结构体变量GPIO_InitStructure
GPIO_InitStructure.GPIO_Pin = DHT11_PIN; // 设置DHT11的IO口
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 设置为推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 设置IO口速度为10MHz
GPIO_Init(DHT11_IO, &GPIO_InitStructure); // 初始化DHT11的IO口
}
/******************
* 函数名称:DHT11_SETIN
* 函数功能:设置为输入模式
* 输入参数:无
* 输出参数:无
* 调用示例:DHT11_SETIN();
*******************/
static void GPIO_SETIN(void)
{
GPIO_InitTypeDef GPIO_InitStructure; // 定义一个GPIO_InitTypeDef类型的结构体变量GPIO_InitStructure
GPIO_InitStructure.GPIO_Pin = DHT11_PIN; // 设置DHT11的IO口
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 设置为浮空输入
GPIO_Init(DHT11_IO, &GPIO_InitStructure); // 初始化DHT11的IO口
}
/******************
* 函数名称:DHT11_Rst
* 函数功能:主机发送开始信号
* 输入参数:无
* 输出参数:无
* 调用示例:DHT11_Rst();
* ****************/
static void DHT11_Rst(void) // static 说明作用域在DHT11.c文件内
{
GPIO_SETOUT(); // 设置为输出模式
// 主机拉低电平20ms
GPIO_ResetBits(DHT11_IO, DHT11_PIN); // 主机拉低DHT11的IO口
Delay_ms(20); // 延时20ms
// 主机拉高电平30us
GPIO_SetBits(DHT11_IO, DHT11_PIN); // 主机拉高DHT11的IO口
Delay_us(30); // 延时30us
}
/******************
* 函数名称:DHT11_Check
* 函数功能:检测DHT11的存在
* 输入参数:无
* 输出参数:u8 返回值1表示存在,0表示不存在
* 调用示例:DHT11_Check();
******************/
static u8 DHT11_Check(void)
{
u8 retry = 0; // 定义一个重试次数
GPIO_SETIN(); // 设置为输入模式
// 首先DHT11拉低电平80us
// 这里检测如果一直是高电平且超过100us 判定为没有响应
while ((GPIO_ReadInputDataBit(DHT11_IO, DHT11_PIN) == 1) && retry < 100) // 当DHT11的IO口为高电平时 且 重试次数小于100
{
retry++; // 重试次数加1
Delay_us(1); // 延时1us
}
if (retry >= 100) // 如果重试次数大于等于100
{
return 0; // 返回0
}
else
{
retry = 0; // 重试次数清零
}
// 接着DHT11拉高电平80us
// 这里检测如果一直是低电平且超过100us 判定为没有响应
while ((GPIO_ReadInputDataBit(DHT11_IO, DHT11_PIN) == 0) && retry < 100) // 当DHT11的IO口为低电平时 且 重试次数小于100
{
retry++; // 重试次数加1
Delay_us(1); // 延时1us
}
if (retry >= 100) // 如果重试次数大于等于100
{
return 0; // 返回0
}
return 1; // 返回1
}
/******************
* 函数名称:DHT11_Init
* 函数功能:初始化DHT11
* 输入参数:无
* 输出参数:u8 返回值1表示初始化成功,0表示初始化失败
* 调用示例:DHT11_Init();
*******************/
u8 DHT11_Init(void)
{
RCC_APB2PeriphClockCmd(DHT11_RCC, ENABLE); // 使能DHT11的时钟
DHT11_Rst(); // 主机发送开始信号
return DHT11_Check(); // 返回检测结果
}
/******************
* 函数名称:static u8 DHT11_ReadBit(void)
* 函数功能:读取一个位
* 输入参数:无
* 输出参数:u8 返回值1表示读取到1,0表示读取到0
* 调用示例:DHT11_ReadBit();
******************/
static u8 DHT11_ReadBit(void)
{
u8 retry = 0; // 定义一个重试次数
// DHT11拉低电平50us
// 这里检测如果一直是高电平且超过100us 退出循环
while ((GPIO_ReadInputDataBit(DHT11_IO, DHT11_PIN) == 1) && retry < 100) // 当DHT11的IO口为高电平时 且 重试次数小于100
{
retry++; // 重试次数加1
Delay_us(1); // 延时1us
}
retry = 0; // 重试次数清零
// 接着DHT11拉高电平26-28us或者70us
// 这里检测如果一直是低电平且超过100us 退出循环
while ((GPIO_ReadInputDataBit(DHT11_IO, DHT11_PIN) == 0) && retry < 100) // 当DHT11的IO口为低电平时 且 重试次数小于100
{
retry++; // 重试次数加1
Delay_us(1); // 延时1us
}
// 0信号为26-28us,1信号则为70us 相当于高电平的第40us时候的判断电平所处状态
Delay_us(40); // 延时40us
if ((GPIO_ReadInputDataBit(DHT11_IO, DHT11_PIN) == 1)) // 如果DHT11的IO口为高电平
{
return 1; // 返回1
}
else
{
return 0; // 返回0
}
}
/******************
* 函数名称:static u8 DHT11_ReadByte(void)
* 函数功能:读取一个字节
* 输入参数:无
* 输出参数:u8 返回值为读取到的字节
* 调用示例:DHT11_ReadByte();
*******************/
static u8 DHT11_ReadByte(void)
{
u8 i, dat = 0; // 定义一个变量i和dat
for (i = 0; i < 8; i++) // 循环8次
{
dat <<= 1; // dat左移1位
dat |= DHT11_ReadBit(); // dat或上读取到的位
}
return dat; // 返回dat
}
/******************
* 函数名称:DHT11_Read_Data
* 函数功能:读取DHT11的数据
* 输入参数:float *temp 温度指针,float *humi 湿度指针
* 输出参数:u8 返回值1表示读取成功,0表示读取失败
* 调用示例:DHT11_ReadData(&temp,&humi);如何调用
*******************/
u8 DHT11_Read_Data(float *temp, float *humi)
{
u8 buf[5]; // 定义一个长度为5的数组buf
memset(buf, 0, sizeof(buf));
u8 i; // 定义一个变量i
DHT11_Rst(); // 主机发送开始信号
if (DHT11_Check() == 1) // 如果检测DHT11存在
{
for (i = 0; i < 5; i++) // 循环5次
{
buf[i] = DHT11_ReadByte(); // 读取一个字节
}
if ((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4]) // 如果前4个字节的和等于第5个字节 说明数据正确
{
*humi = buf[0] + buf[1] / 10.0; // 湿度等于第一个字节加上第二个字节除以10
*temp = buf[2] + buf[3] / 10.0; // 温度等于第三个字节加上第四个字节除以10
// *humi = buf[0]; //将湿度值放入指针humi
// *temp = buf[2]; //将温度值放入指针temp
memset(buf, 0, sizeof(buf)); // 清空buf
return 1; // 返回1
}
}
else
{
memset(buf, 0, sizeof(buf)); // 清空buf
return 0; // 返回0
}
}
2. 光敏传感器(电平输出)
- Light.h
#ifndef __LIGHT_H
#define __LIGHT_H
#include "stm32f10x.h" // Device header
void Light_Init(void);
u8 Light_GetNum(void);
#endif
- Light.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "Light.h"
void Light_Init(void)
{
//使用GPIOA的第0引脚
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
u8 Light_GetNum(void)
{
uint8_t State = 0;
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0)
{
State = 1;
}
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 1)
{
State = 0;
}
return State;
}
3. AS-PRO语音模块(电平读取)
- ALARM.h
#ifndef __ALARM_H
#define __ALARM_H
void Alarm_Init(void);
void Alarm1_ON(void);
void Alarm1_OFF(void);
void Alarm1_Turn(void);
void Alarm2_ON(void);
void Alarm2_OFF(void);
void Alarm2_Turn(void);
#endif
- ALARM.c
#include "stm32f10x.h" // Device header
void Alarm_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_ResetBits(GPIOA, GPIO_Pin_2|GPIO_Pin_3);
}
void Alarm1_ON(void)
{
GPIO_SetBits(GPIOA, GPIO_Pin_2);
}
void Alarm1_OFF(void)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_2);// 低电平触发
}
void Alarm1_Turn(void)
{
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_2) == 0)
{
GPIO_SetBits(GPIOA, GPIO_Pin_2);
}
else
{
GPIO_ResetBits(GPIOA, GPIO_Pin_2);
}
}
void Alarm2_ON(void)
{
GPIO_SetBits(GPIOA, GPIO_Pin_3);
}
void Alarm2_OFF(void)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_3);
}
void Alarm2_Turn(void)
{
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_3) == 0)
{
GPIO_SetBits(GPIOA, GPIO_Pin_3);
}
else
{
GPIO_ResetBits(GPIOA, GPIO_Pin_3);
}
}
4. OLED屏幕(IIC)
- OLED.h
#ifndef __OLED_H
#define __OLED_H
void OLED_Init(void);
void OLED_Clear(void);
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char);
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String);
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length);
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
#endif
- OLED.c
#include "stm32f10x.h"
#include "OLED_Font.h"
/*引脚配置*/
#define OLED_W_SCL(x) GPIO_WriteBit(GPIOB, GPIO_Pin_8, (BitAction)(x))
#define OLED_W_SDA(x) GPIO_WriteBit(GPIOB, GPIO_Pin_9, (BitAction)(x))
/*引脚初始化*/
void OLED_I2C_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOB, &GPIO_InitStructure);
OLED_W_SCL(1);
OLED_W_SDA(1);
}
/**
* @brief I2C开始
* @param 无
* @retval 无
*/
void OLED_I2C_Start(void)
{
OLED_W_SDA(1);
OLED_W_SCL(1);
OLED_W_SDA(0);
OLED_W_SCL(0);
}
/**
* @brief I2C停止
* @param 无
* @retval 无
*/
void OLED_I2C_Stop(void)
{
OLED_W_SDA(0);
OLED_W_SCL(1);
OLED_W_SDA(1);
}
/**
* @brief I2C发送一个字节
* @param Byte 要发送的一个字节
* @retval 无
*/
void OLED_I2C_SendByte(uint8_t Byte)
{
uint8_t i;
for (i = 0; i < 8; i++)
{
OLED_W_SDA(Byte & (0x80 >> i));
OLED_W_SCL(1);
OLED_W_SCL(0);
}
OLED_W_SCL(1); //额外的一个时钟,不处理应答信号
OLED_W_SCL(0);
}
/**
* @brief OLED写命令
* @param Command 要写入的命令
* @retval 无
*/
void OLED_WriteCommand(uint8_t Command)
{
OLED_I2C_Start();
OLED_I2C_SendByte(0x78); //从机地址
OLED_I2C_SendByte(0x00); //写命令
OLED_I2C_SendByte(Command);
OLED_I2C_Stop();
}
/**
* @brief OLED写数据
* @param Data 要写入的数据
* @retval 无
*/
void OLED_WriteData(uint8_t Data)
{
OLED_I2C_Start();
OLED_I2C_SendByte(0x78); //从机地址
OLED_I2C_SendByte(0x40); //写数据
OLED_I2C_SendByte(Data);
OLED_I2C_Stop();
}
/**
* @brief OLED设置光标位置
* @param Y 以左上角为原点,向下方向的坐标,范围:0~7
* @param X 以左上角为原点,向右方向的坐标,范围:0~127
* @retval 无
*/
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
OLED_WriteCommand(0xB0 | Y); //设置Y位置
OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4)); //设置X位置高4位
OLED_WriteCommand(0x00 | (X & 0x0F)); //设置X位置低4位
}
/**
* @brief OLED清屏
* @param 无
* @retval 无
*/
void OLED_Clear(void)
{
uint8_t i, j;
for (j = 0; j < 8; j++)
{
OLED_SetCursor(j, 0);
for(i = 0; i < 128; i++)
{
OLED_WriteData(0x00);
}
}
}
/**
* @brief OLED显示一个字符
* @param Line 行位置,范围:1~4
* @param Column 列位置,范围:1~16
* @param Char 要显示的一个字符,范围:ASCII可见字符
* @retval 无
*/
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{
uint8_t i;
OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8); //设置光标位置在上半部分
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F8x16[Char - ' '][i]); //显示上半部分内容
}
OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8); //设置光标位置在下半部分
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]); //显示下半部分内容
}
}
/**
* @brief OLED显示字符串
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param String 要显示的字符串,范围:ASCII可见字符
* @retval 无
*/
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
uint8_t i;
for (i = 0; String[i] != '\0'; i++)
{
OLED_ShowChar(Line, Column + i, String[i]);
}
}
/**
* @brief OLED次方函数
* @retval 返回值等于X的Y次方
*/
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{
uint32_t Result = 1;
while (Y--)
{
Result *= X;
}
return Result;
}
/**
* @brief OLED显示数字(十进制,正数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~4294967295
* @param Length 要显示数字的长度,范围:1~10
* @retval 无
*/
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
}
}
/**
* @brief OLED显示数字(十进制,带符号数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:-2147483648~2147483647
* @param Length 要显示数字的长度,范围:1~10
* @retval 无
*/
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)
{
uint8_t i;
uint32_t Number1;
if (Number >= 0)
{
OLED_ShowChar(Line, Column, '+');
Number1 = Number;
}
else
{
OLED_ShowChar(Line, Column, '-');
Number1 = -Number;
}
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i + 1, Number1 / OLED_Pow(10, Length - i - 1) % 10 + '0');
}
}
/**
* @brief OLED显示数字(十六进制,正数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~0xFFFFFFFF
* @param Length 要显示数字的长度,范围:1~8
* @retval 无
*/
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i, SingleNumber;
for (i = 0; i < Length; i++)
{
SingleNumber = Number / OLED_Pow(16, Length - i - 1) % 16;
if (SingleNumber < 10)
{
OLED_ShowChar(Line, Column + i, SingleNumber + '0');
}
else
{
OLED_ShowChar(Line, Column + i, SingleNumber - 10 + 'A');
}
}
}
/**
* @brief OLED显示数字(二进制,正数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~1111 1111 1111 1111
* @param Length 要显示数字的长度,范围:1~16
* @retval 无
*/
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i, Number / OLED_Pow(2, Length - i - 1) % 2 + '0');
}
}
/**
* @brief OLED初始化
* @param 无
* @retval 无
*/
void OLED_Init(void)
{
uint32_t i, j;
for (i = 0; i < 1000; i++) //上电延时
{
for (j = 0; j < 1000; j++);
}
OLED_I2C_Init(); //端口初始化
OLED_WriteCommand(0xAE); //关闭显示
OLED_WriteCommand(0xD5); //设置显示时钟分频比/振荡器频率
OLED_WriteCommand(0x80);
OLED_WriteCommand(0xA8); //设置多路复用率
OLED_WriteCommand(0x3F);
OLED_WriteCommand(0xD3); //设置显示偏移
OLED_WriteCommand(0x00);
OLED_WriteCommand(0x40); //设置显示开始行
OLED_WriteCommand(0xA1); //设置左右方向,0xA1正常 0xA0左右反置
OLED_WriteCommand(0xC8); //设置上下方向,0xC8正常 0xC0上下反置
OLED_WriteCommand(0xDA); //设置COM引脚硬件配置
OLED_WriteCommand(0x12);
OLED_WriteCommand(0x81); //设置对比度控制
OLED_WriteCommand(0xCF);
OLED_WriteCommand(0xD9); //设置预充电周期
OLED_WriteCommand(0xF1);
OLED_WriteCommand(0xDB); //设置VCOMH取消选择级别
OLED_WriteCommand(0x30);
OLED_WriteCommand(0xA4); //设置整个显示打开/关闭
OLED_WriteCommand(0xA6); //设置正常/倒转显示
OLED_WriteCommand(0x8D); //设置充电泵
OLED_WriteCommand(0x14);
OLED_WriteCommand(0xAF); //开启显示
OLED_Clear(); //OLED清屏
}
六、软件外设配置
1. 串口配置
- Serial.h
#ifndef __SERIAL_H
#define __SERIAL_H
#include <stdio.h>
void Serial_Init(void);
void Serial2_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial2_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial2_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial2_SendString(char *String);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
void Serial2_SendNumber(uint32_t Number, uint8_t Length);
void Serial_Printf(char *format, ...);
void Serial2_Printf(char *format, ...);
void Serial_ReceiveString(char *String);
uint8_t Serial_GetRxFlag(void);
uint8_t Serial_GetRxData(void);
#endif
- Serial.c
#include "stm32f10x.h" // Device header
#include <stdio.h>
#include <stdarg.h>
#include "timer.h"
uint8_t Serial_RxData;
uint8_t Serial_RxFlag;
void Serial_Init(void)
{
//开启USART1时钟 在APB2总线上
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
//开启GPIOA时钟 在APB2总线上
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//配置A9为复用推挽输出 作为TX
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//配置A10为浮空输入 作为RX
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//配置USART1参数
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//不使用流控
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //配置 USART 为同时支持发送和接收数据
USART_InitStructure.USART_Parity = USART_Parity_No; //不进行数据奇偶校验
USART_InitStructure.USART_StopBits = USART_StopBits_1; //一个停止位
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //不用校验所以选择8位
USART_Init(USART1, &USART_InitStructure); //初始化USART1
//使能USART1接收中断 (USART_IT_RXNE 表示接收寄存器非空中断)。
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
配置NVIC 配置中断优先级分组为Group 2。这是一种设置中断优先级的方式。
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //指定中断通道为USART1串口中断
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //启动外部通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;//配置中断抢占优先级为1。
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //配置中断子优先级为1。
NVIC_Init(&NVIC_InitStructure);
USART_Cmd(USART1, ENABLE);
}
// 串口2初始化
void Serial2_Init(void)
{
// Enable USART2 clock on APB1 bus
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
// Enable GPIOA clock on APB2 bus
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// Configure GPIOA pins for USART2
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // Alternate function push-pull
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; // TX pin
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // Input floating
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; // RX pin
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// Configure USART2 parameters
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // No flow control
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; // Enable both transmission and reception
USART_InitStructure.USART_Parity = USART_Parity_No; // No parity check
USART_InitStructure.USART_StopBits = USART_StopBits_1; // 1 stop bit
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 8 data bits
USART_Init(USART2, &USART_InitStructure); // Initialize USART2
// Enable USART2 receive interrupt (USART_IT_RXNE indicates non-empty receive register interrupt)
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
// Configure NVIC with interrupt priority group 2
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; // Specify USART2 interrupt channel
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // Enable external channel
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // Configure preemption priority as 1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // Configure sub priority as 1
NVIC_Init(&NVIC_InitStructure);
USART_Cmd(USART2, ENABLE);
}
//串口1发送字
void Serial_SendByte(uint8_t Byte)
{
USART_SendData(USART1, Byte);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);//1则清零完成
}
//串口2发送字
void Serial2_SendByte(uint8_t Byte)
{
USART_SendData(USART2, Byte);
while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
}
//串口1发送数组
void Serial_SendArray(uint8_t *Array, uint16_t Length)
{
uint16_t i;
for(i = 0; i < Length; i++)
{
Serial_SendByte(Array[i]);
}
}
//串口2发送数组
void Serial2_SendArray(uint8_t *Array, uint16_t Length)
{
uint16_t i;
for(i = 0; i < Length; i++)
{
Serial2_SendByte(Array[i]);
}
}
//串口1发送字符串
void Serial_SendString(char *String)
{
uint8_t i;
for(i = 0; String[i] != '\0'; i++)//0->'\0'
{
Serial_SendByte(String[i]);
}
}
//串口2发送字符串
void Serial2_SendString(char *String)
{
uint8_t i;
for(i = 0; String[i] != '\0'; i++)
{
Serial2_SendByte(String[i]);
}
}
//计算X的Y次方
uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
uint32_t Result = 1;
while(Y--)
{
Result *= X;
}
return Result;
}
//串口1发送数字字符
void Serial_SendNumber(uint32_t Number, uint8_t Length)
{
uint8_t i;
for(i = 0; i<Length; i++)
{
Serial_SendByte(Number / Serial_Pow(10, Length-i-1)%10 + '0');
}
}
//串口2发送数字字符
void Serial2_SendNumber(uint32_t Number, uint8_t Length)
{
uint8_t i;
for(i = 0; i<Length; i++)
{
Serial2_SendByte(Number / Serial_Pow(10, Length-i-1)%10 + '0');
}
}
//重定向printf到串口
int fputc(int ch, FILE *f)
{
Serial_SendByte(ch);
return ch;
}
//串口1打印
void Serial_Printf(char *format, ...)
{
char String[100];
va_list arg;
va_start(arg, format);
vsprintf(String, format, arg);
va_end(arg);
Serial_SendString(String);
}
void Serial2_Printf(char *format, ...)
{
char String[100];
va_list arg;
va_start(arg, format);
vsprintf(String, format, arg);
va_end(arg);
Serial2_SendString(String);
}
//用于检查串口接收标志位是否被设置。
//如果 Serial_RxFlag 为1(已经接收到数据),它将清零标志位并返回1;
//否则,返回0。
uint8_t Serial_GetRxFlag(void)
{
if(Serial_RxFlag==1)
{
Serial_RxFlag=0;
return 1;
}
return 0;
}
//用于获取串口接收到的数据。字节将被存储在 Serial_RxData 中。
uint8_t Serial_GetRxData(void)
{
return Serial_RxData;
}
//一个串口接收字符串的函数,收到\r\n符号就停止接收,并返回接收的字符串。
//串口接收字符串
u8 time_out_rx = 0;
void Serial_ReceiveString(char *String)
{
uint8_t i = 0;
//启动定时器1
TIM_Cmd(TIM1, ENABLE);
//清除定时器1的更新中断标志位
TIM_ClearFlag(TIM1, TIM_FLAG_Update);
while(1)
{
while(Serial_GetRxFlag() == 0 && time_out_rx == 0); //等待接收到数据
if(time_out_rx == 1) //如果串口接收超时
{
time_out_rx = 0; //清除串口接收超时标志位
TIM_Cmd(TIM1, DISABLE); //关闭定时器1
break; //返回0
}
String[i] = Serial_GetRxData(); //将接收到的数据存储在String中
i++;
if(String[i-1] == '\n' && String[i-2] == '\r')
{
String[i] = '\0';
time_out_rx = 0; //清除串口接收超时标志位
TIM_Cmd(TIM1, DISABLE); //关闭定时器1
break;
}
}
}
//如何调用这个函数?
// char String[100];
// Serial_ReceiveString(String);
//串口1中断处理函数,运行机制是串口接收到数据时,会触发接收中断
//当USART1串口接收到数据时,会触发接收中断。
void USART1_IRQHandler(void)
{
if(USART_GetFlagStatus(USART1, USART_IT_RXNE) == SET)//检查接收中断标志位 收到数据 标志位为1
{
//如果接收到数据,它将数据存储在 Serial_RxData 中
Serial_RxData = USART_ReceiveData(USART1);//数据转存
//Serial_RxFlag 设置为1,表示已经接收到数据
Serial_RxFlag = 1;
//清除接收中断标志位。这一步是可选的,因为读取数据后,标志位会自动清零。
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
}
// //定时器1中断服务函数 用于串口接收超时
void TIM1_UP_IRQHandler(void)
{
if (TIM_GetITStatus(TIM1, TIM_IT_Update) == SET) //检查TIM1的更新中断是否发生
{
time_out_rx = 1; //设置串口接收标志位 1s超时
//清除TIM1的更新中断标志位
TIM_ClearITPendingBit(TIM1, TIM_IT_Update);
}
}
2. 定时器配置
- Timer.h
#ifndef __TIMER_H
#define __TIMER_H
void Timer1_Init(void);
void Timer2_Init(void);
#endif
- Timer.c
#include "stm32f10x.h" // Device header
//TIM2
void Timer2_Init(void)
{
//启用了TIM2定时器的时钟 TIM2是连接到APB1总线上的一个定时器。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
//配置TIM2的时钟源为内部时钟 这意味着TIM2的时钟将由微控制器的内部时钟源提供。
TIM_InternalClockConfig(TIM2);
//定时器TIM2初始化
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//配置时钟分频为1 即不分频。
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
TIM_TimeBaseInitStructure.TIM_Period = 10000-1; //10kHZ除以10000等于1s计时(计数9999次溢出实现定时1s)
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200-1; //对72M进行7200分频,得到10kHZ (1/10000s计数一次)
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //参数用于高级计数器,这里设置为0
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
//用于清除TIM2的更新中断标志位 以确保在初始化后不会立即进入中断。
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
//启用TIM2的更新中断 当TIM2的计数器溢出时,将产生更新事件并触发中断。
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//配置中断优先级分组为Group 2。
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //指定中断通道为TIM2的通道。
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //启用中断通道。
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //配置中断抢占优先级为2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //配置中断子优先级为1
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM2, ENABLE);
}
//TIM1
void Timer1_Init(void)
{
//启用了TIM1定时器的时钟 TIM1是连接到APB2总线上的一个定时器。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
//配置TIM1的时钟源为内部时钟 这意味着TIM1的时钟将由微控制器的内部时钟源提供。
TIM_InternalClockConfig(TIM1);
//定时器TIM1初始化
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//配置时钟分频为1 即不分频。
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
TIM_TimeBaseInitStructure.TIM_Period = 15000-1; //10kHZ除以10000等于1s计时(计数9999次溢出实现定时1s)
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200-1; //对72M进行7200分频,得到10kHZ (1/10000s计数一次)
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //参数用于高级计数器,这里设置为0
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStructure);
//用于清除TIM1的更新中断标志位 以确保在初始化后不会立即进入中断。
TIM_ClearFlag(TIM1, TIM_FLAG_Update);
//启用TIM1的更新中断 当TIM2的计数器溢出时,将产生更新事件并触发中断。
TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//配置中断优先级分组为Group 2。
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn; //指定中断通道为TIM1的通道。
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //启用中断通道。
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //配置中断抢占优先级为2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //配置中断子优先级为1
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM1, DISABLE);
}
//void TIM2_IRQHandler(void)
//{
// if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
// {
// TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
// }
//}
3. 延时函数配置
- Delay.h
#ifndef __DELAY_H
#define __DELAY_H
void Delay_us(uint32_t us);
void Delay_ms(uint32_t ms);
void Delay_s(uint32_t s);
#endif
- Delay.c
#include "stm32f10x.h"
/**
* @brief 微秒级延时
* @param xus 延时时长,范围:0~233015
* @retval 无
*/
void Delay_us(uint32_t xus)
{
SysTick->LOAD = 72 * xus; //设置定时器重装值
SysTick->VAL = 0x00; //清空当前计数值
SysTick->CTRL = 0x00000005; //设置时钟源为HCLK,启动定时器
while(!(SysTick->CTRL & 0x00010000)); //等待计数到0
SysTick->CTRL = 0x00000004; //关闭定时器
}
/**
* @brief 毫秒级延时
* @param xms 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_ms(uint32_t xms)
{
while(xms--)
{
Delay_us(1000);
}
}
/**
* @brief 秒级延时
* @param xs 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_s(uint32_t xs)
{
while(xs--)
{
Delay_ms(1000);
}
}
八、主函数编写
- main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
#include "Esp8266.h"
#include "DHT11.h"
#include "Timer.h"
#include "ALARM.h"
#include "Light.h"
u8 temp_humi_send_flag = 0; //温湿度数据发送标志位
u8 count_flag = 0; //计数标志位
u8 hour = 0; //时钟
u8 minute = 0; //分钟
u8 second = 0; //秒钟
int main(void)
{
OLED_Init(); // OLED初始化
Timer1_Init(); // 定时器1初始化
Serial_Init(); // 串口1初始化
// Serial2_Init(); // 串口2初始化
Alarm_Init(); // 语音初始化
Light_Init(); // 光照传感器初始化
DHT11_Init(); // DHT11初始化
OLED_ShowNum(1, 14, 0, 1);
OLED_ShowString(1, 15, "/2");
ESP8266_Init(); // ESP8266初始化
ESP8266_Time_Get(&hour, &minute, &second); //获取ESP8266的时间
OLED_ShowNum(1, 14, 1, 1);
OLED_ShowString(1, 15, "/2");
Delay_s(1);
ESP8266_Init(); // ESP8266初始化 需初始化两次 否则时间不正确
OLED_ShowNum(1, 14, 2, 1);
OLED_ShowString(1, 15, "/2");
ESP8266_Time_Get(&hour, &minute, &second); //获取ESP8266的时间
Timer2_Init(); // 定时器2初始化
OLED_Clear(); // OLED清屏
// OLED_ShowString(1,1,"DHT11 Test");
float temp, humi; //温湿度数据
u8 light_state = 0; //光照强度
u8 temp_set = 32; //温度报警阈值
u8 humi_set = 78; //湿度报警阈值
u8 hour_set = 0; //时钟报警阈值1
u8 minute_set = 0; //时钟报警阈值1
u8 second_set = 0; //时钟报警阈值1
u8 hour_set2 = 0; //时钟报警阈值1
u8 minute_set2 = 0; //时钟报警阈值1
u8 second_set2 = 0; //时钟报警阈值1
u8 temp_humi_flag = 0; //温湿度报警标志位
u8 time_flag = 0; //时钟报警标志位
u8 s_d_flag = 0;
u8 time_get_flag = 0;
while(1)
{
// 如果count_flag被设置,说明已经过了1s,此时将second加1
if(count_flag == 1)
{
time_get_flag = time_get_flag + 1;
if (time_get_flag == 600) //如果time_get_flag等于600,说明已经过了10分钟,此时重新获取时间网络校准
{
//关闭定时器
TIM_Cmd(TIM2, DISABLE);
//重新获取时间
ESP8266_Time_Get(&hour, &minute, &second);
//打开定时器
TIM_Cmd(TIM2, ENABLE);
time_get_flag = 0;
}
second = second + 1;
if(second >= 60) //如果second大于等于60,说明已经过了1分钟,此时将minute加1
{
second = 0;
minute++;
}
if(minute >= 60) //如果minute大于等于60,说明已经过了1小时,此时将hour加1
{
minute = 0;
hour++;
}
if(hour >= 24) //如果hour大于等于24,说明已经过了1天,此时将hour置0
{
hour = 0;
}
// OLED显示时间
OLED_ShowString(1,1,"Time: ");
OLED_ShowNum(1,7,hour, 2);
OLED_ShowString(1,9,":");
OLED_ShowNum(1,10,minute, 2);
OLED_ShowString(1,12,":");
OLED_ShowNum(1,13,second, 2);
// 标志位清零
count_flag = 0;
}
// 如果temp_humi_send_flag被设置,说明已经过了1s,此时读取温湿度数据
if(temp_humi_send_flag == 1)
{
// 读取温湿度数据
if(DHT11_Read_Data(&temp, &humi) == 1)
{
static char temp_str[5]; //温度字符串
static char humi_str[5]; //湿度字符串
sprintf(temp_str, "Temp: %.1f C", temp); //温度数据转化为字符串
OLED_ShowString(2,1,temp_str); //在OLED上显示温度数据
sprintf(humi_str, "Humi: %.1f %%", humi); //湿度数据转化为字符串
OLED_ShowString(3,1,humi_str); //在OLED上显示湿度数据
// 在后面显示温湿度阈值
OLED_ShowNum(2,14, temp_set, 2);
OLED_ShowNum(3,14, humi_set, 2);
}
// 如果DHT11读取数据失败
else
{
OLED_ShowString(1,1,"DHT11 Error"); //在OLED上显示DHT11错误
}
light_state = Light_GetNum(); //读取光照强度
if (light_state == 1 )
{
time_flag = 0; //说明打开过药箱
}
OLED_ShowNum(1, 16, light_state, 1); //在OLED上显示光照强度
if(s_d_flag == 0)
{
ESP8266_Send("humidity", humi, "hour_set", hour_set, "minute_set", minute_set,"temp_set", temp_set, "hour_set2", hour_set2, "minute_set2", minute_set2); //发送温湿度数据到阿里云
s_d_flag =1;
}
else
{
ESP8266_Send("temperature", temp, "switch", light_state, "humi_set",humi_set, "temp_humi_flag", temp_humi_flag, "time_flag", time_flag, "second_set", second_set); //发送温湿度数据到阿里云
s_d_flag = 0;
}
temp_humi_send_flag = 0; //标志位清零
}
// 温度湿度报警功能
if(temp > temp_set || humi > humi_set) // 如果温度或湿度大于阈值,蜂鸣警报
{
// Serial2_SendString("T_alarm\r\n"); // 串口2发送温度报警信息
Alarm1_ON(); // 蜂鸣警报
temp_humi_flag = 1; // 温度报警标志位
}
else
{
Alarm1_OFF(); // 关闭蜂鸣警报
temp_humi_flag = 0; // 温度报警标志位
}
// 时钟报警功能
if((hour == hour_set && minute == minute_set && second < 15) || (hour == hour_set2 && minute == minute_set2 && second < 15)) // 如果时间等于设置时间,语音提醒
{
// Serial2_SendString("H_alarm\r\n"); // 串口2发送时钟语音提醒
Alarm2_ON(); // 语音提醒
time_flag = 1; // 时钟报警标志位 只有打开药箱 才能消除
}
else
{
Alarm2_OFF(); // 语音提醒
}
// 判断串口1中断标志位是否被设置, 远程开关功能
if(Serial_GetRxFlag() == 1)
{
if(ESP8266_Received(&hour_set, &minute_set, &temp_set, &humi_set, &hour_set2, &minute_set2)) //接收阿里云发送的时间数据
{
ESP8266_Send("humidity", humi, "hour_set", hour_set, "minute_set", minute_set,"temp_set", temp_set, "hour_set2", hour_set2, "minute_set2", minute_set2); //发送温湿度数据到阿里云
s_d_flag =0;
}
}
// OLED显示设置的时间
OLED_ShowString(4,1,"Hint: ");
OLED_ShowNum(4,7,hour_set, 2);
OLED_ShowString(4,9,":");
OLED_ShowNum(4,10,minute_set, 2);
// OLED_ShowString(4,12,"|");
OLED_ShowNum(4,12,hour_set2, 2);
OLED_ShowString(4,14,":");
OLED_ShowNum(4,15,minute_set2, 2);
}
}
// TIM2中断服务函数
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) //检查TIM2的更新中断是否发生
{
temp_humi_send_flag = 1; //设置温湿度数据发送标志位
count_flag = 1; //设置计数变量
// ESP8266_Send("humidity", humidity); //发送湿度数据
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除TIM2的更新中断标志位
}
}
九、语音模块配置
主体思路就是 配置模块的A1\A0引脚为下拉输入模式,当检测到高电平(来自STM32 的信号)时,分别输出播放不同的音频.
十、Web端制作
具体步骤可参考我之前的文章
链接: 基于树莓派和阿里云物联网平台的传感器读取及控制
作者:Super_Chao_H