当前位置: 首页 > news >正文

STM32F103C8T6 BC20模块NBIOT GPS北斗模块采集温湿度和经纬度发送到EMQX

云平台配置

访问下载页面:免费试用 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 

http://www.lryc.cn/news/610018.html

相关文章:

  • 攻防世界-easyphp-lever1
  • k8s常见问题
  • 【ECCV2024】AdaCLIP:基于混合可学习提示适配 CLIP 的零样本异常检测
  • Design Compiler:高层次优化与数据通路优化
  • 【Spring Boot 快速入门】六、配置文件
  • Java 发送 HTTP POST请求教程
  • Scikit-learn - 机器学习库初步了解
  • MoonBit Pearls Vol.04:用MoonBit 探索协同式编程
  • Spring IoC容器与Bean管理
  • GPTs——定制的小型智能体
  • 白杨SEO:百度搜索开放平台发布AI计划是什么?MCP网站红利来了?顺带说说其它
  • [Oracle] || 连接运算符
  • 关于如何自定义vscode(wsl连接linux)终端路径文件夹文件名字颜色的步骤:
  • 【PHP】获取图片的主要颜色值RGB值
  • 【Django】-3- 处理HTTP响应
  • Django 性能优化详解:从数据库到缓存,打造高效 Web 应用
  • CNN卷积神经网络之MobileNet和ResNet(五)
  • AWS Lambda Function 全解:无服务器计算
  • CAD格式转换器HOOPS Exchange:全方位支持HOOPS系列产品
  • Webpack 搭建 Vue3 脚手架详细步骤
  • Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现人脸面部表情的追踪识别(C#代码UI界面版)
  • [3D数据存储] Archive (File Container) | 创建/写入/读取 | 存储格式HDF5
  • pyqt5-tools/pyqt6-tools 安装失败,解决办法
  • app-1
  • Spring P1 | 创建你的第一个Spring MVC项目(IDEA图文详解版,社区版专业版都有~)
  • 理解 Agent 的基本概念与功能
  • 正点原子STM32MP257开发板移植ubuntu24.04根文件系统(带桌面版)
  • RTSP/RTMP播放器超低延迟实战:无人机远控视觉链路的工程实践
  • [特殊字符]️ 整个键盘控制无人机系统框架
  • 链表与数组面试常见问题详解与实现