[嵌入式開發模塊]Coap開源庫libnyoci移植及簡單使用(基於wiz/W5500官方io庫)

前言

最近研究了下實現coap協議,幾個純C開源庫看下來感覺還是libnyoci移植起來靠譜點,其他庫要不然對POSIX的API強耦合,要不反正就是不知道怎麼搞。

折騰好久起碼現在能夠實現一個正常的Coap服務器了,發上來供需要的人蔘考。

因爲這個庫依賴了大量相對高級版本的標準庫函數以及(如果要完整移植的話)要求實現很多應用層網絡功能,如DNS、TSL什麼的,所以着實折騰了好久才成功編譯。

這裏只講到要實現一個基於UDP的Coap服務器所需要移植的部分。

工程環境配置

首先,先去全球最大的同性交友網站下載好libnyoci的源文件,完不成的話點擊下右上角的×

然後libnyoci\src\libnyoci中的.c和.h文件全扔進工程中,基本都會用到。
libnyoci\src\libnyociextra中的文件則根據需要放進工程中,取決於你使用這個庫的方法。

然後我們需要加入配置文件,在這之前先打開編譯器選項,加入宏定義HAVE_CONFIG_H=1

在我的CodeWarrior上是這樣子實現的:

不同的IDE找各自的方法。

配置文件

這樣子nyoci就知道了你會包含一個叫做config.h的配置文件;以及默認會有的nyoci-config.h配置文件。這兩個文件如下:

config.h

/* src/config.h.  Generated from config.h.in by configure.  */
/* src/config.h.in.  Generated from configure.ac by autoheader.  */

/* rl_completion_entry_function has the wrong return type */
#define HAS_LIBEDIT_COMPLETION_ENTRY_BUG 1

/* Define to 1 if you have the `alloca' function. */
/* #undef HAVE_ALLOCA */

/* Define to 1 if you have the <alloca.h> header file. */
#define HAVE_ALLOCA_H 0

/* Define to 1 if you have the <dlfcn.h> header file. */
#define HAVE_DLFCN_H 0

/* Define to 1 if you have the <errno.h> header file. */
#define HAVE_ERRNO_H 1

/* Define to 1 if you have the `fprintf' function. */
#define HAVE_FPRINTF 0

/* Define to 1 if the system has the `deprecated' function attribute */
#define HAVE_FUNC_ATTRIBUTE_DEPRECATED 0

/* Define to 1 if the system has the `pure' function attribute */
#define HAVE_FUNC_ATTRIBUTE_PURE 0

/* Define to 1 if you have the `getline' function. */
#define HAVE_GETLINE 0

/* Define to 1 if you have the `getloadavg' function. */
#define HAVE_GETLOADAVG 0

/* Define to 1 if you have the `gettimeofday' function. */
#define HAVE_GETTIMEOFDAY 0

/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 0

/* Define to 1 if you have the `readline' library (-lreadline). */
#define HAVE_LIBREADLINE 0

/* Define to 1 if you have the `malloc' function. */
#define HAVE_MALLOC 1

/* Define to 1 if you have the `memcmp' function. */
#define HAVE_MEMCMP 1

/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 0

/* Define to 1 if you have the `memset' function. */
#define HAVE_MEMSET 1

/* Set if OpenSSL is present */
/* #undef HAVE_OPENSSL */

/* Set if OpenSSL has DTLSv1_method() */
/* #undef HAVE_OPENSSL_DTLSV1_METHOD */

/* Set if OpenSSL has DTLS_method() */
/* #undef HAVE_OPENSSL_DTLS_METHOD */

/* Set if OpenSSL has SSL_CONF_CTX_new() */
/* #undef HAVE_OPENSSL_SSL_CONF_CTX_NEW */

/* Set if OpenSSL has SSL_CONF_finish() */
/* #undef HAVE_OPENSSL_SSL_CONF_FINISH */

/* Define to 1 if you have the `printf' function. */
#define HAVE_PRINTF 1

/* Define if you have POSIX threads libraries and header files. */
#define HAVE_PTHREAD 0

/* Have PTHREAD_PRIO_INHERIT. */
#define HAVE_PTHREAD_PRIO_INHERIT 0

/* Define to 1 if you have the `rl_set_prompt' function. */
#define HAVE_RL_SET_PROMPT 0

/* Define to 1 if you have the `setenv' function. */
#define HAVE_SETENV 0

/* Define to 1 if you have the `snprintf' function. */
#define HAVE_SNPRINTF 1

/* Define to 1 if you have the `sprintf' function. */
#define HAVE_SPRINTF 1

/* Define to 1 if you have the <stdarg.h> header file. */
#define HAVE_STDARG_H 1

/* Define to 1 if you have the <stdbool.h> header file. */
#define HAVE_STDBOOL_H 1

/* Define to 1 if you have the <stddef.h> header file. */
#define HAVE_STDDEF_H 1

