IoT应用程序开发的模块化方法—第2部分:软件设计---凯利讯半导体

  物联网应用程序在硬件和软件组件之间实现了一种特别紧密的融合,要求开发人员在每个领域中考虑无数的细节。这个由两部分组成的系列文章介绍了一个使用模块化设计来促进聚合的平台。第1部分:硬件选项讨论了平台的硬件选项如何简化了物联网设备的实现。第2部分介绍了平台的软件体系结构及其在快速端对IoT应用程序的开发过程中的作用。

  端到端的物联网应用程序开发为工程师提供了从物联网硬件到云资源的广泛需求。开发人员发现自己处理大量的集成挑战,因为他们将不同的硬件和软件组件组合成一个功能系统。

  但是,对于三星ARTIK平台,开发人员可以利用统一的方法来更快地设计和部署复杂的IoT应用程序。

  对于IoT应用软件的开发,ARTIK平台是全面的。它融合了三星的两大软件环境——TizenRT和ARTIK云。在底层的IoT硬件层中,诸如三星ARTIK 053模块的ARTIK设备使用TizenRT,这是公司广泛使用的基于linux的Tizen操作系统(OS)的开源实时版本,用于智能手机、可穿戴设备和家庭娱乐系统。TizenRT是专门为物联网设备创建的,它的设计目的是在IoT设计的有限资源范围内进行操作。例如,开发人员可以调整TizenRT配置,将其映像占用压缩到仅40个Kbytes。

  对于云服务,三星的ARTIK是基于一种叫做三星架构的多模式交互(SAMI)的概念,它提供了一种框架,将基于云的机制抽象为基础数据结构和应用程序编程接口(API)。在实现SAMI概念的过程中,ARTIK云将云交互简化为一系列简单的API调用,使用了包括REST、WebSockets、MQTT和CoAP在内的各种协议。

  ARTIK模块化硬件和这个多层软件环境的结合导致了一个简化的物联网开发方法。如第1部分所述:硬件选择,ARTIK开发委员会为IoT终端节点和网关设计实现了完整的系统。因此,开发人员只需将一个ARTIK板插入到基于windows的开发系统中,就可以创建一个完整的物联网系统。反过来,软件工程师可以开始使用免费工具来实现代码,包括三星自己的ARTIK集成开发环境(IDE),GCC C/ c++编译器,或者OpenOCD芯片上的调试器和编程环境。

  完成与ARTIK云的连接同样简单。在ARTIK环境中,开发人员在名为设备清单的数据包中描述其硬件设备的属性。为了将他们的ARTIK硬件模块连接到云,他们只需将相应的设备清单加载到他们的ARTIK云仪表板中。反过来,他们获得了一个云API访问令牌。在这个简短的程序之后,软件开发人员可以很快开始使用ARTIK API开发他们的物联网应用程序。


  模块化的API

  三星将其ARTIK API组织为一组对应于不同硬件功能或中间件服务的模块。例如,单独的模块抽象了访问ADC、GPIO、PWM和其他硬件相关功能的低级交互。其他模块支持特定的无线协议,如wi - fi和蓝牙或云连接协议,如HTTP或MQTT与ARTIK云交互。

  为了使用许多可用的模块简化开发,API本身有助于消除运行时模块错误。在这里,API提供了一个请求函数,可以使开发人员不必跟踪模块位置或其负载状态。开发人员只是通过一个常见的名称(例如“wifi”)来请求模块,而不是在需要的环境中测试模块。例如,电话:

  artik_wifi_module *wifi =(artik_wifi_module *)artik_request_api_module(“wifi”);

  加载wifi模块并返回一个引用(* wifi)。稍后在代码中,当不再需要该模块时,开发人员可以通过调用来卸载模块:

  artik_release_api_module(wifi);

  每个模块反过来都构建在底层服务上,这些服务是基于硬件的,例如模拟数字转换器或基于协议的组件(如MQTT服务)。在模块中,向基于硬件的元素或基于协议的组件读取或写入数据的事务依赖于相关结构的集合。

  例如,开发人员使用一个简单的配置结构(清单1)来访问ADC操作的模块例程(清单2),比如访问ADC(ADC - >请求)和读取其采样值(ADC -> get_value())。

  typedef void * artik_adc_handle;

  typedef struct {

  int pin_num;

  char *名称;

  void * user_data;

  } artik_adc_config;

  typedef struct {

  artik_error请求(*)(artik_adc_handle *处理,

  artik_adc_config *配置);

  artik_error(*)(artik_adc_handle处理);

  artik_error(* get_value)(artik_adc_handle处理,int *值);

  } artik_adc_module;

  走读生artik_adc_module adc_module;

  清单1:ARTIK API将硬件(如ARTIK 053模块的ADC)抽象为API模块函数使用的一对结构(artik_adc_config和artik_adc_module)。(代码来源:凯利讯半导体)

  # include

  # include

  # include

  # include

  # include

  # include

  #定义CHECK_RET(x){if(x != S_OK)转到退出;}

  静态artik_adc_config = {0,“adc”,NULL};

  artik_error adc_test_value(空白)

  {

  artik_adc_handle处理;

  artik_error ret = S_OK;

  artik_adc_module *adc =(artik_adc_module *)artik_request_api_module(“adc”);

  int val = 1;

  int i = 0;

  流(stdout,“测试:% s \ n”,__func__);

  if(adc == INVALID_MODULE){

  fprintf(stderr,“TEST:% s -未请求模块\ n”,__func__);

  返回E_NOT_SUPPORTED;

  }

  ret = adc - >请求(锁、配置);

  如果(ret != S_OK){

  fprintf(stderr,“TEST:% s—未请求adc(err =%d)\ n”,__func__,ret);

  goto退出;

  }

  (我= 0;我< 5;我+ +){

  ret = adc - > get_value(句柄,val);

  如果(ret != S_OK){

  fprintf(stderr,“TEST:% s -未得到adc值(err =%d)\ n”,__func__,ret);

  goto退出;

  }

  流(stdout,“测试:% s - Value = % d \ n”,__func__,val);

  usleep(1000 * 1000);

  }

  adc - >发布(处理);

  退出:

  流(stdout,“测试:% s % s \ n”,__func__,(ret = = S_OK)?“成功”:“失败”);

  ret = artik_release_api_module(adc);

  如果(ret != S_OK){

  fprintf(stderr,“TEST:% s -未能释放模块\ n”,__func__);

  返回受潮湿腐烂;

  }

  返回S_OK;

  }

  int主要(空白)

  {

  artik_error ret = S_OK;

  ret = adc_test_value();

  CHECK_RET(ret);

  退出:

  返回(ret == S_OK)?0:1;

  清单2:ARTIK生态系统提供了大量的代码示例,如ADC测试例程,它演示了对ADC模块访问(ADC - >请求)的简单调用,读取数据(ADC -> get_value),并释放由关联结构(config)和实例(handle)表示的ADC通道(ADC - >发行版)。(代码来源:凯利讯半导体)

  对于异步操作,ARTIK架构依赖回调来提供完成服务。低级循环模块ARTIK API提供了服务管理回调功能设置超时,创建重复的回调,设置一个手表,和更多的(清单3),开发人员可以检查ARTIK示例代码演示了功能体系结构,说明了特定的设计模式操作,比如MQTT事务(清单4)。

  typedef struct {

  空白(*)(空白);

  空白(*辞职)(空白);

  artik_error(*add_timeout_callback)(int * timeout_id,unsigned int msec,

  timeout_callback func,void * user_data);

  artik_error(* remove_timeout_callback)(int timeout_id);

  artik_error(*add_periodic_callback)(int * periodic_id,unsigned int msec,

  periodic_callback func,void * user_data);

  artik_error(* remove_periodic_callback)(int periodic_id);

  artik_error(*add_fd_watch)(int fd,enum watch_io io,watch_callback func,void * user_data,int *watch_id);

  artik_error(* remove_fd_watch)(int watch_id);

  artik_error(*add_idle_callback)(int * idle_id,idle_callback func,void *user_data);

  artik_error(* remove_idle_callback)(int idle_id);

  } artik_loop_module;

  清单3:与设备和面向服务的模块一起,ARTIK API提供了一些实用模块,例如loop模块,它提供了用于管理复杂的回调服务的结构。(代码来源:凯利讯半导体)

  void on_publish(artik_mqtt_config * client_config,void * user_data,int result)

  {

  fprintf(stdout),“消息发布(% d)\ n”,结果);

  }

  。

  。

  。

  mqtt =(artik_mqtt_module *)artik_request_api_module(“mqtt”);

  循环=(artik_loop_module *)artik_request_api_module(“循环”);

  memset(&subscribe_msg 0 sizeof(artik_mqtt_msg));

  snprintf(sub_topic sizeof(sub_topic)/ v1.1 /行动/ % s,device_id);

  subscribe_msg。主题= sub_topic;

  subscribe_msg。qos = 0;

  memset(配置0 sizeof(artik_mqtt_config));

  配置。client_id =“sub_client”;

  配置。块= true;

  配置。user_name = device_id;

  配置。pwd =令牌;

  / * * / TLS配置

  memset(ssl 0 sizeof(artik_ssl_config));

  ssl。verify_cert = ARTIK_SSL_VERIFY_REQUIRED;

  ssl.ca_cert。data =(char *)akc_root_ca;

  ssl.ca_cert。len = strlen(akc_root_ca);

  配置。tls = ssl;

  / *连接到服务器* /

  mqtt - > create_client(客户端、配置);

  mqtt - > set_connect(客户、on_connect_subscribe &subscribe_msg);

  mqtt - > set_disconnect(mqtt客户机,on_disconnect);

  mqtt - > set_publish(mqtt客户机,on_publish);

  mqtt - > set_message(mqtt客户机,on_message_disconnect);

  api.artik mqtt - >连接(客户端。”云”,broker_port);

  循环- > run();

  artik_release_api_module(mqtt);

  artik_release_api_module(循环);

  。

  。

  。

  清单4:开发人员可以通过检查样例代码(如下面的代码片段)来获得关于ARTIK设计模式的经验,该代码片段演示了对异步操作(如MQTT事务)的回调使用(这里只显示了一个回调,on_publish)。(代码来源:凯利讯半导体)


  安全体系结构

  如清单4所示,每个API调用都需要一个有效的设备id和访问令牌(config)。user_name和配置。在清单4中,ARTIK平台部署了一个特别健壮的安全策略系列,它基于基于硬件的安全机制。

  在最基本的层次上,ARTIK安全构建在三星可信的执行环境(TEE)之上。三通是基于底层手臂TrustZone内置硬件架构的手臂®皮层®——处理器,比如那些用在三星ARTIK 5系列的硬件模块。在TrustZone模型中,可信的软件运行在一个单独的执行环境中。使用基于硬件的虚拟化机制,这种方法将需要访问通用软件应用程序的所有系统资源的可信软件隔离开来。

  在TEE分区中,三星将关键数据存储在基于闪存的安全存储系统中,或者在称为安全元素的硬件组件中。三星在每个ARTIK硬件模块中都构建了一个安全元素,为在不同的ARTIK硬件模块中安全地存储私钥提供了一致的防篡改机制。在应用程序级别,开发人员使用一个单独的安全API模块来访问这个安全分区及其底层硬件组件(图1)。


  三星ARTIK家族中模块的形象,利用ARM的TrustZone安全架构

