想玩物聯網的時候物色了很多國内的雲平台,最後選了中移雲去實作。其實阿裡雲華為雲機智雲都不錯,機智雲網頁界面不行但是帶APP,阿裡雲也有個貝殼物聯APP功能更強大,華為雲不太了解不過好像也還行。中移雲感覺更加的純粹,沒有那麼多花裡胡哨的功能(其實是我用不到而已)。這裡記錄一下基于4個stm32+lora建立組網,上傳資料到雲平台這個簡單的項目。
硬體環境

硬體環境即一個主要闆(照片找不到了)+三個采集闆,基于LORA建構組網,采集3個不同地點的環境資料,通過WIFI子產品上發資料到雲平台。外加一個OpenMV3做人臉識别。采集闆挂載了溫濕度+光照強度+LORA子產品。
軟體環境
IDE:Keil 5.15
OS:Ucos-III
ESP8266 WIFI子產品基本知識詳解
市面上的WIFI子產品大多數都是樂鑫的,通過序列槽+AT指令,即可簡單的進行操作。
硬體連接配接:
AT指令基本結構:
WIFI子產品和伺服器建立連接配接有兩種方式:
TCP連接配接通信比較常用,再該模式下,相應的裝置必須處于同一網絡中,即處于同一個區域網路中!!!AP模式下ESP8266做服務端時就是,手機和電腦必須連在ESP8266的WiFi,才能用網絡調試助手等軟體與ESP8266通信。
WIFI 子產品的AT指令配置大家看官方文檔即可,整體了來說還是比較簡單的。WIFI一般有WIFI-AP模式,WIFI-STA模式,WIFI-AP兼STA模式三種,這裡隻說常用的前兩種:
①、WIFI-AP模式:首先,我們可以把WIFI子產品當成一個熱點,開啟熱點以後别的裝置可以連接配接這個熱點,進而我們就可以向連接配接熱點的裝置
!!發送資料!!
當ESP8266建立TCP伺服器時,步驟為:
1.AT+CWMODE=2//配置ESP8266為AP模式,(路由器模式,發出WIFI)。
2.AT+RST // 重新開機子產品使剛剛配置模式生效
3.AT+CWSAP=“wifi賬号”,“WIFI密碼”,1,4
4.AT+CIPMUX = 1 // 啟動多連接配接
5.AT+CIPSERVER = 1,8080 // 子產品開啟伺服器模式,端口号8080
這一步一定要保證裝置連接配接到了ESP8266的WIFI,不然會連接配接失敗
6.AT+CIPSEND=X,X // 第一個X是對應的ID号,第二個X是對應的字元個數,調用這個AT指令就可以向對應的用戶端發送資料,注意對應的字元個數問題,如果個數少了就不發,個數多了就隻發前對應的個數。
以上,便是ESP8266做伺服器,PC端(移動端)做用戶端的情況。
當ESP8266做用戶端,PC端(移動端)做伺服器時(單路連接配接),具體的步驟簡要為:
完成連接配接後,調用AT+CIPSEND這個指令即可與伺服器收發資料。這裡我們隻介紹單路連接配接,這種模式下是可以做到最多5路連接配接,感興趣的話可以查一下相關資料。
②.WIFI-STA模式:這個模式與AP模式相反,是ESP8266去連接配接别的路由器(WIFI),隻要保證與通信方處于同一區域網路下即可,可以了解為一個基站去接收信号。
ESP8266做伺服器,PC做用戶端,具體步驟為:
到這裡,我們發送AT+CWLAP指令可以查詢周圍的WIFI信号(去酒店之類的可以用這招預防那種WIFI攝像頭,但是SD卡那種還是防不了的。。)
發送AT+CWJAP?可以檢視WIFI是否真的連接配接成功。
4.AT+CIPMUX=1啟動多連接配接
5.AT+CIPSERVER=1,8080 // 打開伺服器端口為8080
6.AT+CIFSR // 看一下你子產品現在配置設定的ip是多少,隻要子產品聯網就會重新配置設定ip位址,這也是網絡調試助手裡面遠端主機欄要填寫的ip位址。
ESP8266做用戶端,PC端(移動端)做伺服器,具體步驟如下:
1.AT+CWMODE=1,設定為STA模式
2.AT+RST , 重新開機生效
3.AT+CWJAP=“WIFI賬号”,“WIFI密碼”
4.AT+CIPMUX=0 ,單連接配接
5.AT+CIPSTART=“TCP”,“192.168.1.101”,8080
下面用AT+CIPSEND發送資料即可
這次工程是把WIFI子產品配成STA模式下的用戶端,網站就當成伺服器,友善操作。
OneNET建立自己的物聯網應用
首先來個官網網址中移雲網址,進去以後注冊一個自己的賬号
點選開發者中心,然後建立自己的産品。這裡跟着官方文檔走就好了,不多贅述建立産品
接入協定這裡選擇http協定,因為我們隻需要POST上報資料,GET獲得資料即可。有能力的話建議搞一下MQTT協定,近兩年最火的物聯網協定。然後建一個裝置,這裡跟着官方文檔走就可以了。最後我們會得到這些必要資訊:
這四個ID/key是上報資料必須的東西,要做好記錄。至此平台端我們已經解決了大半。
收發協定
因為涉及了不同裝置多個資料的采集,這裡制定了一個簡單的協定,隻有一個幀頭采集闆發過來的幀頭到主要闆會被識别為ASCII碼,是以主要接收從機幀頭時一定要以ASCII碼形式判斷。其實出來實習以後,發現這些協定走16進制的話會更友善,便于不同平台之間互動。下面上代碼:
u8 a = 0;
u8 b = 0;
u8 c = 0;
u8 RX1_Buff[3];
u8 RX2_Buff[3];
u8 RX3_Buff[3];
int i = 1;
int j = 1;
int k = 1;
char one = '1';
char two = '2';
char three = '3';
void USART2_IRQHandler(void)
{
u8 res;
{
res=USART_ReceiveData(USART2);
//printf("%c",res);
if(res == one)
{
a = 1;
two = '7';
three = '8';
}
if(a == 1)
{
switch(i)
{
case 1:i++;break;
case 2:i++;RX1_Buff[0] = res;break;
case 3:i++;RX1_Buff[1] = res;break;
case 4:i++;RX1_Buff[2] = res;i=1;a=0;two = '2';three = '3';break;
default:break;
}
}
if(res == two)
{
b=1;
one = '6';
three = '8';
}
if(b == 1)
{
switch(j)
{
case 1:j++;break;
case 2:j++;RX2_Buff[0]=res;;break;
case 3:j++;RX2_Buff[1]=res;;break;
case 4:j++;RX2_Buff[2]=res;j=1;b=0;one = '1';three = '3';break;
default:break;
}
}
if(res == three)
{
c = 1;
one = '6';
two = '7';
}
if(c == 1)
{
switch(k)
{
case 1:k++;break;
case 2:k++;RX3_Buff[0]=res;break;
case 3:k++;RX3_Buff[1]=res;break;
case 4:k++;RX3_Buff[2]=res;k=1;c=0;one = '1';two = '2';break;
default:break;
}
}
LED0 = ~LED0;
printf("從機1%d,%d,%d\r\n",RX1_Buff[0],RX1_Buff[1],RX1_Buff[2]);
printf("從機2%d,%d,%d\r\n",RX2_Buff[0],RX2_Buff[1],RX2_Buff[2]);
printf("從機3%d,%d,%d\r\n",RX3_Buff[0],RX3_Buff[1],RX3_Buff[2]);
USART_ClearITPendingBit(USART2,USART_IT_RXNE);
}
}
其實思想就是每個從機都有自己的一個幀頭,後面分别跟着溫度濕度光照強度等環境資料。
與OneNET雲平台的互動
這裡我們把WIFI子產品設定為STA工作模式,就是把WIFI子產品連接配接我們手機的熱點,然後與OenNET雲平台建立TCP連接配接,開啟透傳模式。資料通過POST發送,打包成JSON格式。現在知道了官方有個cJSON,經過簡單的移植就可以自動打包JSON資料,但是當時年少無知,就直接手打JSON格式就好了。datastreams這個資料後面跟着傳感器的名稱和資料流的名稱即可。api-key從前面我們說的地方擷取即可,Host則直接用這個網址就好,Content-Length的值一定要和datastreams後面的大小對應,不然會報格式錯誤。
#include "common.h"
#include "stdlib.h"
#include "string.h"
#include "dht11.h"
#include "adc.h"
#include "lsens.h"
#include "led.h"
//ATK-ESP8266 WIFI STA測試
//用于測試TCP/UDP連接配接
//傳回值:0,正常
// 其他,錯誤代碼
extern u8 RX1_Buff[3];
extern u8 RX2_Buff[3];
extern u8 RX3_Buff[3];
u8 atk_8266_wifista_test(void)
{
u8 key;
u8 timex=0;
u8 b;
char RX_Buff[200];
u8 strData[2];
u8 *p;
u8 *q;
u8 *m;
u16 t=9999; //加速第一次擷取連結狀态
u8 res=0;
u16 rlen=0;
u8 constate=0; //連接配接狀态
HC_Init();//LORA資料收發協定初始化
atk_8266_send_cmd("AT+CWMODE=1","OK",500); //設定WIFI STA模式
atk_8266_send_cmd("AT+RST","OK",200); //DHCP伺服器關閉(僅AP模式有效)
delay_ms(1000); //延時3S等待重新開機成功
delay_ms(1000);
//設定連接配接到的WIFI網絡名稱/加密方式/密碼,這幾個參數需要根據自己的路由器設定進行修改!!
//sprintf((char*)p,"AT+CWJAP=\"%s\",\"%s\"",wifista_ssid,wifista_password);//設定無線參數:ssid,密碼
p = "AT+CWJAP=\"mayday123\",\"sunshine998\"";
while(atk_8266_send_cmd(p,"WIFI GOT IP",3000)); //連接配接目标路由器,并且獲得IP
{
atk_8266_send_cmd("AT+CIPMUX=0","OK",200); //0:單連接配接,1:多連接配接
q = "AT+CIPSTART=\"TCP\",\"183.230.40.33\",80";
//sprintf((char*)p,"AT+CIPSTART=\"TCP\",\"%s\",%s",(u8*)ipbuf,(u8*)portnum); //配置目标TCP伺服器
while(atk_8266_send_cmd(q,"OK",2000));
atk_8266_send_cmd("AT+CIPMODE=1","OK",2000); //傳輸模式為:透傳
}
USART3_RX_STA=0;
atk_8266_quit_trans();
atk_8266_send_cmd("AT+CIPSEND","OK",200); //開始透傳
while(1)
{
/*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
/*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
/*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
///***********************************發送第一從機溫度資料**************************************/
sprintf(RX_Buff,"POST /devices/526305478/datapoints HTTP/1.1\r\n\
api-key:AXFPHI6MWoj6oTvz6MQVyEGANR0=\r\n\
Host:api.heclouds.com\r\n\
Content-Length:63\r\n\
\r\n\
{\"datastreams\":[{\"id\":\"temp_num\",\"datapoints\":[{\"value\":%d}]}]}",RX1_Buff[0]);
u3_printf("%s",RX_Buff);
// delay_ms(500);
for(b = 0;b < sizeof(RX_Buff);b++)
{
RX_Buff[b] = 0 ;
}
///******************************************************************************************/
delay_ms(500);
///***********************************發送第一從機濕度資料**************************************/
sprintf(RX_Buff,"POST /devices/526305478/datapoints HTTP/1.1\r\n\
api-key:AXFPHI6MWoj6oTvz6MQVyEGANR0=\r\n\
Host:api.heclouds.com\r\n\
Content-Length:63\r\n\
\r\n\
{\"datastreams\":[{\"id\":\"humi_num\",\"datapoints\":[{\"value\":%d}]}]}",RX1_Buff[1]);
u3_printf("%s",RX_Buff);
// delay_ms(500);
for(b = 0;b < sizeof(RX_Buff);b++)
{
RX_Buff[b] = 0 ;
}
///******************************************************************************************/
delay_ms(500);
///***********************************發送第一從機光照強度資料************************************/
sprintf(RX_Buff,"POST /devices/526305478/datapoints HTTP/1.1\r\n\
api-key:AXFPHI6MWoj6oTvz6MQVyEGANR0=\r\n\
Host:api.heclouds.com\r\n\
Content-Length:63\r\n\
\r\n\
{\"datastreams\":[{\"id\":\"illu_num\",\"datapoints\":[{\"value\":%d}]}]}",RX1_Buff[2]);
u3_printf("%s",RX_Buff);
// delay_ms(500);
for(b = 0;b < sizeof(RX_Buff);b++)
{
RX_Buff[b] = 0 ;
}
///******************************************************************************************/
delay_ms(500);
/*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
/*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
/*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
/***********************************發送第二從機溫度資料**************************************/
sprintf(RX_Buff,"POST /devices/526305478/datapoints HTTP/1.1\r\n\
api-key:AXFPHI6MWoj6oTvz6MQVyEGANR0=\r\n\
Host:api.heclouds.com\r\n\
Content-Length:64\r\n\
\r\n\
{\"datastreams\":[{\"id\":\"temp1_num\",\"datapoints\":[{\"value\":%d}]}]}",RX2_Buff[0]);
u3_printf("%s",RX_Buff);
// delay_ms(500);
for(b = 0;b < sizeof(RX_Buff);b++)
{
RX_Buff[b] = 0 ;
}
/*********************************************************************************************/
delay_ms(500);
///***********************************發送第二從機濕度資料**************************************/
sprintf(RX_Buff,"POST /devices/526305478/datapoints HTTP/1.1\r\n\
api-key:AXFPHI6MWoj6oTvz6MQVyEGANR0=\r\n\
Host:api.heclouds.com\r\n\
Content-Length:64\r\n\
\r\n\
{\"datastreams\":[{\"id\":\"humi1_num\",\"datapoints\":[{\"value\":%d}]}]}",RX2_Buff[1]);
u3_printf("%s",RX_Buff);
// delay_ms(500);
for(b = 0;b < sizeof(RX_Buff);b++)
{
RX_Buff[b] = 0 ;
}
///******************************************************************************************/
delay_ms(500);
///***********************************發送第二從機光照強度資料************************************/
sprintf(RX_Buff,"POST /devices/526305478/datapoints HTTP/1.1\r\n\
api-key:AXFPHI6MWoj6oTvz6MQVyEGANR0=\r\n\
Host:api.heclouds.com\r\n\
Content-Length:64\r\n\
\r\n\
{\"datastreams\":[{\"id\":\"illu1_num\",\"datapoints\":[{\"value\":%d}]}]}",RX2_Buff[2]);
u3_printf("%s",RX_Buff);
// delay_ms(500);
for(b = 0;b < sizeof(RX_Buff);b++)
{
RX_Buff[b] = 0 ;
}
/******************************************************************************************/
/*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
/*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
/*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
/***********************************發送第三從機溫度資料**************************************/
sprintf(RX_Buff,"POST /devices/526305478/datapoints HTTP/1.1\r\n\
api-key:AXFPHI6MWoj6oTvz6MQVyEGANR0=\r\n\
Host:api.heclouds.com\r\n\
Content-Length:64\r\n\
\r\n\
{\"datastreams\":[{\"id\":\"temp2_num\",\"datapoints\":[{\"value\":%d}]}]}",RX3_Buff[0]);
u3_printf("%s",RX_Buff);
// delay_ms(500);
for(b = 0;b < sizeof(RX_Buff);b++)
{
RX_Buff[b] = 0 ;
}
/*********************************************************************************************/
delay_ms(500);
/***********************************發送第三從機濕度資料*************************************/
sprintf(RX_Buff,"POST /devices/526305478/datapoints HTTP/1.1\r\n\
api-key:AXFPHI6MWoj6oTvz6MQVyEGANR0=\r\n\
Host:api.heclouds.com\r\n\
Content-Length:64\r\n\
\r\n\
{\"datastreams\":[{\"id\":\"humi2_num\",\"datapoints\":[{\"value\":%d}]}]}",RX3_Buff[1]);
u3_printf("%s",RX_Buff);
// delay_ms(500);
for(b = 0;b < sizeof(RX_Buff);b++)
{
RX_Buff[b] = 0 ;
}
///******************************************************************************************/
delay_ms(500);
///***********************************發送第三從機光照強度資料*********************************/
sprintf(RX_Buff,"POST /devices/526305478/datapoints HTTP/1.1\r\n\
api-key:AXFPHI6MWoj6oTvz6MQVyEGANR0=\r\n\
Host:api.heclouds.com\r\n\
Content-Length:64\r\n\
\r\n\
{\"datastreams\":[{\"id\":\"illu2_num\",\"datapoints\":[{\"value\":%d}]}]}",RX3_Buff[2]);
u3_printf("%s",RX_Buff);
// delay_ms(500);
for(b = 0;b < sizeof(RX_Buff);b++)
{
RX_Buff[b] = 0 ;
}
/******************************************************************************************/
delay_ms(500);
if(LED2 == 1)
{
sprintf(RX_Buff,"POST /devices/526305478/datapoints HTTP/1.1\r\n\
api-key:AXFPHI6MWoj6oTvz6MQVyEGANR0=\r\n\
Host:api.heclouds.com\r\n\
Content-Length:63\r\n\
\r\n\
{\"datastreams\":[{\"id\":\"open_see\",\"datapoints\":[{\"value\":11}]}]}");
u3_printf("%s",RX_Buff);
// delay_ms(500);
for(b = 0;b < sizeof(RX_Buff);b++)
{
RX_Buff[b] = 0 ;
}
}else{
sprintf(RX_Buff,"POST /devices/526305478/datapoints HTTP/1.1\r\n\
api-key:AXFPHI6MWoj6oTvz6MQVyEGANR0=\r\n\
Host:api.heclouds.com\r\n\
Content-Length:63\r\n\
\r\n\
{\"datastreams\":[{\"id\":\"open_see\",\"datapoints\":[{\"value\":22}]}]}");
u3_printf("%s",RX_Buff);
// delay_ms(500);
for(b = 0;b < sizeof(RX_Buff);b++)
{
RX_Buff[b] = 0 ;
}
}
/*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
/*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
/*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
atk_8266_at_response(1);
for(b = 0;b < sizeof(RX_Buff);b++)
{
RX_Buff[b] = 0 ;
}
}
return res;
}
AT指令的發送直接用了原子的程式,在序列槽中斷裡根據協定接收資料然後上發到雲平台即可。簡要來說就是:
通過将WIFI連接配接上網絡,解析出OneNET雲平台的IP和端口号,在雲平台建立裝置,每個裝置會配置設定一個獨有的序列号(用于WIFI與裝置連接配接),然後通過AT指令,将WIFI子產品連接配接上雲平台,此時用WIFI發送在雲平台建立好的裝置的序列号,即可連接配接裝置,與裝置進行通信。然後定時發送從采集闆收到的資料,當資料超過雲平台的報警值之後,雲平台會自動報警,并将報警資訊通過使用者綁定郵箱發送給使用者,實作實時監測的效果。
把每個步驟都列印出來:
當執行到指令AT+CIPSEND時,将資料打包成JSON格式發送即可。
回報的資訊為:
這個項目比較簡單不需要解析回報的JSON格式的資料,當error後面為0時,則代表資料上傳正确。
下面介紹兩個比較重要的點。
如何截取整段回報的JSON封包
//序列槽接收緩存區
u8 USART3_RX_BUF[USART3_MAX_RECV_LEN]; //接收緩沖,最大USART3_MAX_RECV_LEN個位元組.
u8 USART3_TX_BUF[USART3_MAX_SEND_LEN]; //發送緩沖,最大USART3_MAX_SEND_LEN位元組
//通過判斷接收連續2個字元之間的時間差不大于10ms來決定是不是一次連續的資料.
//如果2個字元接收間隔超過10ms,則認為不是1次連續資料.也就是超過10ms沒有接收到
//任何資料,則表示此次接收完畢.
//接收到的資料狀态
//[15]:0,沒有接收到資料;1,接收到了一批資料.
//[14:0]:接收到的資料長度
vu16 USART3_RX_STA=0;
void USART3_IRQHandler(void)
{
u8 res;
if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)//接收到資料
{
res =USART_ReceiveData(USART3);
if((USART3_RX_STA&(1<<15))==0)//接收完的一批資料,還沒有被處理,則不再接收其他資料
{
if(USART3_RX_STA<USART3_MAX_RECV_LEN) //還可以接收資料
{
TIM_SetCounter(TIM7,0);//計數器清空 //計數器清空
if(USART3_RX_STA==0) //使能定時器7的中斷
{
TIM_Cmd(TIM7,ENABLE);//使能定時器7
}
USART3_RX_BUF[USART3_RX_STA++]=res; //記錄接收到的值
}else
{
USART3_RX_STA|=1<<15; //強制标記接收完成
}
}
}
}
//初始化IO 序列槽3
//pclk1:PCLK1時鐘頻率(Mhz)
//bound:波特率
void usart3_init(u32 bound)
{
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // GPIOB時鐘
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE); //序列槽3時鐘使能
USART_DeInit(USART3); //複位序列槽3
//USART3_TX PB10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PB10
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //複用推挽輸出
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化PB10
//USART3_RX PB11
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化PB11
USART_InitStructure.USART_BaudRate = bound;//波特率一般設定為9600;
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(USART3, &USART_InitStructure); //初始化序列槽 3
USART_Cmd(USART3, ENABLE); //使能序列槽
//使能接收中斷
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//開啟中斷
//設定中斷優先級
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;//搶占優先級3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子優先級3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根據指定的參數初始化VIC寄存器
TIM7_Int_Init(10000-1,7200-1); //100ms中斷
USART3_RX_STA=0; //清零
TIM_Cmd(TIM7,DISABLE); //關閉定時器7
}
這裡也是用原子的程式,其實思想很簡單,第一次進入序列槽中斷時打開定時器,一定時間(10ms)沒有接收到資料就判定為一幀資料接收完畢。其實如果現在做這個的話直接用IDEL更友善,不需要這麼麻煩。
序列槽3的重定向
//va_list 是C語言定義的宏,解決變量參數的一個宏,可以傳入一個參數或多個參數,且對類型無要求,用法:
//首先在函數中,定義一個具有va_list類型的參數ap
//然後在va_start宏初始化變量剛定義的va_list類型的變量,執行:ap = (va_list)&fmt + _INTSIZEOF(fmt),此時ap指向第一個可變參數在堆棧的位址。
//函數聲明:int vsprintf(char *str, const char *format, va_list arg)
//str -- str指向一個字元數組的指針,該數組存儲了C字元串。
//format -- 這是字元串,包含了要被寫入到字元串str的文本。它可以包含嵌入的format标簽,format标簽可被随後的附加參數中指定的值替換
//并按需求進行格式化。format标簽屬性是%[flags][width][.precision][length]specifier。
//va_end(ap)的作用是清空va_list ap。
//下面是事例,一看就懂。
void u3_printf(char* fmt,...)
{
u16 i,j;
va_list ap;
va_start(ap,fmt);
vsprintf((char*)USART3_TX_BUF,fmt,ap);
va_end(ap);
i=strlen((const char*)USART3_TX_BUF); //此次發送資料的長度
for(j=0;j<i;j++) //循環發送資料
{
while(USART_GetFlagStatus(USART3,USART_FLAG_TC)==RESET); //循環發送,直到發送完畢
USART_SendData(USART3,USART3_TX_BUF[j]);
}
}
這個其實沒啥講的,用的東西是非開源的,隻要直到怎麼用就好了,注釋的很清楚。這樣做就可以把序列槽3以printf的形式調用,非常友善。
效果展示
在我們的雲平台是可以搭建界面的,這裡顯示了3地溫濕度,光照強度的曲線比較,并且也可以單獨顯示某一個地點的環境資料,并且還有異常報警機制,就是發郵箱了。。