在上一篇文章中已经介绍怎么通过串口调试助手与BC28模块直接通信,从而与云平台进行通信。但是要实现实时上报单片机采集到的那就要把BC28模块的开关拨到“To MCU”。在板子的内部,串口3是直接与BC28模块通信的,所以MCU可以通过串口3给BC28模块发送数据,从而与云平台进行通信。
首先就是配置,RCC、SYS、时钟树正常配置,其次使能串口1和串口3,串口1是用来与串口调试助手通信,方便调试打印代码,串口3是MCU用来发送数据给BC28模块,与云平台进行通信的,其中串口3使能中断,采用中断的方式收发数据,调节串口3的波特率为9600。
配置好之后,ctrl+生成代码,首先先添加printf重定向代码,也可以参考这篇文章CSDN。
usart.c文件添加重定向代码
/* USER CODE BEGIN 1 */
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch,FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch,1,0xffff);
return ch;
}
/* USER CODE END 1 */
usart.h添加stdio.h
#include <stdio.h>
以上就完成了printf重定向,可以在main.c中添加测试代码,在串口调试助手上查看是否有输出来验证printf输出功能是否正常。
然后添加nbiot.h文件和nbiot.c文件,在下面两个文件夹下分别建立.h和.c文件。
编写nbiot.h文件代码
#ifndef INC_NBIOT_H_
#define INC_NBIOT_H_
#include <stdio.h>
#include "usart.h"
#include <string.h>
extern char g_uart3_rxbuf[100];
extern uint16_t g_uart3_bytes;
#define nbiot_huart &huart3 /*NBIOT模块使用的串口*/
#define g_nbiot_rxbuf g_uart3_rxbuf /*NBIOT模块的接收buffer*/
#define g_nbiot_rxbytes g_uart3_bytes /*NBIOT模块接收的数据大小*/
/*清楚NBIOT模块接收buffer里的数据内容宏,用宏不用函数是因为函数调用需要额外时间开销*/
#define clear_atcmd_buf() do {memset(g_nbiot_rxbuf,0,sizeof(g_nbiot_rxbuf));\
g_nbiot_rxbytes=0;} while(0)
/*NBIOT模块发送AT命令函数。返回值0表示成功,!0表示失败*/
#define EXPECT_OK "OK\r\n"
extern int send_atcmd(char *atcmd,char *expect_reply,unsigned int timeout);
/*NBIOT模块初始化函数。返回值0表示成功,!0表示失败*/
extern int nbiot_module_reset(void);
/*NBIOT连接天翼云*/
extern int setup_nbiot();
#endif /*INC_BC28_H_*/
编写nbiot.c文件代码,封装了发送AT命令的函数send_atcmd()以及连接物联网云平台的函数setup_nbiot()
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "usart.h"
#include "nbiot.h"
#include <unistd.h>
/*nbiot模块驱动调试宏,注释下面两个宏定义可以取消调试打印*/
//#define CONFIG_NBIOT_DEBUG
#define CONFIG_NBIOT_PRINT
#ifdef CONFIG_NBIOT_DEBUG
#define nbiot_dbg(format,args...) printf(format,##args)
#else
#define nbiot_dbg(format,args...) do{} while(0)
#endif
#ifdef CONFIG_NBIOT_PRINT
#define nbiot_printf(format,args...) do{} while(0)
#endif
char g_uart3_rxbuf[100];
uint16_t g_uart3_bytes;
int send_atcmd(char *atcmd,char *expect_reply,unsigned int timeout)
{
int rv = 1;
unsigned int i;
char *expect;
/*check function input arguments validation,检查函数输入参数验证*/
if( !atcmd || strlen(atcmd)<=0 ) //如果atcmd为空或者长度小于等于0,条件为真
{
nbiot_printf("ERROR:Invalid input arguments\r\n");
return -1;
}
nbiot_dbg("\r\nStart send AT command: %s",atcmd);
clear_atcmd_buf();
HAL_UART_Transmit(nbiot_huart,(uint8_t *)atcmd,strlen(atcmd),1000);
expect = expect_reply ? expect_reply:"OK\r\n";//如果 expect_reply 为真,则将 expect 赋值为 expect_reply 的值;如果 expect_reply 为假,则将 expect 赋值为字符串字面量 "OK\r\n"。
/*Receive AT reply string by UART interrupt handler,stop by "OK/ERRROR" or timeout,通过UART中断处理程序接收AT应答字符串,通过“OK/ error”或超时停止*/
for(i=0;i<timeout;i++)
{
if(strstr(g_nbiot_rxbuf,expect))
{
nbiot_dbg("AT command Got expect reply '%s'\r\n",expect);
rv = 0;
goto CleanUp;
}
if (strstr(g_nbiot_rxbuf,"ERROR\r\n")||strstr(g_nbiot_rxbuf,"FAIL\r\n"))
{
rv = 2;
goto CleanUp;
}
HAL_Delay(1);
}
CleanUp:
nbiot_dbg("<<<< AT command reply:\r\n%s",g_nbiot_rxbuf);
return rv;
}
/*初始化BC28模块*/
int bc28_module_init(void)
{
int i;
/*开始重置BC28模块*/
nbiot_printf("INFO:Reset BC28 module now...\r\n");
/*调用send_atcmd()函数发送AT指令"AT+RST\r\n",期望接收到"OK"作为响应,并设置超时时间为500毫秒。该指令用于重置BC28模块*/
send_atcmd("AT+RST\r\n",EXPECT_OK,500);
/*使用一个循环,重复6次发送AT指令"AT\r\n",期望接收到"OK"作为响应,以确认BC28模块是否已经重启完成。*/
for(i=0;i<6;i++)
{
if(!send_atcmd("AT\r\n",EXPECT_OK,500))
{/*如果发送AT指令并成功接收到"OK"作为响应,则打印信息"/INFO:Send AT to BC28 and got reply OK",表示成功发送AT指令并接收到了正确的响应。*/
nbiot_printf("/INFO:Send AT to BC28 and got reply OK\r\n");
break;
}
HAL_Delay(100);
}
if(i>=6)
{/*如果循环6次后仍未成功接收到"OK"作为响应,则打印错误信息"ERROR:Can't receive AT replay after reset",表示无法在重置后接收到BC28模块的响应,返回值为-2。*/
nbiot_printf("ERROR:Can't receive AT replay after reset\r\n");
return -2;
}
/*调用send_atcmd()函数发送AT指令"AT+CWMODE=1\r\n",期望接收到"OK"作为响应,并设置超时时间为500毫秒。该指令用于设置BC28模块为Station模式。*/
if(send_atcmd("AT+CWMODE=1\r\n",EXPECT_OK,500))
{/*如果发送AT指令并未成功接收到"OK"作为响应,则打印错误信息"ERROR:Set BC28 word as Station mode failure",表示设置BC28模块的模式失败,返回值为-3。*/
nbiot_printf("ERROR:Set BC28 word as Station mode failure\r\n");
return -3;
}
/*调用send_atcmd()函数发送AT指令"AT+CWMODE=1,1\r\n",期望接收到"OK"作为响应,并设置超时时间为500毫秒。该指令用于启用BC28模块的Station模式的DHCP功能。*/
if(send_atcmd("AT+CWMODE=1,1\r\n",EXPECT_OK,500))
{/*如果发送AT指令并未成功接收到"OK"作为响应,则打印错误信息"ERROR:Enable BC28 Station mode DHCP failure",表示启用BC28模块的DHCP功能失败,返回值为-4。*/
nbiot_printf("ERROR:Enable BC28 Station mode DHCP failure\r\n");
return -4;
}
#if 0 /*使用#if 0注释掉的代码块,发送AT指令"AT+GMR\r\n",期望接收到"OK"作为响应,并设置超时时间为500毫秒。该指令用于检查BC28模块的版本号。*/
if(send_atcmd("AT+GMR\r\n",EXPECT_OK,500))
{
nbiot_print("ERROR:AT+GMR check BC28 revesion failure\r\n");
return -5;
}
#endif/*返回值为0,表示BC28模块初始化成功。*/
HAL_Delay(500);
return 0;
}
// 设置NB-IoT上电后自动附着网络
int setAutoConnect()
{
char response[256];
if (send_atcmd("AT+NCONFIG=AUTOCONNECT,TRUE", response, sizeof(response)) == 0)
{
if (strncmp(response, "OK", 2) == 0)
{
return 0;
}
}
return -1;
}
// 设置IoT平台服务器地址和端口为电信云平台
int setServerAddress(const char* address, int port)
{
char command[64];
sprintf(command, "AT+NCDP=%s,%d", address, port);
char response[256];
if (send_atcmd(command, response, sizeof(response)) == 0)
{
if (strncmp(response, "OK", 2) == 0)
{
return 0;
}
}
return -1;
}
//设置附着网络后自动触发电信IoT云平台服务器连接
int setAutoConnectServer()
{
char response[256];
if (send_atcmd("AT+QREGSWT=1", response, sizeof(response)) == 0)
{
if (strncmp(response, "OK", 2) == 0)
{
return 0;
}
}
return -1;
}
// 重启NB-IoT模块
int rebootModule()
{
char response[256];
if (send_atcmd("AT+NRB", response, sizeof(response)) == 0)
{
if (strncmp(response, "REBOOTING", 9) == 0)
{
return 0;
}
}
return -1;
}
// 查看NB-IoT模块后,是否能正常附着网络并获取到IP地址
int checkNetworkStatus()
{
char response[256];/*声明一个字符数组response,用于存储AT指令的响应结果*/
/*用send_atcmd()函数发送AT指令AT+CGPADDR,并将响应结果存储在response数组中*/
if (send_atcmd("AT+CGPADDR", response, sizeof(response)) == 0)/*如果send_atcmd()函数返回值为0,表示AT指令发送成功并接收到了响应*/
{
char* ipStart = strstr(response, ",");/*在响应结果response中查找字符串",",并将其后的字符地址赋给指针ipStart*/
if (ipStart != NULL)/*如果ipStart指针不为空,则表示找到了IP地址的起始位置*/
{
ipStart++;/*将ipStart指针向后移动一个位置,指向具体的IP地址字符串*/
printf("IP Address: %s\n", ipStart);/*使用printf()函数打印出IP地址*/
return 0;/*返回值0,表示网络状态正常,IP地址获取成功*/
}
}
return -1;/*如果send_atcmd()函数返回值不为0,或者未找到IP地址的起始位置,则返回值为-1,表示网络状态异常,IP地址获取失败*/
}
// 查看IoT云平台的配置是否正确
int checkServerConfig()
{
char response[256];/*声明一个字符数组response,用于存储AT指令的响应结果*/
/*调用send_atcmd()函数发送AT指令AT+NCDP?,并将响应结果存储在response数组中*/
if (send_atcmd("AT+NCDP?", response, sizeof(response)) == 0)/*如果send_atcmd()函数返回值为0,表示AT指令发送成功并接收到了响应*/
{
char* addressStart = strstr(response, ":");/*在响应结果response中查找字符串":",并将其地址赋给指针addressStart*/
char* portStart = strstr(response, ",");/*在响应结果response中查找字符串",",并将其地址赋给指针portStart*/
if (addressStart != NULL && portStart != NULL)/*如果addressStart和portStart指针都不为空,则表示找到了服务器地址和端口号的起始位置*/
{
addressStart++;/*将addressStart指针向后移动一个位置,指向具体的服务器地址字符串*/
*portStart = '\0';/*将portStart指针所指向的位置设置为空字符'\0',以截断字符串,同时将portStart指针移动到端口号的起始位置*/
portStart++;
printf("Server Address: %s\n", addressStart);/*使用printf()函数打印出服务器地址和端口号*/
printf("Server Port: %s\n", portStart);
return 0;/*返回值0,表示服务器配置正常,服务器地址和端口号获取成功*/
}
}
return -1;/*如果send_atcmd()函数返回值不为0,或者未找到服务器地址或端口号的起始位置,则返回值为-1,表示服务器配置异常,服务器地址和端口号获取失败。*/
}
// 查询NB-IoT是否成功注册上电信IoT云平台
int checkRegistrationStatus()
{
char response[256];
if (send_atcmd("AT+NMSTATUS?", response, sizeof(response)) == 0)
{
char* statusStart = strstr(response, ":");
if (statusStart != NULL)
{
statusStart++;
printf("Registration Status: %s\n", statusStart);
return 0;
}
}
return -1;
}
//NBIOT连接天翼云
int setup_nbiot()
{
const char* address;
int port;
setAutoConnect();// 设置NB-IoT上电后自动附着网络
setServerAddress(address,port); // 设置IoT平台服务器地址和端口为电信云平台
setAutoConnectServer();//设置附着网络后自动触发电信IoT云平台服务器连接
rebootModule();// 重启NB-IoT模块
checkNetworkStatus();// 查看NB-IoT模块后,是否能正常附着网络并获取到IP地址
checkServerConfig();// 查看IoT云平台的配置是否正确
checkRegistrationStatus();// 查询NB-IoT是否成功注册上电信IoT云平台
return 0;
}
在main.c文件添加nbiot.h文件和nbiot.c文件
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include "nbiot.h"
/* USER CODE END Includes */
然后添加打开BC28模块射频代码,以及声明和调用连接云平台的函数setup_nbiot();
/*main()函数外声明*/
extern int setup_nbiot();//NBIOT联网
/*while()外面进行*/
send_atcmd("AT+CFUN=1\r\n",EXPECT_OK,500);//打开射频
setup_nbiot();//NBIOT联网
发送测试数据
/*while里面进行*/
send_atcmd("AT+QLWULDATAEX=9,02003000040000001C,0x0100\r\n",EXPECT_OK,500);
HAL_Delay(1000);
在云平台上查看是否收到发送的测试数据,如果收到则证明联网成功。