本章节以设备侧开发板采用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);
#endifcoap.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设备接口”窗口,选择对应的设备–设备接入端接口中对应的协议接入样例中下载。
长按关注宜联科技公众号