IoT应用程序开发的模块化方法—第2部分:软件设计---凯利讯半导体

  图1:ARTIK家庭中的模块使用ARM的TrustZone安全架构,并提供基于硬件的安全机制,例如用于保护关键数据(如私有加***)的安全存储。(图片来源:凯利讯半导体)

  ARTIK平台建立在这些设备级的安全特性上,以确保ARTIK硬件模块和ARTIK云之间的端到端事务。在创建新设备时,开发人员可以生成并上载到用于验证该设备和其他类型的设备的证书颁发机构(CA)证书。在设备本身中,开发人员可以在硬件模块的安全元素中存储客户证书和私钥。当用户稍后注册该设备与ARTIK云时,ARTIK平台使用证书来识别授权的设备。在正常进行的操作中,ARTIK使用存储在安全存储中的私有客户端**来进行TLS(传输层安全)事务的相互身份验证(清单5)。

  int send_data_to_artik(int *数据)

  {

  sockfd int ret,我;

  bool重定向;

  结构指向sockaddr_in服务器;

  struct wget_s ws;

  struct http_client_tls_t *client_tls =(结构化http_client_tls_t *)malloc(sizeof(struct http_client_tls_t));

  struct http_client_ssl_config_t ssl_config;

  / *

  *设置ssl配置

  * /

  ssl_config。c_ca_crt_rsa root_ca =(char *);

  ssl_config。root_ca_len = sizeof(c_ca_crt_rsa);

  c_srv_crt_rsa ssl_config.dev_cert =(char *);

  ssl_config.dev_cert_len = sizeof(c_srv_crt_rsa);

  ssl_config。c_srv_key_rsa private_key =(char *);

  ssl_config。private_key_len = sizeof(c_srv_key_rsa);

  w_message char r_message[1024],[1024];

  char身体[1024];

  char body_main[512];

  char data_string[256];

  char要求[300],界面[50],body2[150];

  int req_len body_len;

  ws。缓冲= r_message;

  ws。buflen = 1024;

  {做

  ws。httpstatus = HTTPSTATUS_NONE;

  ws。抵消= 0;

  ws。datend = 0;

  ws。ndx = 0;

  。

  。

  。

  sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

  。

  。

  。

  / *

  *尝试连接服务器

  * /

  连接(sockfd,结构sockaddr *)和服务器,

  sizeof(struct指向sockaddr_in));

  如果(ret < 0){

  / *错误:连接失败* /

  ndbg(“错误:连接失败:% d\ n”,ret);

  免费(client_tls);

  返回错误;

  }

  client_tls - > client_fd = sockfd;

  。

  。

  。

  / *

  *装配TLS头,车身,页脚

  * /

  sprintf(要求,“HTTP / POST / v1.1 /消息1.1 \ r \ nhost:api.artik。云\ r\ ncontent - type:应用/ json\r\ n授权:承载% s\ ncontent - length:",config_artik_cloud_来人);

  sprintf(界面,“{ \“数据\”:{ ");

  sprintf(body2," } \“sdid \ ":\ " % s \”,\“类型\ ":\ "消息\“}”,CONFIG_ARTIK_CLOUD_DID);

  body_main[0]= ' \ 0 ';

  (我= 0;我< field_count;我+ +){

  if(i == field_count - 1)

  sprintf(data_string " \ " % s \”:% d”,field_name[我],[我])数据;

  其他的

  sprintf(data_string " \ " % s \”:% d”,field_name[我],[我])数据;

  strcat(body_main data_string);

  }

  / *

  检查body_main发送

  * /

  ndbg(“发送数据:% s\ n”,body_main);

  sprintf(身体,“% s%s%s % s”,body1,body_main,body2);

  / *

  *获取内容的长度

  * /

  req_len = strlen(要求的);

  body_len = strlen(身体);

  sprintf(w_message“% s % d \ r \ n \ r \ n % s”,要求,body_len,身体);

  ndbg(“send_message:% s \ n”,w_message);

  ret = mbedtls_ssl_write(&(client_tls-> tls_ssl),(unsigned char *)w_message,strlen(w_message));

  如果(ret < 0){

  / * * /错误发生

  ndbg(“错误:发送失败:% d\ n”,ret);

  goto errout;

  其他} {

  / * * /发送成功

  ndbg(“发送成功:发送% d字节\ n”,ret);

  }

  。

  。

  。

  清单5:这段代码演示了开发人员如何设置SSL(安全套接字层)配置、TLS(传输层安全)会话初始化和安全数据传输(mbedtls_ssl_write)。(代码来源:凯利讯半导体)

  如清单5所示,三星的软件示例演示了使用基于tls的通信流数据到云的关键设计模式。通过利用这些软件样本,开发人员可以快速构建应用程序,可以将ARTIK硬件模块的数据发送到ARTIK云,使用send_data_to_artik()这样的例程来隐藏相互身份验证和安全数据交换的细节(清单6)。

  artik_demo_run(int argc,char *argv[])

  {

  int受潮湿腐烂;

  int field_value[field_count];

  / *

  *初始化Artik网络

  * /

  wifiAutoConnect();

  而(1){

  printf("发送…”);

  / *

  *使用dm_conn_get_rssi api获取网络的Rssi

  * /

  ret = dm_conn_get_rssi(&field_value[0]);

  printf(" % s[% d]:% d /”,field_name[0],ret,field_value[0]);

  / *

  *使用dm_conn_get_tx_power api获取网络的Tx能量

  * /

  ret = dm_conn_get_tx_power(&field_value[1]);

  printf(" % s[% d]:% d /”,field_name[1],ret,field_value[1]);

  / *

  *使用dm_conn_get_channel api获取网络通道

  * /

  ret = dm_conn_get_channel(&field_value[2]);

  printf(" % s[% d]:% d \ n”,field_name[2],ret,field_value[2]);

  / *

  *发送数据到Artik云

  * /

  send_data_to_artik(field_value);

  睡眠(3);

  }

  }

  清单6:这个三星代码示例展示了流数据的基本设计模式(在本例中,使用清单5中所示的send_data_to_artik()例程来获取ARTIK云的信号强度指示器(RSSI)和发送器(Tx)功率读数)。(代码来源:凯利讯半导体)

  然而,对于某些IoT应用程序来说,即使是使用ARTIK平台的IoT设备部署的相对容易程度也会被证明是有限的。例如,云应用程序开发人员经常发现他们自己在等待IoT设备部署和驱动应用程序所需的数据流的生成。

  软件开发人员通常会构建测试数据集来执行应用程序,而不是等待,但是构建现实测试数据流的任务可能会与应用程序本身的开发相匹敌。但是,在ARTIK云中,开发人员可以更容易地开发他们的高层基于云的应用程序,在他们拥有最终的IoT硬件的生产数据之前。


  模拟数据流

  三星提供了一个基于java的命令行设备模拟器,它可以生成测试数据并向ARTIK云传输正确格式化的数据包。在交互模式下使用模拟器,开发人员可以执行基本命令以获得与ARTIK云交互的经验。然而,在其自动模式中,设备模拟器可以生成随机数据或提供一个模板,开发人员可以使用自己的测试数据完成这个模板。例如,在启动命令行设备模拟器之后,开发人员可以为带有设备ID“device_id”的特定设备创建一个名为“场景”的测试包:

  创建场景device_id mytest

  这将创建一个包含基本模板的JSON文件(清单7)。

  {

  :“sdid device_id”,

  “deviceToken”:“”,

  “数据”:{ },

  “配置”:{ },

  “api”:“文章”,

  “期”:1000

  }

  清单7:使用ARTIK基于java的命令行设备模拟器,开发人员可以自动生成一个正确格式化的模板,以便将数据发送到ARTIK云。(代码来源:凯利讯半导体)

  开发人员可以用JSON格式填写数据块,并使用适当的键/值对来完成测试包(清单8)。

  "数据":{

  “someNumber”:0,

  “someText”:“”,

  心率:0,

  “stepCount”:0

  “randomWord”:“”,

  “内容”:“恒定的东西”

  }

  清单8:开发人员可以通过提供具有键值的数据块来扩展基本的模拟生成的数据模板:值对适合他们的应用程序。(代码来源:凯利讯导体)

  配置块(清单7中的配置)定义了模拟器如何修改模板中的数据块,从而将一系列数据消息发送给ARTIK云。

  例如,清单9中所示的配置块指定,someNumber应该按顺序从1到10循环,而其他值应该以指定的方式进行更改。使用设备模拟器和这个简单的数据生成器,开发人员可以在开发过程的早期测试基于实际数据流的基于云的应用程序。

  ...."配置":{

  “someNumber”:{“功能”:“周期”、“类型”:“整数”、“价值”:[1,2,3,4,5,6,7,8,9,10)},

  “someText”:{“功能”:“周期”、“类型”:“弦”、“价值”:[“文本”、“css”、“json)},

  心率:{“功能”:“随机”、“类型”:“整数”,“最小值”:0,“马克斯”:200 },

  “stepCount”:{“功能”:“增量”,“类型”:“整数”,“最小值”:1000年,“马克斯”:10000年,“增量”:2,“时期”:5000 },

  “randomWord”:{“功能”:“随机”、“类型”:“字符串”}

  } ....

  清单9:通过在simulator生成的模板中添加配置块配置,开发人员可以使用设备模拟器将一系列数据包发送到ARTIK云,从而使开发人员能够在实际的物联网设备硬件可用之前,在实际数据的基础上测试基于云的应用程序。


  对于开发人员来说,ARTIK平台简化了在开发和最终部署过程中安全地向云交付数据的任务。然而,对于任何IoT应用程序,关键的区别在于开发人员能够通过基于云的处理、分析、预测机制等来增加数据的价值。为了支持云级别的IoT应用程序,ARTIK云对其丰富的服务集进行了补充,这些服务包括为任何高吞吐量数据驱动的应用程序和IoT应用程序设计的大量第三方服务。

  在他们构建了基于云的应用程序之后,开发人员甚至可以将他们的ARTIK数据流绑定到构建在其他云平台上的现有应用程序上。在这里,ARTIK云连接器定义了一组用于在云平台之间传输数据的服务,允许开发人员将ARTIK云数据流连接到第三方服务,如Amazon Web service Kinesis,用于在高吞吐量数据流上执行复杂的分析。


  结论

  物联网应用可以为连接和云服务提供广泛多样的需求。过去,IoT的开发人员几乎没有选择的余地,只能从不同的硬件平台和云服务中集成软件,推迟最终应用程序本身的进度。相比之下,三星的ARTIK生态系统将多个硬件模块(参见第1部分:硬件选择)、软件库和云服务组合成一个统一的平台。

  通过使用这个平台,开发人员可以构建复杂的应用程序,构建基于一致的硬件特性和软件功能,以满足日益复杂的物联网需求。