STM32智能家居项目设计

发布时间:2024-11-26 14:29

如何DIY智能家居项目 #生活技巧# #居家生活技巧# #家庭装修技巧# #智能家居应用#

前言

该项目是嵌入式课程学习最后的一个项目设计,做的不是很好(大佬勿喷…),特别是STM32数据处理部分,因为刚学的STM32,并且对C语言的指针等的使用也有些生疏(虽然学过,但大部分都忘了),不敢随便用,所以有些代码设计不好,只能完成一些简单功能。ESP8266使用的是NodeMCU开发板,用ArduinoIDE开发(因为有很多现成的库,资料也多)。APP制作用的是Android Studio开发,从网上参考了很多人的代码,最后修改成自己的。前后花了差不多2周时间(主要是中间还有课要上,一些知识也得现学),不过最后功能基本算实现了,也学到了不少东西。

具体实现的功能包括以下三个方面:

TFTLCD可以显示STM32通过DHT11模块和光敏电阻模块采集到的温度(精度±2°C)、湿度(±5%RH)、光照(0~100,最暗为0,最亮为100)信息;以及显示设置的报警温度、最低光照;还有ESP8266WIFI模块通过网络获取到的时间(年-月-日 时:分:秒)、当地城市的天气信息(气温、湿度、天气、风力)。

STM32可以在室内温度过高(超过报警温度)时驱动蜂鸣器发出声音,在室内亮度较低(低于设定的最低光照)时驱动LED点亮。

在任意可以连接网络的地方,自己设计的手机APP可以显示室内环境信息(温度、湿度、光照强度),并且可以设置报警温度(0~100°C)和最低光照(0~100)。

STM32端程序设计

功能介绍

STM32单片机通过DHT11温湿度传感器采集温湿度

通过模数转换器ADC采集光敏电阻分压后的电压,然后转成光照强度

串口接收ESP8266的信息,提取出时间、天气信息,以及报警温度、最低光照信息(来自APP,用于控制蜂鸣器、led的开闭)

当室内温度超过报警温度时,蜂鸣器启动;当室内光照较暗(低于设定的最低光照)时,led点亮

总体框图如下:

image-20211116235243617

具体实现

下面将主要介绍一些功能函数,完整代码请看:

完整代码开源地址-Gitee

完整代码开源地址-Github

温湿度采集

初始化DHT11模块

//DHT11初始化 //返回0:初始化成功,1:失败 u8 DHT11_Init() {GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG,ENABLE);GPIO_InitStructure.GPIO_Pin=DHT11;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIO_DHT11,&GPIO_InitStructure);GPIO_SetBits(GPIO_DHT11,DHT11); //拉高DHT11_Rst();return DHT11_Check(); } //复位DHT11 void DHT11_Rst() {DHT11_IO_OUT();//SET OUTPUT DHT11_DQ_OUT=0;//拉低DQ delay_ms(20);//拉低至少18ms DHT11_DQ_OUT=1;//DQ=1delay_us(30);//主机拉高20~40us } //等待DHT11的回应 //返回1:未检测到DHT11的存在 //返回0:存在 u8 DHT11_Check() {u8 retry=0;DHT11_IO_IN();//SET INPUT while (DHT11_DQ_IN&&retry<100)//DHT11会拉低40~50us{retry++;delay_us(1);};if(retry>=100)return 1;else retry=0; while (!DHT11_DQ_IN&&retry<100)//DHT11拉低后会再次拉高40~50us{retry++;delay_us(1);};if(retry>=100)return 1;return 0; }

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950

读取数据

//从DHT11读取一个位 //返回值:1/0 u8 DHT11_Read_Bit(void) { u8 retry=0; while(DHT11_DQ_IN&&retry<100)//等待变为低电平 12-14us 开始 { retry++; delay_us(1); } retry=0; while(!DHT11_DQ_IN&&retry<100)//等待变高电平 26-28us表示0,116-118us表示1 { retry++; delay_us(1); } delay_us(40);//等待40us if(DHT11_DQ_IN)return 1; else return 0; } //从DHT11读取一个字节 //返回值:读到的数据 u8 DHT11_Read_Byte(void) { u8 i,dat; dat=0; for (i=0;i<8;i++) { dat<<=1; dat|=DHT11_Read_Bit(); } return dat; } //从DHT11读取一次数据,存储到参数变量temp、humi中 //temp:温度值(范围:0~50°) //humi:湿度值(范围:20%~90%) //返回值:0,正常;1,读取失败 u8 DHT11_Read_Data(u8 *temp,u8 *humi) { u8 buf[5]; u8 i; DHT11_Rst(); if(DHT11_Check()==0) { for(i=0;i<5;i++)//读取40位数据 { buf[i]=DHT11_Read_Byte(); } if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4]) { *humi=buf[0]; *temp=buf[2]; } }else return 1; return 0; }

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859 光照采集

使用ADC3模数转换器采集光敏电阻的分压,然后转换为光照强度(转换过程把最亮的当作100,最暗当作0来作为最终结果)

首先初始化配置ADC3