/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1

/* Define to 1 if you have the <stdio.h> header file. */
#define HAVE_STDIO_H 1

/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1

/* Define to 1 if you have the `stpncpy' function. */
#define HAVE_STPNCPY 1

/* Define to 1 if you have the `strchr' function. */
#define HAVE_STRCHR 1

/* Define to 1 if you have the `strdup' function. */
#define HAVE_STRDUP 0

#define HAVE_STRSEP 0

/* Define to 1 if you have the `strerror' function. */
#define HAVE_STRERROR 0

/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 0

/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1

/* Define to 1 if you have the `strlcat' function. */
#define HAVE_STRLCAT 1

/* Define to 1 if you have the `strlcpy' function. */
#define HAVE_STRLCPY 0

/* Define to 1 if you have the `strndup' function. */
#define HAVE_STRNDUP 0

/* Define to 1 if you have the `strstr' function. */
#define HAVE_STRSTR 1

/* Define to 1 if you have the `strtol' function. */
#define HAVE_STRTOL 1

/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 0

/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 0

/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 0

/* Define to 1 if you have the `vsnprintf' function. */
#define HAVE_VSNPRINTF 0

/* Define to 1 if you have the `vsprintf' function. */
#define HAVE_VSPRINTF 0

#define HAVE_C99_VLA 0

/* Define to the sub-directory where libtool stores uninstalled libraries. */
//#define LT_OBJDIR ".libs/"

/* . */
//#define NYOCI_API_EXTERN  extern

/* . */
/* #undef NYOCI_AVOID_MALLOC */

/* . */
/* #undef NYOCI_AVOID_PRINTF */

/* . */
/* #undef NYOCI_CONF_NODE_ROUTER */

/* . */
/* #undef NYOCI_CONF_TRANS_ENABLE_BLOCK2 */

/* . */
/* #undef NYOCI_CONF_TRANS_ENABLE_OBSERVING */

/* . */
/* #undef NYOCI_EMBEDDED */

/* . */
//#define NYOCI_INTERNAL_EXTERN extern

/* . */
/* #undef NYOCI_MAX_OBSERVERS */

/* . */
/* #undef NYOCI_MAX_VHOSTS */

/* LibNyoci network abstraction */
//#define NYOCI_PLAT_NET posix

/* . */
/* #undef NYOCI_PLAT_NET_POSIX_FAMILY */

/* LibNyoci TLS abstraction */
/* #undef NYOCI_PLAT_TLS */

/* . */
/* #undef NYOCI_SINGLETON */

/* . */
/* #undef NYOCI_USE_CASCADE_COUNT */

/* Name of package */
#define PACKAGE "libnyoci"

/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "https://github.com/darconeous/libnyoci/"

/* Define to the full name of this package. */
#define PACKAGE_NAME "LibNyoci"

/* Define to the full name and version of this package. */
#define PACKAGE_STRING "LibNyoci 0.07.00rc1"

/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "libnyoci"

/* Define to the home page for this package. */
#define PACKAGE_URL "http://libnyoci.org/"

/* Define to the version of this package. */
#define PACKAGE_VERSION "0.07.00rc1"

/* Define to necessary symbol if this constant uses a non-standard name on
   your system. */
/* #undef PTHREAD_CREATE_JOINABLE */

/* Source version */
#define SOURCE_VERSION "0.07.00rc1-6-g5ad1f3d"

/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1

/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
#define TIME_WITH_SYS_TIME 0

/* Enable extensions on AIX 3, Interix.  */
//#ifndef _ALL_SOURCE
// #define _ALL_SOURCE 1
//#endif
/* Enable GNU extensions on systems that have them.  */
//#ifndef _GNU_SOURCE
//# define _GNU_SOURCE 1
//#endif
/* Enable threading extensions on Solaris.  */
//#ifndef _POSIX_PTHREAD_SEMANTICS
//# define _POSIX_PTHREAD_SEMANTICS 1
//#endif
/* Enable extensions on HP NonStop.  */
//#ifndef _TANDEM_SOURCE
//# define _TANDEM_SOURCE 1
//#endif
/* Enable general extensions on Solaris.  */
//#ifndef __EXTENSIONS__
//# define __EXTENSIONS__ 1
//#endif
#define DEBUG 1
#define VERBOSE_DEBUG 1
#define __ORDER_BIG_ENDIAN__ 1
#define __BYTE_ORDER__  0

/* Version number of package */
#define VERSION "0.07.00rc1"

/* Define to 1 if on MINIX. */
/* #undef _MINIX */

/* Define to 2 if the system does not provide POSIX.1 features except with
   this defined. */
/* #undef _POSIX_1_SOURCE */

/* Define to 1 if you need to in order for `stat' and other things to work. */
/* #undef _POSIX_SOURCE */

