摘要

  本文将介绍云端链式闭环协同算法,该算法以TCP云/MQTT3.1.1协议的云端主题耦合思想为核心,建立在物联网云平台应用层的基础上,具有开关功能,是一种面向低算力场景下的设备间协同算法。云端耦合特性使该算法具有跨平台能力,该算法下又包含n重功能位校验算法与数据类型转换算法,能实现复杂指令轻量化自定义传输,利用异步通讯结合闭环反馈的思想能保证通讯数据可信情况下减少云端和本地双向资源占用,并使用软多线程保证通讯的稳定性。为提升该算法的应用范围及稳定性,本文还将探讨如何将传统设备接入本算法以及使用本地TCP Sever作为备用方案。
关键词: 设备协同算法;轻量化;异步闭环;传统设备接入及本地TCP Sever;跨平台

引言

  无线通讯技术的发展使“万物互联”成为可能,以华为的Openharmony开源物联网系统为代表,已经为支持运行操作系统的较高算力芯片,提供设备间协同解决方案。但是目前面向低算力低内存场景下的跨平台设备间协同解决方案依然很少,物联网云平台失效情况下的备用解决方案较少,并且如何将传统不搭载无线通讯模块的设备接入物联网待需解决。
因此作者针对低算力场景下的设备间协同,提出云端链式闭环协同算法概念,使用云端主题间的耦合进行数据传递,利用网络通信中的字符串数据流校验进行设备状态识别与控制,并且包含多种子算法。经测试该算法具有轻量化,使用简便,稳定性较高,数据可信性较强等特性,还可基于此探究跨平台硬件协同和设备硬件资源通过云端优势互补等方案设计。

云端链式闭环协同算法框架

算法框架解析

  该算法通过向另一设备订阅的主题1发布信息,另一设备通过本地解析该信息达到设备间协同的目的。
其中“静态信息”在本算法中指在本次协同过程中不会改变的量如联网IP信息,设备ID,WIFI信息等。
“动态信息”在本算法中指当前设备在协同过程中会动态变化的量,如所带负载的状态信息变化。
开关灯这样的BOOL量本算法可以轻易处理,若遇到像电机转速控制这样的连续的状态变化量,建议读者设置转速等级标志位,再通过功能位校验进行协同,或者通过定时器定时发送转速来达到异步闭环的目的。
同时两者均带有闭环反馈以保证数据的可信度。

算法扩展性解析

  图1使用设备1 作为带有显示器的主设备,接收设备2的状态信息。如果读者想要设备1,2均作为主设备互相显示状态信息和协同控制,只需利用轮换对称思想即可改进该框架。
使用该算法可以绕过软上位机,通过本地按键实现两台设备的互相控制和状态显示,一定程度上做到了设备间硬件资源的共享。
由于该算法的云端耦合特性,使用云端主题耦合进行数据传输,不需要本地有线通讯所以可以做到跨平台的设备协同。

算法的局限性

  云端链式闭环协同算法作为面向低算力芯片的,较为简易的协同算法。不建议将该算法用在实时性要求比较强的场景,开发者可用一台主设备协同多台从设备,但要控制同一时间内的并发数量,以免低算力芯片因内存溢出而软重启。
该算法是建立在物联网云平台上的,需要TCP云服务器具有订阅和发布主题功能,或者使用MQTT3.1.1及以上协议才可使用,基于无线通讯特性,该算法稳定性不足,无法用于工业控制。且无法传输图片和视频流,具有一定的局限性。仅仅是一套应用层上的设备间协同解决方案之一。

云端链式闭环协同算法下的关键子算法

  上文已经介绍了云端链式闭环协同算法的总体框架,下文将介绍该算法下的部分关键子算法的核心思想,还将以搭载了ESP8266-NodeMcu 1.0的物联网开发板为例,在Arduino IDE下使用支持MQTT协议的第三方"PubSubClient.cpp","PubSubClient.h"库[2],定义MQTT收发数据为String类型,以此为基础,提供部分子算法的示例程序。

轻量化子算法

复杂指令的轻量化传输N重位校验算法

  微处理器能处理的指令是有限的,可以先将复杂指令转换为约定好的“功能位”,通过已烧入的程序由微处理器进行功能位校验并可在中间穿插自定义参数实现复杂指令的轻量化自定义传输。

