云平台配置
访问下载页面:免费试用 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) {c
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(); // 手动清空
// 初始化GPS
printf("【步骤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, ","); // $GNRMC
if (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/100
int deg = (int)lat_deg;
gps->latitude = deg + (lat_deg - deg) * 100 / 60;
token = strtok(NULL, ","); // N/S
if (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/100
deg = (int)lon_deg;
gps->longitude = deg + (lon_deg - deg) * 100 / 60;
token = strtok(NULL, ","); // E/W
if (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和消息ID
strtok(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;
}
// 处理payload
printf("【消息处理】主题:%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_USART1
UART_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.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA9
//USART1_RX PA.10
GPIO_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;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3
NVIC_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);//USART2
USART_DeInit(USART2); //复位串口2
//USART2_TX PA.2
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA.2
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA2
//USART2_RX PA.3
GPIO_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;//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(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;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =1; //子优先级3
NVIC_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_USART3
UART_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);//USART3
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); //初始化PA2
//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 初始化设置
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(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;//抢占优先级1
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3
NVIC_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();
}
#endif
void 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 ON
OLED_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 OFF
OLED_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;//速度50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOD3,6
GPIO_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;//速度50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化GPIOD3,6
GPIO_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 panel
OLED_WR_Byte(0x00,OLED_CMD);//---set low column address
OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
OLED_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 register
OLED_WR_Byte(0xCF,OLED_CMD); // Set SEG Output Current Brightness
OLED_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 display
OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty
OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset Shift Mapping RAM Counter (0x00~0x3F)
OLED_WR_Byte(0x00,OLED_CMD);//-not offset
OLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
OLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
OLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge period
OLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
OLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration
OLED_WR_Byte(0x12,OLED_CMD);
OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomh
OLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level
OLED_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/disable
OLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disable
OLED_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 panel
OLED_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