STM32课设-智能物联网家居系统(UCOSIII+STEMWIN)
工程與源代碼下載地址
Gitee:源碼點這里
Github: 源碼點這里
目錄
- 一、功能分析與效果展示
- 1.功能需求與分析
- 2.硬件選型
- 3.效果展示
- 硬件實物圖
- UI界面
- 前端界面
- 展示視頻
- 二、下位機-STM32程序
- 1.系統任務設計
- 1.1 數據采集任務
- 1.2 場景處理任務
- 1.3 OneNet連接任務
- 1.3 OneNet上傳數據任務
- 1.4 OneNet下發命令處理任務
- 1.5 UI顯示任務、觸摸檢測任務
- 1.6 系統指示燈與堆棧檢測任務
- 2.UI界面設計
- 2.1主界面
- 2.1燈光界面
- 2.2 數據顯示界面
- 2.3 電器控制界面
- 2.4系統設置界面
- 2.5 關于界面
- 三、上位機-OneNet云端前端界面
- 四、總結
一、功能分析與效果展示
1.功能需求與分析
簡單分析一下
- 主控使用STM32,采集數據使用常規的傳感器就可以采集到這些數據。在程序中對采集回來的值做一個判斷,然后控制舵機和電機。
- UI界面這塊,使用STEMWIN這個UI庫來做,這個庫整體使用起來比較簡單好上手,做出來的UI也OK。
- 連網這方面使用ESP8266模塊,只要配置好串口就可以輕松駕馭。平臺基本上百度、阿里、OneNet都可以,只要支持MQTT協議接入的平臺就行。
- 為了方便開發和項目管理,使用UCOS-III系統,將每個情況分成小任務來完成,這樣做快捷方便拓展性強
2.硬件選型
| 1 | STM32F103ZET6開發板 | 1 |
| 2 | 4.3寸電容屏 | 1 |
| 3 | DHT11模塊 | 1 |
| 4 | MQ-2 氣體檢測模塊 | 1 |
| 5 | BH1750 光照強度檢測模塊 | 1 |
| 6 | ESP8266 WIFI 模塊 | 1 |
| 7 | 3.3V4路繼電器模塊 | 1 |
| 8 | L298N 驅動模塊 | 1 |
| 9 | SG90舵機 | 2 |
| 10 | 電線、杜邦線 | 若干 |
| 11 | 12V電機 | 2 |
| 12 | 12V燈泡 | 4 |
3.效果展示
硬件實物圖
UI界面
UI界面使用圖標均使用阿里圖標庫中的圖案,具體請看UI界面設計。
前端界面
展示視頻
基于STM32的物聯網智能家居系統
b站
二、下位機-STM32程序
整體框架圖
硬件定時器分配:
| TIM1 | 舵機1 |
| TIM2 | L298N電機調速 |
| TIM3 | 心跳包定時發送 |
| TIM4 | 保存云端下發數據 |
| TIM8 | 舵機2 |
UCOSIII軟件定時器:蜂鳴器控制
1.系統任務設計
STM32中,使用UCOS-III系統,將功能分成了8個小任務分別實現。具體每個模塊如何使用網上教程很多,這里就不一一貼出,有需要可以自行搜索。詳細代碼可以參考開源連接
1.1 數據采集任務
將每個模塊讀取數據單獨封裝一個函數,將函數放入任務中,再將數據讀取至數組中,完畢后通過消息隊列發送到需要數據的任務。對每個模塊數據的大小要有了解,不然有可能會出現數組越界卡死的情況。考慮到居家情況,可以對數據做一個小范圍的限幅。同時在這里通過全局變量更新數據顯示界面的數據可以讓數據顯示更加流暢
這里注意模塊讀取數據有嚴格時序,使用delay函數時,會引起任務調度,所以數據讀取函數前后需要給調度器上鎖,防止調度器打亂時序,這里的MQ-2模塊是使用ADC采集電壓與DMA傳輸數據,不受時序影響,所以就沒有加鎖
1.2 場景處理任務
這里主要是接收采集任務發來的數據,然后根據數據的不同來控制不同的外設。簡單判斷一下即可,這里每個判斷加一個標志位與手動控制區分開,這樣就可以通過按鈕來切換自動或者手動控制外設。
void Autocontrol_task(void *p_arg) {OS_ERR err;OS_MSG_SIZE MsgSize;while(1){int *TEXT_Buffer; TEXT_Buffer = OSQPend ((OS_Q *)&SensorDataMsg, (OS_TICK )0, (OS_OPT )OS_OPT_PEND_BLOCKING, //如果沒有獲取到信號量就等待(OS_MSG_SIZE *)&MsgSize, (CPU_TS *)0, (OS_ERR *)&err);/* TEXT_Buffer[0] = 溫度 TEXT_Buffer[1] = 濕度TEXT_Buffer[2] = 光強TEXT_Buffer[3] = 煤氣含量檢測*//**********************************場景1:出現煤氣泄漏的情況 **********************************//* 可能出現煤氣泄漏 */if(TEXT_Buffer[3] >= 300 && control_flag){printf("場景1\r\n");/* 蜂鳴器報警 */OSTmrStart(&BeepTmr,&err);/* 開窗通風 */TIM_SetCompare1(TIM8,175);/* 開排氣扇 */MotorPWM = 1;}else if(TEXT_Buffer[3] <= 300 && control_flag){/* 關閉蜂鳴器報警 */BEEP = 0;OSTmrStop(&BeepTmr,OS_OPT_TMR_NONE,0,&err);/* 關閉窗口 */TIM_SetCompare1(TIM8,195);/* 關閉排氣扇 */MotorPWM = 0; }/**********************************場景2:根據光照強度調節窗簾的開關 **********************************/if(TEXT_Buffer[2] <= 10 && control_flag){/* 到了深夜 */TIM_SetCompare1(TIM1,195);}else if(TEXT_Buffer[2] <= 30 && TEXT_Buffer[2] >= 10 && control_flag){/* 有一點光 */TIM_SetCompare1(TIM1,190); // 45度}else if(TEXT_Buffer[2] <= 120 && TEXT_Buffer[2] >= 30 && control_flag){/* 有光照 */TIM_SetCompare1(TIM1,185); // 90度}else if (TEXT_Buffer[2] <= 400 && TEXT_Buffer[2] >= 120 && control_flag){/* 日出 */TIM_SetCompare1(TIM1,175); // 180度}/**********************************場景3:根據溫度開關風扇**********************************/if(TEXT_Buffer[1] >= 30 && TEXT_Buffer[1] <= 35 && control_flag ){TIM_SetCompare2(TIM2,50);}else if(TEXT_Buffer[1] >= 35 && control_flag){TIM_SetCompare2(TIM2,100);}else if (TEXT_Buffer[1] <= 30 && control_flag) {TIM_SetCompare2(TIM2,0);}} }1.3 OneNet連接任務
接下來就是要將數據發送到云端和接受云端數據了,這個可以選擇的平臺和教程也很多,這里貼幾個給大家參考,我是選擇使用電信部的OneNet平臺。
連接OneNet平臺
STM32連接OneNet
大致步驟就是上網站注冊,拿到IP、端口號、產品ID、設備號、密碼。通過TCP連接到服務器之后使用MQTT協議發送報文就可以建立連接了。具體參考開源代碼
注意OneNet是選擇多協議接入,不是選MQTT套件
這里OneNet的IP和端口號是固定的,直接復制即可
| 183.230.40.39 | 6002 |
產品ID:
設備號和密碼
在MQTT.h中填入
在任務中,首先初始化WIFI模塊、MQTT堆棧、OneNet連接信息等,然后與OneNet發起TCP連接,連接成功后。向OneNet發送訂閱報文,訂閱成功后,啟動定時器3,定時30S,每30S向OneNet發送一次心跳,保證我們時刻在線,同時啟動上傳數據任務和下發命令處理來上傳我們采集的數據和處理云端對下位機的控制。連接任務具體見下代碼,定時器中斷、WIFI連接代碼和TCP代碼參考開源代碼,這里就不一一貼出
//連接OneNet任務 void connectOneNet_task(void *p_arg) {OS_ERR err;WiFi_ResetIO_Init(); //初始化WiFi的復位IOMQTT_Buff_Init(); //初始化接收,發送,命令數據的 緩沖區 以及各狀態參數OneNetIoT_Parameter_Init(); //初始化連接OneNet平臺MQTT服務器的參數 while(1) //主循環{ /*--------------------------------------------------------------------*//* Connect_flag=1同服務器建立了連接,我們可以發布數據和接收推送了 *//*--------------------------------------------------------------------*/if(Connect_flag==1){ /*-------------------------------------------------------------*//* 處理發送緩沖區數據 *//*-------------------------------------------------------------*/if(MQTT_TxDataOutPtr != MQTT_TxDataInPtr){ //if成立的話,說明發送緩沖區有數據了//3種情況可進入if//第1種:0x10 連接報文//第2種:0x82 訂閱報文,且ConnectPack_flag置位,表示連接報文成功//第3種:SubcribePack_flag置位,說明連接和訂閱均成功,其他報文可發if((MQTT_TxDataOutPtr[2]==0x10)||((MQTT_TxDataOutPtr[2]==0x82)&&(ConnectPack_flag==1))||(SubcribePack_flag==1)){ printf("發送數據:0x%x\r\n",MQTT_TxDataOutPtr[2]); //串口提示信息MQTT_TxData(MQTT_TxDataOutPtr); //發送數據MQTT_TxDataOutPtr += BUFF_UNIT; //指針下移if(MQTT_TxDataOutPtr==MQTT_TxDataEndPtr) //如果指針到緩沖區尾部了MQTT_TxDataOutPtr = MQTT_TxDataBuf[0]; //指針歸位到緩沖區開頭} }//處理發送緩沖區數據的else if分支結尾/*-------------------------------------------------------------*//* 處理接收緩沖區數據 *//*-------------------------------------------------------------*/if(MQTT_RxDataOutPtr != MQTT_RxDataInPtr){ //if成立的話,說明接收緩沖區有數據了 printf("接收到數據:");/*-----------------------------------------------------*//* 處理CONNACK報文 *//*-----------------------------------------------------*/ //if判斷,如果第一個字節是0x20,表示收到的是CONNACK報文//接著我們要判斷第4個字節,看看CONNECT報文是否成功if(MQTT_RxDataOutPtr[2]==0x20){ switch(MQTT_RxDataOutPtr[5]){ case 0x00 : printf("CONNECT報文成功\r\n"); //串口輸出信息 ConnectPack_flag = 1; //CONNECT報文成功,訂閱報文可發break; //跳出分支case 0x00 case 0x01 : printf("連接已拒絕,不支持的協議版本,準備重啟\r\n"); //串口輸出信息Connect_flag = 0; //Connect_flag置零,重啟連接break; //跳出分支case 0x01 case 0x02 : printf("連接已拒絕,不合格的客戶端標識符,準備重啟\r\n"); //串口輸出信息Connect_flag = 0; //Connect_flag置零,重啟連接break; //跳出分支case 0x02 case 0x03 : printf("連接已拒絕,服務端不可用,準備重啟\r\n"); //串口輸出信息Connect_flag = 0; //Connect_flag置零,重啟連接break; //跳出分支case 0x03case 0x04 : printf("連接已拒絕,無效的用戶名或密碼,準備重啟\r\n"); //串口輸出信息Connect_flag = 0; //Connect_flag置零,重啟連接 break; //跳出分支case 0x04case 0x05 : printf("連接已拒絕,未授權,準備重啟\r\n"); //串口輸出信息Connect_flag = 0; //Connect_flag置零,重啟連接 break; //跳出分支case 0x05 default : printf("連接已拒絕,未知狀態,準備重啟\r\n"); //串口輸出信息 Connect_flag = 0; //Connect_flag置零,重啟連接 break; //跳出分支case default }} //if判斷,第一個字節是0x90,表示收到的是SUBACK報文//接著我們要判斷訂閱回復,看看是不是成功else if(MQTT_RxDataOutPtr[2]==0x90){ switch(MQTT_RxDataOutPtr[6]){ case 0x00 :case 0x01 : printf("訂閱成功\r\n"); //串口輸出信息SubcribePack_flag = 1; //SubcribePack_flag置1,表示訂閱報文成功,其他報文可發送Ping_flag = 0; //Ping_flag清零LED1 = 0; //連接指示燈TIM3_ENABLE_30S(); //啟動30s的PING定時器OS_TaskResume(&TASK9_TCB,&err); //啟動數據上傳任務OS_TaskResume(&TASK10_TCB,&err); //啟動下發命令處理任務break; //跳出分支 default : printf("訂閱失敗,準備重啟\r\n"); //串口輸出信息 Connect_flag = 0; //Connect_flag置零,重啟連接break; //跳出分支 } }//if判斷,第一個字節是0xD0,表示收到的是PINGRESP報文else if(MQTT_RxDataOutPtr[2]==0xD0){ printf("PING報文回復\r\n"); //串口輸出信息 if(Ping_flag==1){ //如果Ping_flag=1,表示第一次發送Ping_flag = 0; //要清除Ping_flag標志}else if(Ping_flag>1){ //如果Ping_flag>1,表示是多次發送了,而且是2s間隔的快速發送Ping_flag = 0; //要清除Ping_flag標志TIM3_ENABLE_30S(); //PING定時器重回30s的時間} } //if判斷,如果第一個字節是0x30,表示收到的是服務器發來的推送數據//我們要提取控制命令else if((MQTT_RxDataOutPtr[2]==0x30)){ printf("服務器等級0推送\r\n"); //串口輸出信息 MQTT_DealPushdata_Qs0(MQTT_RxDataOutPtr); //處理等級0推送數據} MQTT_RxDataOutPtr += BUFF_UNIT; //指針下移if(MQTT_RxDataOutPtr==MQTT_RxDataEndPtr) //如果指針到緩沖區尾部了MQTT_RxDataOutPtr = MQTT_RxDataBuf[0]; //指針歸位到緩沖區開頭 }//處理接收緩沖區數據的else if分支結尾 }//Connect_flag=1的if分支的結尾/*--------------------------------------------------------------------*//* Connect_flag=0同服務器斷開了連接,我們要重啟連接服務器 *//*--------------------------------------------------------------------*/else{ printf("需要連接服務器\r\n"); //串口輸出信息TIM_Cmd(TIM4,DISABLE); //關閉TIM4 TIM_Cmd(TIM3,DISABLE); //關閉TIM3 WiFi_RxCounter=0; //WiFi接收數據量變量清零 memset(WiFi_RX_BUF,0,WiFi_RXBUFF_SIZE); //清空WiFi接收緩沖區 if(WiFi_Connect_IoTServer()==0){ //如果WiFi連接云服務器函數返回0,表示正確,進入ifprintf("建立TCP連接成功\r\n"); //串口輸出信息Connect_flag = 1; //Connect_flag置1,表示連接成功 WiFi_RxCounter=0; //WiFi接收數據量變量清零 memset(WiFi_RX_BUF,0,WiFi_RXBUFF_SIZE); //清空WiFi接收緩沖區 MQTT_Buff_ReInit(); //重新初始化發送緩沖區 } }delay_ms(100);} }1.3 OneNet上傳數據任務
成功連接上OneNet后,啟動數據上傳任務,接收傳感器的數據,并將數據拼接成MQTT發布報文,發送到云端上,這里每5秒上傳一次數據,因為前端顯示那邊是5s更新一次顯示,這樣剛好可以同步更新。
這里注意報文每個數據的拼接方式,平臺不同,拼接方式會有不同,但基本上大同小異,按使用網站的手冊修改即可
1.4 OneNet下發命令處理任務
這里檢測接收的數據是否與我們預先設定的命令相符,如果相符,則執行我們設定的動作。具體見下代碼
/* 云端命令處理任務 */ void cmd_task(void *p_arg) {OS_ERR err;char online_cmd[20];char *cmd;unsigned char OnlinePwmControl;while(1){/* 云端指令處理 */if(MQTT_CMDOutPtr != MQTT_CMDInPtr) //if成立的話,說明命令緩沖區有數據了 { printf("命令:%s\r\n",&MQTT_CMDOutPtr[2]); //串口輸出信息/* 接收到云控制信號之后關閉自動控制模式 */control_flag = 0;/************************** 接受到控制燈的指令**************************/if(!memcmp(&MQTT_CMDOutPtr[2],LED1_ON,strlen(LED1_ON)))//判斷指令,如果是LED1_ON{ Light1 = 1;printf("已打開LED1!\r\n");}if(!memcmp(&MQTT_CMDOutPtr[2],LED1_OFF,strlen(LED1_OFF)))//判斷指令,如果是LED1_ON{ Light1 = 0;printf("已關閉LED1!\r\n");}if(!memcmp(&MQTT_CMDOutPtr[2],LED2_ON,strlen(LED2_ON)))//判斷指令,如果是LED1_ON{ Light2 = 1;printf("已打開LED2!\r\n");}if(!memcmp(&MQTT_CMDOutPtr[2],LED2_OFF,strlen(LED2_OFF)))//判斷指令,如果是LED1_ON{ Light2 = 0;printf("已關閉LED2!\r\n");}if(!memcmp(&MQTT_CMDOutPtr[2],LED3_ON,strlen(LED3_ON)))//判斷指令,如果是LED1_ON{ Light3 = 1;printf("已打開LED3!\r\n");}if(!memcmp(&MQTT_CMDOutPtr[2],LED3_OFF,strlen(LED3_OFF)))//判斷指令,如果是LED1_ON{ Light3 = 0;printf("已關閉LED3!\r\n");}if(!memcmp(&MQTT_CMDOutPtr[2],LED4_ON,strlen(LED4_ON)))//判斷指令,如果是LED1_ON{ Light4 = 1;printf("已打開LED4!\r\n");}if(!memcmp(&MQTT_CMDOutPtr[2],LED4_OFF,strlen(LED4_OFF)))//判斷指令,如果是LED1_ON{ Light4 = 0;printf("已關閉LED4!\r\n");}/************************** 接受到窗戶和排氣扇的指令 **************************/if(!memcmp(&MQTT_CMDOutPtr[2],Paiqishan_ON,strlen(Paiqishan_ON)))//判斷指令,如果是LED1_ON{ MotorPWM = 1;printf("已打開排氣扇!\r\n");}if(!memcmp(&MQTT_CMDOutPtr[2],Paiqishan_OFF,strlen(Paiqishan_OFF)))//判斷指令,如果是LED1_ON{ MotorPWM = 0;printf("已關閉排氣扇!\r\n");}if(!memcmp(&MQTT_CMDOutPtr[2],Windos_ON,strlen(Windos_ON)))//判斷指令,如果是LED1_ON{ TIM_SetCompare1(TIM8,175); // 180度;printf("已打開窗戶!\r\n");}if(!memcmp(&MQTT_CMDOutPtr[2],Windos_OFF,strlen(Windos_OFF)))//判斷指令,如果是LED1_ON{ TIM_SetCompare1(TIM8,195);printf("已關閉窗戶!\r\n");}/************************** 接受到控制電機的指令**************************/if(!memcmp(&MQTT_CMDOutPtr[2],dianji,strlen(dianji))){/* OneNet控制命令為:fangshan:控制數值 控制數值范圍為:0 - 100取出指令中的控制數值字符串并轉化為u8類型*/strcpy(online_cmd,(char*)&MQTT_CMDOutPtr[2]);cmd = &online_cmd[9];OnlinePwmControl = atoi(cmd);printf("%d",OnlinePwmControl);TIM_SetCompare2(TIM2,OnlinePwmControl);} //不做處理,后面會發送狀態else printf("未知指令\r\n"); //串口輸出信息 MQTT_CMDOutPtr += BUFF_UNIT; //指針下移if(MQTT_CMDOutPtr==MQTT_CMDEndPtr) //如果指針到緩沖區尾部了MQTT_CMDOutPtr = MQTT_CMDBuf[0]; //指針歸位到緩沖區開頭 }OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);//延時1ms} }1.5 UI顯示任務、觸摸檢測任務
直接在循環中調用UI顯示和觸摸檢測即可
//UI顯示任務 void emwindemo_task(void *p_arg) {while(1){Main_Ui();}} //觸摸檢測任務 void touch_task(void *p_arg) {OS_ERR err;while(1){GUI_TOUCH_Exec(); OSTimeDlyHMSM(0,0,0,5,OS_OPT_TIME_PERIODIC,&err);//延時5ms} }1.6 系統指示燈與堆棧檢測任務
用指示燈閃爍來證明系統正在工作,堆棧檢測是前期做UI時,監控UI任務是否有出現爆棧的情況
//LED心跳燈 void SystemLED_task(void *p_arg) {OS_ERR err;CPU_STK_SIZE free,used; while(1){LED0 = !LED0;OSTaskStkChk (&EmwindemoTaskTCB,&free,&used,&err); //printf("EmwindemoTaskTCB used/free:%d/%d usage:%%%d\r\n",used,free,(used*100)/(used+free));OSTimeDlyHMSM(0,0,0,500,OS_OPT_TIME_PERIODIC,&err);//延時500ms} }2.UI界面設計
UI界面這邊是使用STEMWIN來制作,因為F1的RAM比較小,在顯示大量控件和圖片時會出現卡頓的情況,所以UI整體只是將基本控件放置在屏幕中,沒有做換膚和美化。
STEMWIN這個庫,使用起來非常方便,在界面構建時,可以使用自帶的GUIBuilder來構建,在完成基本布局之后可以自動生成C代碼,在具體功能調試時,可以使用仿真文件進行仿真,功能完成之后,再移植到板子上。
UI界面代碼中,樣式占了大多數,具體的樣式代碼就不一一貼出,這里就只貼出關鍵功能代碼,具體可以參考開源代碼
2.1主界面
主界面的布局如下圖所示,使用Dialog為主窗口,中間使用IconView控件,在IconView中添加5個小圖標作為5個二級界面的入口,右上角使用系統RTC來記錄時間,使用STEMWIN的軟件定時器更新到屏幕上顯示。
這里偷了一個小賴,可以直接將圖標轉化成.c文件保存在ROM中,直接讀取顯示,中文也只是按照界面做了部分字庫。
圖片顯示具體方式參考:STEMWIN顯示BMP
中文顯示具體方式參考:STEMWINC字庫制作方式
向IconView添加圖標
圖標被按下
case WM_NOTIFY_PARENT:Id = WM_GetId(pMsg->hWinSrc);NCode = pMsg->Data.v;switch(Id) {case ID_ICONVIEW_0: // Notifications sent by 'Iconview'switch(NCode) {case WM_NOTIFICATION_CLICKED:// USER START (Optionally insert code for reacting on notification message)// USER ENDbreak;case WM_NOTIFICATION_RELEASED:/* 當小圖標按下且松開時 檢測是哪個小圖標被按下 */indx = ICONVIEW_GetSel(WM_GetDialogItem(pMsg->hWin, ID_ICONVIEW_0));/* 根據按下的小圖標來切換界面 */switch(indx){/* 如果是燈光界面被按下 */case Light:GUI_EndDialog(pMsg->hWin,0);CreateLightFramewin(); break;/* 如果是關于界面被按下 */case info:GUI_EndDialog(pMsg->hWin,0);CreatehuanyinFramewin(); break;/* 如果是居家數據界面被按下 */case data:GUI_EndDialog(pMsg->hWin,0);CreateDataUi();break;/* 如果是電氣控制界面被按下 */case control:GUI_EndDialog(pMsg->hWin,0);ElectricalControl();break;/* 如果是系統配置界面被按下 */case config:GUI_EndDialog(pMsg->hWin,0);CreateConfig();break;}break;在創建界面時創建一個1s周期的軟件定時器
WM_HWIN CreateFramewin(void) {WM_HWIN hWin;/* 定時器定義 */WM_HTIMER hTimer;WM_SetCreateFlags(WM_CF_MEMDEV_ON_REDRAW);hWin = GUI_CreateDialogBox(_aDialogCreate, GUI_COUNTOF(_aDialogCreate), _cbDialog, WM_HBKWIN, 0, 0);/* 設定定時器周期和應用窗體 */hTimer = WM_CreateTimer(WM_GetClientWindow(hWin), 0, 1000, 0);return hWin; }然后界面的定時器觸發中,更新RTC時間,這里的RTC庫是使用的正點原子的RTC歷程
case WM_TIMER:sprintf(date,"%0.2d/%0.2d/%0.2d %0.2d:%0.2d:%0.2d",calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);TEXT_SetText(WM_GetDialogItem(pMsg->hWin, ID_TEXT_1),date);WM_RestartTimer(pMsg->Data.v, 1000);break;2.1燈光界面
使用Dialog為主窗體,控制使用Button控制來控制GPIO的高低電平,通過IO電平來控制繼電器的吸合從而控制12V的點燈。
4個Button的寫法都是一樣,這里只貼出一個,其余只需要改IO口和控件號就可以
2.2 數據顯示界面
使用Dialog為主窗體,顯示4個圖標來表示數據。
這里的數據更新并不是在UI里面完成,而是在外面的數據采集任務中完成,進入此界面置位標志位,讓任務對這里的數據進行更新。具體可以看數據采集任務
2.3 電器控制界面
使用Dialog為主窗體,2Slider滑條分別控制風扇的PWM和舵機的旋轉角度,兩個Button分別控制另一個風扇和舵機旋轉角度。一個Checkbox來切換手動和自動模式的標志位,上電默認是自動模式。
這樣做主要是考慮到在實際中,廚房排氣扇一般開就滿速,不需要做調節,同理窗戶也是只有全開和全關。
Slider滑條控制舵機轉動
Slider控制風扇轉速
case ID_SLIDER_0: // Notifications sent by 'Slider'switch(NCode) {case WM_NOTIFICATION_CLICKED:break;case WM_NOTIFICATION_RELEASED:break;case WM_NOTIFICATION_VALUE_CHANGED: dis_Value = SLIDER_GetValue(WM_GetDialogItem(pMsg->hWin, ID_SLIDER_0));TEXT_SetFont(hItem, &GUI_Fontkongzhi_font24);sprintf(fanshan_data,"風扇轉速:%d%%",dis_Value);TEXT_SetText(WM_GetDialogItem(pMsg->hWin, ID_TEXT_0), fanshan_data);TIM_SetCompare2(TIM2,dis_Value);sprintf(dis_data,"風扇轉速:%d%%",dis_Value);break;}break;2.4系統設置界面
使用Dialog為主窗體,中間使用Multiedit來做多個子頁面切換的效果。這里只做了系統時間設置,可以在后續的拓展中,做WIFI信號選擇等。
Multiedit效果就是在本身這個界面中,再添加一個其他子界面到這個頁面中,然后通過上面的橫條進行切換,但本身這個界面不能關閉或者隱藏掛起。具體使用方法可以參考官方的手冊
時間設置主要是使用了Listwheel控制來做一個類似滑輪的效果來選擇時間。
滑輪控制的寫法是參考了野火的教程:野火列表輪教程
滑輪選擇核心代碼,原理參考野火的教程
case ID_LISTWHEEL_0: // Notifications sent by 'Listwheel'switch(NCode) {case WM_NOTIFICATION_CLICKED:hItem = WM_GetDialogItem(pMsg->hWin, ID_LISTWHEEL_0);LISTWHEEL_SetTextColor(hItem, LISTWHEEL_CI_SEL, 0x191919);break;case WM_NOTIFICATION_RELEASED:break;case WM_NOTIFICATION_SEL_CHANGED:hItem = WM_GetDialogItem(pMsg->hWin, ID_LISTWHEEL_0);LISTWHEEL_SetTextColor(hItem, LISTWHEEL_CI_SEL, 0x007dfe);index1 = LISTWHEEL_GetPos(hItem);LISTWHEEL_SetSel(hItem,index1);LISTWHEEL_GetItemText(hItem,index1,Year,10);break;}break;2.5 關于界面
顯示信息,沒什么好說的
三、上位機-OneNet云端前端界面
這里的前端是使用了OneNet自帶的大數據顯示制作
首先在產品界面右側找到應用管理
然后點這個
點新建項目
進去之后就可以在這里找到我們常用的控件了。有一些控件需要會員才能解鎖。
當我們獲取到數據之后,就可以在前端直接拿后臺的數據來進行顯示
具體前端操作可以參考官方手冊或者
View 2.0控件使用
View 2.0教程
這里列出我的數據源和過濾器
在新建數據源這里設備選擇你上傳數據的設備。在下面的數據流就可以選擇你上傳時候的文本了。
比如我上傳數據時,報文是這樣
那么就會有4個數據流,分別是temperature、humidity、Lux、ppm。在這里進行選擇就可以拿到單個數據了。
溫度計過濾器
function filter(data, rootData, variables) { data.forEach((item, index) => {x = 2 * (item.value / 100)y = item.value});return [{graphic: [{text: y +"°C"}, ],wave: [x] }] }儀表盤過濾器
function filter(data, rootData, variables) { function last(arr) {var len = arr ? arr.length : 0;if (len) return arr[len - 1]; } return [{value: last(data).value, }] }折線圖過濾器
function filter(data, rootData, variables){ rootData.meiqi_lfWa.forEach((item, index) => {item.x = item.update_atitem.y = item.valueitem.y1 = 250item.y2 = 200 });return rootData.meiqi_lfWa }按鈕和旋鈕使用就比較簡單了,只需要選擇一個數據源,然后在樣式里面就可以修改下發的命令了。貪方便可以全部控制控件都使用一個數據源來下發命令。這里的按鈕1和按鈕2就是按下和松開的狀態,命令內容里面改成和下位機匹配的命令即可。
按鈕
旋鈕
注意旋扭要發送當前的數值,只能寫成{V}這個格式
四、總結
首先感謝大家看到這里,簡單總結一下
這次課設的作品,總體來說做的完成度一般,沒有特別深入,做到了基本的控制、數據顯示、物聯網,整體實現了一個簡單的家居系統,系統可能還有許多BUG沒有解決,大家參考的時候酌情參考。當然這個項目后續可以拓展的點很多
- 增加OpenMv模塊來實現人臉檢測,這部分做到門鎖中
- 增加語音識別模塊來提供語音控制
- 增加外紅遙控模塊來控制空調、或者其他紅外設備
- 增加火焰傳感器來檢測火情
- UI界面可以進行二次美化
這是我第一次寫博客,寫的不好的地方請見諒,來都來了點個贊再走吧!
總結
以上是生活随笔為你收集整理的STM32课设-智能物联网家居系统(UCOSIII+STEMWIN)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用python做了个桌球瞄准器
- 下一篇: 计算机网络线接法,电脑网线水晶头接法图解