/* Define to empty if `const' does not conform to ANSI C. */
/* #undef const */

/* Define to `__inline__' or `__inline' if that's what the C compiler
   calls it, or to nothing if 'inline' is not supported under any name.  */
#ifndef __cplusplus
/* #undef inline */
#endif

/* Define to `unsigned int' if <sys/types.h> does not define. */
/* #undef size_t */

/* Define to `int' if <sys/types.h> does not define. */
/* #undef ssize_t */

/* Define to empty if the keyword `volatile' does not work. Warning: valid
   code using `volatile' can become incorrect without. Disable with care. */
/* #undef volatile */

這個文件主要是用於告知nyoci你的開發環境有沒有包含某個函數以配置代碼,上面是我目前的配置。請參照自己的開發環境按註釋配置好其中未註釋掉的部分,以減少編譯鏈接時一堆報警。。

要注意的是

#define __ORDER_BIG_ENDIAN__ 1
#define __BYTE_ORDER__  0

這裏兩個其實並不是讓你回答機器是大端還是小端的。它的功能就是幫助nyoci確定位序以確定:

struct coap_header_s {
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
	uint8_t
		version:2,
		tt:2,
		token_len:4;
#else
	uint8_t
		token_len:4,
		tt:2,
		version:2;
#endif
……
};

這個結構體中到底應該按什麼順序排這三個字段。這個可能和單片機與編譯器都有關,不見得大端字節序的就一定大端位序。反正就是兩個值相等和兩個值不等的都試一下,用成功通訊的那個就好。

nyoci-config.h

