fix the conflict
| 
						 | 
				
			
			@ -286,6 +286,10 @@ menu "test app"
 | 
			
		|||
                bool "Config test ftp client"
 | 
			
		||||
                default n
 | 
			
		||||
            
 | 
			
		||||
            menuconfig USER_TEST_FTPCLIENT_RISCV
 | 
			
		||||
                bool "Config test ftp client on riscv"
 | 
			
		||||
                default n
 | 
			
		||||
 | 
			
		||||
            menuconfig USER_TEST_LORA_P2P
 | 
			
		||||
                bool "Config test lora p2p"
 | 
			
		||||
                default n
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,7 +24,7 @@ endif
 | 
			
		|||
 | 
			
		||||
ifeq ($(CONFIG_ADD_XIZI_FEATURES),y)
 | 
			
		||||
    SRC_FILES := test_shell.c
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    ifeq ($(CONFIG_USER_TEST_ADC),y)
 | 
			
		||||
        SRC_FILES += test_adc.c
 | 
			
		||||
    endif
 | 
			
		||||
| 
						 | 
				
			
			@ -124,17 +124,23 @@ ifeq ($(CONFIG_ADD_XIZI_FEATURES),y)
 | 
			
		|||
    ifeq ($(CONFIG_USER_TEST_MODBUS_TCP),y)
 | 
			
		||||
        SRC_DIR += test_modbus_tcp
 | 
			
		||||
    endif 
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
    ifeq ($(CONFIG_USER_TEST_WEBSERVER),y)
 | 
			
		||||
        SRC_FILES += 
 | 
			
		||||
        SRC_FILES += test_webserver/test_webserver.c
 | 
			
		||||
    endif
 | 
			
		||||
 | 
			
		||||
    ifeq ($(CONFIG_USER_TEST_MQTTCLIENT),y)
 | 
			
		||||
        SRC_DIR:= test_mqttclient
 | 
			
		||||
        SRC_FILES += 
 | 
			
		||||
    endif
 | 
			
		||||
 | 
			
		||||
    ifeq ($(CONFIG_USER_TEST_FTPCLIENT),y)
 | 
			
		||||
        SRC_FILES += test_ftpclient_final/test_ftpclient_final.c test_ftpclient_final/ftp_client/ftp_client.c test_ftpclient_final/ftp_client/my_socket.c
 | 
			
		||||
        SRC_FILES += test_ftpclient/test_ftpclient.c test_ftpclient/ftp_client/ftp_client.c\
 | 
			
		||||
                     test_ftpclient/ftp_client/my_socket.c
 | 
			
		||||
    endif
 | 
			
		||||
 | 
			
		||||
	ifeq ($(CONFIG_USER_TEST_FTPCLIENT_RISCV),y)
 | 
			
		||||
        SRC_FILES += test_ftpclient_riscv/test_ftpclient_riscv.c
 | 
			
		||||
    endif
 | 
			
		||||
 | 
			
		||||
    ifeq ($(CONFIG_USER_TEST_LORA_P2P),y)
 | 
			
		||||
| 
						 | 
				
			
			@ -151,7 +157,11 @@ ifeq ($(CONFIG_ADD_XIZI_FEATURES),y)
 | 
			
		|||
 | 
			
		||||
    ifeq ($(CONFIG_USER_TEST_USB_CAMERA),y)
 | 
			
		||||
        SRC_FILES += 
 | 
			
		||||
    endif     
 | 
			
		||||
    endif
 | 
			
		||||
 | 
			
		||||
    ifeq ($(CONFIG_USER_TEST_FTPCLIENT_FINAL),y)
 | 
			
		||||
        SRC_FILES += test_ftpclient_final/test_ftpclient_final.c test_ftpclient_final/ftp_client/ftp_client.c test_ftpclient_final/ftp_client/my_socket.c
 | 
			
		||||
    endif   
 | 
			
		||||
 | 
			
		||||
    include $(KERNEL_ROOT)/compiler.mk
 | 
			
		||||
endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,60 @@
 | 
			
		|||
# 初赛一级赛题3:基于矽璓已实现的Lwip,在ARM上实现FTP协议的Client功能
 | 
			
		||||
 | 
			
		||||
## 1. 简介
 | 
			
		||||
本项目是基于矽璓已实现的Lwip,在ARM上实现FTP协议的Client功能
 | 
			
		||||
test_ftpclient.h声明了下载10个文件的测试函数
 | 
			
		||||
test_ftpclient.c实现了下载10个文件的测试函数
 | 
			
		||||
ftp_client文件夹定义了ftp_client的相关类库其中my_socket.h,my_socket.c定义了socket抽象层,并基于
 | 
			
		||||
Lwip实现了该抽象层,ftp_client.h,ftp_client.c实现了ftp登录,获取文件大小,下载文件等功能
 | 
			
		||||
 | 
			
		||||
## 2. 数据结构设计说明
 | 
			
		||||
- ftp_client.c 的设计
 | 
			
		||||
分别定义了发送命令和接收数据的socket和相应的缓冲区并且实现了登录、发送命令、接收响应数据、查找文件大小、进入被动模式、下载文件、关闭ftp客户端等操作
 | 
			
		||||
```c
 | 
			
		||||
static int  m_socket_cmd; // 发送命令的socket文件描述符
 | 
			
		||||
static int  m_socket_data; // 接收ftp服务器文件的socket文件描述符
 | 
			
		||||
static char m_send_buffer[1024]; // 发送缓冲区
 | 
			
		||||
static char m_recv_buffer[1024];  // 接收缓冲区
 | 
			
		||||
```
 | 
			
		||||
## 3. 测试程序说明
 | 
			
		||||
- test_ftpclient.c用于测试下载10个文件
 | 
			
		||||
连接电脑上的ftp服务器下载10个4KB文件,通过终端日志打印确定文件是否下载成功,以及追踪通信流程和下载进度
 | 
			
		||||
```c
 | 
			
		||||
void TestFtpClient(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
    FtpInitCmd();
 | 
			
		||||
    int ret = FtpLogin("192.168.0.248", 21, "anonymous", "anonymous");
 | 
			
		||||
    int size;
 | 
			
		||||
    char *buf;
 | 
			
		||||
    for(int i = 1;i <= 10;i++){
 | 
			
		||||
        char fileName[20] = "/file"; 
 | 
			
		||||
        char temp[5] = "";
 | 
			
		||||
        sprintf(temp,"%d",i);
 | 
			
		||||
        strcat(fileName,temp);
 | 
			
		||||
        size = FtpFileSize(fileName);
 | 
			
		||||
        buf = malloc(size);
 | 
			
		||||
        FtpInitData();  // data socket 每次下载都要重新创建,下载完都要关闭
 | 
			
		||||
        ret = FtpDownload(fileName, buf, size);
 | 
			
		||||
        free(buf);
 | 
			
		||||
    }
 | 
			
		||||
    FtpQuit();
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## 4. 运行结果(##需结合运行测试截图按步骤说明##)
 | 
			
		||||
1. 配置开启BSP_USING_LWIP、USER_TEST_FTPCLIENT
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
2. 编译
 | 
			
		||||

 | 
			
		||||
3. 烧写
 | 
			
		||||

 | 
			
		||||
4. xshell连接串口终端
 | 
			
		||||

 | 
			
		||||
6. 配置ip
 | 
			
		||||

 | 
			
		||||
7. 运行TestFtpClient,开始下载文件
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,256 @@
 | 
			
		|||
/*
 | 
			
		||||
* Copyright (c) 2020 AIIT XUOS Lab
 | 
			
		||||
* XiUOS is licensed under Mulan PSL v2.
 | 
			
		||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
 | 
			
		||||
* You may obtain a copy of Mulan PSL v2 at:
 | 
			
		||||
*        http://license.coscl.org.cn/MulanPSL2
 | 
			
		||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 | 
			
		||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 | 
			
		||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 | 
			
		||||
* See the Mulan PSL v2 for more details.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
* @file:    ftp_client.c
 | 
			
		||||
* @brief:   ftp client tool
 | 
			
		||||
* @version: 1.0
 | 
			
		||||
* @author:  bdislab_final
 | 
			
		||||
* @date:    2023/7/25
 | 
			
		||||
* @reference https://kerndev.blog.csdn.net/article/details/89383888
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include "my_socket.h"
 | 
			
		||||
#include "ftp_client.h"
 | 
			
		||||
 
 | 
			
		||||
static int  m_socket_cmd;
 | 
			
		||||
static int  m_socket_data;
 | 
			
		||||
static char m_send_buffer[1024];
 | 
			
		||||
static char m_recv_buffer[1024];
 | 
			
		||||
 
 | 
			
		||||
static int FtpSendCommand(char *cmd)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
	printf("send command: %s\r\n", cmd);
 | 
			
		||||
	ret = SocketSend(m_socket_cmd, cmd, (int)strlen(cmd));
 | 
			
		||||
	if(ret < 0)
 | 
			
		||||
	{
 | 
			
		||||
		printf("failed to send command: %s\r\n",cmd);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
static int FtpRecvRespond(char *resp, int len)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
	int off;
 | 
			
		||||
	len -= 1;
 | 
			
		||||
	for(off=0; off<len; off+=ret)
 | 
			
		||||
	{
 | 
			
		||||
		ret = SocketRecv(m_socket_cmd, &resp[off], 1);
 | 
			
		||||
		if(ret < 0)
 | 
			
		||||
		{
 | 
			
		||||
			printf("recv respond error(ret=%d)!\r\n", ret);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
		if(resp[off] == '\n')
 | 
			
		||||
		{
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	resp[off+1] = 0;
 | 
			
		||||
	printf("respond:%s\r\n", resp);
 | 
			
		||||
	return atoi(resp);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
static int FtpEnterPasv(char *ipaddr, int *port)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
	char *find;
 | 
			
		||||
	int a,b,c,d;
 | 
			
		||||
	int pa,pb;
 | 
			
		||||
	ret = FtpSendCommand("PASV\r\n");
 | 
			
		||||
	if(ret != 1)
 | 
			
		||||
	{
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	ret = FtpRecvRespond(m_recv_buffer, 1024);
 | 
			
		||||
	if(ret != 227)
 | 
			
		||||
	{
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	find = strrchr(m_recv_buffer, '(');
 | 
			
		||||
	sscanf(find, "(%d,%d,%d,%d,%d,%d)", &a, &b, &c, &d, &pa, &pb);
 | 
			
		||||
	sprintf(ipaddr, "%d.%d.%d.%d", a, b, c, d);
 | 
			
		||||
	*port = pa * 256 + pb;
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
int  FtpDownload(char *name, char *buf, int len)
 | 
			
		||||
{
 | 
			
		||||
	int   i;
 | 
			
		||||
	int   ret;
 | 
			
		||||
	char  ipaddr[32];
 | 
			
		||||
	int   port;
 | 
			
		||||
    
 | 
			
		||||
	//查询数据地址
 | 
			
		||||
	ret = FtpEnterPasv(ipaddr, &port);
 | 
			
		||||
	if(ret != 1)
 | 
			
		||||
	{
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	//连接数据端口
 | 
			
		||||
	ret = SocketConnect(m_socket_data, ipaddr, port);
 | 
			
		||||
	if(ret != 1)
 | 
			
		||||
	{
 | 
			
		||||
		printf("failed to connect data port\r\n");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
	//准备下载
 | 
			
		||||
	sprintf(m_send_buffer, "RETR %s\r\n", name);
 | 
			
		||||
	ret = FtpSendCommand(m_send_buffer);
 | 
			
		||||
	if(ret != 1)
 | 
			
		||||
	{
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	ret = FtpRecvRespond(m_recv_buffer, 1024);
 | 
			
		||||
	if(ret != 150)
 | 
			
		||||
	{
 | 
			
		||||
		SocketClose(m_socket_data);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	//开始下载,读取完数据后,服务器会自动关闭连接
 | 
			
		||||
	for(i=0; i<len; i+=ret)
 | 
			
		||||
	{
 | 
			
		||||
		ret = SocketRecv(m_socket_data, ((char *)buf) + i, len);
 | 
			
		||||
		printf("download %d/%d.\r\n", i + ret, len);
 | 
			
		||||
		if(ret < 0)
 | 
			
		||||
		{
 | 
			
		||||
			printf("download was interrupted.\r\n");
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	//下载完成
 | 
			
		||||
	printf("download %d/%d bytes complete.\r\n", i, len);
 | 
			
		||||
	printf("====file content====\n");
 | 
			
		||||
	buf[100] = '\0';
 | 
			
		||||
	printf("%s\n",buf);
 | 
			
		||||
	printf("====================\n");
 | 
			
		||||
    // FILE *fp = NULL;
 | 
			
		||||
 | 
			
		||||
    // fp = fopen(name+1, "wb");
 | 
			
		||||
    // fwrite(buf,len,1,fp);
 | 
			
		||||
    // fclose(fp);
 | 
			
		||||
 | 
			
		||||
	SocketClose(m_socket_data);
 | 
			
		||||
	ret = FtpRecvRespond(m_recv_buffer, 1024);
 | 
			
		||||
	return (ret==226);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
int  FtpFileSize(char *name)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
	int size;
 | 
			
		||||
	sprintf(m_send_buffer,"SIZE %s\r\n",name);
 | 
			
		||||
	ret = FtpSendCommand(m_send_buffer);
 | 
			
		||||
	if(ret != 1)
 | 
			
		||||
	{
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	ret = FtpRecvRespond(m_recv_buffer, 1024);
 | 
			
		||||
	if(ret != 213)
 | 
			
		||||
	{
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	size = atoi(m_recv_buffer + 4);
 | 
			
		||||
	return size;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
int FtpLogin(char *addr, int port, char *username, char *password)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
	printf("connect...\r\n");
 | 
			
		||||
	ret = SocketConnect(m_socket_cmd, addr, port);
 | 
			
		||||
	if(ret != 1)
 | 
			
		||||
	{
 | 
			
		||||
		printf("connect server failed!\r\n");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	printf("connect ok.\r\n");
 | 
			
		||||
    //等待欢迎信息
 | 
			
		||||
	ret = FtpRecvRespond(m_recv_buffer, 1024);
 | 
			
		||||
	if(ret != 220)
 | 
			
		||||
	{
 | 
			
		||||
		printf("bad server, ret=%d!\r\n", ret);
 | 
			
		||||
		SocketClose(m_socket_cmd);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	printf("login...\r\n");
 | 
			
		||||
    //发送USER
 | 
			
		||||
	sprintf(m_send_buffer, "USER %s\r\n", username);
 | 
			
		||||
	ret = FtpSendCommand(m_send_buffer);
 | 
			
		||||
	if(ret != 1)
 | 
			
		||||
	{
 | 
			
		||||
		SocketClose(m_socket_cmd);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	ret = FtpRecvRespond(m_recv_buffer, 1024);
 | 
			
		||||
	if(ret != 331)
 | 
			
		||||
	{
 | 
			
		||||
		SocketClose(m_socket_cmd);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
    //发送PASS
 | 
			
		||||
	sprintf(m_send_buffer, "PASS %s\r\n", password);
 | 
			
		||||
	ret = FtpSendCommand(m_send_buffer);
 | 
			
		||||
	if(ret != 1)
 | 
			
		||||
	{
 | 
			
		||||
		SocketClose(m_socket_cmd);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	ret = FtpRecvRespond(m_recv_buffer, 1024);
 | 
			
		||||
	if(ret != 230)
 | 
			
		||||
	{
 | 
			
		||||
		SocketClose(m_socket_cmd);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	printf("login success.\r\n");
 | 
			
		||||
	
 | 
			
		||||
    //设置为二进制模式
 | 
			
		||||
	ret = FtpSendCommand("TYPE I\r\n");
 | 
			
		||||
	if(ret != 1)
 | 
			
		||||
	{
 | 
			
		||||
		SocketClose(m_socket_cmd);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	ret = FtpRecvRespond(m_recv_buffer, 1024);
 | 
			
		||||
	if(ret != 200)
 | 
			
		||||
	{
 | 
			
		||||
		SocketClose(m_socket_cmd);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
void FtpQuit(void)
 | 
			
		||||
{
 | 
			
		||||
	FtpSendCommand("QUIT\r\n");
 | 
			
		||||
	SocketClose(m_socket_cmd);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
void FtpInitCmd(void)
 | 
			
		||||
{
 | 
			
		||||
	m_socket_cmd = SocketCreate();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FtpInitData(void){
 | 
			
		||||
    m_socket_data= SocketCreate();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,42 @@
 | 
			
		|||
/*
 | 
			
		||||
* Copyright (c) 2020 AIIT XUOS Lab
 | 
			
		||||
* XiUOS is licensed under Mulan PSL v2.
 | 
			
		||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
 | 
			
		||||
* You may obtain a copy of Mulan PSL v2 at:
 | 
			
		||||
*        http://license.coscl.org.cn/MulanPSL2
 | 
			
		||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 | 
			
		||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 | 
			
		||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 | 
			
		||||
* See the Mulan PSL v2 for more details.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
* @file:    ftp_client.h
 | 
			
		||||
* @brief:   ftp client tool
 | 
			
		||||
* @version: 1.0
 | 
			
		||||
* @author:  bdislab_final
 | 
			
		||||
* @date:    2023/7/25
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#ifndef FTP_CLIENT_H
 | 
			
		||||
#define FTP_CLIENT_H
 | 
			
		||||
 | 
			
		||||
/* init ftp cmd socket */ 
 | 
			
		||||
void FtpInitCmd(void);
 | 
			
		||||
 | 
			
		||||
/* init ftp data socket */
 | 
			
		||||
void FtpInitData(void);
 | 
			
		||||
 | 
			
		||||
/* quit ftp */
 | 
			
		||||
void FtpQuit(void);
 | 
			
		||||
 | 
			
		||||
/* fpt login */
 | 
			
		||||
int  FtpLogin(char *addr, int port, char *username, char *password);
 | 
			
		||||
 | 
			
		||||
/* get file size */
 | 
			
		||||
int  FtpFileSize(char *name);
 | 
			
		||||
 | 
			
		||||
/* ftp download file*/
 | 
			
		||||
int  FtpDownload(char *name, char *buf, int len);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,57 @@
 | 
			
		|||
/*
 | 
			
		||||
* Copyright (c) 2020 AIIT XUOS Lab
 | 
			
		||||
* XiUOS is licensed under Mulan PSL v2.
 | 
			
		||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
 | 
			
		||||
* You may obtain a copy of Mulan PSL v2 at:
 | 
			
		||||
*        http://license.coscl.org.cn/MulanPSL2
 | 
			
		||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 | 
			
		||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 | 
			
		||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 | 
			
		||||
* See the Mulan PSL v2 for more details.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
* @file:    my_socket.c
 | 
			
		||||
* @brief:   a abstract socket api
 | 
			
		||||
* @version: 1.0
 | 
			
		||||
* @author:  bdislab_final
 | 
			
		||||
* @date:    2023/7/25
 | 
			
		||||
* @reference https://kerndev.blog.csdn.net/article/details/89383888
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include "my_socket.h"
 | 
			
		||||
#include <sockets.h>
 | 
			
		||||
#include <def.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
int  SocketCreate(void){
 | 
			
		||||
    return socket(AF_INET,SOCK_STREAM,0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int  SocketConnect(int sock, const char *addr, int port){
 | 
			
		||||
	// unsigned int iRemoteAddr = 0;
 | 
			
		||||
	struct sockaddr_in stRemoteAddr = {0}; //对端,即目标地址信息
 | 
			
		||||
	
 | 
			
		||||
	stRemoteAddr.sin_family = AF_INET;
 | 
			
		||||
	stRemoteAddr.sin_port = htons(port);
 | 
			
		||||
	stRemoteAddr.sin_addr.s_addr = inet_addr(addr);
 | 
			
		||||
	// inet_pton(AF_INET, addr, &iRemoteAddr);
 | 
			
		||||
	// stRemoteAddr.sin_addr.s_addr=iRemoteAddr;
 | 
			
		||||
	int res = connect(sock,(struct sockaddr *)&stRemoteAddr,sizeof(stRemoteAddr));
 | 
			
		||||
	if(res == -1){
 | 
			
		||||
		printf("error:%d\n",errno);
 | 
			
		||||
	}
 | 
			
		||||
    return res == 0 ? 1 : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int  SocketSend(int sock, void *data, int len){
 | 
			
		||||
    return send(sock,data,len,0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int  SocketRecv(int sock, void *data, int len){
 | 
			
		||||
    return recv(sock,data,len,0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SocketClose(int sock){
 | 
			
		||||
	close(sock);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,46 @@
 | 
			
		|||
/*
 | 
			
		||||
* Copyright (c) 2020 AIIT XUOS Lab
 | 
			
		||||
* XiUOS is licensed under Mulan PSL v2.
 | 
			
		||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
 | 
			
		||||
* You may obtain a copy of Mulan PSL v2 at:
 | 
			
		||||
*        http://license.coscl.org.cn/MulanPSL2
 | 
			
		||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 | 
			
		||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 | 
			
		||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 | 
			
		||||
* See the Mulan PSL v2 for more details.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @file my_socket.h
 | 
			
		||||
 * @brief a abstract socket api
 | 
			
		||||
 * @version 1.0
 | 
			
		||||
 * @author bdislab_final
 | 
			
		||||
 * @date 2023/7/25
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef MYSOCKET_H
 | 
			
		||||
#define MYSOCKET_H
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
/* create a socket */
 | 
			
		||||
int  SocketCreate(void);
 | 
			
		||||
 | 
			
		||||
/* connect a socket */
 | 
			
		||||
int  SocketConnect(int sock, const char *addr, int port);
 | 
			
		||||
 | 
			
		||||
/* send data through socket*/
 | 
			
		||||
int  SocketSend(int sock, void *data, int len);
 | 
			
		||||
 | 
			
		||||
/* receive data from socket*/
 | 
			
		||||
int  SocketRecv(int sock, void *data, int len);
 | 
			
		||||
 | 
			
		||||
/* close socket*/
 | 
			
		||||
void SocketClose(int sock);
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
		 After Width: | Height: | Size: 214 KiB  | 
| 
		 After Width: | Height: | Size: 121 KiB  | 
| 
		 After Width: | Height: | Size: 108 KiB  | 
| 
		 After Width: | Height: | Size: 117 KiB  | 
| 
		 After Width: | Height: | Size: 38 KiB  | 
| 
		 After Width: | Height: | Size: 50 KiB  | 
| 
		 After Width: | Height: | Size: 73 KiB  | 
| 
		 After Width: | Height: | Size: 48 KiB  | 
| 
		 After Width: | Height: | Size: 136 KiB  | 
| 
						 | 
				
			
			@ -0,0 +1,50 @@
 | 
			
		|||
/*
 | 
			
		||||
* Copyright (c) 2020 AIIT XUOS Lab
 | 
			
		||||
* XiUOS is licensed under Mulan PSL v2.
 | 
			
		||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
 | 
			
		||||
* You may obtain a copy of Mulan PSL v2 at:
 | 
			
		||||
*        http://license.coscl.org.cn/MulanPSL2
 | 
			
		||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 | 
			
		||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 | 
			
		||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 | 
			
		||||
* See the Mulan PSL v2 for more details.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
* @file:    test_ftpclient.c
 | 
			
		||||
* @brief:   a ftpClient test sample
 | 
			
		||||
* @version: 1.0
 | 
			
		||||
* @author:  bdislab_final
 | 
			
		||||
* @date:    2023/7/25
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include "test_ftpclient.h"
 | 
			
		||||
#include "ftp_client/ftp_client.h"
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <transform.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* test for ftp client */
 | 
			
		||||
void TestFtpClient(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
    FtpInitCmd();
 | 
			
		||||
    int ret = FtpLogin("192.168.1.248", 21, "anonymous", "anonymous");
 | 
			
		||||
    int size;
 | 
			
		||||
    char *buf;
 | 
			
		||||
    for(int i = 1;i <= 10;i++){
 | 
			
		||||
        char fileName[20] = "/file"; 
 | 
			
		||||
        char temp[5] = "";
 | 
			
		||||
        sprintf(temp,"%d",i-1);
 | 
			
		||||
        strcat(fileName,temp);
 | 
			
		||||
        size = FtpFileSize(fileName);
 | 
			
		||||
        buf = malloc(size);
 | 
			
		||||
        FtpInitData();  // data socket 每次下载都要重新创建,下载完都要关闭
 | 
			
		||||
        ret = FtpDownload(fileName, buf, size);
 | 
			
		||||
        free(buf);
 | 
			
		||||
    }
 | 
			
		||||
    FtpQuit();
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
PRIV_SHELL_CMD_FUNCTION(TestFtpClient, a ftpClient test sample, PRIV_SHELL_CMD_MAIN_ATTR);
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,35 @@
 | 
			
		|||
/*
 | 
			
		||||
* Copyright (c) 2020 AIIT XUOS Lab
 | 
			
		||||
* XiUOS is licensed under Mulan PSL v2.
 | 
			
		||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
 | 
			
		||||
* You may obtain a copy of Mulan PSL v2 at:
 | 
			
		||||
*        http://license.coscl.org.cn/MulanPSL2
 | 
			
		||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 | 
			
		||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 | 
			
		||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 | 
			
		||||
* See the Mulan PSL v2 for more details.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @file test_ftpclient.h
 | 
			
		||||
 * @brief a ftpClient test sample
 | 
			
		||||
 * @version 1.0
 | 
			
		||||
 * @author bdislab_final
 | 
			
		||||
 * @date 2023/7/25
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef TEST_FTPCLIENT_H
 | 
			
		||||
#define TEST_FTPCLIENT_H
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* test for ftp client */
 | 
			
		||||
void TestFtpClient(int argc, char* argv[]);
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,43 @@
 | 
			
		|||
# 基于矽璓已实现的W5500网卡驱动,在Riscv上实现FTP协议的Client功能 #
 | 
			
		||||
 | 
			
		||||
## 1. 简介
 | 
			
		||||
利用w5500网络编程的api实现了ftp客户端的功能,具体功能包括向ftp服务器发送指定命令,根据用户名登陆ftp服务器,获取文件大小,下载指定文件并保存至sd卡内,并提供了测试函数模拟从ftp服务器上下载10个4KB文件的过程
 | 
			
		||||
 | 
			
		||||
## 2. 数据结构设计说明
 | 
			
		||||
全局变量:
 | 
			
		||||
char sendBuffer[]:输出缓冲区  
 | 
			
		||||
char recvBuffer[]:输入缓冲区
 | 
			
		||||
 | 
			
		||||
包括以下函数功能,分别为:  
 | 
			
		||||
`SendCommand`:发送指定的指令  
 | 
			
		||||
`EnterPasv`:进入被动模式  
 | 
			
		||||
`Login`:根据用户名登陆ftp服务器  
 | 
			
		||||
`GetFileSize`:获取指定文件的大小  
 | 
			
		||||
`Download`:下载指定文件
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
## 3. 测试程序说明
 | 
			
		||||
测试了创建套接字,与服务器建立连接并根据传入的用户名和密码发送登陆指令。登陆成功后下载服务器上指定的10个4KB大小的txt文件,然后调用开发板的相关api在系统根目录中创建对应的文件,写入从服务器接收到的数据。
 | 
			
		||||
 | 
			
		||||
## 4. 运行结果(##需结合运行测试截图按步骤说明##)
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
打开menuconfig之后,将test_ftp开启(y),将Using TFcard device 和 Using W5500 as network device 开启(y)保存后退出
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
编译XiZi-edu-riscv64.elf成功
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
启动kflash烧录bin文件,按reset键重置成功进入系统并显示SD卡已挂载
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
执行TestTfp命令,可以看到成功登陆ftp服务器,并开始下载10个大小为4KB的文件。
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
服务器日志信息
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
读取sd卡中的文件可以看到10个大小为4KB的文件被成功下载
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
| 
		 After Width: | Height: | Size: 172 KiB  | 
| 
		 After Width: | Height: | Size: 161 KiB  | 
| 
		 After Width: | Height: | Size: 258 KiB  | 
| 
		 After Width: | Height: | Size: 116 KiB  | 
| 
		 After Width: | Height: | Size: 117 KiB  | 
| 
		 After Width: | Height: | Size: 182 KiB  | 
| 
		 After Width: | Height: | Size: 38 KiB  | 
| 
		 After Width: | Height: | Size: 62 KiB  | 
| 
		 After Width: | Height: | Size: 61 KiB  | 
| 
		 After Width: | Height: | Size: 62 KiB  | 
| 
						 | 
				
			
			@ -0,0 +1,241 @@
 | 
			
		|||
/*
 | 
			
		||||
* Copyright (c) 2020 AIIT Ubiquitous Team
 | 
			
		||||
* XiUOS is licensed under Mulan PSL v2.
 | 
			
		||||
* You can use this software according to the terms and conditions of the Mulan PSL
 | 
			
		||||
v2.
 | 
			
		||||
* You may obtain a copy of Mulan PSL v2 at:
 | 
			
		||||
*
 | 
			
		||||
http://license.coscl.org.cn/MulanPSL2
 | 
			
		||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 | 
			
		||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 | 
			
		||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 | 
			
		||||
* See the Mulan PSL v2 for more details.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include <transform.h>
 | 
			
		||||
#include <socket.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include "test_ftpclient_riscv.h"
 | 
			
		||||
#define SOCKET_DATA 2
 | 
			
		||||
#define SOCKET_CMD 3
 | 
			
		||||
char sendBuffer[1024];
 | 
			
		||||
char recvBuffer[1024];
 | 
			
		||||
 | 
			
		||||
//quoted from https://kerndev.blog.csdn.net/article/details/89383888
 | 
			
		||||
int SendCommand(char *cmd)
 | 
			
		||||
{
 | 
			
		||||
    int ret;
 | 
			
		||||
    ret = wiz_sock_send(SOCKET_CMD,cmd,(int)strlen(cmd));
 | 
			
		||||
    if(ret < 0 ){
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
int RecvRespond(char * respond,int len)
 | 
			
		||||
{
 | 
			
		||||
    int ret;
 | 
			
		||||
    int off;
 | 
			
		||||
    len -= -1;
 | 
			
		||||
    for(off = 0;off < len;off += ret){
 | 
			
		||||
        ret = wiz_sock_recv(SOCKET_CMD,&respond[off],1);
 | 
			
		||||
        if(ret < 0 ){
 | 
			
		||||
            printf("recv respond error\r\n");
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        if(respond[off] == '\n'){
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    } 
 | 
			
		||||
    respond[off + 1]=0;
 | 
			
		||||
    return atoi(respond);
 | 
			
		||||
}
 | 
			
		||||
int EnterPasv(uint8_t *ipaddr,int *port)
 | 
			
		||||
{
 | 
			
		||||
    int ret;
 | 
			
		||||
    char *find;
 | 
			
		||||
    int ip1,ip2,ip3,ip4,tmp1,tmp2;
 | 
			
		||||
    ret = SendCommand("PASV\r\n");
 | 
			
		||||
    if(ret != 1){
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    ret=RecvRespond(recvBuffer,1024);
 | 
			
		||||
    if(ret != 227){
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    find=strrchr(recvBuffer,'(');
 | 
			
		||||
    sscanf(find,"(%d,%d,%d,%d,%d,%d)",&ip1,&ip2,&ip3,&ip4,&tmp1,&tmp2);
 | 
			
		||||
    ipaddr[0] = ip1;
 | 
			
		||||
    ipaddr[1] = ip2;
 | 
			
		||||
    ipaddr[2] = ip3;
 | 
			
		||||
    ipaddr[3] = ip4;
 | 
			
		||||
    *port = tmp1 * 256 + tmp2;
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
int Login(uint8_t addr[4],int port,char *username,char *password)
 | 
			
		||||
{
 | 
			
		||||
    int ret;
 | 
			
		||||
    printf("connect....\n");
 | 
			
		||||
    ret = wiz_sock_connect(SOCKET_CMD,addr,port);
 | 
			
		||||
    if(ret != 1){
 | 
			
		||||
        printf("connect server failed\r\n");
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    printf("connect ok.\r\n");
 | 
			
		||||
 | 
			
		||||
    ret = RecvRespond(recvBuffer,1024);
 | 
			
		||||
    if(ret != 220){
 | 
			
		||||
        printf("bad server ret=%d\r\n",ret);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    printf("login....\r\n");
 | 
			
		||||
    sprintf(sendBuffer,"USER %s\r\n",username);
 | 
			
		||||
    ret = SendCommand(sendBuffer);
 | 
			
		||||
 | 
			
		||||
    if(ret != 1){
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    ret = RecvRespond(recvBuffer,1024);
 | 
			
		||||
    if(ret == 220)ret = RecvRespond(recvBuffer,1024);
 | 
			
		||||
    if(ret != 331){
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    sprintf(sendBuffer,"PASS %s\r\n",password);
 | 
			
		||||
    ret = SendCommand(sendBuffer);
 | 
			
		||||
    if(ret != 1){
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    ret = RecvRespond(recvBuffer,1024);
 | 
			
		||||
    if(ret != 230){
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    printf("login sucess \r\n");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    ret = SendCommand("TYPE I\r\n");
 | 
			
		||||
    if(ret != 1){
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    ret = RecvRespond(recvBuffer,1024);
 | 
			
		||||
    if(ret != 200){
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
int GetFileSize(char * name)
 | 
			
		||||
{
 | 
			
		||||
    int ret;
 | 
			
		||||
    int size;
 | 
			
		||||
    sprintf(sendBuffer,"SIZE %s\r\n",name);
 | 
			
		||||
    ret = SendCommand(sendBuffer);
 | 
			
		||||
    if(ret != 1){
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    ret = RecvRespond(recvBuffer,1024);
 | 
			
		||||
    if(ret != 213){
 | 
			
		||||
        printf("get file size failed\r\n");
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    size = atoi(recvBuffer + 4);
 | 
			
		||||
    return size;
 | 
			
		||||
}
 | 
			
		||||
int Download(char *name)
 | 
			
		||||
{
 | 
			
		||||
    wiz_socket(SOCKET_DATA, Sn_MR_TCP,12345,0);
 | 
			
		||||
    int len = GetFileSize(name);
 | 
			
		||||
    char *buf = malloc(len+1);
 | 
			
		||||
    printf("downloading file %s\r\n",name);
 | 
			
		||||
    int it;
 | 
			
		||||
    int ret;
 | 
			
		||||
    uint8_t addr[4];
 | 
			
		||||
    int port;
 | 
			
		||||
    ret = EnterPasv(addr,&port);
 | 
			
		||||
 | 
			
		||||
    if(ret != 1){
 | 
			
		||||
        wiz_sock_disconnect(SOCKET_DATA);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    ret = wiz_sock_connect(SOCKET_DATA,addr,port);
 | 
			
		||||
    if(ret != 1){
 | 
			
		||||
        wiz_sock_disconnect(SOCKET_DATA);
 | 
			
		||||
        printf("fail to connect data port\r\n");
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    sprintf(sendBuffer,"RETR %s\r\n",name);
 | 
			
		||||
    ret = SendCommand(sendBuffer);
 | 
			
		||||
    if(ret != 1){
 | 
			
		||||
        wiz_sock_disconnect(SOCKET_DATA);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    ret =  RecvRespond(recvBuffer,1024);
 | 
			
		||||
    if(ret!=150){
 | 
			
		||||
        wiz_sock_disconnect(SOCKET_DATA);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    for(it = 0;it < len;it += ret){
 | 
			
		||||
        ret = wiz_sock_recv(SOCKET_DATA,((char *)buf + it),len);
 | 
			
		||||
        if(ret<0){
 | 
			
		||||
            printf("download was interupted\r\n");
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    buf[len] = 0;
 | 
			
		||||
    printf("download %d/%d bytes complete. \r\n",it,len);
 | 
			
		||||
    ret = RecvRespond(recvBuffer,1024);
 | 
			
		||||
    if(ret != 226){
 | 
			
		||||
        wiz_sock_disconnect(SOCKET_DATA);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    wiz_sock_disconnect(SOCKET_DATA);
 | 
			
		||||
 | 
			
		||||
    printf("creating file %s ....\r\n",name);
 | 
			
		||||
    int fd=open(name,O_RDWR | O_CREAT);
 | 
			
		||||
    if(fd > 0){
 | 
			
		||||
        ret = write(fd,buf,strlen(buf));
 | 
			
		||||
        if(ret < 0 ){
 | 
			
		||||
            printf("write failed\r\n");
 | 
			
		||||
        }
 | 
			
		||||
        else printf("success!\r\n");
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        printf("create file %s failed\r\n",name);
 | 
			
		||||
    }
 | 
			
		||||
    if(buf != NULL){
 | 
			
		||||
        free(buf);
 | 
			
		||||
    }
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
void Init()
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    wiz_socket(SOCKET_CMD, Sn_MR_TCP,54321,0);
 | 
			
		||||
    printf("socket created\n");
 | 
			
		||||
}
 | 
			
		||||
void Quit()
 | 
			
		||||
{
 | 
			
		||||
    wiz_sock_disconnect(SOCKET_CMD);
 | 
			
		||||
}
 | 
			
		||||
int TestFtp()
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    Init();
 | 
			
		||||
 | 
			
		||||
    uint8_t ip[4] = {192,168,130,70};
 | 
			
		||||
    Login(ip,21,"frank","114514");
 | 
			
		||||
 | 
			
		||||
    Download("File0.txt");
 | 
			
		||||
    Download("File1.txt");
 | 
			
		||||
    Download("File2.txt");
 | 
			
		||||
    Download("File3.txt");
 | 
			
		||||
    Download("File4.txt");
 | 
			
		||||
    Download("File5.txt");
 | 
			
		||||
    Download("File6.txt");
 | 
			
		||||
    Download("File7.txt");
 | 
			
		||||
    Download("File8.txt");
 | 
			
		||||
    Download("File9.txt");
 | 
			
		||||
 | 
			
		||||
    Quit();
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
PRIV_SHELL_CMD_FUNCTION(TestFtp, Implement ftp, PRIV_SHELL_CMD_MAIN_ATTR);
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
#include <transform.h>
 | 
			
		||||
#include <socket.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
 | 
			
		||||
int SendCommand(char *cmd);
 | 
			
		||||
int RecvRespond(char * respond,int len);
 | 
			
		||||
int EnterPasv(uint8_t *ipaddr,int *port);
 | 
			
		||||
int Login(uint8_t addr[4],int port,char *username,char *password);
 | 
			
		||||
int GetFileSize(char * name);
 | 
			
		||||
int Download(char *name);
 | 
			
		||||
void Init();
 | 
			
		||||
void Quit();
 | 
			
		||||
int TestFtp();
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,187 @@
 | 
			
		|||
# 基于初赛二级赛题1,实现多个LoRa节点和LoRa网关通信私有协议
 | 
			
		||||
## 1. 简介
 | 
			
		||||
基于 E220-400T22S Lora模块设计实现通信私有协议,主要包含以下几个方面:<br>
 | 
			
		||||
- 协议实现完全遵循LoRa Adapter代码规范,可以直接使用原有的Adapter相关函数基于本协议构建Lora网络。
 | 
			
		||||
- 重新设计实现E220底层函数,增加了IOCTL函数实现,支持在运行时对地址、信道、通信模式、空中速率等参数的动态配置。
 | 
			
		||||
- E220模块只支持单通道通信,本协议基于定向传输模式实现了多下行信道通信。
 | 
			
		||||
- 支持数据帧完整性校验、支持数据帧丢失或者跳帧检测。
 | 
			
		||||
- 支持节点断网自动重联,在数据发送失败时可以进行重联或者搜索其他网关。
 | 
			
		||||
- 支持节点对网关的搜索,可以主动寻找在线网关入网。
 | 
			
		||||
- 支持信道冲突检测,检测上行通道是否占用。
 | 
			
		||||
## 2. 数据结构说明
 | 
			
		||||
### E220模块相关数据结构
 | 
			
		||||
 | 
			
		||||
```c
 | 
			
		||||
