阿里云IOT-C-SDK系列(2)快速体验:移植+示例C代码

阿里云IOT-C-SDK系列(1)概述:移植流程、程序框架、代码目录

 在上一篇文章我们分析了C-SDK的移植流程一级程序框架, 为了快速的体验一下这个移植流程,本文按照上篇文章提供2 种移植方式分别举例如何移植。官方的快速 移植案例为:Quick_start, 我对移植案例进行整理和修改,为了快速的体验,我们使用ubuntu来运行示例程序,原因是SDK中已经提供了HAL_XXX接口函数的实现。

   移植环境:

   SDK版本:v3.0.1

   程序运行环境:ubuntu18.04.2    64bit

   物联网设备类型:高级版+物模型,物模型使用SDK提供的 \src\dev_model\examples\model_for_examples.json, 具体如何使用,物联网的文档中有详细的说明。

  在阿里云物联网上创建的自己设备四元组(具体内容做了隐藏):

{
  "ProductKey": "xxxxxxxxxxx",                        //隐藏
  "ProductSecret":"xxxxxxxxxxxxxxxx"                 //隐藏
  "DeviceName": "20190810001",                  
  "DeviceSecret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"  //隐藏
}

一、使用SDK自带的编译系统进行移植

1、修改源码中 wrappers/os/ubuntu/HAL_OS_linux.c  

  由于我们选择的是物联网高级版+物模型,所以将我们在阿里云上创建的产品设备的四元组信息填到上图的位置,这里可能会有一个疑惑:难不成以后每次 移植都要这么改,那岂不是很不灵活,答案:否,SDK提供了设备四元组设定的API函数,这里只是为了快速的体验,因为SDK的编译系统会默认编译该文件和 linkkit_example_solo.c  示例代码。

2、在源码目录下,执行 make  menuconfig。

    注意必须是Ubuntu 16 版本以上。如果版本不够,可以在Windows下执行相应的脚本,执行该命令的目的是 选择 我们要使用的 SDK中的 功能,毕竟SDK支持的功能很多,我们不需要全都选择。如下图所示:

   SDK中默认就是物联网设备 高级版+物模型,所以这里其实不用做任何修改,直接save就可以了,然后就会在目录下生成make.setting,其中罗列了我们选择的功能。这个make.setting尽量不要自己编写,还是用make menuconfig比较方便,也不容易出错,make.setting不仅仅是为了让我们检查我们选择了那些功能,它更是 “提取脚本extract.sh”的输入文件,提取脚本根据make.setting中选择的内容,提取对应功能的源码,输出到output的eng下。

3、执行 make 命令。

    SDK自带的编译系统(gcc)会对整个源码进行编译,编译完成后,会显示编译了哪些文件,选择了哪些功能,以及各功能的ROM、RAM等所需大小,如下图所示:

make 指令还会自动运行extract.sh ,将我们选择的功能对应的源码抽取到 output/eng目录下,所以 执行make 后会有下面几个结果:

(1)编译 我们选择的功能对应的代码。

(2)执行extract.sh,将我们所选的功能的相关代码抽取到output目录 下。

(3)生成库文件 output/release/lib ,对于我们有用的就是

          libiot_sda.a  -- iot 核心软件功能静态库。

          libiot_hal.a   -- iot HAL层 静态库,可以理解为底层硬件驱动接口驱动,这里使用的默认Ubuntu对应的HAL案例。

          libiot_tls.a    --  iot TLS静态库 ,这是一个SDK使用的安全加密库,对于用户来讲,目前没什么用。

(4)生成这些库的头文件,在output/release/include, 这个是使用库文件的必备,必须要将这些头文件+库文件放到我们的工程项目中,我们才能使用SDK 提供的API接口。

(5)生成 示例代码对应的 可执行文件,在output/release/bin 下,这里我们 主要是运行 linkkit-example-solo,因为它是 SDK中提供的 物模型 案例 linkkit_example_solo.c 对应的可执行代码。

(6)抽取我们所选功能对应的源码 到 output/eng下,抽取出的代码更加的间接,毕竟把那些我们不需要的功能都去掉了。