/*!	@file nyoci-config.h
**	@author Robert Quattlebaum <[email protected]>
**	@brief LibNyoci Build Options
**
**	Copyright (C) 2017 Robert Quattlebaum
**
**	Permission is hereby granted, free of charge, to any person
**	obtaining a copy of this software and associated
**	documentation files (the "Software"), to deal in the
**	Software without restriction, including without limitation
**	the rights to use, copy, modify, merge, publish, distribute,
**	sublicense, and/or sell copies of the Software, and to
**	permit persons to whom the Software is furnished to do so,
**	subject to the following conditions:
**
**	The above copyright notice and this permission notice shall
**	be included in all copies or substantial portions of the
**	Software.
**
**	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
**	KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
**	WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
**	PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
**	OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
**	OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
**	OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
**	SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// 1/0
#define NYOCI_EMBEDDED 1
// 1/0
#define NYOCI_SINGLETON 0

#undef NYOCI_PLAT_NET_POSIX_FAMILY

#undef NYOCI_PLAT_NET

// define/undef
#undef NYOCI_PLAT_TLS

#undef NYOCI_AVOID_MALLOC

#undef NYOCI_AVOID_PRINTF

#undef NYOCI_DEFAULT_PORT

#undef NYOCI_CONF_DUPE_BUFFER_SIZE

#undef NYOCI_CONF_ENABLE_VHOSTS

#define NYOCI_CONF_MAX_ALLOCED_NODES 2

#undef NYOCI_CONF_MAX_GROUPS

#undef NYOCI_CONF_MAX_OBSERVERS

#undef NYOCI_CONF_MAX_PAIRINGS

#undef NYOCI_CONF_MAX_TIMEOUT

#define NYOCI_CONF_NODE_ROUTER

#undef NYOCI_CONF_TRANS_ENABLE_BLOCK2

#undef NYOCI_CONF_TRANS_ENABLE_OBSERVING

// 0/1
#define NYOCI_CONF_USE_DNS  0

#undef NYOCI_ADD_NEWLINES_TO_LIST_OUTPUT

#undef NYOCI_ASYNC_RESPONSE_MAX_LENGTH 50

#undef NYOCI_DEBUG_INBOUND_DROP_PERCENT

#undef NYOCI_DEBUG_OUTBOUND_DROP_PERCENT

#undef NYOCI_MAX_CASCADE_COUNT

#define NYOCI_MAX_CONTENT_LENGTH  50

#undef NYOCI_MAX_OBSERVERS

#undef NYOCI_MAX_PACKET_LENGTH

#undef NYOCI_MAX_PATH_LENGTH

#undef NYOCI_MAX_URI_LENGTH

#undef NYOCI_MAX_VHOSTS

#undef NYOCI_TRANSACTION_BURST_COUNT

#undef NYOCI_TRANSACTION_BURST_TIMEOUT_MAX

#undef NYOCI_TRANSACTION_BURST_TIMEOUT_MIN

#undef NYOCI_THREAD_SAFE

#undef NYOCI_NODE_ROUTER_USE_BTREE

#undef NYOCI_OBSERVATION_DEFAULT_MAX_AGE

#undef NYOCI_OBSERVATION_KEEPALIVE_INTERVAL

#undef NYOCI_OBSERVER_CON_EVENT_EXPIRATION

#undef NYOCI_OBSERVER_NON_EVENT_EXPIRATION

#undef NYOCI_TRANSACTIONS_USE_BTREE

#undef NYOCI_TRANSACTION_BURST_COUNT

#undef NYOCI_TRANSACTION_POOL_SIZE

#undef NYOCI_USE_CASCADE_COUNT

#undef NYOCI_VARIABLE_MAX_KEY_LENGTH

#undef NYOCI_VARIABLE_MAX_VALUE_LENGTH

#undef NYOCI_INTERNAL_EXTERN

#undef NYOCI_API_EXTERN

#undef NYOCI_DEPRECATED

這個配置文件主要用於配置NYOCI本身。反正看名字大概也能猜出大概每個配置變量是幹嘛用的,自己瞅一眼大概設設就是。要設置的話就把#undef改成#define然後設置值,不設置的話基本都會在nyoci-defaults.h文件裏有默認值。上面也是我目前根據我的環境設的值。

把這兩個配置文件創建好後放在自己的工程目錄下,然後扔進工程裏就好。

平臺移植

libnyoci和(網絡棧)平臺相關的代碼被組織在libnyoci\src\plat-net目錄下。目前,只支持posix和uip。

我們要爲wiz的io庫做一個移植,然後因爲我用的RTOS是uCOS-II,所以還會有些uCOS-II的相關代碼。首先,把uip那個文件夾複製然後黏貼,改個名字叫wiz。就像這樣:

然後我直接給出我目前移植好的文件:

nyoci-plat-net.h

#ifndef NYOCI_nyoci_plat_wiz_h
#define NYOCI_nyoci_plat_wiz_h

#if !defined(NYOCI_INCLUDED_FROM_LIBNYOCI_H) && !defined(BUILDING_LIBNYOCI)
#error "Do not include this header directly, include <libnyoci/libnyoci.h> instead"
#endif

NYOCI_BEGIN_C_DECLS

typedef uint8_t nyoci_addr_t[4];

typedef struct {
	nyoci_addr_t nyoci_addr;
	uint16_t nyoci_port;
} nyoci_sockaddr_t;

NYOCI_API_EXTERN uint8_t nyoci_plat_get_socketnumber(nyoci_t self);
NYOCI_API_EXTERN void nyoci_plat_set_socketnumber(nyoci_t self, uint8_t sn);

NYOCI_BEGIN_C_DECLS

#include "nyoci-plat-net-func.h"

#endif

nyoci-plat-net-internal.h

#ifndef NYOCI_nyoci_plat_wiz_internal_h
#define NYOCI_nyoci_plat_wiz_internal_h

#ifndef wiz_is_addr_mcast
#define wiz_is_addr_mcast(addrptr)		(*(const uint8_t*)(addrptr)==224)
#endif

#ifndef wiz_is_addr_unspecified
#define wiz_is_addr_unspecified(addrptr)		(*(addrptr)==0)
#endif

#ifndef wiz_is_addr_loopback
#define wiz_is_addr_loopback(addrptr)		(*(const uint8_t*)(addrptr)==127)
#endif

#define NYOCI_IS_ADDR_MULTICAST wiz_is_addr_mcast
#define NYOCI_IS_ADDR_UNSPECIFIED wiz_is_addr_unspecified
#define NYOCI_IS_ADDR_LOOPBACK wiz_is_addr_loopback

NYOCI_BEGIN_C_DECLS

struct nyoci_plat_s {
	nyoci_sockaddr_t	sockaddr_local;
	nyoci_sockaddr_t	sockaddr_remote;
	nyoci_session_type_t session_type;
  uint8_t socketNum;
	uint8_t outbound_packet_bytes[NYOCI_MAX_PACKET_LENGTH+1];
	uint8_t inbound_packet_bytes[NYOCI_MAX_PACKET_LENGTH+1];
};

NYOCI_END_C_DECLS

#endif

nyoci-plat-net.c

#if HAVE_CONFIG_H
#include <config.h>
#endif

#ifndef VERBOSE_DEBUG
#define VERBOSE_DEBUG 0
#endif

#ifndef DEBUG
#define DEBUG VERBOSE_DEBUG
#endif

#define NYOCI_COAP_MULTICAST_ALLDEVICES_ADDR COAP_MULTICAST_STR_ALLDEVICES

#include <stdio.h>
#include "assert-macros.h"

#include "libnyoci.h"
#include "nyoci-internal.h"
#include "nyoci-logging.h"

#include "lib_lite.h"
#include "Network.h"
#include "ucos_ii.h"
#include "os_cfg.h"
#include "socket.h"

uint8_t nyoci_plat_get_socketnumber(nyoci_t self) {
  return self->plat.socketNum;
};

void nyoci_plat_set_socketnumber(nyoci_t self, uint8_t sn){
  self->plat.socketNum = sn;
}

nyoci_status_t
nyoci_plat_join_standard_groups(nyoci_t self, int interface)
{
	NYOCI_SINGLETON_SELF_HOOK;
	// TODO: Implement me!
	return NYOCI_STATUS_NOT_IMPLEMENTED;
}

nyoci_t
nyoci_plat_init(nyoci_t self) {
	NYOCI_SINGLETON_SELF_HOOK;
	self->plat.socketNum = -1;
	return self;
}

nyoci_status_t
nyoci_plat_bind_to_port(
	nyoci_t self,
	nyoci_session_type_t type,
	uint16_t port
) {
	nyoci_status_t ret = NYOCI_STATUS_FAILURE;
	NYOCI_SINGLETON_SELF_HOOK;

	switch(type) {
	case NYOCI_SESSION_TYPE_UDP:
//#if NYOCI_DTLS
//	case NYOCI_SESSION_TYPE_DTLS:
//#endif
//#if NYOCI_TCP
//	case NYOCI_SESSION_TYPE_TCP:
//#endif
//#if NYOCI_TLS
//	case NYOCI_SESSION_TYPE_TLS:
//#endif
		break;

	default:
		ret = NYOCI_STATUS_NOT_IMPLEMENTED;
		// Unsupported session type.
		goto bail;
	}
	self->plat.session_type = type;
	self->plat.sockaddr_local.nyoci_port = port;
	ret = NYOCI_STATUS_OK;
bail:
	return ret;
}

void
nyoci_plat_finalize(nyoci_t self) {
	NYOCI_SINGLETON_SELF_HOOK;
	close(self->plat.socketNum);
}

nyoci_status_t
nyoci_plat_set_remote_hostname_and_port(const char* hostname, uint16_t port)
{
	nyoci_status_t ret;
	NYOCI_NON_RECURSIVE nyoci_sockaddr_t saddr;

	DEBUG_PRINTF("Outbound: Dest host [%s]:%d",hostname,port);

#if NYOCI_DTLS
	nyoci_plat_tls_set_remote_hostname(hostname);
#endif

	// Check to see if this host is a group we know about.
	if (strcasecmp(hostname, COAP_MULTICAST_STR_ALLDEVICES) == 0) {
		hostname = NYOCI_COAP_MULTICAST_ALLDEVICES_ADDR;
	}

	ret = nyoci_plat_lookup_hostname(hostname, &saddr, NYOCI_LOOKUP_HOSTNAME_FLAG_DEFAULT);
	require_noerr(ret, bail);

	saddr.nyoci_port = port;

	nyoci_plat_set_remote_sockaddr(&saddr);

bail:
	return ret;
}


void
nyoci_plat_set_remote_sockaddr(const nyoci_sockaddr_t* addr)
{
	nyoci_t const self = nyoci_get_current_instance();

	if (addr) {
		self->plat.sockaddr_remote = *addr;
	} else {
		memset(&self->plat.sockaddr_remote,0,sizeof(self->plat.sockaddr_remote));
	}
}

void
nyoci_plat_set_local_sockaddr(const nyoci_sockaddr_t* addr)
{
	nyoci_t const self = nyoci_get_current_instance();

	if (addr) {
		self->plat.sockaddr_local = *addr;
	} else {
		memset(&self->plat.sockaddr_local,0,sizeof(self->plat.sockaddr_local));
	}
}

void
nyoci_plat_set_session_type(nyoci_session_type_t type)
{
	nyoci_t const self = nyoci_get_current_instance();

	self->plat.session_type = type;
}


const nyoci_sockaddr_t*
nyoci_plat_get_remote_sockaddr(void)
{
	return &nyoci_get_current_instance()->plat.sockaddr_remote;
}

const nyoci_sockaddr_t*
nyoci_plat_get_local_sockaddr(void)
{
	return &nyoci_get_current_instance()->plat.sockaddr_local;
}

nyoci_session_type_t
nyoci_plat_get_session_type(void)
{
	return nyoci_get_current_instance()->plat.session_type;
}

uint16_t
nyoci_plat_get_port(nyoci_t self) {
	NYOCI_SINGLETON_SELF_HOOK;
	return self->plat.sockaddr_local.nyoci_port;
}

nyoci_status_t
nyoci_plat_outbound_start(nyoci_t self, uint8_t** data_ptr, coap_size_t *data_len)
{
	NYOCI_SINGLETON_SELF_HOOK;
	if (data_ptr) {
		*data_ptr = (uint8_t*)self->plat.outbound_packet_bytes;
	}
	if (data_len) {
		*data_len = sizeof(self->plat.outbound_packet_bytes);
	}
	self->outbound.packet = (struct coap_header_s*)self->plat.outbound_packet_bytes;
	return NYOCI_STATUS_OK;
}

// 
nyoci_status_t
nyoci_plat_outbound_finish(nyoci_t self,const uint8_t* data_ptr, coap_size_t data_len, int flags)
{
	nyoci_status_t ret = NYOCI_STATUS_FAILURE;
	int32_t sent_bytes = -1;
	int sn;
	NYOCI_SINGLETON_SELF_HOOK;
#if NYOCI_DTLS
	if (nyoci_plat_get_session_type() == NYOCI_SESSION_TYPE_DTLS) {
		ret = nyoci_plat_tls_outbound_packet_process(self, data_ptr, data_len);
	} else
#endif
	if (nyoci_plat_get_session_type() == NYOCI_SESSION_TYPE_UDP) {
		sn = nyoci_get_current_instance()->plat.socketNum;

		assert(sn < 8);

		require(data_len > 0, bail);

#if VERBOSE_DEBUG
		{
			char addr_str[30] = "???";
			uint16_t port = nyoci_plat_get_remote_sockaddr()->nyoci_port;
			IPToStr(nyoci_plat_get_remote_sockaddr()->nyoci_addr,addr_str);
			DEBUG_PRINTF("nyoci(%p): Outbound packet to [%s]:%u", self,addr_str,port);
			coap_dump_header(
				NYOCI_DEBUG_OUT_FILE,
				"Outbound:\t",
				(struct coap_header_s*)data_ptr,
				(coap_size_t)data_len
			);
		}
#endif

		sent_bytes = sendto(
			sn,
			data_ptr,
			data_len,
			self->plat.sockaddr_remote.nyoci_addr,
			self->plat.sockaddr_remote.nyoci_port
		);

		require_action_string(
			(sent_bytes >= 0),
			bail, ret = NYOCI_STATUS_ERRNO, "sendto() fail"
		);

		require_action_string(
			(sent_bytes == data_len),
			bail, ret = NYOCI_STATUS_FAILURE, "sendto() returned less than len"
		);

		ret = NYOCI_STATUS_OK;

	} else {
		ret = NYOCI_STATUS_NOT_IMPLEMENTED;
	}
bail:
	return ret;
}

// MARK: -

nyoci_status_t
nyoci_plat_wait(
	nyoci_t self, nyoci_cms_t cms
) {
#ifdef OS_uCOS_II_H
  OSTimeDly(cms * OS_TICKS_PER_SEC / MSEC_PER_SEC);
	return NYOCI_STATUS_OK;
#else
  return NYOCI_STATUS_NOT_IMPLEMENTED;
#endif
}

nyoci_status_t
nyoci_plat_process(nyoci_t self) {
	uint8_t sn;
	int32_t bytesCnt;
	nyoci_status_t ret = NYOCI_STATUS_FAILURE;
	NYOCI_SINGLETON_SELF_HOOK;
	require_action(self != NULL, bail, ret = NYOCI_STATUS_INVALID_ARGUMENT);
	sn = self->plat.socketNum;
	if(!Network_hasReady()){
#ifdef OS_uCOS_II_H
	  OSTimeDlyHMSM(0, 0, 3, 0);
#endif
		goto bail;
	}
	
	require(sn < 8,bail);
	
	if(getSn_SR(sn) != SOCK_UDP){
	  if(socket(sn,Sn_MR_UDP,self->plat.sockaddr_local.nyoci_port,0x00) != sn){
	    DEBUG_PRINTF("nyoci init fail");
#ifdef OS_uCOS_II_H
	    OSTimeDlyHMSM(0, 0, 3, 0);
#endif
	    goto bail;
	  }else{
	    DEBUG_PRINTF("nyoci process on socket %d", (int)sn);
	  }
	}
	if(getSn_RX_RSR(sn)>0){
	  bytesCnt = recvfrom(sn, self->plat.inbound_packet_bytes, sizeof(self->plat.inbound_packet_bytes), 
      self->plat.sockaddr_remote.nyoci_addr, &self->plat.sockaddr_remote.nyoci_port);
#if VERBOSE_DEBUG
		{
			char addr_str[30] = "???";
			uint16_t port = self->plat.sockaddr_remote.nyoci_port;
			IPToStr(self->plat.sockaddr_remote.nyoci_addr,addr_str);
			DEBUG_PRINTF("nyoci received data from %s:%u  len:%d", addr_str, port, (int)bytesCnt);
		}
#endif
    require(bytesCnt >= 0, bail);
	  nyoci_plat_set_session_type(NYOCI_SESSION_TYPE_UDP);
	  nyoci_inbound_packet_process(self, self->plat.inbound_packet_bytes, bytesCnt, 0);
	}
	nyoci_handle_timers(self);
	ret = NYOCI_STATUS_OK;

bail:
	nyoci_set_current_instance(NULL);
	self->is_responding = false;

	return ret;
}

nyoci_status_t
nyoci_plat_lookup_hostname(const char* hostname, nyoci_sockaddr_t* saddr, int flags)
{
/*	nyoci_status_t ret;
	memset(saddr, 0, sizeof(*saddr));

	ret = StrToIP(hostname,	(uint8_t *)&saddr->nyoci_addr) ? NYOCI_STATUS_OK : NYOCI_STATUS_HOST_LOOKUP_FAILURE;

#if NYOCI_CONF_USE_DNS
#if CONTIKI
	if(ret) {
		NYOCI_NON_RECURSIVE uip_ipaddr_t *temp = NULL;
		switch(resolv_lookup(hostname,&temp)) {
			case RESOLV_STATUS_CACHED:
				memcpy(&saddr->nyoci_addr, temp, sizeof(uip_ipaddr_t));
				ret = NYOCI_STATUS_OK;
				break;
			case RESOLV_STATUS_UNCACHED:
			case RESOLV_STATUS_EXPIRED:
				resolv_query(hostname);
			case RESOLV_STATUS_RESOLVING:
				ret = NYOCI_STATUS_WAIT_FOR_DNS;
				break;
			default:
			case RESOLV_STATUS_ERROR:
			case RESOLV_STATUS_NOT_FOUND:
				ret = NYOCI_STATUS_HOST_LOOKUP_FAILURE;
				break;
		}
	}
#else // CONTIKI
#warning NYOCI_CONF_USE_DNS was set, but no DNS lookup mechamism is known!
#endif
#endif // NYOCI_CONF_USE_DNS

	require_noerr(ret,bail);

bail:
	return ret; //*/
	
	// TODO: Implement me!
	return NYOCI_STATUS_NOT_IMPLEMENTED;
}

