解码来自传感器的endian(?)数据

时间:2019-05-30 18:53:38

标签: elixir uart nerves-project

我不确定我什至不了解这些数据在这里发生了什么,但是我正在尝试复制hereherehere之类的功能来解码我自己的数据通过Elixir中的Plantower PMS5003传感器(请参见datasheet)传感器通过UART接收。

它由0x420x4d分隔,并开始如下:

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,但我不太了解帧缓冲区的功能。

任何见识表示赞赏

编辑:标签

1 个答案:

答案 0 :(得分:2)

因此,erlang / elixir是解构二进制格式的原始数据包的绝佳语言,这是Circuits.UART.read()返回的结果。您用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作为检查码。

==========

您能否发布一个示例,以期望数据看起来像什么?

"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()返回一个元组,其中第一个元素是整数,第二个元素是“字符串的余数”,即所有不能解释为整数的元素。

输出:

[
  {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|有点儿纠结,在我的代码片段中,这对我来说毫无意义。但是,在长生不老药中,您可以使用Bitwise.bor(a, b)执行此操作。我真的认为Java代码应如下所示:

...forEach(b -> bts.append(Integer.toHexString(b))

换句话说,forEach()将lambda作为参数。哎呀,这让我很好奇,我要问那个家伙什么意思。

编辑:

好的,那个家伙回答了我,那不是Java,而是某种DSL语言中的lambda语法。