4、执行    linkkit-example-solo 。

   注意,要确保我们的ubuntu 环境能够联网,然后这个时候我们再来看我们在物联网平台上创建的设备状态:

     说明示例程序上云成功,并且能够向物联网平台发送数据。

二、将所需功能的源码抽取,自己编译进行移植。

    移植方式一看起来很简单,也很流畅,好像就没必要自己去抽取源码,然后再加入到自己的工程项目中,再编译了,其实不是的,方式一之所以简单的原因是因为我们的运行环境是ubuntu,我们直接采用了SDK提供的HAL_xxx函数,不需要自己去编写HAL接口层,所以才会那么简单的,但是更多的时候,我们的设备硬件,尤其是ARM设备,HAL层的接口是需要我们自己去实现的。正如《阿里云IOT-C-SDK系列(1)概述:移植流程、程序框架、代码目录》所说,还是最好自己进行功能源码,然后进行编译,这样不仅代码更加简洁,我们也能深入的理解SDK的使用。详细步骤如下:

1、仍然是执行make menuconfig或者执行 config.bat (Windows),选择所需功能。

2、执行 extract.sh 脚本 或者 extract.bat,进行代码抽取。

3、步骤2后,会在当前的目录下,生成output文件夹。

output目录如下:

这里我们可以看到,抽取的功能源码,更加的简洁了,而且在wrappers文件夹下生成了 wrapper.c文件,该文件内容如下:

/**
 * NOTE:
 *
 * HAL_TCP_xxx API reference implementation: wrappers/os/ubuntu/HAL_TCP_linux.c
 *
 */
#include "infra_types.h"
#include "infra_defs.h"
#include "wrappers_defs.h"
#include "stdarg.h"

/**
 * @brief Deallocate memory block
 *
 * @param[in] ptr @n Pointer to a memory block previously allocated with platform_malloc.
 * @return None.
 * @see None.
 * @note None.
 */
void HAL_Free(void *ptr)
{
	return;
}


/**
 * @brief Get device name from user's system persistent storage
 *
 * @param [ou] device_name: array to store device name, max length is IOTX_DEVICE_NAME_LEN
 * @return the actual length of device name
 */
int HAL_GetDeviceName(char device_name[IOTX_DEVICE_NAME_LEN + 1])
{
	return (int)1;
}


/**
 * @brief Get device secret from user's system persistent storage
 *
 * @param [ou] device_secret: array to store device secret, max length is IOTX_DEVICE_SECRET_LEN
 * @return the actual length of device secret
 */
int HAL_GetDeviceSecret(char device_secret[IOTX_DEVICE_SECRET_LEN + 1])
{
	return (int)1;
}


/**
 * @brief Get firmware version
 *
 * @param [ou] version: array to store firmware version, max length is IOTX_FIRMWARE_VER_LEN
 * @return the actual length of firmware version
 */
int HAL_GetFirmwareVersion(char *version)
{
	return (int)1;
}


/**
 * @brief Get product key from user's system persistent storage
 *
 * @param [ou] product_key: array to store product key, max length is IOTX_PRODUCT_KEY_LEN
 * @return  the actual length of product key
 */
int HAL_GetProductKey(char product_key[IOTX_PRODUCT_KEY_LEN + 1])
{
	return (int)1;
}


int HAL_GetProductSecret(char product_secret[IOTX_PRODUCT_SECRET_LEN + 1])
{
	return (int)1;
}


/**
 * @brief Allocates a block of size bytes of memory, returning a pointer to the beginning of the block.
 *
 * @param [in] size @n specify block size in bytes.
 * @return A pointer to the beginning of the block.
 * @see None.
 * @note Block value is indeterminate.
 */
void *HAL_Malloc(uint32_t size)
{
	return (void*)1;
}


/**
 * @brief Create a mutex.
 *
 * @retval NULL : Initialize mutex failed.
 * @retval NOT_NULL : The mutex handle.
 * @see None.
 * @note None.
 */
void *HAL_MutexCreate(void)
{
	return (void*)1;
}