//初始化光敏传感器 void Lsens_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; ADC_InitTypeDef ADC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,ENABLE);//使能PORTF时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3, ENABLE ); //使能ADC3通道时钟 RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC3, ENABLE);//ADC复位 RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC3, DISABLE);//复位结束 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;//PF8 anolog输入 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//模拟输入引脚 GPIO_Init(GPIOF, &GPIO_InitStructure); ADC_DeInit(ADC3); //复位ADC3,将外设 ADC3的全部寄存器重设为缺省值 ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//ADC工作模式: 独立模式 ADC_InitStructure.ADC_ScanConvMode = DISABLE;//模数转换工作在单通道模式 ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//模数转换工作在单次转换模式 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//转换由软件而不是外部触发启动 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//ADC数据右对齐 ADC_InitStructure.ADC_NbrOfChannel = 1;//顺序进行规则转换的ADC通道的数目 ADC_Init(ADC3, &ADC_InitStructure);//根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器 ADC_Cmd(ADC3, ENABLE);//使能指定的ADC3 ADC_ResetCalibration(ADC3);//使能复位校准 while(ADC_GetResetCalibrationStatus(ADC3));//等待复位校准结束 ADC_StartCalibration(ADC3); //开启AD校准 while(ADC_GetCalibrationStatus(ADC3)); //等待校准结束 }

123456789101112131415161718192021222324252627282930313233343536

然后采集数据

//获得ADC3某个通道的值 //ch:通道值 0~16 //返回值:转换结果 u16 Get_ADC3(u8 ch) { //设置指定ADC的规则组通道,一个序列,采样时间 ADC_RegularChannelConfig(ADC3, ch, 1, ADC_SampleTime_239Cycles5 );//ADC3,ADC通道,采样时间为239.5周期 ADC_SoftwareStartConvCmd(ADC3, ENABLE);//使能指定的ADC3的软件转换启动功能 while(!ADC_GetFlagStatus(ADC3, ADC_FLAG_EOC ));//等待转换结束 return ADC_GetConversionValue(ADC3);//返回最近一次ADC3规则组的转换结果 } //读取Light Sens的值 //0~100:0,最暗;100,最亮 u8 Lsens_Get_Val(void) { u32 temp_val=0; u8 t; for(t=0;t<LSENS_READ_TIMES;t++) { temp_val+=Get_ADC3(ADC_Channel_6);//读取ADC值 delay_ms(5); } temp_val/=LSENS_READ_TIMES;//得到平均值 if(temp_val>4000)temp_val=4000; return (u8)(100-(temp_val/40)); }

1234567891011121314151617181920212223242526272829303132 接收并处理串口USART2数据

初始化配置USART2

void usart2_init( void ) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE ); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA2 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出 GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;//PA3 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入 GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA //USART2 初始化设置 USART_InitStructure.USART_BaudRate = 115200;//串口波特率 USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式 USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位 USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发模式 USART_Init(USART2, &USART_InitStructure); //初始化串口2 USART_ClearFlag(USART2, USART_FLAG_TC); USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启串口接受和总线空闲中断 //USART_ITConfig(USART2, USART_IT_IDLE, ENABLE); USART_Cmd(USART2, ENABLE); //使能串口2 //USART2 NVIC 配置 NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;//子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//IRQ通道使能 NVIC_Init(&NVIC_InitStructure);//根据指定的参数初始化VIC寄存器 }

123456789101112131415161718192021222324252627282930313233343536373839404142434445

接收USART2数据

