STM32F103C8T6 BC20模块采集温湿度和经纬度发送到ONENET
- 云平台配置
第一步:配置OneNet平台
OneNET - 中国移动物联网开放平台
登入个人账号后,点击进入开发者中心,进入管理后台。
在页面左侧导航栏中,依次选择[产品开发]→[创建产品],进入产品创建页面。
在产品创建页面配置产品基本信息
产品品类:根据实际需求在下拉菜单中选择(如智能家居等)。
智能化方式选择:
1.设备接入(自定义开发)
适用于已有硬件设备需接入平台的场景,可自主开发设备通信协议。
2.产品智能化(OneNet平台方案)
可直接使用OneNet自带的[中移和物]APP实现设备管理与控制,无需额外开发客户端。
在产品列表找到已创建产品,点击对应产品开发按钮,进入产品开发详情页。
在产品开发详情页,设置物模型。
可选自定义功能,按需填写功能名称、数据类型、标识符等信息。
在此处,我们新增三组数据:一组用于传输温度信息,一组用于传输湿度信息,另一组用于传输经纬度信息。
在页面左侧导航栏,找到设备接入管理分类下的设备管理选项并点击,随后在对应功能区,点击添加设备。
经上述步骤,OneNet平台相关配置已全部完成。
设备接线
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) {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;
}// 发送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);
}
效果--串口调试信息
数据上报成功后,可通过OneNet平台实时查看数据:
在平台控制台找到已创建设备,点击进入设备详情页,切换至属性标签页,即可直观查看设备成功上报的实时数据
下发控制操作:
在 OneNet 平台控制台找到已创建设备,点击进入设备详情页。
切换至设备调试标签页,进入数据下发调试界面。在调试界面中找到[属性设置]模块。
点击[启动调试]按钮,激活数据输入框。
Token获取:
Token软件及获取Token密钥方式链接: https://pan.baidu.com/s/1FMKOybgvJ_rniJfiSsCUIg?pwd=yths 提取码: yths
代码湖获取:STM32F103C8T6+BC20+DHT11+OneNet链接: https://pan.baidu.com/s/1i9Wutoc6mSzixiLa1MG5MQ 提取码: b5yq