六、coap协议接入开发指南

六、coap协议接入开发指南


1)、简介

本章节以设备侧开发板采用coap协议接入本IOT中继系统平台为例,主要实现从接入嵌入设备上报物模型属性数据。 设备上报报文数据采用字符串BASE64加密传输

2)、设备接入认证

用户名: 159xxxxxxxxIOT设备接入–设备接入配置 填写的连接账号

密码: XXXXXXIOT设备接入–设备接入配置 填写的连接口令

设备认证请求: 生成认证请求内容,根据设备id 、 连接账号、 连接密钥 、 连接时间戳组成的json字符串BASE64加密生成。 Post请求向COAP服务器设备认证请求地址(如后)提交认证请求报文获取设备认证通过token。

设备认证请求地址: coap://192.168.0.105:5683/coapApi/auth

请求参数说明:

Method请求方法。只支持POST方法

URLURL地址,取值: 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
3)、上报数据

上报报文请求地址: coap://192.168.0.105:5683/coapApi/44080000001111000020

请求参数属性说明:

参数说明
token设备连接认证通过返回的token 字符串(见前第2节所述的token)
payLoad报文字符串,上报设备属性数据标识,由上报的属性数据JSON字符串BASE64 加密生成
加密前报文上报属性数据json字符串内容
[
       {"propertiesId": "P_1695782510161", "dataValue": "110" },
       {"propertiesId": "P_1695888406309",  "dataValue": "12"},
         ...... ]
json字符串内容说明propertiesId上报设备属性数据ID标识,为设备物模型配置的属性ID标识 如上例黄色 "P_1695782510161" 、 "P_1695888406309"dataValue上报设备属性数据值,dataValue 为属性值标识 上例黄色 "110" 、 "12" 上报设备属性数据值
加密后的报文上报属性数据json字符串内容(范例)
W3sicHJvcGVydGllc0lkIjoiUF8xNjk1NzgyNTEwMTYxIiwiZGF0YVZhbHVlIjoiMTEwIn0seyJwcm9wZXJ0aWVzSWQiOiJQXzE2OTU4ODg0MDYzMDkiLCJkYXRhVmFsdWUiOiIxMiJ9XQ==

4)、Java客户端coap协议接入样例

  本例程以接入设备为Java客户端设备,接入本IOT中继平台使用。 此设备采用coap协议接入本IOT中继系统平台,主要实现从接入设备上报物模型属性数据。 设备侧根据业务需要周期性调用此接口向平台上报设备属性数据,例如:间隔10分钟上报设备温度、湿度等,本IOT平台coap协议接入只支持上报物模型属性数据。

a、 核心代码
java pom.xml 配置文件
<?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>
CoapClient.java
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);//发送请求



    }

}
b、java客户端例程源码下载

     coap协议接入JAVA客户端例程打包源码进入IOT中继宝盒主操作界面打开“IOT设备接口”窗口,选择对应的设备–设备接入端接口中对应的协议接入样例中下载。

5)、STM32芯片coap协议接入样例
a、简介:

本文档适用对象为嵌入式芯片设备,接入本IOT中继平台使用。 此设备采用coap协议接入本IOT中继系统平台,主要实现从接入设备上报物模型属性数据。 设备侧根据业务需要周期性调用此接口向平台上报设备属性数据,例如:间隔10分钟上报设备温度、湿度等,本IOT平台coap协议接入只支持上报物模型属性数据。

针对嵌入式设备, 上报设备属性数据报文由json字符串组成,数据报文对json字符串base64加密传输(本例程以**STM32L151C8T6A核心板+通讯模块sim7020c芯片+移动物联网卡**接入为例)。

嵌入式芯片C代码中接入参数配置

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终端绑定界面业务平台注册登记的子域名
b、核心代码

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;
}
c、范例代码下载

coap协议接入STM例程打包源码进入宜联IOT中继宝盒主操作界面打开“IOT设备接口”窗口,选择对应的设备–设备接入端接口中对应的协议接入样例中下载。



IOT中继宝盒 长按关注宜联科技公众号

QQ在线咨询

点击这里给我发消息 咨询客服专员

QQ咨询

专业咨询

199-4502-1328

电话咨询

微信扫一扫

IOT中继宝盒

微信咨询
返回顶部