云平臺配置
訪問下載頁面:免費試用 EMQX Cloud 或 EMQX Enterprise | 下載 EMQX,根據需求選擇對應版本下載。將下載的壓縮包上傳至服務器(推薦存放于C盤根目錄,便于后續操作),并解壓至指定路徑(例如:C:\emqx)。按住Win+R鍵,輸入cmd后按回車,打開Windows命令行界面。
輸入cd C:\emqx\bin(若解壓路徑不同,請替換為實際路徑),按回車進入bin文件夾。
輸入emqx start,按回車執行啟動命令。
打開瀏覽器,輸入服務器公網 IP 及管理端口:http://公網IP:18083
登錄系統:默認用戶名:admin默認密碼:public
部署在公網服務器后,可通過任意設備訪問該 IP 地址,實現遠程管理。
設備接線
GPS的天線要放室外。
實物接線
代碼
main.c
#include "stm32f10x.h"
#include "string.h"
#include "stdio.h"
#include "delay.h"
#include "bsp_usart.h"
#include "oled.h"
#include "BC20.h"
#include "bsp_dht11.h"GPS_Data gps_data = {0};
float temp,humi;
extern BC20_Status s_bc20_status;
int count=0;
char OLEDBuff[512];
int main(void)
{NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);SysTick_Init(72); //系統時鐘初始化 usart1_init(115200);//串口1初始化printf("USART1 OK!\r\n");usart2_init(9600);//串口2初始化(BC20模塊)usart3_init(115200);//串口3初始化 OLED_Init(); OLED_Clear();BC20_Init(); // 連接OneNet平臺,帶重試機制int connect_retry = 3;while(connect_retry-- > 0) {if(BC20_ConnectOneNet() == BC20_OK) {break;}printf("OneNet連接失敗,剩余重試次數: %d\r\n", connect_retry);delay_ms(2000);}if(connect_retry <= 0) {printf("OneNet連接失敗,系統將重啟...\r\n");delay_ms(2000);NVIC_SystemReset();}DHT11_Init(); //DHT11溫濕度傳感器初始化OLED_ShowString(40,0,"BC20");OLED_ShowString(0, 2, "溫度: 濕度: "); OLED_ShowString(0,4,"經度"); OLED_ShowString(0,6,"緯度"); while(1){BC20_ProcessReceivedData();// 讀取溫濕度if(DHT11_Read_TempAndHumidity(&DHT11_Data) == SUCCESS) {temp = DHT11_Data.temp_int + DHT11_Data.temp_deci * 0.1;humi = DHT11_Data.humi_int + DHT11_Data.humi_deci * 0.1;OLED_ShowNum(40, 2, (int)temp, 2, 16);OLED_ShowNum(100, 2, (int)humi, 3, 16);} else {printf("DHT11讀取失敗\r\n");}// 定時發送數據count++;if(count >= 40) { // 40 * 50ms = 2秒printf("溫度: %.1f 濕度: %.1f\r\n", temp, humi);count = 0;// 獲取GPS數據(增加超時限制,避免長時間阻塞)static uint32_t gps_retry = 0;uint32_t gps_start = 0; gps_start++;if(BC20_GetGPSData(&gps_data) != BC20_OK) {gps_retry++;printf("GPS數據獲取失敗,重試次數: %d\r\n", gps_retry);if(gps_retry >= 5) {BC20_InitGPS();gps_retry = 0;}} else {gps_retry = 0;sprintf(OLEDBuff,"%.6f",gps_data.latitude);OLED_ShowString(40,4,OLEDBuff);sprintf(OLEDBuff,"%.6f",gps_data.longitude);OLED_ShowString(40,6,OLEDBuff);}BC20_SendToOneNet(temp, humi, &gps_data);}delay_ms(50);}
}
bc20.c
#include "BC20.h"
#include "stm32f10x_iwdg.h"
#include "oled.h"
#include "bsp_usart.h"
#include "delay.h"
#include <math.h>// 靜態內部變量
static char s_recv_buf[BC20_BUF_LEN]; // 接收緩沖區
BC20_Status s_bc20_status = {0}; // 模塊狀態// 私有函數聲明
void BC20_ClearRecvBuf(void);
static BC20_ErrorCode BC20_SendAT(const char *cmd, const char *expected_resp, uint32_t timeout_ms);
static BC20_ErrorCode BC20_SendAT_NOOLED(const char *cmd, const char *expected_resp, uint32_t timeout_ms);
BC20_ErrorCode BC20_InitGPS(void);
static BC20_ErrorCode BC20_ParseGPRMC(const char *rmc_str, GPS_Data *gps);// 清空接收緩沖區
void BC20_ClearRecvBuf(void)
{memset(s_recv_buf, 0, BC20_BUF_LEN);Clear_Buffer_UART2(); // 確保串口2緩沖區也被清空delay_ms(50); // 縮短延遲,避免阻塞
}// OLED顯示AT指令(輔助函數)
void OLED_ShowAT(char* cmd)
{char cmd_display[32] = {0};if (strncmp(cmd, "AT", 2) == 0) {const char *after_at_plus = cmd + 0;const char *q_pos = strchr(after_at_plus, '=');if (q_pos != NULL) {size_t len = q_pos - after_at_plus;if (len > 0) {strncpy(cmd_display, after_at_plus, len);cmd_display[len] = '\0';} else {strcpy(cmd_display, "+");}} else {strncpy(cmd_display, after_at_plus, sizeof(cmd_display)-1);char *newline = strchr(cmd_display, '\r');if (newline) *newline = '\0';newline = strchr(cmd_display, '\n');if (newline) *newline = '\0';}} else {strncpy(cmd_display, cmd, sizeof(cmd_display)-1);}OLED_Clear();OLED_ShowString(0,2,cmd_display);
}// 發送AT命令(帶OLED顯示)
static BC20_ErrorCode BC20_SendAT(const char *cmd, const char *expected_resp, uint32_t timeout_ms) {uint32_t count = 0;uint32_t max_count = timeout_ms / 10;if (timeout_ms % 10 != 0) max_count++;BC20_ClearRecvBuf();printf("\r\n【發送AT指令】: %s", cmd);USART2_SendStr((char*)cmd);OLED_ShowAT((char*)cmd);while (count < max_count) {if (buf_uart2.rx_flag) {strncpy(s_recv_buf, buf_uart2.buf, BC20_BUF_LEN-1);s_recv_buf[BC20_BUF_LEN-1] = '\0';printf("【接收響應】: %s", s_recv_buf);if (strstr(s_recv_buf, expected_resp) != NULL) {cprintf("【指令執行成功】\r\n");return BC20_OK;}buf_uart2.rx_flag = 0;}delay_ms(10);count++;}printf("【指令超時】- 預期響應: %s, 實際接收: %s\r\n", expected_resp, s_recv_buf);return BC20_ERR_TIMEOUT;
}// 發送AT命令(不帶OLED顯示)- 關鍵修復:移除成功時的緩沖區清空
static BC20_ErrorCode BC20_SendAT_NOOLED(const char *cmd, const char *expected_resp, uint32_t timeout_ms) {uint32_t count = 0;uint32_t max_count = timeout_ms / 10;if (timeout_ms % 10 != 0) max_count++;BC20_ClearRecvBuf();printf("\r\n【發送AT指令】: %s", cmd);USART2_SendStr((char*)cmd);while (count < max_count) {if (buf_uart2.rx_flag) {strncpy(s_recv_buf, buf_uart2.buf, BC20_BUF_LEN-1);s_recv_buf[BC20_BUF_LEN-1] = '\0';printf("【接收響應】: %s", s_recv_buf);if (strstr(s_recv_buf, expected_resp) != NULL) {// 關鍵修復:不在此處清空緩沖區,保留數據供后續解析printf("【指令執行成功】\r\n");return BC20_OK;}buf_uart2.rx_flag = 0;}delay_ms(10);count++;}printf("【指令超時】- 預期響應: %s, 實際接收: %s\r\n", expected_resp, s_recv_buf);return BC20_ERR_TIMEOUT;
}// BC20模塊初始化
void BC20_Init(void) {BC20_ErrorCode ret;BC20_ClearRecvBuf();printf("\r\n====================================\r\n");printf("【開始初始化BC20模塊】\r\n");// 檢查模塊響應printf("【步驟1/5】檢查模塊響應...\r\n");ret = BC20_SendAT("ATi\r\n", "OK", 3000);if (ret != BC20_OK) {s_bc20_status.err_code = BC20_ERR_RESPONSE;printf("【錯誤】模塊無響應\r\n");return;}// 關閉回顯ret = BC20_SendAT("ATE0\r\n", "OK", 3000);if (ret != BC20_OK) {s_bc20_status.err_code = BC20_ERR_RESPONSE;printf("【錯誤】關閉回顯失敗\r\n");return;}// 檢查SIM卡printf("【步驟2/5】檢查SIM卡...\r\n");ret = BC20_SendAT("AT+CIMI\r\n", "OK", 3000);if (ret != BC20_OK) {s_bc20_status.err_code = BC20_ERR_SIM;printf("【錯誤】SIM卡異常\r\n");return;}printf("【SIM卡正常】IMSI: %s\r\n", s_recv_buf + 8);BC20_ClearRecvBuf(); // 手動清空,避免影響后續指令// 檢查網絡注冊printf("【步驟3/5】檢查網絡注冊...\r\n");ret = BC20_SendAT("AT+CGATT?\r\n", "+CGATT: 1", 10000);if (ret != BC20_OK) {s_bc20_status.err_code = BC20_ERR_NETWORK;printf("【錯誤】網絡注冊失敗\r\n");return;}BC20_ClearRecvBuf(); // 手動清空// 檢查信號質量printf("【步驟4/5】檢查信號質量...\r\n");ret = BC20_SendAT("AT+CSQ\r\n", "+CSQ", 3000);if (ret == BC20_OK) {printf("【信號質量信息】: %s\r\n", s_recv_buf);}BC20_ClearRecvBuf(); // 手動清空// 初始化GPSprintf("【步驟5/5】初始化GPS...\r\n");if (BC20_InitGPS() != BC20_OK) {printf("【警告】GPS初始化失敗(不影響主功能)\r\n");} else {printf("【GPS初始化成功】\r\n");}s_bc20_status.err_code = BC20_OK;printf("【BC20模塊初始化完成】\r\n");printf("====================================\r\n");
}// GPS初始化
BC20_ErrorCode BC20_InitGPS(void) {BC20_ErrorCode ret;printf("【啟動GNSS】\r\n");ret = BC20_SendAT_NOOLED("AT+QGNSSC=1\r\n", "", 5000);if (ret != BC20_OK) {printf("【錯誤】啟動GNSS失敗\r\n");return BC20_ERR_GPS;}BC20_ClearRecvBuf(); // 手動清空printf("【確認GPS啟動狀態】\r\n");ret = BC20_SendAT_NOOLED("AT+QGNSSC?\r\n", "+QGNSSC: 1", 5000);if (ret != BC20_OK) {printf("【錯誤】GPS未啟動\r\n");return BC20_ERR_GPS;}BC20_ClearRecvBuf(); // 手動清空return BC20_OK;
}// 解析GPRMC語句
static BC20_ErrorCode BC20_ParseGPRMC(const char *rmc_str, GPS_Data *gps) {char *token;char rmc_copy[BC20_BUF_LEN];strncpy(rmc_copy, rmc_str, BC20_BUF_LEN-1);rmc_copy[BC20_BUF_LEN-1] = '\0';// 分割字段(逗號分隔)token = strtok(rmc_copy, ","); // $GNRMCif (token == NULL) return BC20_ERR_GPS;token = strtok(NULL, ","); // 時間token = strtok(NULL, ","); // 狀態(A=有效,V=無效)if (token == NULL || strcmp(token, "A") != 0) {gps->is_valid = 0;printf("【GPS定位無效】狀態位為V\r\n");return BC20_OK;}// 解析緯度(格式:ddmm.mmmm)token = strtok(NULL, ","); // 緯度值if (token == NULL || strlen(token) < 5) { // 增加長度檢查printf("【錯誤】緯度數據無效\r\n");return BC20_ERR_GPS;}float lat_deg = atof(token) / 100; // dd + mm.mmmm/100int deg = (int)lat_deg;gps->latitude = deg + (lat_deg - deg) * 100 / 60;token = strtok(NULL, ","); // N/Sif (token != NULL && strcmp(token, "S") == 0) {gps->latitude = -gps->latitude;}// 解析經度(格式:dddmm.mmmm)token = strtok(NULL, ","); // 經度值if (token == NULL || strlen(token) < 6) { // 增加長度檢查printf("【錯誤】經度數據無效\r\n");return BC20_ERR_GPS;}float lon_deg = atof(token) / 100; // ddd + mm.mmmm/100deg = (int)lon_deg;gps->longitude = deg + (lon_deg - deg) * 100 / 60;token = strtok(NULL, ","); // E/Wif (token != NULL && strcmp(token, "W") == 0) {gps->longitude = -gps->longitude;}gps->is_valid = 1;printf("【GPS定位有效】緯度: %.6f, 經度: %.6f\r\n", gps->latitude, gps->longitude);return BC20_OK;
}// 獲取GPS數據 - 修復緩沖區處理
BC20_ErrorCode BC20_GetGPSData(GPS_Data *gps) {BC20_ErrorCode ret;char *rmc_ptr;char *end_ptr;if(gps == NULL) {printf("【錯誤】GPS數據指針為空\r\n");return BC20_ERR_GPS;}// 初始化GPS數據結構memset(gps, 0, sizeof(GPS_Data));gps->is_valid = 0;printf("\r\n【獲取GPS數據】\r\n");// 發送指令獲取RMC數據,超時設為5秒ret = BC20_SendAT_NOOLED("AT+QGNSSRD=\"NMEA/RMC\"\r\n", "$GNRMC", 5000);if (ret != BC20_OK) {printf("【錯誤】獲取GPS數據超時\r\n");BC20_ClearRecvBuf(); // 超時后清空return BC20_ERR_GPS;}// 提取$GNRMC語句(從緩沖區中查找)rmc_ptr = strstr(s_recv_buf, "$GNRMC");if (rmc_ptr == NULL) {printf("【錯誤】未找到GNRMC語句(緩沖區內容:%s)\r\n", s_recv_buf);BC20_ClearRecvBuf(); // 失敗后清空return BC20_ERR_GPS;}// 截斷到換行符,確保解析安全end_ptr = strchr(rmc_ptr, '\r');if (end_ptr != NULL) {*end_ptr = '\0';}printf("【解析GNRMC數據】: %s\r\n", rmc_ptr);// 解析RMC數據ret = BC20_ParseGPRMC(rmc_ptr, gps);if (ret != BC20_OK) {gps->is_valid = 0;printf("【錯誤】GNRMC數據解析失敗\r\n");}BC20_ClearRecvBuf(); // 解析完成后手動清空緩沖區return ret;
}// 連接OneNet平臺
BC20_ErrorCode BC20_ConnectOneNet(void) {BC20_ErrorCode ret;char cmd[BC20_BUF_LEN];printf("\r\n====================================\r\n");printf("【開始連接OneNet平臺】\r\n");// 斷開現有連接printf("【斷開現有連接】\r\n");BC20_SendAT("AT+QMTDISC=0\r\n", "OK", 3000);BC20_SendAT("AT+QMTCLOSE=0\r\n", "OK", 3000);// 配置MQTT版本printf("【配置MQTT版本為3.1.1】\r\n");ret = BC20_SendAT("AT+QMTCFG=\"version\",0,4\r\n", "OK", 3000);if (ret != BC20_OK) {printf("【錯誤】配置MQTT版本失敗\r\n");return BC20_ERR_OneNet;}// 連接OneNet服務器printf("【連接OneNet MQTT服務器】\r\n");snprintf(cmd, BC20_BUF_LEN, "AT+QMTOPEN=0,\"%s\",1883\r\n", OneNet_SERVER);ret = BC20_SendAT(cmd, "+QMTOPEN: 0,0", 10000);if (ret != BC20_OK) {printf("【錯誤】連接服務器失敗\r\n");return BC20_ERR_OneNet;}// 登錄設備printf("【登錄設備】\r\n");snprintf(cmd, BC20_BUF_LEN, "AT+QMTCONN=0,\"%s\",\"%s\",\"%s\"\r\n",OneNet_DEVICE_ID, OneNet_PRODUCT_ID, OneNet_TOKEN);ret = BC20_SendAT(cmd, "+QMTCONN: 0,0,0", 10000);if (ret != BC20_OK) {printf("【錯誤】設備登錄失敗\r\n");return BC20_ERR_OneNet;}// 訂閱主題printf("【訂閱屬性設置主題】\r\n");snprintf(cmd, BC20_BUF_LEN, "AT+QMTSUB=0,1,\"$sys/%s/%s/thing/property/set\",2\r\n",OneNet_PRODUCT_ID,OneNet_DEVICE_ID);ret = BC20_SendAT(cmd, "+QMTSUB: 0,1,0,0", 5000);if (ret != BC20_OK) {printf("【錯誤】訂閱主題失敗\r\n");return BC20_ERR_OneNet;}s_bc20_status.is_connected = 1;printf("【OneNet平臺連接成功】\r\n");printf("====================================\r\n");return BC20_OK;
}// 發送數據到OneNet平臺
BC20_ErrorCode BC20_SendToOneNet(float temp, float humi, const GPS_Data *gps) {BC20_ErrorCode ret;if (!s_bc20_status.is_connected) {printf("【錯誤】未連接到OneNet平臺,無法發送數據\r\n");return BC20_ERR_OneNet;}char cmd[BC20_BUF_LEN];snprintf(cmd, BC20_BUF_LEN, "AT+QMTPUB=0,0,0,0,\"$sys/%s/%s/thing/property/post\",""{\"id\":\"123\",\"version\":\"1.0\",\"params\":{""\"temp\":{\"value\":%.1f},""\"humi\":{\"value\":%.1f},""\"map\":{\"value\":{\"Lon\":\"%.6f\",\"Lat\":\"%.6f\"}}""}}\r\n",OneNet_PRODUCT_ID,OneNet_DEVICE_ID,temp, humi, gps->longitude, gps->latitude);printf("\r\n【發送數據到OneNet】\r\n");ret = BC20_SendAT_NOOLED(cmd, "OK", 5000);if (ret == BC20_OK) {printf("【數據發送成功】\r\n");} else {printf("【數據發送失敗】\r\n");}BC20_ClearRecvBuf(); // 發送完成后清空return ret;
}// 處理OneNet接收的數據
void BC20_ProcessReceivedData(void) {if (!buf_uart2.rx_flag) {return;}// 復制接收數據到處理緩沖區char recv_data[BC20_BUF_LEN];strncpy(recv_data, buf_uart2.buf, BC20_BUF_LEN-1);recv_data[BC20_BUF_LEN-1] = '\0';// 清除接收標志和緩沖區BC20_ClearRecvBuf();// 檢查是否是MQTT接收消息if (strstr(recv_data, "+QMTRECV:") == NULL) {return;}printf("\r\n【檢測到OneNet下發消息】:%s\r\n", recv_data);// 解析+QMTRECV格式char *topic_str, *payload_str;char *token = strtok(recv_data, ","); // 提取"+QMTRECV:"// 跳過客戶端ID和消息IDstrtok(NULL, ","); // 客戶端ID// 提取主題topic_str = strtok(NULL, ",");if (topic_str == NULL) {printf("【解析失敗】無主題\r\n");return;}payload_str = strtok(NULL, "\r\n");if (payload_str == NULL) {printf("【解析失敗】無消息內容\r\n");return;}// 處理payloadprintf("【消息處理】主題:%s,內容:%s\r\n", topic_str, payload_str);
}
bsp_usart.c
#include "stm32f10x.h"
#include "bsp_usart.h"
#include "stdio.h"
#include "string.h"
#include "stm32f10x_tim.h"////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
//加入以下代碼,支持printf函數,而不需要選擇use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//標準庫需要的支持函數
struct __FILE
{ int handle;
}; FILE __stdout;
//定義_sys_exit()以避免使用半主機模式
int _sys_exit(int x)
{ x = x; return 0;
}
//重定義fputc函數
int fputc(int ch, FILE *f)
{ while((USART1->SR&0X40)==0);//循環發送,直到發送完畢 USART1->DR = (u8) ch; return ch;
}
#endif#if EN_USART1UART_BUF buf_uart1; //CH340
//初始化IO 串口1
//bound:波特率
void usart1_init(u32 bound){//GPIO端口設置GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA時鐘USART_DeInit(USART1); //復位串口1//USART1_TX PA.9GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復用推挽輸出GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA9//USART1_RX PA.10GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//浮空輸入GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA10//USART 初始化設置USART_InitStructure.USART_BaudRate = bound;//一般設置為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(USART1, &USART_InitStructure); //初始化串口USART_Cmd(USART1, ENABLE); //使能串口 #if EN_USART1_RX USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//開啟相關中斷USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//開啟相關中斷USART_ClearFlag(USART1, USART_FLAG_TC);//Usart1 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中斷通道NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//搶占優先級3NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子優先級3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根據指定的參數初始化VIC寄存器、#endif}/*********************************串口1的服務函數*************************************************/
void USART1_Send_byte(char data)
{while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);USART_SendData(USART1, data);
}
/*-------------------------------------------------*/
/*函數名:串口1 發送數組 */
/*參 數:bound:波特率 */
/*返回值:無 */
/*-------------------------------------------------*/
void USART1_Send(char *Data,uint16_t Len)
{ uint16_t i;for(i=0; i<Len; i++){USART1_Send_byte(Data[i]);}
}
void USART1_SendStr(char*SendBuf)//串口1打印數據
{while(*SendBuf){while((USART1->SR&0X40)==0);//等待發送完成 USART1->DR = (u8) *SendBuf; SendBuf++;}
}/*****************************************************
清空電腦反饋的緩沖數據 串口1
*****************************************************/
void Clear_Buffer_UART1(void)//清空緩存
{buf_uart1.index=0;buf_uart1.rx_flag=0;memset(buf_uart1.buf,0,BUFLEN);
}
void UART1_receive_process_event(char ch ) //串口2給4g用
{if(buf_uart1.index >= BUFLEN){buf_uart1.index = 0 ;}else{buf_uart1.buf[buf_uart1.index++] = ch;}
}//串口1的接收中斷程序
void USART1_IRQHandler(void) //串口1中斷服務程序
{uint8_t Res;Res=Res;if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中斷,可以擴展來控制{Res=USART_ReceiveData(USART1);//接收模塊的數據;UART1_receive_process_event(Res);//接收模塊的數據} if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) //模塊空閑{Res=USART_ReceiveData(USART1);//接收模塊的數據;buf_uart1.rx_flag=1;} } #endif#if EN_USART2
UART_BUF buf_uart2; //EC200T
//初始化IO 串口2
//pclk1:PCLK1時鐘頻率(Mhz)
//bound:波特率
void usart2_init(u32 bound)
{ GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能,GPIOA時鐘RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//USART2USART_DeInit(USART2); //復位串口2//USART2_TX PA.2GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA.2GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復用推挽輸出GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA2//USART2_RX PA.3GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA3//USART 初始化設置USART_InitStructure.USART_BaudRate = bound;//115200USART_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); //初始化串口USART_Cmd(USART2, ENABLE); //使能串口 #if EN_USART2_RX USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//開啟相關中斷USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);//開啟相關中斷//Usart1 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;//串口1中斷通道NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;//搶占優先級3NVIC_InitStructure.NVIC_IRQChannelSubPriority =1; //子優先級3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根據指定的參數初始化VIC寄存器、
#endif
}void Clear_Buffer_UART2(void)//清空緩存
{buf_uart2.index=0;buf_uart2.rx_flag=0;memset(buf_uart2.buf,0,BUFLEN);
}/*********************************串口2的服務函數*************************************************/
void USART2_Send_byte(char data)
{while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);USART_SendData(USART2, data);
}/*-------------------------------------------------*/
/*函數名:串口2 發送數組 */
/*參 數:bound:波特率 */
/*返回值:無 */
/*-------------------------------------------------*/
void USART2_Send(char *Data,uint16_t Len)
{ uint16_t i;for(i=0; i<Len; i++){USART2_Send_byte(Data[i]);}
}void USART2_SendStr(char*SendBuf)//串口1打印數據
{while(*SendBuf){while((USART2->SR&0X40)==0);//等待發送完成 USART2->DR = (u8) *SendBuf; SendBuf++;}
}void usart2_receive_process_event(unsigned char ch ) //串口2給4g用
{if(buf_uart2.index >= BUFLEN){buf_uart2.index = 0 ;}else{buf_uart2.buf[buf_uart2.index++] = ch;}
}
void USART2_IRQHandler(void) //串口2接收函數
{char Res;Res=Res;if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收中斷,可以擴展來控制{Res=USART_ReceiveData(USART2);//接收模塊的數據;usart2_receive_process_event(Res);//接收模塊的數據} if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET) //模塊空閑{Res=USART_ReceiveData(USART2);//接收模塊的數據;buf_uart2.rx_flag=1;}
}#endif#if EN_USART3UART_BUF buf_uart3; //TTL
void usart3_init(u32 bound)
{//GPIO端口設置GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能,GPIOA時鐘RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);//USART3USART_DeInit(USART3); //復位串口3//USART3_TX PB10GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PB10GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復用推挽輸出GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化PA2//USART3_RX PB11GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化PB11//USART 初始化設置USART_InitStructure.USART_BaudRate = bound;//115200USART_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); //初始化串口USART_Cmd(USART3, ENABLE); //使能串口 #if EN_USART3_RX USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//開啟相關中斷USART_ITConfig(USART3, USART_IT_IDLE, ENABLE);//開啟相關中斷USART_ClearFlag(USART3, USART_FLAG_TC);//Usart3 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;//串口3中斷通道NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//搶占優先級1NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子優先級3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根據指定的參數初始化VIC寄存器、
#endif}void USART3_Send_byte(char data)
{while(USART_GetFlagStatus(USART3, USART_FLAG_TXE) == RESET);USART_SendData(USART3, data);
}/*-------------------------------------------------*/
/*函數名:串口2 發送數組 */
/*參 數:bound:波特率 */
/*返回值:無 */
/*-------------------------------------------------*/
void USART3_Send(char *Data,uint16_t Len)
{ uint16_t i;for(i=0; i<Len; i++){USART3_Send_byte(Data[i]);}
}void USART3_SendStr(char*SendBuf)//串口3打印數據
{while(*SendBuf){while((USART3->SR&0X40)==0);//等待發送完成 USART3->DR = (u8) *SendBuf; SendBuf++;}
}/*****************************************************
清空電腦反饋的緩沖數據 串口1
*****************************************************/
void Clear_Buffer_UART3(void)//清空緩存
{buf_uart3.index=0;buf_uart3.rx_flag=0;memset(buf_uart3.buf,0,BUFLEN);
}
void USART3_receive_process_event(char ch ) //串口2給4g用
{if(buf_uart3.index >= BUFLEN){buf_uart3.index = 0 ;}else{buf_uart3.buf[buf_uart3.index++] = ch;}
}
void USART3_IRQHandler(void) //串口3中斷服務程序
{char Res;Res=Res;if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) //接收中斷,可以擴展來控制{Res=USART_ReceiveData(USART3);//接收模塊的數據;USART3_receive_process_event(Res);} if(USART_GetITStatus(USART3, USART_IT_IDLE) != RESET) //模塊空閑{Res=USART_ReceiveData(USART3);//接收模塊的數據;buf_uart3.rx_flag=1;}
} #endif
oled.c
#include "oled.h"
#include "stdlib.h"
#include "oledfont.h"
#include "delay.h"
//OLED的顯存
//存放格式如下.
//[0]0 1 2 3 ... 127
//[1]0 1 2 3 ... 127
//[2]0 1 2 3 ... 127
//[3]0 1 2 3 ... 127
//[4]0 1 2 3 ... 127
//[5]0 1 2 3 ... 127
//[6]0 1 2 3 ... 127
//[7]0 1 2 3 ... 127 #if OLED_MODE==1
//向SSD1106寫入一個字節。
//dat:要寫入的數據/命令
//cmd:數據/命令標志 0,表示命令;1,表示數據;
void OLED_WR_Byte(u8 dat,u8 cmd)
{DATAOUT(dat); if(cmd)OLED_DC_Set();else OLED_DC_Clr(); OLED_CS_Clr();OLED_WR_Clr(); OLED_WR_Set();OLED_CS_Set(); OLED_DC_Set();
}
#else
//向SSD1106寫入一個字節。
//dat:要寫入的數據/命令
//cmd:數據/命令標志 0,表示命令;1,表示數據;
void OLED_WR_Byte(u8 dat,u8 cmd)
{ u8 i; if(cmd)OLED_DC_Set();else OLED_DC_Clr(); for(i=0;i<8;i++){ OLED_SCLK_Clr();if(dat&0x80)OLED_SDIN_Set();else OLED_SDIN_Clr();OLED_SCLK_Set();dat<<=1; } OLED_DC_Set();
}
#endifvoid OLED_Set_Pos(unsigned char x, unsigned char y)
{ OLED_WR_Byte(0xb0+y,OLED_CMD);OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);OLED_WR_Byte((x&0x0f)|0x01,OLED_CMD);
}
//開啟OLED顯示
void OLED_Display_On(void)
{OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令OLED_WR_Byte(0X14,OLED_CMD); //DCDC ONOLED_WR_Byte(0XAF,OLED_CMD); //DISPLAY ON
}
//關閉OLED顯示
void OLED_Display_Off(void)
{OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令OLED_WR_Byte(0X10,OLED_CMD); //DCDC OFFOLED_WR_Byte(0XAE,OLED_CMD); //DISPLAY OFF
}
//清屏函數,清完屏,整個屏幕是黑色的!和沒點亮一樣!!!
void OLED_Clear(void)
{ u8 i,n; for(i=0;i<8;i++) { OLED_WR_Byte (0xb0+i,OLED_CMD); //設置頁地址(0~7)OLED_WR_Byte (0x00,OLED_CMD); //設置顯示位置—列低地址OLED_WR_Byte (0x10,OLED_CMD); //設置顯示位置—列高地址 for(n=0;n<128;n++)OLED_WR_Byte(0,OLED_DATA); } //更新顯示
}//在指定位置顯示一個字符,包括部分字符
//x:0~127
//y:0~63
//mode:0,反白顯示;1,正常顯示
//size:選擇字體 16/12
void OLED_ShowChar(u8 x,u8 y,u8 chr)
{ unsigned char c=0,i=0; c=chr-' ';//得到偏移后的值 if(x>Max_Column-1){x=0;y=y+2;}if(SIZE ==16){OLED_Set_Pos(x,y); for(i=0;i<8;i++)OLED_WR_Byte(F8X16[c*16+i],OLED_DATA);OLED_Set_Pos(x,y+1);for(i=0;i<8;i++)OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA);}else { OLED_Set_Pos(x,y+1);for(i=0;i<6;i++)OLED_WR_Byte(F6x8[c][i],OLED_DATA);}
}
//m^n函數
u32 oled_pow(u8 m,u8 n)
{u32 result=1; while(n--)result*=m; return result;
}
//顯示2個數字
//x,y :起點坐標
//len :數字的位數
//size:字體大小
//mode:模式 0,填充模式;1,疊加模式
//num:數值(0~4294967295);
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size)
{ u8 t,temp;u8 enshow=0; for(t=0;t<len;t++){temp=(num/oled_pow(10,len-t-1))%10;if(enshow==0&&t<(len-1)){if(temp==0){OLED_ShowChar(x+(size/2)*t,y,' ');continue;}else enshow=1; }OLED_ShowChar(x+(size/2)*t,y,temp+'0'); }
} //顯示漢字
static void OLED_ShowCHinese(uint8_t x,uint8_t y,char *str)
{ uint16_t k;uint16_t HZnum;HZnum=sizeof(tfont16)/sizeof(typFNT_GB16); //自動統計漢字數目for (k=0;k<HZnum;k++) {if ((tfont16[k].Index[0]==*(str))&&(tfont16[k].Index[1]==*(str+1))){ uint8_t t,adder=0;OLED_Set_Pos(x,y); for(t=0;t<16;t++){OLED_WR_Byte(tfont16[k].Msk[t],OLED_DATA);adder+=1;} OLED_Set_Pos(x,y+1); for(t=0;t<16;t++){ OLED_WR_Byte(tfont16[k].Msk[t+16],OLED_DATA);adder+=1;}} continue; //查找到對應點陣字庫立即退出,防止多個漢字重復取模帶來影響}}//顯示一個字符號串(中文也可以)
void OLED_ShowString(uint8_t x,uint8_t y,char *chr)
{unsigned char j=0; uint8_t bHz=0; //字符或者中文 while(chr[j]!=0)//數據未結束{ if(!bHz){if(chr[j]>0x80)bHz=1;//中文 else //字符{OLED_ShowChar(x,y,chr[j]);x+=8;if(x>120){x=0;y+=2;}j++;}}else//中文 {bHz = 0;OLED_ShowCHinese(x,y,&chr[j]);x+=16;if(x>114){x=0;y+=2;}j+=2;}}
}/***********功能描述:顯示顯示BMP圖片128×64起始點坐標(x,y),x的范圍0~127,y為頁的范圍0~7*****************/
void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[])
{ unsigned int j=0;unsigned char x,y;if(y1%8==0) y=y1/8; else y=y1/8+1;for(y=y0;y<y1;y++){OLED_Set_Pos(x0,y);for(x=x0;x<x1;x++){ OLED_WR_Byte(BMP[j++],OLED_DATA); }}
} //初始化SSD1306
void OLED_Init(void)
{ GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能A端口時鐘GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽輸出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHzGPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOD3,6GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_7); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能A端口時鐘GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽輸出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHzGPIO_Init(GPIOB, &GPIO_InitStructure); //初始化GPIOD3,6GPIO_SetBits(GPIOB,GPIO_Pin_0|GPIO_Pin_1); OLED_RST_Set();delay_ms(100);OLED_RST_Clr();delay_ms(200);OLED_RST_Set(); OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panelOLED_WR_Byte(0x00,OLED_CMD);//---set low column addressOLED_WR_Byte(0x10,OLED_CMD);//---set high column addressOLED_WR_Byte(0x40,OLED_CMD);//--set start line address Set Mapping RAM Display Start Line (0x00~0x3F)OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control registerOLED_WR_Byte(0xCF,OLED_CMD); // Set SEG Output Current BrightnessOLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping 0xa0左右反置 0xa1正常OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction 0xc0上下反置 0xc8正常OLED_WR_Byte(0xA6,OLED_CMD);//--set normal displayOLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 dutyOLED_WR_Byte(0xD3,OLED_CMD);//-set display offset Shift Mapping RAM Counter (0x00~0x3F)OLED_WR_Byte(0x00,OLED_CMD);//-not offsetOLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequencyOLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/SecOLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge periodOLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 ClockOLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configurationOLED_WR_Byte(0x12,OLED_CMD);OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomhOLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect LevelOLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)OLED_WR_Byte(0x02,OLED_CMD);//OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disableOLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disableOLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7) OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panelOLED_WR_Byte(0xAF,OLED_CMD); /*display ON*/ OLED_Clear();OLED_Set_Pos(0,0);
}
效果--串口調試信息
在左側導航欄中依次選擇?「工具」→「WebSocket」,進入 WebSocket 交互頁面。
輸入目標服務器的 IP 地址,填寫 WebSocket 服務端口,輸入需要監聽的主題,完成后點擊「訂閱」按鈕,建立主題監聽。輸入數據上報的目標主題,用于后續發送消息。
在[訂閱消息列表]中可實時查看上報的數據內容。
平臺數據下發操作
在消息輸入框中,輸入需要下發的指令內容,確認發布主題為設備訂閱的目標主題,點擊[發送]按鈕,平臺將通過WebSocket向設備推送指令。
在串口調試助手中,可實時監控設備接收數據的狀態:
STM32F103C8T6+BC20+DHT11+Emqx代碼鏈接: https://pan.baidu.com/s/1gxcBwb2ZM6VUQ8sBIEOlEw 提取碼: 7n73?