本章节以设备侧开发板采用coap协议接入本IOT中继系统平台为例,主要实现从接入嵌入设备上报物模型属性数据。 设备上报报文数据采用字符串BASE64加密传输。
用户名: 159xxxxxxxx(IOT设备接入–设备接入配置 填写的连接账号)
密码: XXXXXX(IOT设备接入–设备接入配置 填写的连接口令)
设备认证请求: 生成认证请求内容,根据设备id 、 连接账号、 连接密钥 、 连接时间戳组成的json字符串BASE64加密生成。 Post请求向COAP服务器设备认证请求地址(如后)提交认证请求报文获取设备认证通过token。
设备认证请求地址: coap://192.168.0.105:5683/coapApi/auth
请求参数说明:
Method:请求方法。只支持POST方法
URL:URL地址,取值: coap://192.168.0.105:5683/coapApi/auth
加密前设备认证请求json字符串格式(样例):
{ "userName": "159xxxxxxxx" , "password": "xxxxxxx" , "clientId": "44080000001111000020.159xxxxxxxx" , "deviceId": "44080000001111000020" , "timestamp": "1695777837040"}
json字符串内容说明:
参数 | 说明 |
---|---|
userName | 配置的Coap协议连接设备连接账户 如上例 “159xxxxxxxx” |
password | 配置的Coap协议连接设备连接账户口令 如上例 “xxxxxxx” |
deviceId | 上报设备ID 上例 “44080000001111000020” |
clientId | 客户端ID 上例 客户端ID值 “ 44080000001111000020.159xxxxxxxx " 由 “设备ID+”.”+设备连接账户 " 組合而成 |
timestamp | 时间戳 上例 "1695777837040"为连接时时间戳 |
加密后设备认证请求json字符串(样例):
eyAgInVzZXJOYW1lIjogIjE1OTI3NDk4OTI1IiAsICJwYXNzd29yZCI6ICIxMjM0NTYiICwgImNsaWVudElkIjogIjQ0MDgwMDAwMDAxMTExMDAwMDIwLjE1OTI3NDk4OTI1IiAsICJkZXZpY2VJZCI6ICI0NDA4MDAwMDAwMTExMTAwMDAyMCIgLCAidGltZXN0YW1wIjoiMTY5NTc3NzgzNzA0MCJ
设备认证通过返回的token(样例):
eyJhbGciOiJIUzI1NiJ9.eyJwYXNzd29yZCI6IjEyMzQ1NiIsInVzZXJOYW1lIjoiMTU5Mjc0OTg5MjUiLCJleHAiOjE2OTU5MjYwOTIsImRldmljZUlkIjoiNDQwODAwMDAwMDExMTEwMDAwMjAifQ.mjY_IoGcqQTjlG6uelk_OxQf9AI-yByeqimi1EG0K54
上报报文请求地址: coap://192.168.0.105:5683/coapApi/44080000001111000020
请求参数属性说明:
参数 | 说明 |
---|---|
token | 设备连接认证通过返回的token 字符串(见前第2节所述的token) |
payLoad | 报文字符串,上报设备属性数据标识,由上报的属性数据JSON字符串BASE64 加密生成 |
加密前报文上报属性数据json字符串内容 | [ json字符串内容说明propertiesId上报设备属性数据ID标识,为设备物模型配置的属性ID标识 如上例黄色 "P_1695782510161" 、 "P_1695888406309"dataValue上报设备属性数据值,dataValue 为属性值标识 上例黄色 "110" 、 "12" 上报设备属性数据值 |
加密后的报文上报属性数据json字符串内容(范例) | W3sicHJvcGVydGllc0lkIjoiUF8xNjk1NzgyNTEwMTYxIiwiZGF0YVZhbHVlIjoiMTEwIn0seyJwcm9wZXJ0aWVzSWQiOiJQXzE2OTU4ODg0MDYzMDkiLCJkYXRhVmFsdWUiOiIxMiJ9XQ== |
本例程以接入设备为Java客户端设备,接入本IOT中继平台使用。 此设备采用coap协议接入本IOT中继系统平台,主要实现从接入设备上报物模型属性数据。 设备侧根据业务需要周期性调用此接口向平台上报设备属性数据,例如:间隔10分钟上报设备温度、湿度等,本IOT平台coap协议接入只支持上报物模型属性数据。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.2</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.txb</groupId> <artifactId>CoapClient</artifactId> <version>0.0.1-SNAPSHOT</version> <name>CoapClient</name> <description>CoapClient</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!-- coap协议包--> <dependency> <groupId>org.eclipse.californium</groupId> <artifactId>californium-core</artifactId> <version>2.0.0-M17</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.5</version> </dependency> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.13</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.83</version> </dependency> </dependencies> <repositories> <repository> <id>public</id> <name>aliyun nexus</name> <url>https://maven.aliyun.com/repository/public</url> <releases> <enabled>true</enabled> </releases> </repository> <repository> <releases> <enabled>false</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots> <id>ias-snapshots</id> <name>Infinite Automation Snapshot Repository</name> <url>https://maven.mangoautomation.net/repository/ias-snapshot/</url> </repository> <repository> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> <id>ias-releases</id> <name>Infinite Automation Release Repository</name> <url>https://maven.mangoautomation.net/repository/ias-release/</url> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>public</id> <name>aliyun nexus</name> <url>https://maven.aliyun.com/repository/public</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> </pluginRepositories> </project>
package com.txb.coap.client; import com.alibaba.fastjson.JSONObject; import org.apache.logging.log4j.util.Base64Util; import org.eclipse.californium.core.CoapResponse; import org.eclipse.californium.core.Utils; import org.eclipse.californium.core.coap.*; import org.eclipse.californium.elements.exception.ConnectorException; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; /****** *@description: $ * * author: txb0727 * date: 2023-06-06 13:48 ****/ public class CoapClient { private static final int COAP2_OPTION_TOKEN = 2088; String ip="192.168.0.105"; //为IOT中继平台设备接入接口配置的IP, String port="5683"; //为IOT中继平台设备接入接口配置的连接端口 URI uri = null; org.eclipse.californium.core.CoapClient coapClient = null; String body = ""; public void init(String ip,String port) throws URISyntaxException{ // define the ip and resource name here, ‘test’ is the resource name this.ip=ip; this.port=port; uri = new URI("coap://"+ip+":"+port+"/coapApi/auth"); coapClient = new org.eclipse.californium.core.CoapClient(uri); // 只支持POST方法。 Request request = new Request(CoAP.Code.POST, CoAP.Type.CON); // 设置option。 OptionSet optionSet = new OptionSet(); optionSet.addOption(new Option(OptionNumberRegistry.CONTENT_FORMAT, MediaTypeRegistry.APPLICATION_JSON)); optionSet.addOption(new Option(OptionNumberRegistry.ACCEPT, MediaTypeRegistry.APPLICATION_JSON)); request.setOptions(optionSet); } public String token(String secrecet) throws URISyntaxException{ // define the ip and resource name here, ‘auth’ is the resource name uri = new URI("coap://"+ip+":"+port+"/coapApi/auth"); coapClient = new org.eclipse.californium.core.CoapClient(uri); // 只支持POST方法。 Request request = new Request(CoAP.Code.POST, CoAP.Type.CON); // 设置option。 OptionSet optionSet = new OptionSet(); optionSet.addOption(new Option(OptionNumberRegistry.CONTENT_FORMAT, MediaTypeRegistry.APPLICATION_JSON)); optionSet.addOption(new Option(OptionNumberRegistry.ACCEPT, MediaTypeRegistry.APPLICATION_JSON)); request.setOptions(optionSet); // 设置认证uri。 request.setURI(uri); // 设置认证请求payload。 request.setPayload(secrecet); // 发送认证请求。 CoapResponse response = null; try { response = coapClient.advanced(request); } catch (ConnectorException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } System.out.println(Utils.prettyPrint(response)); System.out.println("the respnse body is "+response.getResponseText()); return response.getResponseText(); } // send with post public void sendPost(String deviceId,String token,int format) throws ConnectorException, IOException, URISyntaxException { uri = new URI("coap://"+ip+":"+port+"/coapApi/"+deviceId); coapClient = new org.eclipse.californium.core.CoapClient(uri); // 只支持POST方法。 Request request = new Request(CoAP.Code.POST, CoAP.Type.CON); // 设置option。 OptionSet optionSet = new OptionSet(); optionSet.addOption(new Option(OptionNumberRegistry.CONTENT_FORMAT, MediaTypeRegistry.APPLICATION_JSON)); optionSet.addOption(new Option(OptionNumberRegistry.ACCEPT, MediaTypeRegistry.APPLICATION_JSON)); optionSet.addOption(new Option(COAP2_OPTION_TOKEN, token)); request.setOptions(optionSet); // 设置认证uri。 request.setURI(uri); // 设置消息payload request.setPayload(body); // 发送认证请求。 CoapResponse response = coapClient.advanced(request); // System.out.println("----------------"); // System.out.println(request.getPayload().length); // System.out.println("----------------"); System.out.println(Utils.prettyPrint(response)); } public void setBody(String body) { this.body = body; } /** * 生成认证请求内容。 * * @param deviceId,设备ID。 * @param userName,连接账号。 * @param password,连接密钥。 * @return 认证请求。 */ public String authBody(String deviceId, String userName, String password) { // 构建认证请求。 JSONObject body = new JSONObject(); body.put("deviceId", deviceId); body.put("userName", userName); body.put("clientId", deviceId + "." + userName); body.put("timestamp", String.valueOf(System.currentTimeMillis())); body.put("password", password); System.out.println("----- auth body -----"); System.out.println(body.toJSONString()); String secretResult= Base64Util.encode(body.toJSONString()); return secretResult; } public static void main(String[] args) throws ConnectorException, IOException, InterruptedException, URISyntaxException { /************ * [{"propertiesId":"P_1695782510161","dataValue":"110"},{"propertiesId":"P_1695888406309","dataValue":"12"}] * 为上报属性数据json字符串内容 * json字符串内容说明 : propertiesId 上报设备属性数据ID标识,上例中"P_1695782510161" 、 "P_1695888406309"为设备物模型配置的属性ID标识值 * dataValue 为属性值标识 上例黄色 "110" 、 "12" 为上报设备属性数据值,为实际获取 *********/ String payLoad = "[{\"propertiesId\":\"P_1695782510161\",\"dataValue\":\"110\"},{\"propertiesId\":\"P_1695888406309\",\"dataValue\":\"12\"}]"; CoapClient myclient = new CoapClient(); /********* * 客户端连接初始化 * "192.168.0.105" 为IOT中继平台设备接入接口配置的IP,"5683"为IOT中继平台设备接入接口配置的连接端口 */ myclient.init("192.168.0.105","5683"); //客户端连接初始化 /****** * 生成BASE64加密认证报文 * "44080000001111000020" 为接入设备ID * "15911111111" 为IOT中继平台设备接入配置配置的连接账号 * "******" 为IOT中继平台设备接入配置配置的连接口令 */ String secrecet =myclient.authBody("44080000001111000020", "15911111111", "******"); //生成BASE64加密认证报文 String token=myclient.token(secrecet); //客户端连接认证获取token // System.out.println(token); /***** * 上报业务数据 * Base64Util.encode(payLoad) 为base64加密工具类 */ myclient.setBody(Base64Util.encode(payLoad));//上报base64加密数据 myclient.sendPost("44080000001111000020",token,50);//发送请求 } }
coap协议接入JAVA客户端例程打包源码进入IOT中继宝盒主操作界面打开“IOT设备接口”窗口,选择对应的设备–设备接入端接口中对应的协议接入样例中下载。
本文档适用对象为嵌入式芯片设备,接入本IOT中继平台使用。 此设备采用coap协议接入本IOT中继系统平台,主要实现从接入设备上报物模型属性数据。 设备侧根据业务需要周期性调用此接口向平台上报设备属性数据,例如:间隔10分钟上报设备温度、湿度等,本IOT平台coap协议接入只支持上报物模型属性数据。
针对嵌入式设备, 上报设备属性数据报文由json字符串组成,数据报文对json字符串base64加密传输(本例程以**STM32L151C8T6A核心板+通讯模块sim7020c芯片+移动物联网卡**接入为例)。
coap.h文件中定义:
#define PRODUCTKEY "44080000001111000020" //本IOT中继平台里面定义的设备ID #define USERNAME //IOT中继宝盒里面设备接入配置页面配置的设备连接账号 #define DEVICESECRE xxxxxx //IOT中继宝盒里面设备接入配置页面配置的设备连接密钥 #define SUBDOMAIN "" //用户申请的服务器子域名 --- IOT中继宝盒里面内网穿透功能,在IOT终端绑定界面业务平台注册登记的子域名
参数说明:
参数 | 说明 |
---|---|
PRODUCTKEY | 本IOT中继平台里面定义的设备ID |
USERNAME | 本IOT中继平台设备接入配置的设备连接账号 |
DEVICESECRE | 在本IOT中继平台设备接入配置的设备连接密钥 |
SUBDOMAIN | 用户申请的服务器子域名 ,如果 IOT中继宝盒里面内网穿透功能,在IOT终端绑定界面业务平台注册登记的子域名 |
main.c
/*-----------------------------------------------------------------------------*/ /* COAP协议接入示例 */ /*-----------------------------------------------------------------------------*/ /* @attention * 本范例用于嵌入式开发版采用coap协议发布上报设备属性数据,仅供参考 * 本范例硬件为 STM32L151C8T6A核心板,通讯模块为sim7020c芯片+移动物联网卡 * * <h2><center> COPYRIGHT 2023 Wuhan Yilian Technology.</center></h2> ******************************************************************************/ #include "stm32l1xx.h" //包含需要的头文件 #include "main.h" //包含需要的头文件 #include "delay.h" //包含需要的头文件 #include "key.h" //包含需要的头文件 #include "led.h" //包含需要的头文件 #include "usart1.h" //包含需要的头文件 #include "usart2.h" //包含需要的头文件 #include "timer2.h" //包含需要的头文件 #include "timer4.h" //包含需要的头文件 #include "sim7020.h" //包含需要的头文件 #include "dht12.h" //包含需要的头文件 #include "iic.h" //包含需要的头文件 #include "coap.h" //包含需要的头文件 #include "standby.h" //包含需要的头文件 #include "rtc.h" //包含需要的头文件 #include "eeprom.h" //包含需要的头文件 char Sta_flag = 1; //0:配置完成处理数据状态 1:AT指令配置模块状态 int main(void) { char num,csq; Delay_Init(); //延时功能初始化 Usart1_Init(9600); //串口1功能初始化,波特率9600 Usart2_Init(115200); //串口2功能初始化,波特率115200 TIM4_Init(300,3200); //TIM4初始化,定时时间 300*3200*1000/32000000 = 30ms LED_Init(); //初始化LED指示灯 KEY_Init(); //初始化按键 IIC_Init(); //初始化IIC接口 Rtc_Init(); //初始化RTC CoapServer_Parameter_Init(); //初始化连接IOT中继宝盒coap服务器的参数 IoT_CoAP_Auth(COAP_MESSAGE_TYPE_CON,COAP_MESSAGE_CODE_POST,&Auth_CB); //初始化认证的数据包 if(PWR_GetFlagStatus(PWR_FLAG_SB) != RESET){ //查看PWR_FLAG_SB标志,如果置位表示系统从待机模式恢复,进入if u1_printf("系统从待机模式恢复\r\n"); //串口输出信息 PWR_ClearFlag(PWR_FLAG_SB); //清除PWR_FLAG_SB标志 u1_printf("准备退出PSM模式... ...\r\n"); //串口输出信息 if(SIM7020_ExitPSM(30)){ //超时单位1s,超时时间30s u1_printf("退出PSM模式超时,准备重启\r\n"); //串口输出信息 //返回 }else u1_printf("退出PSM模式成功\r\n"); //串口输出信息 num = 10; //最多查询10次 while(num--){ //循环查询 u1_printf("准备检查信号质量... ...\r\n"); //串口输出信息 csq = SIM7020_CSQ_Cmd(50); //超时单位100ms,超时时间5s if((csq>0)&&(csq<=31)){ //按到信号范围是否正确 u1_printf("信号强度范围0~31,99表示无信号\r\n"); //串口输出信息 u1_printf("信号强度:%d\r\n",csq); //串口输出信息 break; //跳出while } Delay_Ms(1000); //延时 } u1_printf("检查信号质量成功\r\n"); //串口输出信息 Delay_Ms(1000); //延时 EEPROM_ReadData(0,(unsigned int *)DNSIP,4); //内部EEprom读取数据 u1_printf("EEPROM中读到的IP:%s\r\n",DNSIP); //串口输出信息 TempHumi_State(); //发送数据,然后进入PSM } else{ u1_printf("正常开机\r\n"); //串口输出信息 while(SIM7020_Init()){ //初始化7020,向IOT中继宝盒coap服务器发起认证 Delay_Ms(500); //延时 } TempHumi_State(); //发送数据,然后进入PSM } while(1) //主循环 { } } /*-------------------------------------------------*/ /*函数名:采集温湿度,并发布给服务器 */ /*参 数:无 */ /*返回值:无 */ /*-------------------------------------------------*/ char TempHumi_State(void) { unsigned char data[5]; DHT12_ReadData(data); //比较校验值,成功,进入if memset(Data_CB.payload,0,1024); //清空缓冲区 /************ * [{"propertiesId":"P_1695782510161","dataValue":"110"},{"propertiesId":"P_1695888406309","dataValue":"12"}] * 为上报属性数据json字符串内容 * json字符串内容说明 : propertiesId 上报设备属性数据ID标识,上例中"P_1695782510161" 、 "P_1695888406309"为设备物模型配置的属性ID标识值 * dataValue 为属性值标识 上例黄色 "110" 、 "12" 为上报设备属性数据值,为实际获取 *********/ // sprintf(Data_CB.payload,"[{\"propertiesId\":\"P_1695782510161\",\"dataValue\":\"%d.%d\"},{\"propertiesId\":\"P_1695888406309\",\"dataValue\":\"%d.%d\"}]",data[0],data[1],data[2],data[3]); //构建回复湿度温度数据 sprintf(Data_CB.payload,"[{\"propertiesId\":\"P_1695782510161\",\"dataValue\":\"110\"},{\"propertiesId\":\"P_1695888406309\",\"dataValue\":\"12\"}]"); //构建回复湿度温度数据 u1_printf("%s\r\n",Data_CB.payload); u1_printf("准备连接CoAP服务器... ...\r\n"); //串口输出信息 if(SIM7020_CCOAPNEW(10)){ //超时单位1s,超时时间10s u1_printf("连接CoAP服务器超时,准备重启\r\n"); //串口输出信息 }else u1_printf("连接CoAP服务器成功\r\n"); //串口输出信息 u1_printf("准备发送IOT中继宝盒Coap服务器认证CoAP包... ...\r\n"); //串口输出信息 if(SIM7020_CCOASend_Auth(100,CoAPpack,CoAPpack_len)){ //超时单位100ms,超时时间10s u1_printf("发送IOT中继宝盒Coap服务器认证CoAP包失败\r\n"); //串口输出信息 }else u1_printf("发送IOT中继宝盒Coap服务器认证CoAP包成功\r\n"); //串口输出信息 IoT_CoAP_Data(COAP_MESSAGE_TYPE_CON,COAP_MESSAGE_CODE_POST,&Data_CB); //准备数据上报数据包 u1_printf("准备发送温湿度数据包... ...\r\n"); //串口输出信息 if(SIM7020_CCOASend_Data(100,CoAPpack,CoAPpack_len)){ //超时单位100ms,超时时间10s u1_printf("发送温湿度数据包失败\r\n"); //串口输出信息 }else u1_printf("发送温湿度数据包成功\r\n"); //串口输出信息 u1_printf("准备关闭连接... ...\r\n"); //串口输出信息 if(SIM7020_CCOAPDEL(100)){ //超时单位100ms,超时时间10s u1_printf("关闭连接超时,准备重启\r\n"); //串口输出信息 }else u1_printf("关闭连接成功\r\n"); //串口输出信息 u1_printf("准备进入PSM模式... ...\r\n"); //串口输出信息 if(SIM7020_EnterPSM(60)){ //超时单位1s,超时时间60s u1_printf("进入PSM模式超时,准备重启\r\n"); //串口输出信息 }else{ u1_printf("进入PSM模式成功\r\n"); //串口输出信息 SysEnter_Standby(); //进入待机模式 } return 0; }
coap.h
/*-------------------------------------------------*/ /* */ /* 实现COAP协议功能的头文件 */ /* */ /*-------------------------------------------------*/ #ifndef __COAP_H #define __COAP_H #define PRODUCTKEY "44080000001111000020" //产品ID --- IOT中继宝盒里面定义的设备ID #define PRODUCTKEY_LEN strlen(PRODUCTKEY) //产品ID长度 #define USERNAME "19945021328" //设备连接账号 --- IOT中继宝盒里面配置的设备连接账号 #define USERNAME_LEN strlen(DEVICENAME) //设备连接账号长度 #define DEVICESECRE "123456" //设备连接秘钥 --- IOT中继宝盒里面配置的设备连接密钥 #define DEVICESECRE_LEN strlen(DEVICESECRE) //设备连接秘钥长度 #define SUBDOMAIN "txb" //用户申请的服务器子域名 --- IOT中继宝盒里面内网穿透功能,在IOT终端绑定界面业务平台注册登记的子域名 /******** *coap服务器上传数据路径为 "coapApi/44080000001111000020", "coapApi" 为固定值,后面“44080000001111000020”为设备ID *********************/ #define TOPIC_PATH1 "coapApi\0" //上报数据时路径段1 \0是结束符必须有 #define TOPIC_PATH2 "44080000001111000020\0" //上报数据时路径段2 \0是结束符必须有 #define TOPIC_PATH_NUM 2 //上报数据时路径段的个数 //数据类型化的定义 //Accept和Content_Format 可选的类型 #define COAP_CT_TEXT_PLAIN 0 /* text/plain (UTF-8) */ #define COAP_CT_APP_LINK_FORMAT 40 /* application/link-format */ #define COAP_CT_APP_XML 41 /* application/xml */ #define COAP_CT_APP_OCTET_STREAM 42 /* application/octet-stream */ #define COAP_CT_APP_RDF_XML 43 /* application/rdf+xml */ #define COAP_CT_APP_EXI 47 /* application/exi */ #define COAP_CT_APP_JSON 50 /* application/json */ #define COAP_CT_APP_CBOR 60 /* application/cbor */ //CoAP中所有的Option的编号 //2088 是IOT中继宝盒Coap服务器自定义的 #define COAP_OPTION_IF_MATCH 1 /* C, opaque, 0-8 B, (none) */ #define COAP_OPTION_URI_HOST 3 /* C, String, 1-255 B, destination address */ #define COAP_OPTION_ETAG 4 /* E, opaque, 1-8 B, (none) */ #define COAP_OPTION_IF_NONE_MATCH 5 /* empty, 0 B, (none) */ #define COAP_OPTION_URI_PORT 7 /* C, uint, 0-2 B, destination port */ #define COAP_OPTION_LOCATION_PATH 8 /* E, String, 0-255 B, - */ #define COAP_OPTION_URI_PATH 11 /* C, String, 0-255 B, (none) */ #define COAP_OPTION_CONTENT_FORMAT 12 /* E, uint, 0-2 B, (none) */ #define COAP_OPTION_MAXAGE 14 /* E, uint, 0--4 B, 60 Seconds */ #define COAP_OPTION_URI_QUERY 15 /* C, String, 1-255 B, (none) */ #define COAP_OPTION_ACCEPT 17 /* C, uint, 0-2 B, (none) */ #define COAP_OPTION_LOCATION_QUERY 20 /* E, String, 0-255 B, (none) */ #define COAP_OPTION_BLOCK2 23 /* C, uint, 0--3 B, (none) */ #define COAP_OPTION_BLOCK1 27 /* C, uint, 0--3 B, (none) */ #define COAP_OPTION_PROXY_URI 35 /* C, String, 1-1024 B, (none) */ #define COAP_OPTION_PROXY_SCHEME 39 /* C, String, 1-255 B, (none) */ #define COAP_OPTION_SIZE1 60 /* E, uint, 0-4 B, (none) */ #define COAP_OPTION_AUTH_TOKEN 2088 /* C, String, 1-255B, (none)*/ #define COAP_OPTION_SEQ 2089 /* C, String, 1-255B, (none)*/ //CoAP中 T可选的参数 //4中报文类型 #define COAP_MESSAGE_TYPE_CON 0 #define COAP_MESSAGE_TYPE_NON 1 #define COAP_MESSAGE_TYPE_ACK 2 #define COAP_MESSAGE_TYPE_RST 3 //CoAP中 CODE可选的参数 //4中功能方法 #define COAP_MESSAGE_CODE_GET 0X01 #define COAP_MESSAGE_CODE_POST 0X02 #define COAP_MESSAGE_CODE_PUT 0X03 #define COAP_MESSAGE_CODE_DEL 0X04 //IOT中继宝盒Coap服务器的响应码 #define IOTX_COAP_RESP_CODE_CONTENT 0x45 //正确 #define IOTX_COAP_RESP_CODE_BAD_REQUEST 0x80 //请求发送的Payload非法 #define IOTX_COAP_RESP_CODE_UNAUTHORIZED 0x81 //未授权的请求 #define IOTX_COAP_RESP_CODE_FORBIDDEN 0x83 //禁止的请求 #define IOTX_COAP_RESP_CODE_NOT_FOUND 0x84 //请求的路径不存在 #define IOTX_COAP_RESP_CODE_NOT_METHOD 0x85 //请求方法不是指定值 #define IOTX_COAP_RESP_CODE_UNSUPPORT_ACCPTABLE 0x86 //Accept不是指定的类型 #define IOTX_COAP_RESP_CODE_UNSUPPORT_CF 0x8F //请求的content不是指定类型 #define IOTX_COAP_RESP_CODE_INTERNAL_SERVER_ERROR 0xA0 //auth服务器超时或错误 typedef struct //IOT中继宝盒Coap服务器CoAP控制块 { char path[128]; //url路径 char host[128]; //主机名 int port; //端口号 unsigned char Accept; //接收类型 仅支持application/json和application/cbor两种格式 unsigned char Content_Format; //内容格式类型 仅支持application/json和application/cbor两种格式 char auth_token[64]; //服务器下发的认证信息,每次post数据,需要携带认证信息,否则上报数据认为是非法数据 unsigned char auth_key[16]; //根据服务器下发的random和设备秘钥计算出来的,共后续的AES加密使用 char payload[1024]; //需要上报的数据 }IOTCoAP_CB; extern unsigned char ServerIP[128]; //外部变量声明,存放服务器IP或是域名 extern int ServerPort; //外部变量声明,存放服务器的端口号 extern IOTCoAP_CB Auth_CB; //外部变量声明,IOT中继宝盒CoAP认证控制块 extern IOTCoAP_CB Data_CB; //外部变量声明,IOT中继宝盒CoAP数据控制块 extern unsigned char CoAPpack[1024]; //外部变量声明,存放需要发送的coap协议包数据 extern int CoAPpack_len; //外部变量声明,存放需要发送的coap协议包数据长度 void CoapServer_Parameter_Init(void); void IoT_CoAP_Auth(unsigned char T, unsigned char Code, IOTCoAP_CB * coap_cb); void IoT_CoAP_Data(unsigned char T, unsigned char Code, IOTCoAP_CB * coap_cb); #endif
coap.c
/*-------------------------------------------------*/ /* */ /* 实现COAP协议功能的源文件 */ /* */ /*-------------------------------------------------*/ #include "stm32l1xx.h" //包含需要的头文件 #include "coap.h" //包含需要的头文件 #include "string.h" //包含需要的头文件 #include "stdio.h" //包含需要的头文件 #include "base64.h" //包含需要的头文件 #include "usart1.h" //包含需要的头文件 #include "sim7020.h" //包含需要的头文件 unsigned char Passward[128]; //存放密码的缓冲区 unsigned char Certification[256]; //存放发给服务器的认证信息的缓冲区 unsigned char ServerIP[128]; //存放服务器IP或是域名的缓冲区 int ServerPort; //存放服务器的端口号 unsigned char CoAPpack[1024]; //存放需要发送的coap协议包数据 int CoAPpack_len; //存放需要发送的coap协议包数据长度 unsigned short int Message_ID = 0x0001; //CoAP协议ID从1开始 IOTCoAP_CB Auth_CB; //IOT中继宝盒CoAP认证控制块 IOTCoAP_CB Data_CB; //IOT中继宝盒CoAP数据控制块 char Data_Path[TOPIC_PATH_NUM][32]={ //上报数据时的Path TOPIC_PATH1, TOPIC_PATH2, }; /*----------------------------------------------------------*/ /*函数名:coap服务器初始化参数 */ /*参 数:无 */ /*返回值:无 */ /*----------------------------------------------------------*/ void CoapServer_Parameter_Init(void) { memset(ServerIP,0,128); sprintf((char *)ServerIP,"%s.iotrelay.cn",SUBDOMAIN); //构建服务器域名 ServerPort = 5683; //服务器端口号5683 char send_buff[512]; //发送数据 memset(send_buff,0,sizeof(send_buff)); char *uploadAuthData;//认证数据 /*************************** * 下面JSON字符串说明 * userName 为IOT中继宝盒里面配置的设备连接账号 * password 为IOT中继宝盒里面配置的设备连接密钥 * deviceId 为IOT中继宝盒里面定义的设备ID ************************************************************/ sprintf((char *)uploadAuthData,"{\"userName\":\"%s\",\"password\":\"%s\",\"deviceId\":\"%s\"}",USERNAME,DEVICESECRE,PRODUCTKEY); //构建发给服务器的认证信息 明文 //对拼接的上报报文为json格式字符串base64加密 char secretbody[300]={0}; //base64加密:后的报文内容 int secretbodylen; base64_encode(uploadAuthData,strlen(uploadAuthData), secretbody,&secretbodylen); //对上传报文base64加密 sprintf((char *)Certification,"%s",uploadAuthData); //构建发给服务器的认证信息 u1_printf("服 务 器:%s:%d\r\n",ServerIP,ServerPort); //串口输出调试信息 u1_printf("认证信息:%s\r\n",Certification); //串口输出调试信息 memset(&Auth_CB,0,sizeof(IOTCoAP_CB)); //清空认证控制块缓冲区 sprintf(Auth_CB.path,"coapApi/auth"); //认证URL路径 sprintf(Auth_CB.host,"%s",ServerIP); //主机名 Auth_CB.port = 5683; //端口号 Auth_CB.Accept = 0x32; //Accept类型application/json Auth_CB.Content_Format = 0x32; //Content_Format类型application/json sprintf(Auth_CB.payload,"%s",Certification); //认证的时候,需要上报的认证信息数据 memset(&Data_CB,0,sizeof(IOTCoAP_CB)); //清空数据控制块缓冲区 sprintf(Data_CB.host,"%s",ServerIP); //主机名 Data_CB.port = 5683; //端口号 Data_CB.Accept = 0x32; //Accept类型application/json Data_CB.Content_Format = 0x32; //Content_Format类型application/json } /*----------------------------------------------------------*/ /*函数名:IOT中继宝盒coap服务器 认证报文 */ /*参 数:T:报文类型 CON报文,NON报文,ACK报文和RST报文 */ /*参 数:Code:功能码 GET、POST、PUT和DELETE */ /*参 数:coap_cb:IOT中继宝盒CoAP控制块 */ /*返回值:无 */ /*----------------------------------------------------------*/ void IoT_CoAP_Auth(unsigned char T, unsigned char Code, IOTCoAP_CB * coap_cb) { int i; unsigned char CoAP_head[4]; //报文头部,占用4个字节 unsigned char Host_Option[128]; //报文中主机名选项缓冲区 unsigned char Port_Option[3]; //报文中端口号选项缓冲区,占用3个字节 unsigned char Path_Option[128]; //报文中URL路径选项缓冲区 unsigned char Accept_Option[2]; //报文中Accept选项缓冲区,占用2个字节 unsigned char Content_Option[2]; //报文中Content-Format选项缓冲区,占用2个字节 unsigned char Payload[256]; //报文中payload数据缓冲区 int Host_Option_len; //报文中主机名选项的最终长度 int Path_Option_len; //报文中路径选项的最终长度 int Payload_len; //报文中数据的最终长度 CoAP_head[0] = 0x40|T; //T:只有4中选项可选,代表4种类型的报文,一定要注意 CoAP_head[1] = Code; //Code:功能方法,只有4种可选 CoAP_head[2] = Message_ID/256; //消息编码ID,每次都一样 CoAP_head[3] = Message_ID%256; //消息编码ID,每次都一样 Message_ID ++; //Message_ID加一,这样用的时候,都不一样 memset(Host_Option,0,128); //清空缓冲区 if(strlen(coap_cb->host)<=12){ //如果主机名长度小于等于12,进入该if分支 Host_Option[0] = 0x30 | strlen(coap_cb->host); //主机名选项的Option Delta和Option Length memcpy(&Host_Option[1],coap_cb->host,strlen(coap_cb->host)); //添加主机名选项的Option Value Host_Option_len = strlen(coap_cb->host) + 1; //报文中主机名选项的最终长度 }else if((strlen(coap_cb->host)>12)&&(strlen(coap_cb->host)<=268)){ //如果主机名长在13至268范围内,进入该分支 Host_Option[0] = 0x3D; //主机名选项的Option Delta和Option Length Host_Option[1] = strlen(coap_cb->host) - 13; //主机名选项的Option Length的扩展字节 memcpy(&Host_Option[2],coap_cb->host,strlen(coap_cb->host)); //添加主机名选项的Option Value Host_Option_len = strlen(coap_cb->host) + 2; //报文中主机名选项的最终长度 }else if(strlen(coap_cb->host)>268){ //如果主机名长度大于268了 Host_Option[0] = 0x3E; //主机名选项的Option Delta和Option Length Host_Option[1] = (strlen(coap_cb->host) - 269)/256; //主机名选项的Option Length的扩展字节 Host_Option[2] = (strlen(coap_cb->host) - 269)%256; //主机名选项的Option Length的扩展字节 memcpy(&Host_Option[3],coap_cb->host,strlen(coap_cb->host)); //添加主机名选项的Option Value Host_Option_len = strlen(coap_cb->host) + 3; //报文中主机名选项的最终长度 } Port_Option[0] = 0x42; //端口号选项的Option Delta和Option Length Port_Option[1] = (coap_cb->port)/256; //添加端口号选项的Option Value Port_Option[2] = (coap_cb->port)%256; //添加端口号选项的Option Value memset(Path_Option,0,128); //清空缓冲区 if(strlen(coap_cb->path)<=12){ //如果URL路径长度小于等于12,进入该if分支 Path_Option[0] = 0x40 | strlen(coap_cb->path); //URL路径选项的Option Delta和Option Length memcpy(&Path_Option[1],coap_cb->path,strlen(coap_cb->path)); //添加URL路径选项的Option Value Path_Option_len = strlen(coap_cb->path) + 1; //报文中URL路径选项的最终长度 }else if((strlen(coap_cb->path)>12)&&(strlen(coap_cb->path)<=268)){ //如果URL路径长在13至268范围内,进入该分支 Path_Option[0] = 0x4D; //URL路径选项的Option Delta和Option Length Path_Option[1] = strlen(coap_cb->path) - 13; //URL路径选项的Option Length的扩展字节 memcpy(&Path_Option[2],coap_cb->path,strlen(coap_cb->path)); //添加URL路径选项的Option Value Path_Option_len = strlen(coap_cb->path) + 2; //报文中URL路径选项的最终长度 }else if(strlen(coap_cb->path)>268){ //如果URL路径长度大于268了 Path_Option[0] = 0x4E; //URL路径选项的Option Delta和Option Length Path_Option[1] = (strlen(coap_cb->path) - 269)/256; //URL路径选项的Option Length的扩展字节 Path_Option[2] = (strlen(coap_cb->path) - 269)%256; //URL路径选项的Option Length的扩展字节 memcpy(&Path_Option[3],coap_cb->path,strlen(coap_cb->path)); //添加URL路径选项的Option Value Path_Option_len = strlen(coap_cb->path) + 3; //报文中URL路径选项的最终长度 } Accept_Option[0] = 0x11; //Accept选项的Option Delta和Option Length Accept_Option[1] = 0x32; //添加Accept选项的Option Value Content_Option[0] = 0x51; //Content-Format选项的Option Delta和Option Length Content_Option[1] = 0x32; //添加Content-Format选项的Option Value memset(Payload,0,256); //清空缓冲区 Payload[0] = 0xFF; //0xFF作为数据和前面报文内容的分割符 memcpy(&Payload[1],coap_cb->payload,strlen(coap_cb->payload)); //拷贝数据 Payload_len = strlen(coap_cb->payload) + 1; //数据缓冲区的最终长度 memset(CoAPpack,0,1024); //清空缓冲区 memcpy(&CoAPpack[0],CoAP_head,4); //拷贝4个字节的头部 memcpy(&CoAPpack[4],Host_Option,Host_Option_len); //拷贝主机名缓冲区 memcpy(&CoAPpack[4+Host_Option_len],Port_Option,3); //拷贝3个字节的端口号缓冲区 memcpy(&CoAPpack[4+Host_Option_len+3],Path_Option,Path_Option_len); //拷贝路径缓冲区 memcpy(&CoAPpack[4+Host_Option_len+3+Path_Option_len],Accept_Option,2); //拷贝Accept选项缓冲区 memcpy(&CoAPpack[4+Host_Option_len+3+Path_Option_len+2],Content_Option,2); //拷贝Content-Format选项缓冲区 memcpy(&CoAPpack[4+Host_Option_len+3+Path_Option_len+2+2],Payload,Payload_len); //拷贝数据缓冲区 CoAPpack_len = 4+Host_Option_len+3+Path_Option_len+2+2+Payload_len; //最终的报文长度 u1_printf("CoAP认证数据包数据如下:\r\n"); //串口输出信息 for(i=0;i<CoAPpack_len;i++) u1_printf("%02x ",CoAPpack[i]); //串口输出整个报文详细信息 u1_printf("\r\n\r\n"); //串口输出信息 } /*----------------------------------------------------------*/ /*函数名:IOT中继宝盒coap 数据上报报文 */ /*参 数:T:报文类型 CON报文,NON报文,ACK报文和RST报文 */ /*参 数:Code:功能码 GET、POST、PUT和DELETE */ /*参 数:coap_cb:IOT中继宝盒CoAP控制块 */ /*返回值:无 */ /*----------------------------------------------------------*/ void IoT_CoAP_Data(unsigned char T, unsigned char Code, IOTCoAP_CB * coap_cb) { int i; unsigned char CoAP_head[4]; //报文头部,占用4个字节 unsigned char Host_Option[128]; //报文中主机名选项缓冲区 unsigned char Port_Option[3]; //报文中端口号选项缓冲区,占用3个字节 unsigned char Path_Option[128]; //报文中URL路径选项缓冲区 unsigned char Accept_Option[2]; //报文中Accept选项缓冲区,占用2个字节 unsigned char Content_Option[2]; //报文中Content-Format选项缓冲区,占用2个字节 unsigned char IOT_2088[35]; //报文中IOT中继宝盒自定义的选项缓冲区,占用35个字节 unsigned char Payload[1024]; //报文中payload数据缓冲区 int Host_Option_len; //报文中主机名选项的最终长度 int Path_Option_len; //报文中主机名选项的最终长度 int Payload_len; //报文中数据的最终长度 int temp_len; CoAP_head[0] = 0x40|T; //T:只有4中选项可选,代表4种类型的报文,一定要注意 CoAP_head[1] = Code; //Code:功能方法,只有4种可选 CoAP_head[2] = Message_ID/256; //消息编码ID,每次都不能一样 CoAP_head[3] = Message_ID%256; //消息编码ID,每次都不能一样 Message_ID ++; //Message_ID加一,这样用的时候,都不一样 memset(Host_Option,0,128); //清空缓冲区 if(strlen(coap_cb->host)<=12){ //如果主机名长度小于等于12,进入该if分支 Host_Option[0] = 0x30 | strlen(coap_cb->host); //主机名选项的Option Delta和Option Length memcpy(&Host_Option[1],coap_cb->host,strlen(coap_cb->host)); //添加主机名选项的Option Value Host_Option_len = strlen(coap_cb->host) + 1; //报文中主机名选项的最终长度 }else if((strlen(coap_cb->host)>12)&&(strlen(coap_cb->host)<=268)){ //如果主机名长在13至268范围内,进入该分支 Host_Option[0] = 0x3D; //主机名选项的Option Delta和Option Length Host_Option[1] = strlen(coap_cb->host) - 13; //主机名选项的Option Length的扩展字节 memcpy(&Host_Option[2],coap_cb->host,strlen(coap_cb->host)); //添加主机名选项的Option Value Host_Option_len = strlen(coap_cb->host) + 2; //报文中主机名选项的最终长度 }else if(strlen(coap_cb->host)>268){ //如果主机名长度大于268了 Host_Option[0] = 0x3E; //主机名选项的Option Delta和Option Length Host_Option[1] = (strlen(coap_cb->host) - 269)/256; //主机名选项的Option Length的扩展字节 Host_Option[2] = (strlen(coap_cb->host) - 269)%256; //主机名选项的Option Length的扩展字节 memcpy(&Host_Option[3],coap_cb->host,strlen(coap_cb->host)); //添加主机名选项的Option Value Host_Option_len = strlen(coap_cb->host) + 3; //报文中主机名选项的最终长度 } Port_Option[0] = 0x42; //端口号选项的Option Delta和Option Length Port_Option[1] = (coap_cb->port)/256; //添加端口号选项的Option Value Port_Option[2] = (coap_cb->port)%256; //添加端口号选项的Option Value memset(Path_Option,0,128); //清空缓冲区 Path_Option_len = 0; //PATH选项长度变量清零 for(i=0;i<TOPIC_PATH_NUM;i++){ if(strlen(Data_Path[i])<=12){ //如果URL路径长度小于等于12,进入该if分支 Path_Option[0+Path_Option_len] = 0x00 | strlen(Data_Path[i]); //URL路径选项的Option Delta和Option Length memcpy(&Path_Option[1+Path_Option_len],Data_Path[i],strlen(Data_Path[i])); //添加URL路径选项的Option Value Path_Option_len += strlen(Data_Path[i]) + 1; //报文中URL路径选项的最终长度 }else if((strlen(Data_Path[i])>12)&&(strlen(Data_Path[i])<=268)){ //如果URL路径长在13至268范围内,进入该分支 Path_Option[0+Path_Option_len] = 0x0D; //URL路径选项的Option Delta和Option Length Path_Option[1+Path_Option_len] = strlen(Data_Path[i]) - 13; //URL路径选项的Option Length的扩展字节 memcpy(&Path_Option[2+Path_Option_len],Data_Path[i],strlen(Data_Path[i])); //添加URL路径选项的Option Value Path_Option_len += strlen(Data_Path[i]) + 2; //报文中URL路径选项的最终长度 }else if(strlen(Data_Path[i])>268){ //如果URL路径长度大于268了 Path_Option[0+Path_Option_len] = 0x0E; //URL路径选项的Option Delta和Option Length Path_Option[1+Path_Option_len] = (strlen(Data_Path[i]) - 269)/256; //URL路径选项的Option Length的扩展字节 Path_Option[2+Path_Option_len] = (strlen(Data_Path[i]) - 269)%256; //URL路径选项的Option Length的扩展字节 memcpy(&Path_Option[3+Path_Option_len],Data_Path[i],strlen(Data_Path[i])); //添加URL路径选项的Option Value Path_Option_len += strlen(Data_Path[i]) + 3; //报文中URL路径选项的最终长度 } } Path_Option[0]|=0x40; //路径段1要或0X40 Accept_Option[0] = 0x11; //Accept选项的Option Delta和Option Length Accept_Option[1] = 0x32; //添加Accept选项的Option Value Content_Option[0] = 0x51; //Content-Format选项的Option Delta和Option Length Content_Option[1] = 0x32; //添加Content-Format选项的Option Value IOT_2088[0] = 0xED; //IOT中继宝盒coap服务器自定义的选项的Option Delta和Option Length IOT_2088[1] = 0x07; //IOT中继宝盒coap服务器自定义的选项的Option Delta的扩展字节 IOT_2088[2] = 0x0A; //IOT中继宝盒coap服务器自定义的选项的Option Delta的扩展字节 IOT_2088[3] = 0x12; //IOT中继宝盒coap服务器自定义的选项的Option Length的扩展字节 memcpy(&IOT_2088[4],coap_cb->auth_token,strlen(coap_cb->auth_token)); //拷贝数据,认证时服务器下发的token memset(Payload,0,1024); //清空缓冲区 Payload[0] = 0xFF; //0xFF作为数据和前面报文内容的分割符 char *uploadData; memset(uploadData, '\0', strlen(uploadData)); //清空缓冲区 sprintf(uploadData,"%s",coap_cb->payload); //准备要加密的发给服务器的明文 temp_len = strlen(uploadData); //计算要加密的明文的长度 if((temp_len%16)!=0){ //判断是否是32的整数倍,如果不是进入if for(i=0;i<((temp_len/16)+1)*16;i++){ if(uploadData[i]==0x00) uploadData[i]=16-temp_len%16; } temp_len = ((temp_len/16)+1)*16; } //对拼接的上报报文为json格式字符串base64加密 char secretbody[256]={0}; //base64加密:后的报文内容 int secretbodylen; base64_encode(uploadData,strlen(uploadData), secretbody,&secretbodylen); //对上传报文base64加密 *(&Payload[1])=*secretbody; Payload_len = temp_len + 1; //数据缓冲区的最终长度. memset(CoAPpack,0,1024); //清空缓冲区 memcpy(&CoAPpack[0],CoAP_head,4); //拷贝4个字节的头部 memcpy(&CoAPpack[4],Host_Option,Host_Option_len); //拷贝主机名缓冲区 memcpy(&CoAPpack[4+Host_Option_len],Port_Option,3); //拷贝3个字节的端口号缓冲区 memcpy(&CoAPpack[4+Host_Option_len+3],Path_Option,Path_Option_len); //拷贝路径缓冲区 memcpy(&CoAPpack[4+Host_Option_len+3+Path_Option_len],Accept_Option,2); //拷贝Accept选项缓冲区 memcpy(&CoAPpack[4+Host_Option_len+3+Path_Option_len+2],Content_Option,2); //拷贝Content-Format选项缓冲区 memcpy(&CoAPpack[4+Host_Option_len+3+Path_Option_len+2+2],IOT_2088,35); //拷贝IOT中继宝盒coap服务器自定义的选项缓冲区 memcpy(&CoAPpack[4+Host_Option_len+3+Path_Option_len+2+2+35+18],Payload,Payload_len); //拷贝数据缓冲区 CoAPpack_len = 4+Host_Option_len+3+Path_Option_len+2+2+35+18+Payload_len; //最终的报文长度 u1_printf("CoAP数据包数据如下:\r\n"); //串口输出信息 for(i=0;i<CoAPpack_len;i++) u1_printf("%02x ",CoAPpack[i]); //串口输出整个报文详细信息 u1_printf("\r\n\r\n"); //串口输出信息 }
sim7020.h
/*-------------------------------------------------*/ /* */ /* 实现SIM7020功能的头文件 */ /* */ /*-------------------------------------------------*/ #ifndef __SIM7020_H #define __SIM7020_H #define POWER_KEY(x) GPIO_WriteBit(GPIOB, GPIO_Pin_0, (BitAction)x) //PB0 控制sim7020模块的开关机按键 #define RESET_KEY(x) GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)x) //PA7 控制sim7020模块的复位按键 #define POWER_STA GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) //PB1的电平,可以判断sim7020是开机还是关机 #define SIM7020_printf u2_printf //串口2控制 7020模块 #define SIM7020_RxCounter USART2_RxCounter //串口2控制 7020模块 #define SIM7020_RX_BUF USART2_RxBuff //串口2控制 7020模块 #define SIM7020_RXBUFF_SIZE USART2_RXBUFF_SIZE //串口2控制 7020模块 extern char DNSIP[128]; void SIM_PwrKeyInit(void); void SIM_PwrStaInit(void); void SIM_RstKeyInit(void); char SIM_PowerON(void); char SIM7020_ATTest_Cmd(int counter); char SIM7020_CPIN_Cmd(int timeout); char SIM7020_CGREG_Cmd(int timeout); char SIM7020_CGACT_Cmd(int timeout); char SIM7020_CSQ_Cmd(int timeout); char SIM7020_CGCONTRDP_Cmd(int timeout); char SIM7020_IPR_Cmd(int bound, int timeout); char SIM7020_PSM_OFF(int timeout); char SIM7020_eDRX_OFF(int timeout); char SIM7020_Init(void); void SIM7020_Hex_to_Str(char *data, int data_len, char *out, int out_len); void SIM7020_Str_to_Hex(char *data, int data_len, char *out, int out_len); int SIM7020_StrNum_to_HexNum(char *data, int data_len); char SIM7020_DNS(unsigned char * domainname, int timeout); char SIM7020_CCOAPNEW(int timeout); char SIM7020_CCOAPDEL(int timeout); char SIM7020_CCOASend_Auth(int timeout,unsigned char *data, int data_len); char SIM7020_CCOASend_Data(int timeout,unsigned char *data, int data_len); char SIM7020_EnterPSM(int timeout); char SIM7020_ExitPSM(int timeout); #endif
sim7020.c
/*-------------------------------------------------*/ /* */ /* 实现SIM7020功能的源文件 */ /* */ /*-------------------------------------------------*/ #include "stm32l1xx.h" //包含需要的头文件 #include "main.h" //包含需要的头文件 #include "sim7020.h" //包含需要的头文件 #include "delay.h" //包含需要的头文件 #include "usart1.h" //包含需要的头文件 #include "usart2.h" //包含需要的头文件 #include "led.h" //包含需要的头文件 #include "coap.h" //包含需要的头文件 #include "eeprom.h" //包含需要的头文件 char DNSIP[128]; //DNS解析后的IP char data_temp1[2048]; //处理数据时,需要用的缓冲区 char data_temp2[2048]; //处理数据时,需要用的缓冲区 /*-------------------------------------------------*/ /*函数名:初始化sim7020开关机IO */ /*参 数:无 */ /*返回值:无 */ /*-------------------------------------------------*/ void SIM_PwrKeyInit(void) { GPIO_InitTypeDef GPIO_InitStructure; //定义一个设置GPIO的变量 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); //使能GPIOB端口时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //准备设置PB0 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //输出模式 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推免输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_400KHz; //400K速度 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //下拉 GPIO_Init(GPIOB, &GPIO_InitStructure); //设置PB0 POWER_KEY(0); //初始化的时候设置成低电平 } /*-------------------------------------------------*/ /*函数名:初始化sim7020开关机状态IO */ /*参 数:无 */ /*返回值:无 */ /*-------------------------------------------------*/ void SIM_PwrStaInit(void) { GPIO_InitTypeDef GPIO_InitStructure; //定义一个设置GPIO的变量 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); //使能GPIOB端口时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //准备设置PB1 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; //输入模式 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_400KHz; //400K速度 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //下拉 GPIO_Init(GPIOB, &GPIO_InitStructure); //设置PB1 } /*-------------------------------------------------*/ /*函数名:初始化sim7020复位IO */ /*参 数:无 */ /*返回值:无 */ /*-------------------------------------------------*/ void SIM_RstKeyInit(void) { GPIO_InitTypeDef GPIO_InitStructure; //定义一个设置GPIO的变量 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); //使能GPIO7端口时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //准备设置PA7 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; //输入模式 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_400KHz; //400K速度 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //下拉 GPIO_Init(GPIOA, &GPIO_InitStructure); //设置PA7 RESET_KEY(0); //初始化的时候设置成低电平 } /*-------------------------------------------------*/ /*函数名:开机 */ /*参 数:无 */ /*返回值:无 */ /*------------------------------------------------*/ char SIM_PowerON(void) { SIM_PwrKeyInit(); //初始化sim7020开关机IO SIM_PwrStaInit(); //初始化sim7020开关机状态IO SIM_RstKeyInit(); //初始化sim7020复位IO if(POWER_STA==0){ //如果PB1是低电平,表示目前处于关机状态 u1_printf("\r\n目前处于关机状态,准备开机\r\n"); //串口输出信息 POWER_KEY(1); //先拉高PB0 Delay_Ms(800); //延时 POWER_KEY(0); //再拉低PB0,开机 }else{ //反之PB1是高电平,表示目前处于开机状态 u1_printf("\r\n目前处于开机状态,准备重启\r\n"); //串口输出信息 POWER_KEY(1); //先拉高PB0 Delay_Ms(1200); //延时 POWER_KEY(0); //再拉低PB0 Delay_Ms(2000); //延时 POWER_KEY(1); //先拉高PB0 Delay_Ms(800); //延时 POWER_KEY(0); //再拉低PB0,开机 } Delay_Ms(2000); //延时 if(POWER_STA){ //如果PB1是低电平,表示开机或是重启成功 u1_printf("开机成功\r\n"); //串口输出信息 }else{ //反之PB1是高电平,表示开始或是重启失败 u1_printf("开机失败\r\n"); //串口输出信息 return 1; //返回1,表示失败 } return 0; //返回0,表示成功 } /*-------------------------------------------------*/ /*函数名:AT测试指令 */ /*参 数:timeout:超时时间(100ms的倍数) */ /*返回值:0:正确 其他:错误 */ /*-------------------------------------------------*/ char SIM7020_ATTest_Cmd(int timeout) { while(timeout--){ //等待超时时间到0 SIM7020_RxCounter=0; //7020接收数据量变量清零 memset(SIM7020_RX_BUF,0,SIM7020_RXBUFF_SIZE); //清空7020接收缓冲区 SIM7020_printf("AT\r\n"); //发送指令 u1_printf("%d ",timeout); //串口输出现在的剩余超时时间 Delay_Ms(100); //延时100ms if(strstr(SIM7020_RX_BUF,"OK")) //如果接收到OK表示指令成功 break; //主动跳出while循环 } u1_printf("\r\n"); //串口输出信息 if(timeout<=0)return 1; //如果timeout<=0,说明超时时间到零了,也没能收到OK,返回1,表示错误 return 0; //返回0,正确,说明收到OK,通过break主动跳出while } /*-------------------------------------------------*/ /*函数名:检查SIM卡状态指令 */ /*参 数:timeout:超时时间(1s的倍数) */ /*返回值:0:正确 其他:错误 */ /*-------------------------------------------------*/ char SIM7020_CPIN_Cmd(int timeout) { SIM7020_RxCounter=0; //7020接收数据量变量清零 memset(SIM7020_RX_BUF,0,SIM7020_RXBUFF_SIZE); //清空7020接收缓冲区 SIM7020_printf("AT+CPIN?\r\n"); //发送指令 while(timeout--){ //等待超时时间到0 u1_printf("%d ",timeout); //串口输出现在的剩余超时时间 Delay_Ms(1000); //延时1s if(strstr(SIM7020_RX_BUF,"READY")) //如果接收到READY表示指令成功 break; //主动跳出while循环 } u1_printf("\r\n"); //串口输出信息 if(timeout<=0)return 1; //如果timeout<=0,说明超时时间到零了,也没能收到READY,返回1,表示错误 return 0; //返回0,正确,说明收到READY,通过break主动跳出while } /*-------------------------------------------------*/ /*函数名:检查网络附着指令 */ /*参 数:timeout:超时时间(1s的倍数) */ /*返回值:0:正确 其他:错误 */ /*-------------------------------------------------*/ char SIM7020_CGREG_Cmd(int timeout) { while(timeout--){ //等待超时时间到0 SIM7020_RxCounter=0; //7020接收数据量变量清零 memset(SIM7020_RX_BUF,0,SIM7020_RXBUFF_SIZE); //清空7020接收缓冲区 SIM7020_printf("AT+CGREG?\r\n"); //发送指令 u1_printf("%d ",timeout); //串口输出现在的剩余超时时间 Delay_Ms(1000); //延时1s if(strstr(SIM7020_RX_BUF,"+CGREG: 0,1")) //如果接收到+CGREG: 0,1表示指令成功 break; //主动跳出while循环 } u1_printf("\r\n"); //串口输出信息 if(timeout<=0)return 1; //如果timeout<=0,说明超时时间到零了,也没能收到+CGREG: 0,1,返回1,表示错误 return 0; //返回0,正确,说明收到+CGREG: 0,1,通过break主动跳出while } /*-------------------------------------------------*/ /*函数名:检查PDN激活状态 */ /*参 数:timeout:超时时间(1s的倍数) */ /*返回值:0:正确 其他:错误 */ /*-------------------------------------------------*/ char SIM7020_CGACT_Cmd(int timeout) { while(timeout--){ //等待超时时间到0 SIM7020_RxCounter=0; //7020接收数据量变量清零 memset(SIM7020_RX_BUF,0,SIM7020_RXBUFF_SIZE); //清空7020接收缓冲区 SIM7020_printf("AT+CGACT?\r\n"); //发送指令 u1_printf("%d ",timeout); //串口输出现在的剩余超时时间 Delay_Ms(1000); //延时1s if(strstr(SIM7020_RX_BUF,"+CGACT: 1,1")) //如果接收到+CGACT: 1,1表示指令成功 break; //主动跳出while循环 } u1_printf("\r\n"); //串口输出信息 if(timeout<=0)return 1; //如果timeout<=0,说明超时时间到零了,也没能收到+CGACT: 1,1,返回1,表示错误 return 0; //返回0,正确,说明收到+CGACT: 1,1,通过break主动跳出while } /*-------------------------------------------------*/ /*函数名:检查信号质量指令 */ /*参 数:timeout:超时时间(100ms的倍数) */ /*返回值:0:正确 其他:错误 */ /*-------------------------------------------------*/ char SIM7020_CSQ_Cmd(int timeout) { char temp[20],csq[20]; //临时缓冲区 SIM7020_RxCounter=0; //7020接收数据量变量清零 memset(SIM7020_RX_BUF,0,SIM7020_RXBUFF_SIZE); //清空7020接收缓冲区 memset(temp,0,20); //清空缓冲区 memset(csq,0,20); //清空缓冲区 SIM7020_printf("AT+CSQ\r\n"); //发送指令 while(timeout--){ //等待超时时间到0 u1_printf("%d ",timeout); //串口输出现在的剩余超时时间 Delay_Ms(100); //延时100ms if(strstr(SIM7020_RX_BUF,"OK")) //如果接收到OK表示指令成功 break; //主动跳出while循环 } u1_printf("\r\n"); //串口输出信息 if(timeout<=0) return 99; //如果timeout<=0,说明超时时间到零了,也没能收到OK,返回99,表示错误 else{ //反之,表示正确,说明收到OK,通过break主动跳出while sscanf(SIM7020_RX_BUF,"%[^ ] %[^,],%[^\r]",temp,csq,temp); //格式化搜索,信号质量 } return SIM7020_StrNum_to_HexNum(csq,strlen(csq)); } /*-------------------------------------------------*/ /*函数名:查询网络下发APN和分配的IP地址指令 */ /*参 数:timeout:超时时间(100ms的倍数) */ /*返回值:0:正确 其他:错误 */ /*-------------------------------------------------*/ char SIM7020_CGCONTRDP_Cmd(int timeout) { char temp[40],apn[40],ip[40]; //临时缓冲区 SIM7020_RxCounter=0; //7020接收数据量变量清零 memset(SIM7020_RX_BUF,0,SIM7020_RXBUFF_SIZE); //清空7020接收缓冲区 memset(temp,0,40); //清空缓冲区 memset(apn,0,40); //清空缓冲区 memset(ip,0,40); //清空缓冲区 SIM7020_printf("AT+CGCONTRDP\r\n"); //发送指令 while(timeout--){ //等待超时时间到0 u1_printf("%d ",timeout); //串口输出现在的剩余超时时间 Delay_Ms(100); //延时100ms if(strstr(SIM7020_RX_BUF,"OK")) //如果接收到OK表示指令成功 break; //主动跳出while循环 } u1_printf("\r\n"); //串口输出信息 if(timeout<=0)return 1; //如果timeout<=0,说明超时时间到零了,也没能收到OK,返回1,表示错误 else{ //反之,表示正确,说明收到OK,通过break主动跳出while sscanf(SIM7020_RX_BUF,"%[^,],%[^,],%[^,],%[^\r]",temp,temp,apn,ip); //格式化搜索,apn和ip信息 u1_printf("APN:%s\r\n",apn); //串口输出信息 u1_printf("ip和子网掩码:%s\r\n",ip); //串口输出信息 } return 0; //返回0,正确, } /*-------------------------------------------------*/ /*函数名:锁定波特率指令 */ /*参 数:bound:波特率 */ /*参 数:timeout:超时时间(100ms的倍数) */ /*返回值:0:正确 其他:错误 */ /*-------------------------------------------------*/ char SIM7020_IPR_Cmd(int bound, int timeout) { SIM7020_RxCounter=0; //7020接收数据量变量清零 memset(SIM7020_RX_BUF,0,SIM7020_RXBUFF_SIZE); //清空7020接收缓冲区 SIM7020_printf("AT+IPR=%d\r\n",bound); //发送指令 while(timeout--){ //等待超时时间到0 u1_printf("%d ",timeout); //串口输出现在的剩余超时时间 Delay_Ms(100); //延时100ms if(strstr(SIM7020_RX_BUF,"OK")) //如果接收到OK表示指令成功 break; //主动跳出while循环 } u1_printf("\r\n"); //串口输出信息 if(timeout<=0)return 1; //如果timeout<=0,说明超时时间到零了,也没能收到OK,返回1,表示错误 return 0; //返回0,正确,说明收到OK,通过break主动跳出while } /*-------------------------------------------------*/ /*函数名:关闭PSM模式指令 */ /*参 数:timeout:超时时间(1s的倍数) */ /*返回值:0:正确 其他:错误 */ /*-------------------------------------------------*/ char SIM7020_PSM_OFF(int timeout) { while(timeout--){ //等待超时时间到0 SIM7020_RxCounter=0; //7020接收数据量变量清零 memset(SIM7020_RX_BUF,0,SIM7020_RXBUFF_SIZE); //清空7020接收缓冲区 SIM7020_printf("AT+CPSMS=0\r\n"); //发送指令 u1_printf("%d ",timeout); //串口输出现在的剩余超时时间 Delay_Ms(1000); //延时1s if(strstr(SIM7020_RX_BUF,"OK")) //如果接收到OK表示指令成功 break; //主动跳出while循环 } u1_printf("\r\n"); //串口输出信息 if(timeout<=0)return 1; //如果timeout<=0,说明超时时间到零了,也没能收到OK,返回1,表示错误 return 0; //返回0,正确,说明收到OK,通过break主动跳出while } /*-------------------------------------------------*/ /*函数名:关闭eDRX模式指令 */ /*参 数:timeout:超时时间(1s的倍数) */ /*返回值:0:正确 其他:错误 */ /*-------------------------------------------------*/ char SIM7020_eDRX_OFF(int timeout) { while(timeout--){ //等待超时时间到0 SIM7020_RxCounter=0; //7020接收数据量变量清零 memset(SIM7020_RX_BUF,0,SIM7020_RXBUFF_SIZE); //清空7020接收缓冲区 SIM7020_printf("AT+CEDRXS=0\r\n"); //发送指令 u1_printf("%d ",timeout); //串口输出现在的剩余超时时间 Delay_Ms(1000); //延时1s if(strstr(SIM7020_RX_BUF,"OK")) //如果接收到OK表示指令成功 break; //主动跳出while循环 } u1_printf("\r\n"); //串口输出信息 if(timeout<=0)return 1; //如果timeout<=0,说明超时时间到零了,也没能收到OK,返回1,表示错误 else return 0; //返回0,正确,说明收到OK,通过break主动跳出while } /*-------------------------------------------------*/ /*函数名:SIM7020 初始化 */ /*参 数:无 */ /*返回值:无 */ /*-------------------------------------------------*/ char SIM7020_Init(void) { char num,csq; SIM_PowerON(); //开机或重启 u1_printf("发送测试指令,等待串口正常... ...\r\n"); //串口输出信息 if(SIM7020_ATTest_Cmd(50)){ //超时单位100ms,超时时间5s u1_printf("测试指令超时,准备重启\r\n"); //串口输出信息 return 1; //返回1 }else u1_printf("测试指令成功\r\n"); //串口输出信息 u1_printf("准备检查SIM卡状态... ...\r\n"); //串口输出信息 if(SIM7020_CPIN_Cmd(10)){ //超时单位1s,超时时间10s u1_printf("检查SIM卡状态超时,准备重启\r\n"); //串口输出信息 return 2; //返回2 }else u1_printf("SIM卡状态正常\r\n"); //串口输出信息 num = 5; //最多查询5次 while(num--){ //循环查询 u1_printf("准备检查信号质量... ...\r\n"); //串口输出信息 csq = SIM7020_CSQ_Cmd(50); //超时单位100ms,超时时间5s if((csq>0)&&(csq<=31)){ //按到信号范围是否正确 u1_printf("信号强度范围0~31,99表示无信号\r\n"); //串口输出信息 u1_printf("信号强度:%d\r\n",csq); //串口输出信息 break; //跳出while } Delay_Ms(1000); //延时1s } if(num<=0)return 3; //如果num<=0,说明超过最大查询次数,也没成功,返回3,表示错误 else u1_printf("检查信号质量成功\r\n"); //串口输出信息 u1_printf("准备查询网络附着... ...\r\n"); //串口输出信息 if(SIM7020_CGREG_Cmd(10)){ //超时单位1s,超时时间10s u1_printf("查询网络附着超时,准备重启\r\n"); //串口输出信息 return 4; //返回4 }else u1_printf("网络附着成功\r\n"); //串口输出信息 u1_printf("准备查询PDN 激活... ...\r\n"); //串口输出信息 if(SIM7020_CGACT_Cmd(10)){ //超时单位1s,超时时间10s u1_printf("查询PDN 激活超时,准备重启\r\n"); //串口输出信息 return 5; //返回5 }else u1_printf("PDN 激活成功\r\n"); //串口输出信息 u1_printf("准备查询APN和分配的IP地址... ...\r\n"); //串口输出信息 if(SIM7020_CGCONTRDP_Cmd(50)){ //超时单位100ms,超时时间5s u1_printf("查询APN和分配的IP地址超时,准备重启\r\n"); //串口输出信息 return 6; //返回6 } u1_printf("准备关闭PCM模式... ...\r\n"); //串口输出信息 if(SIM7020_PSM_OFF(20)){ //超时单位1s,超时时间20s u1_printf("关闭PCM模式超时,准备重启\r\n"); //串口输出信息 return 7; //返回7 }else u1_printf("关闭PCM模式成功\r\n"); //串口输出信息 u1_printf("准备关闭eDRX模式... ...\r\n"); //串口输出信息 if(SIM7020_eDRX_OFF(20)){ //超时单位1s,超时时间20s u1_printf("关闭eDRX模式超时,准备重启\r\n"); //串口输出信息 return 8; //返回8 }else u1_printf("关闭eDRX模式成功\r\n"); //串口输出信息 u1_printf("准备锁定波特率... ...\r\n"); //串口输出信息 if(SIM7020_IPR_Cmd(115200,100)){ //超时单位100ms,超时时间10s u1_printf("锁定波特率超时,准备重启\r\n"); //串口输出信息 return 9; //返回9 }else u1_printf("进锁定波特率成功\r\n"); //串口输出信息 u1_printf("准备DNS解析服务器域名... ...\r\n"); //串口输出信息 if(SIM7020_DNS(ServerIP,10)){ //超时单位1s,超时时间10s u1_printf("解析服务器域名超时,准备重启\r\n"); //串口输出信息 return 10; //返回10 }else u1_printf("解析服务器域名成功\r\n"); //串口输出信息 Sta_flag = 0; //配置完成,模块状态=1,处于处理数据状态 SIM7020_RxCounter=0; //7020接收数据量变量清零 memset(SIM7020_RX_BUF,0,SIM7020_RXBUFF_SIZE); //清空7020接收缓冲区 return 0; //正确返回0 } /*-------------------------------------------------*/ /*函数名:把16进制数据转换成字符串 */ /*参 数:data:需要转换的数据 */ /*参 数:data_len:需要转换的数据长度 */ /*参 数:out:存放转换后的数据 */ /*参 数:out_len:缓冲区长度 */ /*返回值:无 */ /*-------------------------------------------------*/ void SIM7020_Hex_to_Str(char *data, int data_len, char *out, int out_len) { char temp[2]; int i; memset(out,0,out_len); //清空缓冲区 for(i=0;i<data_len;i++){ //for循环 sprintf(temp,"%02X",data[i]); //转化数据,转成16进制字符串 strcat(out,temp); //追加到out缓冲区 } } /*-------------------------------------------------*/ /*函数名:把字符串转换成16进制数据 */ /*参 数:data:需要转换的数据 */ /*参 数:data_len:需要转换的数据长度 */ /*参 数:out:存放转换后的数据 */ /*参 数:out_len:缓冲区长度 */ /*返回值:无 */ /*-------------------------------------------------*/ void SIM7020_Str_to_Hex(char *data, int data_len, char *out, int out_len) { int i; memset(out,0,out_len); //清空临时缓冲区 for(i=0;i<data_len/2;i++){ //for循环 if(((data[2*i]>='0')&&(data[2*i]<='9'))&&((data[2*i+1]>='0')&&(data[2*i+1]<='9'))) out[i] = (data[2*i]-0x30)*16 + (data[2*i+1]-0x30); else if(((data[2*i]>='0')&&(data[2*i]<='9'))&&((data[2*i+1]>='A')&&(data[2*i+1]<='F'))) out[i] = (data[2*i]-0x30)*16 + (data[2*i+1]-0x37); else if(((data[2*i]>='0')&&(data[2*i]<='9'))&&((data[2*i+1]>='a')&&(data[2*i+1]<='f'))) out[i] = (data[2*i]-0x30)*16 + (data[2*i+1]-0x57); else if(((data[2*i]>='A')&&(data[2*i]<='F'))&&((data[2*i+1]>='0')&&(data[2*i+1]<='9'))) out[i] = (data[2*i]-0x37)*16 + (data[2*i+1]-0x30); else if(((data[2*i]>='a')&&(data[2*i]<='f'))&&((data[2*i+1]>='0')&&(data[2*i+1]<='9'))) out[i] = (data[2*i]-0x57)*16 + (data[2*i+1]-0x30); else if(((data[2*i]>='A')&&(data[2*i]<='F'))&&((data[2*i+1]>='A')&&(data[2*i+1]<='F'))) out[i] = (data[2*i]-0x37)*16 + (data[2*i+1]-0x37); else if(((data[2*i]>='a')&&(data[2*i]<='f'))&&((data[2*i+1]>='a')&&(data[2*i+1]<='f'))) out[i] = (data[2*i]-0x57)*16 + (data[2*i+1]-0x57); } } /*-------------------------------------------------*/ /*函数名:把字符串数字转换成16进制数字 */ /*参 数:data:需要转换的数据 */ /*参 数:data_len:需要转换的数据位数 */ /*返回值:转换后的数字 */ /*-------------------------------------------------*/ int SIM7020_StrNum_to_HexNum(char *data, int data_len) { int num; num = 0; switch(data_len){ case 1: num += data[0] - 0x30; break; case 2: num += (data[0] - 0x30)*10; num += (data[1] - 0x30); break; case 3: num += (data[0] - 0x30)*100; num += (data[1] - 0x30)*10; num += (data[2] - 0x30); break; case 4: num += (data[0] - 0x30)*1000; num += (data[1] - 0x30)*100; num += (data[2] - 0x30)*10; num += (data[3] - 0x30); break; } return num; } /*-------------------------------------------------*/ /*函数名:解析域名 */ /*参 数:domainname:域名 */ /*参 数:timeout:超时时间(1s的倍数) */ /*返回值:0:正确 其他:错误 */ /*-------------------------------------------------*/ char SIM7020_DNS(unsigned char * domainname, int timeout) { char *ptr; char temp[64]; SIM7020_RxCounter=0; //7020接收数据量变量清零 memset(SIM7020_RX_BUF,0,SIM7020_RXBUFF_SIZE); //清空7020接收缓冲区 SIM7020_printf("AT+CDNSGIP=\"%s\"\r\n",domainname); //发送指令 while(timeout--){ //等待超时时间到0 u1_printf("%d ",timeout); //串口输出现在的剩余超时时间 Delay_Ms(1000); //延时1s ptr = strstr(SIM7020_RX_BUF,"+CDNSGIP: 1"); //搜索+CDNSGIP: 1 if(ptr!=NULL) //如果接收到","表示指令成功 break; //主动跳出while循环 } u1_printf("\r\n"); //串口输出信息 if(timeout<=0)return 1; //如果timeout<=0,说明超时时间到零了,也没能收到",",返回1,表示错误 else{ //反之,表示正确,说明收到",",通过break主动跳出while memset(DNSIP,0,128); //清空缓冲区 sscanf(ptr,"%[^\"]\"%[^\"]\"%[^\"]\"%[^\"]",temp,temp,temp,DNSIP); //格式化搜索ip信息 u1_printf("DNS解析到的IP:%s\r\n",DNSIP); //串口输出信息 EEPROM_WriteData(0,(unsigned int *)DNSIP,4); //内部EEprom写入数据 } return 0; //正确返回0 } /*-------------------------------------------------*/ /*函数名:创建coAP客户端,连接服务器 */ /*参 数:timeout:超时时间(1s的倍数) */ /*返回值:0:正确 其他:错误 */ /*-------------------------------------------------*/ char SIM7020_CCOAPNEW(int timeout) { SIM7020_RxCounter=0; //7020接收数据量变量清零 memset(SIM7020_RX_BUF,0,SIM7020_RXBUFF_SIZE); //清空7020接收缓冲区 SIM7020_printf("AT+CCOAPNEW=\"%s\",%d,1\r\n",DNSIP,ServerPort); //发送指令 while(timeout--){ //等待超时时间到0 u1_printf("%d ",timeout); //串口输出现在的剩余超时时间 Delay_Ms(1000); //延时1s if(strstr(SIM7020_RX_BUF,"OK")) //如果接收到OK表示指令成功 break; //主动跳出while循环 } u1_printf("\r\n"); //串口输出信息 if(timeout<=0)return 1; //如果timeout<=0,说明超时时间到零了,也没能收到OK,返回1,表示错误 return 0; //正确返回0,说明收到OK,通过break主动跳出while } /*-------------------------------------------------*/ /*函数名:关闭coAP客户端,连接服务器 */ /*参 数:timeout:超时时间(100ms的倍数) */ /*返回值:0:正确 其他:错误 */ /*-------------------------------------------------*/ char SIM7020_CCOAPDEL(int timeout) { SIM7020_RxCounter=0; //7020接收数据量变量清零 memset(SIM7020_RX_BUF,0,SIM7020_RXBUFF_SIZE); //清空7020接收缓冲区 SIM7020_printf("AT+CCOAPDEL=1\r\n"); //发送指令 while(timeout--){ //等待超时时间到0 u1_printf("%d ",timeout); //串口输出现在的剩余超时时间 Delay_Ms(100); //延时100ms if(strstr(SIM7020_RX_BUF,"OK")) //如果接收到OK表示指令成功 break; //主动跳出while循环 } u1_printf("\r\n"); //串口输出信息 if(timeout<=0)return 1; //如果timeout<=0,说明超时时间到零了,也没能收到OK,返回1,表示错误 return 0; //正确返回0,说明收到OK,通过break主动跳出while } /*-------------------------------------------------*/ /*函数名:coAP发送认证数据 包 */ /*参 数:timeout:超时时间(100ms的倍数) */ /*参 数:data:需要发送的数据 */ /*参 数:data_len:需要发送的数据位数 */ /*返回值:0:正确 其他:错误 */ /*-------------------------------------------------*/ char SIM7020_CCOASend_Auth(int timeout,unsigned char *data, int data_len) { int i; char *str; char token_temp[64]; char temp[64]; SIM7020_Hex_to_Str((char *)data,data_len,data_temp1,2048); //转化数据 SIM7020_RxCounter=0; //7020接收数据量变量清零 memset(SIM7020_RX_BUF,0,SIM7020_RXBUFF_SIZE); //清空7020接收缓冲区 SIM7020_printf("AT+CCOAPSEND=1,%d,\"%s\"\r\n",data_len,data_temp1); //发送数据指令 while(timeout--){ //等待超时时间到0 u1_printf("%d ",timeout); //串口输出现在的剩余超时时间 Delay_Ms(100); //延时100ms if(strstr(SIM7020_RX_BUF,"+CCOAPNMI:")) //如果接收到+CCOAPNMI:表示指令成功,并且服务器回复了数据 break; //主动跳出while循环 } u1_printf("\r\n"); //串口输出信息 if(timeout<=0)return 1; //如果timeout<=0,说明超时时间到零了,也没能收到+CCOAPNMI:,返回1,表示错误 else{ //反之,表示正确,说明收到+CCOAPNMI:,通过break主动跳出while str = strstr(SIM7020_RX_BUF,"+CCOAPNMI:"); //找到+CCOAPNMI:开始的位置 memset(data_temp1,0,2048); //清空缓冲区 sscanf(str,"%[^,],%[^,],%[^\r]",temp,temp,data_temp1); //格式化搜索,保存数据信息 SIM7020_Str_to_Hex(data_temp1,strlen(data_temp1),data_temp2,2048); //转换数据 memset(token_temp,0,64); //清空缓冲区 str =data_temp2; if(str!=NULL){ //如果找到了,进入if u1_printf("服务器发来的数据:%s\r\n\r\n",str); //串口输出信息 //sscanf(str,"{\"random\":\"%[^\"]\",\"seqOffset\":%[^,],\"token\":\"%[^\"]\"}",random_temp,seqoffset_temp,token_temp); //格式化搜索,保存数据信息 memcpy(Data_CB.auth_token,token_temp,strlen(token_temp)); //拷贝token u1_printf("token:%s\r\n",Data_CB.auth_token); //串口输出信息 u1_printf("key:"); //串口输出信息 for(i=0;i<16;i++) u1_printf("0x%02x ",Data_CB.auth_key[i]); //串口输出信息 u1_printf("\r\n\r\n"); //串口输出信息 }else{ //如果没找到,进入else u1_printf("数据格式错误,重启\r\n"); //串口输出信息 return 2; //返回2 } } return 0; //正确,返回0 } /*-------------------------------------------------*/ /*函数名:coAP发送数据 */ /*参 数:timeout:超时时间(100ms的倍数) */ /*参 数:data:需要发送的数据 */ /*参 数:data_len:需要发送的数据位数 */ /*返回值:0:正确 其他:错误 */ /*-------------------------------------------------*/ char SIM7020_CCOASend_Data(int timeout,unsigned char *data, int data_len) { char *str; SIM7020_Hex_to_Str((char *)data,data_len,data_temp1,2048); //转化数据 SIM7020_RxCounter=0; //7020接收数据量变量清零 memset(SIM7020_RX_BUF,0,SIM7020_RXBUFF_SIZE); //清空7020接收缓冲区 SIM7020_printf("AT+CCOAPSEND=1,%d,\"%s\"\r\n",data_len,data_temp1); //发送数据指令 while(timeout--){ //等待超时时间到0 u1_printf("%d ",timeout); //串口输出现在的剩余超时时间 Delay_Ms(100); //延时1s if(strstr(SIM7020_RX_BUF,"+CCOAPNMI:")) //如果接收到+CCOAPNMI:表示指令成功,并且服务器回复了数据 break; //主动跳出while循环 if(strstr(SIM7020_RX_BUF,"ERROR")) //如果接收到ERROR表示发送失败 return 1; //直接返回1 } u1_printf("\r\n"); //串口输出信息 if(timeout<=0)return 2; //如果timeout<=0,说明超时时间到零了,也没能收到+CCOAPNMI:,返回2,表示错误 else{ //反之,表示正确,说明收到+CCOAPNMI:,通过break主动跳出while str = strstr(SIM7020_RX_BUF,",60"); //找到,60的位置 if((str[3]!='4')&&(str[4]!='5')) //如果不是0x45表示错误 return 3; //返回3 else //反之是0x45表示正确 return 0; //返回0 } } /*-------------------------------------------------*/ /*函数名:进入PSM模式 */ /*参 数:timeout:超时时间(1s的倍数) */ /*返回值:0:正确 其他:错误 */ /*-------------------------------------------------*/ char SIM7020_EnterPSM(int timeout) { SIM7020_RxCounter=0; //7020接收数据量变量清零 memset(SIM7020_RX_BUF,0,SIM7020_RXBUFF_SIZE); //清空7020接收缓冲区 SIM7020_printf("AT+CPSMSTATUS=1\r\n"); //发送指令 while(timeout--){ //等待超时时间到0 u1_printf("%d ",timeout); //串口输出现在的剩余超时时间 Delay_Ms(1000); //延时1s if(strstr(SIM7020_RX_BUF,"OK")) //如果接收到OK表示指令成功,并且服务器回复了数据 break; //主动跳出while循环 } u1_printf("\r\n"); //串口输出信息 if(timeout<=0)return 1; //如果timeout<=0,说明超时时间到零了,也没能收到OK,返回1,表示错误 else{ //反之,表示正确,说明收到OK,通过break主动跳出while SIM7020_RxCounter=0; //7020接收数据量变量清零 memset(SIM7020_RX_BUF,0,SIM7020_RXBUFF_SIZE); //清空7020接收缓冲区 SIM7020_printf("AT+CPSMS=1,,,\"00011111\",\"00000101\"\r\n"); //发送指令 while(timeout--){ //等待超时时间到0 u1_printf("%d ",timeout); //串口输出现在的剩余超时时间 Delay_Ms(1000); //延时1s if(strstr(SIM7020_RX_BUF,"ENTER PSM")) //如果接收到ENTER PSM表示指令成功,并且服务器回复了数据 break; //主动跳出while循环 } u1_printf("\r\n"); //串口输出信息 if(timeout<=0)return 2; //如果timeout<=0,说明超时时间到零了,也没能收到ENTER PSM,返回2`,表示错误 else return 0; //反之,表示正确,说明收到ENTER PSM,通过break主动跳出while } } /*-------------------------------------------------*/ /*函数名:退出PSM模式 */ /*参 数:timeout:超时时间(1s的倍数) */ /*返回值:0:正确 其他:错误 */ /*-------------------------------------------------*/ char SIM7020_ExitPSM(int timeout) { SIM_PwrKeyInit(); //初始化sim7020开关机IO SIM_PwrStaInit(); //初始化sim7020开关机状态IO Delay_Ms(500); //延时 POWER_KEY(1); //先拉高PB0 Delay_Ms(1000); //延时 POWER_KEY(0); //再拉低PB0 while(timeout--){ //等待超时时间到0 u1_printf("%d ",timeout); //串口输出现在的剩余超时时间 Delay_Ms(1000); //延时1s if(strstr(SIM7020_RX_BUF,"EXIT PSM")) //如果接收到EXIT PSM表示退出PSM成功 break; //主动跳出while循环 if(strstr(SIM7020_RX_BUF,"*MATREADY: 1")) //如果接收到*MATREADY: 1表示退出PSM成功 break; //主动跳出while循环 if(strstr(SIM7020_RX_BUF,"+CFUN: 1")) //如果接收到+CFUN: 1表示退出PSM成功 break; //主动跳出while循环 if(strstr(SIM7020_RX_BUF,"+CPIN: READY")) //如果接收到+CPIN: READY表示退出PSM成功 break; //主动跳出while循环 } u1_printf("\r\n"); //串口输出信息 if(timeout<=0)return 1; //如果timeout<=0,说明超时时间到零了,也没能收到EXIT PSM,返回1,表示错误 else{ //反之,表示正确,说明收到EXIT PSM,通过break主动跳出while u1_printf("准备关闭PCM模式... ...\r\n"); //串口输出信息 if(SIM7020_PSM_OFF(10)){ //超时单位1s,超时时间10s u1_printf("关闭PCM模式超时,准备重启\r\n"); //串口输出信息 return 2; //返回 }else u1_printf("关闭PCM模式成功\r\n"); //串口输出信息 } return 0; }
coap协议接入STM例程打包源码进入宜联IOT中继宝盒主操作界面打开“IOT设备接口”窗口,选择对应的设备–设备接入端接口中对应的协议接入样例中下载。