fix the conflict

This commit is contained in:
yanglong 2023-10-23 19:50:31 +08:00
commit 261a2a1653
94 changed files with 7239 additions and 5 deletions

View File

@ -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

View File

@ -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

View File

@ -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
![](./img/image.png)
![](./img/image-1.png)
2. 编译
![](./img/image-2.png)
3. 烧写
![](./img/image-3.png)
4. xshell连接串口终端
![](./img/image-4.png)
6. 配置ip
![](./img/image-5.png)
7. 运行TestFtpClient开始下载文件
![](./img/image-6.png)
![](./img/image-7.png)
![](./img/image-8.png)

View File

@ -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();
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

View File

@ -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);

View File

@ -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

View File

@ -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. 运行结果(##需结合运行测试截图按步骤说明##
![image](img/01.png)
![image](img/02.png)
打开menuconfig之后将test_ftp开启(y)将Using TFcard device 和 Using W5500 as network device 开启(y)保存后退出
![image](img/03.png)
编译XiZi-edu-riscv64.elf成功
![image](img/04.png)
启动kflash烧录bin文件按reset键重置成功进入系统并显示SD卡已挂载
![image](img/05.png)
执行TestTfp命令可以看到成功登陆ftp服务器并开始下载10个大小为4KB的文件。
![image](img/06.png)
服务器日志信息
![image](img/07.png)
读取sd卡中的文件可以看到10个大小为4KB的文件被成功下载
![image](img/08.png)
![image](img/09.png)
![image](img/10.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

@ -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);

View File

@ -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();

View File

@ -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
![image](./images/0-1.png)
> 开启连接框架支持同时开启Lora Adapter支持
![image](./images/0-2.png)
> 选择E220模块
![image](./images/0-3.png)
> 开启测试函数
![image](./images/0-4.png)
> 测试单通道网关
- 修改MakeFilexiuos/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打开网关
![image](./images/1.png)
> 网关打开地址OXFFFF信道OXA空中速率2.4K
### 3客户端连接网关
![image](./images/2.png)
> 两边客户端同时发送入网请求,可以看见信道检测效果
### 4客户端发送数据
![image](./images/3.png)
> 客户端向网关发送数据
![image](./images/4.png)
> 两边客户端同时发送数据
使用自带的天线2.4K空中速率载荷64字节在无大型遮盖物的情况下传输距离近1500米

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 970 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@ -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,&register_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,&register_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,&register_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;
}

View File

@ -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个信道083
#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__

File diff suppressed because it is too large Load Diff

View File

@ -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);
/*网关操作*************************************************************************/

View File

@ -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

View File

@ -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);

View File

@ -0,0 +1,5 @@
SRC_FILES += test_mqttclient.c
SRC_DIR += mqtt
include $(KERNEL_ROOT)/compiler.mk

View File

@ -0,0 +1,55 @@
## 1. 简介
本代码实现了MQTT对服务器订阅主体并发送信息功能
## 2. 数据结构设计说明
### 2.1 MQTT数据结构定义
![image-20231007142702891](./imgs/图片4.png)
### 2.2 数据解析与加密
![image-20231007143235579](./imgs/图片5.png)
### 2.3 MQTT连接与订阅
![image-20231007143448685](./imgs/图片6.png)
![image-20231007143607579](./imgs/图片7.png)
## 3. 测试程序说明
MQTT基于TCP/IP协议分为客户端与服务端。在本任务中ARM终端作为客户端与服务端连接并进行主题订阅。
以下代码设置了进行订阅时需要的Client ID、用户名、密码及订阅主题。
![image-20231007144555989](./imgs/图片8.png)
客户端首先需要根据以上信息向服务端发起连接请求,验证用户名及密码
![image-20231007144710185](./imgs/图片9.png)
随后进行主题订阅,接收服务端发布的消息
![image-20231007144823940](./imgs/图片10.png)
在订阅过程中需要对获取的信息进行读取同时每隔一段时间向服务端发送保持活性ping请求以维持与服务端的连接
![image-20231007144921430](./imgs/图片11.png)
## 4. 测试流程
测试流程为:
首先执行setip命令设置设备ip地址
![图片2](./imgs/图片2.png)
随后执行“MqttSocketRecvTest 服务器ip”命令订阅主题然后在服务器端发布消息如图所示
![图片1](./imgs/pic1.png)
最终设备端接收到信息
![图片3](./imgs/pic2.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

View File

@ -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_ */

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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_ */

View File

@ -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);
}

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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

View File

@ -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>
uint8_t humi_low8bit; //ԭʼ<D4AD><CABC><EFBFBD>ݣ<EFBFBD>ʪ<EFBFBD>ȵ<EFBFBD>
uint8_t temp_high8bit; //ԭʼ<D4AD><CABC><EFBFBD>ݣ<EFBFBD><DDA3>¶ȸ<C2B6>
uint8_t temp_low8bit; //ԭʼ<D4AD><CABC><EFBFBD>ݣ<EFBFBD><DDA3>¶ȸ<C2B6>
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);

View File

@ -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;

View File

@ -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。

View File

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>表单</title>
</head>
<body>
<!--
表单form
action表单提交的位置可以是网站也可以是一个请求处理地址
method有postget提交方式
-->
<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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

View File

@ -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);

View File

@ -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

View File

@ -1,3 +1,4 @@
SRC_FILES := cJSON.c
SRC_FILES += cJSON_Process.c
include $(KERNEL_ROOT)/compiler.mk

View File

@ -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); //释放内存
}

View File

@ -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