enum LoraMode                        // E220工作模式,支持四种,配置模式与休眠模式一致
 | 
			
		||||
{
 | 
			
		||||
    MODE_TRANSFER_MODE = 0,          // M0 : M1 = 0 : 0 可发可收
 | 
			
		||||
    MODE_WOR_SEND,                   // M0 : M1 = 1 : 0 可发可收
 | 
			
		||||
    MODE_WOR_RECV,                   // M0 : M1 = 0 : 1 可收不发
 | 
			
		||||
    MODE_CONFIG_SLEEP                // M0 : M1 = 1 : 1 不收不发
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum LoraState                       // 硬件状态
 | 
			
		||||
{
 | 
			
		||||
    STATE_CLOSED,                    // 硬件关闭
 | 
			
		||||
    STATE_OPENED,                    // 已正常开启
 | 
			
		||||
    STATE_BROKEN                     // 硬件损坏
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum LoraTransMode                   // 模块传输模式
 | 
			
		||||
{
 | 
			
		||||
    TRANS_MODE_HYALINE = 0X0,        // 透明传输
 | 
			
		||||
    TRANS_MODE_ORIENTED = 0X40,      // 定点传输
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum LoraAirRate                     // 空中速率,通信双方必须设置一致的空中速率方可进行通信
 | 
			
		||||
{
 | 
			
		||||
    AIR_RATE_2D4K = 0X0,             // 2.4K
 | 
			
		||||
    AIR_RATE_4D8K = 0X3,             // 4.8K
 | 
			
		||||
    AIR_RATE_9D6K = 0X4,             // 9.6K
 | 
			
		||||
    AIR_RATE_19D2K = 0X5,            // 19.2K
 | 
			
		||||
    AIR_RATE_38D4K = 0X6,            // 38.4K
 | 
			
		||||
    AIR_RATE_62D5K = 0X7             // 62.5K
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum LoraConfig                      // 支持IOCTL函数进行配置的配置项枚举,某些项只支持9600波特率配置
 | 
			
		||||
{
 | 
			
		||||
    CONFIG_SERIAL_TIME_OUT = 0X0,    // 串口超时时间
 | 
			
		||||
    CONFIG_LORA_MODE = 0X1,          // 配置Lora模式
 | 
			
		||||
    CONFIG_TRANS_MODE = 0X2,         // 配置Lora传输模式(9600)
 | 
			
		||||
    CONFIG_AIR_RATE = 0X3,           // 配置空中速率(9600)
 | 
			
		||||
    CONFIG_BAUD_RATE = 0X4,          // 配置波特率
 | 
			
		||||
    CONFIG_ADDRESS = 0X5,            // 配置设备地址(9600)
 | 
			
		||||
    CONFIG_CHANNEL = 0X6,            // 配置设备信道(9600)
 | 
			
		||||
    CONFIG_SHOW = 0X7                // 打印配置信息
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 数据帧相关数据结构
 | 
			
		||||
 | 
			
		||||
```c
 | 
			
		||||
enum FrameType      // 数据帧类型枚举
 | 
			
		||||
{
 | 
			
		||||
    E_G_JOIN = 0,   // 客户端入网请求,需回复
 | 
			
		||||
    E_G_QUIT,       // 客户端退网请求,不需回复
 | 
			
		||||
    E_G_DATA,       // 客户端上传数据,需回复
 | 
			
		||||
    G_E_ANS,        // 网关响应客户端请求
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct LoraFrame                // 数据帧消息类型
 | 
			
		||||
{
 | 
			
		||||
    uint16 begin_mark;          // 开始标志 0x3C3C
 | 
			
		||||
    uint16 session_id;          // 会话ID,客户端每次数据上传会随机设置,若是回复ID不一致则表示数据丢失
 | 
			
		||||
    uint16 dev_addr;            // 设备地址,特指终端地址,网关地址固定为 0XFFFF
 | 
			
		||||
    uint8 down_channel;         // 下行通道,即终端所在的信道
 | 
			
		||||
    uint8 frame_type;           // 数据帧的类型
 | 
			
		||||
    uint8 frame_confirm;        // 该数据帧是否需要确认回复
 | 
			
		||||
    uint8 frame_attach;         // 用于携带简单的数据,例如用户数据长度
 | 
			
		||||
    uint8 *user_data;           // 需要额外携带的大量数据
 | 
			
		||||
    uint8 crc_hi;               // 校验位高字节
 | 
			
		||||
    uint8 crc_lo;               // 校验位低字节
 | 
			
		||||
    uint16 end_mark;            // 结束标志 0X5A5A
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Lora节点相关数据结构
 | 
			
		||||
 | 
			
		||||
```c
 | 
			
		||||
enum EndNodeState                     //节点状态
 | 
			
		||||
{
 | 
			
		||||
    NODE_STATE_DISCONNECT = 0,        // 硬件开启,但是没有联网,后台程序可能开启
 | 
			
		||||
    NODE_STATE_CONNECT,               // 硬件开启,已经联网,后台程序可能开启
 | 
			
		||||
    NODE_STATE_BROKEN,                // 硬件损坏,无后台程序
 | 
			
		||||
    NODE_STATE_CLOSED                 // 硬件关闭,无后台程序
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct EndNodeParam                   // 节点参数结构
 | 
			
		||||
{
 | 
			
		||||
    uint16 dev_addr;                  // 设备地址信息
 | 
			
		||||
    uint8 down_channel;               // 下行通道,终端可以接收信息的信道
 | 
			
		||||
    uint8 upload_channel;             // 上行通道,终端可以给网关发送信息的信道
 | 
			
		||||
    uint16 session_id;                // 会话ID,用于判定数据帧是否存在丢失
 | 
			
		||||
 | 
			
		||||
    enum EndNodeState node_state;     // 客户端状态
 | 
			
		||||
    enum LoraAirRate air_rate;        // 终端的空中速率
 | 
			
		||||
    uint8 adr_enable;                 // 终端是否开启自动寻找上行网关
 | 
			
		||||
    uint8 reconnect_enable;           // 终端断网自动重联
 | 
			
		||||
    uint8 recv_time;                  // 接受下行消息的时间窗口长度,单位为秒
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Lora网关相关数据结构
 | 
			
		||||
 | 
			
		||||
```c
 | 
			
		||||
struct EndNodeInfo                    // 网关存储的客户端信息
 | 
			
		||||
{
 | 
			
		||||
    uint16 node_addr;                 // 客户端地址
 | 
			
		||||
    uint8 node_down_channel;          // 客户端下行通道
 | 
			
		||||
    uint16 session_id;                // 上一个命令的会话ID
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum GatewayState                   // 网关的状态枚举
 | 
			
		||||
{
 | 
			
		||||
    GATEWAY_CLOSED,                 // Lora还没有打开
 | 
			
		||||
    GATEWAY_OPENED,                 // 已打开,但是未开始工作
 | 
			
		||||
    GATEWAY_WORKING,                // 正常打开且已经开始正常工作
 | 
			
		||||
    GATEWAY_BROKEN                  // 模块损坏
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GatewayParam                                             // 网关参数
 | 
			
		||||
{
 | 
			
		||||
    uint16 dev_addr;                                            // 设备ID
 | 
			
		||||
    uint8 channel;                                              // 网络编号
 | 
			
		||||
 | 
			
		||||
    int recv_time;                                              // 串口超时时间
 | 
			
		||||
    enum LoraAirRate air_rate;                                  // 网关空中速率
 | 
			
		||||
    uint8 frame_retry;                                          // 重传次数
 | 
			
		||||
    enum GatewayState gateway_state;                            // 网关状态
 | 
			
		||||
    struct EndNodeInfo *node_infos[LORA_GATEWAY_MAX_NODE];      // 客户端信息
 | 
			
		||||
    uint16 node_count;                                          // 已链接客户端数量
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## 3. 测试程序说明
 | 
			
		||||
- 编译烧录两个不同地址、信道的客户端和一个网关。
 | 
			
		||||
- 分别打开客户端和网关并联网。
 | 
			
		||||
- 两个客户端分别向网关发送数据。
 | 
			
		||||
## 4. 运行结果
 | 
			
		||||
### (1)编译模块:
 | 
			
		||||
- 配置MENUCONFIG
 | 
			
		||||

 | 
			
		||||
> 开启连接框架支持,同时开启Lora Adapter支持
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
> 选择E220模块
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
> 开启测试函数
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
> 测试单通道网关
 | 
			
		||||
 | 
			
		||||
- 修改MakeFile:xiuos/APP_Framework/Applications/app_test/Makefile第141行修改为
 | 
			
		||||
```c
 | 
			
		||||
        SRC_FILES += test_lora_net_final/lora_driver/e220.c
 | 
			
		||||
        SRC_FILES += test_lora_net_final/lora_mac/lora_mac.c
 | 
			
		||||
        SRC_FILES += test_lora_net_final/test_lora_net_final.c
 | 
			
		||||
```
 | 
			
		||||
- 修改框架初始函数:
 | 
			
		||||
> 1. 文件:xiuos/APP_Framework/Framework/framework_init.c 第27行修改为<br> extern int UsrAdapterLoraInit(void);
 | 
			
		||||
> 2. 文件:xiuos/APP_Framework/Framework/framework_init.c 第170行的AdapterLoraInit修改为UsrAdapterLoraInit
 | 
			
		||||
### (2)打开网关
 | 
			
		||||

 | 
			
		||||
> 网关打开,地址OXFFFF,信道OXA,空中速率2.4K
 | 
			
		||||
 | 
			
		||||
### (3)客户端连接网关
 | 
			
		||||

 | 
			
		||||
> 两边客户端同时发送入网请求,可以看见信道检测效果
 | 
			
		||||
 | 
			
		||||
### (4)客户端发送数据
 | 
			
		||||

 | 
			
		||||
> 客户端向网关发送数据
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
> 两边客户端同时发送数据
 | 
			
		||||
 | 
			
		||||
使用自带的天线,2.4K空中速率,载荷64字节,在无大型遮盖物的情况下传输距离近1500米
 | 
			
		||||
| 
		 After Width: | Height: | Size: 320 KiB  | 
| 
		 After Width: | Height: | Size: 344 KiB  | 
| 
		 After Width: | Height: | Size: 233 KiB  | 
| 
		 After Width: | Height: | Size: 436 KiB  | 
| 
		 After Width: | Height: | Size: 970 KiB  | 
| 
		 After Width: | Height: | Size: 1.1 MiB  | 
| 
		 After Width: | Height: | Size: 1.1 MiB  | 
| 
		 After Width: | Height: | Size: 1.3 MiB  | 
| 
						 | 
				
			
			@ -0,0 +1,591 @@
 | 
			
		|||
/*
 | 
			
		||||
* Copyright (c) 2020 AIIT XUOS Lab
 | 
			
		||||
* XiUOS is licensed under Mulan PSL v2.
 | 
			
		||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
 | 
			
		||||
* You may obtain a copy of Mulan PSL v2 at:
 | 
			
		||||
*        http://license.coscl.org.cn/MulanPSL2
 | 
			
		||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 | 
			
		||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 | 
			
		||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 | 
			
		||||
* See the Mulan PSL v2 for more details.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
* @file:    e220.c
 | 
			
		||||
* @brief:   E220 module underlying related configuration driver functions implement
 | 
			
		||||
* @version: 0.1
 | 
			
		||||
* @author:  YUNFEI CHU
 | 
			
		||||
* @date:    2023/10/01 
 | 
			
		||||
*
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include "e220.h"
 | 
			
		||||
 | 
			
		||||
struct RegisterCfg                  // 寄存器结构
 | 
			
		||||
{
 | 
			
		||||
    uint8 ADDH;
 | 
			
		||||
    uint8 ADDL;                     // 地址
 | 
			
		||||
    uint8 REG0;                     // 波特率/串口校验/空中速率
 | 
			
		||||
    uint8 REG1;                     // 分包设定/RSSI环境噪声/发射功率
 | 
			
		||||
    uint8 REG2;                     // 信道
 | 
			
		||||
    uint8 REG3;                     // RSSI字节/传输方式/LBT使能/WOR周期
 | 
			
		||||
    uint8 KEYH;
 | 
			
		||||
    uint8 KEYL;                     // 密钥
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 默认的寄存器配置信息,在每次配置更新后会同步更新该数据,保持其与硬件寄存器数据的一致性
 | 
			
		||||
*/
 | 
			
		||||
static struct RegisterCfg register_cfg =
 | 
			
		||||
{
 | 
			
		||||
    .ADDH = (E220_ADDRESS >> 8) & 0XFF,
 | 
			
		||||
    .ADDL = E220_ADDRESS & 0XFF,
 | 
			
		||||
    .REG0 = E220_DEFAULT_CONFIG_BAUD_RATE_BYTE | E220_DEFAULT_AIR_RATE,
 | 
			
		||||
    .REG1 = 0X0,
 | 
			
		||||
    .REG2 = E220_DEFAULT_CHANNEL,
 | 
			
		||||
    .REG3 = E220_DEFAULT_TRANS_MODE,
 | 
			
		||||
    .KEYH = 0X0,
 | 
			
		||||
    .KEYL = 0X0,       
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 默认的串口配置信息,初始化时波特率为9600,保持与硬件一致,在波特率更改过程中也需要保证一致
 | 
			
		||||
*/
 | 
			
		||||
static struct SerialDataCfg serial_cfg =
 | 
			
		||||
{
 | 
			
		||||
    .serial_baud_rate = E220_DEFAULT_CONFIG_BAUD_RATE,// 串口波特率
 | 
			
		||||
    .serial_data_bits = DATA_BITS_8,
 | 
			
		||||
    .serial_stop_bits = STOP_BITS_1,
 | 
			
		||||
    .serial_parity_mode = PARITY_NONE,
 | 
			
		||||
    .serial_bit_order = BIT_ORDER_LSB,
 | 
			
		||||
    .serial_invert_mode = NRZ_NORMAL,
 | 
			
		||||
    .serial_buffer_size = SERIAL_RB_BUFSZ,
 | 
			
		||||
    .serial_timeout = E220_DAFAULT_SERIAL_TIMEOUT,     // 串口超时配置
 | 
			
		||||
    .is_ext_uart = 0,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum LoraMode current_mode = -1;                       // 当前模块处于什么模式
 | 
			
		||||
enum LoraState lora_state = STATE_CLOSED;              // 当前模块的状态
 | 
			
		||||
uint8 lora_init = E220_NO;                             // 当前模块是否已经被初始化过了
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 将硬件切换至指定的模式
 | 
			
		||||
 * @param mode: 支持休眠/配置模式 和 传输模式
 | 
			
		||||
*/
 | 
			
		||||
static uint8 E220LoraModeConfig(enum LoraMode mode)
 | 
			
		||||
{
 | 
			
		||||
    // 模式和状态判断
 | 
			
		||||
    if (lora_state == STATE_BROKEN)
 | 
			
		||||
    {
 | 
			
		||||
        DDBG("E220LoraModeConfig-Fail: lora broken!\n");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    if(mode == current_mode)
 | 
			
		||||
    {
 | 
			
		||||
        // 同一个模式直接返回,不输出信息
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    // 打开PIN驱动
 | 
			
		||||
    int pin_fd = PrivOpen(E220_PIN_DRIVER,O_RDWR);
 | 
			
		||||
    if (pin_fd < 0)
 | 
			
		||||
    {
 | 
			
		||||
        DDBG("E220LoraModeConfig-Fail: open pin driver fail %s!\n",E220_PIN_DRIVER);
 | 
			
		||||
        lora_state = STATE_BROKEN;
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    // 配置管脚属性
 | 
			
		||||
    struct PinParam pin_param ={.cmd = GPIO_CONFIG_MODE,.mode = GPIO_CFG_OUTPUT,.pin = E220_M0_PATH};
 | 
			
		||||
    struct PrivIoctlCfg ioctl_config = {.ioctl_driver_type = PIN_TYPE, .args = &pin_param};
 | 
			
		||||
    if (0 != PrivIoctl(pin_fd,OPE_CFG,&ioctl_config))
 | 
			
		||||
    {
 | 
			
		||||
        DDBG("E220LoraModeConfig-Fail: config m0 fail!\n");
 | 
			
		||||
        lora_state = STATE_BROKEN;
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    pin_param.pin = E220_M1_PATH;
 | 
			
		||||
    ioctl_config.args = &pin_param;
 | 
			
		||||
    if (0 != PrivIoctl(pin_fd,OPE_CFG,&ioctl_config))
 | 
			
		||||
    {
 | 
			
		||||
        DDBG("E220LoraModeConfig-Fail: config m1 fail!\n");
 | 
			
		||||
        lora_state = STATE_BROKEN;
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct PinStat pin_stat;
 | 
			
		||||
    switch (mode)
 | 
			
		||||
    {
 | 
			
		||||
    case MODE_TRANSFER_MODE:
 | 
			
		||||
    {   
 | 
			
		||||
        pin_stat.pin = E220_M0_PATH;
 | 
			
		||||
        pin_stat.val = GPIO_LOW;
 | 
			
		||||
        if (-1 == PrivWrite(pin_fd, &pin_stat, 1))
 | 
			
		||||
        {
 | 
			
		||||
            DDBG("E220LoraModeConfig-Fail: config m0 low fail!\n");
 | 
			
		||||
            lora_state = STATE_BROKEN;
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        pin_stat.pin = E220_M1_PATH;
 | 
			
		||||
        pin_stat.val = GPIO_LOW;
 | 
			
		||||
        if (-1 == PrivWrite(pin_fd, &pin_stat, 1))
 | 
			
		||||
        {
 | 
			
		||||
            DDBG("E220LoraModeConfig-Fail: config m1 low fail!\n");
 | 
			
		||||
            lora_state = STATE_BROKEN;
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    case MODE_CONFIG_SLEEP:
 | 
			
		||||
    {
 | 
			
		||||
        pin_stat.pin = E220_M0_PATH;
 | 
			
		||||
        pin_stat.val = GPIO_HIGH;
 | 
			
		||||
        if (-1 == PrivWrite(pin_fd, &pin_stat, 1))
 | 
			
		||||
        {
 | 
			
		||||
            DDBG("E220LoraModeConfig-Fail: config m0 high fail!\n");
 | 
			
		||||
            lora_state = STATE_BROKEN;
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        pin_stat.pin = E220_M1_PATH;
 | 
			
		||||
        pin_stat.val = GPIO_HIGH;
 | 
			
		||||
        if (-1 == PrivWrite(pin_fd, &pin_stat, 1))
 | 
			
		||||
        {
 | 
			
		||||
            DDBG("E220LoraModeConfig-Fail: config m1 high fail!\n");
 | 
			
		||||
            lora_state = STATE_BROKEN;
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
        DDBG("E220LoraModeConfig-Fail: unsupported lora mode!\n");
 | 
			
		||||
        PrivClose(pin_fd);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    PrivTaskDelay(E220_CONFIG_DELAY);
 | 
			
		||||
    PrivClose(pin_fd);
 | 
			
		||||
    // 记录当前模式,避免不必要的重复配置
 | 
			
		||||
    current_mode = mode;
 | 
			
		||||
    DDBG("E220LoraModeConfig-Success: change to mode %s !\n",mode == MODE_CONFIG_SLEEP ? "MODE_CONFIG_SLEEP" : "MODE_TRANSFER_MODE");
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 通过指令获取硬件寄存器参数后将其保存并输出
 | 
			
		||||
 * @param adapter: 需要打开
 | 
			
		||||
*/
 | 
			
		||||
static void PrintRegister(char* prefix)
 | 
			
		||||
{       
 | 
			
		||||
    // 打印数据
 | 
			
		||||
    DDBG("%s Register:",prefix);
 | 
			
		||||
    DDBG("ADDH:%X,ADDL:%X,REG0:%X,",register_cfg.ADDH,register_cfg.ADDL,register_cfg.REG0);
 | 
			
		||||
    DDBG("REG1:%X,REG2:%X,REG3:%X,",register_cfg.REG1,register_cfg.REG2,register_cfg.REG3);
 | 
			
		||||
    DDBG("KEYH:%X,KEYL:%X\n",register_cfg.KEYH,register_cfg.KEYL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 根据配置的数据覆写硬件寄存器参数
 | 
			
		||||
 * @param adapter: 一个已经打开了UART串口的Lora硬件
 | 
			
		||||
 * @param start_addr: 需要覆写的寄存器起始地址
 | 
			
		||||
 * @param byte_length: 需要覆写的寄存器数量
 | 
			
		||||
 * @param args: 需要覆写的寄存器参数
 | 
			
		||||
 * @return: 0 -> Success; -1 -> Fail
 | 
			
		||||
*/
 | 
			
		||||
static uint8 WriteRegister(struct Adapter *adapter,uint8 start_addr,uint8 byte_length,void* args)
 | 
			
		||||
{
 | 
			
		||||
    if (STATE_BROKEN == lora_state)
 | 
			
		||||
    {
 | 
			
		||||
        DDBG("WriteRegister-Fail: lora broken!\n");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    // 切换硬件到配置模式
 | 
			
		||||
    E220LoraModeConfig(MODE_CONFIG_SLEEP);
 | 
			
		||||
    // 构造寄存器覆写数据
 | 
			
		||||
    uint8 buffer[11] = {0};
 | 
			
		||||
    buffer[0] = 0XC0;
 | 
			
		||||
    buffer[1] = start_addr;
 | 
			
		||||
    buffer[2] = byte_length;
 | 
			
		||||
    memcpy(&buffer[3],args,byte_length);
 | 
			
		||||
    // 覆写寄存器参数
 | 
			
		||||
    if (PrivWrite(adapter->fd, (void *)buffer, byte_length + 3) < 0)
 | 
			
		||||
    {
 | 
			
		||||
        PrintRegister("WriteRegister-Fail");
 | 
			
		||||
        lora_state = STATE_BROKEN;
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    // 等待配置完成后读取回写数据
 | 
			
		||||
    PrivTaskDelay(E220_CONFIG_DELAY);
 | 
			
		||||
    PrivRead(adapter->fd, buffer, byte_length + 3);             
 | 
			
		||||
    PrintRegister("WriteRegister-Success");
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 根据默认的配置对一个已经打开UART串口的Lora硬件进行寄存器参数的初始化,开启后只会被调用一次
 | 
			
		||||
 * @param adapter: 需要打开
 | 
			
		||||
*/
 | 
			
		||||
static uint8 E220RegisterInit(struct Adapter *adapter)
 | 
			
		||||
{
 | 
			
		||||
    if(STATE_BROKEN == lora_state)
 | 
			
		||||
    {
 | 
			
		||||
        DDBG("E220RegisterInit-Fail: lora broken!\n");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    if (lora_init == E220_YES)
 | 
			
		||||
    {
 | 
			
		||||
        DDBG("E220RegisterInit-Success: inited before!\n");
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    // 按照默认的参数进行寄存器初始化
 | 
			
		||||
	if (0 != WriteRegister(adapter,0X0,0X8,®ister_cfg))
 | 
			
		||||
    {
 | 
			
		||||
        lora_state = STATE_BROKEN;
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    // 防止重复初始化
 | 
			
		||||
    lora_init = E220_YES;
 | 
			
		||||
    DDBG("E220RegisterConfig-Success!\n");
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8 E220Open(struct Adapter *adapter)
 | 
			
		||||
{
 | 
			
		||||
    if (lora_state == STATE_BROKEN)
 | 
			
		||||
    {
 | 
			
		||||
        DDBG("E220Open-Fail: lora broken!\n");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    if (lora_state == STATE_OPENED)
 | 
			
		||||
    {
 | 
			
		||||
        // 避免重复打开
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    // 打开UART串口
 | 
			
		||||
    adapter->fd = PrivOpen(E220_UART_DRIVER,O_RDWR);
 | 
			
		||||
    if (adapter->fd < 0)
 | 
			
		||||
    {
 | 
			
		||||
        DDBG("E220Open-Fail: open uart fail - %s \n",E220_UART_DRIVER);
 | 
			
		||||
        lora_state = STATE_BROKEN;
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    // 按照默认串口配置信息初始化串口
 | 
			
		||||
    struct PrivIoctlCfg ioctl_cfg;
 | 
			
		||||
    ioctl_cfg.ioctl_driver_type = SERIAL_TYPE;
 | 
			
		||||
    ioctl_cfg.args = &serial_cfg;
 | 
			
		||||
    if (0 != PrivIoctl(adapter->fd, OPE_INT,&ioctl_cfg))
 | 
			
		||||
    {
 | 
			
		||||
        DDBG("E220Open-Fail: uart config fail - %s \n",E220_UART_DRIVER);
 | 
			
		||||
        lora_state = STATE_BROKEN;
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    // 硬件的寄存器初始化
 | 
			
		||||
    if (0 != E220RegisterInit(adapter))
 | 
			
		||||
    {
 | 
			
		||||
        DDBG("E220Open-Fail: register config fail!\n");
 | 
			
		||||
        lora_state = STATE_BROKEN;
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    lora_state = STATE_OPENED;
 | 
			
		||||
    DDBG("E220Open-Success: e220 open success!\n");
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8 E220Close(struct Adapter *adapter)
 | 
			
		||||
{
 | 
			
		||||
    if (lora_state != STATE_OPENED)
 | 
			
		||||
    {
 | 
			
		||||
        DDBG("E220Close-Success: broken or closed before!\n");
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    // 关闭UART串口
 | 
			
		||||
    if (adapter->fd > 0)
 | 
			
		||||
    {
 | 
			
		||||
        if (0 != PrivClose(adapter->fd))
 | 
			
		||||
        {
 | 
			
		||||
            DDBG("E220Close-Failed: result != 0\n");
 | 
			
		||||
            lora_state = STATE_BROKEN;
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    adapter->fd = -1;
 | 
			
		||||
    lora_init = E220_NO;
 | 
			
		||||
    lora_state = STATE_CLOSED;
 | 
			
		||||
    DDBG("E220Close-Success: success!\n");
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8 E220Ioctl(struct Adapter *adapter, int config_item, void *args)
 | 
			
		||||
{
 | 
			
		||||
    if (STATE_OPENED != lora_state)
 | 
			
		||||
    {
 | 
			
		||||
        DDBG("E220Ioctl-Fail: open lora or the lora is broken!\n");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    switch (config_item)
 | 
			
		||||
    {
 | 
			
		||||
    // 配置Lora串口超时时间
 | 
			
		||||
    case CONFIG_SERIAL_TIME_OUT:
 | 
			
		||||
    {
 | 
			
		||||
        int32 time_out = *((int32*)args);
 | 
			
		||||
        if (time_out == serial_cfg.serial_timeout)
 | 
			
		||||
        {
 | 
			
		||||
            DDBG("ConfigSerialTimeOut-Success: the same time_out %d !\n", time_out);
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        serial_cfg.serial_timeout = time_out;
 | 
			
		||||
        struct PrivIoctlCfg ioctl_cfg = { .ioctl_driver_type = SERIAL_TYPE,.args = &serial_cfg };
 | 
			
		||||
        // 修改串口配置信息
 | 
			
		||||
        if (0 != PrivIoctl(adapter->fd, OPE_INT, &ioctl_cfg))
 | 
			
		||||
        {
 | 
			
		||||
            DDBG("ConfigSerialTimeOut-Fail: %d\n", serial_cfg.serial_timeout);
 | 
			
		||||
            lora_state = STATE_BROKEN;
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        DDBG("ConfigSerialTimeOut-Success: %d\n", serial_cfg.serial_timeout);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    // 配置Lora模式
 | 
			
		||||
    case CONFIG_LORA_MODE:
 | 
			
		||||
    {
 | 
			
		||||
        enum LoraMode mode = *((enum LoraMode*)args);
 | 
			
		||||
        if (mode == current_mode)
 | 
			
		||||
        {
 | 
			
		||||
            DDBG("ConfigLoraMode-Success: the same mode %s!\n",mode == MODE_TRANSFER_MODE ? "MODE_TRANSFER_MODE" : "MODE_CONFIG_SLEEP");
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (mode == MODE_TRANSFER_MODE || mode == MODE_CONFIG_SLEEP)
 | 
			
		||||
        {
 | 
			
		||||
            if (0 != E220LoraModeConfig(mode))
 | 
			
		||||
            {
 | 
			
		||||
                DDBG("ConfigLoraMode-Fail: config mode fail %s!\n",mode == MODE_TRANSFER_MODE ? "MODE_TRANSFER_MODE" : "MODE_CONFIG_SLEEP");
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
            DDBG("ConfigLoraMode-Success: change lora mode to %s!\n",mode == MODE_TRANSFER_MODE ? "MODE_TRANSFER_MODE" : "MODE_CONFIG_SLEEP");
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        DDBG("ConfigLoraMode-Fail: unsupported lora mode %x!\n",mode);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    // 配置Lora传输模式
 | 
			
		||||
    case CONFIG_TRANS_MODE:
 | 
			
		||||
    {
 | 
			
		||||
        if (serial_cfg.serial_baud_rate != E220_DEFAULT_CONFIG_BAUD_RATE)
 | 
			
		||||
        {
 | 
			
		||||
            DDBG("ConfigTransMode-Fail: only config by baud rate 9600!\n");
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        uint8 trans_mode = *((uint8*)args);
 | 
			
		||||
        if (trans_mode == TRANS_MODE_HYALINE || trans_mode == TRANS_MODE_ORIENTED)
 | 
			
		||||
        {
 | 
			
		||||
            // 判断参数是否有必要覆写
 | 
			
		||||
            if (register_cfg.REG3 & 0X40 == trans_mode)
 | 
			
		||||
            {
 | 
			
		||||
                DDBG("ConfigTransMode-Success: the same trans mode %x!\n",trans_mode);
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
            trans_mode = register_cfg.REG3 & 0xBF | trans_mode;
 | 
			
		||||
            if (0 != WriteRegister(adapter,0X5,1,&trans_mode))
 | 
			
		||||
            {
 | 
			
		||||
                lora_state = STATE_BROKEN;
 | 
			
		||||
                DDBG("ConfigTransMode-Fail: config trans mode fail %x!\n",trans_mode);
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
            register_cfg.REG3 = trans_mode;
 | 
			
		||||
            DDBG("ConfigTransMode-Success: config trans mode success %x!\n",trans_mode);
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        DDBG("ConfigTransMode-Fail: unsupported trans mode %x!\n",trans_mode);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    // 配置Lora空中速率
 | 
			
		||||
    case CONFIG_AIR_RATE:
 | 
			
		||||
    {
 | 
			
		||||
        if (serial_cfg.serial_baud_rate != E220_DEFAULT_CONFIG_BAUD_RATE)
 | 
			
		||||
        {
 | 
			
		||||
            DDBG("ConfigAirRate-Fail: only config by baud rate 9600!\n");
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        uint8 air_rate = *((uint8*)args);
 | 
			
		||||
        if (air_rate >= AIR_RATE_2D4K && air_rate <= AIR_RATE_62D5K)
 | 
			
		||||
        {
 | 
			
		||||
            // 判断参数是否有必要覆写
 | 
			
		||||
            if (register_cfg.REG0 & 0X7 == air_rate)
 | 
			
		||||
            {
 | 
			
		||||
                DDBG("ConfigAirRate-Success: the same air rate %x!\n",air_rate);
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
            air_rate = ((register_cfg.REG0 >> 3) << 3) | air_rate;
 | 
			
		||||
            if (0 != WriteRegister(adapter,0X2,1,&air_rate))
 | 
			
		||||
            {
 | 
			
		||||
                lora_state = STATE_BROKEN;
 | 
			
		||||
                DDBG("ConfigAirRate-Fail: write register fail %x!\n",air_rate);
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
            register_cfg.REG0 = air_rate;
 | 
			
		||||
            DDBG("ConfigAirRate-Success: config air rate success %x!\n",air_rate & 0X7);
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        DDBG("ConfigAirRate-Fail: unsupported air rate %x!\n",air_rate);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    // 配置Lora的波特率
 | 
			
		||||
    case CONFIG_BAUD_RATE:
 | 
			
		||||
    {
 | 
			
		||||
        uint8 arg_rate = *((uint8*)args);
 | 
			
		||||
        uint8 temp_rate = arg_rate;
 | 
			
		||||
        if (arg_rate == E220_DEFAULT_CONFIG_BAUD_RATE_BYTE || arg_rate == E220_DEFAULT_USED_BAUD_RATE_BYTE)
 | 
			
		||||
        {
 | 
			
		||||
            // 判断参数是否有必要覆写
 | 
			
		||||
            if (register_cfg.REG0 & 0XE0 == arg_rate)
 | 
			
		||||
            {
 | 
			
		||||
                DDBG("ConfigBaudRate-Success: the same baud rate %x!\n",arg_rate);
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
            temp_rate = register_cfg.REG0 & 0X1F | arg_rate;
 | 
			
		||||
            // 先更改寄存器的波特率
 | 
			
		||||
            if (0 != WriteRegister(adapter,0X2,1,&temp_rate))
 | 
			
		||||
            {
 | 
			
		||||
                lora_state = STATE_BROKEN;
 | 
			
		||||
                DDBG("ConfigBaudRate-Fail: write register config fail %x!\n",temp_rate);
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
            register_cfg.REG0 = temp_rate;
 | 
			
		||||
            // 重新配置串口波特率
 | 
			
		||||
            serial_cfg.serial_baud_rate = arg_rate == E220_DEFAULT_CONFIG_BAUD_RATE_BYTE ? BAUD_RATE_9600 : BAUD_RATE_115200;
 | 
			
		||||
            struct PrivIoctlCfg ioctl_cfg;
 | 
			
		||||
            ioctl_cfg.ioctl_driver_type = SERIAL_TYPE;
 | 
			
		||||
            ioctl_cfg.args = &serial_cfg;
 | 
			
		||||
            // 重新配置串口波特率
 | 
			
		||||
            if (0 != PrivIoctl(adapter->fd, OPE_INT, &ioctl_cfg))
 | 
			
		||||
            {
 | 
			
		||||
                lora_state = STATE_BROKEN;
 | 
			
		||||
                DDBG("ConfigBaudRate-Fail: config serail fail %x!\n",temp_rate);
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
            DDBG("ConfigBaudRate-Success: config baud rate success %d !\n",arg_rate == E220_DEFAULT_CONFIG_BAUD_RATE_BYTE ? BAUD_RATE_9600 : BAUD_RATE_115200);
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        DDBG("ConfigBaudRate-Fail: unsupported baud rate %x!\n",arg_rate);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    // 配置Lora地址参数
 | 
			
		||||
    case CONFIG_ADDRESS:
 | 
			
		||||
    {
 | 
			
		||||
        if (serial_cfg.serial_baud_rate != E220_DEFAULT_CONFIG_BAUD_RATE)
 | 
			
		||||
        {
 | 
			
		||||
            DDBG("ConfigAddress-Fail: only config by baud rate 9600!\n");
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        uint16 address = *((uint16*)args);
 | 
			
		||||
        if ((address >> 8) | 0XFF == register_cfg.ADDH && address & 0XFF == register_cfg.ADDL)
 | 
			
		||||
        {
 | 
			
		||||
            DDBG("ConfigAddress-Success: the same address %d!\n",address);
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        register_cfg.ADDH = (address >> 8) & 0XFF;
 | 
			
		||||
        register_cfg.ADDL = address & 0XFF;
 | 
			
		||||
        if (0 != WriteRegister(adapter,0X0,2,®ister_cfg))
 | 
			
		||||
        {
 | 
			
		||||
            lora_state = STATE_BROKEN;
 | 
			
		||||
            DDBG("ConfigAddress-Fail: write register fail %d!\n",address);
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        DDBG("ConfigAddress-Success: config address success %d!\n",address);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    // 配置Lora信道参数
 | 
			
		||||
    case CONFIG_CHANNEL:
 | 
			
		||||
    {
 | 
			
		||||
        if (serial_cfg.serial_baud_rate != E220_DEFAULT_CONFIG_BAUD_RATE)
 | 
			
		||||
        {
 | 
			
		||||
            DDBG("ConfigChannel-Fail: only config by baud rate 9600!\n");
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        uint8 channel = *((uint8*)args);
 | 
			
		||||
        if (channel <= E220_MAX_CHANNEL_NUMBER)
 | 
			
		||||
        {
 | 
			
		||||
            if (register_cfg.REG2 == channel)
 | 
			
		||||
            {
 | 
			
		||||
                DDBG("ConfigChannel-Success: the same channel %d!\n",channel);
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
            register_cfg.REG2 = channel;
 | 
			
		||||
            if (0 != WriteRegister(adapter,0X4,1,®ister_cfg.REG2))
 | 
			
		||||
            {
 | 
			
		||||
                lora_state = STATE_BROKEN;
 | 
			
		||||
                DDBG("ConfigChannel-Fail: write register fail %d!\n",channel);
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
            DDBG("ConfigChannel-Success: config channel success %X !\n",channel);
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        DDBG("ConfigChannel-Fail: unsupported channel %d!\n",channel);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    // 打印配置信息
 | 
			
		||||
    case CONFIG_SHOW:
 | 
			
		||||
    {
 | 
			
		||||
        DDBG("ConfigShow SerailTime: %d",serial_cfg.serial_timeout);
 | 
			
		||||
        PrintRegister("CONFIG_SHOW");
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
        DDBG("E220Ioctl-Failed: config item unsupported %d!\n",config_item);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8 E220Send(struct Adapter *adapter, const void *buf, uint32 len)
 | 
			
		||||
{
 | 
			
		||||
    if (STATE_OPENED != lora_state)
 | 
			
		||||
    {
 | 
			
		||||
        DDBG("E220Send-Fail: open lora before send data!\n");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    // 切换至传输模式
 | 
			
		||||
    if (0 != E220LoraModeConfig(MODE_TRANSFER_MODE))
 | 
			
		||||
    {
 | 
			
		||||
        DDBG("E220Send-Fail: change mode transfer fail!\n");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (len != PrivWrite(adapter->fd, (void *)buf, len))
 | 
			
		||||
    {
 | 
			
		||||
        DDBG("E220Send-Fail: send data fail!\n");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;    
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8 E220Recv(struct Adapter *adapter, void *buf, uint32 len)
 | 
			
		||||
{
 | 
			
		||||
    if (STATE_OPENED != lora_state)
 | 
			
		||||
    {
 | 
			
		||||
        DDBG("E220Recv-Fail: open lora before recv!\n");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    // 确保传输模式
 | 
			
		||||
    if (0 != E220LoraModeConfig(MODE_TRANSFER_MODE))
 | 
			
		||||
    {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int recv_len = 0, recv_len_continue = 0;
 | 
			
		||||
 | 
			
		||||
    uint8 *recv_buf = PrivMalloc(len);
 | 
			
		||||
    // 读取数据并返回已读取数据量
 | 
			
		||||
    recv_len = PrivRead(adapter->fd, recv_buf, len);
 | 
			
		||||
    if (recv_len) {
 | 
			
		||||
        while (recv_len < len) {
 | 
			
		||||
            // 读取超时后退出循环
 | 
			
		||||
            recv_len_continue = PrivRead(adapter->fd, recv_buf + recv_len, len - recv_len);
 | 
			
		||||
            if (recv_len_continue) {
 | 
			
		||||
                recv_len += recv_len_continue;
 | 
			
		||||
            } else {
 | 
			
		||||
                recv_len = 0;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        memcpy(buf, recv_buf, len);
 | 
			
		||||
    }
 | 
			
		||||
    PrivFree(recv_buf);
 | 
			
		||||
    return recv_len;  
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,146 @@
 | 
			
		|||
/*
 | 
			
		||||
* Copyright (c) 2020 AIIT XUOS Lab
 | 
			
		||||
* XiUOS is licensed under Mulan PSL v2.
 | 
			
		||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
 | 
			
		||||
* You may obtain a copy of Mulan PSL v2 at:
 | 
			
		||||
*        http://license.coscl.org.cn/MulanPSL2
 | 
			
		||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 | 
			
		||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 | 
			
		||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 | 
			
		||||
* See the Mulan PSL v2 for more details.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
* @file:    e220.h
 | 
			
		||||
* @brief:   E220 module underlying related configuration driver functions declarations
 | 
			
		||||
* @version: 0.1
 | 
			
		||||
* @author:  YUNFEI CHU
 | 
			
		||||
* @date:    2023/10/01 
 | 
			
		||||
*
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include <transform.h>
 | 
			
		||||
#include <adapter.h>
 | 
			
		||||
 | 
			
		||||
#ifndef __E220_H__
 | 
			
		||||
#define __E220_H__
 | 
			
		||||
 | 
			
		||||
// #define __DRIVER_DEBUG__                             // 驱动调试信息输出开关
 | 
			
		||||
#ifdef __DRIVER_DEBUG__  
 | 
			
		||||
#define DDBG(format,...) printf(format, ##__VA_ARGS__)  
 | 
			
		||||
#else  
 | 
			
		||||
#define DDBG(format,...)  
 | 
			
		||||
#endif // __DRIVER_DEBUG__
 | 
			
		||||
 | 
			
		||||
#define E220_M0_PATH 32                                  // M0管脚位置
 | 
			
		||||
#define E220_M1_PATH 33                                  // M1管脚位置
 | 
			
		||||
#define E220_PIN_DRIVER "/dev/pin_dev"                   // PIN驱动名称
 | 
			
		||||
#define E220_UART_DRIVER "/dev/uart2_dev2"               // UART驱动名称
 | 
			
		||||
#define E220_ADDRESS 0XA                                 // 地址默认值
 | 
			
		||||
#define E220_DAFAULT_SERIAL_TIMEOUT 3000                 // 串口默认3S超时
 | 
			
		||||
#define E220_DEFAULT_CONFIG_BAUD_RATE BAUD_RATE_9600     // 在进行寄存器参数配置时,除了波特率本身的配置外,其他的配置只能在9600进行配置
 | 
			
		||||
#define E220_DEFAULT_CONFIG_BAUD_RATE_BYTE 0X60          // 9600波特率
 | 
			
		||||
#define E220_DEFAULT_USED_BAUD_RATE BAUD_RATE_115200     // 在进行使用时可以切换为该波特率,但是在配置时需要切换回配置波特率(9600)
 | 
			
		||||
#define E220_DEFAULT_USED_BAUD_RATE_BYTE 0XE0            // 115200波特率
 | 
			
		||||
 | 
			
		||||
#define E220_MAX_CHANNEL_NUMBER 83                       // E220最多支持84个信道,0~83
 | 
			
		||||
#define E220_DEFAULT_CHANNEL 0XA                         // 默认通道
 | 
			
		||||
 | 
			
		||||
#define E220_DEFAULT_AIR_RATE 0X0                        // 默认模块以2.4K的空中速率进行通信
 | 
			
		||||
 | 
			
		||||
#define E220_DEFAULT_TRANS_MODE 0X40                     // 默认使用定点传输传输模式
 | 
			
		||||
#define E220_CONFIG_DELAY 1000                           // 配置的硬件响应时间
 | 
			
		||||
 | 
			
		||||
#define E220_YES 0XFF
 | 
			
		||||
#define E220_NO 0X00
 | 
			
		||||
 | 
			
		||||
enum LoraMode                        // E220工作模式,支持四种,配置模式与休眠模式一致
 | 
			
		||||
{
 | 
			
		||||
    MODE_TRANSFER_MODE = 0,          // M0 : M1 = 0 : 0 可发可收
 | 
			
		||||
    MODE_WOR_SEND,                   // M0 : M1 = 1 : 0 可发可收
 | 
			
		||||
    MODE_WOR_RECV,                   // M0 : M1 = 0 : 1 可收不发
 | 
			
		||||
    MODE_CONFIG_SLEEP                // M0 : M1 = 1 : 1 不收不发
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum LoraState                       // 硬件状态
 | 
			
		||||
{
 | 
			
		||||
    STATE_CLOSED,                    // 硬件关闭
 | 
			
		||||
    STATE_OPENED,                    // 已正常开启
 | 
			
		||||
    STATE_BROKEN                     // 硬件损坏
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum LoraTransMode                   // 模块传输模式
 | 
			
		||||
{
 | 
			
		||||
    TRANS_MODE_HYALINE = 0X0,        // 透明传输
 | 
			
		||||
    TRANS_MODE_ORIENTED = 0X40,      // 定点传输
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum LoraAirRate                     // 空中速率,通信双方必须设置一致的空中速率方可进行通信
 | 
			
		||||
{
 | 
			
		||||
    AIR_RATE_2D4K = 0X0,             // 2.4K
 | 
			
		||||
    AIR_RATE_4D8K = 0X3,             // 4.8K
 | 
			
		||||
    AIR_RATE_9D6K = 0X4,             // 9.6K
 | 
			
		||||
    AIR_RATE_19D2K = 0X5,            // 19.2K
 | 
			
		||||
    AIR_RATE_38D4K = 0X6,            // 38.4K
 | 
			
		||||
    AIR_RATE_62D5K = 0X7             // 62.5K
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum LoraConfig                      // 支持IOCTL函数进行配置的配置项枚举,某些项只支持9600波特率配置
 | 
			
		||||
{
 | 
			
		||||
    CONFIG_SERIAL_TIME_OUT = 0X0,    // 串口超时时间
 | 
			
		||||
    CONFIG_LORA_MODE = 0X1,          // 配置Lora模式
 | 
			
		||||
    CONFIG_TRANS_MODE = 0X2,         // 配置Lora传输模式(9600)
 | 
			
		||||
    CONFIG_AIR_RATE = 0X3,           // 配置空中速率(9600)
 | 
			
		||||
    CONFIG_BAUD_RATE = 0X4,          // 配置波特率
 | 
			
		||||
    CONFIG_ADDRESS = 0X5,            // 配置设备地址(9600)
 | 
			
		||||
    CONFIG_CHANNEL = 0X6,            // 配置设备信道(9600)
 | 
			
		||||
    CONFIG_SHOW = 0X7                // 打印配置信息
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern enum LoraMode current_mode;   // 当前模块处于什么模式
 | 
			
		||||
extern enum LoraState lora_state;    // 当前模块的状态
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 打开硬件设施,开启串口并按照配置的初始化参数配置相关硬件、串口
 | 
			
		||||
 *         重复开启会直接返回,开启失败后不能重复尝试
 | 
			
		||||
 * @param adapter: 任何状态
 | 
			
		||||
 * @return: 0 -> Success : -1 -> Fail
 | 
			
		||||
*/
 | 
			
		||||
uint8 E220Open(struct Adapter *adapter);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 关闭硬件设施,关闭串口,同时会将所有的数据回复到原始的配置状态
 | 
			
		||||
 *         关闭失败默认为硬件损坏,未开启不可关闭
 | 
			
		||||
 * @param adapter: 任何状态
 | 
			
		||||
 * @return: 0 -> Success (硬件不是开启状态即成功): -1 -> Fail (硬件关闭过错报错)
 | 
			
		||||
*/
 | 
			
		||||
uint8 E220Close(struct Adapter *adapter);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 给处于开启状态的硬件配置相关信息,包括串口配置,寄存器参数等等
 | 
			
		||||
 *         支持的配置类型为@LoraConfig
 | 
			
		||||
 * @param adapter: 打开状态
 | 
			
		||||
 * @param config_item: 配置类型
 | 
			
		||||
 * @param args: 配置参数
 | 
			
		||||
 * @return: 0 -> Success : -1 -> Fail 
 | 
			
		||||
*/
 | 
			
		||||
uint8 E220Ioctl(struct Adapter *adapter, int config_item, void *args);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 通过硬件发送指定长度的数据
 | 
			
		||||
 * @param adapter: 打开状态
 | 
			
		||||
 * @param buf: 数据缓存区
 | 
			
		||||
 * @param len: 缓存区大小
 | 
			
		||||
 * @return 0 -> Success(全部发送完毕) : -1 -> Fail(未发送或发送不完整)
 | 
			
		||||
*/
 | 
			
		||||
uint8 E220Send(struct Adapter *adapter, const void *buf, uint32 len);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 通过硬件接受指定长度的数据到缓冲区
 | 
			
		||||
 * @param adapter: 打开状态
 | 
			
		||||
 * @param buf: 接受缓冲区
 | 
			
		||||
 * @param len: 需要接受的数据长度,该长度小于等于接受缓存区长度
 | 
			
		||||
 * @return 0 -> 接收到了指定长度的数据 : -1 -> 未接受到指定长度的数据或者串口超时
 | 
			
		||||
*/
 | 
			
		||||
uint8 E220Recv(struct Adapter *adapter, void *buf, uint32 len);
 | 
			
		||||
#endif // __E220_H__
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,324 @@
 | 
			
		|||
/*
 | 
			
		||||
* Copyright (c) 2020 AIIT XUOS Lab
 | 
			
		||||
* XiUOS is licensed under Mulan PSL v2.
 | 
			
		||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
 | 
			
		||||
* You may obtain a copy of Mulan PSL v2 at:
 | 
			
		||||
*        http://license.coscl.org.cn/MulanPSL2
 | 
			
		||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 | 
			
		||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 | 
			
		||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 | 
			
		||||
* See the Mulan PSL v2 for more details.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
* @file:    lora_mac.h
 | 
			
		||||
* @brief:   Definition of data structures related to the Lora private protocol
 | 
			
		||||
* @version: 0.1
 | 
			
		||||
* @author:  YUNFEI CHU
 | 
			
		||||
* @date:    2023/10/01 
 | 
			
		||||
*
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include <transform.h>
 | 
			
		||||
#include <adapter.h>
 | 
			
		||||
#include "../lora_driver/e220.h"
 | 
			
		||||
 | 
			
		||||
// #define AS_LORA_GATEWAY
 | 
			
		||||
 | 
			
		||||
/*数据帧**************************************************************************/
 | 
			
		||||
#define FRAME_BEGIN_MARK 0x3C3C                                              // 帧开始标志,高低字节相同
 | 
			
		||||
#define FRAME_END_MARK 0X5A5A                                                // 帧结束标志,高低字节相同
 | 
			
		||||
#define FRAME_MAX_DATA_LENGTH 200                                            // 默认支持的最大数据包为200字节
 | 
			
		||||
#define FRAME_ADDR_INFO_LENGTH 3                                             // 定点传输时前三字节为地址和信道共三个字节
 | 
			
		||||
#define FRAME_META_LENGTH 14                                                 // 数据帧相关信息长度
 | 
			
		||||
#define FRAME_MAX_USER_DATA_LENGTH FRAME_MAX_DATA_LENGTH - FRAME_META_LENGTH - FRAME_ADDR_INFO_LENGTH
 | 
			
		||||
#define FRAME_CRC_INDEX_BEGIN 2                                              // CRC校验开始地址
 | 
			
		||||
#define FRAME_CRC_LENGTH 8                                                   // CRC校验元数据量长度
 | 
			
		||||
#define FRAME_BYTE_OK 0XFF                                                   // 是/成功
 | 
			
		||||
#define FRAME_BYTE_NO 0X00                                                   // 否/失败
 | 
			
		||||
#define FRAME_NECK_LENGTH 8                                                  // 帧头以下,额外数据以上的长度
 | 
			
		||||
#define FRAME_FOOT_LENGTH 4                                                  // 额外数据以下至帧尾的数据长度
 | 
			
		||||
#define FRAME_MARK_LENGTH 2                                                  // 帧头帧尾的数据长度
 | 
			
		||||
 | 
			
		||||
enum FrameType                  // 数据帧类型枚举
 | 
			
		||||
{
 | 
			
		||||
    E_G_JOIN = 0,               // 客户端入网请求,需回复
 | 
			
		||||
    E_G_QUIT,                   // 客户端退网请求,不需回复
 | 
			
		||||
    E_G_DATA,                   // 客户端上传数据,需回复
 | 
			
		||||
    G_E_ANS,                    // 网关响应客户端请求
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct LoraFrame                // 数据帧消息类型
 | 
			
		||||
{
 | 
			
		||||
    uint16 begin_mark;          // 开始标志 0x3C3C
 | 
			
		||||
    uint16 session_id;          // 会话ID,客户端每次数据上传会随机设置,若是回复ID不一致则表示数据丢失
 | 
			
		||||
    uint16 dev_addr;            // 设备地址,特指终端地址,网关地址固定为 0XFFFF
 | 
			
		||||
    uint8 down_channel;         // 下行通道,即终端所在的信道
 | 
			
		||||
    uint8 frame_type;           // 数据帧的类型
 | 
			
		||||
    uint8 frame_confirm;        // 该数据帧是否需要确认回复
 | 
			
		||||
    uint8 frame_attach;         // 用于携带简单的数据,例如用户数据长度
 | 
			
		||||
    uint8 *user_data;           // 需要额外携带的大量数据
 | 
			
		||||
    uint8 crc_hi;               // 校验位高字节
 | 
			
		||||
    uint8 crc_lo;               // 校验位低字节
 | 
			
		||||
    uint16 end_mark;            // 结束标志 0X5A5A
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern struct LoraFrame send_frame;                             // 数据帧发送缓冲区
 | 
			
		||||
extern struct LoraFrame recv_frame;                             // 数据帧接受缓存区
 | 
			
		||||
extern uint8 send_data_buffer[FRAME_MAX_USER_DATA_LENGTH];      // 数据发送缓冲区
 | 
			
		||||
extern uint8 recv_data_buffer[FRAME_MAX_USER_DATA_LENGTH];      // 数据接受缓存区
 | 
			
		||||
extern uint8 total_data_buffer[FRAME_MAX_DATA_LENGTH];          // 数据帧加数据的最大缓冲区
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 将数据帧缓冲区置为 0 ,并添加帧头,帧尾数据
 | 
			
		||||
 * @param frame: 数据帧缓冲区
 | 
			
		||||
*/
 | 
			
		||||
void InitLoraFrame(struct LoraFrame* frame);                  
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 打印数据帧结构信息,便于运行、开发调试
 | 
			
		||||
 * @param fram: 需要打印的数据帧
 | 
			
		||||
 * @param prefix: 那里打印的数据帧信息
 | 
			
		||||
*/
 | 
			
		||||
void ShowLoraFrame(struct LoraFrame* frame,char* prefix);                   
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 计算数据帧的校验值,除去帧头、帧尾、校验值本身,若是应该携带数据的类型未携带数据时报错
 | 
			
		||||
 * @return: 消息类型和数据不匹配时为 -1
 | 
			
		||||
*/
 | 
			
		||||
uint8 CalCrlLoraFrame(struct LoraFrame* frame);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 校验数据帧的完整性
 | 
			
		||||
 * @return: 校验失败为 -1
 | 
			
		||||
*/
 | 
			
		||||
uint8 CheckCrcLoraFrame(struct LoraFrame* frame);
 | 
			
		||||
/*数据帧**************************************************************************/
 | 
			
		||||
 | 
			
		||||
/*基础操作************************************************************************/
 | 
			
		||||
#define LORA_ADAPTER_NAME "e220"                // Lora适配器名称
 | 
			
		||||
#define LORA_OK 0XFF                            // 标识成功、确认等
 | 
			
		||||
#define LORA_NO 0X00                            // 标识失败、丢失等
 | 
			
		||||
#define LORA_TIME_ON_AIR 2                      // 估计的空中时间
 | 
			
		||||
#define LORA_AIR_RATE 0                         // 模块的空中速率
 | 
			
		||||
#define LORA_FRAME_RETRY 5                      // 数据帧重传次数,超过这个次数判定断网
 | 
			
		||||
#define LORA_GATEWAY_MAX_NODE 20                // 每个网关支持的最大客户端个数,防止上行通道过于拥挤
 | 
			
		||||
#define LORA_GATEWAY_ADDRESS 0XFFFF             // 网关地址,单信道网关的监听地址
 | 
			
		||||
 | 
			
		||||
#ifdef AS_LORA_GATEWAY
 | 
			
		||||
 | 
			
		||||
#define LORA_RECV_TIME 10                       // 串口超时时间
 | 
			
		||||
#define LORA_GATEWAY_CHANNEL 0XA                // 网关默认信道
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
#define LORA_ADDRESS 0X9                        // 节点默认地址
 | 
			
		||||
#define LORA_RECV_TIME 5                        // 节点默认串口超时时间
 | 
			
		||||
#define LORA_DOWN_CHANNEL 0X9                   // 节点默认下行信道
 | 
			
		||||
#define LORA_UP_CHANNEL 0XA                     // 节点默认上行通道
 | 
			
		||||
#define LORA_ADR_ENABLE LORA_NO                 // 节点是否开启网关自动搜索
 | 
			
		||||
#define LORA_RECONNECT_ENABLE LORA_OK           // 节点是否开启断网自动重联
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 从硬件获取一个完整的数据帧
 | 
			
		||||
 * @param recv_buffer: 数据帧缓冲区,在检测到带数据的数据帧时会自动关联数据缓冲区
 | 
			
		||||
 * @param prefix: 调用位置,便于调试
 | 
			
		||||
 * @return 0: 成功接受到一个数据帧(从侦测到数据帧头部开始、成功接受到帧尾部且数据校验成功)
 | 
			
		||||
 *         -1:串口超时;数据帧接受不完整;数据帧完整性校验失败;
 | 
			
		||||
*/
 | 
			
		||||
uint8 LoraRecvFrame(struct Adapter* adapter, struct LoraFrame* recv_buffer);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 通过硬件向指定的位置的信道发送数据
 | 
			
		||||
 * @param send_buffer: 数据帧缓冲区,在检测到带数据的数据帧时会自动发送关联数据缓冲区
 | 
			
		||||
 * @param addr: 目标的地址
 | 
			
		||||
 * @param channel: 目标的信道
 | 
			
		||||
 * @return 0: 成功发送指定长度的数据
 | 
			
		||||
 *         -1:未能成功发送制定数据
 | 
			
		||||
*/
 | 
			
		||||
uint8 LoraSendFrame(struct Adapter* adapter, struct LoraFrame* send_buffer,uint16 addr,uint8 channel);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 在一轮超时后未能接收到信号时判定信道空闲
 | 
			
		||||
 * @param download_channel: 下行信道
 | 
			
		||||
 * @param upload_channel: 上行信道
 | 
			
		||||
*/
 | 
			
		||||
void SignalDetector(struct Adapter *adapter,uint8 download_channel,uint8 upload_channel);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 获取随机数,用于客户端和网关之间的会话
 | 
			
		||||
*/
 | 
			
		||||
uint16 GetSessionId(void);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 获取操作函数集合,客户端和网关各自实现
 | 
			
		||||
*/
 | 
			
		||||
AdapterProductInfoType LoraAttach(struct Adapter *adapter);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 注册函数,需要各自实现
 | 
			
		||||
*/
 | 
			
		||||
uint8 UsrAdapterLoraRegister(struct Adapter *adapter);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 框架初始化函数,需要替换掉原有的初始化函数
 | 
			
		||||
*/
 | 
			
		||||
int UsrAdapterLoraInit(void);
 | 
			
		||||
 | 
			
		||||
/*基础操作************************************************************************/
 | 
			
		||||
 | 
			
		||||
/*客户端操作************************************************************************/
 | 
			
		||||
enum EndNodeState                     //节点状态
 | 
			
		||||
{
 | 
			
		||||
    NODE_STATE_DISCONNECT = 0,        // 硬件开启,但是没有联网,后台程序可能开启
 | 
			
		||||
    NODE_STATE_CONNECT,               // 硬件开启,已经联网,后台程序可能开启
 | 
			
		||||
    NODE_STATE_BROKEN,                // 硬件损坏,无后台程序
 | 
			
		||||
    NODE_STATE_CLOSED                 // 硬件关闭,无后台程序
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct EndNodeParam                   // 节点参数结构
 | 
			
		||||
{
 | 
			
		||||
    uint16 dev_addr;                  // 设备地址信息
 | 
			
		||||
    uint8 down_channel;               // 下行通道,终端可以接收信息的信道
 | 
			
		||||
    uint8 upload_channel;             // 上行通道,终端可以给网关发送信息的信道
 | 
			
		||||
    uint16 session_id;                // 会话ID,用于判定数据帧是否存在丢失
 | 
			
		||||
 | 
			
		||||
    enum EndNodeState node_state;     // 客户端状态
 | 
			
		||||
    enum LoraAirRate air_rate;        // 终端的空中速率
 | 
			
		||||
    uint8 adr_enable;                 // 终端是否开启自动寻找上行网关
 | 
			
		||||
    uint8 reconnect_enable;           // 终端断网自动重联
 | 
			
		||||
    uint8 recv_time;                  // 接受下行消息的时间窗口长度,单位为秒
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern struct EndNodeParam node_param;                                      // 客户端参数
 | 
			
		||||
extern int (*node_handlers[])(struct Adapter*,struct LoraFrame*);           // 客户端处理流程
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 打开终端硬件,按照配置的CLASS决定是否开启硬件接收和后台处理程序
 | 
			
		||||
 * @return: 0 -> Success : -1 -> Fail
 | 
			
		||||
*/
 | 
			
		||||
int NodeOpen(struct Adapter *adapter);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 关闭终端硬件设施和后台处理程序
 | 
			
		||||
 * @return: 0 -> Success : -1 -> Fail
 | 
			
		||||
*/
 | 
			
		||||
int NodeClose(struct Adapter *adapter);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 终端接入网关,向该上行通道发送入网请求
 | 
			
		||||
 * @param adapter: 已经打开的适配器
 | 
			
		||||
 * @param net_group: 需要加入的网络编号
 | 
			
		||||
 * @return: 0 -> Success : -1 -> Fail
 | 
			
		||||
*/
 | 
			
		||||
int NodeJoin(struct Adapter *adapter, unsigned char *net_group);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 发送数据到网关
 | 
			
		||||
 * @param adapter: 已经联网的适配器
 | 
			
		||||
 * @param buf: 待发送数据
 | 
			
		||||
 * @param len: 待发送数据长度
 | 
			
		||||
 * @return: 0 -> Success : -1 -> Fail
 | 
			
		||||
*/
 | 
			
		||||
int NodeSend(struct Adapter *adapter, const void *buf, size_t len);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 终端退出某个网络
 | 
			
		||||
 * @param adapter: 已经联网的适配器
 | 
			
		||||
 * @param net_group: 需要退出的网络编号
 | 
			
		||||
 * @return: 0 -> Success : -1 -> Fail
 | 
			
		||||
*/
 | 
			
		||||
int NodeQuit(struct Adapter *adapter, unsigned char *net_group);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 打印终端的信息
 | 
			
		||||
*/
 | 
			
		||||
int NodeNetState(struct Adapter *adapter);
 | 
			
		||||
 | 
			
		||||
/*客户端操作************************************************************************/
 | 
			
		||||
 | 
			
		||||
/*网关操作*************************************************************************/
 | 
			
		||||
struct EndNodeInfo                                         // 网关存储的客户端信息
 | 
			
		||||
{
 | 
			
		||||
    uint16 node_addr;                                      // 客户端地址
 | 
			
		||||
    uint8 node_down_channel;                               // 客户端下行通道
 | 
			
		||||
    uint16 session_id;                                     // 上一个命令的会话ID
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum GatewayState                   // 网关的状态枚举
 | 
			
		||||
{
 | 
			
		||||
    GATEWAY_CLOSED,                 // Lora还没有打开
 | 
			
		||||
    GATEWAY_OPENED,                 // 已打开,但是未开始工作
 | 
			
		||||
    GATEWAY_WORKING,                // 正常打开且已经开始正常工作
 | 
			
		||||
    GATEWAY_BROKEN                  // 模块损坏
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GatewayParam                                                   // 网关参数
 | 
			
		||||
{
 | 
			
		||||
    uint16 dev_addr;                                                  // 设备ID
 | 
			
		||||
    uint8 channel;                                                    // 网络编号
 | 
			
		||||
 | 
			
		||||
    int recv_time;                                                    // 串口超时时间
 | 
			
		||||
    enum LoraAirRate air_rate;                                        // 网关空中速率
 | 
			
		||||
    uint8 frame_retry;                                                // 重传次数
 | 
			
		||||
    enum GatewayState gateway_state;                                  // 网关状态
 | 
			
		||||
    struct EndNodeInfo *node_infos[LORA_GATEWAY_MAX_NODE];            // 客户端信息
 | 
			
		||||
    uint16 node_count;                                                // 已链接客户端数量
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern struct GatewayParam gateway_param;                             // 网关参数
 | 
			
		||||
extern int (*gateway_handlers[])(struct Adapter*,struct LoraFrame*);  // 处理程序
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 打开Lora硬件设施,但是不会将其接入网络中
 | 
			
		||||
*/
 | 
			
		||||
int GatewayOpen(struct Adapter *adapter);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 关闭硬件设施,需要先断网
 | 
			
		||||
*/
 | 
			
		||||
int GatewayClose(struct Adapter *adapter);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 配置网关参数的配置接口,这个函数配置的是网关的参数
 | 
			
		||||
*/
 | 
			
		||||
int GatewayIoctl(struct Adapter *adapter, int cmd, void *args);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 启用网关,使其可以为其他终端服务
 | 
			
		||||
*/
 | 
			
		||||
int GatewaySetup(struct Adapter *adapter);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 停用网关,但是不会给网络中的终端下线通知
 | 
			
		||||
*/
 | 
			
		||||
int GatewaySetDown(struct Adapter *adapter);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 打印网关相关信息
 | 
			
		||||
*/
 | 
			
		||||
int GatewayNetState(struct Adapter *adapter);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 网关主任务,负责终端各项事务的自动化处理
 | 
			
		||||
*/
 | 
			
		||||
void* GatewayTask(void* adapter);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 处理终端入网请求
 | 
			
		||||
*/
 | 
			
		||||
int GatewayJoinHandler(struct Adapter* adapter,struct LoraFrame* frame);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 处理终端退网
 | 
			
		||||
*/
 | 
			
		||||
int GatewayQuitHandler(struct Adapter* adapter,struct LoraFrame* frame);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 处理终端数据传输
 | 
			
		||||
*/
 | 
			
		||||
int GatewayDataSendHandler(struct Adapter* adapter,struct LoraFrame* frame);
 | 
			
		||||
 | 
			
		||||
/*网关操作*************************************************************************/
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,117 @@
 | 
			
		|||
/*
 | 
			
		||||
* Copyright (c) 2020 AIIT XUOS Lab
 | 
			
		||||
* XiUOS is licensed under Mulan PSL v2.
 | 
			
		||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
 | 
			
		||||
* You may obtain a copy of Mulan PSL v2 at:
 | 
			
		||||
*        http://license.coscl.org.cn/MulanPSL2
 | 
			
		||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 | 
			
		||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 | 
			
		||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 | 
			
		||||
* See the Mulan PSL v2 for more details.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
* @file:    test_lora_net_final.c
 | 
			
		||||
* @brief:   Private protocol test functions
 | 
			
		||||
* @version: 0.1
 | 
			
		||||
* @author:  YUNFEI CHU
 | 
			
		||||
* @date:    2023/10/01 
 | 
			
		||||
*
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include "test_lora_net_final.h"
 | 
			
		||||
 | 
			
		||||
#ifdef AS_LORA_GATEWAY
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 网关测试函数
 | 
			
		||||
 * 1. 打开Lora硬件设施.
 | 
			
		||||
 * 2. 接入网络.
 | 
			
		||||
*/
 | 
			
		||||
void TestGateway(void)
 | 
			
		||||
{
 | 
			
		||||
    struct Adapter* gateway_adapter = (struct Adapter*)AdapterDeviceFindByName(ADAPTER_LORA_NAME);
 | 
			
		||||
    // 打开设备
 | 
			
		||||
    if (0 != AdapterDeviceOpen(gateway_adapter))
 | 
			
		||||
    {
 | 
			
		||||
        printf("TestGateway-Fail: open lora fail!\n");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    printf("TestGateway-Info: open lora success!\n");
 | 
			
		||||
    // 接入网络
 | 
			
		||||
    if (0 != AdapterDeviceSetUp(gateway_adapter))
 | 
			
		||||
    {
 | 
			
		||||
        printf("TestGateway-Fail: setup net fail!\n");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    printf("TestGateway-Success: gateway set up success!\n");
 | 
			
		||||
    AdapterDeviceNetstat(gateway_adapter);
 | 
			
		||||
}
 | 
			
		||||
PRIV_SHELL_CMD_FUNCTION(TestGateway,TestGateway, PRIV_SHELL_CMD_MAIN_ATTR);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 打印网关信息
 | 
			
		||||
*/
 | 
			
		||||
void TestGatewayInfo(void)
 | 
			
		||||
{
 | 
			
		||||
    struct Adapter* gateway_adapter = (struct Adapter*)AdapterDeviceFindByName(ADAPTER_LORA_NAME);
 | 
			
		||||
    AdapterDeviceNetstat(gateway_adapter);
 | 
			
		||||
}
 | 
			
		||||
PRIV_SHELL_CMD_FUNCTION(TestGatewayInfo,TestGatewayInfo, PRIV_SHELL_CMD_MAIN_ATTR);
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 节点测试函数
 | 
			
		||||
 * 1. 打开Lora硬件设施.
 | 
			
		||||
 * 2. 连接到网关.
 | 
			
		||||
*/
 | 
			
		||||
void TestNodeUp(void)
 | 
			
		||||
{
 | 
			
		||||
    struct Adapter* node_adapter = (struct Adapter*)AdapterDeviceFindByName(ADAPTER_LORA_NAME);
 | 
			
		||||
    // 打开设备
 | 
			
		||||
    if (0 != AdapterDeviceOpen(node_adapter))
 | 
			
		||||
    {
 | 
			
		||||
        printf("TestNodeUp-Fail: open lora fail!\n");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    AdapterDeviceNetstat(node_adapter);
 | 
			
		||||
    // 接入网络
 | 
			
		||||
    uint8 net_id = 0XA;
 | 
			
		||||
    if (0 != AdapterDeviceJoin(node_adapter,&net_id))
 | 
			
		||||
    {
 | 
			
		||||
        printf("TestNodeUp-Fail: join net fail!\n");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    AdapterDeviceNetstat(node_adapter);
 | 
			
		||||
}
 | 
			
		||||
PRIV_SHELL_CMD_FUNCTION(TestNodeUp,TestNodeUp, PRIV_SHELL_CMD_MAIN_ATTR);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 发送数据
 | 
			
		||||
*/
 | 
			
		||||
void TestNodeSend(void)
 | 
			
		||||
{
 | 
			
		||||
    char* data = "Hello,Gateway-OtherNode!";
 | 
			
		||||
    struct Adapter* node_adapter = (struct Adapter*)AdapterDeviceFindByName(ADAPTER_LORA_NAME);
 | 
			
		||||
    AdapterDeviceSend(node_adapter,data,strlen(data));
 | 
			
		||||
    AdapterDeviceNetstat(node_adapter);
 | 
			
		||||
}
 | 
			
		||||
PRIV_SHELL_CMD_FUNCTION(TestNodeSend,TestNodeSend, PRIV_SHELL_CMD_MAIN_ATTR);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 循环发送数据
 | 
			
		||||
*/
 | 
			
		||||
void TestNodeSendCycle(void)
 | 
			
		||||
{
 | 
			
		||||
    int cycle_count = 5;
 | 
			
		||||
    char* data = "Hello,Gateway-OtherNode-Cycle!";
 | 
			
		||||
    struct Adapter *node_adapter = (struct Adapter *)AdapterDeviceFindByName(ADAPTER_LORA_NAME);
 | 
			
		||||
    while (cycle_count -- > 0)
 | 
			
		||||
    {
 | 
			
		||||
        AdapterDeviceSend(node_adapter, data, strlen(data));
 | 
			
		||||
        AdapterDeviceNetstat(node_adapter);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
PRIV_SHELL_CMD_FUNCTION(TestNodeSendCycle,TestNodeSendCycle, PRIV_SHELL_CMD_MAIN_ATTR);
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,49 @@
 | 
			
		|||
/*
 | 
			
		||||
* Copyright (c) 2020 AIIT XUOS Lab
 | 
			
		||||
* XiUOS is licensed under Mulan PSL v2.
 | 
			
		||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
 | 
			
		||||
* You may obtain a copy of Mulan PSL v2 at:
 | 
			
		||||
*        http://license.coscl.org.cn/MulanPSL2
 | 
			
		||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 | 
			
		||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 | 
			
		||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 | 
			
		||||
* See the Mulan PSL v2 for more details.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
* @file:    test_lora_net_final.h
 | 
			
		||||
* @brief:   Private protocol test function definition
 | 
			
		||||
* @version: 0.1
 | 
			
		||||
* @author:  YUNFEI CHU
 | 
			
		||||
* @date:    2023/10/01 
 | 
			
		||||
*
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include <transform.h>
 | 
			
		||||
#include <adapter.h>
 | 
			
		||||
 | 
			
		||||
// #define AS_LORA_GATEWAY
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 节点测试函数
 | 
			
		||||
 * 1. 打开Lora硬件设施.
 | 
			
		||||
 * 2. 连接到网关.
 | 
			
		||||
*/
 | 
			
		||||
void TestNodeUp(void);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 发送数据
 | 
			
		||||
*/
 | 
			
		||||
void TestNodeSend(void);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 循环发送数据
 | 
			
		||||
*/
 | 
			
		||||
void TestNodeSendCycle(void);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief: 网关测试函数
 | 
			
		||||
 * 1. 打开Lora硬件设施.
 | 
			
		||||
 * 2. 接入网络.
 | 
			
		||||
*/
 | 
			
		||||
void TestGateway(void);
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
SRC_FILES += test_mqttclient.c
 | 
			
		||||
			
 | 
			
		||||
 | 
			
		||||
SRC_DIR += mqtt
 | 
			
		||||
include $(KERNEL_ROOT)/compiler.mk
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,55 @@
 | 
			
		|||
## 1. 简介
 | 
			
		||||
 | 
			
		||||
本代码实现了MQTT对服务器订阅主体并发送信息功能
 | 
			
		||||
 | 
			
		||||
## 2. 数据结构设计说明
 | 
			
		||||
 | 
			
		||||
### 2.1 MQTT数据结构定义
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
### 2.2 数据解析与加密
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
### 2.3 MQTT连接与订阅
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
## 3. 测试程序说明
 | 
			
		||||
 | 
			
		||||
MQTT基于TCP/IP协议,分为客户端与服务端。在本任务中,ARM终端作为客户端,与服务端连接并进行主题订阅。
 | 
			
		||||
 | 
			
		||||
以下代码设置了进行订阅时需要的Client ID、用户名、密码及订阅主题。
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
客户端首先需要根据以上信息向服务端发起连接请求,验证用户名及密码
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
随后进行主题订阅,接收服务端发布的消息
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
在订阅过程中需要对获取的信息进行读取,同时每隔一段时间向服务端发送保持活性ping请求,以维持与服务端的连接
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
## 4. 测试流程
 | 
			
		||||
 | 
			
		||||
测试流程为:
 | 
			
		||||
 | 
			
		||||
首先执行setip命令,设置设备ip地址
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
随后执行“MqttSocketRecvTest 服务器ip”命令,订阅主题,然后在服务器端发布消息如图所示
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
最终设备端接收到信息
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
| 
		 After Width: | Height: | Size: 8.3 KiB  | 
| 
		 After Width: | Height: | Size: 30 KiB  | 
| 
		 After Width: | Height: | Size: 27 KiB  | 
| 
		 After Width: | Height: | Size: 74 KiB  | 
| 
		 After Width: | Height: | Size: 14 KiB  | 
| 
		 After Width: | Height: | Size: 39 KiB  | 
| 
		 After Width: | Height: | Size: 20 KiB  | 
| 
		 After Width: | Height: | Size: 20 KiB  | 
| 
		 After Width: | Height: | Size: 35 KiB  | 
| 
		 After Width: | Height: | Size: 41 KiB  | 
| 
		 After Width: | Height: | Size: 106 KiB  | 
| 
						 | 
				
			
			@ -0,0 +1,137 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 * Copyright (c) 2014, 2017 IBM Corp.
 | 
			
		||||
 *
 | 
			
		||||
 * All rights reserved. This program and the accompanying materials
 | 
			
		||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
			
		||||
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 | 
			
		||||
 *
 | 
			
		||||
 * The Eclipse Public License is available at
 | 
			
		||||
 *    http://www.eclipse.org/legal/epl-v10.html
 | 
			
		||||
 * and the Eclipse Distribution License is available at
 | 
			
		||||
 *   http://www.eclipse.org/org/documents/edl-v10.php.
 | 
			
		||||
 *
 | 
			
		||||
 * Contributors:
 | 
			
		||||
 *    Ian Craggs - initial API and implementation and/or initial documentation
 | 
			
		||||
 *    Xiang Rong - 442039 Add makefile to Embedded C client
 | 
			
		||||
 *    Ian Craggs - fix for issue #64, bit order in connack response
 | 
			
		||||
 *******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#ifndef MQTTCONNECT_H_
 | 
			
		||||
#define MQTTCONNECT_H_
 | 
			
		||||
 | 
			
		||||
#if !defined(DLLImport)
 | 
			
		||||
  #define DLLImport
 | 
			
		||||
#endif
 | 
			
		||||
#if !defined(DLLExport)
 | 
			
		||||
  #define DLLExport
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
typedef union
 | 
			
		||||
{
 | 
			
		||||
	unsigned char all;	/**< all connect flags */
 | 
			
		||||
#if defined(REVERSED)
 | 
			
		||||
	struct
 | 
			
		||||
	{
 | 
			
		||||
		unsigned int username : 1;			/**< 3.1 user name */
 | 
			
		||||
		unsigned int password : 1; 			/**< 3.1 password */
 | 
			
		||||
		unsigned int willRetain : 1;		/**< will retain setting */
 | 
			
		||||
		unsigned int willQoS : 2;				/**< will QoS value */
 | 
			
		||||
		unsigned int will : 1;			    /**< will flag */
 | 
			
		||||
		unsigned int cleansession : 1;	  /**< clean session flag */
 | 
			
		||||
		unsigned int : 1;	  	          /**< unused */
 | 
			
		||||
	} bits;
 | 
			
		||||
#else
 | 
			
		||||
	struct
 | 
			
		||||
	{
 | 
			
		||||
		unsigned int : 1;	     					/**< unused */
 | 
			
		||||
		unsigned int cleansession : 1;	  /**< cleansession flag */
 | 
			
		||||
		unsigned int will : 1;			    /**< will flag */
 | 
			
		||||
		unsigned int willQoS : 2;				/**< will QoS value */
 | 
			
		||||
		unsigned int willRetain : 1;		/**< will retain setting */
 | 
			
		||||
		unsigned int password : 1; 			/**< 3.1 password */
 | 
			
		||||
		unsigned int username : 1;			/**< 3.1 user name */
 | 
			
		||||
	} bits;
 | 
			
		||||
#endif
 | 
			
		||||
} MQTTConnectFlags;	/**< connect flags byte */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Defines the MQTT "Last Will and Testament" (LWT) settings for
 | 
			
		||||
 * the connect packet.
 | 
			
		||||
 */
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
	/** The eyecatcher for this structure.  must be MQTW. */
 | 
			
		||||
	char struct_id[4];
 | 
			
		||||
	/** The version number of this structure.  Must be 0 */
 | 
			
		||||
	int struct_version;
 | 
			
		||||
	/** The LWT topic to which the LWT message will be published. */
 | 
			
		||||
	MQTTString topicName;
 | 
			
		||||
	/** The LWT payload. */
 | 
			
		||||
	MQTTString message;
 | 
			
		||||
	/**
 | 
			
		||||
      * The retained flag for the LWT message (see MQTTAsync_message.retained).
 | 
			
		||||
      */
 | 
			
		||||
	unsigned char retained;
 | 
			
		||||
	/**
 | 
			
		||||
      * The quality of service setting for the LWT message (see
 | 
			
		||||
      * MQTTAsync_message.qos and @ref qos).
 | 
			
		||||
      */
 | 
			
		||||
	char qos;
 | 
			
		||||
} MQTTPacket_willOptions;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define MQTTPacket_willOptions_initializer { {'M', 'Q', 'T', 'W'}, 0, {NULL, {0, NULL}}, {NULL, {0, NULL}}, 0, 0 }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
	/** The eyecatcher for this structure.  must be MQTC. */
 | 
			
		||||
	char struct_id[4];
 | 
			
		||||
	/** The version number of this structure.  Must be 0 */
 | 
			
		||||
	int struct_version;
 | 
			
		||||
	/** Version of MQTT to be used.  3 = 3.1 4 = 3.1.1
 | 
			
		||||
	  */
 | 
			
		||||
	unsigned char MQTTVersion;
 | 
			
		||||
	MQTTString clientID;
 | 
			
		||||
	unsigned short keepAliveInterval;
 | 
			
		||||
	unsigned char cleansession;
 | 
			
		||||
	unsigned char willFlag;
 | 
			
		||||
	MQTTPacket_willOptions will;
 | 
			
		||||
	MQTTString username;
 | 
			
		||||
	MQTTString password;
 | 
			
		||||
} MQTTPacket_connectData;
 | 
			
		||||
 | 
			
		||||
typedef union
 | 
			
		||||
{
 | 
			
		||||
	unsigned char all;	/**< all connack flags */
 | 
			
		||||
#if defined(REVERSED)
 | 
			
		||||
	struct
 | 
			
		||||
	{
 | 
			
		||||
    unsigned int reserved : 7;	  	    /**< unused */
 | 
			
		||||
		unsigned int sessionpresent : 1;    /**< session present flag */
 | 
			
		||||
	} bits;
 | 
			
		||||
#else
 | 
			
		||||
	struct
 | 
			
		||||
	{
 | 
			
		||||
		unsigned int sessionpresent : 1;    /**< session present flag */
 | 
			
		||||
    unsigned int reserved: 7;	     			/**< unused */
 | 
			
		||||
	} bits;
 | 
			
		||||
#endif
 | 
			
		||||
} MQTTConnackFlags;	/**< connack flags byte */
 | 
			
		||||
 | 
			
		||||
#define MQTTPacket_connectData_initializer { {'M', 'Q', 'T', 'C'}, 0, 4, {NULL, {0, NULL}}, 60, 1, 0, \
 | 
			
		||||
		MQTTPacket_willOptions_initializer, {NULL, {0, NULL}}, {NULL, {0, NULL}} }
 | 
			
		||||
 | 
			
		||||
DLLExport int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options);
 | 
			
		||||
DLLExport int MQTTDeserialize_connect(MQTTPacket_connectData* data, unsigned char* buf, int len);
 | 
			
		||||
 | 
			
		||||
DLLExport int MQTTSerialize_connack(unsigned char* buf, int buflen, unsigned char connack_rc, unsigned char sessionPresent);
 | 
			
		||||
DLLExport int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen);
 | 
			
		||||
 | 
			
		||||
DLLExport int MQTTSerialize_disconnect(unsigned char* buf, int buflen);
 | 
			
		||||
DLLExport int MQTTSerialize_pingreq(unsigned char* buf, int buflen);
 | 
			
		||||
 | 
			
		||||
#endif /* MQTTCONNECT_H_ */
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,214 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 * Copyright (c) 2014 IBM Corp.
 | 
			
		||||
 *
 | 
			
		||||
 * All rights reserved. This program and the accompanying materials
 | 
			
		||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
			
		||||
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 | 
			
		||||
 *
 | 
			
		||||
 * The Eclipse Public License is available at
 | 
			
		||||
 *    http://www.eclipse.org/legal/epl-v10.html
 | 
			
		||||
 * and the Eclipse Distribution License is available at
 | 
			
		||||
 *   http://www.eclipse.org/org/documents/edl-v10.php.
 | 
			
		||||
 *
 | 
			
		||||
 * Contributors:
 | 
			
		||||
 *    Ian Craggs - initial API and implementation and/or initial documentation
 | 
			
		||||
 *******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "MQTTPacket.h"
 | 
			
		||||
#include "StackTrace.h"
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  * Determines the length of the MQTT connect packet that would be produced using the supplied connect options.
 | 
			
		||||
  * @param options the options to be used to build the connect packet
 | 
			
		||||
  * @return the length of buffer needed to contain the serialized version of the packet
 | 
			
		||||
  */
 | 
			
		||||
int MQTTSerialize_connectLength(MQTTPacket_connectData* options)
 | 
			
		||||
{
 | 
			
		||||
	int len = 0;
 | 
			
		||||
 | 
			
		||||
	FUNC_ENTRY;
 | 
			
		||||
 | 
			
		||||
	if (options->MQTTVersion == 3)
 | 
			
		||||
		len = 12; /* variable depending on MQTT or MQIsdp */
 | 
			
		||||
	else if (options->MQTTVersion == 4)
 | 
			
		||||
		len = 10;
 | 
			
		||||
 | 
			
		||||
	len += MQTTstrlen(options->clientID)+2;
 | 
			
		||||
	if (options->willFlag)
 | 
			
		||||
		len += MQTTstrlen(options->will.topicName)+2 + MQTTstrlen(options->will.message)+2;
 | 
			
		||||
	if (options->username.cstring || options->username.lenstring.data)
 | 
			
		||||
		len += MQTTstrlen(options->username)+2;
 | 
			
		||||
	if (options->password.cstring || options->password.lenstring.data)
 | 
			
		||||
		len += MQTTstrlen(options->password)+2;
 | 
			
		||||
 | 
			
		||||
	FUNC_EXIT_RC(len);
 | 
			
		||||
	return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  * Serializes the connect options into the buffer.
 | 
			
		||||
  * @param buf the buffer into which the packet will be serialized
 | 
			
		||||
  * @param len the length in bytes of the supplied buffer
 | 
			
		||||
  * @param options the options to be used to build the connect packet
 | 
			
		||||
  * @return serialized length, or error if 0
 | 
			
		||||
  */
 | 
			
		||||
int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options)
 | 
			
		||||
{
 | 
			
		||||
	unsigned char *ptr = buf;
 | 
			
		||||
	MQTTHeader header = {0};
 | 
			
		||||
	MQTTConnectFlags flags = {0};
 | 
			
		||||
	int len = 0;
 | 
			
		||||
	int rc = -1;
 | 
			
		||||
 | 
			
		||||
	FUNC_ENTRY;
 | 
			
		||||
	if (MQTTPacket_len(len = MQTTSerialize_connectLength(options)) > buflen)
 | 
			
		||||
	{
 | 
			
		||||
		rc = MQTTPACKET_BUFFER_TOO_SHORT;
 | 
			
		||||
		goto exit;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	header.byte = 0;
 | 
			
		||||
	header.bits.type = CONNECT;
 | 
			
		||||
	writeChar(&ptr, header.byte); /* write header */
 | 
			
		||||
 | 
			
		||||
	ptr += MQTTPacket_encode(ptr, len); /* write remaining length */
 | 
			
		||||
 | 
			
		||||
	if (options->MQTTVersion == 4)
 | 
			
		||||
	{
 | 
			
		||||
		writeCString(&ptr, "MQTT");
 | 
			
		||||
		writeChar(&ptr, (char) 4);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		writeCString(&ptr, "MQIsdp");
 | 
			
		||||
		writeChar(&ptr, (char) 3);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	flags.all = 0;
 | 
			
		||||
	flags.bits.cleansession = options->cleansession;
 | 
			
		||||
	flags.bits.will = (options->willFlag) ? 1 : 0;
 | 
			
		||||
	if (flags.bits.will)
 | 
			
		||||
	{
 | 
			
		||||
		flags.bits.willQoS = options->will.qos;
 | 
			
		||||
		flags.bits.willRetain = options->will.retained;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (options->username.cstring || options->username.lenstring.data)
 | 
			
		||||
		flags.bits.username = 1;
 | 
			
		||||
	if (options->password.cstring || options->password.lenstring.data)
 | 
			
		||||
		flags.bits.password = 1;
 | 
			
		||||
 | 
			
		||||
	writeChar(&ptr, flags.all);
 | 
			
		||||
	writeInt(&ptr, options->keepAliveInterval);
 | 
			
		||||
	writeMQTTString(&ptr, options->clientID);
 | 
			
		||||
	if (options->willFlag)
 | 
			
		||||
	{
 | 
			
		||||
		writeMQTTString(&ptr, options->will.topicName);
 | 
			
		||||
		writeMQTTString(&ptr, options->will.message);
 | 
			
		||||
	}
 | 
			
		||||
	if (flags.bits.username)
 | 
			
		||||
		writeMQTTString(&ptr, options->username);
 | 
			
		||||
	if (flags.bits.password)
 | 
			
		||||
		writeMQTTString(&ptr, options->password);
 | 
			
		||||
 | 
			
		||||
	rc = ptr - buf;
 | 
			
		||||
 | 
			
		||||
	exit: FUNC_EXIT_RC(rc);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  * Deserializes the supplied (wire) buffer into connack data - return code
 | 
			
		||||
  * @param sessionPresent the session present flag returned (only for MQTT 3.1.1)
 | 
			
		||||
  * @param connack_rc returned integer value of the connack return code
 | 
			
		||||
  * @param buf the raw buffer data, of the correct length determined by the remaining length field
 | 
			
		||||
  * @param len the length in bytes of the data in the supplied buffer
 | 
			
		||||
  * @return error code.  1 is success, 0 is failure
 | 
			
		||||
  */
 | 
			
		||||
int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen)
 | 
			
		||||
{
 | 
			
		||||
	MQTTHeader header = {0};
 | 
			
		||||
	unsigned char* curdata = buf;
 | 
			
		||||
	unsigned char* enddata = NULL;
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	int mylen;
 | 
			
		||||
	MQTTConnackFlags flags = {0};
 | 
			
		||||
 | 
			
		||||
	FUNC_ENTRY;
 | 
			
		||||
	header.byte = readChar(&curdata);
 | 
			
		||||
	if (header.bits.type != CONNACK)
 | 
			
		||||
		goto exit;
 | 
			
		||||
 | 
			
		||||
	curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
 | 
			
		||||
	enddata = curdata + mylen;
 | 
			
		||||
	if (enddata - curdata < 2)
 | 
			
		||||
		goto exit;
 | 
			
		||||
 | 
			
		||||
	flags.all = readChar(&curdata);
 | 
			
		||||
	*sessionPresent = flags.bits.sessionpresent;
 | 
			
		||||
	*connack_rc = readChar(&curdata);
 | 
			
		||||
 | 
			
		||||
	rc = 1;
 | 
			
		||||
exit:
 | 
			
		||||
	FUNC_EXIT_RC(rc);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  * Serializes a 0-length packet into the supplied buffer, ready for writing to a socket
 | 
			
		||||
  * @param buf the buffer into which the packet will be serialized
 | 
			
		||||
  * @param buflen the length in bytes of the supplied buffer, to avoid overruns
 | 
			
		||||
  * @param packettype the message type
 | 
			
		||||
  * @return serialized length, or error if 0
 | 
			
		||||
  */
 | 
			
		||||
int MQTTSerialize_zero(unsigned char* buf, int buflen, unsigned char packettype)
 | 
			
		||||
{
 | 
			
		||||
	MQTTHeader header = {0};
 | 
			
		||||
	int rc = -1;
 | 
			
		||||
	unsigned char *ptr = buf;
 | 
			
		||||
 | 
			
		||||
	FUNC_ENTRY;
 | 
			
		||||
	if (buflen < 2)
 | 
			
		||||
	{
 | 
			
		||||
		rc = MQTTPACKET_BUFFER_TOO_SHORT;
 | 
			
		||||
		goto exit;
 | 
			
		||||
	}
 | 
			
		||||
	header.byte = 0;
 | 
			
		||||
	header.bits.type = packettype;
 | 
			
		||||
	writeChar(&ptr, header.byte); /* write header */
 | 
			
		||||
 | 
			
		||||
	ptr += MQTTPacket_encode(ptr, 0); /* write remaining length */
 | 
			
		||||
	rc = ptr - buf;
 | 
			
		||||
exit:
 | 
			
		||||
	FUNC_EXIT_RC(rc);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  * Serializes a disconnect packet into the supplied buffer, ready for writing to a socket
 | 
			
		||||
  * @param buf the buffer into which the packet will be serialized
 | 
			
		||||
  * @param buflen the length in bytes of the supplied buffer, to avoid overruns
 | 
			
		||||
  * @return serialized length, or error if 0
 | 
			
		||||
  */
 | 
			
		||||
int MQTTSerialize_disconnect(unsigned char* buf, int buflen)
 | 
			
		||||
{
 | 
			
		||||
	return MQTTSerialize_zero(buf, buflen, DISCONNECT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  * Serializes a disconnect packet into the supplied buffer, ready for writing to a socket
 | 
			
		||||
  * @param buf the buffer into which the packet will be serialized
 | 
			
		||||
  * @param buflen the length in bytes of the supplied buffer, to avoid overruns
 | 
			
		||||
  * @return serialized length, or error if 0
 | 
			
		||||
  */
 | 
			
		||||
int MQTTSerialize_pingreq(unsigned char* buf, int buflen)
 | 
			
		||||
{
 | 
			
		||||
	return MQTTSerialize_zero(buf, buflen, PINGREQ);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,148 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 * Copyright (c) 2014 IBM Corp.
 | 
			
		||||
 *
 | 
			
		||||
 * All rights reserved. This program and the accompanying materials
 | 
			
		||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
			
		||||
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 | 
			
		||||
 *
 | 
			
		||||
 * The Eclipse Public License is available at
 | 
			
		||||
 *    http://www.eclipse.org/legal/epl-v10.html
 | 
			
		||||
 * and the Eclipse Distribution License is available at
 | 
			
		||||
 *   http://www.eclipse.org/org/documents/edl-v10.php.
 | 
			
		||||
 *
 | 
			
		||||
 * Contributors:
 | 
			
		||||
 *    Ian Craggs - initial API and implementation and/or initial documentation
 | 
			
		||||
 *******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "StackTrace.h"
 | 
			
		||||
#include "MQTTPacket.h"
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#define min(a, b) ((a < b) ? a : b)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  * Validates MQTT protocol name and version combinations
 | 
			
		||||
  * @param protocol the MQTT protocol name as an MQTTString
 | 
			
		||||
  * @param version the MQTT protocol version number, as in the connect packet
 | 
			
		||||
  * @return correct MQTT combination?  1 is true, 0 is false
 | 
			
		||||
  */
 | 
			
		||||
int MQTTPacket_checkVersion(MQTTString* protocol, int version)
 | 
			
		||||
{
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
 | 
			
		||||
	if (version == 3 && memcmp(protocol->lenstring.data, "MQIsdp",
 | 
			
		||||
			min(6, protocol->lenstring.len)) == 0)
 | 
			
		||||
		rc = 1;
 | 
			
		||||
	else if (version == 4 && memcmp(protocol->lenstring.data, "MQTT",
 | 
			
		||||
			min(4, protocol->lenstring.len)) == 0)
 | 
			
		||||
		rc = 1;
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  * Deserializes the supplied (wire) buffer into connect data structure
 | 
			
		||||
  * @param data the connect data structure to be filled out
 | 
			
		||||
  * @param buf the raw buffer data, of the correct length determined by the remaining length field
 | 
			
		||||
  * @param len the length in bytes of the data in the supplied buffer
 | 
			
		||||
  * @return error code.  1 is success, 0 is failure
 | 
			
		||||
  */
 | 
			
		||||
int MQTTDeserialize_connect(MQTTPacket_connectData* data, unsigned char* buf, int len)
 | 
			
		||||
{
 | 
			
		||||
	MQTTHeader header = {0};
 | 
			
		||||
	MQTTConnectFlags flags = {0};
 | 
			
		||||
	unsigned char* curdata = buf;
 | 
			
		||||
	unsigned char* enddata = &buf[len];
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	MQTTString Protocol;
 | 
			
		||||
	int version;
 | 
			
		||||
	int mylen = 0;
 | 
			
		||||
 | 
			
		||||
	FUNC_ENTRY;
 | 
			
		||||
	header.byte = readChar(&curdata);
 | 
			
		||||
	if (header.bits.type != CONNECT)
 | 
			
		||||
		goto exit;
 | 
			
		||||
 | 
			
		||||
	curdata += MQTTPacket_decodeBuf(curdata, &mylen); /* read remaining length */
 | 
			
		||||
 | 
			
		||||
	if (!readMQTTLenString(&Protocol, &curdata, enddata) ||
 | 
			
		||||
		enddata - curdata < 0) /* do we have enough data to read the protocol version byte? */
 | 
			
		||||
		goto exit;
 | 
			
		||||
 | 
			
		||||
	version = (int)readChar(&curdata); /* Protocol version */
 | 
			
		||||
	/* If we don't recognize the protocol version, we don't parse the connect packet on the
 | 
			
		||||
	 * basis that we don't know what the format will be.
 | 
			
		||||
	 */
 | 
			
		||||
	if (MQTTPacket_checkVersion(&Protocol, version))
 | 
			
		||||
	{
 | 
			
		||||
		flags.all = readChar(&curdata);
 | 
			
		||||
		data->cleansession = flags.bits.cleansession;
 | 
			
		||||
		data->keepAliveInterval = readInt(&curdata);
 | 
			
		||||
		if (!readMQTTLenString(&data->clientID, &curdata, enddata))
 | 
			
		||||
			goto exit;
 | 
			
		||||
		data->willFlag = flags.bits.will;
 | 
			
		||||
		if (flags.bits.will)
 | 
			
		||||
		{
 | 
			
		||||
			data->will.qos = flags.bits.willQoS;
 | 
			
		||||
			data->will.retained = flags.bits.willRetain;
 | 
			
		||||
			if (!readMQTTLenString(&data->will.topicName, &curdata, enddata) ||
 | 
			
		||||
				  !readMQTTLenString(&data->will.message, &curdata, enddata))
 | 
			
		||||
				goto exit;
 | 
			
		||||
		}
 | 
			
		||||
		if (flags.bits.username)
 | 
			
		||||
		{
 | 
			
		||||
			if (enddata - curdata < 3 || !readMQTTLenString(&data->username, &curdata, enddata))
 | 
			
		||||
				goto exit; /* username flag set, but no username supplied - invalid */
 | 
			
		||||
			if (flags.bits.password &&
 | 
			
		||||
				(enddata - curdata < 3 || !readMQTTLenString(&data->password, &curdata, enddata)))
 | 
			
		||||
				goto exit; /* password flag set, but no password supplied - invalid */
 | 
			
		||||
		}
 | 
			
		||||
		else if (flags.bits.password)
 | 
			
		||||
			goto exit; /* password flag set without username - invalid */
 | 
			
		||||
		rc = 1;
 | 
			
		||||
	}
 | 
			
		||||
exit:
 | 
			
		||||
	FUNC_EXIT_RC(rc);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  * Serializes the connack packet into the supplied buffer.
 | 
			
		||||
  * @param buf the buffer into which the packet will be serialized
 | 
			
		||||
  * @param buflen the length in bytes of the supplied buffer
 | 
			
		||||
  * @param connack_rc the integer connack return code to be used 
 | 
			
		||||
  * @param sessionPresent the MQTT 3.1.1 sessionPresent flag
 | 
			
		||||
  * @return serialized length, or error if 0
 | 
			
		||||
  */
 | 
			
		||||
int MQTTSerialize_connack(unsigned char* buf, int buflen, unsigned char connack_rc, unsigned char sessionPresent)
 | 
			
		||||
{
 | 
			
		||||
	MQTTHeader header = {0};
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	unsigned char *ptr = buf;
 | 
			
		||||
	MQTTConnackFlags flags = {0};
 | 
			
		||||
 | 
			
		||||
	FUNC_ENTRY;
 | 
			
		||||
	if (buflen < 2)
 | 
			
		||||
	{
 | 
			
		||||
		rc = MQTTPACKET_BUFFER_TOO_SHORT;
 | 
			
		||||
		goto exit;
 | 
			
		||||
	}
 | 
			
		||||
	header.byte = 0;
 | 
			
		||||
	header.bits.type = CONNACK;
 | 
			
		||||
	writeChar(&ptr, header.byte); /* write header */
 | 
			
		||||
 | 
			
		||||
	ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */
 | 
			
		||||
 | 
			
		||||
	flags.all = 0;
 | 
			
		||||
	flags.bits.sessionpresent = sessionPresent;
 | 
			
		||||
	writeChar(&ptr, flags.all); 
 | 
			
		||||
	writeChar(&ptr, connack_rc);
 | 
			
		||||
 | 
			
		||||
	rc = ptr - buf;
 | 
			
		||||
exit:
 | 
			
		||||
	FUNC_EXIT_RC(rc);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,107 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 * Copyright (c) 2014 IBM Corp.
 | 
			
		||||
 *
 | 
			
		||||
 * All rights reserved. This program and the accompanying materials
 | 
			
		||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
			
		||||
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 | 
			
		||||
 *
 | 
			
		||||
 * The Eclipse Public License is available at
 | 
			
		||||
 *    http://www.eclipse.org/legal/epl-v10.html
 | 
			
		||||
 * and the Eclipse Distribution License is available at
 | 
			
		||||
 *   http://www.eclipse.org/org/documents/edl-v10.php.
 | 
			
		||||
 *
 | 
			
		||||
 * Contributors:
 | 
			
		||||
 *    Ian Craggs - initial API and implementation and/or initial documentation
 | 
			
		||||
 *******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "StackTrace.h"
 | 
			
		||||
#include "MQTTPacket.h"
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#define min(a, b) ((a < b) ? 1 : 0)
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  * Deserializes the supplied (wire) buffer into publish data
 | 
			
		||||
  * @param dup returned integer - the MQTT dup flag
 | 
			
		||||
  * @param qos returned integer - the MQTT QoS value
 | 
			
		||||
  * @param retained returned integer - the MQTT retained flag
 | 
			
		||||
  * @param packetid returned integer - the MQTT packet identifier
 | 
			
		||||
  * @param topicName returned MQTTString - the MQTT topic in the publish
 | 
			
		||||
  * @param payload returned byte buffer - the MQTT publish payload
 | 
			
		||||
  * @param payloadlen returned integer - the length of the MQTT payload
 | 
			
		||||
  * @param buf the raw buffer data, of the correct length determined by the remaining length field
 | 
			
		||||
  * @param buflen the length in bytes of the data in the supplied buffer
 | 
			
		||||
  * @return error code.  1 is success
 | 
			
		||||
  */
 | 
			
		||||
int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName,
 | 
			
		||||
		unsigned char** payload, int32_t* payloadlen, unsigned char* buf, int buflen)
 | 
			
		||||
{
 | 
			
		||||
	MQTTHeader header = {0};
 | 
			
		||||
	unsigned char* curdata = buf;
 | 
			
		||||
	unsigned char* enddata = NULL;
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	int mylen = 0;
 | 
			
		||||
 | 
			
		||||
	FUNC_ENTRY;
 | 
			
		||||
	header.byte = readChar(&curdata);
 | 
			
		||||
	if (header.bits.type != PUBLISH)
 | 
			
		||||
		goto exit;
 | 
			
		||||
	*dup = header.bits.dup;
 | 
			
		||||
	*qos = header.bits.qos;
 | 
			
		||||
	*retained = header.bits.retain;
 | 
			
		||||
 | 
			
		||||
	curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
 | 
			
		||||
	enddata = curdata + mylen;
 | 
			
		||||
 | 
			
		||||
	if (!readMQTTLenString(topicName, &curdata, enddata) ||
 | 
			
		||||
		enddata - curdata < 0) /* do we have enough data to read the protocol version byte? */
 | 
			
		||||
		goto exit;
 | 
			
		||||
 | 
			
		||||
	if (*qos > 0)
 | 
			
		||||
		*packetid = readInt(&curdata);
 | 
			
		||||
 | 
			
		||||
	*payloadlen = enddata - curdata;
 | 
			
		||||
	*payload = curdata;
 | 
			
		||||
	rc = 1;
 | 
			
		||||
exit:
 | 
			
		||||
	FUNC_EXIT_RC(rc);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  * Deserializes the supplied (wire) buffer into an ack
 | 
			
		||||
  * @param packettype returned integer - the MQTT packet type
 | 
			
		||||
  * @param dup returned integer - the MQTT dup flag
 | 
			
		||||
  * @param packetid returned integer - the MQTT packet identifier
 | 
			
		||||
  * @param buf the raw buffer data, of the correct length determined by the remaining length field
 | 
			
		||||
  * @param buflen the length in bytes of the data in the supplied buffer
 | 
			
		||||
  * @return error code.  1 is success, 0 is failure
 | 
			
		||||
  */
 | 
			
		||||
int MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen)
 | 
			
		||||
{
 | 
			
		||||
	MQTTHeader header = {0};
 | 
			
		||||
	unsigned char* curdata = buf;
 | 
			
		||||
	unsigned char* enddata = NULL;
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	int mylen;
 | 
			
		||||
 | 
			
		||||
	FUNC_ENTRY;
 | 
			
		||||
	header.byte = readChar(&curdata);
 | 
			
		||||
	*dup = header.bits.dup;
 | 
			
		||||
	*packettype = header.bits.type;
 | 
			
		||||
 | 
			
		||||
	curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
 | 
			
		||||
	enddata = curdata + mylen;
 | 
			
		||||
 | 
			
		||||
	if (enddata - curdata < 2)
 | 
			
		||||
		goto exit;
 | 
			
		||||
	*packetid = readInt(&curdata);
 | 
			
		||||
 | 
			
		||||
	rc = 1;
 | 
			
		||||
exit:
 | 
			
		||||
	FUNC_EXIT_RC(rc);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,262 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 * Copyright (c) 2014 IBM Corp.
 | 
			
		||||
 *
 | 
			
		||||
 * All rights reserved. This program and the accompanying materials
 | 
			
		||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
			
		||||
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 | 
			
		||||
 *
 | 
			
		||||
 * The Eclipse Public License is available at
 | 
			
		||||
 *    http://www.eclipse.org/legal/epl-v10.html
 | 
			
		||||
 * and the Eclipse Distribution License is available at
 | 
			
		||||
 *   http://www.eclipse.org/org/documents/edl-v10.php.
 | 
			
		||||
 *
 | 
			
		||||
 * Contributors:
 | 
			
		||||
 *    Ian Craggs - initial API and implementation and/or initial documentation
 | 
			
		||||
 *******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "StackTrace.h"
 | 
			
		||||
#include "MQTTPacket.h"
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const char* MQTTPacket_names[] =
 | 
			
		||||
{
 | 
			
		||||
	"RESERVED", "CONNECT", "CONNACK", "PUBLISH", "PUBACK", "PUBREC", "PUBREL",
 | 
			
		||||
	"PUBCOMP", "SUBSCRIBE", "SUBACK", "UNSUBSCRIBE", "UNSUBACK",
 | 
			
		||||
	"PINGREQ", "PINGRESP", "DISCONNECT"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const char* MQTTPacket_getName(unsigned short packetid)
 | 
			
		||||
{
 | 
			
		||||
	return MQTTPacket_names[packetid];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int MQTTStringFormat_connect(char* strbuf, int strbuflen, MQTTPacket_connectData* data)
 | 
			
		||||
{
 | 
			
		||||
	int strindex = 0;
 | 
			
		||||
 | 
			
		||||
	strindex = snprintf(strbuf, strbuflen,
 | 
			
		||||
			"CONNECT MQTT version %d, client id %.*s, clean session %d, keep alive %d",
 | 
			
		||||
			(int)data->MQTTVersion, data->clientID.lenstring.len, data->clientID.lenstring.data,
 | 
			
		||||
			(int)data->cleansession, data->keepAliveInterval);
 | 
			
		||||
	if (data->willFlag)
 | 
			
		||||
		strindex += snprintf(&strbuf[strindex], strbuflen - strindex,
 | 
			
		||||
				", will QoS %d, will retain %d, will topic %.*s, will message %.*s",
 | 
			
		||||
				data->will.qos, data->will.retained,
 | 
			
		||||
				data->will.topicName.lenstring.len, data->will.topicName.lenstring.data,
 | 
			
		||||
				data->will.message.lenstring.len, data->will.message.lenstring.data);
 | 
			
		||||
	if (data->username.lenstring.data && data->username.lenstring.len > 0)
 | 
			
		||||
		strindex += snprintf(&strbuf[strindex], strbuflen - strindex,
 | 
			
		||||
				", user name %.*s", data->username.lenstring.len, data->username.lenstring.data);
 | 
			
		||||
	if (data->password.lenstring.data && data->password.lenstring.len > 0)
 | 
			
		||||
		strindex += snprintf(&strbuf[strindex], strbuflen - strindex,
 | 
			
		||||
				", password %.*s", data->password.lenstring.len, data->password.lenstring.data);
 | 
			
		||||
	return strindex;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int MQTTStringFormat_connack(char* strbuf, int strbuflen, unsigned char connack_rc, unsigned char sessionPresent)
 | 
			
		||||
{
 | 
			
		||||
	int strindex = snprintf(strbuf, strbuflen, "CONNACK session present %d, rc %d", sessionPresent, connack_rc);
 | 
			
		||||
	return strindex;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int MQTTStringFormat_publish(char* strbuf, int strbuflen, unsigned char dup, int qos, unsigned char retained,
 | 
			
		||||
		unsigned short packetid, MQTTString topicName, unsigned char* payload, int payloadlen)
 | 
			
		||||
{
 | 
			
		||||
	int strindex = snprintf(strbuf, strbuflen,
 | 
			
		||||
				"PUBLISH dup %d, QoS %d, retained %d, packet id %d, topic %.*s, payload length %d, payload %.*s",
 | 
			
		||||
				dup, qos, retained, packetid,
 | 
			
		||||
				(topicName.lenstring.len < 20) ? topicName.lenstring.len : 20, topicName.lenstring.data,
 | 
			
		||||
				payloadlen, (payloadlen < 20) ? payloadlen : 20, payload);
 | 
			
		||||
	return strindex;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int MQTTStringFormat_ack(char* strbuf, int strbuflen, unsigned char packettype, unsigned char dup, unsigned short packetid)
 | 
			
		||||
{
 | 
			
		||||
	int strindex = snprintf(strbuf, strbuflen, "%s, packet id %d", MQTTPacket_names[packettype], packetid);
 | 
			
		||||
	if (dup)
 | 
			
		||||
		strindex += snprintf(strbuf + strindex, strbuflen - strindex, ", dup %d", dup);
 | 
			
		||||
	return strindex;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int MQTTStringFormat_subscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, int count,
 | 
			
		||||
		MQTTString topicFilters[], int requestedQoSs[])
 | 
			
		||||
{
 | 
			
		||||
	return snprintf(strbuf, strbuflen,
 | 
			
		||||
		"SUBSCRIBE dup %d, packet id %d count %d topic %.*s qos %d",
 | 
			
		||||
		dup, packetid, count,
 | 
			
		||||
		topicFilters[0].lenstring.len, topicFilters[0].lenstring.data,
 | 
			
		||||
		requestedQoSs[0]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int MQTTStringFormat_suback(char* strbuf, int strbuflen, unsigned short packetid, int count, int* grantedQoSs)
 | 
			
		||||
{
 | 
			
		||||
	return snprintf(strbuf, strbuflen,
 | 
			
		||||
		"SUBACK packet id %d count %d granted qos %d", packetid, count, grantedQoSs[0]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int MQTTStringFormat_unsubscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid,
 | 
			
		||||
		int count, MQTTString topicFilters[])
 | 
			
		||||
{
 | 
			
		||||
	return snprintf(strbuf, strbuflen,
 | 
			
		||||
					"UNSUBSCRIBE dup %d, packet id %d count %d topic %.*s",
 | 
			
		||||
					dup, packetid, count,
 | 
			
		||||
					topicFilters[0].lenstring.len, topicFilters[0].lenstring.data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if defined(MQTT_CLIENT)
 | 
			
		||||
char* MQTTFormat_toClientString(char* strbuf, int strbuflen, unsigned char* buf, int buflen)
 | 
			
		||||
{
 | 
			
		||||
	int index = 0;
 | 
			
		||||
	int rem_length = 0;
 | 
			
		||||
	MQTTHeader header = {0};
 | 
			
		||||
	int strindex = 0;
 | 
			
		||||
 | 
			
		||||
	header.byte = buf[index++];
 | 
			
		||||
	index += MQTTPacket_decodeBuf(&buf[index], &rem_length);
 | 
			
		||||
 | 
			
		||||
	switch (header.bits.type)
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
	case CONNACK:
 | 
			
		||||
	{
 | 
			
		||||
		unsigned char sessionPresent, connack_rc;
 | 
			
		||||
		if (MQTTDeserialize_connack(&sessionPresent, &connack_rc, buf, buflen) == 1)
 | 
			
		||||
			strindex = MQTTStringFormat_connack(strbuf, strbuflen, connack_rc, sessionPresent);
 | 
			
		||||
	}
 | 
			
		||||
	break;
 | 
			
		||||
	case PUBLISH:
 | 
			
		||||
	{
 | 
			
		||||
		unsigned char dup, retained, *payload;
 | 
			
		||||
		unsigned short packetid;
 | 
			
		||||
		int qos, payloadlen;
 | 
			
		||||
		MQTTString topicName = MQTTString_initializer;
 | 
			
		||||
		if (MQTTDeserialize_publish(&dup, &qos, &retained, &packetid, &topicName,
 | 
			
		||||
				&payload, &payloadlen, buf, buflen) == 1)
 | 
			
		||||
			strindex = MQTTStringFormat_publish(strbuf, strbuflen, dup, qos, retained, packetid,
 | 
			
		||||
					topicName, payload, payloadlen);
 | 
			
		||||
	}
 | 
			
		||||
	break;
 | 
			
		||||
	case PUBACK:
 | 
			
		||||
	case PUBREC:
 | 
			
		||||
	case PUBREL:
 | 
			
		||||
	case PUBCOMP:
 | 
			
		||||
	{
 | 
			
		||||
		unsigned char packettype, dup;
 | 
			
		||||
		unsigned short packetid;
 | 
			
		||||
		if (MQTTDeserialize_ack(&packettype, &dup, &packetid, buf, buflen) == 1)
 | 
			
		||||
			strindex = MQTTStringFormat_ack(strbuf, strbuflen, packettype, dup, packetid);
 | 
			
		||||
	}
 | 
			
		||||
	break;
 | 
			
		||||
	case SUBACK:
 | 
			
		||||
	{
 | 
			
		||||
		unsigned short packetid;
 | 
			
		||||
		int maxcount = 1, count = 0;
 | 
			
		||||
		int grantedQoSs[1];
 | 
			
		||||
		if (MQTTDeserialize_suback(&packetid, maxcount, &count, grantedQoSs, buf, buflen) == 1)
 | 
			
		||||
			strindex = MQTTStringFormat_suback(strbuf, strbuflen, packetid, count, grantedQoSs);
 | 
			
		||||
	}
 | 
			
		||||
	break;
 | 
			
		||||
	case UNSUBACK:
 | 
			
		||||
	{
 | 
			
		||||
		unsigned short packetid;
 | 
			
		||||
		if (MQTTDeserialize_unsuback(&packetid, buf, buflen) == 1)
 | 
			
		||||
			strindex = MQTTStringFormat_ack(strbuf, strbuflen, UNSUBACK, 0, packetid);
 | 
			
		||||
	}
 | 
			
		||||
	break;
 | 
			
		||||
	case PINGREQ:
 | 
			
		||||
	case PINGRESP:
 | 
			
		||||
	case DISCONNECT:
 | 
			
		||||
		strindex = snprintf(strbuf, strbuflen, "%s", MQTTPacket_names[header.bits.type]);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	return strbuf;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(MQTT_SERVER)
 | 
			
		||||
char* MQTTFormat_toServerString(char* strbuf, int strbuflen, unsigned char* buf, int buflen)
 | 
			
		||||
{
 | 
			
		||||
	int index = 0;
 | 
			
		||||
	int rem_length = 0;
 | 
			
		||||
	MQTTHeader header = {0};
 | 
			
		||||
	int strindex = 0;
 | 
			
		||||
 | 
			
		||||
	header.byte = buf[index++];
 | 
			
		||||
	index += MQTTPacket_decodeBuf(&buf[index], &rem_length);
 | 
			
		||||
 | 
			
		||||
	switch (header.bits.type)
 | 
			
		||||
	{
 | 
			
		||||
	case CONNECT:
 | 
			
		||||
	{
 | 
			
		||||
		MQTTPacket_connectData data;
 | 
			
		||||
		int rc;
 | 
			
		||||
		if ((rc = MQTTDeserialize_connect(&data, buf, buflen)) == 1)
 | 
			
		||||
			strindex = MQTTStringFormat_connect(strbuf, strbuflen, &data);
 | 
			
		||||
	}
 | 
			
		||||
	break;
 | 
			
		||||
	case PUBLISH:
 | 
			
		||||
	{
 | 
			
		||||
		unsigned char dup, retained, *payload;
 | 
			
		||||
		unsigned short packetid;
 | 
			
		||||
		int qos, payloadlen;
 | 
			
		||||
		MQTTString topicName = MQTTString_initializer;
 | 
			
		||||
		if (MQTTDeserialize_publish(&dup, &qos, &retained, &packetid, &topicName,
 | 
			
		||||
				&payload, &payloadlen, buf, buflen) == 1)
 | 
			
		||||
			strindex = MQTTStringFormat_publish(strbuf, strbuflen, dup, qos, retained, packetid,
 | 
			
		||||
					topicName, payload, payloadlen);
 | 
			
		||||
	}
 | 
			
		||||
	break;
 | 
			
		||||
	case PUBACK:
 | 
			
		||||
	case PUBREC:
 | 
			
		||||
	case PUBREL:
 | 
			
		||||
	case PUBCOMP:
 | 
			
		||||
	{
 | 
			
		||||
		unsigned char packettype, dup;
 | 
			
		||||
		unsigned short packetid;
 | 
			
		||||
		if (MQTTDeserialize_ack(&packettype, &dup, &packetid, buf, buflen) == 1)
 | 
			
		||||
			strindex = MQTTStringFormat_ack(strbuf, strbuflen, packettype, dup, packetid);
 | 
			
		||||
	}
 | 
			
		||||
	break;
 | 
			
		||||
	case SUBSCRIBE:
 | 
			
		||||
	{
 | 
			
		||||
		unsigned char dup;
 | 
			
		||||
		unsigned short packetid;
 | 
			
		||||
		int maxcount = 1, count = 0;
 | 
			
		||||
		MQTTString topicFilters[1];
 | 
			
		||||
		int requestedQoSs[1];
 | 
			
		||||
		if (MQTTDeserialize_subscribe(&dup, &packetid, maxcount, &count,
 | 
			
		||||
				topicFilters, requestedQoSs, buf, buflen) == 1)
 | 
			
		||||
			strindex = MQTTStringFormat_subscribe(strbuf, strbuflen, dup, packetid, count, topicFilters, requestedQoSs);;
 | 
			
		||||
	}
 | 
			
		||||
	break;
 | 
			
		||||
	case UNSUBSCRIBE:
 | 
			
		||||
	{
 | 
			
		||||
		unsigned char dup;
 | 
			
		||||
		unsigned short packetid;
 | 
			
		||||
		int maxcount = 1, count = 0;
 | 
			
		||||
		MQTTString topicFilters[1];
 | 
			
		||||
		if (MQTTDeserialize_unsubscribe(&dup, &packetid, maxcount, &count, topicFilters, buf, buflen) == 1)
 | 
			
		||||
			strindex =  MQTTStringFormat_unsubscribe(strbuf, strbuflen, dup, packetid, count, topicFilters);
 | 
			
		||||
	}
 | 
			
		||||
	break;
 | 
			
		||||
	case PINGREQ:
 | 
			
		||||
	case PINGRESP:
 | 
			
		||||
	case DISCONNECT:
 | 
			
		||||
		strindex = snprintf(strbuf, strbuflen, "%s", MQTTPacket_names[header.bits.type]);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	strbuf[strbuflen] = '\0';
 | 
			
		||||
	return strbuf;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 * Copyright (c) 2014 IBM Corp.
 | 
			
		||||
 *
 | 
			
		||||
 * All rights reserved. This program and the accompanying materials
 | 
			
		||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
			
		||||
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 | 
			
		||||
 *
 | 
			
		||||
 * The Eclipse Public License is available at
 | 
			
		||||
 *    http://www.eclipse.org/legal/epl-v10.html
 | 
			
		||||
 * and the Eclipse Distribution License is available at
 | 
			
		||||
 *   http://www.eclipse.org/org/documents/edl-v10.php.
 | 
			
		||||
 *
 | 
			
		||||
 * Contributors:
 | 
			
		||||
 *    Ian Craggs - initial API and implementation and/or initial documentation
 | 
			
		||||
 *******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#if !defined(MQTTFORMAT_H)
 | 
			
		||||
#define MQTTFORMAT_H
 | 
			
		||||
 | 
			
		||||
#include "StackTrace.h"
 | 
			
		||||
#include "MQTTPacket.h"
 | 
			
		||||
 | 
			
		||||
const char* MQTTPacket_getName(unsigned short packetid);
 | 
			
		||||
int MQTTStringFormat_connect(char* strbuf, int strbuflen, MQTTPacket_connectData* data);
 | 
			
		||||
int MQTTStringFormat_connack(char* strbuf, int strbuflen, unsigned char connack_rc, unsigned char sessionPresent);
 | 
			
		||||
int MQTTStringFormat_publish(char* strbuf, int strbuflen, unsigned char dup, int qos, unsigned char retained,
 | 
			
		||||
		unsigned short packetid, MQTTString topicName, unsigned char* payload, int payloadlen);
 | 
			
		||||
int MQTTStringFormat_ack(char* strbuf, int strbuflen, unsigned char packettype, unsigned char dup, unsigned short packetid);
 | 
			
		||||
int MQTTStringFormat_subscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, int count,
 | 
			
		||||
		MQTTString topicFilters[], int requestedQoSs[]);
 | 
			
		||||
int MQTTStringFormat_suback(char* strbuf, int strbuflen, unsigned short packetid, int count, int* grantedQoSs);
 | 
			
		||||
int MQTTStringFormat_unsubscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid,
 | 
			
		||||
		int count, MQTTString topicFilters[]);
 | 
			
		||||
char* MQTTFormat_toClientString(char* strbuf, int strbuflen, unsigned char* buf, int buflen);
 | 
			
		||||
char* MQTTFormat_toServerString(char* strbuf, int strbuflen, unsigned char* buf, int buflen);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,412 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 * Copyright (c) 2014 IBM Corp.
 | 
			
		||||
 *
 | 
			
		||||
 * All rights reserved. This program and the accompanying materials
 | 
			
		||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
			
		||||
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 | 
			
		||||
 *
 | 
			
		||||
 * The Eclipse Public License is available at
 | 
			
		||||
 *    http://www.eclipse.org/legal/epl-v10.html
 | 
			
		||||
 * and the Eclipse Distribution License is available at
 | 
			
		||||
 *   http://www.eclipse.org/org/documents/edl-v10.php.
 | 
			
		||||
 *
 | 
			
		||||
 * Contributors:
 | 
			
		||||
 *    Ian Craggs - initial API and implementation and/or initial documentation
 | 
			
		||||
 *    Sergio R. Caprile - non-blocking packet read functions for stream transport
 | 
			
		||||
 *******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "StackTrace.h"
 | 
			
		||||
#include "MQTTPacket.h"
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Encodes the message length according to the MQTT algorithm
 | 
			
		||||
 * @param buf the buffer into which the encoded data is written
 | 
			
		||||
 * @param length the length to be encoded
 | 
			
		||||
 * @return the number of bytes written to buffer
 | 
			
		||||
 */
 | 
			
		||||
int MQTTPacket_encode(unsigned char* buf, int length)
 | 
			
		||||
{
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
 | 
			
		||||
	FUNC_ENTRY;
 | 
			
		||||
	do
 | 
			
		||||
	{
 | 
			
		||||
		char d = length % 128;
 | 
			
		||||
		length /= 128;
 | 
			
		||||
		/* if there are more digits to encode, set the top bit of this digit */
 | 
			
		||||
		if (length > 0)
 | 
			
		||||
			d |= 0x80;
 | 
			
		||||
		buf[rc++] = d;
 | 
			
		||||
	} while (length > 0);
 | 
			
		||||
	FUNC_EXIT_RC(rc);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Decodes the message length according to the MQTT algorithm
 | 
			
		||||
 * @param getcharfn pointer to function to read the next character from the data source
 | 
			
		||||
 * @param value the decoded length returned
 | 
			
		||||
 * @return the number of bytes read from the socket
 | 
			
		||||
 */
 | 
			
		||||
int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value)
 | 
			
		||||
{
 | 
			
		||||
	unsigned char c;
 | 
			
		||||
	int multiplier = 1;
 | 
			
		||||
	int len = 0;
 | 
			
		||||
#define MAX_NO_OF_REMAINING_LENGTH_BYTES 4
 | 
			
		||||
 | 
			
		||||
	FUNC_ENTRY;
 | 
			
		||||
	*value = 0;
 | 
			
		||||
	do
 | 
			
		||||
	{
 | 
			
		||||
		int rc = MQTTPACKET_READ_ERROR;
 | 
			
		||||
 | 
			
		||||
		if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES)
 | 
			
		||||
		{
 | 
			
		||||
			rc = MQTTPACKET_READ_ERROR;	/* bad data */
 | 
			
		||||
			goto exit;
 | 
			
		||||
		}
 | 
			
		||||
		rc = (*getcharfn)(&c, 1);
 | 
			
		||||
		if (rc != 1)
 | 
			
		||||
			goto exit;
 | 
			
		||||
		*value += (c & 127) * multiplier;
 | 
			
		||||
		multiplier *= 128;
 | 
			
		||||
	} while ((c & 128) != 0);
 | 
			
		||||
exit:
 | 
			
		||||
	FUNC_EXIT_RC(len);
 | 
			
		||||
	return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int MQTTPacket_len(int rem_len)
 | 
			
		||||
{
 | 
			
		||||
	rem_len += 1; /* header byte */
 | 
			
		||||
 | 
			
		||||
	/* now remaining_length field */
 | 
			
		||||
	if (rem_len < 128)
 | 
			
		||||
		rem_len += 1;
 | 
			
		||||
	else if (rem_len < 16384)
 | 
			
		||||
		rem_len += 2;
 | 
			
		||||
	else if (rem_len < 2097151)
 | 
			
		||||
		rem_len += 3;
 | 
			
		||||
	else
 | 
			
		||||
		rem_len += 4;
 | 
			
		||||
	return rem_len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static unsigned char* bufptr;
 | 
			
		||||
 | 
			
		||||
int bufchar(unsigned char* c, int count)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < count; ++i)
 | 
			
		||||
		*c = *bufptr++;
 | 
			
		||||
	return count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int MQTTPacket_decodeBuf(unsigned char* buf, int* value)
 | 
			
		||||
{
 | 
			
		||||
	bufptr = buf;
 | 
			
		||||
	return MQTTPacket_decode(bufchar, value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Calculates an integer from two bytes read from the input buffer
 | 
			
		||||
 * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
 | 
			
		||||
 * @return the integer value calculated
 | 
			
		||||
 */
 | 
			
		||||
int readInt(unsigned char** pptr)
 | 
			
		||||
{
 | 
			
		||||
	unsigned char* ptr = *pptr;
 | 
			
		||||
	int len = 256*(*ptr) + (*(ptr+1));
 | 
			
		||||
	*pptr += 2;
 | 
			
		||||
	return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Reads one character from the input buffer.
 | 
			
		||||
 * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
 | 
			
		||||
 * @return the character read
 | 
			
		||||
 */
 | 
			
		||||
char readChar(unsigned char** pptr)
 | 
			
		||||
{
 | 
			
		||||
	char c = **pptr;
 | 
			
		||||
	(*pptr)++;
 | 
			
		||||
	return c;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Writes one character to an output buffer.
 | 
			
		||||
 * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
 | 
			
		||||
 * @param c the character to write
 | 
			
		||||
 */
 | 
			
		||||
void writeChar(unsigned char** pptr, char c)
 | 
			
		||||
{
 | 
			
		||||
	**pptr = c;
 | 
			
		||||
	(*pptr)++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Writes an integer as 2 bytes to an output buffer.
 | 
			
		||||
 * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
 | 
			
		||||
 * @param anInt the integer to write
 | 
			
		||||
 */
 | 
			
		||||
void writeInt(unsigned char** pptr, int anInt)
 | 
			
		||||
{
 | 
			
		||||
	**pptr = (unsigned char)(anInt / 256);
 | 
			
		||||
	(*pptr)++;
 | 
			
		||||
	**pptr = (unsigned char)(anInt % 256);
 | 
			
		||||
	(*pptr)++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Writes a "UTF" string to an output buffer.  Converts C string to length-delimited.
 | 
			
		||||
 * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
 | 
			
		||||
 * @param string the C string to write
 | 
			
		||||
 */
 | 
			
		||||
void writeCString(unsigned char** pptr, const char* string)
 | 
			
		||||
{
 | 
			
		||||
	int len = strlen(string);
 | 
			
		||||
	writeInt(pptr, len);
 | 
			
		||||
	memcpy(*pptr, string, len);
 | 
			
		||||
	*pptr += len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int getLenStringLen(char* ptr)
 | 
			
		||||
{
 | 
			
		||||
	int len = 256*((unsigned char)(*ptr)) + (unsigned char)(*(ptr+1));
 | 
			
		||||
	return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void writeMQTTString(unsigned char** pptr, MQTTString mqttstring)
 | 
			
		||||
{
 | 
			
		||||
	if (mqttstring.lenstring.len > 0)
 | 
			
		||||
	{
 | 
			
		||||
		writeInt(pptr, mqttstring.lenstring.len);
 | 
			
		||||
		memcpy(*pptr, mqttstring.lenstring.data, mqttstring.lenstring.len);
 | 
			
		||||
		*pptr += mqttstring.lenstring.len;
 | 
			
		||||
	}
 | 
			
		||||
	else if (mqttstring.cstring)
 | 
			
		||||
		writeCString(pptr, mqttstring.cstring);
 | 
			
		||||
	else
 | 
			
		||||
		writeInt(pptr, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param mqttstring the MQTTString structure into which the data is to be read
 | 
			
		||||
 * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
 | 
			
		||||
 * @param enddata pointer to the end of the data: do not read beyond
 | 
			
		||||
 * @return 1 if successful, 0 if not
 | 
			
		||||
 */
 | 
			
		||||
int readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata)
 | 
			
		||||
{
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
 | 
			
		||||
	FUNC_ENTRY;
 | 
			
		||||
	/* the first two bytes are the length of the string */
 | 
			
		||||
	if (enddata - (*pptr) > 1) /* enough length to read the integer? */
 | 
			
		||||
	{
 | 
			
		||||
		mqttstring->lenstring.len = readInt(pptr); /* increments pptr to point past length */
 | 
			
		||||
		if (&(*pptr)[mqttstring->lenstring.len] <= enddata)
 | 
			
		||||
		{
 | 
			
		||||
			mqttstring->lenstring.data = (char*)*pptr;
 | 
			
		||||
			*pptr += mqttstring->lenstring.len;
 | 
			
		||||
			rc = 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	mqttstring->cstring = NULL;
 | 
			
		||||
	FUNC_EXIT_RC(rc);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Return the length of the MQTTstring - C string if there is one, otherwise the length delimited string
 | 
			
		||||
 * @param mqttstring the string to return the length of
 | 
			
		||||
 * @return the length of the string
 | 
			
		||||
 */
 | 
			
		||||
int MQTTstrlen(MQTTString mqttstring)
 | 
			
		||||
{
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
 | 
			
		||||
	if (mqttstring.cstring)
 | 
			
		||||
		rc = strlen(mqttstring.cstring);
 | 
			
		||||
	else
 | 
			
		||||
		rc = mqttstring.lenstring.len;
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Compares an MQTTString to a C string
 | 
			
		||||
 * @param a the MQTTString to compare
 | 
			
		||||
 * @param bptr the C string to compare
 | 
			
		||||
 * @return boolean - equal or not
 | 
			
		||||
 */
 | 
			
		||||
int MQTTPacket_equals(MQTTString* a, char* bptr)
 | 
			
		||||
{
 | 
			
		||||
	int alen = 0,
 | 
			
		||||
		blen = 0;
 | 
			
		||||
	char *aptr;
 | 
			
		||||
	
 | 
			
		||||
	if (a->cstring)
 | 
			
		||||
	{
 | 
			
		||||
		aptr = a->cstring;
 | 
			
		||||
		alen = strlen(a->cstring);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		aptr = a->lenstring.data;
 | 
			
		||||
		alen = a->lenstring.len;
 | 
			
		||||
	}
 | 
			
		||||
	blen = strlen(bptr);
 | 
			
		||||
	
 | 
			
		||||
	return (alen == blen) && (strncmp(aptr, bptr, alen) == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Helper function to read packet data from some source into a buffer
 | 
			
		||||
 * @param buf the buffer into which the packet will be serialized
 | 
			
		||||
 * @param buflen the length in bytes of the supplied buffer
 | 
			
		||||
 * @param getfn pointer to a function which will read any number of bytes from the needed source
 | 
			
		||||
 * @return integer MQTT packet type, or -1 on error
 | 
			
		||||
 * @note  the whole message must fit into the caller's buffer
 | 
			
		||||
 */
 | 
			
		||||
int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int))
 | 
			
		||||
{
 | 
			
		||||
	int rc = -1;
 | 
			
		||||
	MQTTHeader header = {0};
 | 
			
		||||
	int len = 0;
 | 
			
		||||
	int rem_len = 0;
 | 
			
		||||
 | 
			
		||||
	/* 1. read the header byte.  This has the packet type in it */
 | 
			
		||||
	if ((*getfn)(buf, 1) != 1)
 | 
			
		||||
		goto exit;
 | 
			
		||||
 | 
			
		||||
	len = 1;
 | 
			
		||||
	/* 2. read the remaining length.  This is variable in itself */
 | 
			
		||||
	MQTTPacket_decode(getfn, &rem_len);
 | 
			
		||||
	len += MQTTPacket_encode(buf + 1, rem_len); /* put the original remaining length back into the buffer */
 | 
			
		||||
 | 
			
		||||
	/* 3. read the rest of the buffer using a callback to supply the rest of the data */
 | 
			
		||||
	if((rem_len + len) > buflen)
 | 
			
		||||
		goto exit;
 | 
			
		||||
	if (rem_len && ((*getfn)(buf + len, rem_len) != rem_len))
 | 
			
		||||
		goto exit;
 | 
			
		||||
 | 
			
		||||
	header.byte = buf[0];
 | 
			
		||||
	rc = header.bits.type;
 | 
			
		||||
exit:
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Decodes the message length according to the MQTT algorithm, non-blocking
 | 
			
		||||
 * @param trp pointer to a transport structure holding what is needed to solve getting data from it
 | 
			
		||||
 * @param value the decoded length returned
 | 
			
		||||
 * @return integer the number of bytes read from the socket, 0 for call again, or -1 on error
 | 
			
		||||
 */
 | 
			
		||||
static int MQTTPacket_decodenb(MQTTTransport *trp)
 | 
			
		||||
{
 | 
			
		||||
	unsigned char c;
 | 
			
		||||
	int rc = MQTTPACKET_READ_ERROR;
 | 
			
		||||
 | 
			
		||||
	FUNC_ENTRY;
 | 
			
		||||
	if(trp->len == 0){		/* initialize on first call */
 | 
			
		||||
		trp->multiplier = 1;
 | 
			
		||||
		trp->rem_len = 0;
 | 
			
		||||
	}
 | 
			
		||||
	do {
 | 
			
		||||
		int frc;
 | 
			
		||||
		if (trp->len >= MAX_NO_OF_REMAINING_LENGTH_BYTES)
 | 
			
		||||
			goto exit;
 | 
			
		||||
		if ((frc=(*trp->getfn)(trp->sck, &c, 1)) == -1)
 | 
			
		||||
			goto exit;
 | 
			
		||||
		if (frc == 0){
 | 
			
		||||
			rc = 0;
 | 
			
		||||
			goto exit;
 | 
			
		||||
		}
 | 
			
		||||
		++(trp->len);
 | 
			
		||||
		trp->rem_len += (c & 127) * trp->multiplier;
 | 
			
		||||
		trp->multiplier *= 128;
 | 
			
		||||
	} while ((c & 128) != 0);
 | 
			
		||||
	rc = trp->len;
 | 
			
		||||
exit:
 | 
			
		||||
	FUNC_EXIT_RC(rc);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Helper function to read packet data from some source into a buffer, non-blocking
 | 
			
		||||
 * @param buf the buffer into which the packet will be serialized
 | 
			
		||||
 * @param buflen the length in bytes of the supplied buffer
 | 
			
		||||
 * @param trp pointer to a transport structure holding what is needed to solve getting data from it
 | 
			
		||||
 * @return integer MQTT packet type, 0 for call again, or -1 on error
 | 
			
		||||
 * @note  the whole message must fit into the caller's buffer
 | 
			
		||||
 */
 | 
			
		||||
int MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp)
 | 
			
		||||
{
 | 
			
		||||
	int rc = -1, frc;
 | 
			
		||||
	MQTTHeader header = {0};
 | 
			
		||||
 | 
			
		||||
	switch(trp->state){
 | 
			
		||||
	default:
 | 
			
		||||
		trp->state = 0;
 | 
			
		||||
		/*FALLTHROUGH*/
 | 
			
		||||
	case 0:
 | 
			
		||||
		/* read the header byte.  This has the packet type in it */
 | 
			
		||||
		if ((frc=(*trp->getfn)(trp->sck, buf, 1)) == -1)
 | 
			
		||||
			goto exit;
 | 
			
		||||
		if (frc == 0)
 | 
			
		||||
			return 0;
 | 
			
		||||
		trp->len = 0;
 | 
			
		||||
		++trp->state;
 | 
			
		||||
		/*FALLTHROUGH*/
 | 
			
		||||
		/* read the remaining length.  This is variable in itself */
 | 
			
		||||
	case 1:
 | 
			
		||||
		if((frc=MQTTPacket_decodenb(trp)) == MQTTPACKET_READ_ERROR)
 | 
			
		||||
			goto exit;
 | 
			
		||||
		if(frc == 0)
 | 
			
		||||
			return 0;
 | 
			
		||||
		trp->len = 1 + MQTTPacket_encode(buf + 1, trp->rem_len); /* put the original remaining length back into the buffer */
 | 
			
		||||
		if((trp->rem_len + trp->len) > buflen)
 | 
			
		||||
			goto exit;
 | 
			
		||||
		++trp->state;
 | 
			
		||||
		/*FALLTHROUGH*/
 | 
			
		||||
	case 2:
 | 
			
		||||
		if(trp->rem_len){
 | 
			
		||||
			/* read the rest of the buffer using a callback to supply the rest of the data */
 | 
			
		||||
			if ((frc=(*trp->getfn)(trp->sck, buf + trp->len, trp->rem_len)) == -1)
 | 
			
		||||
				goto exit;
 | 
			
		||||
			if (frc == 0)
 | 
			
		||||
				return 0;
 | 
			
		||||
			trp->rem_len -= frc;
 | 
			
		||||
			trp->len += frc;
 | 
			
		||||
			if(trp->rem_len)
 | 
			
		||||
				return 0;
 | 
			
		||||
		}
 | 
			
		||||
		header.byte = buf[0];
 | 
			
		||||
		rc = header.bits.type;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
exit:
 | 
			
		||||
	trp->state = 0;
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,134 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 * Copyright (c) 2014 IBM Corp.
 | 
			
		||||
 *
 | 
			
		||||
 * All rights reserved. This program and the accompanying materials
 | 
			
		||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
			
		||||
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 | 
			
		||||
 *
 | 
			
		||||
 * The Eclipse Public License is available at
 | 
			
		||||
 *    http://www.eclipse.org/legal/epl-v10.html
 | 
			
		||||
 * and the Eclipse Distribution License is available at
 | 
			
		||||
 *   http://www.eclipse.org/org/documents/edl-v10.php.
 | 
			
		||||
 *
 | 
			
		||||
 * Contributors:
 | 
			
		||||
 *    Ian Craggs - initial API and implementation and/or initial documentation
 | 
			
		||||
 *    Xiang Rong - 442039 Add makefile to Embedded C client
 | 
			
		||||
 *******************************************************************************/
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#ifndef MQTTPACKET_H_
 | 
			
		||||
#define MQTTPACKET_H_
 | 
			
		||||
 | 
			
		||||
#if defined(__cplusplus) /* If this is a C++ compiler, use C linkage */
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(WIN32_DLL) || defined(WIN64_DLL)
 | 
			
		||||
  #define DLLImport __declspec(dllimport)
 | 
			
		||||
  #define DLLExport __declspec(dllexport)
 | 
			
		||||
#elif defined(LINUX_SO)
 | 
			
		||||
  #define DLLImport extern
 | 
			
		||||
  #define DLLExport  __attribute__ ((visibility ("default")))
 | 
			
		||||
#else
 | 
			
		||||
  #define DLLImport
 | 
			
		||||
  #define DLLExport  
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
enum errors
 | 
			
		||||
{
 | 
			
		||||
	MQTTPACKET_BUFFER_TOO_SHORT = -2,
 | 
			
		||||
	MQTTPACKET_READ_ERROR = -1,
 | 
			
		||||
	MQTTPACKET_READ_COMPLETE
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum msgTypes
 | 
			
		||||
{
 | 
			
		||||
	CONNECT = 1, CONNACK, PUBLISH, PUBACK, PUBREC, PUBREL,
 | 
			
		||||
	PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK,
 | 
			
		||||
	PINGREQ, PINGRESP, DISCONNECT
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Bitfields for the MQTT header byte.
 | 
			
		||||
 */
 | 
			
		||||
typedef union
 | 
			
		||||
{
 | 
			
		||||
	unsigned char byte;	                /**< the whole byte */
 | 
			
		||||
#if defined(REVERSED)
 | 
			
		||||
	struct
 | 
			
		||||
	{
 | 
			
		||||
		unsigned int type : 4;			/**< message type nibble */
 | 
			
		||||
		unsigned int dup : 1;				/**< DUP flag bit */
 | 
			
		||||
		unsigned int qos : 2;				/**< QoS value, 0, 1 or 2 */
 | 
			
		||||
		unsigned int retain : 1;		/**< retained flag bit */
 | 
			
		||||
	} bits;
 | 
			
		||||
#else
 | 
			
		||||
	struct
 | 
			
		||||
	{
 | 
			
		||||
		unsigned int retain : 1;		/**< retained flag bit */
 | 
			
		||||
		unsigned int qos : 2;				/**< QoS value, 0, 1 or 2 */
 | 
			
		||||
		unsigned int dup : 1;				/**< DUP flag bit */
 | 
			
		||||
		unsigned int type : 4;			/**< message type nibble */
 | 
			
		||||
	} bits;
 | 
			
		||||
#endif
 | 
			
		||||
} MQTTHeader;
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
	int len;
 | 
			
		||||
	char* data;
 | 
			
		||||
} MQTTLenString;
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
	char* cstring;
 | 
			
		||||
	MQTTLenString lenstring;
 | 
			
		||||
} MQTTString;
 | 
			
		||||
 | 
			
		||||
#define MQTTString_initializer {NULL, {0, NULL}}
 | 
			
		||||
 | 
			
		||||
int MQTTstrlen(MQTTString mqttstring);
 | 
			
		||||
 | 
			
		||||
#include "MQTTConnect.h"
 | 
			
		||||
#include "MQTTPublish.h"
 | 
			
		||||
#include "MQTTSubscribe.h"
 | 
			
		||||
#include "MQTTUnsubscribe.h"
 | 
			
		||||
#include "MQTTFormat.h"
 | 
			
		||||
 | 
			
		||||
DLLExport int MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char type, unsigned char dup, unsigned short packetid);
 | 
			
		||||
DLLExport int MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen);
 | 
			
		||||
 | 
			
		||||
int MQTTPacket_len(int rem_len);
 | 
			
		||||
DLLExport int MQTTPacket_equals(MQTTString* a, char* b);
 | 
			
		||||
 | 
			
		||||
DLLExport int MQTTPacket_encode(unsigned char* buf, int length);
 | 
			
		||||
int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value);
 | 
			
		||||
int MQTTPacket_decodeBuf(unsigned char* buf, int* value);
 | 
			
		||||
 | 
			
		||||
int readInt(unsigned char** pptr);
 | 
			
		||||
char readChar(unsigned char** pptr);
 | 
			
		||||
void writeChar(unsigned char** pptr, char c);
 | 
			
		||||
void writeInt(unsigned char** pptr, int anInt);
 | 
			
		||||
int readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata);
 | 
			
		||||
void writeCString(unsigned char** pptr, const char* string);
 | 
			
		||||
void writeMQTTString(unsigned char** pptr, MQTTString mqttstring);
 | 
			
		||||
 | 
			
		||||
DLLExport int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int));
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
	int (*getfn)(void *, unsigned char*, int); /* must return -1 for error, 0 for call again, or the number of bytes read */
 | 
			
		||||
	void *sck;	/* pointer to whatever the system may use to identify the transport */
 | 
			
		||||
	int multiplier;
 | 
			
		||||
	int rem_len;
 | 
			
		||||
	int len;
 | 
			
		||||
	char state;
 | 
			
		||||
}MQTTTransport;
 | 
			
		||||
 | 
			
		||||
int MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp);
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus /* If this is a C++ compiler, use C linkage */
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif /* MQTTPACKET_H_ */
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,38 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 * Copyright (c) 2014 IBM Corp.
 | 
			
		||||
 *
 | 
			
		||||
 * All rights reserved. This program and the accompanying materials
 | 
			
		||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
			
		||||
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 | 
			
		||||
 *
 | 
			
		||||
 * The Eclipse Public License is available at
 | 
			
		||||
 *    http://www.eclipse.org/legal/epl-v10.html
 | 
			
		||||
 * and the Eclipse Distribution License is available at
 | 
			
		||||
 *   http://www.eclipse.org/org/documents/edl-v10.php.
 | 
			
		||||
 *
 | 
			
		||||
 * Contributors:
 | 
			
		||||
 *    Ian Craggs - initial API and implementation and/or initial documentation
 | 
			
		||||
 *    Xiang Rong - 442039 Add makefile to Embedded C client
 | 
			
		||||
 *******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#ifndef MQTTPUBLISH_H_
 | 
			
		||||
#define MQTTPUBLISH_H_
 | 
			
		||||
 | 
			
		||||
#if !defined(DLLImport)
 | 
			
		||||
  #define DLLImport 
 | 
			
		||||
#endif
 | 
			
		||||
#if !defined(DLLExport)
 | 
			
		||||
  #define DLLExport
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
DLLExport int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid,
 | 
			
		||||
		MQTTString topicName, unsigned char* payload, int payloadlen);
 | 
			
		||||
 | 
			
		||||
DLLExport int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName,
 | 
			
		||||
		unsigned char** payload, int32_t* payloadlen, unsigned char* buf, int len);
 | 
			
		||||
 | 
			
		||||
DLLExport int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid);
 | 
			
		||||
DLLExport int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid);
 | 
			
		||||
DLLExport int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid);
 | 
			
		||||
 | 
			
		||||
#endif /* MQTTPUBLISH_H_ */
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,169 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 * Copyright (c) 2014 IBM Corp.
 | 
			
		||||
 *
 | 
			
		||||
 * All rights reserved. This program and the accompanying materials
 | 
			
		||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
			
		||||
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 | 
			
		||||
 *
 | 
			
		||||
 * The Eclipse Public License is available at
 | 
			
		||||
 *    http://www.eclipse.org/legal/epl-v10.html
 | 
			
		||||
 * and the Eclipse Distribution License is available at
 | 
			
		||||
 *   http://www.eclipse.org/org/documents/edl-v10.php.
 | 
			
		||||
 *
 | 
			
		||||
 * Contributors:
 | 
			
		||||
 *    Ian Craggs - initial API and implementation and/or initial documentation
 | 
			
		||||
 *    Ian Craggs - fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=453144
 | 
			
		||||
 *******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "MQTTPacket.h"
 | 
			
		||||
#include "StackTrace.h"
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  * Determines the length of the MQTT publish packet that would be produced using the supplied parameters
 | 
			
		||||
  * @param qos the MQTT QoS of the publish (packetid is omitted for QoS 0)
 | 
			
		||||
  * @param topicName the topic name to be used in the publish  
 | 
			
		||||
  * @param payloadlen the length of the payload to be sent
 | 
			
		||||
  * @return the length of buffer needed to contain the serialized version of the packet
 | 
			
		||||
  */
 | 
			
		||||
int MQTTSerialize_publishLength(int qos, MQTTString topicName, int payloadlen)
 | 
			
		||||
{
 | 
			
		||||
	int len = 0;
 | 
			
		||||
 | 
			
		||||
	len += 2 + MQTTstrlen(topicName) + payloadlen;
 | 
			
		||||
	if (qos > 0)
 | 
			
		||||
		len += 2; /* packetid */
 | 
			
		||||
	return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  * Serializes the supplied publish data into the supplied buffer, ready for sending
 | 
			
		||||
  * @param buf the buffer into which the packet will be serialized
 | 
			
		||||
  * @param buflen the length in bytes of the supplied buffer
 | 
			
		||||
  * @param dup integer - the MQTT dup flag
 | 
			
		||||
  * @param qos integer - the MQTT QoS value
 | 
			
		||||
  * @param retained integer - the MQTT retained flag
 | 
			
		||||
  * @param packetid integer - the MQTT packet identifier
 | 
			
		||||
  * @param topicName MQTTString - the MQTT topic in the publish
 | 
			
		||||
  * @param payload byte buffer - the MQTT publish payload
 | 
			
		||||
  * @param payloadlen integer - the length of the MQTT payload
 | 
			
		||||
  * @return the length of the serialized data.  <= 0 indicates error
 | 
			
		||||
  */
 | 
			
		||||
int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid,
 | 
			
		||||
		MQTTString topicName, unsigned char* payload, int payloadlen)
 | 
			
		||||
{
 | 
			
		||||
	unsigned char *ptr = buf;
 | 
			
		||||
	MQTTHeader header = {0};
 | 
			
		||||
	int rem_len = 0;
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
 | 
			
		||||
	FUNC_ENTRY;
 | 
			
		||||
	if (MQTTPacket_len(rem_len = MQTTSerialize_publishLength(qos, topicName, payloadlen)) > buflen)
 | 
			
		||||
	{
 | 
			
		||||
		rc = MQTTPACKET_BUFFER_TOO_SHORT;
 | 
			
		||||
		goto exit;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	header.bits.type = PUBLISH;
 | 
			
		||||
	header.bits.dup = dup;
 | 
			
		||||
	header.bits.qos = qos;
 | 
			
		||||
	header.bits.retain = retained;
 | 
			
		||||
	writeChar(&ptr, header.byte); /* write header */
 | 
			
		||||
 | 
			
		||||
	ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */;
 | 
			
		||||
 | 
			
		||||
	writeMQTTString(&ptr, topicName);
 | 
			
		||||
 | 
			
		||||
	if (qos > 0)
 | 
			
		||||
		writeInt(&ptr, packetid);
 | 
			
		||||
 | 
			
		||||
	memcpy(ptr, payload, payloadlen);
 | 
			
		||||
	ptr += payloadlen;
 | 
			
		||||
 | 
			
		||||
	rc = ptr - buf;
 | 
			
		||||
 | 
			
		||||
exit:
 | 
			
		||||
	FUNC_EXIT_RC(rc);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  * Serializes the ack packet into the supplied buffer.
 | 
			
		||||
  * @param buf the buffer into which the packet will be serialized
 | 
			
		||||
  * @param buflen the length in bytes of the supplied buffer
 | 
			
		||||
  * @param type the MQTT packet type
 | 
			
		||||
  * @param dup the MQTT dup flag
 | 
			
		||||
  * @param packetid the MQTT packet identifier
 | 
			
		||||
  * @return serialized length, or error if 0
 | 
			
		||||
  */
 | 
			
		||||
int MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char packettype, unsigned char dup, unsigned short packetid)
 | 
			
		||||
{
 | 
			
		||||
	MQTTHeader header = {0};
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	unsigned char *ptr = buf;
 | 
			
		||||
 | 
			
		||||
	FUNC_ENTRY;
 | 
			
		||||
	if (buflen < 4)
 | 
			
		||||
	{
 | 
			
		||||
		rc = MQTTPACKET_BUFFER_TOO_SHORT;
 | 
			
		||||
		goto exit;
 | 
			
		||||
	}
 | 
			
		||||
	header.bits.type = packettype;
 | 
			
		||||
	header.bits.dup = dup;
 | 
			
		||||
	header.bits.qos = (packettype == PUBREL) ? 1 : 0;
 | 
			
		||||
	writeChar(&ptr, header.byte); /* write header */
 | 
			
		||||
 | 
			
		||||
	ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */
 | 
			
		||||
	writeInt(&ptr, packetid);
 | 
			
		||||
	rc = ptr - buf;
 | 
			
		||||
exit:
 | 
			
		||||
	FUNC_EXIT_RC(rc);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  * Serializes a puback packet into the supplied buffer.
 | 
			
		||||
  * @param buf the buffer into which the packet will be serialized
 | 
			
		||||
  * @param buflen the length in bytes of the supplied buffer
 | 
			
		||||
  * @param packetid integer - the MQTT packet identifier
 | 
			
		||||
  * @return serialized length, or error if 0
 | 
			
		||||
  */
 | 
			
		||||
int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid)
 | 
			
		||||
{
 | 
			
		||||
	return MQTTSerialize_ack(buf, buflen, PUBACK, 0, packetid);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  * Serializes a pubrel packet into the supplied buffer.
 | 
			
		||||
  * @param buf the buffer into which the packet will be serialized
 | 
			
		||||
  * @param buflen the length in bytes of the supplied buffer
 | 
			
		||||
  * @param dup integer - the MQTT dup flag
 | 
			
		||||
  * @param packetid integer - the MQTT packet identifier
 | 
			
		||||
  * @return serialized length, or error if 0
 | 
			
		||||
  */
 | 
			
		||||
int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid)
 | 
			
		||||
{
 | 
			
		||||
	return MQTTSerialize_ack(buf, buflen, PUBREL, dup, packetid);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  * Serializes a pubrel packet into the supplied buffer.
 | 
			
		||||
  * @param buf the buffer into which the packet will be serialized
 | 
			
		||||
  * @param buflen the length in bytes of the supplied buffer
 | 
			
		||||
  * @param packetid integer - the MQTT packet identifier
 | 
			
		||||
  * @return serialized length, or error if 0
 | 
			
		||||
  */
 | 
			
		||||
int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid)
 | 
			
		||||
{
 | 
			
		||||
	return MQTTSerialize_ack(buf, buflen, PUBCOMP, 0, packetid);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,39 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 * Copyright (c) 2014 IBM Corp.
 | 
			
		||||
 *
 | 
			
		||||
 * All rights reserved. This program and the accompanying materials
 | 
			
		||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
			
		||||
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 | 
			
		||||
 *
 | 
			
		||||
 * The Eclipse Public License is available at
 | 
			
		||||
 *    http://www.eclipse.org/legal/epl-v10.html
 | 
			
		||||
 * and the Eclipse Distribution License is available at
 | 
			
		||||
 *   http://www.eclipse.org/org/documents/edl-v10.php.
 | 
			
		||||
 *
 | 
			
		||||
 * Contributors:
 | 
			
		||||
 *    Ian Craggs - initial API and implementation and/or initial documentation
 | 
			
		||||
 *    Xiang Rong - 442039 Add makefile to Embedded C client
 | 
			
		||||
 *******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#ifndef MQTTSUBSCRIBE_H_
 | 
			
		||||
#define MQTTSUBSCRIBE_H_
 | 
			
		||||
 | 
			
		||||
#if !defined(DLLImport)
 | 
			
		||||
  #define DLLImport 
 | 
			
		||||
#endif
 | 
			
		||||
#if !defined(DLLExport)
 | 
			
		||||
  #define DLLExport
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
DLLExport int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid,
 | 
			
		||||
		int count, MQTTString topicFilters[], int32_t requestedQoSs[]);
 | 
			
		||||
 | 
			
		||||
DLLExport int MQTTDeserialize_subscribe(unsigned char* dup, unsigned short* packetid,
 | 
			
		||||
		int maxcount, int* count, MQTTString topicFilters[], int requestedQoSs[], unsigned char* buf, int len);
 | 
			
		||||
 | 
			
		||||
DLLExport int MQTTSerialize_suback(unsigned char* buf, int buflen, unsigned short packetid, int count, int* grantedQoSs);
 | 
			
		||||
 | 
			
		||||
DLLExport int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int32_t* count, int32_t grantedQoSs[], unsigned char* buf, int len);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif /* MQTTSUBSCRIBE_H_ */
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,137 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 * Copyright (c) 2014 IBM Corp.
 | 
			
		||||
 *
 | 
			
		||||
 * All rights reserved. This program and the accompanying materials
 | 
			
		||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
			
		||||
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 | 
			
		||||
 *
 | 
			
		||||
 * The Eclipse Public License is available at
 | 
			
		||||
 *    http://www.eclipse.org/legal/epl-v10.html
 | 
			
		||||
 * and the Eclipse Distribution License is available at
 | 
			
		||||
 *   http://www.eclipse.org/org/documents/edl-v10.php.
 | 
			
		||||
 *
 | 
			
		||||
 * Contributors:
 | 
			
		||||
 *    Ian Craggs - initial API and implementation and/or initial documentation
 | 
			
		||||
 *******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "MQTTPacket.h"
 | 
			
		||||
#include "StackTrace.h"
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  * Determines the length of the MQTT subscribe packet that would be produced using the supplied parameters
 | 
			
		||||
  * @param count the number of topic filter strings in topicFilters
 | 
			
		||||
  * @param topicFilters the array of topic filter strings to be used in the publish
 | 
			
		||||
  * @return the length of buffer needed to contain the serialized version of the packet
 | 
			
		||||
  */
 | 
			
		||||
int MQTTSerialize_subscribeLength(int count, MQTTString topicFilters[])
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	int len = 2; /* packetid */
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < count; ++i)
 | 
			
		||||
		len += 2 + MQTTstrlen(topicFilters[i]) + 1; /* length + topic + req_qos */
 | 
			
		||||
	return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  * Serializes the supplied subscribe data into the supplied buffer, ready for sending
 | 
			
		||||
  * @param buf the buffer into which the packet will be serialized
 | 
			
		||||
  * @param buflen the length in bytes of the supplied bufferr
 | 
			
		||||
  * @param dup integer - the MQTT dup flag
 | 
			
		||||
  * @param packetid integer - the MQTT packet identifier
 | 
			
		||||
  * @param count - number of members in the topicFilters and reqQos arrays
 | 
			
		||||
  * @param topicFilters - array of topic filter names
 | 
			
		||||
  * @param requestedQoSs - array of requested QoS
 | 
			
		||||
  * @return the length of the serialized data.  <= 0 indicates error
 | 
			
		||||
  */
 | 
			
		||||
int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, int count,
 | 
			
		||||
		MQTTString topicFilters[], int32_t requestedQoSs[])
 | 
			
		||||
{
 | 
			
		||||
	unsigned char *ptr = buf;
 | 
			
		||||
	MQTTHeader header = {0};
 | 
			
		||||
	int rem_len = 0;
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	int i = 0;
 | 
			
		||||
 | 
			
		||||
	FUNC_ENTRY;
 | 
			
		||||
	if (MQTTPacket_len(rem_len = MQTTSerialize_subscribeLength(count, topicFilters)) > buflen)
 | 
			
		||||
	{
 | 
			
		||||
		rc = MQTTPACKET_BUFFER_TOO_SHORT;
 | 
			
		||||
		goto exit;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	header.byte = 0;
 | 
			
		||||
	header.bits.type = SUBSCRIBE;
 | 
			
		||||
	header.bits.dup = dup;
 | 
			
		||||
	header.bits.qos = 1;
 | 
			
		||||
	writeChar(&ptr, header.byte); /* write header */
 | 
			
		||||
 | 
			
		||||
	ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */;
 | 
			
		||||
 | 
			
		||||
	writeInt(&ptr, packetid);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < count; ++i)
 | 
			
		||||
	{
 | 
			
		||||
		writeMQTTString(&ptr, topicFilters[i]);
 | 
			
		||||
		writeChar(&ptr, requestedQoSs[i]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = ptr - buf;
 | 
			
		||||
exit:
 | 
			
		||||
	FUNC_EXIT_RC(rc);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  * Deserializes the supplied (wire) buffer into suback data
 | 
			
		||||
  * @param packetid returned integer - the MQTT packet identifier
 | 
			
		||||
  * @param maxcount - the maximum number of members allowed in the grantedQoSs array
 | 
			
		||||
  * @param count returned integer - number of members in the grantedQoSs array
 | 
			
		||||
  * @param grantedQoSs returned array of integers - the granted qualities of service
 | 
			
		||||
  * @param buf the raw buffer data, of the correct length determined by the remaining length field
 | 
			
		||||
  * @param buflen the length in bytes of the data in the supplied buffer
 | 
			
		||||
  * @return error code.  1 is success, 0 is failure
 | 
			
		||||
  */
 | 
			
		||||
int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int32_t* count, int32_t grantedQoSs[], unsigned char* buf, int buflen)
 | 
			
		||||
{
 | 
			
		||||
	MQTTHeader header = {0};
 | 
			
		||||
	unsigned char* curdata = buf;
 | 
			
		||||
	unsigned char* enddata = NULL;
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	int mylen;
 | 
			
		||||
 | 
			
		||||
	FUNC_ENTRY;
 | 
			
		||||
	header.byte = readChar(&curdata);
 | 
			
		||||
	if (header.bits.type != SUBACK)
 | 
			
		||||
		goto exit;
 | 
			
		||||
 | 
			
		||||
	curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
 | 
			
		||||
	enddata = curdata + mylen;
 | 
			
		||||
	if (enddata - curdata < 2)
 | 
			
		||||
		goto exit;
 | 
			
		||||
 | 
			
		||||
	*packetid = readInt(&curdata);
 | 
			
		||||
 | 
			
		||||
	*count = 0;
 | 
			
		||||
	while (curdata < enddata)
 | 
			
		||||
	{
 | 
			
		||||
		if (*count > maxcount)
 | 
			
		||||
		{
 | 
			
		||||
			rc = -1;
 | 
			
		||||
			goto exit;
 | 
			
		||||
		}
 | 
			
		||||
		grantedQoSs[(*count)++] = readChar(&curdata);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = 1;
 | 
			
		||||
exit:
 | 
			
		||||
	FUNC_EXIT_RC(rc);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,112 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 * Copyright (c) 2014 IBM Corp.
 | 
			
		||||
 *
 | 
			
		||||
 * All rights reserved. This program and the accompanying materials
 | 
			
		||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
			
		||||
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 | 
			
		||||
 *
 | 
			
		||||
 * The Eclipse Public License is available at
 | 
			
		||||
 *    http://www.eclipse.org/legal/epl-v10.html
 | 
			
		||||
 * and the Eclipse Distribution License is available at
 | 
			
		||||
 *   http://www.eclipse.org/org/documents/edl-v10.php.
 | 
			
		||||
 *
 | 
			
		||||
 * Contributors:
 | 
			
		||||
 *    Ian Craggs - initial API and implementation and/or initial documentation
 | 
			
		||||
 *******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "MQTTPacket.h"
 | 
			
		||||
#include "StackTrace.h"
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  * Deserializes the supplied (wire) buffer into subscribe data
 | 
			
		||||
  * @param dup integer returned - the MQTT dup flag
 | 
			
		||||
  * @param packetid integer returned - the MQTT packet identifier
 | 
			
		||||
  * @param maxcount - the maximum number of members allowed in the topicFilters and requestedQoSs arrays
 | 
			
		||||
  * @param count - number of members in the topicFilters and requestedQoSs arrays
 | 
			
		||||
  * @param topicFilters - array of topic filter names
 | 
			
		||||
  * @param requestedQoSs - array of requested QoS
 | 
			
		||||
  * @param buf the raw buffer data, of the correct length determined by the remaining length field
 | 
			
		||||
  * @param buflen the length in bytes of the data in the supplied buffer
 | 
			
		||||
  * @return the length of the serialized data.  <= 0 indicates error
 | 
			
		||||
  */
 | 
			
		||||
int MQTTDeserialize_subscribe(unsigned char* dup, unsigned short* packetid, int maxcount, int* count, MQTTString topicFilters[],
 | 
			
		||||
	int requestedQoSs[], unsigned char* buf, int buflen)
 | 
			
		||||
{
 | 
			
		||||
	MQTTHeader header = {0};
 | 
			
		||||
	unsigned char* curdata = buf;
 | 
			
		||||
	unsigned char* enddata = NULL;
 | 
			
		||||
	int rc = -1;
 | 
			
		||||
	int mylen = 0;
 | 
			
		||||
 | 
			
		||||
	FUNC_ENTRY;
 | 
			
		||||
	header.byte = readChar(&curdata);
 | 
			
		||||
	if (header.bits.type != SUBSCRIBE)
 | 
			
		||||
		goto exit;
 | 
			
		||||
	*dup = header.bits.dup;
 | 
			
		||||
 | 
			
		||||
	curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
 | 
			
		||||
	enddata = curdata + mylen;
 | 
			
		||||
 | 
			
		||||
	*packetid = readInt(&curdata);
 | 
			
		||||
 | 
			
		||||
	*count = 0;
 | 
			
		||||
	while (curdata < enddata)
 | 
			
		||||
	{
 | 
			
		||||
		if (!readMQTTLenString(&topicFilters[*count], &curdata, enddata))
 | 
			
		||||
			goto exit;
 | 
			
		||||
		if (curdata >= enddata) /* do we have enough data to read the req_qos version byte? */
 | 
			
		||||
			goto exit;
 | 
			
		||||
		requestedQoSs[*count] = readChar(&curdata);
 | 
			
		||||
		(*count)++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = 1;
 | 
			
		||||
exit:
 | 
			
		||||
	FUNC_EXIT_RC(rc);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  * Serializes the supplied suback data into the supplied buffer, ready for sending
 | 
			
		||||
  * @param buf the buffer into which the packet will be serialized
 | 
			
		||||
  * @param buflen the length in bytes of the supplied buffer
 | 
			
		||||
  * @param packetid integer - the MQTT packet identifier
 | 
			
		||||
  * @param count - number of members in the grantedQoSs array
 | 
			
		||||
  * @param grantedQoSs - array of granted QoS
 | 
			
		||||
  * @return the length of the serialized data.  <= 0 indicates error
 | 
			
		||||
  */
 | 
			
		||||
int MQTTSerialize_suback(unsigned char* buf, int buflen, unsigned short packetid, int count, int* grantedQoSs)
 | 
			
		||||
{
 | 
			
		||||
	MQTTHeader header = {0};
 | 
			
		||||
	int rc = -1;
 | 
			
		||||
	unsigned char *ptr = buf;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	FUNC_ENTRY;
 | 
			
		||||
	if (buflen < 2 + count)
 | 
			
		||||
	{
 | 
			
		||||
		rc = MQTTPACKET_BUFFER_TOO_SHORT;
 | 
			
		||||
		goto exit;
 | 
			
		||||
	}
 | 
			
		||||
	header.byte = 0;
 | 
			
		||||
	header.bits.type = SUBACK;
 | 
			
		||||
	writeChar(&ptr, header.byte); /* write header */
 | 
			
		||||
 | 
			
		||||
	ptr += MQTTPacket_encode(ptr, 2 + count); /* write remaining length */
 | 
			
		||||
 | 
			
		||||
	writeInt(&ptr, packetid);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < count; ++i)
 | 
			
		||||
		writeChar(&ptr, grantedQoSs[i]);
 | 
			
		||||
 | 
			
		||||
	rc = ptr - buf;
 | 
			
		||||
exit:
 | 
			
		||||
	FUNC_EXIT_RC(rc);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,38 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 * Copyright (c) 2014 IBM Corp.
 | 
			
		||||
 *
 | 
			
		||||
 * All rights reserved. This program and the accompanying materials
 | 
			
		||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
			
		||||
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 | 
			
		||||
 *
 | 
			
		||||
 * The Eclipse Public License is available at
 | 
			
		||||
 *    http://www.eclipse.org/legal/epl-v10.html
 | 
			
		||||
 * and the Eclipse Distribution License is available at
 | 
			
		||||
 *   http://www.eclipse.org/org/documents/edl-v10.php.
 | 
			
		||||
 *
 | 
			
		||||
 * Contributors:
 | 
			
		||||
 *    Ian Craggs - initial API and implementation and/or initial documentation
 | 
			
		||||
 *    Xiang Rong - 442039 Add makefile to Embedded C client
 | 
			
		||||
 *******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#ifndef MQTTUNSUBSCRIBE_H_
 | 
			
		||||
#define MQTTUNSUBSCRIBE_H_
 | 
			
		||||
 | 
			
		||||
#if !defined(DLLImport)
 | 
			
		||||
  #define DLLImport 
 | 
			
		||||
#endif
 | 
			
		||||
#if !defined(DLLExport)
 | 
			
		||||
  #define DLLExport
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
DLLExport int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid,
 | 
			
		||||
		int count, MQTTString topicFilters[]);
 | 
			
		||||
 | 
			
		||||
DLLExport int MQTTDeserialize_unsubscribe(unsigned char* dup, unsigned short* packetid, int max_count, int* count, MQTTString topicFilters[],
 | 
			
		||||
		unsigned char* buf, int len);
 | 
			
		||||
 | 
			
		||||
DLLExport int MQTTSerialize_unsuback(unsigned char* buf, int buflen, unsigned short packetid);
 | 
			
		||||
 | 
			
		||||
DLLExport int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int len);
 | 
			
		||||
 | 
			
		||||
#endif /* MQTTUNSUBSCRIBE_H_ */
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,106 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 * Copyright (c) 2014 IBM Corp.
 | 
			
		||||
 *
 | 
			
		||||
 * All rights reserved. This program and the accompanying materials
 | 
			
		||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
			
		||||
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 | 
			
		||||
 *
 | 
			
		||||
 * The Eclipse Public License is available at
 | 
			
		||||
 *    http://www.eclipse.org/legal/epl-v10.html
 | 
			
		||||
 * and the Eclipse Distribution License is available at
 | 
			
		||||
 *   http://www.eclipse.org/org/documents/edl-v10.php.
 | 
			
		||||
 *
 | 
			
		||||
 * Contributors:
 | 
			
		||||
 *    Ian Craggs - initial API and implementation and/or initial documentation
 | 
			
		||||
 *******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "MQTTPacket.h"
 | 
			
		||||
#include "StackTrace.h"
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  * Determines the length of the MQTT unsubscribe packet that would be produced using the supplied parameters
 | 
			
		||||
  * @param count the number of topic filter strings in topicFilters
 | 
			
		||||
  * @param topicFilters the array of topic filter strings to be used in the publish
 | 
			
		||||
  * @return the length of buffer needed to contain the serialized version of the packet
 | 
			
		||||
  */
 | 
			
		||||
int MQTTSerialize_unsubscribeLength(int count, MQTTString topicFilters[])
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	int len = 2; /* packetid */
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < count; ++i)
 | 
			
		||||
		len += 2 + MQTTstrlen(topicFilters[i]); /* length + topic*/
 | 
			
		||||
	return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  * Serializes the supplied unsubscribe data into the supplied buffer, ready for sending
 | 
			
		||||
  * @param buf the raw buffer data, of the correct length determined by the remaining length field
 | 
			
		||||
  * @param buflen the length in bytes of the data in the supplied buffer
 | 
			
		||||
  * @param dup integer - the MQTT dup flag
 | 
			
		||||
  * @param packetid integer - the MQTT packet identifier
 | 
			
		||||
  * @param count - number of members in the topicFilters array
 | 
			
		||||
  * @param topicFilters - array of topic filter names
 | 
			
		||||
  * @return the length of the serialized data.  <= 0 indicates error
 | 
			
		||||
  */
 | 
			
		||||
int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid,
 | 
			
		||||
		int count, MQTTString topicFilters[])
 | 
			
		||||
{
 | 
			
		||||
	unsigned char *ptr = buf;
 | 
			
		||||
	MQTTHeader header = {0};
 | 
			
		||||
	int rem_len = 0;
 | 
			
		||||
	int rc = -1;
 | 
			
		||||
	int i = 0;
 | 
			
		||||
 | 
			
		||||
	FUNC_ENTRY;
 | 
			
		||||
	if (MQTTPacket_len(rem_len = MQTTSerialize_unsubscribeLength(count, topicFilters)) > buflen)
 | 
			
		||||
	{
 | 
			
		||||
		rc = MQTTPACKET_BUFFER_TOO_SHORT;
 | 
			
		||||
		goto exit;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	header.byte = 0;
 | 
			
		||||
	header.bits.type = UNSUBSCRIBE;
 | 
			
		||||
	header.bits.dup = dup;
 | 
			
		||||
	header.bits.qos = 1;
 | 
			
		||||
	writeChar(&ptr, header.byte); /* write header */
 | 
			
		||||
 | 
			
		||||
	ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */;
 | 
			
		||||
 | 
			
		||||
	writeInt(&ptr, packetid);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < count; ++i)
 | 
			
		||||
		writeMQTTString(&ptr, topicFilters[i]);
 | 
			
		||||
 | 
			
		||||
	rc = ptr - buf;
 | 
			
		||||
exit:
 | 
			
		||||
	FUNC_EXIT_RC(rc);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  * Deserializes the supplied (wire) buffer into unsuback data
 | 
			
		||||
  * @param packetid returned integer - the MQTT packet identifier
 | 
			
		||||
  * @param buf the raw buffer data, of the correct length determined by the remaining length field
 | 
			
		||||
  * @param buflen the length in bytes of the data in the supplied buffer
 | 
			
		||||
  * @return error code.  1 is success, 0 is failure
 | 
			
		||||
  */
 | 
			
		||||
int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int buflen)
 | 
			
		||||
{
 | 
			
		||||
	unsigned char type = 0;
 | 
			
		||||
	unsigned char dup = 0;
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
 | 
			
		||||
	FUNC_ENTRY;
 | 
			
		||||
	rc = MQTTDeserialize_ack(&type, &dup, packetid, buf, buflen);
 | 
			
		||||
	if (type == UNSUBACK)
 | 
			
		||||
		rc = 1;
 | 
			
		||||
	FUNC_EXIT_RC(rc);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,102 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 * Copyright (c) 2014 IBM Corp.
 | 
			
		||||
 *
 | 
			
		||||
 * All rights reserved. This program and the accompanying materials
 | 
			
		||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
			
		||||
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 | 
			
		||||
 *
 | 
			
		||||
 * The Eclipse Public License is available at
 | 
			
		||||
 *    http://www.eclipse.org/legal/epl-v10.html
 | 
			
		||||
 * and the Eclipse Distribution License is available at
 | 
			
		||||
 *   http://www.eclipse.org/org/documents/edl-v10.php.
 | 
			
		||||
 *
 | 
			
		||||
 * Contributors:
 | 
			
		||||
 *    Ian Craggs - initial API and implementation and/or initial documentation
 | 
			
		||||
 *******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "MQTTPacket.h"
 | 
			
		||||
#include "StackTrace.h"
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  * Deserializes the supplied (wire) buffer into unsubscribe data
 | 
			
		||||
  * @param dup integer returned - the MQTT dup flag
 | 
			
		||||
  * @param packetid integer returned - the MQTT packet identifier
 | 
			
		||||
  * @param maxcount - the maximum number of members allowed in the topicFilters and requestedQoSs arrays
 | 
			
		||||
  * @param count - number of members in the topicFilters and requestedQoSs arrays
 | 
			
		||||
  * @param topicFilters - array of topic filter names
 | 
			
		||||
  * @param buf the raw buffer data, of the correct length determined by the remaining length field
 | 
			
		||||
  * @param buflen the length in bytes of the data in the supplied buffer
 | 
			
		||||
  * @return the length of the serialized data.  <= 0 indicates error
 | 
			
		||||
  */
 | 
			
		||||
int MQTTDeserialize_unsubscribe(unsigned char* dup, unsigned short* packetid, int maxcount, int* count, MQTTString topicFilters[],
 | 
			
		||||
		unsigned char* buf, int len)
 | 
			
		||||
{
 | 
			
		||||
	MQTTHeader header = {0};
 | 
			
		||||
	unsigned char* curdata = buf;
 | 
			
		||||
	unsigned char* enddata = NULL;
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	int mylen = 0;
 | 
			
		||||
 | 
			
		||||
	FUNC_ENTRY;
 | 
			
		||||
	header.byte = readChar(&curdata);
 | 
			
		||||
	if (header.bits.type != UNSUBSCRIBE)
 | 
			
		||||
		goto exit;
 | 
			
		||||
	*dup = header.bits.dup;
 | 
			
		||||
 | 
			
		||||
	curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
 | 
			
		||||
	enddata = curdata + mylen;
 | 
			
		||||
 | 
			
		||||
	*packetid = readInt(&curdata);
 | 
			
		||||
 | 
			
		||||
	*count = 0;
 | 
			
		||||
	while (curdata < enddata)
 | 
			
		||||
	{
 | 
			
		||||
		if (!readMQTTLenString(&topicFilters[*count], &curdata, enddata))
 | 
			
		||||
			goto exit;
 | 
			
		||||
		(*count)++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = 1;
 | 
			
		||||
exit:
 | 
			
		||||
	FUNC_EXIT_RC(rc);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  * Serializes the supplied unsuback data into the supplied buffer, ready for sending
 | 
			
		||||
  * @param buf the buffer into which the packet will be serialized
 | 
			
		||||
  * @param buflen the length in bytes of the supplied buffer
 | 
			
		||||
  * @param packetid integer - the MQTT packet identifier
 | 
			
		||||
  * @return the length of the serialized data.  <= 0 indicates error
 | 
			
		||||
  */
 | 
			
		||||
int MQTTSerialize_unsuback(unsigned char* buf, int buflen, unsigned short packetid)
 | 
			
		||||
{
 | 
			
		||||
	MQTTHeader header = {0};
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	unsigned char *ptr = buf;
 | 
			
		||||
 | 
			
		||||
	FUNC_ENTRY;
 | 
			
		||||
	if (buflen < 2)
 | 
			
		||||
	{
 | 
			
		||||
		rc = MQTTPACKET_BUFFER_TOO_SHORT;
 | 
			
		||||
		goto exit;
 | 
			
		||||
	}
 | 
			
		||||
	header.byte = 0;
 | 
			
		||||
	header.bits.type = UNSUBACK;
 | 
			
		||||
	writeChar(&ptr, header.byte); /* write header */
 | 
			
		||||
 | 
			
		||||
	ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */
 | 
			
		||||
 | 
			
		||||
	writeInt(&ptr, packetid);
 | 
			
		||||
 | 
			
		||||
	rc = ptr - buf;
 | 
			
		||||
exit:
 | 
			
		||||
	FUNC_EXIT_RC(rc);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
 SRC_FILES +=MQTTPacket.c\
 | 
			
		||||
			 MQTTConnectClient.c \
 | 
			
		||||
		     MQTTConnectServer.c \
 | 
			
		||||
			 MQTTDeserializePublish.c \
 | 
			
		||||
			 MQTTFormat.c \
 | 
			
		||||
			 MQTTSerializePublish.c \
 | 
			
		||||
			 MQTTSubscribeClient.c \
 | 
			
		||||
			 MQTTSubscribeServer.c \
 | 
			
		||||
			 MQTTUnsubscribeClient.c \
 | 
			
		||||
			 MQTTUnsubscribeServer.c \
 | 
			
		||||
			 transport.c
 | 
			
		||||
 | 
			
		||||
include $(KERNEL_ROOT)/compiler.mk
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,78 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 * Copyright (c) 2014 IBM Corp.
 | 
			
		||||
 *
 | 
			
		||||
 * All rights reserved. This program and the accompanying materials
 | 
			
		||||
 * are made available under the terms of the Eclipse Public License v1.0
 | 
			
		||||
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 | 
			
		||||
 *
 | 
			
		||||
 * The Eclipse Public License is available at
 | 
			
		||||
 *    http://www.eclipse.org/legal/epl-v10.html
 | 
			
		||||
 * and the Eclipse Distribution License is available at
 | 
			
		||||
 *   http://www.eclipse.org/org/documents/edl-v10.php.
 | 
			
		||||
 *
 | 
			
		||||
 * Contributors:
 | 
			
		||||
 *    Ian Craggs - initial API and implementation and/or initial documentation
 | 
			
		||||
 *    Ian Craggs - fix for bug #434081
 | 
			
		||||
 *******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#ifndef STACKTRACE_H_
 | 
			
		||||
#define STACKTRACE_H_
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#define NOSTACKTRACE 1
 | 
			
		||||
 | 
			
		||||
#if defined(NOSTACKTRACE)
 | 
			
		||||
#define FUNC_ENTRY
 | 
			
		||||
#define FUNC_ENTRY_NOLOG
 | 
			
		||||
#define FUNC_ENTRY_MED
 | 
			
		||||
#define FUNC_ENTRY_MAX
 | 
			
		||||
#define FUNC_EXIT
 | 
			
		||||
#define FUNC_EXIT_NOLOG
 | 
			
		||||
#define FUNC_EXIT_MED
 | 
			
		||||
#define FUNC_EXIT_MAX
 | 
			
		||||
#define FUNC_EXIT_RC(x)
 | 
			
		||||
#define FUNC_EXIT_MED_RC(x)
 | 
			
		||||
#define FUNC_EXIT_MAX_RC(x)
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
#if defined(WIN32)
 | 
			
		||||
#define inline __inline
 | 
			
		||||
#define FUNC_ENTRY StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MINIMUM)
 | 
			
		||||
#define FUNC_ENTRY_NOLOG StackTrace_entry(__FUNCTION__, __LINE__, -1)
 | 
			
		||||
#define FUNC_ENTRY_MED StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MEDIUM)
 | 
			
		||||
#define FUNC_ENTRY_MAX StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MAXIMUM)
 | 
			
		||||
#define FUNC_EXIT StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MINIMUM)
 | 
			
		||||
#define FUNC_EXIT_NOLOG StackTrace_exit(__FUNCTION__, __LINE__, -1)
 | 
			
		||||
#define FUNC_EXIT_MED StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MEDIUM)
 | 
			
		||||
#define FUNC_EXIT_MAX StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MAXIMUM)
 | 
			
		||||
#define FUNC_EXIT_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MINIMUM)
 | 
			
		||||
#define FUNC_EXIT_MED_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MEDIUM)
 | 
			
		||||
#define FUNC_EXIT_MAX_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MAXIMUM)
 | 
			
		||||
#else
 | 
			
		||||
#define FUNC_ENTRY StackTrace_entry(__func__, __LINE__, TRACE_MINIMUM)
 | 
			
		||||
#define FUNC_ENTRY_NOLOG StackTrace_entry(__func__, __LINE__, -1)
 | 
			
		||||
#define FUNC_ENTRY_MED StackTrace_entry(__func__, __LINE__, TRACE_MEDIUM)
 | 
			
		||||
#define FUNC_ENTRY_MAX StackTrace_entry(__func__, __LINE__, TRACE_MAXIMUM)
 | 
			
		||||
#define FUNC_EXIT StackTrace_exit(__func__, __LINE__, NULL, TRACE_MINIMUM)
 | 
			
		||||
#define FUNC_EXIT_NOLOG StackTrace_exit(__func__, __LINE__, NULL, -1)
 | 
			
		||||
#define FUNC_EXIT_MED StackTrace_exit(__func__, __LINE__, NULL, TRACE_MEDIUM)
 | 
			
		||||
#define FUNC_EXIT_MAX StackTrace_exit(__func__, __LINE__, NULL, TRACE_MAXIMUM)
 | 
			
		||||
#define FUNC_EXIT_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MINIMUM)
 | 
			
		||||
#define FUNC_EXIT_MED_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MEDIUM)
 | 
			
		||||
#define FUNC_EXIT_MAX_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MAXIMUM)
 | 
			
		||||
 | 
			
		||||
void StackTrace_entry(const char* name, int line, int trace);
 | 
			
		||||
void StackTrace_exit(const char* name, int line, void* return_value, int trace);
 | 
			
		||||
 | 
			
		||||
void StackTrace_printStack(FILE* dest);
 | 
			
		||||
char* StackTrace_get(unsigned long);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif /* STACKTRACE_H_ */
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,59 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2022 AIIT XUOS Lab
 | 
			
		||||
 * XiUOS is licensed under Mulan PSL v2.
 | 
			
		||||
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 | 
			
		||||
 * You may obtain a copy of Mulan PSL v2 at:
 | 
			
		||||
 *        http://license.coscl.org.cn/MulanPSL2
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 | 
			
		||||
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 | 
			
		||||
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 | 
			
		||||
 * See the Mulan PSL v2 for more details.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @file transport.c
 | 
			
		||||
 * @brief mqtt transport function
 | 
			
		||||
 * @version 3.0
 | 
			
		||||
 * @author AIIT XUOS Lab
 | 
			
		||||
 * @date 2023.8.10
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "transport.h"
 | 
			
		||||
#include "lwip/opt.h"
 | 
			
		||||
#include "lwip/arch.h"
 | 
			
		||||
#include "lwip/api.h"
 | 
			
		||||
#include "lwip/inet.h"
 | 
			
		||||
#include "lwip/sockets.h"
 | 
			
		||||
#include "string.h"
 | 
			
		||||
 | 
			
		||||
static int mysock;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int32_t transport_sendPacketBuffer( uint8_t* buf, int32_t buflen)
 | 
			
		||||
{
 | 
			
		||||
	int32_t rc;
 | 
			
		||||
	rc = write(mysock, buf, buflen);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int transport_getdata(unsigned char* buf, int count)
 | 
			
		||||
{
 | 
			
		||||
	int32_t rc;
 | 
			
		||||
	
 | 
			
		||||
  rc = recv(mysock, buf, count, 0);
 | 
			
		||||
  lw_print("get data : %lx\n",rc);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int32_t transport_close(void)
 | 
			
		||||
{
 | 
			
		||||
  
 | 
			
		||||
	int rc;
 | 
			
		||||
//	rc = close(mysock);
 | 
			
		||||
  rc = shutdown(mysock, SHUT_WR);
 | 
			
		||||
	rc = recv(mysock, NULL, (size_t)0, 0);
 | 
			
		||||
	rc = close(mysock);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,40 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2022 AIIT XUOS Lab
 | 
			
		||||
 * XiUOS is licensed under Mulan PSL v2.
 | 
			
		||||
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 | 
			
		||||
 * You may obtain a copy of Mulan PSL v2 at:
 | 
			
		||||
 *        http://license.coscl.org.cn/MulanPSL2
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 | 
			
		||||
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 | 
			
		||||
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 | 
			
		||||
 * See the Mulan PSL v2 for more details.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @file transport.h
 | 
			
		||||
 * @brief mqtt transport function
 | 
			
		||||
 * @version 3.0
 | 
			
		||||
 * @author AIIT XUOS Lab
 | 
			
		||||
 * @date 2023.8.10
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __TRANSPORT_H
 | 
			
		||||
#define __TRANSPORT_H
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int32_t transport_sendPacketBuffer( uint8_t* buf, int32_t buflen);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int transport_getdata(unsigned char* buf, int count);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int32_t transport_open(int8_t* servip, int32_t port);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int32_t transport_close(void);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,748 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2022 AIIT XUOS Lab
 | 
			
		||||
 * XiUOS is licensed under Mulan PSL v2.
 | 
			
		||||
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 | 
			
		||||
 * You may obtain a copy of Mulan PSL v2 at:
 | 
			
		||||
 *        http://license.coscl.org.cn/MulanPSL2
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 | 
			
		||||
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 | 
			
		||||
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 | 
			
		||||
 * See the Mulan PSL v2 for more details.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @file test_mqttclient.c
 | 
			
		||||
 * @brief mqtt subscribe fuction test
 | 
			
		||||
 * @version 3.0
 | 
			
		||||
 * @author AIIT XUOS Lab
 | 
			
		||||
 * @date 2023.8.10
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <transform.h>
 | 
			
		||||
#include "test_mqttclient.h"
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#ifdef ADD_XIZI_FEATURES
 | 
			
		||||
#include <sys_arch.h>
 | 
			
		||||
#include <lwip/sockets.h>
 | 
			
		||||
#include "lwip/sys.h"
 | 
			
		||||
#include "lwip/api.h"
 | 
			
		||||
 | 
			
		||||
#include "mqtt/MQTTPacket.h"
 | 
			
		||||
#include "mqtt/MQTTSubscribe.h"
 | 
			
		||||
#include "mqtt/transport.h"
 | 
			
		||||
#include <cJSON_Process.h>
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef ADD_NUTTX_FEATURES
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#include "stdio.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define MQTT_DEMO_BUF_SIZE                    65535
 | 
			
		||||
#define MQTT_DEMO_SEND_TIMES                  20
 | 
			
		||||
#define LWIP_MQTT_DEMO_TASK_STACK_SIZE        4096
 | 
			
		||||
#define LWIP_MQTT_DEMO_TASK_PRIO              20
 | 
			
		||||
 | 
			
		||||
static char mqtt_demo_ipaddr[] = {192, 168, 130, 77};
 | 
			
		||||
static char mqtt_demo_netmask[] = {255, 255, 254, 0};
 | 
			
		||||
static char mqtt_demo_gwaddr[] = {192, 168, 130, 1};
 | 
			
		||||
 | 
			
		||||
static pthread_t mqtt_client_task;
 | 
			
		||||
static pthread_t mqtt_server_task;
 | 
			
		||||
 | 
			
		||||
static uint16_t mqtt_socket_port = 1883;
 | 
			
		||||
static char mqtt_ip_str[128] = {192,168,100,1};
 | 
			
		||||
 | 
			
		||||
void MqttSocketConfigParam(char *ip_str)
 | 
			
		||||
{
 | 
			
		||||
    int ip1, ip2, ip3, ip4, port = 0;
 | 
			
		||||
 | 
			
		||||
    if(ip_str == NULL)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if(sscanf(ip_str, "%d.%d.%d.%d:%d", &ip1, &ip2, &ip3, &ip4, &port)) {
 | 
			
		||||
        printf("config ip %s port %d\n", ip_str, port);
 | 
			
		||||
        strcpy(mqtt_ip_str, ip_str);
 | 
			
		||||
        if(port)
 | 
			
		||||
            mqtt_socket_port = port;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(sscanf(ip_str, "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4)) {
 | 
			
		||||
        printf("config ip %s\n", ip_str);
 | 
			
		||||
        strcpy(mqtt_ip_str, ip_str);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MQTT_USER_MSG  mqtt_user_msg;
 | 
			
		||||
 | 
			
		||||
uint8_t MQTT_Connect(void)
 | 
			
		||||
{
 | 
			
		||||
    MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
 | 
			
		||||
    uint8_t buf[200];
 | 
			
		||||
    int buflen = sizeof(buf);
 | 
			
		||||
    int len = 0;
 | 
			
		||||
    data.clientID.cstring = CLIENT_ID;                   //随机
 | 
			
		||||
    data.keepAliveInterval = KEEPLIVE_TIME;         //保持活跃
 | 
			
		||||
    data.username.cstring = USER_NAME;              //用户名
 | 
			
		||||
    data.password.cstring = PASSWORD;               //密钥
 | 
			
		||||
    data.MQTTVersion = MQTT_VERSION;                //3表示3.1版本,4表示3.11版本
 | 
			
		||||
    data.cleansession = 1;
 | 
			
		||||
    //组装消息
 | 
			
		||||
    len = MQTTSerialize_connect((unsigned char *)buf, buflen, &data);
 | 
			
		||||
    //发送消息
 | 
			
		||||
    transport_sendPacketBuffer(buf, len);
 | 
			
		||||
    
 | 
			
		||||
    /* 等待连接响应 */
 | 
			
		||||
    if (MQTTPacket_read(buf, buflen, transport_getdata) == CONNACK)
 | 
			
		||||
    {
 | 
			
		||||
        unsigned char sessionPresent, connack_rc;
 | 
			
		||||
        if (MQTTDeserialize_connack(&sessionPresent, &connack_rc, buf, buflen) != 1 || connack_rc != 0)
 | 
			
		||||
        {
 | 
			
		||||
          lw_print("无法连接,错误代码是: %d!\n", connack_rc);
 | 
			
		||||
            return Connect_NOK;
 | 
			
		||||
        }
 | 
			
		||||
        else 
 | 
			
		||||
        {
 | 
			
		||||
            lw_print("用户名与密钥验证成功,MQTT连接成功!\n");
 | 
			
		||||
            return Connect_OK;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        lw_print("MQTT连接无响应!\n");
 | 
			
		||||
        return Connect_NOTACK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int32_t MQTT_PingReq(int32_t sock)
 | 
			
		||||
{
 | 
			
		||||
	  int32_t len;
 | 
			
		||||
		uint8_t buf[200];
 | 
			
		||||
		int32_t buflen = sizeof(buf);	 
 | 
			
		||||
		fd_set readfd;
 | 
			
		||||
	  struct timeval tv;
 | 
			
		||||
	  tv.tv_sec = 5;
 | 
			
		||||
	  tv.tv_usec = 0;
 | 
			
		||||
	
 | 
			
		||||
	  FD_ZERO(&readfd);
 | 
			
		||||
	  FD_SET(sock,&readfd);			
 | 
			
		||||
	
 | 
			
		||||
		len = MQTTSerialize_pingreq(buf, buflen);
 | 
			
		||||
		transport_sendPacketBuffer(buf, len);
 | 
			
		||||
	
 | 
			
		||||
		//等待可读事件
 | 
			
		||||
		if(select(sock+1,&readfd,NULL,NULL,&tv) == 0)
 | 
			
		||||
			return -1;
 | 
			
		||||
		
 | 
			
		||||
	  //有可读事件
 | 
			
		||||
		if(FD_ISSET(sock,&readfd) == 0)
 | 
			
		||||
			return -2;
 | 
			
		||||
		
 | 
			
		||||
		if(MQTTPacket_read(buf, buflen, transport_getdata) != PINGRESP)
 | 
			
		||||
			return -3;
 | 
			
		||||
		
 | 
			
		||||
		return 0;
 | 
			
		||||
	
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/************************************************************************
 | 
			
		||||
** 函数名称: MQTTSubscribe								
 | 
			
		||||
** 函数功能: 订阅消息
 | 
			
		||||
** 入口参数: int32_t sock:套接字
 | 
			
		||||
**           int8_t *topic:主题
 | 
			
		||||
**           enum QoS pos:消息质量
 | 
			
		||||
** 出口参数: >=0:发送成功 <0:发送失败
 | 
			
		||||
** 备    注: 
 | 
			
		||||
************************************************************************/
 | 
			
		||||
int32_t MQTTSubscribe(int32_t sock,char *topic,enum QoS pos)
 | 
			
		||||
{
 | 
			
		||||
	  static uint32_t PacketID = 0;
 | 
			
		||||
	  uint16_t packetidbk = 0;
 | 
			
		||||
	  int32_t conutbk = 0;
 | 
			
		||||
		uint8_t buf[100];
 | 
			
		||||
		int32_t buflen = sizeof(buf);
 | 
			
		||||
	  MQTTString topicString = MQTTString_initializer;  
 | 
			
		||||
		int32_t len;
 | 
			
		||||
	  int32_t req_qos,qosbk;
 | 
			
		||||
 | 
			
		||||
		fd_set readfd;
 | 
			
		||||
	  struct timeval tv;
 | 
			
		||||
	  tv.tv_sec = 2;
 | 
			
		||||
	  tv.tv_usec = 0;
 | 
			
		||||
	
 | 
			
		||||
	  FD_ZERO(&readfd);
 | 
			
		||||
	  FD_SET(sock,&readfd);		
 | 
			
		||||
	
 | 
			
		||||
	  //复制主题
 | 
			
		||||
    topicString.cstring = (char *)topic;
 | 
			
		||||
		//订阅质量
 | 
			
		||||
	  req_qos = pos;
 | 
			
		||||
	
 | 
			
		||||
	  //串行化订阅消息
 | 
			
		||||
    len = MQTTSerialize_subscribe(buf, buflen, 0, PacketID++, 1, &topicString, &req_qos);
 | 
			
		||||
		//发送TCP数据
 | 
			
		||||
	  if(transport_sendPacketBuffer(buf, len) < 0)
 | 
			
		||||
				return -1;
 | 
			
		||||
	  
 | 
			
		||||
    //等待可读事件--等待超时
 | 
			
		||||
		if(select(sock+1,&readfd,NULL,NULL,&tv) == 0)
 | 
			
		||||
				return -2;
 | 
			
		||||
		//有可读事件--没有可读事件
 | 
			
		||||
		if(FD_ISSET(sock,&readfd) == 0)
 | 
			
		||||
				return -3;
 | 
			
		||||
 | 
			
		||||
		//等待订阅返回--未收到订阅返回
 | 
			
		||||
		if(MQTTPacket_read(buf, buflen, transport_getdata) != SUBACK)
 | 
			
		||||
				return -4;	
 | 
			
		||||
		
 | 
			
		||||
		//拆订阅回应包
 | 
			
		||||
		if(MQTTDeserialize_suback(&packetidbk,1, &conutbk, &qosbk, buf, buflen) != 1)
 | 
			
		||||
				return -5;
 | 
			
		||||
		
 | 
			
		||||
		//检测返回数据的正确性
 | 
			
		||||
		if((qosbk == 0x80)||(packetidbk != (PacketID-1)))
 | 
			
		||||
				return -6;
 | 
			
		||||
		
 | 
			
		||||
    //订阅成功
 | 
			
		||||
		return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int32_t ReadPacketTimeout(int32_t sock,uint8_t *buf,int32_t buflen,uint32_t timeout)
 | 
			
		||||
{
 | 
			
		||||
		fd_set readfd;
 | 
			
		||||
	  struct timeval tv;
 | 
			
		||||
	  if(timeout != 0)
 | 
			
		||||
		{
 | 
			
		||||
				tv.tv_sec = timeout;
 | 
			
		||||
				tv.tv_usec = 0;
 | 
			
		||||
				FD_ZERO(&readfd);
 | 
			
		||||
				FD_SET(sock,&readfd); 
 | 
			
		||||
 | 
			
		||||
				
 | 
			
		||||
				if(select(sock+1,&readfd,NULL,NULL,&tv) == 0)
 | 
			
		||||
						return -1;
 | 
			
		||||
				
 | 
			
		||||
				if(FD_ISSET(sock,&readfd) == 0)
 | 
			
		||||
						return -1;
 | 
			
		||||
	  }
 | 
			
		||||
		
 | 
			
		||||
		return MQTTPacket_read(buf, buflen, transport_getdata);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void deliverMessage(MQTTString  *TopicName,MQTTMessage *msg,MQTT_USER_MSG *mqtt_user_msg)
 | 
			
		||||
{
 | 
			
		||||
		//消息质量
 | 
			
		||||
		mqtt_user_msg->msgqos = msg->qos;
 | 
			
		||||
		//保存消息
 | 
			
		||||
		memcpy(mqtt_user_msg->msg,msg->payload,msg->payloadlen);
 | 
			
		||||
		mqtt_user_msg->msg[msg->payloadlen] = 0;
 | 
			
		||||
		//保存消息长度
 | 
			
		||||
		mqtt_user_msg->msglenth = msg->payloadlen;
 | 
			
		||||
		//消息主题
 | 
			
		||||
		memcpy((char *)mqtt_user_msg->topic,TopicName->lenstring.data,TopicName->lenstring.len);
 | 
			
		||||
		mqtt_user_msg->topic[TopicName->lenstring.len] = 0;
 | 
			
		||||
		//消息ID
 | 
			
		||||
		mqtt_user_msg->packetid = msg->id;
 | 
			
		||||
		//标明消息合法
 | 
			
		||||
		mqtt_user_msg->valid = 1;		
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void UserMsgCtl(MQTT_USER_MSG  *msg)
 | 
			
		||||
{
 | 
			
		||||
		//这里处理数据只是打印,用户可以在这里添加自己的处理方式
 | 
			
		||||
	  lw_print("****收到订阅的消息******\n");
 | 
			
		||||
		//<2F><><EFBFBD>غ<EFBFBD><D8BA><EFBFBD><EFBFBD><EFBFBD>Ϣ
 | 
			
		||||
	  switch(msg->msgqos)
 | 
			
		||||
		{
 | 
			
		||||
			case 0:
 | 
			
		||||
				    lw_print("MQTT>>消息质量QoS0\n");
 | 
			
		||||
				    break;
 | 
			
		||||
			case 1:
 | 
			
		||||
				    lw_print("MQTT>>消息质量QoS1\n");
 | 
			
		||||
				    break;
 | 
			
		||||
			case 2:
 | 
			
		||||
				    lw_print("MQTT>>消息质量QoS2\n");
 | 
			
		||||
				    break;
 | 
			
		||||
			default:
 | 
			
		||||
				    lw_print("MQTT>>错误的消息质量\n");
 | 
			
		||||
				    break;
 | 
			
		||||
		}
 | 
			
		||||
		lw_print("MQTT>>消息主题:%s\n",msg->topic);	
 | 
			
		||||
		lw_print("MQTT>>消息内容:%s\n",msg->msg);	
 | 
			
		||||
		lw_print("MQTT>>消息长度:%d\n",msg->msglenth);	 
 | 
			
		||||
    Proscess(msg->msg);
 | 
			
		||||
	  //处理完后销毁数据
 | 
			
		||||
	  msg->valid  = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void mqtt_pktype_ctl(uint8_t packtype,uint8_t *buf,uint32_t buflen)
 | 
			
		||||
{
 | 
			
		||||
	  MQTTMessage msg;
 | 
			
		||||
		int32_t rc;
 | 
			
		||||
	  MQTTString receivedTopic;
 | 
			
		||||
	  uint32_t len;
 | 
			
		||||
    lw_print("packtype:%d\n",packtype);
 | 
			
		||||
		switch(packtype)
 | 
			
		||||
		{
 | 
			
		||||
			case PUBLISH:
 | 
			
		||||
        
 | 
			
		||||
        if(MQTTDeserialize_publish(&msg.dup,(int*)&msg.qos, &msg.retained, &msg.id, &receivedTopic,
 | 
			
		||||
          (unsigned char **)&msg.payload, &msg.payloadlen, buf, buflen) != 1)
 | 
			
		||||
            return;	
 | 
			
		||||
        
 | 
			
		||||
        deliverMessage(&receivedTopic,&msg,&mqtt_user_msg);
 | 
			
		||||
        
 | 
			
		||||
      
 | 
			
		||||
        if(msg.qos == QOS0)
 | 
			
		||||
        {
 | 
			
		||||
            //QOS0-不需要ACK
 | 
			
		||||
           //直接处理数据
 | 
			
		||||
           UserMsgCtl(&mqtt_user_msg);
 | 
			
		||||
           return;
 | 
			
		||||
        }
 | 
			
		||||
        //发送PUBACK消息
 | 
			
		||||
        if(msg.qos == QOS1)
 | 
			
		||||
        {
 | 
			
		||||
            len =MQTTSerialize_puback(buf,buflen,mqtt_user_msg.packetid);
 | 
			
		||||
            if(len == 0)
 | 
			
		||||
              return;
 | 
			
		||||
             //发送返回
 | 
			
		||||
            if(transport_sendPacketBuffer(buf,len)<0)
 | 
			
		||||
               return;	
 | 
			
		||||
             //返回后处理消息
 | 
			
		||||
            UserMsgCtl(&mqtt_user_msg); 
 | 
			
		||||
            return;												
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //对于质量2,只需要发送PUBREC就可以了
 | 
			
		||||
        if(msg.qos == QOS2)
 | 
			
		||||
        {
 | 
			
		||||
           len = MQTTSerialize_ack(buf, buflen, PUBREC, 0, mqtt_user_msg.packetid);			                
 | 
			
		||||
           if(len == 0)
 | 
			
		||||
             return;
 | 
			
		||||
            //发送返回
 | 
			
		||||
           transport_sendPacketBuffer(buf,len);	
 | 
			
		||||
        }		
 | 
			
		||||
        break;
 | 
			
		||||
			case  PUBREL:				           
 | 
			
		||||
          //解析包数据,必须包ID相同才可以
 | 
			
		||||
        rc = MQTTDeserialize_ack(&msg.type,&msg.dup, &msg.id, buf,buflen);
 | 
			
		||||
        if((rc != 1)||(msg.type != PUBREL)||(msg.id != mqtt_user_msg.packetid))
 | 
			
		||||
          return ;
 | 
			
		||||
       //收到PUBREL,需要处理并抛弃数据
 | 
			
		||||
        if(mqtt_user_msg.valid == 1)
 | 
			
		||||
        {
 | 
			
		||||
            //返回后处理消息
 | 
			
		||||
           UserMsgCtl(&mqtt_user_msg);
 | 
			
		||||
        }      
 | 
			
		||||
        //串行化PUBCMP消息
 | 
			
		||||
        len = MQTTSerialize_pubcomp(buf,buflen,msg.id);	                   	
 | 
			
		||||
        if(len == 0)
 | 
			
		||||
          return;									
 | 
			
		||||
       //发送返回--PUBCOMP
 | 
			
		||||
        transport_sendPacketBuffer(buf,len);										
 | 
			
		||||
        break;
 | 
			
		||||
			case   PUBACK://等级1客户端推送数据后,服务器返回
 | 
			
		||||
				break;
 | 
			
		||||
			case   PUBREC://等级2客户端推送数据后,服务器返回
 | 
			
		||||
				break;
 | 
			
		||||
			case   PUBCOMP://等级2客户端推送PUBREL后,服务器返回
 | 
			
		||||
        break;
 | 
			
		||||
			default:
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void *MqttSocketRecvTask(void *arg)
 | 
			
		||||
{
 | 
			
		||||
MQTT_START: 
 | 
			
		||||
    lw_print("Recv begin**********\n");
 | 
			
		||||
    int fd = -1, clientfd;
 | 
			
		||||
    int recv_len;
 | 
			
		||||
    int ret;
 | 
			
		||||
    char *recv_buf;
 | 
			
		||||
    struct sockaddr_in mqtt_addr;
 | 
			
		||||
    socklen_t addr_len;
 | 
			
		||||
 | 
			
		||||
     fd = socket(AF_INET, SOCK_STREAM, 0);
 | 
			
		||||
    if (fd < 0) {
 | 
			
		||||
        lw_print("Socket error\n");
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct sockaddr_in mqtt_sock;
 | 
			
		||||
    mqtt_sock.sin_family = AF_INET;
 | 
			
		||||
    mqtt_sock.sin_port = htons(mqtt_socket_port);
 | 
			
		||||
    mqtt_sock.sin_addr.s_addr = inet_addr(mqtt_ip_str);
 | 
			
		||||
 | 
			
		||||
    memset(&(mqtt_sock.sin_zero), 0, sizeof(mqtt_sock.sin_zero));
 | 
			
		||||
 | 
			
		||||
    ret = connect(fd, (struct sockaddr *)&mqtt_sock, sizeof(struct sockaddr));
 | 
			
		||||
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        lw_print("Unable to connect %s:%d = %d\n", mqtt_ip_str, mqtt_socket_port, ret);
 | 
			
		||||
        close(fd);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    lw_print("MQTT connect %s:%d success, begin to verify username and password.\n", mqtt_ip_str, mqtt_socket_port);
 | 
			
		||||
    
 | 
			
		||||
    if(MQTT_Connect() != Connect_OK)
 | 
			
		||||
    {
 | 
			
		||||
        lw_print("MQTT verify failed.\n");
 | 
			
		||||
        shutdown(fd, SHUT_WR);
 | 
			
		||||
        recv(fd, NULL, (size_t)0, 0);
 | 
			
		||||
        close(fd);
 | 
			
		||||
        PrivTaskDelay(1000);
 | 
			
		||||
        goto MQTT_START;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    lw_print("MQTT subscribe begin.\n");
 | 
			
		||||
    if(MQTTSubscribe(fd,(char *)TOPIC,QOS1) < 0)
 | 
			
		||||
    {
 | 
			
		||||
        lw_print("MQTT subscribe failed.\n");
 | 
			
		||||
        shutdown(fd, SHUT_WR);
 | 
			
		||||
        recv(fd, NULL, (size_t)0, 0);
 | 
			
		||||
        close(fd);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    lw_print("subscribe success.\n");
 | 
			
		||||
 | 
			
		||||
    fd_set readfd;
 | 
			
		||||
    uint8_t no_mqtt_msg_exchange = 1;
 | 
			
		||||
    uint8_t buf[MSG_MAX_LEN];
 | 
			
		||||
	int32_t buflen = sizeof(buf);
 | 
			
		||||
    int32_t type;
 | 
			
		||||
    struct timeval tv;    
 | 
			
		||||
	tv.tv_sec = 0;
 | 
			
		||||
	tv.tv_usec = 10;
 | 
			
		||||
 | 
			
		||||
  int32_t curtick=0;
 | 
			
		||||
 | 
			
		||||
    while(1)
 | 
			
		||||
    {
 | 
			
		||||
      // lw_print("waiting********\n");
 | 
			
		||||
      curtick +=1;
 | 
			
		||||
      no_mqtt_msg_exchange = 1;
 | 
			
		||||
			
 | 
			
		||||
		  FD_ZERO(&readfd);
 | 
			
		||||
		  FD_SET(fd,&readfd);
 | 
			
		||||
      select(fd+1,&readfd,NULL,NULL,&tv);
 | 
			
		||||
 | 
			
		||||
      if(FD_ISSET(fd,&readfd) != 0)
 | 
			
		||||
      {
 | 
			
		||||
          
 | 
			
		||||
          type = ReadPacketTimeout(fd,buf,buflen,0);
 | 
			
		||||
          if(type != -1)
 | 
			
		||||
          {
 | 
			
		||||
            lw_print("ctl***********\n");
 | 
			
		||||
              mqtt_pktype_ctl(type,buf,buflen);
 | 
			
		||||
  
 | 
			
		||||
              no_mqtt_msg_exchange = 0;
 | 
			
		||||
								
 | 
			
		||||
					}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
      if( curtick >(2*10000))
 | 
			
		||||
        {
 | 
			
		||||
            curtick =0;
 | 
			
		||||
            //判断是否有数据交换
 | 
			
		||||
            if(no_mqtt_msg_exchange == 0)
 | 
			
		||||
            {
 | 
			
		||||
               //如果有数据交换,这次就不需要发送PING消息
 | 
			
		||||
               continue;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            if(MQTT_PingReq(fd) < 0)
 | 
			
		||||
            {
 | 
			
		||||
               //重连服务器
 | 
			
		||||
               lw_print("发送保持活性ping失败....\n");
 | 
			
		||||
               goto CLOSE;	 
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            lw_print("发送保持活性ping作为心跳成功....\n");
 | 
			
		||||
 | 
			
		||||
            no_mqtt_msg_exchange = 0;
 | 
			
		||||
        } 
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
CLOSE:
 | 
			
		||||
	  lw_print("MQTT subscribe failed.\n");
 | 
			
		||||
    shutdown(fd, SHUT_WR);
 | 
			
		||||
    recv(fd, NULL, (size_t)0, 0);
 | 
			
		||||
    close(fd);
 | 
			
		||||
    return NULL;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void MqttSocketRecvTest(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
    if(argc >= 2) {
 | 
			
		||||
        lw_print("lw: [%s] target ip %s\n", __func__, argv[1]);
 | 
			
		||||
        MqttSocketConfigParam(argv[1]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // ip4_addr_t dns_ip;
 | 
			
		||||
    // netconn_gethostbyname(HOST_NAME, &dns_ip);
 | 
			
		||||
    // char* host_ip = ip_ntoa(&dns_ip);
 | 
			
		||||
    // lw_print("host name : %s , host_ip : %s\n",HOST_NAME,host_ip);
 | 
			
		||||
    // MqttSocketConfigParam(host_ip);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef ADD_XIZI_FEATURES
 | 
			
		||||
    lwip_config_tcp(0, mqtt_demo_ipaddr, mqtt_demo_netmask, mqtt_demo_gwaddr);
 | 
			
		||||
 | 
			
		||||
    pthread_attr_t attr;
 | 
			
		||||
    attr.schedparam.sched_priority = LWIP_MQTT_DEMO_TASK_PRIO;
 | 
			
		||||
    attr.stacksize = LWIP_MQTT_DEMO_TASK_STACK_SIZE;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef ADD_NUTTX_FEATURES
 | 
			
		||||
    pthread_attr_t attr = PTHREAD_ATTR_INITIALIZER;
 | 
			
		||||
    attr.priority = LWIP_mqtt_DEMO_TASK_PRIO;
 | 
			
		||||
    attr.stacksize = LWIP_mqtt_DEMO_TASK_STACK_SIZE;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    PrivTaskCreate(&mqtt_server_task, &attr, &MqttSocketRecvTask, NULL);
 | 
			
		||||
    PrivTaskStartup(&mqtt_server_task);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PRIV_SHELL_CMD_FUNCTION(MqttSocketRecvTest, a tcp send sample, PRIV_SHELL_CMD_MAIN_ATTR);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
  uint8_t  humi_high8bit;                //ԭʼ<D4AD><CABC><EFBFBD>ݣ<EFBFBD>ʪ<EFBFBD>ȸ<EFBFBD>8λ
 | 
			
		||||
  uint8_t  humi_low8bit;                 //ԭʼ<D4AD><CABC><EFBFBD>ݣ<EFBFBD>ʪ<EFBFBD>ȵ<EFBFBD>8λ
 | 
			
		||||
  uint8_t  temp_high8bit;                 //ԭʼ<D4AD><CABC><EFBFBD>ݣ<EFBFBD><DDA3>¶ȸ<C2B6>8λ
 | 
			
		||||
  uint8_t  temp_low8bit;                 //ԭʼ<D4AD><CABC><EFBFBD>ݣ<EFBFBD><DDA3>¶ȸ<C2B6>8λ
 | 
			
		||||
  uint8_t  check_sum;                     //У<><D0A3><EFBFBD>
 | 
			
		||||
  double    humidity;        //ʵ<><CAB5>ʪ<EFBFBD><CAAA>
 | 
			
		||||
  double    temperature;     //ʵ<><CAB5><EFBFBD>¶<EFBFBD>  
 | 
			
		||||
} DHT11_Data_TypeDef;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
uint16_t GetNextPackID(void)
 | 
			
		||||
{
 | 
			
		||||
	 static uint16_t pubpacketid = 0;
 | 
			
		||||
	 return pubpacketid++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int32_t WaitForPacket(int32_t sock,uint8_t packettype,uint8_t times)
 | 
			
		||||
{
 | 
			
		||||
	  int32_t type;
 | 
			
		||||
		uint8_t buf[MSG_MAX_LEN];
 | 
			
		||||
	  uint8_t n = 0;
 | 
			
		||||
		int32_t buflen = sizeof(buf);
 | 
			
		||||
		do
 | 
			
		||||
		{
 | 
			
		||||
				//读取数据包
 | 
			
		||||
				type = ReadPacketTimeout(sock,buf,buflen,2);
 | 
			
		||||
			  if(type != -1)
 | 
			
		||||
					mqtt_pktype_ctl(type,buf,buflen);
 | 
			
		||||
				n++;
 | 
			
		||||
		}while((type != packettype)&&(n < times));
 | 
			
		||||
		//收到期望的包
 | 
			
		||||
		if(type == packettype)
 | 
			
		||||
			 return 0;
 | 
			
		||||
		else 
 | 
			
		||||
			 return -1;		
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int32_t MQTTMsgPublish(int32_t sock, char *topic, int8_t qos, uint8_t* msg)
 | 
			
		||||
{
 | 
			
		||||
    int8_t retained = 0;      //保留标志位
 | 
			
		||||
    uint32_t msg_len;         //数据长度
 | 
			
		||||
		uint8_t buf[MSG_MAX_LEN];
 | 
			
		||||
		int32_t buflen = sizeof(buf),len;
 | 
			
		||||
		MQTTString topicString = MQTTString_initializer;
 | 
			
		||||
	  uint16_t packid = 0,packetidbk;
 | 
			
		||||
	
 | 
			
		||||
		//填充主题
 | 
			
		||||
	  topicString.cstring = (char *)topic;
 | 
			
		||||
 | 
			
		||||
	  //填充数据包ID
 | 
			
		||||
	  if((qos == QOS1)||(qos == QOS2))
 | 
			
		||||
		{ 
 | 
			
		||||
			packid = GetNextPackID();
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			  qos = QOS0;
 | 
			
		||||
			  retained = 0;
 | 
			
		||||
			  packid = 0;
 | 
			
		||||
		}
 | 
			
		||||
     
 | 
			
		||||
    msg_len = strlen((char *)msg);
 | 
			
		||||
    
 | 
			
		||||
		//推送消息
 | 
			
		||||
		len = MQTTSerialize_publish(buf, buflen, 0, qos, retained, packid, topicString, (unsigned char*)msg, msg_len);
 | 
			
		||||
		if(len <= 0)
 | 
			
		||||
				return -1;
 | 
			
		||||
		if(transport_sendPacketBuffer(buf, len) < 0)	
 | 
			
		||||
				return -2;	
 | 
			
		||||
		
 | 
			
		||||
		//质量等级0,不需要返回
 | 
			
		||||
		if(qos == QOS0)
 | 
			
		||||
		{
 | 
			
		||||
				return 0;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		//等级1
 | 
			
		||||
		if(qos == QOS1)
 | 
			
		||||
		{
 | 
			
		||||
				//等待PUBACK
 | 
			
		||||
			  if(WaitForPacket(sock,PUBACK,5) < 0)
 | 
			
		||||
					 return -3;
 | 
			
		||||
				return 1;
 | 
			
		||||
			  
 | 
			
		||||
		}
 | 
			
		||||
		//等级2
 | 
			
		||||
		if(qos == QOS2)	
 | 
			
		||||
		{
 | 
			
		||||
			  //等待PUBREC
 | 
			
		||||
			  if(WaitForPacket(sock,PUBREC,5) < 0)
 | 
			
		||||
					 return -3;
 | 
			
		||||
			  //发送PUBREL
 | 
			
		||||
        len = MQTTSerialize_pubrel(buf, buflen,0, packetidbk);
 | 
			
		||||
				if(len == 0)
 | 
			
		||||
					return -4;
 | 
			
		||||
				if(transport_sendPacketBuffer(buf, len) < 0)	
 | 
			
		||||
					return -6;			
 | 
			
		||||
			  //等待PUBCOMP
 | 
			
		||||
			  if(WaitForPacket(sock,PUBREC,5) < 0)
 | 
			
		||||
					 return -7;
 | 
			
		||||
				return 2;
 | 
			
		||||
		}
 | 
			
		||||
		//等级错误
 | 
			
		||||
		return -8;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void *MqttSocketSendTask(void *arg)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
  int fd = -1, clientfd;
 | 
			
		||||
    int recv_len;
 | 
			
		||||
    int ret;
 | 
			
		||||
    char *recv_buf;
 | 
			
		||||
    struct sockaddr_in mqtt_addr;
 | 
			
		||||
    socklen_t addr_len;
 | 
			
		||||
 | 
			
		||||
     fd = socket(AF_INET, SOCK_STREAM, 0);
 | 
			
		||||
    if (fd < 0) {
 | 
			
		||||
        lw_print("Socket error\n");
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct sockaddr_in mqtt_sock;
 | 
			
		||||
    mqtt_sock.sin_family = AF_INET;
 | 
			
		||||
    mqtt_sock.sin_port = htons(mqtt_socket_port);
 | 
			
		||||
    mqtt_sock.sin_addr.s_addr = inet_addr(mqtt_ip_str);
 | 
			
		||||
 | 
			
		||||
    memset(&(mqtt_sock.sin_zero), 0, sizeof(mqtt_sock.sin_zero));
 | 
			
		||||
 | 
			
		||||
    ret = connect(fd, (struct sockaddr *)&mqtt_sock, sizeof(struct sockaddr));
 | 
			
		||||
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        lw_print("Unable to connect %s:%d = %d\n", mqtt_ip_str, mqtt_socket_port, ret);
 | 
			
		||||
        close(fd);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    lw_print("MQTT connect %s:%d success, begin to verify hostname and password.\n", mqtt_ip_str, mqtt_socket_port);
 | 
			
		||||
    
 | 
			
		||||
    if(MQTT_Connect() != Connect_OK)
 | 
			
		||||
    {
 | 
			
		||||
        lw_print("MQTT verify failed.\n");
 | 
			
		||||
        shutdown(fd, SHUT_WR);
 | 
			
		||||
        recv(fd, NULL, (size_t)0, 0);
 | 
			
		||||
        close(fd);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    lw_print("MQTT subscribe begin.\n");
 | 
			
		||||
    if(MQTTSubscribe(fd,(char *)TOPIC,QOS1) < 0)
 | 
			
		||||
    {
 | 
			
		||||
        lw_print("MQTT subscribe failed.\n");
 | 
			
		||||
        shutdown(fd, SHUT_WR);
 | 
			
		||||
        recv(fd, NULL, (size_t)0, 0);
 | 
			
		||||
        close(fd);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    lw_print("subscribe success.\n");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    uint8_t no_mqtt_msg_exchange = 1;
 | 
			
		||||
    uint32_t curtick=0;
 | 
			
		||||
    uint8_t res;
 | 
			
		||||
 | 
			
		||||
    cJSON* cJSON_Data = NULL;
 | 
			
		||||
    cJSON_Data = cJSON_Data_Init();
 | 
			
		||||
    DHT11_Data_TypeDef* recv_data;
 | 
			
		||||
 | 
			
		||||
    double a,b;
 | 
			
		||||
    while(1)
 | 
			
		||||
    {
 | 
			
		||||
      curtick+=1;
 | 
			
		||||
      char* p ="Hello,here is hc";
 | 
			
		||||
      ret = MQTTMsgPublish(fd,(char*)TOPIC,QOS0,(uint8_t*)p);
 | 
			
		||||
      if(ret >= 0)
 | 
			
		||||
      {
 | 
			
		||||
          no_mqtt_msg_exchange = 0;
 | 
			
		||||
          PrivTaskDelay(1000);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void MqttSocketSendTest(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
    if(argc >= 2) {
 | 
			
		||||
        lw_print("lw: [%s] target ip %s\n", __func__, argv[1]);
 | 
			
		||||
        MqttSocketConfigParam(argv[1]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // ip4_addr_t dns_ip;
 | 
			
		||||
    // netconn_gethostbyname(HOST_NAME, &dns_ip);
 | 
			
		||||
    // char* host_ip = ip_ntoa(&dns_ip);
 | 
			
		||||
    // lw_print("host name : %s , host_ip : %s\n",HOST_NAME,host_ip);
 | 
			
		||||
    // MqttSocketConfigParam(host_ip);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef ADD_XIZI_FEATURES
 | 
			
		||||
    lwip_config_tcp(0, mqtt_demo_ipaddr, mqtt_demo_netmask, mqtt_demo_gwaddr);
 | 
			
		||||
 | 
			
		||||
    pthread_attr_t attr;
 | 
			
		||||
    attr.schedparam.sched_priority = LWIP_MQTT_DEMO_TASK_PRIO;
 | 
			
		||||
    attr.stacksize = LWIP_MQTT_DEMO_TASK_STACK_SIZE;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef ADD_NUTTX_FEATURES
 | 
			
		||||
    pthread_attr_t attr = PTHREAD_ATTR_INITIALIZER;
 | 
			
		||||
    attr.priority = LWIP_mqtt_DEMO_TASK_PRIO;
 | 
			
		||||
    attr.stacksize = LWIP_mqtt_DEMO_TASK_STACK_SIZE;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    PrivTaskCreate(&mqtt_client_task, &attr, &MqttSocketSendTask, NULL);
 | 
			
		||||
    PrivTaskStartup(&mqtt_client_task);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PRIV_SHELL_CMD_FUNCTION(MqttSocketSendTest, a tcp send sample, PRIV_SHELL_CMD_MAIN_ATTR);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,99 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2022 AIIT XUOS Lab
 | 
			
		||||
 * XiUOS is licensed under Mulan PSL v2.
 | 
			
		||||
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 | 
			
		||||
 * You may obtain a copy of Mulan PSL v2 at:
 | 
			
		||||
 *        http://license.coscl.org.cn/MulanPSL2
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 | 
			
		||||
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 | 
			
		||||
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 | 
			
		||||
 * See the Mulan PSL v2 for more details.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @file test_mqttclient.h
 | 
			
		||||
 * @brief mqtt subscribe fuction test
 | 
			
		||||
 * @version 3.0
 | 
			
		||||
 * @author AIIT XUOS Lab
 | 
			
		||||
 * @date 2023.8.10
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#define   MSG_MAX_LEN     1024
 | 
			
		||||
#define   MSG_TOPIC_LEN   50
 | 
			
		||||
#define   KEEPLIVE_TIME   650
 | 
			
		||||
#define   MQTT_VERSION    4
 | 
			
		||||
 | 
			
		||||
#ifdef    LWIP_DNS
 | 
			
		||||
#define   HOST_NAME       "iot-06z00im0uwa0ki2.mqtt.iothub.aliyuncs.com"     //服务器域名
 | 
			
		||||
#else
 | 
			
		||||
#define   HOST_NAME       "iot-06z00im0uwa0ki2.mqtt.iothub.aliyuncs.com"     //服务器IP地址
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//#define   HOST_IP       "129.204.201.235"
 | 
			
		||||
#define   HOST_PORT     1883    //由于是TCP连接,端口必须是1883
 | 
			
		||||
 | 
			
		||||
#define   CLIENT_ID     "iw3rn3pa11K.test|securemode=2,signmethod=hmacsha256,timestamp=1689296035604|"         //随机的id
 | 
			
		||||
#define   USER_NAME     "test&iw3rn3pa11K"     //用户名
 | 
			
		||||
#define   PASSWORD      "7b948d22fe46f0f63d1a403376d26e7cb298abc227d29e44311d7040307a71f8"  //秘钥
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define   TOPIC         "/iw3rn3pa11K/test/user/Test"      //订阅的主题
 | 
			
		||||
 | 
			
		||||
#define   TEST_MESSAGE  "test_message"  //发送测试消息
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// #define   CLIENT_ID     "hc123456789"         //随机的id
 | 
			
		||||
// #define   USER_NAME     "xiuos"     //用户名
 | 
			
		||||
// #define   PASSWORD      "xiuos"  //秘钥
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
enum QoS 
 | 
			
		||||
{ QOS0 = 0, 
 | 
			
		||||
  QOS1, 
 | 
			
		||||
  QOS2 
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum MQTT_Connect
 | 
			
		||||
{
 | 
			
		||||
  Connect_OK = 0,
 | 
			
		||||
  Connect_NOK,
 | 
			
		||||
  Connect_NOTACK
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//数据交互结构体
 | 
			
		||||
typedef struct __MQTTMessage
 | 
			
		||||
{
 | 
			
		||||
    uint32_t qos;
 | 
			
		||||
    uint8_t retained;
 | 
			
		||||
    uint8_t dup;
 | 
			
		||||
    uint16_t id;
 | 
			
		||||
	  uint8_t type;
 | 
			
		||||
    void *payload;
 | 
			
		||||
    int32_t payloadlen;
 | 
			
		||||
}MQTTMessage;
 | 
			
		||||
 | 
			
		||||
//用户接收消息结构体
 | 
			
		||||
typedef struct __MQTT_MSG
 | 
			
		||||
{
 | 
			
		||||
	  uint8_t  msgqos;                 //消息质量
 | 
			
		||||
		uint8_t  msg[MSG_MAX_LEN];       //消息
 | 
			
		||||
	  uint32_t msglenth;               //消息长度
 | 
			
		||||
	  uint8_t  topic[MSG_TOPIC_LEN];   //主题    
 | 
			
		||||
	  uint16_t packetid;               //消息ID
 | 
			
		||||
	  uint8_t  valid;                  //标明消息是否有效
 | 
			
		||||
}MQTT_USER_MSG;
 | 
			
		||||
 | 
			
		||||
//发送消息结构体
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
    int8_t topic[MSG_TOPIC_LEN];
 | 
			
		||||
    int8_t qos;
 | 
			
		||||
    int8_t retained;
 | 
			
		||||
 | 
			
		||||
    uint8_t msg[MSG_MAX_LEN];
 | 
			
		||||
    uint8_t msglen;
 | 
			
		||||
} mqtt_recv_msg_t, *p_mqtt_recv_msg_t, mqtt_send_msg_t, *p_mqtt_send_msg_t;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,67 @@
 | 
			
		|||
# 基于矽璓已实现的Lwip,在ARM上实现基本的Web服务##
 | 
			
		||||
 | 
			
		||||
## 1. 简介
 | 
			
		||||
基于矽璓已实现的Lwip,在ARM上实现基本的Web服务,其功能支持IP、GateWay、DNS、子网掩码等网络参数的设置。
 | 
			
		||||
主要功能为:web服务等待Http请求的到来,解析Http报文后,设置本机的网络参数信息,在终端打印设置后的网络参数信息,并返回浏览器客户端成功响应。
 | 
			
		||||
 | 
			
		||||
## 2. 数据结构设计说明
 | 
			
		||||
Server的网络结构参数为:
 | 
			
		||||
```
 | 
			
		||||
struct ServerNet 
 | 
			
		||||
{
 | 
			
		||||
    char local_ip[20];
 | 
			
		||||
    char local_mask[20];
 | 
			
		||||
    char local_gw[20];
 | 
			
		||||
    char local_dns[20];
 | 
			
		||||
}ServerNet;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
主要实现了以下几个函数:
 | 
			
		||||
```
 | 
			
		||||
TcpThread:  建立socket监听,循环等待网络通信事件的到来;
 | 
			
		||||
HandleHttpRequest:处理并解析Http报文,得到网络参数信息;
 | 
			
		||||
AddrUpdate: 根据到来的参数,更新本机网络参数;
 | 
			
		||||
LwipShowIP:显示网络参数信息,包括IP、GateWay、DNS、子网掩码;
 | 
			
		||||
LwipSetIP: 设置网络参数信息,包括IP、GateWay、DNS、子网掩码;
 | 
			
		||||
ShowDns: 显示dns网址; 
 | 
			
		||||
SetDns: 设置dns网址;
 | 
			
		||||
```
 | 
			
		||||
除此之外,还包括两个工具函数:
 | 
			
		||||
```
 | 
			
		||||
SubstractServerNet: 截取http报文的请求体,格式为:ip=192.168.1.9&netmask=255.255.255.0&iproute=192.168.1.1&dns=114.114.114.114
 | 
			
		||||
substring: 截取字符串
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## 3. 测试程序说明
 | 
			
		||||
1. 通过数据线将开发板连接电脑,并通过网线连接使开发板接入家庭局域网。
 | 
			
		||||
2. 将TestWebserver注册为shell 命令,编译前通过menuconfig打开lwip和TestWebserver功能。
 | 
			
		||||
3. 将编译成功的XiZi-edu-arm32.bin文件烧录到arm板子。烧录bin文件前,需要将开发板boot引脚拉高(盖上跳帽),上电后再按下reset按键,之后可以点击烧写工具的执行按钮,等待烧写完成。
 | 
			
		||||
4. 将boot引脚拉低(摘下跳帽),按下reset按键,使用串口工具显示xiuos的命令行界面。
 | 
			
		||||
5. 将开发板的本机ip通过setip命令设置为与电脑端在同一个局域网之下,利用ping命令检测成功通信。
 | 
			
		||||
6. 运行TestWebserver命令验证功能。
 | 
			
		||||
 | 
			
		||||
## 4. 运行结果(##需结合运行测试截图按步骤说明##)
 | 
			
		||||
<img width="800" src="./photos/1.png"/>  
 | 
			
		||||
 | 
			
		||||
烧录程序,boot引脚拉高,按下reset按键。
 | 
			
		||||
 | 
			
		||||
<img width="800"  src="./photos/2.png"/>  
 | 
			
		||||
 | 
			
		||||
点击执行烧录,程序烧录成功。
 | 
			
		||||
 | 
			
		||||
<img width="800" src="./photos/3.png"/>  
 | 
			
		||||
 | 
			
		||||
打开串口调试工具,xiuos程序成功在开发板上运行。
 | 
			
		||||
 | 
			
		||||
<img width="800"  src="./photos/4.png"/>  
 | 
			
		||||
 | 
			
		||||
利用setip命令,修改开发板ip,使得开发板和电脑在同一个局域网内,并使用ping命令测试。
 | 
			
		||||
 | 
			
		||||
<img width="800"  src="./photos/5.png"/>  
 | 
			
		||||
 | 
			
		||||
在服务端运行TestWebserver命令,打开index.html,设置网络参数信息,点击提交,发送http请求。
 | 
			
		||||
 | 
			
		||||
<img width="800"  src="./photos/6.png"/> 
 | 
			
		||||
 | 
			
		||||
web服务收到http请求后并打印post请求报文,以及修改后的开发板的网络参数信息,包括IP、子网掩码、GateWay和DNS。
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="UTF-8">
 | 
			
		||||
    <title>表单</title>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
 | 
			
		||||
<!--
 | 
			
		||||
表单form
 | 
			
		||||
action:表单提交的位置,可以是网站,也可以是一个请求处理地址
 | 
			
		||||
method:有post,get提交方式
 | 
			
		||||
-->
 | 
			
		||||
<form action="http://192.168.1.5/" method="post">
 | 
			
		||||
  <h2>设置路由信息</h2>
 | 
			
		||||
    
 | 
			
		||||
<!--
 | 
			
		||||
<input type="text">是文本输入框
 | 
			
		||||
<input type="password">是密码输入框,默认是用小黑点表示密码的
 | 
			
		||||
-->
 | 
			
		||||
    <p>IP地址:<input type="text" name="ip"></p>
 | 
			
		||||
    <p>子网掩码:<input type="text" name="netmask"></p>
 | 
			
		||||
    <p>默认网关:<input type="text" name="iproute"></p>
 | 
			
		||||
    <p>DNS服务器<input type="text" name="dns"></p>
 | 
			
		||||
    <p>
 | 
			
		||||
        <input type="submit">
 | 
			
		||||
        <input type="reset">
 | 
			
		||||
    </p>
 | 
			
		||||
 | 
			
		||||
</form>
 | 
			
		||||
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 19 MiB  | 
| 
		 After Width: | Height: | Size: 180 KiB  | 
| 
		 After Width: | Height: | Size: 81 KiB  | 
| 
		 After Width: | Height: | Size: 101 KiB  | 
| 
		 After Width: | Height: | Size: 52 KiB  | 
| 
		 After Width: | Height: | Size: 175 KiB  | 
| 
						 | 
				
			
			@ -0,0 +1,218 @@
 | 
			
		|||
/**
 | 
			
		||||
* @file:    test_webserver.c
 | 
			
		||||
* @brief:   a application of test webserver function
 | 
			
		||||
* @version: 1.0
 | 
			
		||||
* @author:  Yao wenying
 | 
			
		||||
* @date:    2023/08/20
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include <transform.h>
 | 
			
		||||
#include "test_webserver.h"
 | 
			
		||||
 | 
			
		||||
/**设置ip,网关,子网掩码这部分参考lwip_config_demo.c*/
 | 
			
		||||
void LwipShowIP()
 | 
			
		||||
{
 | 
			
		||||
    // find default netdev
 | 
			
		||||
    lw_notice("\r\n************************************************\r\n");
 | 
			
		||||
    lw_notice(" Network Configuration\r\n");
 | 
			
		||||
    lw_notice("************************************************\r\n");
 | 
			
		||||
    lw_notice(" ETH0 IPv4 Address   : %u.%u.%u.%u\r\n", ((u8_t*)&lwip_eth0_ipaddr)[0], ((u8_t*)&lwip_eth0_ipaddr)[1],
 | 
			
		||||
        ((u8_t*)&lwip_eth0_ipaddr)[2], ((u8_t*)&lwip_eth0_ipaddr)[3]);
 | 
			
		||||
    lw_notice(" ETH0 IPv4 Subnet mask : %u.%u.%u.%u\r\n", ((u8_t*)&lwip_eth0_netmask)[0], ((u8_t*)&lwip_eth0_netmask)[1],
 | 
			
		||||
        ((u8_t*)&lwip_eth0_netmask)[2], ((u8_t*)&lwip_eth0_netmask)[3]);
 | 
			
		||||
    lw_notice(" ETH0 IPv4 Gateway   : %u.%u.%u.%u\r\n", ((u8_t*)&lwip_gwaddr)[0], ((u8_t*)&lwip_eth0_gwaddr)[1],
 | 
			
		||||
        ((u8_t*)&lwip_eth0_gwaddr)[2], ((u8_t*)&lwip_eth0_gwaddr)[3]);
 | 
			
		||||
    lw_notice("************************************************\r\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t enet_idd = 0;
 | 
			
		||||
void LwipSetIP(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
    if(argc >= 4)
 | 
			
		||||
    {
 | 
			
		||||
        printf("lw: [%s] ip %s mask %s gw %s netport %s\n", __func__, argv[1], argv[2], argv[3], argv[4]);
 | 
			
		||||
        sscanf(argv[1], "%d.%d.%d.%d", &lwip_ipaddr[0], &lwip_ipaddr[1], &lwip_ipaddr[2], &lwip_ipaddr[3]);
 | 
			
		||||
        sscanf(argv[2], "%d.%d.%d.%d", &lwip_netmask[0], &lwip_netmask[1], &lwip_netmask[2], &lwip_netmask[3]);
 | 
			
		||||
        sscanf(argv[3], "%d.%d.%d.%d", &lwip_gwaddr[0], &lwip_gwaddr[1], &lwip_gwaddr[2], &lwip_gwaddr[3]);
 | 
			
		||||
        sscanf(argv[4], "%d", &enet_idd);
 | 
			
		||||
 | 
			
		||||
        if(0 == enet_idd)
 | 
			
		||||
        {
 | 
			
		||||
            printf("save eth0 info\n");
 | 
			
		||||
            memcpy(lwip_eth0_ipaddr, lwip_ipaddr, 20);
 | 
			
		||||
            memcpy(lwip_eth0_netmask, lwip_netmask, 20);
 | 
			
		||||
            memcpy(lwip_eth0_gwaddr, lwip_gwaddr, 20);
 | 
			
		||||
        }
 | 
			
		||||
        else if(1 == enet_idd)
 | 
			
		||||
        {
 | 
			
		||||
            printf("save eth1 info\n");
 | 
			
		||||
            memcpy(lwip_eth1_ipaddr, lwip_ipaddr, 20);
 | 
			
		||||
            memcpy(lwip_eth1_netmask, lwip_netmask, 20);
 | 
			
		||||
            memcpy(lwip_eth1_gwaddr, lwip_gwaddr, 20);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else if(argc == 2)
 | 
			
		||||
    {
 | 
			
		||||
        printf("lw: [%s] set eth0 ipaddr %s \n", __func__, argv[1]);
 | 
			
		||||
        sscanf(argv[1], "%d.%d.%d.%d", &lwip_ipaddr[0], &lwip_ipaddr[1], &lwip_ipaddr[2], &lwip_ipaddr[3]);
 | 
			
		||||
        memcpy(lwip_eth0_ipaddr, lwip_ipaddr, strlen(lwip_ipaddr));
 | 
			
		||||
    }
 | 
			
		||||
    // sys_thread_new("SET ip address", LwipSetIPTask, &enet_id, LWIP_TASK_STACK_SIZE, LWIP_DEMO_TASK_PRIO);
 | 
			
		||||
    printf("lw: [%s] config netport id[%d]\n", __func__, enet_idd);
 | 
			
		||||
    lwip_config_net(enet_idd, lwip_ipaddr, lwip_netmask, lwip_gwaddr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ShowDns(){
 | 
			
		||||
    const ip_addr_t *dns = dns_getserver(0);
 | 
			
		||||
    char *dns_ip = ipaddr_ntoa(dns);
 | 
			
		||||
    printf("lw: eth0 dns is %s \n", dns_ip);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SetDns(char* ip){
 | 
			
		||||
    ip_addr_t dns;
 | 
			
		||||
    ipaddr_aton(ip, &dns);	
 | 
			
		||||
    dns_setserver(0, &dns);
 | 
			
		||||
    printf("ETH0 Set DNS Success!\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AddrUpdate()
 | 
			
		||||
{
 | 
			
		||||
    char *argv[5];
 | 
			
		||||
    argv[1] = ServerNet.local_ip;
 | 
			
		||||
    argv[2] = ServerNet.local_mask;
 | 
			
		||||
    argv[3] = ServerNet.local_gw ;
 | 
			
		||||
    argv[4] = 0;
 | 
			
		||||
    LwipSetIP(4, argv); 
 | 
			
		||||
    LwipShowIP();
 | 
			
		||||
    SetDns(ServerNet.local_dns);
 | 
			
		||||
    ShowDns();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char *substring(char *dst,char *src,int start,int len)
 | 
			
		||||
{  
 | 
			
		||||
	char *p=dst;  
 | 
			
		||||
	char *q=src;  
 | 
			
		||||
	int length=strlen(src); 
 | 
			
		||||
	if(start>=length||start<0) 
 | 
			
		||||
		return NULL;  
 | 
			
		||||
	if(len>length) 
 | 
			
		||||
		len=length-start; 
 | 
			
		||||
	q+=start;
 | 
			
		||||
	while(len--)  
 | 
			
		||||
	{   
 | 
			
		||||
		*(p++)=*(q++); 
 | 
			
		||||
	}  
 | 
			
		||||
	*(p++)='\0';  
 | 
			
		||||
	return dst;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SubstractServerNet(char * params){
 | 
			
		||||
    char *p =params;
 | 
			
		||||
    p +=3;
 | 
			
		||||
    char *end = strchr(p, '&');
 | 
			
		||||
    int len = (int)(end-p);
 | 
			
		||||
    substring(ServerNet.local_ip, p, 0,len);
 | 
			
		||||
    p +=len;
 | 
			
		||||
    p += 9;
 | 
			
		||||
    end = strchr(p, '&');
 | 
			
		||||
    len = (int)(end-p);
 | 
			
		||||
    substring(ServerNet.local_mask , p, 0, len);
 | 
			
		||||
    p +=len;
 | 
			
		||||
    p +=9;
 | 
			
		||||
    end = strchr(p, '&');
 | 
			
		||||
    len = (int)(end-p);
 | 
			
		||||
    substring(ServerNet.local_gw, p, 0, len);
 | 
			
		||||
    p +=len;
 | 
			
		||||
    p += 5;
 | 
			
		||||
    strcpy(ServerNet.local_dns, p);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void HandleHttpRequest(char *params){
 | 
			
		||||
    SubstractServerNet(params);
 | 
			
		||||
    AddrUpdate(); //修改本机参数信息;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//设置socket
 | 
			
		||||
//循环监听socket并修改网络信息,单线程
 | 
			
		||||
void tcpecho_thread(){
 | 
			
		||||
    int sock=-1, connected;
 | 
			
		||||
    char *recv_data;
 | 
			
		||||
    struct sockaddr_in server_addr, client_addr;
 | 
			
		||||
    socklen_t sin_size;
 | 
			
		||||
    int recv_data_len;
 | 
			
		||||
 | 
			
		||||
    recv_data = (char *) malloc(RECV_DATA);
 | 
			
		||||
 | 
			
		||||
    if(recv_data == NULL){
 | 
			
		||||
        printf("No memory");
 | 
			
		||||
        goto __exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sock = socket(AF_INET, SOCK_STREAM, 0);
 | 
			
		||||
    if(sock < 0){
 | 
			
		||||
        printf("Socket error\n");
 | 
			
		||||
        goto __exit;
 | 
			
		||||
    }
 | 
			
		||||
    server_addr.sin_family = AF_INET;
 | 
			
		||||
    server_addr.sin_addr.s_addr = INADDR_ANY;
 | 
			
		||||
    server_addr.sin_port = htons(PORT);
 | 
			
		||||
    memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
 | 
			
		||||
 | 
			
		||||
    if(bind(sock,(struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == 1){
 | 
			
		||||
        printf("Unable to bind\n");
 | 
			
		||||
        goto __exit;
 | 
			
		||||
    }
 | 
			
		||||
    if (listen(sock, 5) == -1)
 | 
			
		||||
    {
 | 
			
		||||
        printf("Listen error\n");
 | 
			
		||||
        goto __exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    while(1){
 | 
			
		||||
        sin_size = sizeof(struct sockaddr_in);
 | 
			
		||||
        connected = accept(sock, (struct sockaddr *)&client_addr, &sin_size);
 | 
			
		||||
 | 
			
		||||
        printf("new client connected from (%s, %d)\n",
 | 
			
		||||
            inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            int flag = 1;
 | 
			
		||||
 | 
			
		||||
            setsockopt(connected,
 | 
			
		||||
                    IPPROTO_TCP,     /* set option at TCP level */
 | 
			
		||||
                    TCP_NODELAY,     /* name of option */
 | 
			
		||||
                    (void *) &flag, /* the cast is historical cruft */
 | 
			
		||||
                    sizeof(int));    /* length of option value */
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        recv_data_len = recv(connected, recv_data, RECV_DATA, 0);
 | 
			
		||||
 | 
			
		||||
        printf("recv %d len data\n",recv_data_len);
 | 
			
		||||
        recv_data[recv_data_len] = '\0';
 | 
			
		||||
        printf("%s\n", recv_data);
 | 
			
		||||
 | 
			
		||||
        char * params = strstr(recv_data, "ip=");
 | 
			
		||||
        HandleHttpRequest(params);
 | 
			
		||||
 | 
			
		||||
        char *response = "HTTP/1.1 200 OK\r\n"
 | 
			
		||||
                         "Content-Type: application/x-www-form-urlencoded\r\n"
 | 
			
		||||
                         "\r\n";
 | 
			
		||||
        write(connected,response,strlen(response));
 | 
			
		||||
        if (connected >= 0)
 | 
			
		||||
            closesocket(connected);
 | 
			
		||||
 | 
			
		||||
        connected = -1;
 | 
			
		||||
    }
 | 
			
		||||
    __exit:
 | 
			
		||||
    if (sock >= 0) closesocket(sock);
 | 
			
		||||
    if (recv_data) free(recv_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TestWebserver(){
 | 
			
		||||
    tcpecho_thread();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PRIV_SHELL_CMD_FUNCTION(TestWebserver, Implement web_server, PRIV_SHELL_CMD_MAIN_ATTR);
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,43 @@
 | 
			
		|||
/**
 | 
			
		||||
* @file:    test_webserver.h
 | 
			
		||||
* @brief:   a application of test webserver function
 | 
			
		||||
* @version: 1.0
 | 
			
		||||
* @author:  Yao wenying
 | 
			
		||||
* @date:    2023/08/20
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifndef __WEBSERVER_H__
 | 
			
		||||
#define __WEBSERVER_H__
 | 
			
		||||
 | 
			
		||||
#include "lwip/opt.h"
 | 
			
		||||
#include <lwip/sockets.h>
 | 
			
		||||
#include "lwip/api.h"
 | 
			
		||||
#include "lwip/sys.h"
 | 
			
		||||
#include "netif/ethernet.h"
 | 
			
		||||
#include "lwip/tcpip.h"
 | 
			
		||||
#include "sys_arch.h"
 | 
			
		||||
#include <netdev.h>
 | 
			
		||||
#include "dns.h"
 | 
			
		||||
#include "string.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define PORT              80
 | 
			
		||||
#define RECV_DATA         1024
 | 
			
		||||
struct ServerNet 
 | 
			
		||||
{
 | 
			
		||||
    char local_ip[20];
 | 
			
		||||
    char local_mask[20];
 | 
			
		||||
    char local_gw[20];
 | 
			
		||||
    char local_dns[20];
 | 
			
		||||
}ServerNet;
 | 
			
		||||
 | 
			
		||||
void LwipShowIP();
 | 
			
		||||
void LwipSetIP(int argc, char *argv[]); 
 | 
			
		||||
void ShowDns();
 | 
			
		||||
void SetDns();
 | 
			
		||||
void AddrUpdate();  //更新本机网络参数信息
 | 
			
		||||
void HandleHttpRequest(); //解析报文,并返回响应
 | 
			
		||||
void TcpThread();//建立socket监听,循环等待网络通信事件到来
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -1,3 +1,4 @@
 | 
			
		|||
SRC_FILES :=  cJSON.c
 | 
			
		||||
SRC_FILES +=  cJSON_Process.c
 | 
			
		||||
 | 
			
		||||
include $(KERNEL_ROOT)/compiler.mk
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,85 @@
 | 
			
		|||
#include "cJSON_Process.h"
 | 
			
		||||
#include <lwip/sockets.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cJSON* cJSON_Data_Init(void)
 | 
			
		||||
{
 | 
			
		||||
  cJSON* cJSON_Root = NULL;    //json根节点
 | 
			
		||||
  
 | 
			
		||||
  
 | 
			
		||||
  cJSON_Root = cJSON_CreateObject();  /*创建项目*/
 | 
			
		||||
  if(NULL == cJSON_Root)
 | 
			
		||||
  {
 | 
			
		||||
      return NULL;
 | 
			
		||||
  }
 | 
			
		||||
  cJSON_AddStringToObject(cJSON_Root, NAME, DEFAULT_NAME);  /*添加元素  键值对*/
 | 
			
		||||
  cJSON_AddNumberToObject(cJSON_Root, TEMP_NUM, DEFAULT_TEMP_NUM);
 | 
			
		||||
  cJSON_AddNumberToObject(cJSON_Root, HUM_NUM, DEFAULT_HUM_NUM);
 | 
			
		||||
  
 | 
			
		||||
  char* p = cJSON_Print(cJSON_Root);  /*p 指向的字符串是json格式的*/
 | 
			
		||||
 | 
			
		||||
  p = NULL;
 | 
			
		||||
  
 | 
			
		||||
  return cJSON_Root;
 | 
			
		||||
  
 | 
			
		||||
}
 | 
			
		||||
uint8_t cJSON_Update(const cJSON * const object,const char * const string,void *d)
 | 
			
		||||
{
 | 
			
		||||
  cJSON* node = NULL;    //json根节点
 | 
			
		||||
  node = cJSON_GetObjectItem(object,string);
 | 
			
		||||
  if(node == NULL)
 | 
			
		||||
    return 0;
 | 
			
		||||
  if(cJSON_IsBool(node))
 | 
			
		||||
  {
 | 
			
		||||
    int *b = (int*)d;
 | 
			
		||||
 | 
			
		||||
    cJSON_GetObjectItem(object,string)->type = *b ? cJSON_True : cJSON_False;
 | 
			
		||||
 | 
			
		||||
    return 1;
 | 
			
		||||
  }
 | 
			
		||||
  else if(cJSON_IsString(node))
 | 
			
		||||
  {
 | 
			
		||||
    cJSON_GetObjectItem(object,string)->valuestring = (char*)d;
 | 
			
		||||
 | 
			
		||||
    return 1;
 | 
			
		||||
  }
 | 
			
		||||
  else if(cJSON_IsNumber(node))
 | 
			
		||||
  {
 | 
			
		||||
    double *num = (double*)d;
 | 
			
		||||
 | 
			
		||||
    cJSON_GetObjectItem(object,string)->valuedouble = (double)*num;
 | 
			
		||||
 | 
			
		||||
    return 1;
 | 
			
		||||
  }
 | 
			
		||||
  else
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Proscess(void* data)
 | 
			
		||||
{
 | 
			
		||||
  
 | 
			
		||||
  cJSON *root,*json_name,*json_temp_num,*json_hum_num;
 | 
			
		||||
  root = cJSON_Parse((char*)data); //解析成json形式
 | 
			
		||||
 | 
			
		||||
  json_name = cJSON_GetObjectItem( root , NAME);   //获取键值内容
 | 
			
		||||
  json_temp_num = cJSON_GetObjectItem( root , TEMP_NUM );
 | 
			
		||||
  json_hum_num = cJSON_GetObjectItem( root , HUM_NUM );
 | 
			
		||||
 | 
			
		||||
  lw_print("name:%s\n temp_num:%f\n hum_num:%f\n",
 | 
			
		||||
              json_name->valuestring,
 | 
			
		||||
              json_temp_num->valuedouble,
 | 
			
		||||
              json_hum_num->valuedouble);
 | 
			
		||||
 | 
			
		||||
  cJSON_Delete(root); //释放内存 
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
#ifndef _CJSON_PROCESS_H_
 | 
			
		||||
#define _CJSON_PROCESS_H_
 | 
			
		||||
#include "cJSON.h"
 | 
			
		||||
#include "stdint.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define   NAME            "name"     
 | 
			
		||||
#define   TEMP_NUM        "temp"  
 | 
			
		||||
#define   HUM_NUM         "hum" 
 | 
			
		||||
 | 
			
		||||
#define   DEFAULT_NAME          "fire"     
 | 
			
		||||
#define   DEFAULT_TEMP_NUM       25.0 
 | 
			
		||||
#define   DEFAULT_HUM_NUM        50.0 
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define   UPDATE_SUCCESS       1 
 | 
			
		||||
#define   UPDATE_FAIL          0
 | 
			
		||||
 | 
			
		||||
cJSON* cJSON_Data_Init(void);
 | 
			
		||||
uint8_t cJSON_Update(const cJSON * const object,const char * const string,void * d);
 | 
			
		||||
void Proscess(void* data);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||