#if defined(CONTIKI)
nyoci_timestamp_t
nyoci_plat_cms_to_timestamp(
	nyoci_cms_t cms
) {
	return clock_time() + cms*CLOCK_SECOND/MSEC_PER_SEC;
}
nyoci_cms_t
nyoci_plat_timestamp_diff(nyoci_timestamp_t lhs, nyoci_timestamp_t rhs) {
	return (lhs - rhs)*MSEC_PER_SEC/CLOCK_SECOND;
}
nyoci_cms_t
nyoci_plat_timestamp_to_cms(nyoci_timestamp_t ts) {
	return nyoci_plat_timestamp_diff(ts, clock_time());
}

#elif defined(OS_uCOS_II_H)
nyoci_timestamp_t
nyoci_plat_cms_to_timestamp(
	nyoci_cms_t cms
) {
	return OSTimeGet() + cms * ((double)OS_TICKS_PER_SEC / MSEC_PER_SEC);
}
nyoci_cms_t
nyoci_plat_timestamp_diff(nyoci_timestamp_t lhs, nyoci_timestamp_t rhs) {
	return (lhs - rhs) * ((double)MSEC_PER_SEC / OS_TICKS_PER_SEC);
}
nyoci_cms_t
nyoci_plat_timestamp_to_cms(nyoci_timestamp_t ts) {
	return nyoci_plat_timestamp_diff(ts, OSTimeGet());
}
#endif

