解码来自传感器的字节序(?)数据
Decoding endian(?) data from a sensor
我不确定我什至不明白这些数据是怎么回事,但我正在尝试在 Elixir 中复制像 here, here or here to decode the data I am receiving over UART from my Plantower PMS5003 sensor (see datasheet) 传感器这样的功能。
它由 0x42
和 0x4d
分隔,并且这样开始:
iex(mygadget@nerves.local)4> {:ok, data} = Circuits.UART.read(pid, 60000)
{:ok,
<<66, 77, 0, 28, 0, 23, 0, 32, 0, 32, 0, 22, 0, 31, 0, 32, 17, 124, 4, 211, 0,
171, 0, 8, 0, 0, 0, 0, 151, 0, 4, 5, 66, 77, 0, 28, 0, 23, 0, 32, 0, 32, 0,
22, 0, 31, 0, 32, ...>>}
然后我对其进行base16编码:
iex(mygadget@nerves.local)5> Base.encode16(data)
"424D001C0017002000200016001F0020117C04D300AB00080000000097000405424D001C0017002000200016001F0020117C04D300AB00080000000097000405424D001C0017001F001F0016001E001F115804BE0098000800
000000970003B5424D001C0018002000200016001F002011BB04D8009F0008000000009700043E424D001C0016001F001F0015001E001F11DC04C3009300080000000097000437424D001C0017001E001E0015001D001E11E20
4C300850008000000009700042C424D001C0016001E001E0015001D001E117304B70087000600000000970003B0424D001C0016001D001D0015001D001D111F049B007B00060000000097000331424D001C0017001E001E0016
001E001E10F5048D007D00060000000097000400424D001C0017001E001E0016001E001E10FB0496008B0004000000009700041B424D001C0016001E001E0015001E001E10B304810089000400000000970003BA424D001C001
5001C001C0014001C001C104A045E008000020000000097000319424D001C0016001C001
并被 424D
拆分
decoded |> String.split("424D")
["", "001C0017002000200016001F0020117C04D300AB00080000000097000405",
"001C0017002000200016001F0020117C04D300AB00080000000097000405",
"001C0017001F001F0016001E001F115804BE0098000800000000970003B5",
"001C0018002000200016001F002011BB04D8009F0008000000009700043E",
然后将其分成 2 块
iex(mygadget@nerves.local)10> "001C0017002000200016001F0020117C04D300AB00080000000097000405" |> String.codepoints |> Enum.chunk(2) |> Enum.map(&Enum.join/1)
["00", "1C", "00", "17", "00", "20", "00", "20", "00", "16", "00", "1F", "00",
"20", "11", "7C", "04", "D3", "00", "AB", "00", "08", "00", "00", "00", "00",
"97", "00", "04", "05"]
我不知道从这里到哪里去。我找到了 this discussion about how to do it in Java,但我不太明白帧缓冲区的情况。
任何见解表示赞赏
编辑:标签
因此,erlang/elixir 是解构二进制格式原始数据包的绝佳语言——这就是 Circuits.UART.read()
returns。您用 binary pattern matching 解构一个二进制 <<...>>
,并且您的数据表包含您将使用的模式的规范。不需要 base16 编码,不需要在 424D
处拆分,也不需要分成 2:
的块
defmodule My do
def match(<<
66, 77,
_fr_len::big-integer-size(16),
data1::big-integer-size(16),
data2::big-integer-size(16),
data3::big-integer-size(16),
data4::big-integer-size(16),
data5::big-integer-size(16),
data6::big-integer-size(16),
data7::big-integer-size(16),
data8::big-integer-size(16),
data9::big-integer-size(16),
data10::big-integer-size(16),
data11::big-integer-size(16),
data12::big-integer-size(16),
_reserved::big-integer-size(16),
_check_code::big-integer-size(16),
rest::binary
>>) do
IO.puts "pm 1.0 cf: #{data1} ug/m^3"
IO.puts "pm 2.5 atmospheric: #{data5} ug/m^3"
match(rest)
end
def match(partial_frame) do
IO.puts "partial_frame:"
IO.inspect partial_frame
end
def go() do
match(<<66, 77, 0, 28, 0, 23, 0, 32, 0, 32, 0, 22, 0, 31, 0, 32,
17, 124, 4, 211, 0, 171, 0, 8, 0, 0, 0, 0, 151, 0, 4, 5,
66, 77, 0, 28, 0, 23, 0, 32, 0, 32, 0, 22, 0, 31, 0, 32>>)
:ok
end
end
在 iex 中:
iex(1)> My.go
pm 1.0 cf: 23 ug/m^3
pm 2.5 atmospheric: 31 ug/m^3
partial_frame:
<<66, 77, 0, 28, 0, 23, 0, 32, 0, 32, 0, 22, 0, 31, 0, 32>>
:ok
您可以在模式中写入 0x42, 0x4D
以完全匹配数据表规范,但我认为使用十进制等价物更清楚,66, 77
,因为 elixir 在输出时不输出十六进制代码一个二进制的,而不是 elixir 输出小数——正如在你的 data
中看到的那样(或者,有时 elixir 输出二进制的双引号字符串,这真的很混乱和愚蠢。)在 66, 77
模式,您可以轻松查看数据并查看匹配的位置。
请注意,最后一段 rest::binary
就像在正则表达式中写入 .*
。
在信任分配给变量的任何数据之前,您可能应该检查帧长度是否为 28 并验证校验码。不幸的是,我无法弄清楚校验码代表什么。我得到 1029
作为校验码。
==========
您能否 post 举例说明您期望数据的样子?
像 "1C"
这样的十六进制字符串等同于十进制 28
。您可以像这样获得所有十进制等价物:
data = [ "00", "1C", "00", "17", "00", "20", "00", "20", "00", "16", "00",
"1F", "00", "20", "11", "7C", "04", "D3", "00", "AB", "00", "08",
"00", "00", "00", "00", "97", "00", "04", "05"]
for str <- data do
Integer.parse(str, 16)
end
|> IO.inspect
|> Enum.map(fn {a, _} -> a end)
Integer.parse() returns 一个元组,其中第一个元素是整数,第二个元素是 "the remainder of the string",即任何不能解释为整数的元素。
输出:
[
{0, ""},
{28, ""},
{0, ""},
{23, ""},
{0, ""},
{32, ""},
{0, ""},
{32, ""},
{0, ""},
{22, ""},
{0, ""},
{31, ""},
{0, ""},
{32, ""},
{17, ""},
{124, ""},
{4, ""},
{211, ""},
{0, ""},
{171, ""},
{0, ""},
{8, ""},
{0, ""},
{0, ""},
{0, ""},
{0, ""},
{151, ""},
{0, ""},
{4, ""},
{5, ""}
]
[0, 28, 0, 23, 0, 32, 0, 32, 0, 22, 0, 31, 0, 32, 17, 124,
4, 211, 0, 171, 0, 8, 0, 0, 0, 0, 151, 0, 4, 5]
你的数据应该是这样的吗?
它看起来像 java 代码:
...forEach[b | bts.append(Integer.toHexString(b)]
对 java's bitwise OR operator: |
, which makes no sense to me in that code snippet. But, in elixir you would do that with Bitwise.bor(a, b) 稍作调整。我真的认为 java 代码应该是这样的:
...forEach(b -> bts.append(Integer.toHexString(b))
换句话说,forEach() 接受一个 lambda 作为参数。哎呀,这让我很好奇,我要问那家伙那是什么意思。
编辑:
好吧,那个人回复了我,那不是 Java——它是 lambda 的语法——在一些 DSL 语言中。
我不确定我什至不明白这些数据是怎么回事,但我正在尝试在 Elixir 中复制像 here, here or here to decode the data I am receiving over UART from my Plantower PMS5003 sensor (see datasheet) 传感器这样的功能。
它由 0x42
和 0x4d
分隔,并且这样开始:
iex(mygadget@nerves.local)4> {:ok, data} = Circuits.UART.read(pid, 60000)
{:ok,
<<66, 77, 0, 28, 0, 23, 0, 32, 0, 32, 0, 22, 0, 31, 0, 32, 17, 124, 4, 211, 0,
171, 0, 8, 0, 0, 0, 0, 151, 0, 4, 5, 66, 77, 0, 28, 0, 23, 0, 32, 0, 32, 0,
22, 0, 31, 0, 32, ...>>}
然后我对其进行base16编码:
iex(mygadget@nerves.local)5> Base.encode16(data)
"424D001C0017002000200016001F0020117C04D300AB00080000000097000405424D001C0017002000200016001F0020117C04D300AB00080000000097000405424D001C0017001F001F0016001E001F115804BE0098000800
000000970003B5424D001C0018002000200016001F002011BB04D8009F0008000000009700043E424D001C0016001F001F0015001E001F11DC04C3009300080000000097000437424D001C0017001E001E0015001D001E11E20
4C300850008000000009700042C424D001C0016001E001E0015001D001E117304B70087000600000000970003B0424D001C0016001D001D0015001D001D111F049B007B00060000000097000331424D001C0017001E001E0016
001E001E10F5048D007D00060000000097000400424D001C0017001E001E0016001E001E10FB0496008B0004000000009700041B424D001C0016001E001E0015001E001E10B304810089000400000000970003BA424D001C001
5001C001C0014001C001C104A045E008000020000000097000319424D001C0016001C001
并被 424D
decoded |> String.split("424D")
["", "001C0017002000200016001F0020117C04D300AB00080000000097000405",
"001C0017002000200016001F0020117C04D300AB00080000000097000405",
"001C0017001F001F0016001E001F115804BE0098000800000000970003B5",
"001C0018002000200016001F002011BB04D8009F0008000000009700043E",
然后将其分成 2 块
iex(mygadget@nerves.local)10> "001C0017002000200016001F0020117C04D300AB00080000000097000405" |> String.codepoints |> Enum.chunk(2) |> Enum.map(&Enum.join/1)
["00", "1C", "00", "17", "00", "20", "00", "20", "00", "16", "00", "1F", "00",
"20", "11", "7C", "04", "D3", "00", "AB", "00", "08", "00", "00", "00", "00",
"97", "00", "04", "05"]
我不知道从这里到哪里去。我找到了 this discussion about how to do it in Java,但我不太明白帧缓冲区的情况。
任何见解表示赞赏
编辑:标签
因此,erlang/elixir 是解构二进制格式原始数据包的绝佳语言——这就是 Circuits.UART.read()
returns。您用 binary pattern matching 解构一个二进制 <<...>>
,并且您的数据表包含您将使用的模式的规范。不需要 base16 编码,不需要在 424D
处拆分,也不需要分成 2:
defmodule My do
def match(<<
66, 77,
_fr_len::big-integer-size(16),
data1::big-integer-size(16),
data2::big-integer-size(16),
data3::big-integer-size(16),
data4::big-integer-size(16),
data5::big-integer-size(16),
data6::big-integer-size(16),
data7::big-integer-size(16),
data8::big-integer-size(16),
data9::big-integer-size(16),
data10::big-integer-size(16),
data11::big-integer-size(16),
data12::big-integer-size(16),
_reserved::big-integer-size(16),
_check_code::big-integer-size(16),
rest::binary
>>) do
IO.puts "pm 1.0 cf: #{data1} ug/m^3"
IO.puts "pm 2.5 atmospheric: #{data5} ug/m^3"
match(rest)
end
def match(partial_frame) do
IO.puts "partial_frame:"
IO.inspect partial_frame
end
def go() do
match(<<66, 77, 0, 28, 0, 23, 0, 32, 0, 32, 0, 22, 0, 31, 0, 32,
17, 124, 4, 211, 0, 171, 0, 8, 0, 0, 0, 0, 151, 0, 4, 5,
66, 77, 0, 28, 0, 23, 0, 32, 0, 32, 0, 22, 0, 31, 0, 32>>)
:ok
end
end
在 iex 中:
iex(1)> My.go
pm 1.0 cf: 23 ug/m^3
pm 2.5 atmospheric: 31 ug/m^3
partial_frame:
<<66, 77, 0, 28, 0, 23, 0, 32, 0, 32, 0, 22, 0, 31, 0, 32>>
:ok
您可以在模式中写入 0x42, 0x4D
以完全匹配数据表规范,但我认为使用十进制等价物更清楚,66, 77
,因为 elixir 在输出时不输出十六进制代码一个二进制的,而不是 elixir 输出小数——正如在你的 data
中看到的那样(或者,有时 elixir 输出二进制的双引号字符串,这真的很混乱和愚蠢。)在 66, 77
模式,您可以轻松查看数据并查看匹配的位置。
请注意,最后一段 rest::binary
就像在正则表达式中写入 .*
。
在信任分配给变量的任何数据之前,您可能应该检查帧长度是否为 28 并验证校验码。不幸的是,我无法弄清楚校验码代表什么。我得到 1029
作为校验码。
==========
您能否 post 举例说明您期望数据的样子?
像 "1C"
这样的十六进制字符串等同于十进制 28
。您可以像这样获得所有十进制等价物:
data = [ "00", "1C", "00", "17", "00", "20", "00", "20", "00", "16", "00",
"1F", "00", "20", "11", "7C", "04", "D3", "00", "AB", "00", "08",
"00", "00", "00", "00", "97", "00", "04", "05"]
for str <- data do
Integer.parse(str, 16)
end
|> IO.inspect
|> Enum.map(fn {a, _} -> a end)
Integer.parse() returns 一个元组,其中第一个元素是整数,第二个元素是 "the remainder of the string",即任何不能解释为整数的元素。
输出:
[
{0, ""},
{28, ""},
{0, ""},
{23, ""},
{0, ""},
{32, ""},
{0, ""},
{32, ""},
{0, ""},
{22, ""},
{0, ""},
{31, ""},
{0, ""},
{32, ""},
{17, ""},
{124, ""},
{4, ""},
{211, ""},
{0, ""},
{171, ""},
{0, ""},
{8, ""},
{0, ""},
{0, ""},
{0, ""},
{0, ""},
{151, ""},
{0, ""},
{4, ""},
{5, ""}
]
[0, 28, 0, 23, 0, 32, 0, 32, 0, 22, 0, 31, 0, 32, 17, 124,
4, 211, 0, 171, 0, 8, 0, 0, 0, 0, 151, 0, 4, 5]
你的数据应该是这样的吗?
它看起来像 java 代码:
...forEach[b | bts.append(Integer.toHexString(b)]
对 java's bitwise OR operator: |
, which makes no sense to me in that code snippet. But, in elixir you would do that with Bitwise.bor(a, b) 稍作调整。我真的认为 java 代码应该是这样的:
...forEach(b -> bts.append(Integer.toHexString(b))
换句话说,forEach() 接受一个 lambda 作为参数。哎呀,这让我很好奇,我要问那家伙那是什么意思。
编辑:
好吧,那个人回复了我,那不是 Java——它是 lambda 的语法——在一些 DSL 语言中。