N重位校验算法核心代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
Int R,G,B;  //全局变量
//String类型的MQTT数据流转换为Char赋值给Str1首地址
strcpy(str1,Mqtt_Buff.c_str());
//截取字符串函数
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;
}
//定位功能位函数
int dw(char *scr,char dst,int b) //遍历scr数据流中的dst功能位字符 从b位开始遍历
{
char *p=scr;
char *q=dst;
int a,len=strlen(scr);
for(int w=b;w<(len-1);w++)
{
if(*(p+w)==dst) //检测到功能位
{a=w;break;} //锁定位置
}
return a;
}
if(str1[0] == 'R') //检测到某一复杂指令的功能位1
{
int a=dw(str1,'L',1),b=dw(str1,'D',2); //定位第2第3功能位并赋值给int值
substring(str2,str1,1,a-1); //截取功能位1的自定义参数1
R=atoi(str10); //转Int类型可用于赋值给对应操作函数
substring(str11,str1,a+1,b-1);
G=atoi(str4); //截取功能位2的自定义参数2
substring(str12,str1,b+1,strlen(str1)-1); //截取最后功能位的自定义参数3
B=atoi(str5);
}

状态扫描子算法

  通过软上位机或本地按键接收到协同指令后开启基于异步通讯的状态扫描算法,主设备开启扫描在带有一定冗余时间(2s的扫描冗余)内接收到了协同设备的在线信息则表示当前协同设备在线,进入下一个阶段的扫描,经验证该方法延迟不超过1s且资源占用低。

主设备实时扫描核心代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//主设备
OH.attach(1,oh2,12); //每隔1s扫描扫描时间12s冗余2s
client.publish(ttopic5, “O”); //向设备2发送开启协同指令
void oh2(int h)
{
if(s<h)
{s++;
if(b==1) //接收到了在线信息标志位b置1
{ Serial.print("m2on");s=0;b=0;z=1; } //扫描到了在线并将s置0重新扫描
}
else
{Serial.print("m2off"); s=0;b=0; z=0;} //显示离线并将s置0重新扫描
if(m!=1){OH.detach();} //检测到关闭协同指令标志位m不为1销毁进程
}

异步闭环通讯子算法

异步闭环的实现方法

  仅当设备2状态信息改变时才会向设备1发送数据流,设备1接收该数据流的同时立即向设备2发送收到的数据流,设备2再对该数据流同本地状态进行校验,若不一致则重新上传给设备1形成异步闭环。经检验,该方法能在资源占用较小的情况下做到数据的可信协同。

异步闭环的优势

  由于异步闭环通讯与其他数据指令在同一时间内同时向设备1发送概率较小,耦合可能性较低,故在设备协同时也可以正常接收软上位机或本地按键的其他控制指令。

软多线程技术

  作为面向低算力的设备协同算法,在硬件本身不支持多线程的情况下使用定时器/中断/时间戳来保护或调用并行线程是必要的,尤其是在网络不佳的情况下可有效避免进程滞塞。

防止进程滞塞

  使用该算法时物联网既要做本地控制又要设备协同还要保持和服务器的连接为有效防止进程滞塞,应该尽量避免使用for,delay等容易使设备陷入循环或等待从而影响其他进程的函数。可用毫秒级定时器定时自增的思想代替for循环也可用定时器或时间戳代替delay如:

1
2
3
long now = millis(); //获取当前时间戳
if (now - lastMsg > timeval) //超过该时间戳几毫秒后执行
.... //执行的操作

关键通讯线程保护

  在实际项目中,遇到网络不稳定的情况,如果短时间反复与服务器重新连接会导致其他线程卡死。所以设备通讯不畅时定时[3]重新与服务器连接至关重要。