簡要說明

這裏大概說一下一些移植會出問題的地方。

首先看一下include
#include “lib_lite.h”
這個文件是我自己加的,用到的裏頭的函數就一個IPtoStr,在打印調試信息時用到的,不是很重要,自己可以根據需要實現一個或直接不管。
#include “Network.h”
這個也是我自己寫的基於W5500的網絡管理模塊的頭文件,主要就是提供nyoci_plat_process這個函數裏頭的Network_hasReady這一個判斷,自己根據需要進行修改即可。
#include “os_cfg.h”
這個文件是uCOS-II的配置文件名,用於引入uCOS-II接口的。

nyoci_plat_set_remote_hostname_and_port和nyoci_plat_lookup_hostname顧名思義,用於通過字符串設置和DNS查詢對方主機。這個不是必要的接口,我也沒有實現它,主要是io庫也沒有提供好用的DNS接口,但我保留了實現的框架,如有需要可以照着實現它。

這裏頭最容易讓人困惑的是時間戳裏的cms到底是什麼鬼。經過我辛苦的鑽研,得出結論:這個變量代表的意思是相對時間,單位ms。
nyoci中同時使用平臺特定的時間戳和單位爲ms的相對時間戳。
nyoci_plat_cms_to_timestamp
就是要你返回相對當前時間cms毫秒(正代表未來,負代表過去)對應的平臺時間戳。
nyoci_plat_timestamp_diff
返回兩個平臺時間戳之間差多少ms。
nyoci_plat_timestamp_to_cms
返回給定平臺時間戳與當前時間差多少ms。
nyoci_plat_wait
也就是sleep多少毫秒的意思。