char USART2_RX_BUF[RX_BUF_MAX_LEN]; u16 USART2_RX_STA = 0; void USART2_IRQHandler(void) { u8 r; if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收中断 { r = USART_ReceiveData(USART2);//(USART2->DR);//读取接收到的数据 if((USART2_RX_STA&0x8000)==0)//接收未完成 { if(USART2_RX_STA&0x4000)//接收到了0x0d(即'\r') { if(r!=0x0a)USART2_RX_STA=0;//接收错误(即没有接收到'\n'),重新开始 else { //USART2_RX_STA|=0x8000;//接收完成了 (接收到'\r'后接收到'\n') USART2_RX_STA = 0; //接收到\n, 重新等待下一次接收 } } else //还没收到0X0D(即'\r') { if(r==0x0d)USART2_RX_STA|=0x4000; //如果接收到\r, 让USART2_RX_STA的14位置1 else { USART2_RX_BUF[USART2_RX_STA&0X3FFF]=r; USART2_RX_STA++; if(USART2_RX_STA>(RX_BUF_MAX_LEN-1))USART2_RX_STA=0;//接收数据错误,重新开始接收 } } } } }

12345678910111213141516171819202122232425262728293031323334

处理USART2接收到的数据

提取时间、天气信息;为了方便,ESP8266发给STM32的数据都是用逗号隔开的,如 时间,天气,气温,湿度,……

天气数据保存在结构体中

struct WeatherData{ char datetime[20]; char city[10]; char weather[10]; //原本为10 char temp[10]; char humi[10]; char windpower[10]; //风力等级 }; 12345678

对USART2接收的数据进行处理

struct WeatherData processWdata(char data[]){ //char data[] = "2022-22-22 33:33:33,yin,47,48,<=7"; u8 i=0, j=0, i0=0, k=0;u8 ind=0, jnd=0; int slen = strlen(data); struct WeatherData weather; //初始化 for(ind=0; ind<8; ind++){ switch(ind){ case 0: { for(jnd=0; jnd<20; jnd++){ weather.datetime[jnd]='\0'; } };break; case 1: { for(jnd=0; jnd<10; jnd++){ weather.city[jnd]='\0'; } };break; case 2: { for(jnd=0; jnd<10; jnd++){ weather.humi[jnd]='\0'; } };break; case 3: { for(jnd=0; jnd<10; jnd++){ weather.temp[jnd]='\0'; } };break; case 4: { for(jnd=0; jnd<10; jnd++){ weather.weather[jnd]='\0'; } };break; case 5: { for(jnd=0; jnd<10; jnd++){ weather.windpower[jnd]='\0'; } };break; case 6: { for(jnd=0; jnd<10; jnd++){ minLsens_str[jnd]='\0'; } };break; case 7: { for(jnd=0; jnd<10; jnd++){ alarmTemp_str[jnd]='\0'; } };break; } } strcpy(weather.city, "西安"); for(i=0; i<slen; i++){ if(data[i]==',') { i0++; for(j=k; j<i; j++){ if(i0==1) weather.datetime[j-k]=data[j]; else if(i0==2) weather.weather[j-k]=data[j]; else if(i0==3) weather.temp[j-k]=data[j]; else if(i0==4) weather.humi[j-k]=data[j]; else if(i0==5) weather.windpower[j-k]=data[j]; else if(i0==6) alarmTemp_str[j-k]=data[j]; else if(i0==7) minLsens_str[j-k]=data[j]; } k=i+1; } } return weather; }

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475

字符串数字转为整型数字

u8 str2int(char s[]){ u8 n=0, i=0, len, sum=0; len = strlen(s); for(i=0; i<len; i++){ if(47<s[i]<58){ n = s[i] - 48; //得到一位数字 sum += (n * pow(10, len-1-i)); } } return sum; } 1234567891011

发送数据

通过定时器TIM2定时1s发送一次数据(json字符串格式)给ESP8266模块

初始化配置TIM2

//初始化TIM2 void TIM2_init(u16 arr, u16 psc){ TIM_TimeBaseInitTypeDef TIM_Structure; NVIC_InitTypeDef NVIC_Structure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //延时时间(单位:s):arr/(72000000/psc) TIM_Structure.TIM_Period = arr - 1; //装载值 TIM_Structure.TIM_Prescaler = psc-1; //分频系数 TIM_Structure.TIM_ClockDivision = 0; TIM_Structure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_Structure); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //开启定时器TIM2中断 TIM_ClearITPendingBit(TIM2, TIM_IT_Update); NVIC_Structure.NVIC_IRQChannel = TIM2_IRQn; NVIC_Structure.NVIC_IRQChannelPreemptionPriority = 6; //抢占优先级 NVIC_Structure.NVIC_IRQChannelSubPriority = 4; //子优先级 NVIC_Structure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_Structure); TIM_ClearFlag(TIM2, TIM_FLAG_Update); TIM_Cmd(TIM2, ENABLE); }

12345678910111213141516171819202122232425

定时时间(项目中使用1s)到达后产生定时器中断,这时候进入中断函数中发送数据

void TIM2_IRQHandler(void){ if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET){ ESP8266_Usart("{\"temp\":\"%d\",\"humi\":\"%d\",\"light\":\"%d\",\"ledsta\":\"%d\",\"beepsta\":\"%d\"}\r\n", dhtData.temp, dhtData.humi, lsens, ledSta, beepSta);//输出【光照、温度、湿度、led、beep状态】到esp8266,再发送给手机 TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } } 1234567 控制LED与蜂鸣器

void ledBeepOnOrNot(u8 lsens, u8 mlsens, u8 t, u8 alarmT){ //是否启动LED、BEEP if(lsens < mlsens){ LED1 = 0; //点亮 ledSta = 1; } else{ LED1 = 1; //熄灭 ledSta = 0; } if(t>alarmT){ BEEP=1; //叫 beepSta = 1; } else{ BEEP=0;//不叫 beepSta = 0; } }

1234567891011121314151617181920

ESP8266端程序设计

ESP8266WIFI模块通过MQTT协议连接服务器,数据发布到服务器后,Android客户端可以向服务器订阅主题来获取到消息,然后进行展示。ESP8266客户端通过订阅也能获取到Android客户端发布的消息。

image-20211116233340883

功能介绍

连接WiFi启动后led点亮,连接上MQTT服务端后闪烁,未连接常亮通过网络获取如下信息 时间:使用【苏宁的API】 http://quan.suning.com/getSysTime.do天气:使用【高德的天气API】http://restapi.amap.com/v3/weather/weatherInfo?key=xxxxx&city=610100)(参数key要自己获取,city是城市编码) 与STM32通过串口进行数据通信建立与MQTT服务器的连接向服务器指定的主题发布消息和接收消息json数据处理

具体实现

ESP8266使用NodeMCU模块,ArduinoIDE开发

一些全局变量

//wifi配置 const char* ssid = "xxxxxx"; const char* password = "xxxxxxxx"; //mqtt服务器连接配置 const char *mqttBroker = "xxxxxx.iotcloud.tencentdevices.com"; const char *mqttUsername = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; const char *mqttPassword = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; char *mqttClientId = "xxxxxxxxxxxesp8266"; const char *pubTopic = "xxxxxx/esp8266/data"; const char *subTopic = "xxxxxx/esp8266/data"; const int mqttPort = 1883; //时间api const String timeUrl = "http://quan.suning.com/getSysTime.do"; //高德地图api(自己获取key, city是城市编码) const String gaoDeUrl = "http://restapi.amap.com/v3/weather/weatherInfo?key=xxxxx&city=610100";

123456789101112131415161718

连接WiFi

void connectWifi(){ WiFi.mode(WIFI_STA); //WIFI_STA、WIFI_AP、WIFI_AP_STA WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); } } 12345678910

发送HTTP请求

String callHttp(String httpUrl) { HTTPClient hClient; String message; hClient.begin(httpUrl); int httpCode = hClient.GET(); if (httpCode > 0) { if (httpCode == HTTP_CODE_OK) { String payload = hClient.getString(); message = payload; } } else { message = "[1-HTTP]failed, error:" + String(hClient.errorToString(httpCode).c_str()); } hClient.end(); return message; }

1234567891011121314151617

连接MQTT服务器

void connectMqttServer(){ while (!mqttClient.connected()) { if (mqttClient.connect(mqttClientId, mqttUsername, mqttPassword)) { subscribeTopic(subTopic); //订阅主题 } else { delay(2000); // Serial.println("连接失败"); } } } 12345678910

发布信息到指定主题

void publishTopic(const char* topic, char* jsonMsg){ mqttClient.publish(topic, jsonMsg, false); } 1234

订阅主题

// 订阅指定主题 void subscribeTopic(const char* topic){ if(mqttClient.subscribe(topic)){ } else { } } 12345678

回调函数(当收到服务器的消息后会调用)

void receiveCallback(char* topic, byte* payload, unsigned int length) { serverMsg = ""; //当下次调用时清空数据 for (int i = 0; i < length; i++) { // Serial.print((char)payload[i]); serverMsg += (char)payload[i]; } // Serial.println("\n"); // Serial.println(serverMsg); } 12345678910

完整代码

#include <ESP8266WiFi.h> #include <PubSubClient.h> #include <ArduinoJson.h> #include <ESP8266HTTPClient.h> #include <WiFiManager.h> //wifi配置 const char* ssid = "xxxxxx"; const char* password = "xxxxxxxx"; //mqtt服务器连接配置 const char *mqttBroker = "xxxxxx.iotcloud.tencentdevices.com"; const char *mqttUsername = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; const char *mqttPassword = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; char *mqttClientId = "xxxxxxxxxxxesp8266"; const char *pubTopic = "xxxxxx/esp8266/data"; const char *subTopic = "xxxxxx/esp8266/data"; const int mqttPort = 1883; WiFiClient wifiClient; PubSubClient mqttClient(wifiClient); struct Weather{ String wea=""; String temp=""; String windpower=""; //风力,单位:级 String humi=""; }; struct ServerMsg{ String alarmTemp=""; String minLight=""; }; //时间api const String timeUrl = "http://quan.suning.com/getSysTime.do"; //高德地图api(自己获取key, city是城市编码) const String gaoDeUrl = "http://restapi.amap.com/v3/weather/weatherInfo?key=xxxxx&city=610100"; String timeMsg=""; String weatherMsg=""; String timeAfterParse=""; struct Weather weatherAfterParse; String data2stm=""; //打包后发送给stm32的信息 String data2server=""; //发布到mqtt服务器的信息 String callHttp(String httpUrl); String parseJsonTime(String tjson); struct Weather parseJsonWeather(String wjson); void ledOnOff(); String tqzh2py(String zhtq); //中文天气转拼音 String windpowerFormat(String wp); void connectWifi(); void setLedOnOff(); void publishTopic(const char* topic, char* jsonMsg); void subscribeTopic(const char* topic); struct ServerMsg parseServerMsg(String jsonMsg); void getSerialDataAndProcess(); void setup() { pinMode(LED_BUILTIN, OUTPUT); // 设置板上LED引脚为输出模式 digitalWrite(LED_BUILTIN, LOW); // 启动后打开板上LED Serial.begin(115200); // 启动串口通讯 connectWifi(); mqttInit(); connectMqttServer(); timeMsg = callHttp(timeUrl); weatherMsg = callHttp(gaoDeUrl); } struct ServerMsg serverMsgAfterParse; String serverMsg=""; String stm32Msg=""; int n = 0; void loop() { delay(500); n++; //获取网络数据(时间、天气) if(n % 2 == 0){ ledOnOff(); timeMsg = callHttp(timeUrl); if(n >= 20){ //10s获取一次天气 weatherMsg = callHttp(gaoDeUrl); n=0; } } //获取stm32数据,并发送到mqtt服务器 getSerialDataAndProcess(); //对网络获取到的数据处理 if(timeMsg!=""){ timeAfterParse = parseJsonTime(timeMsg); } if(weatherMsg!=""){ weatherAfterParse = parseJsonWeather(weatherMsg); } if(serverMsg!=""){ serverMsgAfterParse = parseServerMsg(serverMsg); } //打包发送到stm32 if(timeMsg!="" && weatherMsg!=""){ data2stm = (timeAfterParse + "," + tqzh2py(weatherAfterParse.wea) + "," + weatherAfterParse.temp + "," +weatherAfterParse.humi +"," + windpowerFormat(weatherAfterParse.windpower) + "," + serverMsgAfterParse.alarmTemp + "," + serverMsgAfterParse.minLight + "," + "\r\n"); //日期,天气,温度,湿度,风力等级,报警温度,最低光照 Serial.print(data2stm); } if (mqttClient.connected()) { // 如果开发板成功连接服务器 mqttClient.loop(); // 处理信息以及心跳 } else { // 如果开发板未能成功连接服务器 connectMqttServer(); // 则尝试连接服务器 } } void mqttInit(){ mqttClient.setServer(mqttBroker, mqttPort); // 设置MQTT订阅回调函数 mqttClient.setCallback(receiveCallback); } // 连接MQTT服务器并订阅信息 void connectMqttServer(){ while (!mqttClient.connected()) { if (mqttClient.connect(mqttClientId, mqttUsername, mqttPassword)) { subscribeTopic(subTopic); //订阅主题 } else { delay(2000); // Serial.println("连接失败"); } } } // 订阅指定主题 void subscribeTopic(const char* topic){ if(mqttClient.subscribe(topic)){ } else { } } // 收到信息后的回调函数 void receiveCallback(char* topic, byte* payload, unsigned int length) { serverMsg = ""; //当下次调用时清空数据 for (int i = 0; i < length; i++) { // Serial.print((char)payload[i]); serverMsg += (char)payload[i]; } // Serial.println("\n"); // Serial.println(serverMsg); } struct ServerMsg parseServerMsg(String jsonMsg){ struct ServerMsg smsg; const size_t capacity = 96; DynamicJsonDocument sdoc(capacity); // 反序列化数据 deserializeJson(sdoc, jsonMsg); smsg.alarmTemp = sdoc["atemp"].as<String>(); smsg.minLight = sdoc["mlight"].as<String>(); return smsg; } void publishTopic(const char* topic, char* jsonMsg){ mqttClient.publish(topic, jsonMsg, false); } void ledOnOff(){ static bool LEDState=0; if(mqttClient.connected()){ LEDState = !LEDState; //连上,led闪烁 } else{ LEDState = 0; //没连上,led常亮 } digitalWrite(LED_BUILTIN, LEDState); } String callHttp(String httpUrl) { HTTPClient hClient; String message; hClient.begin(httpUrl); int httpCode = hClient.GET(); if (httpCode > 0) { if (httpCode == HTTP_CODE_OK) { String payload = hClient.getString(); message = payload; } } else { message = "[1-HTTP]failed, error:" + String(hClient.errorToString(httpCode).c_str()); } hClient.end(); return message; } void getTimeMsg(){ timeMsg = callHttp(timeUrl); } void getWeatherMsg(){ weatherMsg = callHttp(gaoDeUrl); } String parseJsonTime(String tjson){ const size_t capacity = 96; DynamicJsonDocument tdoc(capacity); // 反序列化数据 deserializeJson(tdoc, tjson); // 获取解析后的数据信息 String datetime = tdoc["sysTime2"].as<String>(); return datetime; } //{"status":"1","count":"1","info":"OK","infocode":"10000","lives":[{"province":"陕西","city":"西安市","adcode":"610100","weather":"晴","temperature":"13","winddirection":"北","windpower":"≤3","humidity":"88","reporttime":"2021-10-29 21:03:10"}]} struct Weather parseJsonWeather(String wjson){ struct Weather weather; const size_t capacity = 512; DynamicJsonDocument wdoc(capacity); // 反序列化数据 deserializeJson(wdoc, wjson); JsonObject lives_0 = wdoc["lives"][0]; weather.wea = lives_0["weather"].as<String>(); // "晴" weather.temp = lives_0["temperature"].as<String>(); // "13" weather.humi = lives_0["humidity"].as<String>(); // "88" weather.windpower = lives_0["windpower"].as<String>(); // "≤3" return weather; } String tqzh2py(String zhtq){ String zh_cn[68] = {"晴","少云","晴间多云","多云","阴","有风","平静","微风","和风","清风","强风/劲风","疾风","大风","烈风","风暴","狂爆风","飓风","热带风暴","霾","中度霾","重度霾","严重霾","阵雨","雷阵雨","雷阵雨并伴有冰雹","小雨","中雨","大雨","暴雨","大暴雨","特大暴雨","强阵雨","强雷阵雨","极端降雨","毛毛雨/细雨","雨","小雨-中雨","中雨-大雨","大雨-暴雨","暴雨-大暴雨","大暴雨-特大暴雨","雨雪天气","雨夹雪","阵雨夹雪","冻雨","雪","阵雪","小雪","中雪","大雪","暴雪","小雪-中雪","中雪-大雪","大雪-暴雪","浮尘","扬沙","沙尘暴","强沙尘暴","龙卷风","雾","浓雾","强浓雾","轻雾","大雾","特强浓雾","热","冷","未知",}; String py[68] = {"qing","shaoyun","qingjianduoyun","duoyun","yin","youfeng","pingjing","weifeng","hefeng","qingfeng","qiangfeng/jinfeng","jifeng","dafeng","liefeng","fengbao","kuangbaofeng","jufeng","redaifengbao","mai","zhongdumai","zhongdumai","yanzhongmai","zhenyu","leizhenyu","leizhenyubingbanyoubingbao","xiaoyu","zhongyu","dayu","baoyu","dabaoyu","tedabaoyu","qiangzhenyu","qiangleizhenyu","jiduanjiangyu","maomaoyu/xiyu","yu","xiaoyu-zhongyu","zhongyu-dayu","dayu-baoyu","baoyu-dabaoyu","dabaoyu-tedabaoyu","yuxuetianqi","yujiaxue","zhenyujiaxue","dongyu","xue","zhenxue","xiaoxue","zhongxue","daxue","baoxue","xiaoxue-zhongxue","zhongxue-daxue","daxue-baoxue","fuchen","yangsha","shachenbao","qiangshachenbao","longjuanfeng","wu","nongwu","qiangnongwu","qingwu","dawu","teqiangnongwu","re","leng","weizhi"}; for(int i=0; i<68; i++){ // if(strstr()) if(zh_cn[i] == zhtq) return py[i]; } return zhtq; //没有就返回中文 } String windpowerFormat(String wp){ if(wp=="≤3"){ return "<=3"; } return wp; } //得到数据后处理方式,把它当作一个串口(伪)中断函数 void getSerialDataAndProcess(){ if (Serial.available()) { //串口读取到的转发到wifi,因为串口是一位一位的发送所以在这里缓存完再发送 size_t countBytes = Serial.available(); char sbuf[countBytes]; Serial.readBytes(sbuf, countBytes); // Serial.write(sbuf, countBytes); publishTopic(pubTopic, sbuf); Serial.flush(); } } void connectWifi(){ // WiFiManager wifiManager; // // 自动连接WiFi。以下语句的参数是连接ESP8266时的WiFi名称 // wifiManager.autoConnect("IamESP8266"); WiFi.mode(WIFI_STA); //WIFI_STA、WIFI_AP、WIFI_AP_STA WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); } }

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302

Android端程序设计

功能介绍

主要是通过MQTT服务器,获取ESP8266发布的信息,然后进行处理显示出来;同时APP也可以发布消息,用于修改报警温度、最低光照值。

具体实现

使用Android Studio进行开发(具体安装下载、环境配置请百度)

工程代码已开源,前往GitHub请点击前去Github,前往Gitee请点击前去Gitee

添加MQTT依赖 在自己的[project]下的 build.gradle添加如下代码

repositories { google() mavenCentral() //添加下面这句代码 maven { url "https://repo.eclipse.org/content/repositories/paho-releases/" } } 12345678 在[project]/app下的build.gradle添加如下代码

dependencies { compile 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1' compile 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.1' } 1234

然后选择顶部弹出的 Sync Now 信息

image-20211110232735634

权限配置

修改AndroidManifest.xml,添加权限

<!--允许程序打开网络套接字--> <uses-permission android:name="android.permission.INTERNET" /> <!--允许程序获取网络状态--> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> 1234

如下:
image-20211110233017614

显示界面

修改文件activity_main.xml,实现如下显示效果

image-20211110233204303

具体代码如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:layout_marginTop="20dp" android:layout_marginLeft="20dp" android:layout_marginBottom="10dp" android:id="@+id/house_env" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="室内环境" android:textStyle="bold" android:textSize="20dp" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/show_temp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:layout_marginLeft="20dp" android:layout_marginBottom="10dp" android:text="温度(°C)" android:textSize="20dp" /> <TextView android:id="@+id/temp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:layout_marginLeft="20dp" android:layout_marginBottom="10dp" android:text="0" android:textSize="20dp" /> <TextView android:id="@+id/show_humi" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:layout_marginLeft="20dp" android:layout_marginBottom="10dp" android:text="湿度(%RH)" android:textSize="20dp" /> <TextView android:id="@+id/humi" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:layout_marginLeft="20dp" android:layout_marginBottom="10dp" android:text="0" android:textSize="20dp" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <TextView android:id="@+id/show_light" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginTop="10dp" android:layout_marginBottom="10dp" android:text="光照强度" android:textSize="20dp" /> <TextView android:id="@+id/light" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginTop="10dp" android:layout_marginBottom="10dp" android:text="0" android:textSize="20dp" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/ledsta1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginTop="10dp" android:layout_marginBottom="10dp" android:text="LED状态" android:textSize="20dp" /> <TextView android:id="@+id/ledsta2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginTop="10dp" android:layout_marginBottom="10dp" android:text="关" android:textSize="20dp" /> <TextView android:id="@+id/beepsta1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginTop="10dp" android:layout_marginBottom="10dp" android:text="蜂鸣器状态" android:textSize="20dp" /> <TextView android:id="@+id/beepsta2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginTop="10dp" android:layout_marginBottom="10dp" android:text="关" android:textSize="20dp" /> </LinearLayout> <TextView android:id="@+id/sets" android:layout_column="1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginTop="10dp" android:layout_marginBottom="10dp" android:text="设置" android:textStyle="bold" android:textSize="20dp" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/set_temp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginTop="10dp" android:layout_marginBottom="10dp" android:text="报警温度(当前温度超过该值蜂鸣器将报警)" android:textSize="15dp" /> <TextView android:id="@+id/seekbarval1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:layout_marginLeft="10dp" android:text="40"/> </LinearLayout> <SeekBar android:id="@+id/seekBar1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:layout_marginBottom="10dp" android:layout_gravity="center" android:max="100" android:progress="40"/> <!-- android:progressDrawable="@drawable/seekbar_progress" />--> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/set_tlight" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginTop="10dp" android:layout_marginBottom="10dp" android:text="最低光照(当前光照小于该值将点亮led灯)" android:textSize="15dp" /> <TextView android:id="@+id/seekbarval2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:layout_marginLeft="20dp" android:text="12"/> </LinearLayout> <SeekBar android:id="@+id/seekBar2" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:layout_marginBottom="10dp" android:layout_gravity="center" android:max="100" android:progress="12"/> <!-- android:progressDrawable="@drawable/seekbar_progress2" />--> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content"> </LinearLayout> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" > <Button android:layout_marginLeft="100dp" android:layout_marginTop="20dp" android:id="@+id/connectbtn" android:layout_height="wrap_content" android:layout_width="wrap_content" android:textColor="@color/white" android:text="连接"/> <Button android:layout_marginTop="20dp" android:layout_marginRight="20dp" android:layout_marginLeft="20dp" android:id="@+id/exitbtn" android:layout_height="wrap_content" android:layout_width="wrap_content" android:textColor="@color/white" android:text="退出"/> </LinearLayout> <TextView android:layout_margin="5dp" android:id="@+id/msgTxt" android:layout_height="wrap_content" android:layout_width="wrap_content" android:scrollbars="vertical" android:text=""/> </LinearLayout>

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255 界面控制

接下来修改MainActivity.java,添加控制操作,让数据可以通过TextView显示,按下按钮可以作出反应,上面只是完成一个静态界面的创建

package com.ajream.mqttdemo4; import android.annotation.SuppressLint; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.widget.Button; import android.widget.SeekBar; import android.widget.TextView; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; import org.eclipse.paho.client.mqttv3.MqttCallback; import org.eclipse.paho.client.mqttv3.MqttClient; import org.eclipse.paho.client.mqttv3.MqttConnectOptions; import org.eclipse.paho.client.mqttv3.MqttException; import org.eclipse.paho.client.mqttv3.MqttMessage; import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; import org.json.JSONException; import org.json.JSONObject; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class MainActivity extends AppCompatActivity { //定义成员 private Button connectbtn; private Button exitbtn; private TextView temptv; private TextView humitv; private TextView lighttv; private TextView ledtv; private TextView beeptv; private TextView bar1tv; private TextView bar2tv; private TextView showmsgtv; private SeekBar tempbar; private SeekBar lightbar; //MQTT客户端配置所需信息 private String host = "tcp://xxxxxxx.iotcloud.tencentdevices.com:1883"; //mqtt服务器(腾讯云)地址、端口 private String userName = "xxxxxxxxxx"; //用户名(在腾讯云查看自己的设备信息) private String passWord = "xxxxxxxxxx";//密码(在腾讯云查看自己的设备信息) private String mqtt_id = "xxxxxxxxxxxxxxxxxxx"; private String mqtt_sub_topic = "xxxxxxxxxx/AndroidClient/data"; private String mqtt_pub_topic = "xxxxxxxxxx/AndroidClient/data"; private ScheduledExecutorService scheduler; private MqttClient mqttClient; private MqttConnectOptions options; private Handler handler; private String msgToPublish = ""; //要发布的消息 private String alarmTempMsg = ""; private String minLightMsg = ""; JSONObject msgGet; @SuppressLint("HandlerLeak") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //关联控件 connectbtn = findViewById(R.id.connectbtn); exitbtn = findViewById(R.id.exitbtn); temptv = findViewById(R.id.temp); humitv = findViewById(R.id.humi); lighttv = findViewById(R.id.light); ledtv = findViewById(R.id.ledsta2); beeptv = findViewById(R.id.beepsta2); bar1tv = findViewById(R.id.seekbarval1); bar2tv = findViewById(R.id.seekbarval2); showmsgtv = findViewById(R.id.msgTxt); tempbar = findViewById(R.id.seekBar1); lightbar = findViewById(R.id.seekBar2); alarmTempMsg = String.valueOf(tempbar.getProgress()); minLightMsg = String.valueOf(lightbar.getProgress()); /*点击按钮连接*/ connectbtn.setOnClickListener(view -> { Mqtt_init(); startReconnect(); }); exitbtn.setOnClickListener(view -> { android.os.Process.killProcess(android.os.Process.myPid()); }); tempbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { Toast.makeText(MainActivity.this, "设置报警温度为: " + progress, Toast.LENGTH_LONG).show(); bar1tv.setText(check(progress)); alarmTempMsg = check(progress); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { // msgToPublish = alarmTempMsg + "," + minLightMsg; msgToPublish = "{\"atemp\":\"" + alarmTempMsg + "\",\"mlight\":\"" + minLightMsg+"\"}"; publishMsg(mqtt_pub_topic, msgToPublish); } }); lightbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int i, boolean b) { Toast.makeText(MainActivity.this, "设置最低光照为: " + i, Toast.LENGTH_LONG).show(); bar2tv.setText(check(i)); minLightMsg = check(i); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { msgToPublish = "{\"atemp\":\"" + alarmTempMsg + "\",\"mlight\":\"" + minLightMsg+"\"}"; publishMsg(mqtt_pub_topic, msgToPublish); } }); handler = new Handler() { @SuppressLint("SetTextI18n") public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 1: //开机校验更新回传 break; case 2: // 反馈回传 break; case 3: //MQTT 收到消息回传 UTF8Buffer msg=newUTF8Buffer(object.toString()); // Toast.makeText(MainActivity.this,msg.obj.toString(),Toast.LENGTH_SHORT).show(); showmsgtv.setText(msg.obj.toString()); JSONObject msgGet = null; try { msgGet = new JSONObject(msg.obj.toString()); temptv.setText(msgGet.get("temp").toString()); humitv.setText(msgGet.get("humi").toString()); lighttv.setText(msgGet.get("light").toString()); if(Integer.parseInt(msgGet.get("ledsta").toString())==0) ledtv.setText("关"); else ledtv.setText("开"); if(msgGet.get("beepsta").toString().charAt(0)=='0') beeptv.setText("关"); else beeptv.setText("开"); } catch (JSONException e) { e.printStackTrace(); } break; case 30: //连接失败 Toast.makeText(MainActivity.this,"连接失败" ,Toast.LENGTH_SHORT).show(); break; case 31: //连接成功 Toast.makeText(MainActivity.this,"连接成功" ,Toast.LENGTH_SHORT).show(); try { mqttClient.subscribe(mqtt_sub_topic,1); } catch (MqttException e) { e.printStackTrace(); } break; default: break; } } }; } private String check(int progress) { int curValue = 100 * progress/Math.abs(100); return String.valueOf(curValue); } private void publishMsg(String topic, String message2) { if (mqttClient == null || !mqttClient.isConnected()) { return; } MqttMessage message = new MqttMessage(); message.setPayload(message2.getBytes()); try { mqttClient.publish(topic, message); } catch (MqttException e) { e.printStackTrace(); } } private void Mqtt_init() { try { //host为主机名,test为clientid即连接MQTT的客户端ID,一般以客户端唯一标识符表示,MemoryPersistence设置clientid的保存形式,默认为以内存保存 mqttClient = new MqttClient(host, mqtt_id, new MemoryPersistence()); //MQTT的连接设置 options = new MqttConnectOptions(); //设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录,这里设置为true表示每次连接到服务器都以新的身份连接 options.setCleanSession(false); options.setUserName(userName); //设置连接的用户名 options.setPassword(passWord.toCharArray()); //设置连接的密码 // 设置超时时间 单位为秒 options.setConnectionTimeout(10); // 设置会话心跳时间 单位为秒 服务器会每隔1.5*20秒的时间向客户端发送个消息判断客户端是否在线,但这个方法并没有重连的机制 options.setKeepAliveInterval(20); //设置回调函数 mqttClient.setCallback(new MqttCallback() { @Override public void connectionLost(Throwable cause) { //连接丢失后,一般在这里面进行重连 // System.out.println("connectionLost----------"); // startReconnect(); } @Override public void deliveryComplete(IMqttDeliveryToken token) { //publish后会执行到这里 System.out.println("deliveryComplete---------" + token.isComplete()); } @Override public void messageArrived(String topicName, MqttMessage message) throws Exception { //subscribe后得到的消息会执行到这里面 System.out.println("getMsg: "); Message msg = new Message(); msg.what = 3; //收到消息标志位 msg.obj = message.toString(); handler.sendMessage(msg); // hander 回传 } }); } catch (Exception e) { e.printStackTrace(); } } private void Mqtt_connect() { new Thread(new Runnable() { @Override public void run() { try { if(!(mqttClient.isConnected())) { mqttClient.connect(options); Message msg = new Message(); msg.what = 31; handler.sendMessage(msg); } } catch (Exception e) { e.printStackTrace(); Message msg = new Message(); msg.what = 30; handler.sendMessage(msg); } } }).start(); } private void startReconnect() { scheduler = Executors.newSingleThreadScheduledExecutor(); scheduler.scheduleAtFixedRate(new Runnable() { @Override public void run() { if (!mqttClient.isConnected()) { Mqtt_connect(); } } }, 0 * 1000, 10 * 1000, TimeUnit.MILLISECONDS); } }

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288

注意:MQTT服务端配置这里没有介绍,可以使用一些公用的MQTT服务器(可以去百度搜索),也可以自己搭建一个,我这里使用的是自己通过腾讯云配置的MQTT服务,具体搭建可以到我的博客查看或者看官方文档

效果预览

TFTLCD屏幕显示

image-20211117003640156

Android APP界面

网址:STM32智能家居项目设计 https://www.yuejiaxmz.com/news/view/280832

相关内容

stm32项目(8)——基于stm32的智能家居设计
基于STM32的智能家居设计(有三款设计)
基于STM32智能家居系统设计
【stm32项目】多功能智能家居室内灯光控制系统设计与实现(完整工程资料源码)
STM32项目设计:基于stm32的智能家居系统设计
【硬件+代码】STM32 智能家居系统设计+原理图+设计报告
基于STM32的智能家居照明控制系统设计与实现
基于STM32的智能扫地机器人设计
基于STM32的智能节能风扇设计
基于STM32智能家电无线控制系统设计

随便看看