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

|
||||

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

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

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

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

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

|
||||

|
||||

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

|
||||

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

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

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

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

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

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

|
||||

|
||||

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

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

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

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

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

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

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

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

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

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

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

|
||||
|
||||

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

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

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

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

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

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

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

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