上面我加進去了在uCOS-II下的實現,如果你用的是其他操作系統的話則根據具體OS進行修改。

使用示例

這裏只簡單示例下這個移植後的代碼的其中一種用法,其他用法請參照examples裏的幾個示例。

示例代碼

以下是Coap服務器任務/線程的示例

……
#include <libnyoci/libnyoci.h>
#include <libnyociextra/nyoci-node-router.h>
……
#define OBSERVABLE_KEY  35
static nyoci_t CoapInstance = NULL;
static struct nyoci_observable_s observable = { 0 };
static char sensordata[] = "30.32Cel";
……
static nyoci_status_t
request_handler(void* context)
{
  nyoci_observable_t observable = context;
  const uint8_t* opt;
  coap_size_t len;
  
  if (!nyoci_inbound_is_fake()) {
    printf("Got a request!\n");
  }

  // Only handle GET requests for now.
  if (nyoci_inbound_get_code() != COAP_METHOD_GET) {
    return NYOCI_STATUS_NOT_IMPLEMENTED;
  }
  
  // Begin describing the response.
  nyoci_outbound_begin_response(COAP_RESULT_205_CONTENT);
  
  // This is the key line to making a resource observable.
  // It must be placed after nyoci_outbound_begin_response()
  // and before nyoci_outbound_send(). When this resource changes,
  // a simple call to nyoci_observable_trigger() with the given
  // nyoci_observable object and observable key will trigger the
  // observers to be updated. Really --- that's it...!
  nyoci_observable_update(observable, OBSERVABLE_KEY);

  nyoci_outbound_add_option_uint(
    COAP_OPTION_CONTENT_TYPE,
    COAP_CONTENT_TYPE_TEXT_PLAIN
  );

  nyoci_outbound_append_content(sensordata, sizeof(sensordata) - 1);

  return nyoci_outbound_send();
}

