一、模块来源
模块实物展示:
资料链接:https://docs.ai-thinker.com/nb-iot
二、规格参数
工作电压:3.0V-3.6V
工作电流:IMAX = 170mA
模块尺寸:14.4 x 24.7 MM
控制方式:串口
三、移植过程
我们的目标是在立创·CW32F030C8T6开发板上能够完成无线传输的功能。首先要获取资料,查看数据手册应如何实现,再移植至我们的工程。
3.1查看资料
NBIOT模块使用网络是通过物联网卡提供流量,物联网卡请自行到淘宝购买。
说明
使用GPS定位时,不可在室内,室内很难定位。需要到室外进行定位。
其余请参考官方资料:https://docs.ai-thinker.com/nb-iot
3.2引脚选择
使用串口2(串口2-TX=PA2,串口2-RX=PA3)
模块接线图
3.3移植至工程
移植步骤中的导入.c和.h文件与【CW32模块使用】DHT11温湿度传感器相同,只是将.c和.h文件更改为bsp_ec01g.c与bsp_ec01g.h。这里不再过多讲述,移植完成后面修改相关代码。
在文件bsp_ec01g.c中,编写如下代码。
/* * Change Logs: * Date Author Notes * 2024-06-24 LCKFB-LP first version */ #include "bsp_ec01g.h" #include "stdio.h" #include "string.h" #include "stdlib.h" unsigned char EC01G_RX_BUFF[EC01G_RX_LEN_MAX]; unsigned char EC01G_RX_FLAG = 0; unsigned int EC01G_RX_LEN = 0; unsigned char EC01G_RX_API_FLAG = 0; /* ATrn //测试设备是否存在 返回OK说明存在 AT+CFUN=1 //关闭飞行模式 返回OK 说明成功 AT+CEREG? //判断是否附着网络成功 返回+CEREG: 0,1 说明附着网络成功 AT+HTTPCREATE=0,"http://116.62.81.138:80" //设置命令创建一个 http 或 https 客户端实例 返回OK成功 AT+HTTPCON=0 //连接0号实例服务器 返回OK成功 AT+HTTPSEND=0,0,78,"/v3/weather/now.json?key=SIwiiHEWo6pPD42S5&location=beijing&language=en&unit=c" //向0号实例发送GET命令,地址长度为78 AT+HTTPDESTROY=0 //断开连接 当出现 +HTTPERR: 0,11 时,需要断开连接,再重新连接实例服务器 将API返回的数据,+HTTPRESPC: 后面的7B227。。。转为字符串形式 +HTTPRESPH: 0,200,396,Date: Fri, 02 Jun 2023 03:35:05 GMT Content-Type: application/json; charset=utf-8 Content-Length: 260 Connection: keep-alive access-control-allow-origin: * etag: W/"104-BAVuIqnZSk8TcsNHuRGYESoLzao" x-powered-by: Express x-ratelimit-limit: 20;w=60 x-ratelimit-remaining: 19 x-ratelimit-reset: 60.000 x-request-id: F2S6NvFKYBEUQ0zTbqlB x-tenant-id: dbef859d-3503-45ff-ba2f-3e5f0f4de75d +HTTPRESPC: 0,0,260,520,7B22726573756C7473223A5B7B226C6F636174696F6E223A7B226964223A2257583446425858464B45344622 2C226E616D65223A224265696A696E67222C22636F756E747279223A22434E222C2270617468223A224265696A696E672C4265696A696E67 2C4368696E61222C2274696D657A6F6E65223A22417369612F5368616E67686169222C2274696D657A6F6E655F6F6666736574223A222B30 383A3030227D2C226E6F77223A7B2274657874223A2253756E6E79222C22636F6465223A2230222C2274656D7065726174757265223A2232 38227D2C226C6173745F757064617465223A22323032332D30362D30325431313A31343A33382B30383A3030227D5D7D */ /************************************************************ * 函数名称:EC01G_USART_Init * 函数说明:连接EC01G的初始化 * 型 参:bund=串口波特率 * 返 回 值:无 * 备 注:无 *************************************************************/ void EC01G_USART_Init(unsigned int bund) { GPIO_InitTypeDef GPIO_InitStruct; // GPIO初始化结构体 RCC_EC01G_GPIO_ENABLE(); // 使能GPIO时钟 RCC_EC01G_USART_ENABLE(); // 使能UART时钟 GPIO_InitStruct.Pins = GPIO_EC01G_TX; // GPIO引脚 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出 GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; // 输出速度高 GPIO_Init(PORT_EC01G_GPIO, &GPIO_InitStruct); // 初始化 GPIO_InitStruct.Pins = GPIO_EC01G_RX; // GPIO引脚 GPIO_InitStruct.Mode = GPIO_MODE_INPUT_PULLUP; // 上拉输入 GPIO_Init(PORT_EC01G_GPIO, &GPIO_InitStruct); // 初始化 BSP_GPS_AF_UART_TX(); // UART_TX复用 BSP_GPS_AF_UART_RX(); // UART_RX复用 // 配置UART USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate = bund; // 波特率 USART_InitStructure.USART_Over = USART_Over_8; // 配置USART的过采样率。 USART_InitStructure.USART_Source = USART_Source_PCLK; // 设置时钟源 USART_InitStructure.USART_UclkFreq = 64000000; //设置USART时钟频率(和主频一致即可) USART_InitStructure.USART_StartBit = USART_StartBit_FE; //RXD下降沿开始 USART_InitStructure.USART_StopBits = USART_StopBits_1; // 停止位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(EC01G_USART, &USART_InitStructure); // 初始化串口2 // 优先级,无优先级分组 NVIC_SetPriority(EC01G_USART_IRQ, 0); // UARTx中断使能 NVIC_EnableIRQ(EC01G_USART_IRQ); // 使能UARTx RC中断 USART_ITConfig(EC01G_USART, USART_IT_RC, ENABLE); } /****************************************************************** * 函 数 名 称:EC01G_USART_Send_Bit * 函 数 说 明:向EC01G模块发送单个字符 * 函 数 形 参:ch=字符 * 函 数 返 回:无 * 作 者:LC * 备 注:无 ******************************************************************/ void EC01G_USART_Send_Bit(unsigned char ch) { // 发送一个字节 USART_SendData_8bit(EC01G_USART, (uint8_t)ch); // 等待发送完成 while( RESET == USART_GetFlagStatus(EC01G_USART, USART_FLAG_TXE) ){} } /****************************************************************** * 函 数 名 称:EC01G_USART_send_String * 函 数 说 明:向EC01G模块发送字符串 * 函 数 形 参:str=发送的字符串 * 函 数 返 回:无 * 作 者:LC * 备 注:无 ******************************************************************/ void EC01G_USART_send_String(unsigned char *str) { while( str && *str ) // 地址为空或者值为空跳出 { EC01G_USART_Send_Bit(*str++); } } //清除串口接收的数据 /****************************************************************** * 函 数 名 称:Clear_EC01G_RX_BUFF * 函 数 说 明:清除EC01G发过来的数据 * 函 数 形 参:无 * 函 数 返 回:无 * 作 者:LC * 备 注:无 ******************************************************************/ void Clear_EC01G_RX_BUFF(void) { unsigned int i = EC01G_RX_LEN_MAX-1; while(i) { EC01G_RX_BUFF[i--] = 0; } EC01G_RX_LEN = 0; EC01G_RX_FLAG = 0; } /****************************************************************** * 函 数 名 称:EC01G_Send_Cmd * 函 数 说 明:向EC01G模块发送指令,并查看EC01G模块是否返回想要的数据 * 函 数 形 参:cmd=发送的AT指令 ack=想要的应答 waitms=等待应答的时间 cnt=等待应答多少次 * 函 数 返 回:1=得到了想要的应答 0=没有得到想要的应答 * 作 者:LC * 备 注:无 ******************************************************************/ char EC01G_Send_Cmd(char *cmd,char *ack,unsigned int waitms,unsigned char cnt) { Clear_EC01G_RX_BUFF(); //清除接收的数据 EC01G_USART_send_String((unsigned char*)cmd);//发送AT指令 while(cnt--) { //串口中断接收EC01G应答 if( EC01G_RX_FLAG == 1 ) { //查找是否有想要的数据 if( strstr((char*)EC01G_RX_BUFF, ack) != NULL ) { return 1; } } //时间间隔 delay_ms(waitms); } return 0; } /****************************************************************** * 函 数 名 称:EC01G_Init * 函 数 说 明:EC01G模块初始化 * 函 数 形 参:无 * 函 数 返 回:无 * 作 者:LC * 备 注:ESP01S的默认波特率是9600 ******************************************************************/ void EC01G_Init(void) { EC01G_USART_Init(9600);//默认波特率为9600 } /****************************************************************** * 函 数 名 称:EC01G_Seniverse_Init * 函 数 说 明:配置EC01G模块连接心知天气平台 * 函 数 形 参:无 * 函 数 返 回:1=没有检测到设备 * 作 者:LC * 备 注: ATrn //测试设备是否存在 返回OK说明存在 AT+CFUN=1 //关闭飞行模式 返回OK 说明成功 AT+CEREG? //判断是否附着网络成功 返回+CEREG: 0,1 说明附着网络成功 AT+HTTPCREATE=0,"http: //116.62.81.138:80" //设置命令创建一个 http 或 https 客户端实例 返回OK成功 AT+HTTPCON=0 //连接0号实例服务器 返回OK成功 AT+HTTPSEND=0,0,78,"/v3/weather/now.json?key=SIwiiHEWo6pPD42S5&location=beijing&language=en&unit=c" //向0号实例发送GET命令,地址长度为78 AT+HTTPDESTROY=0 //断开连接 当出现 +HTTPERR: 0,11 时,需要断开连接,再重新连接实例服务器 ******************************************************************/ uint8_t EC01G_Seniverse_Init(void) { uint8_t ret = 0; uint8_t i = 0; // uint8_t send_buff[250]={0}; //测试设备是否存在 if( EC01G_Send_Cmd("ATrn", "OK", 1000,3) == 0 ) { return 1; } //关闭飞行模式 if( EC01G_Send_Cmd("AT+CFUN=1rn", "OK", 1000,3) == 0 ) { return 2; } //判断是否附着网络成功 if( EC01G_Send_Cmd("AT+CEREG?rn", "+CEREG: 0,1", 5000,3) == 0 ) { ret = EC01G_Send_Cmd("AT+CEREG?rn", "+CEREG: 0,1", 1000,3); if( ret == 0 ) { return 1; } } again: //如果之前有连接过,则断开连接 EC01G_Send_Cmd("AT+HTTPDESTROY=0rn", "OK", 1000,10); if( EC01G_Send_Cmd("AT+ECDNS= api.seniverse.comrn", "error", 1000,3) == 1 ) { printf("当前域名解析服务器无法工作:%drn",__LINE__); } //创建一个 http客户端实例 while( EC01G_Send_Cmd("AT+HTTPCREATE=0,"http://116.62.81.138:80"rn", "OK", 5000,3) == 0 ) { #if DEBUG printf("AT+HTTPCREATE FAILrn"); #endif i++; if( i >= 3 ) return 2; goto again; } i=0; //连接0号实例服务器 (默认0号服务器) while( EC01G_Send_Cmd("AT+HTTPCON=0rn", "OK", 5000,3) == 0 ) { #if DEBUG printf("AT+HTTPCON FAILrn"); #endif i++; if( i >= 3 ) return 3; goto again; } // //发送获取天气数据API // sprintf((char*)send_buff, "AT+HTTPSEND=0,0,%d,%srn", strlen(WEATHER_FACTS_API)-2, WEATHER_FACTS_API); // if( EC01G_Send_Cmd((char*)send_buff, "OK", 1000,3) == 0 ) // { // #if DEBUG // printf("AT+HTTPSEND FAILrn"); // // #endif // return 4; // } return 0; } /****************************************************************** * 函 数 名 称:Hex_To_Text * 函 数 说 明:将16进制字符串转为字符型字符串 * 函 数 形 参:hex=16进制字符串地址 hex_len= text=要保存的地址 * 函 数 返 回:无 * 作 者:LC * 备 注:无 ******************************************************************/ void Hex_To_Text(char *hex, char *text) { int string_length = strlen((char*)hex) / 2; int i = 0; // 将每两个字符转换为一个字节,并拷贝到转换后的字符串中 for (i = 0; i < string_length; i++) { char HEX[3]; memcpy(HEX, &hex[i * 2], 2); HEX[2] = ''; text[i] = strtol(HEX, NULL, 16); } text[i]=''; } /****************************************************************** * 函 数 名 称:Search_Data * 函 数 说 明:查找对应数据 * 函 数 形 参:original_data=原始数据 weather_data=要保存的数据 find_what查找的数据头 * 函 数 返 回:0=查找失败 1=查找成功 * 作 者:LC * 备 注:无 ******************************************************************/ uint8_t Search_Data(char *original_data, char * weather_data, char *find_what) { char *buff; uint16_t i = 0; if( strstr(original_data, find_what) != NULL ) { //找到内容开始的地址 buff = strstr(original_data, find_what); //过滤find_what的内容 buff += strlen(find_what); //找到'"'的下标 while( buff[i] != '"' ) { i++; } //从buff里复制长度为i的字符串到weather_data strncpy(weather_data, buff, i); //strncpy函数不会补零,需手动补上 weather_data[i+1] = ''; return 1; } return 0; } /****************************************************************** * 函 数 名 称:Weather_Data_Analysis * 函 数 说 明:解析天气数据格式。 * 函 数 形 参:original_data原始JSON格式数据 weather_data解析的数据保存地址 * 函 数 返 回:无 * 作 者:LC * 备 注:原始JSON格式数据见下方 {"results":[{"location":{"id":"WS10730EM8EV","name":"Shenzhen","country":"CN","path": "Shenzhen,Shenzhen,Guangdong,China","timezone":"Asia/Shanghai","timezone_offset": "+08:00"},"now":{"text":"Sunny","code":"0","temperature":"32"},"last_update":"2023-06-02T16:10:15+08:00"}]} ******************************************************************/ void Weather_Data_Analysis(char *original_data, WEATHER_DATA* weather_data) { //查找城市 Search_Data(original_data,weather_data- >city,"name":""); //查找国家 Search_Data(original_data,weather_data- >country,"country":""); //查找具体地址 Search_Data(original_data,weather_data- >path,"path":""); //查找天气状态 Search_Data(original_data,weather_data- >weather,"text":""); //查找天气代码 Search_Data(original_data,weather_data- >weather_code,"code":""); //查找当前温度 Search_Data(original_data,weather_data- >temperature,"temperature":""); //查找更新时间 Search_Data(original_data,weather_data- >last_update,"last_update":""); } /****************************************************************** * 函 数 名 称:Get_Weather_Data * 函 数 说 明:获取天气数据 * 函 数 形 参:data=所以天气数据保存的结构体地址 * 函 数 返 回:无 * 作 者:LC * 备 注:无 ******************************************************************/ void Get_Weather_Data(WEATHER_DATA* data) { int timeout = 10; //5秒的超时时间 unsigned char send_buff[250]={0}; char parse_buff[512]={0}; char *rev_buff = NULL; //整理天气数据API sprintf((char*)send_buff, "AT+HTTPSEND=0,0,%d,%srn", strlen(WEATHER_FACTS_API)-2, WEATHER_FACTS_API); //发送指令 EC01G_USART_send_String((unsigned char*)send_buff);//发送AT指令 //接收数据 while( timeout-- ) { delay_ms(1000); if( EC01G_RX_API_FLAG == 1 ) { EC01G_RX_API_FLAG = 0; //如果接收正确 rev_buff = strstr((const char*)EC01G_RX_BUFF, "+HTTPRESPC:"); printf("rn===================api====================rn"); printf("%srn",rev_buff); printf("rn=======================================rn"); } } int i = 0; //过滤不需要的数据 rev_buff+=11; //过滤+HTTPRESPC: while( i < 4 )//过滤4个逗号 { if( *rev_buff == ',' ) { i++; } rev_buff++; } //将16进制字符串转为字符型字符串 Hex_To_Text(rev_buff, parse_buff); printf("rn过滤后的数据:rn"); printf("==========================rn"); printf("%srn",parse_buff); printf("rn"); printf("==========================rn"); //解析数据保存到data里 Weather_Data_Analysis(parse_buff, data); } /****************************************************************** * 函 数 名 称:EC01G_USART_IRQHandler * 函 数 说 明:连接EC01G的串口中断服务函数 * 函 数 形 参:无 * 函 数 返 回:无 * 作 者:LC * 备 注:无 ******************************************************************/ void EC01G_USART_IRQHandler(void) { if(USART_GetITStatus(EC01G_USART,USART_IT_RC) != RESET) // 接收缓冲区不为空 { //接收数据 EC01G_RX_BUFF[ EC01G_RX_LEN++ ] = USART_ReceiveData(EC01G_USART); if(EC01G_RX_LEN > EC01G_RX_LEN_MAX ) EC01G_RX_LEN = 0; EC01G_RX_BUFF[EC01G_RX_LEN] = ''; //字符串结尾补 '' EC01G_RX_FLAG = 1; // 接收完成 if( EC01G_RX_BUFF[ EC01G_RX_LEN- 1 ] == 'C' && EC01G_RX_BUFF[ EC01G_RX_LEN- 2 ] == 'P' && EC01G_RX_BUFF[ EC01G_RX_LEN- 3 ] == 'S' ) { EC01G_RX_API_FLAG = 1; } //测试,查看接收到了什么数据 #if DEBUG printf("%c", EC01G_RX_BUFF[ EC01G_RX_LEN - 1 ]); #endif USART_ClearITPendingBit(EC01G_USART, USART_IT_RC); } } void HardFault_Handler(void) { }
在文件bsp_ec01g.h中,编写如下代码。
/* * Change Logs: * Date Author Notes * 2024-06-24 LCKFB-LP first version */ #ifndef _BSP_EC01G_H_ #define _BSP_EC01G_H_ #include "string.h" #include "board.h" //是否开启串口0调试,查看WIFI回显数据 #define DEBUG 1 /**************************** 心知天气 ****************************/ //秘钥(需要修改为你的密钥!) #define API_KEY "SpUFyXp2YvP12yxQR" //要查询的城市 (输入城市拼音,免费版只支持部分城市) #define QUERIED_CITY "shenzhen" //天气实况API "/v3/weather/now.json?key=your_api_key&location=beijing&language=zh-Hans&unit=c" #define WEATHER_FACTS_FRONT ""/v3/weather/now.json?key=" #define WEATHER_FACTS_MIDDLE "&location=" #define WEATHER_FACTS_BACK "&language=en&unit=c"" //查询天气实况API #define WEATHER_FACTS_API ( WEATHER_FACTS_FRONT API_KEY WEATHER_FACTS_MIDDLE QUERIED_CITY WEATHER_FACTS_BACK) //未来天气预报API "/v3/weather/daily.json?key=your_api_key&location=beijing&language=zh-Hans&unit=c&start=0&days=5" #define FUTURE_WEATHER_FRONT ""/v3/weather/daily.json?key=" #define FUTURE_WEATHER_MIDDLE "&location=" #define FUTURE_WEATHER_BACK "&&language=en&unit=c&start=0&days=5"" //未来天气预报API #define FUTURE_WEATHER_API ( FUTURE_WEATHER_FRONT API_KEY FUTURE_WEATHER_MIDDLE QUERIED_CITY FUTURE_WEATHER_BACK) //接收的数据保存地址 typedef struct{ char city[20]; //查询的城市 char country[20]; //查询的国家 char path[50]; //具体地址(市市省国,例:Shenzhen,Shenzhen,Guangdong,China) char weather[10]; //天气状态 char weather_code[5]; //天气代码 char temperature[5]; //温度 char last_update[50]; //天气数据更新时间 }WEATHER_DATA; /**************************** 串口配置 ****************************/ #define RCC_EC01G_GPIO_ENABLE() __RCC_GPIOA_CLK_ENABLE() // 串口TX的端口时钟 #define RCC_EC01G_USART_ENABLE() __RCC_UART2_CLK_ENABLE() // 串口2的时钟 #define BSP_GPS_AF_UART_TX() PA02_AFx_UART2TXD() // GPIO的引脚复用 #define BSP_GPS_AF_UART_RX() PA03_AFx_UART2RXD() // GPIO的引脚复用 #define PORT_EC01G_GPIO CW_GPIOA // GPIO的端口 #define GPIO_EC01G_TX GPIO_PIN_2 // 串口TX的引脚 #define GPIO_EC01G_RX GPIO_PIN_3 // 串口RX的引脚 #define EC01G_USART CW_UART2 // 串口2 #define EC01G_USART_IRQ UART2_IRQn // 串口2中断 #define EC01G_USART_IRQHandler UART2_IRQHandler // 串口2中断服务函数 #define EC01G_RX_LEN_MAX 4096 //串口接收最大长度 void EC01G_Init(void); uint8_t EC01G_Seniverse_Init(void); void Get_Weather_Data(WEATHER_DATA* data); #endif
四、案列:接入心知天气平台获取天气数据
1. 心知天气获取
第一次使用需要先注册,进入官网:https://www.seniverse.com。
2. 登陆控制台获取私钥(这里我注册了3个 密钥,随便选择了一个)
3. 根据安信可科技提供的资料,完成心知天气平台的接入。
AT指令发送步骤:
指令相关含义见资料【nb-iot系列模组AT指令集v1.0.pdf】
发送 | 成功则返回 | 说明 |
---|---|---|
ATrn | OK | 测试设备是否存在 |
--- | --- | --- |
AT+CFUN=1rn | OK | 关闭飞行模式(如果返回ERROR:10 说明没有识别到物联网卡) |
AT+CEREG?rn | +CEREG: 0,1 | 判断是否附着网络成功(不成功请注意NB天线是否接好) |
AT+HTTPCREATE=0,"http://116.62.81.138:80"rn | OK | 创建一个http客户端实例 |
AT+HTTPCON=0rn | OK | 连接该实例(连接心知天气平台) |
AT+HTTPSEND=0,0,LEN,APIrn | OK | 向0号实例发送长度为LEN的API指令 |
API指令可上心知天气官网查询,这个贴两个API指令。(可点击超链接跳转到相关网页)
获取天气实况:https://seniverse.yuque.com/hyper_data/api_v3/nyiu3t
/v3/weather/now.json?key=your_api_key&location=beijing&language=zh-Hans&unit=c
获取未来5天天气情况:https://seniverse.yuque.com/hyper_data/api_v3/sl6gvt
/v3/weather/daily.json?key=your_api_key&location=beijing&language=zh-Hans&unit=c&start=0&days=5
API指令中,请将 your_api_key 替换成为你的私钥。
4. 代码移植
实现该步骤请确保完成了移植至工程目录。
在 bsp_ec01g.h 中,将私钥宏定义 API_KEY 修改为你自己的私钥。并输入要查询的城市。
在main.c 中编辑以下代码。
/* * Change Logs: * Date Author Notes * 2024-06-24 LCKFB-LP first version */ #include "board.h" #include "stdio.h" #include "bsp_uart.h" #include "bsp_ec01g.h" int32_t main(void) { WEATHER_DATA weather_data={0}; board_init(); uart1_init(115200); printf("Start... rn %s rn",WEATHER_FACTS_API); EC01G_Init(); EC01G_Seniverse_Init(); printf("rnrn Weather rnrn"); //获取天气数据,并保存到weather_data结构体里 Get_Weather_Data(&weather_data); printf("city = %srn", weather_data.city ); printf("country = %srn", weather_data.country ); printf("path = %srn", weather_data.path ); printf("weather = %srn", weather_data.weather ); printf("weather_code = %srn", weather_data.weather_code ); printf("temperature = %srn", weather_data.temperature ); printf("last_update = %srn", weather_data.last_update ); while(1) { delay_ms(100); } }
上电现象:
模块移植成功案例代码:
链接:https://pan.baidu.com/s/12gapOSbSv8IIJ07tkIzIPA?pwd=LCKF
提取码:LCKF
审核编辑 黄宇
全部0条评论
快来发表一下你的评论吧 !