核心代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//断线时
if(count1>=3)
{ //调用定时器每3s重连
count1=0; //清零以进行下一次计数
if (client.connect(ID_MQTT)) //重新连接mqtt

{
m=1; //连接成功标志位
Serial.println("connected");
client.subscribe(topic); //修改,修改为你的主题
} else {
Serial.println(" try again");
}
//创建间隔1s的计时器
rtd.attach(1, tickerCount1);
//定时计数子函数
void tickerCount1()
{
if(m!=1)
{
count1++;
}
}

Arduino下的线程回收方法

  当我们使用定时器调用的并行线程结束或者被关闭后,如果定时器还在持续计数,那么无疑会造成资源的浪费,在Arduino IDE下可以使用函数来销毁定时器[3]回收这部分内存减少低算力芯片的资源占用。(部分芯片不支持)
核心代码示例:

1
2
3
4
5
//接受到开启指令,通过毫秒级定时器调用函数
ticker.attach_ms(d3, HLED, n); //每d3毫秒调用一次函数HLED(int n)
//循环结束或收到关闭指令
if (count >= DX || DXC != 2) //当前线程循环结束或被关闭
{count=0;ticker.detach();} //计时清零,定时器销毁

传统设备接入本算法及本地TCP Sever多机位控制

  上文中的示例代码开发环境为Arduino IDE且开发板本身就搭载无线通讯模块,不具有普遍适用性。对于如何将传统不搭载无线通讯模块的微处理器,接入物联网M2M技术的方案依然较少,如何在云平台服务器崩溃的环境下提供一种备用的远程控制方案也是值得思考的问题。下文将以传统C52开发板配合ESP8266 01(S)wifi模块连接本地TCP Sever为例,探究如何通过本地TCP Sever进行多机位设备控制,以及如何使用TCP云平台的主题订阅和发布功能使传统设备接入本算法。

建立与无线通讯模块的有线串口通讯

  先按如图6所示,完成ESP8266无线通讯模块与C52的有线连接。

  C52的RXD(P3.0)连WiFi模块的TXD,TXD(P3.1)连RXD 才能实现设备间的有线串口通讯。连线完成后使用相同的波特率建立有线串口通讯[4]。本文使用T1定时器配合12Mhz晶振,串行口工作方式为1,波特率设置为4800。ESP8266模块可以直接使用固件中集成的AT指令设置波特率。
  STC89C52芯片串行口控制寄存器SCON中自带两个接受与发送标志位[5],RI和TI。
  通过发送与接收串口中断中的RI,TI标志位,使C52单片机能与ESP8266WiFi模块建立有线串口通讯。

本地TCP Server多机位控制

  与ESP8266WiFi模块建立有线串口通讯后,可利用其无线通讯特性再建立与服务器的连接。下文使用本地TCP Sever为例,本地TCP Sever可以作为物联网云平台崩溃的情况下,一种可靠的远程控制备用解决方案,且成本很低,对于稳定性要求较高的场景预留备用方案是必要的。
ESP8266WiFi模块可用固件自带的AT指令[6]接入TCP Sever:

1
2
3
4
5
6
//开启透明传输模式
AT+CIPMODE=1
//连接TCP
AT+CIPSTART=”TCP”,”服务器地址”,端口
//进入透传模式用于向服务器发送信息
AT+CIPSEND

  其他型号的wifi模块指令,可查阅官方固件文档,也可以反编译开源固件来自定义函数接口。

  本文使用网络调试助手建立的TCP Sever作为本地服务器,经过测试,使用上文中的方法可以实现同一局域网内的多机位控制如图8所示。

传统设备接入本算法

  上文介绍了一种传统设备通过与WiFi模块的有线通讯连接TCP的方法,本文介绍的云端链式闭环协同算法只需物联网云平台具有发布和订阅主题功能即可使用,部分物联网云平台通过后端开发,将TCP云提供了与MQTT协议类似的发布和订阅主题功能,这种情况可在建立与WIFI模块的有线通讯后,按照物联网云平台的TCP接入文档[7],订阅上主题后实现本算法功能,不用额外移植MQTT协议,使用串口中断定义unsigned char 类型逐位收发数据流,不需要C++编译器支持。
也可使用更为通用的MQTT协议接入本算法,由于MQTT协议需要第三方库支持,需要针对不同的硬件和编译平台进行移植[8]。

结论

  本文介绍了一种面向低算力场景下的轻量化设备间协同解决方案——云端链式闭环协同算法,该算法扩展性较强,使用方法简单,可以在目前物联网云平台广泛使用的TCP云或MQTT3.1.1协议进行适配,兼容性强,同时通过云端主题耦合可以支持跨平台的设备间协同与相互控制,实现硬件资源的优势互补,经实验验证该算法有效。并且本文以C52为例浅谈如何将传统设备接入本算法,以扩展本算法的应用范围。本文还介绍了一种通过本地TCP Sever的备用物联网多机位控制方案,以提升在云平台失效场景下的控制稳定性。

视频示例:

本文节选自本人论文未经授权禁止用于学术及商业用途!