static void CoapTask(void *p_arg){
  nyoci_node_t root_node, hello_node;

  NYOCI_LIBRARY_VERSION_CHECK();
  
  while(!CoapInstance){
    CoapInstance = nyoci_create();
    if (!CoapInstance) {
      printf("Unable to create LibNyoci instance");
      OSTimeDlyHMSM(0, 0, 5, 0);
    }
  }

  nyoci_plat_set_socketnumber(CoapInstance, COAP_SOCKET);
  nyoci_plat_bind_to_port(CoapInstance, NYOCI_SESSION_TYPE_UDP, COAP_DEFAULT_PORT);

  root_node = nyoci_node_init(NULL, NULL, NULL);

  nyoci_set_default_request_handler(
    CoapInstance,
    &nyoci_node_router_handler,
    (void*)root_node
  );

  hello_node = nyoci_node_init(NULL,root_node,"sData");
  hello_node->request_handler = &request_handler;
  hello_node->context = &observable;

  printf("Listening on port %d\n", nyoci_plat_get_port(CoapInstance));

  while (1) {
    MyOS_DlyHMSM(0, 0, 0, 30);
    //nyoci_plat_wait(CoapInstance, INTERVAL_PER_TICK);
    nyoci_plat_process(CoapInstance);
  }

  nyoci_release(CoapInstance);
}

如果要觸發publish的話,則在某處調用nyoci_observable_trigger:

if(++sensordata[1] > '9')
  sensordata[1] = '0';
(void)printf("%d observers registered\n", nyoci_observable_observer_count(&observable, OBSERVABLE_KEY));
nyoci_observable_trigger(&observable, OBSERVABLE_KEY ,0);

簡要說明

由於我這個示例使用的是多實例,所以一開始要create一個nyoci實例。
然後通過nyoci_plat_set_socketnumber設置其socket號,對應W5500的socket號。
然後用nyoci_plat_bind_to_port綁定這個Coap服務的端口爲默認端口號,使用UDP,這個移植也只實現了UDP。

這個代碼裏頭使用了router,這種方式使用鏈表(可選B樹)來管理資源,收到請求時會在所有註冊的資源中尋找匹配的資源,如果存在,則調用對應的request_handler,否則404。這裏我們添加了一個叫sData的資源,又給他設置了一個observable使其可以被訂閱。

然後就開始不斷地用nyoci_plat_process驅動服務器實例的運行了,這個方法的含義是進行一次無阻塞的處理。

還有些其他方式,請自行探索,或者等我研究透了再出篇博文來講。

測試

這裏我通過Chrome瀏覽器插件Copper來進行測試。
http://element-ui.cn/news_show_33612.shtml
我目前配置的設備主機名爲SIA5300.local,所以資源的地址爲:
SIA5300.local/sData
然後我們使用Get方法獲取資源:
獲取資源成功。
然後來試一下Observe即發佈訂閱方式:
主動推送消息成功。

總結

這篇文章講了移植libnyoci到wiz io庫以實現一個簡單的UDP Coap服務器所需要的主要事項,但是實際移植的時候由於開發環境不同等原因估摸還是會遇到很多問題,語法不支持、缺少庫什麼的一堆問題。如果實在搞不定,下面留言。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章