/**
 * @brief Destroy the specified mutex object, it will release related resource.
 *
 * @param [in] mutex @n The specified mutex.
 * @return None.
 * @see None.
 * @note None.
 */
void HAL_MutexDestroy(void *mutex)
{
	return;
}


/**
 * @brief Waits until the specified mutex is in the signaled state.
 *
 * @param [in] mutex @n the specified mutex.
 * @return None.
 * @see None.
 * @note None.
 */
void HAL_MutexLock(void *mutex)
{
	return;
}


/**
 * @brief Releases ownership of the specified mutex object..
 *
 * @param [in] mutex @n the specified mutex.
 * @return None.
 * @see None.
 * @note None.
 */
void HAL_MutexUnlock(void *mutex)
{
	return;
}


/**
 * @brief Writes formatted data to stream.
 *
 * @param [in] fmt: @n String that contains the text to be written, it can optionally contain embedded format specifiers
     that specifies how subsequent arguments are converted for output.
 * @param [in] ...: @n the variable argument list, for formatted and inserted in the resulting string replacing their respective specifiers.
 * @return None.
 * @see None.
 * @note None.
 */
void HAL_Printf(const char *fmt, ...)
{
	return;
}


uint32_t HAL_Random(uint32_t region)
{
	return (uint32_t)1;
}


/**
 * @brief Sleep thread itself.
 *
 * @param [in] ms @n the time interval for which execution is to be suspended, in milliseconds.
 * @return None.
 * @see None.
 * @note None.
 */
void HAL_SleepMs(uint32_t ms)
{
	return;
}


/**
 * @brief Writes formatted data to string.
 *
 * @param [out] str: @n String that holds written text.
 * @param [in] len: @n Maximum length of character will be written
 * @param [in] fmt: @n Format that contains the text to be written, it can optionally contain embedded format specifiers
     that specifies how subsequent arguments are converted for output.
 * @param [in] ...: @n the variable argument list, for formatted and inserted in the resulting string replacing their respective specifiers.
 * @return bytes of character successfully written into string.
 * @see None.
 * @note None.
 */
int HAL_Snprintf(char *str, const int len, const char *fmt, ...)
{
	return (int)1;
}


void HAL_Srandom(uint32_t seed)
{
	return;
}


/**
 * @brief Destroy the specific TCP connection.
 *
 * @param [in] fd: @n Specify the TCP connection by handle.
 *
 * @return The result of destroy TCP connection.
 * @retval < 0 : Fail.
 * @retval   0 : Success.
 */
    int HAL_TCP_Destroy(uintptr_t fd)
{
	return (int)1;
}


/**
 * @brief Establish a TCP connection.
 *
 * @param [in] host: @n Specify the hostname(IP) of the TCP server
 * @param [in] port: @n Specify the TCP port of TCP server
 *
 * @return The handle of TCP connection.
   @retval   0 : Fail.
   @retval > 0 : Success, the value is handle of this TCP connection.
 */
    uintptr_t HAL_TCP_Establish(const char *host, uint16_t port)
{
	return (uintptr_t)1;
}


/**
 * @brief Read data from the specific TCP connection with timeout parameter.
 *        The API will return immediately if 'len' be received from the specific TCP connection.
 *
 * @param [in] fd @n A descriptor identifying a TCP connection.
 * @param [out] buf @n A pointer to a buffer to receive incoming data.
 * @param [out] len @n The length, in bytes, of the data pointed to by the 'buf' parameter.
 * @param [in] timeout_ms @n Specify the timeout value in millisecond. In other words, the API block 'timeout_ms' millisecond maximumly.
 *
 * @retval       -2 : TCP connection error occur.
 * @retval       -1 : TCP connection be closed by remote server.
 * @retval        0 : No any data be received in 'timeout_ms' timeout period.
 * @retval (0, len] : The total number of bytes be received in 'timeout_ms' timeout period.

 * @see None.
 */
    int32_t HAL_TCP_Read(uintptr_t fd, char *buf, uint32_t len, uint32_t timeout_ms)
{
	return (int32_t)1;
}


