设备自身的CPU温度、电源输入电压、内存使用率,以及接入的各类传感器(如温度传感器、光敏传感器等),这些硬件输出的数据被统称为属性。将这些数据上传至阿里云物联网平台,以实时呈现设备状态和实测数据,这一过程就是“上传设备属性”。

1)定义物模型
在阿里云物联网控制台上,依次进入:产品 → 功能定义 → 添加自定义功能,然后填写以下内容:
功能类型:属性
功能名称:CPU温度
标识符:cpu_temperature
数据类型:float (单精度浮点型)
取值范围:0-120
步长:0.1
单位:摄氏度 / °C
读写类型:只读
再定义另一个属性:
功能类型:属性
功能名称:格力空调温度
标识符:gree_temperature
数据类型:float (单精度浮点型)
取值范围:0-35
步长:0.1
单位:摄氏度 / °C
读写类型:读写
注意,标识符区分大小写,相当于C#中的变量名。这里建议统一使用小写,后续会说明原因。此外,两个属性的读写类型不同——一个只读,一个读写。
2)编写模型
前面提到过,Alink JSON是阿里云定义的一种固定格式的JSON。这些属性数据最终需要以JSON形式上传。在C#中,可以通过定义类来快速生成JSON。
| 参数 | 类型 | 说明 |
|---|---|---|
| id | string | 消息ID号,在设备生命周期内唯一,建议使用时间戳或GUID |
| version | string | 协议版本号,目前固定为“1.0” |
| params | Object | 属性数据,包含多个属性对象,每个对象有上报时间(time)和值(value) |
| time | long | 属性上报时间 |
| value | object | 属性值 |
| method | string | 固定取值 thing.event.property.post |
我们需要编写一个类来存储信息,然后转换成Alink JSON上传。先预览一下最终要生成的JSON格式:
{
"id": "123456789",
"version": "1.0",
"params": {
"cpu_temperature": {
"value": 58.6,
"time": 1524448722000
},
"gree_temperature": {
"value": 26.6,
"time": 1524448722000
}
},
"method": "thing.event.property.post"
}
实际上,我们只需要关注params部分的编写即可。
在控制台程序中新建一个类TestModel:
public class TestModel
{
public string id { get { return DateTime.Now.Ticks.ToString(); } set { } }
public string version { get { return "1.0"; } set { } }
public Params @params { get; set; }
public class Params
{
// ...
}
public string @method { get { return "thing.event.property.post"; } set { } }
}
这样定义后,使用时只需关心params部分即可,id、version等字段无需手动赋值,减少了重复劳动。注意字段名使用了@params,因为params是C#的关键字,加@可避免冲突。
接下来根据在阿里云物联网控制台定义的属性,继续补充:
public class TestModel
{
public string id { get { return DateTime.Now.Ticks.ToString(); } set { } }
public string version { get { return "1.0"; } set { } }
public Params @params { get; set; }
public class Params
{
public Cpu_temperature cpu_temperature { get; set; }
public Gree_temperature gree_temperature { get; set; }
public class Cpu_temperature
{
public float value { get; set; }
public long time { get; set; }
}
public class Gree_temperature
{
public float value { get; set; }
public long time { get; set; }
}
}
public string @method { get { return "thing.event.property.post"; } set { } }
}
不过这样写还不够,因为还没有对TestModel中的引用类型进行实例化。可以用构造函数来解决,当然也能用依赖注入容器,但这里用构造函数更直接:
public class TestModel
{
public string id { get { return DateTime.Now.Ticks.ToString(); } set { } }
public string version { get { return "1.0"; } set { } }
public Params @params { get; set; }
public TestModel()
{
@params = new Params();
}
public class Params
{
public Cpu_temperature cpu_temperature { get; set; }
public Gree_temperature gree_temperature { get; set; }
public Params()
{
cpu_temperature = new Cpu_temperature();
gree_temperature = new Gree_temperature();
}
public class Cpu_temperature
{
public float value { get; set; }
public long time { get; set; }
}
public class Gree_temperature
{
public float value { get; set; }
public long time { get; set; }
}
}
public string method { get { return "thing.event.property.post"; } set { } }
}
3)上传设备属性数据
编写控制台程序,引入CZGL.AliIoTClient,编写基础代码(请替换DeviceOptions中的信息):
static AliIoTClientJson client;
static void Main(string[] args)
{
// 创建客户端
client = new AliIoTClientJson(new DeviceOptions
{
ProductKey = "a1A6VVt72pD",
DeviceName = "json",
DeviceSecret = "7QrjTptQYCdepjbQvSoqkuygic2051zM",
RegionId = "cn-shanghai"
});
// 设置要订阅的Topic
string[] topics = new string[] { client.CombineHeadTopic("get") };
// 使用默认事件
client.UseDefaultEventHandler();
// 连接服务器
client.ConnectIoT(topics, null, 60);
ToServer();
// 自定义方法,后面说明
Console.ReadKey();
}
在Program类中编写一个方法,用于收集属性和上传:
public static void ToServer()
{
// 实例化模型
TestModel model = new TestModel();
// 设置属性值
model.@params.cpu_temperature.value = 56.5F;
model.@params.cpu_temperature.time = AliIoTClientJson.GetUnixTime();
// 低碳环保,夏天空调不低于26℃
model.@params.gree_temperature.value = 26.0F;
model.@params.gree_temperature.time = AliIoTClientJson.GetUnixTime();
// 上传属性数据
client.Thing_Property_Post(model, false);
}
启动控制台程序,在阿里云物联网控制台打开设备,点击“运行状态”,就能看到上传的属性数据了。文章后面会详细说明CZGL.AliIoTClient关于属性上传的具体情况。
当然,上面的数据是固定赋值的,仅用于演示。实际开发中需要开发者自己采集数据。下面给出几种模拟数据的方法。
4)模拟数据
这里提供了三个数据模拟方法,不需要深究内部实现,当作工具来用即可。你也可以自己编写相应的模拟方法。每个方法都有四个参数:原始值、最小值、最大值、波动范围。
///
/// 模拟数据
///
public static class DeviceSimulate
{
///
/// int类型模拟
///
public static int Property(ref int original, int min, int max, int range)
{
int num = (new Random()).Next(0, range + 1);
bool addorrm;
if (original + num > max || original > max)
addorrm = false;
else if (original < min || original - num < min)
addorrm = true;
else
addorrm = ((new Random()).Next(1, 3) > 1) ? true : false;
if (addorrm == true)
original += num;
else
original -= num;
return original;
}
public static float Property(ref float original, float min, float max, int range = 8)
{
original = float.Parse(original.ToString("#0.00"));
float num = float.Parse(((new Random()).NextDouble() / range).ToString("#0.00"));
bool addorrm;
if (original + num > max || original > max)
addorrm = false;
else if (original < min || original - num < min)
addorrm = true;
else
addorrm = ((new Random()).Next(1, 3) > 1) ? true : false;
if (addorrm == true)
original += num;
else
original -= num;
original = float.Parse(original.ToString("#0.00"));
return original;
}
public static double Property(ref double original, double min, double max, int range = 8)
{
original = double.Parse(original.ToString("#0.0000"));
double num = double.Parse(((new Random()).NextDouble() / range).ToString("#0.0000"));
bool addorrm;
if (original + num > max || original > max)
addorrm = false;
else if (original < min || original - num < min)
addorrm = true;
else
addorrm = ((new Random()).Next(1, 3) > 1) ? true : false;
if (addorrm == true)
original += num;
else
original -= num;
original = double.Parse(original.ToString("#0.0000"));
return original;
}
}
int模拟数据:
range表示每次生成的增减量范围[0, range]。比如初始值56,range=2,那么可能增减0、1或2个单位,至于增还是减由随机决定。但设置了min和max后,最终值会在这个范围内波动。
float、double模拟数据:
range越大,波动范围越小。默认range=8,大概每次波动0.1左右。float保留两位小数,double保留四位。如果需要更高或更低的小数位数,修改ToString("#0.0000")中的格式即可。
模拟属性数据:
接下来模拟两个属性的变化。在Program中定义两个静态变量存储CPU温度和空调温度:
static float cpu_temperature = 50.0F;
static float gree_temperature = 26.0F;
修改ToServer()方法:
public static void ToServer()
{
TestModel model = new TestModel();
model.@params.cpu_temperature.value = DeviceSimulate.Property(ref cpu_temperature, 40, 60, 8);
model.@params.cpu_temperature.time = AliIoTClientJson.GetUnixTime();
// 低碳环保,夏天空调不低于26℃
model.@params.gree_temperature.value = DeviceSimulate.Property(ref gree_temperature, 40, 60, 8);
model.@params.gree_temperature.time = AliIoTClientJson.GetUnixTime();
client.Thing_Property_Post(model, false);
}
在Main()方法里增加定时上传:
while (true)
{
ToServer();
Thread.Sleep(1000);
}
至此基本完成。完整代码如下:
class Program
{
static AliIoTClientJson client;
static void Main(string[] args)
{
client = new AliIoTClientJson(new DeviceOptions
{
ProductKey = "a1A6VVt72pD",
DeviceName = "json",
DeviceSecret = "7QrjTptQYCdepjbQvSoqkuygic2051zM",
RegionId = "cn-shanghai"
});
string[] topics = new string[] { client.CombineHeadTopic("get") };
client.UseDefaultEventHandler();
client.ConnectIoT(topics, null, 60);
while (true)
{
ToServer();
Thread.Sleep(1000);
}
Console.ReadKey();
}
static float cpu_temperature = 50.0F;
static float gree_temperature = 26.0F;
public static void ToServer()
{
TestModel model = new TestModel();
model.@params.cpu_temperature.value = DeviceSimulate.Property(ref cpu_temperature, 40, 60, 8);
model.@params.cpu_temperature.time = AliIoTClientJson.GetUnixTime();
model.@params.gree_temperature.value = DeviceSimulate.Property(ref gree_temperature, 40, 60, 8);
model.@params.gree_temperature.time = AliIoTClientJson.GetUnixTime();
client.Thing_Property_Post(model, false);
}
public static class DeviceSimulate
{
public static int Property(ref int original, int min, int max, int range) { /* ... */ }
public static float Property(ref float original, float min, float max, int range = 8) { /* ... */ }
public static double Property(ref double original, double min, double max, int range = 8) { /* ... */ }
}
}
运行控制台程序,打开阿里云物联网控制台,查看设备的运行状态,打开“自动刷新”,就能看到数据变化了。如果觉得每次波动范围太大,可以把range改大一些;如果希望数据更稳定,可以把min-max范围改小。
5)设备属性 - CZGL.AliIoTClient
首先说明一点:产品创建前需要设置为Alinkjson/透传产品。因此CZGL.AliIoTClient提供了两个客户端类:
| 类名 | 说明 |
|---|---|
| AliIoTClientJson | 以Alink json形式上传数据 |
| AliIoTClientBinary | 以透传形式上传数据 |
这两个类仅在属性、事件、服务三个功能的数据上传形式上有差别,连接服务器、普通Topic等其他功能完全一致。一个产品只能定义一种上传数据的形式。
CZGL.AliIoTClient中上传属性的方法(Alink json):
// 直接发送原始byte[],不做任何中间处理
public int Thing_Property_Post(byte[] json)
// 发送原始json字符串,可指定是否转换为小写,默认true
public int Thing_Property_Post(string json, [bool isToLower = True])
// 指定编码格式
public int Thing_Property_Post(string json, [bool isToLower = True], [System.Text.Encoding encoding = null])
// 直接传入模型,SDK自动转换后上传
public int Thing_Property_Post(TModel model, [bool isToLower = True])
获取UNIX时间:阿里云要求属性数据要带上Unix时间戳,所以直接集成在客户端里了:
public static long GetUnixTime()
使用示例参考前面的过程。
透传形式:
如果想使用透传,就用AliIoTClientBinary类:
// 设备上传属性——透传
public int Thing_Property_UpRaw(byte[] bytes)
// 设备上传属性——透传,转为Base64加密后上传
public int Thing_Property_UpRawToBase64(byte[] bytes, [System.Text.Encoding encoding = null])
6)关于透传
透传以二进制报文形式上传,比如0x020000007b00,这是十六进制表示,每两位一个字节。如果是二进制,则是8位一个字节。
透传需要在阿里云物联网控制台创建“透传”产品后,设置脚本,将透传数据转为Alink json。透传数据是自定义的,以字节为单位,其中有5个特定字节,按字节位拆分。
记住:以字节为单位。
透传数据格式标准:
| 字段 | 字节数 |
|---|---|
| 帧类型 | 1字节 |
| 请求ID | 4字节 |
| 属性数据 | N个字节 |
帧类型:
| 值(十六进制) | 说明 |
|---|---|
| 0x00 | 属性上报 |
| 0x01 | 属性设置 |
| 0x02 | 上报数据返回结果 |
| 0x03 | 属性设置设备返回结果 |
| 0xff | 未知命令 |
举例说明:
很多人直接把十进制或十六进制转换成二进制,比如0x020000007b00转二进制:100000000000000000000000000111101100000000——但这是错误的。
以上面的CPU和空调温度为例,要上传属性数据,帧类型为0x00。
| 属性 | 十进制 | 十六进制 | 二进制 | 拆分二进制 |
|---|---|---|---|---|
| cpu_temperature | 56 | 0x38 | 00111000 | 00 11 10 00 |
| gree_temperature | 26 | 0x1A | 00011010 | 00 01 10 10 |
正确的拆分和赋值方式:
| 字段 | 字节数 | 十六进制 | 二进制 |
|---|---|---|---|
| 帧类型 | 1字节 | 00 | 00000000 |
| ID | 4字节 | 00 00 00 7B | 00000000 00000000 00000000 01111011 |
| cpu_temperature | 1字节 | 38 | 00111000 |
| gree_temperature | 1字节 | 1A | 00011010 |
最终十六进制数据:0x000000007B381A
二进制数据:00000000000000000000000000000000011110110011100000011010
将十六进制或二进制数据存储到byte[]变量中,切记强制转换。存储时,一个byte为一个字节,M个字节则byte[M]。
建议用十六进制存储透传数据,二进制实在太难搞了
