在本文中,将分享使用LinkIt ONE板作为远程传感器设备的经历。在此之前,强烈建议先阅读之前的文章,因为本文的许多内容都基于之前讨论过的概念。
本项目的源代码托管在GitHub上的仓库中。
作为一个不喜欢C/C++语言,并且没有相关经验的人,更喜欢快速应用开发环境。即使是基于.NET Micro Framework的Netduino,也依赖于Visual Studio这样的优秀IDE。.NET库和出色的调试器使得Netduino板成为使用过的最好的开发板。
每次面对C/C++程序,都会感到沮丧。对于这个项目,不得不将原始的Netduino C#源代码“翻译”成Arduino/C/C++的“工作”草图。之所以称之为“工作”,是因为目标只是让板子工作,而不是编写一个体面的程序。
为什么“沮丧”?因为认为在超冗长的C/C++项目上浪费时间是没有意义的,还不如简单地编写像C#或Java这样的高级语言。LinkIt ONE板甚至比Netduino Plus 2更快,资源也更丰富。它不仅仅是一个8位AVR。
那么,为什么LinkIt ONE板应该是“了不起”的呢?首先,是因为硬件。对于业余爱好者来说,这块板子是一个小杰作,因为它功能齐全,而且价格也相当合理。
其次,认为Arduino API的方法简化了像这样懒惰的程序员的工作。坦率地说,API覆盖了板子的许多特性,但希望能有更好的覆盖。印象是,它更像是一个薄薄的层,包裹着板子上运行的操作系统。这很遗憾,因为用户可能很早就会感受到一些限制。
一个明显的例子是HTTP支持,这是不存在的。一个明确声称是“用于原型设计可穿戴设备和物联网设备”的板子,至少应该提供典型的物联网项目使用的最常见服务。
个人发现,在互联网上寻找关于非常基本服务的开源项目,如JSON操作、HTTP客户端等,是令人烦恼的。幸运的是,Arduino世界仍然很大,有很多用户贡献他们自己的作品。
正如前面提到的,除了JSON和HTTP库之外,几乎所有的东西都是从原始的C#源代码中“克隆”出来的。这是因为中的许多人,也许是C/C++技能娴熟的人,看到是如何编写移植源代码的,可能会微笑(大笑)。
JSON库来自Benoît Blanchon,他做得非常出色。在看来,这个库写得很好,但应该记住,Benoît的目标是“真正的”Arduino,资源非常有限(以及语言特性)。
也就是说,LinkIt板是一个32位ARM核心,因此内存使用量远远高于库中描述的。此外,Arduino默认平台没有“new”和“delete”,而JSON库添加了它自己的“new”运算符重载。然而,这将与LinkIt SDK发生冲突,SDK是一个C/C++特性平台,这样的运算符是完全支持的。
顺便说一下,这块板子有4MB的RAM。分配一些KB的缓存远不是问题!对于HTTP客户端,选择了这个库。这是一个极简的HTTP组合器和解析器,但对于任何业余项目所需的都足够了。它还带有基本认证。
首先,TCP连接是通过Wi-Fi建立的,因为LinkIt ONE没有以太网插口。然而,这是一个非常简单的任务,因为API/样本非常清楚地说明了如何设置无线连接。
另一个区别是GPS传感器监控,在Netduino中是缺失的,但在标准的LinkIt ONE包中是包含的。尽管主要出于好玩而使用它,但相信如果需要跟踪设备的移动位置,它会是有用的。
再次,设置起来非常简单。
/*
* 硬件输入端口定义
**/
InputPortWrapper* _switch0;
InputPortWrapper* _switch1;
AnalogInputWrapper* _analog0;
AnalogInputWrapper* _analog1;
IInput* _inputPorts[16];
int _portCount;
#define LED 13
#define WIFINAME "(your wi-fi name)"
#define WIFIPWD "(your wi-fi password)"
int _wifiStatus;
MobileServiceClient* _ms;
gpsSentenceInfoStruct _gpsinfo;
void setup()
{
/*
* 硬件输入端口定义
**/
_switch0 = new InputPortWrapper("Switch0", 0);
_switch1 = new InputPortWrapper("Switch1", 1);
_analog0 = new AnalogInputWrapper("Analog0", 0, 100.0, 0.0);
_analog1 = new AnalogInputWrapper("Analog1", 1, 100.0, 0.0);
_inputPorts[0] = _switch0;
_inputPorts[1] = _switch1;
_inputPorts[2] = new RampGenerator("Ramp20min", 1200, 100, 0);
_inputPorts[3] = new RampGenerator("Ramp30min", 1800, 150, 50);
_inputPorts[4] = _analog0;
_inputPorts[5] = _analog1;
_portCount = 6;
pinMode(LED, OUTPUT);
LGPS.powerOn();
LWiFi.begin();
_wifiStatus = LWiFi.connectWPA(WIFINAME, WIFIPWD);
_ms = new MobileServiceClient("(your service name).azure-mobile.net", "(your application-id)", "(your master key)");
}
void loop()
{
if (_wifiStatus >= 0)
{
bool hasChanged = false;
for (int i = 0; i < _portCount; i++)
{
if (_inputPorts[i]->sample())
{
hasChanged = true;
}
}
if (hasChanged)
{
StaticJsonBuffer<4096> jsonBuffer;
LGPS.getData(&_gpsinfo);
JsonObject& jobj = jsonBuffer.createObject();
jobj["devId"] = "01234567";
jobj["ver"] = 987654321;
jobj["pos"] = (char*)_gpsinfo.GPGGA;
JsonArray& jdata = jobj.createNestedArray("data");
for (int i = 0; i < _portCount; i++)
{
IInput* port;
if ((port = _inputPorts[i])->getHasChanged())
{
port->serialize(&jdata);
}
}
_ms->apiOperation("myapi", REST_Create, &jobj);
}
digitalWrite(LED, digitalRead(LED) == 0);
delay(1000);
}
}