/**
 * @brief Write data into the specific TCP connection.
 *        The API will return immediately if 'len' be written into the specific TCP connection.
 *
 * @param [in] fd @n A descriptor identifying a connection.
 * @param [in] buf @n A pointer to a buffer containing the data to be transmitted.
 * @param [in] len @n The length, in bytes, of the data pointed to by the 'buf' parameter.
 * @param [in] timeout_ms @n Specify the timeout value in millisecond. In other words, the API block 'timeout_ms' millisecond maximumly.
 *
 * @retval      < 0 : TCP connection error occur..
 * @retval        0 : No any data be write into the TCP connection in 'timeout_ms' timeout period.
 * @retval (0, len] : The total number of bytes be written in 'timeout_ms' timeout period.

 * @see None.
 */
    int32_t HAL_TCP_Write(uintptr_t fd, const char *buf, uint32_t len, uint32_t timeout_ms)
{
	return (int32_t)1;
}


/**
 * @brief Retrieves the number of milliseconds that have elapsed since the system was boot.
 *
 * @return the number of milliseconds.
 * @see None.
 * @note None.
 */
uint64_t HAL_UptimeMs(void)
{
	return (uint64_t)1;
}


int HAL_Vsnprintf(char *str, const int len, const char *format, va_list ap)
{
	return (int)1;
}

其实就是前面一直说的HAL层接口函数,我们只需要自己去实现这些函数就可以了,看到这里,可能会觉得有点头大,这么多函数,自己去实现?岂不是很麻烦,而且容易出错啊。答案是,麻烦是麻烦了点,但是我们可以借鉴SDK中提供的HAL接口案例,比如:

① 开发环境是ARM+linux,那么我们就可以直接照抄源码中 wrappers/os/unbuntu/HAL_XXX中函数的实现。

② 开发环境是 MCU+模组(支持TCP),我们就可以参考 wrappers/atm/at_tcp/sim800.c中的函数实现。

③ 开发环境是 MCU+模组(支持mqtt),我们就可以参考 wrappers/atm/at_mqtt/mqtt_sim800.c

④ 开发环境是 MCU+wifi模组(支持TCP),可以参考wrappers/atm/at_tcp/mk3060.c

⑤ 开发环境是 裸机MCU + 模组,参考 wappers/os/nos/HAL_Nos.c

4.抽取目录中 Makefile的 操作。

   在抽取代码目录output中,我们发现还有一个 Makefile ,执行其会自动生成静态库 libiot_sda.a 和 libiot_hal.a,这里libiot_sda.a是SDK的核心软件功能库,我们是可以直接使用的,而libiot_hal.a是 HAL层的库,从上面的代码我们可以看出来,如果我们不去实现HAL接口函数,那么这个库即便是生成也是没有意义的,因为里面都是空返回,所以我们需要先实现HAL接口函数,然后再进行make,生成的库才能用。这种方式可以减少开发时间,毕竟自己写Makefile也是需要花点时间的。那么有个小问题来了,我们要不要使用这个Makefile呢, 个人建议是,如果我们自己Makefie不是特别熟练的时候,可以先用这个Makefile生成库,然后使用这个库进行开发工程项目,这样我们只需要编写自己的业务逻辑的Makefile部分就好了,只不过在Makefile中引用这个库就好了。而如果我们对自己编写Makefile能力比较自信,还是尽可能自己去写,这样虽然多花一点时间,能对整个工程项目理解的更加深刻。

5、将抽取出的 代码(除了example中的示例)添加到我们自己的工程文件中,然后编写自己的业务逻辑、Makefile, 进行进行make。

    这里如果仍然使用linkkit_example_solo.c,我们需要 实现 

int HAL_SetProductKey(char *product_key);
int HAL_SetDeviceName(char *device_name);
int HAL_SetProductSecret(char *product_secret);
int HAL_SetDeviceSecret(char *device_secret);

  也就是设置设备四元组函数,而且在linkkit_example_solo.c的main函数中调用这4个函数,写入测试设备的四元组参数, 只有这样编译生成的可执行程序才能执行成功。

发布了247 篇原创文章 · 获赞 257 · 访问量 62万+

猜你喜欢

转载自blog.csdn.net/u012351051/article/details/99049614