基於STM32+ESP8266的物聯網環境監測系統設計

                                                          STM32+UCOSII+ESP8266

參考鏈接:https://blog.csdn.net/qq_38410730/article/details/86538288

參考資料:朱有鵬老師網絡編程部分教程、正點原子官方資料、百度查詢

        因爲很早之前就一直想做一套完整的物聯網系統,所以趁着這段時間的一個設計,就一邊學一邊嘗試着做一套。系統數據採集部分採用大衆化的STM32作爲主控,再加上一些具有代表性的傳感器;數據傳輸部分即聯網媒介選擇ESP8266串口WIFI模塊;數據處理部分採用LAMP環境的雲服務器處理,再加上前端網頁共同組成一套完整的IOT系統(功能相對簡單)。

        數據採集部分。室內環境幾個具有代表性的參數,主要是甲醛、可燃性氣體、PM顆粒、溫溼度等,故本系統分別用到了ZE08-CH2O甲醛模組、MQ2、PM、DHT11幾個傳感器。主控採用STM32F103C8T6,這一塊因爲網上資料非常多,全部開源,所以就不不一一講述了。這部分主要講一下我在移植UCOSII的時候,遇到的問題。因爲系統涉及到很多傳感器,需要進行大量任務,如果一條主任務,肯定是滿足不了需求的,所以移植了自己相對較熟悉的UCOSII操作系統。參考教程即爲正點原子官方提供的UCOSII移植資料,移植完了後,遇到了一些小問題,比如浮點數8字節對齊( __align(8) 和任務棧大小的設置,這些都可能會影響到某個任務出現卡死的現象,因爲這些問題以及解決方法移植資料上面都會提及,就不一一贅述,此處說明是希望大家注意細節。

       數據傳輸部分。數據上傳到互聯網則需要聯網媒介作爲中轉,聯網的方式有很多,無線、有線,無線又分很多種:無線串口、zigbee、WIFI、藍牙、GPRS等等,基於多方面考慮,本系統採用ESP8266串口WIFI模塊作爲聯網媒介。關於ESP8266的資料也非常多,原理、代碼都講得非常清楚,因爲我採用的是正點原子開發板配套的WIFI模塊,所以直接移植了自帶的例程,採用的是TCP透傳模式,採集終端作爲客戶端,不斷向服務器上傳數據。因爲MiniStm32例程是自帶TFTLCD顯示屏的例程,加上WIFI模塊,配合按鍵,可以手動輸入模式、IP等等信息,當然了例程運行一切正常。但是因爲我暫時不需要顯示屏,所以就取消顯示屏部分,只需要wifi部分,但是這時候出錯了,始終連接不了服務器。此處卡死:

開始以爲是服務器的問題,結果調試了很久終於發現了問題,因爲例程是自帶的配置串口2作爲WIFI傳輸部分,我換了一個串口3馬上就好了,當然了並不是串口2配置不對,而是因爲例程是開啓了DMA傳輸,即串口2採用DMA傳輸,可能是因爲取消掉顯示屏的原因,導致DMA配置部分有一些遺漏,所以DMA傳輸失敗從而導致串口2通信失敗,取消DMA傳輸立即OK。希望大家遇到了類似的問題引起注意。後面調試甲醛傳感器的時候,因爲其支持兩種通信協議,分別是串口和ADC,剛開始採用串口3,後面發現一直失敗,而且顯示屏字體顏色變淡,讀不到數據導致系統卡死,後面發現是因爲TFTLCD顯示屏和串口3的引腳複用了,PB10,PB11都被顯示屏數據口占用,此時想到了端口複用功能,隨後參考數據手冊發現串口3的複用重映射引腳也同樣被佔用了,馬上看了串口4、5都是一樣的結果,沒辦法,只好改成ADC,採集模擬量,最後成功。

       後臺處理部分。本系統租用了一臺騰訊雲服務器,環境爲LAMP(Linux+Apache+Mysql+Php)。因爲服務器需要不斷接收來自採集終端部分的數據,所以接收服務器採用了socket監聽端口的方式來監聽接收數據。參考:朱有鵬老師網絡編程部分教程。代碼附在後面。接收到數據後,需要實時存儲到數據庫,以更新數據,保證數據的實時性。

       前端網頁部分。採用CSS+DIV佈局,PHP和服務器交互,目前實現了一部分,能夠實時顯示採集到的數據,但是目前採用的是比較古老的刷新頁面的方法來刷新數據,後面將會加上AJAX局部刷新技術來更新動態數據,並且附上數據動態曲線走勢圖。

當前效果還比較簡陋,暫不貼圖,後面完善了再附上詳細代碼和效果圖。望各位見諒...

附代碼:

數據傳輸部分(esp8266):

/***********************************************************
** 函 數 名: wifi_sta_trans_config
** 功能描述: ATK-ESP8266模塊TCP透傳模式配置
** 輸     入: 無
** 輸     出: 無
** 返 回 值: 無
** 日    期: 2019/4/23
************************************************************/
void wifi_sta_trans_config(void)
{
        //一、設置工作模式(需重啓生效) 1:station模式   2:AP模式  3:兼容 AP+station模式
        atk_8266_send_cmd("AT+CWMODE=1","OK",50);
        
        //二、Wifi模塊重啓生效
        atk_8266_send_cmd("AT+RST","ready",20);
        
        delay_ms(1000);         //延時3S等待重啓成功
        delay_ms(1000);
        delay_ms(1000);
        delay_ms(1000);
        
        //三、模塊連接上自己的路由
        while(atk_8266_send_cmd("AT+CWJAP=\"MERCURY\",\"12345678\"","WIFI GOT IP",600));
        
        //四、是否啓用多路連接  0:單路連接模式 1:多路連接模式
        atk_8266_send_cmd("AT+CIPMUX=0","OK",20);
        
        //五、建立TCP連接  這四項分別代表了:要連接的ID號0~4   連接類型  遠程服務器IP地址   遠程服務器端口號
        while(atk_8266_send_cmd("AT+CIPSTART=\"TCP\",\"192.168.x.x\",5000","CONNECT",200));
        
        //六、開啓透傳模式  0:表示關閉 1:表示開啓透傳
        atk_8266_send_cmd("AT+CIPMODE=1","OK",200);
        
        //七、透傳模式下開始發送數據  這個指令之後就可以直接發數據了
        atk_8266_send_cmd("AT+CIPSEND","OK",50);
}
 

/***********************************************************
** 函 數 名: atk_8266_send_cmd
** 功能描述: 向ATK-ESP8266發送命令
** 輸    入: cmd:發送的命令字符串  ack:期待的應答結果,如果爲空,則表示不需要等待應答 waittime:等待時間(單位:10ms)
** 輸    出: 無
** 返 回 值: 0,發送成功(得到了期待的應答結果) 1,發送失敗
** 日    期: 2019/3/23
************************************************************/
u8 atk_8266_send_cmd(u8 *cmd,u8 *ack,u16 waittime)
{
    u8 res=0; 
    USART2_RX_STA=0;
    u2_printf("%s\r\n",cmd);    //發送命令  (向WiFi模塊發送)
    
    if(ack&&waittime)        //需要等待應答
    {
        while(--waittime)    //等待倒計時
        {
            delay_ms(10);
            if(USART2_RX_STA&0X8000)//接收到期待的應答結果
            {
                if(atk_8266_check_cmd(ack))
                {
                    printf("ack:%s\r\n",(u8*)ack);
                    break;//得到有效數據 
                }
                USART2_RX_STA=0;
            } 
        }
        if(waittime==0)res=1; 
    }
    return res;


/***********************************************************
** 函 數 名: atk_8266_send_data
** 功能描述: 向ATK-ESP8266發送指定數據
** 輸    入: data:發送的數據(不需要添加回車了) ack:期待的應答結果,如果爲空,則表示不需要等待應答 waittime:等待時間(單位:10ms) 
** 輸    出: 無
** 返 回 值: 0,發送成功(得到了期待的應答結果)
** 日    期: 2019/3/23
************************************************************/
u8 atk_8266_send_data(u8 *data,u8 *ack,u16 waittime)
{
    u8 res=0; 
    USART2_RX_STA=0;
    u2_printf("%s",data);                     //發送命令
    
    if(ack&&waittime)                           //需要等待應答
    {
        while(--waittime)                       //等待倒計時
        {
            delay_ms(10);
            if(USART2_RX_STA&0X8000)           //接收到期待的應答結果
            {
                if(atk_8266_check_cmd(ack))break;//得到有效數據 
                USART2_RX_STA=0;
            } 
        }
        if(waittime==0)res=1; 
    }
    return res;
}
 

數據接收部分:

<?php      
    $my_severIP = "x.x.x.x";                      //服務器IP
    $my_serverPORT = 5000;                   //服務器端口號
    $my_databs_server = "localhost";        //數據庫服務器
    $my_databs_user = "root";                   //數據庫用戶名
    $my_databs_passwd = "xxxxxxxxx";    //數據庫密碼
    $my_databs_name = "xxx";                 //數據庫名稱
    
    //連接數據庫服務器
    $con = mysql_connect($my_databs_server,$my_databs_user,$my_databs_passwd);//連接數據庫
    if (!$con)
    {
        die('can`t connect to database $my_databs_user: ' . mysql_error());
    }
    else
    {
        echo "connect success...";
    }
    
    //選擇數據庫
    mysql_select_db($my_databs_name, $con);

    
    //**********第1步:socket_create 打開一個網絡文件描述符****************
    $socket_fd = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);  

    //**********第2步:socket_bind 綁定sokfd、服務端IP和端口號*************
    if(socket_bind($socket_fd,$my_severIP,$my_serverPORT) == false) //
    {
        echo 'server bind fail:'.socket_strerror(socket_last_error());
    }
    
    //**********第3步:socket_listen 監聽端口******************************
    if(socket_listen($socket_fd,4)==false)
    {
        echo 'server listen fail:'.socket_strerror(socket_last_error());
    }
    
    //**********第4步:socket_accept 阻塞等待客戶端連接服務器**************
    $client_fd = socket_accept($socket_fd);
    //echo 'clieid :'.$client_fd;
    //**********第5步:建立連接之後,進行通信(服務端接收)*******************************
    while(true)
    {
        $string = (int)socket_read($client_fd,1024);//PHP_BINARY_READ
        //echo 'receive_data:'.$string.PHP_EOL; //PHP_EOL爲php的換行預定義常量
        //$string = "66";
        //更新數據表信息
        var_dump($string);
            $result = mysql_query("update indoor set value = '{$string}' where paraname = 'temp' ");
        echo "update indoor set value = '{$string}' where paraname = 'temp' ";
        var_dump($result);
        
    }

    //關掉數據庫
    mysql_close($con);
    
?>
 

前端網頁部分代碼:

<html>

<head>
<!--<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />-->
<meta http-equiv="refresh" content="5" /><!--每10s刷新一次頁面,後期採用ajax技術優化-->
<title>室內環境監測系統</title>
<style type="text/css">
body {background-color: white}
</style>
</head>

<body>
<br>
<center><font face = 楷體 color = "blue" size = 7>室內環境在線監測系統</font></center>
<br>
<br>
<hr align = center color = "black" width = 100% size = 2>

<?php

    $my_databs_server = "localhost";        //數據庫服務器
    $my_databs_user = "root";               //數據庫用戶名
    $my_databs_passwd = "xxxxxx";    //數據庫密碼
    $my_databs_name = "xxx";                //數據庫名稱
    
    //連接數據庫服務器
    $con = mysql_connect($my_databs_server,$my_databs_user,$my_databs_passwd);//連接數據庫
    if (!$con)
    {
        die('can`t connect to database $my_databs_user: ' . mysql_error());
    }
    
    //選擇數據庫
    mysql_select_db($my_databs_name, $con);

    //選擇數據表查看
    $result = mysql_query("SELECT * FROM indoor");

    //以表格形式輸出
    echo "<table border='1'>
    <tr>
    <th>參數</th>
    <th>值</th>
    <th>走勢曲線</th>
    <th>單位</th>
    </tr>";

    //從數據表中讀取數據
    while($row = mysql_fetch_array($result))
    {
        //echo "<br />";
        //echo $row['paraname'] . "      " . $row['value']. "      " . $row['unit'];
        //echo "<br />";
        
        echo "<tr>";
        echo "<td align=center width='300' height='200'>   ".$row['paraname']."   </td>";
        echo "<td align=center width='300' height='200'>   ".$row['value']. "     </td>";
        echo "<td align=center width='1000' height='200'>  &nbsp                  </td>";
        echo "<td align=center width='300' height='200'>   ".$row['unit']."       </td>";
        echo "</tr>";
    }
    echo "</table>";
    
    //關閉數據庫
    mysql_close($con);
?>

<br>
<hr align = center color = "black" width = 100% size = 2>
<p><center><a href = "../index.html"; style = " font-family:楷體; text-decoration:none; color:black; cursor:pointer; font-size:25px; ">返回首頁</a></center></p>
</body>

</html>

持續更新中。。。

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章