【爱克斯开发板试用】+基于Edgex的RFID设备接入方案

openlab_ed3aac16 更新于 7月前

开发目的:

RFID技术在物联网应用中扮演着重要的角色,通过将RFID设备与Edgex平台集成,可以实现工厂车间内的资产管理、物流运输中的货物流转等功能,其特点是可以高效、可靠的管理设备和采集数据。

一、RFID技术介绍: 

射频识别(RFID)技术是一种无线通信技术,可以实现对物体的自动识别和跟踪。它由RFID标签、读写器和管理系统组成,广泛应用于物流、仓储、供应链管理等领域。

二、Edgex平台简介: 

Edgex是一个开源的分布式物联网边缘计算框架,提供了一组用于管理和运行边缘设备的核心服务。它由设备服务、Core服务和应用服务构成,可以实现设备的接入、数据的收集和分发以及应用的调度和执行。


三、RFID设备接入方案:


技术原理: RFID设备通过读写器与Edgex平台进行通信,读取和上传相关数据。读写器通过RFID标签与物体建立联系,将标签中的信息读取出来,并发送给Edgex平台。


架构设计: 

(a) 设备层:包括RFID读写器、RFID标签等设备,负责具体的RFID数据读取和上传功能。

 (b) Edgex平台层:包括设备服务、Core服务和应用服务,负责设备管理、数据处理和应用调度等功能。 

(c) 应用层:包括数据分析、业务逻辑等应用,基于Edgex平台提供的数据接口进行开发和部署。


具体实施:

硬件设备:使用支持llrp协议的RFID读写器和支持TCP/IP协议的读写器


Edgex运行设备:爱克斯开发板

安装相关软件:


添加设备:使用Edgex UI界面添加设备

在服务器控制台中发送如下:

# Add an ROSpec curl -X PUT -H 'Content-Type: application/json' --data '{"ROSpec": {"ROSpecID": 1,"Priority": 0,"ROSpecCurrentState": 0,"ROBoundarySpec": {"StartTrigger": {"Trigger": 1,"PeriodicTrigger": null,"GPITrigger": null},"StopTrigger": {"Trigger": 1,"DurationTriggerValue": 500,"GPITriggerValue": null}},"AISpecs": [{"AntennaIDs": [0],"StopTrigger": {"Trigger": 0,"DurationTriggerValue": 0,"GPITrigger": null,"TagObservationTrigger": null},"InventoryParameterSpecs": [{"InventoryParameterSpecID": 1,"AirProtocolID": 1,"AntennaConfigurations": null,"Custom": null}],"Custom": null}],"RFSurveySpecs": null,"Custom": null,"LoopSpec": null,"ROReportSpec": {"Trigger": 2,"N": 0,"TagReportContentSelector": {"EnableROSpecID": true,"EnableSpecIndex": true,"EnableInventoryParamSpecID": true,"EnableAntennaID": true,"EnableChannelIndex": true,"EnablePeakRSSI": true,"EnableFirstSeenTimestamp": true,"EnableLastSeenTimestamp": true,"EnableTagSeenCount": true,"EnableAccessSpecID": true,"C1G2EPCMemorySelectors": {"CRCEnabled": true,"PCBitsEnabled": true},"Custom": null},"Custom": null}}}' http://localhost:59882/api/v2/device/name/rfidReader1/ROSpec

# Enable ROSpec //打开读写器

等待数秒

# Disable ROSpec //关闭读写器

开发自己的业务逻辑,对读写器进行操作,接口调用示例如下:

#!/usr/bin/env bash# example.sh - sends an ROSpec, enables it,# waits a bit, prints tag reads, disables/deletes the RO.## This script is very simple and ha***asically no error checking.# Use at your own risk.## This script assumes you have the following tools available:# jq, sed, xargs, curl, base64, od# And it assumes the service/edgex is already running correctly.# The host & ports it will use are found in the variable***elow:HOST=localhostDATA_PORT=59880META_PORT=59881CMDS_PORT=59882ROSPEC_LOCATION="ROSpec.json"set -euo pipefailIFS=$'\n\t'# Get the first devicedevice=$(curl -so- ${HOST}:${META_PORT}/api/v2/device/all | jq '[.devices[]|select(.serviceName=="device-rfid-llrp" )]' | jq '.[0].name' | tr -d '"')dev_url=${HOST}:${CMDS_PORT}/api/v2/device/name/${device}echo "Using ${dev_url}"# Add an ROSpececho "Adding ROSpec ${ROSPEC_LOCATION}"curl -so- "${dev_url}" | jq '.deviceCoreCommand.coreCommands[]|select(.name=="ROSpec")|.url+.path' | sed -e "s/edgex-core-command/${HOST}/" | xargs -L1 \ curl -so- -X PUT -H 'Content-Type: application/json' --data '@'<(echo "{\"ROSpec\":$(<"$ROSPEC_LOCATION")}") # Get ROSpecsecho "Getting ROSpecs from Reader"curl -so- "${dev_url}" | jq '.deviceCoreCommand.coreCommands[]|select(.name=="ROSpec")|.url+.path' | sed -e "s/edgex-core-command/${HOST}/" | xargs -L1 \ curl -so- | jq '.event.readings[].objectValue.ROSpecs'# Enable ROSpececho "Enabling ROSpec 1"curl -so- "${dev_url}" | jq '.deviceCoreCommand.coreCommands[]|select(.name=="enableROSpec")|.url+.path' | sed -e "s/edgex-core-command/${HOST}/" | xargs -L1 \ curl -so- -X PUT -H 'Content-Type: application/json' --data '{"ROSpecID": "1"}'# wait a bitfor i in {10..1}; do echo "Waiting ${i} more seconds..." sleep 1done# Disable ROSpececho "Disabling ROSpec 1"curl -so- "${dev_url}" | jq '.deviceCoreCommand.coreCommands[]|select(.name=="disableROSpec")|.url+.path' | sed -e "s/edgex-core-command/${HOST}/" | xargs -L1 \ curl -so- -X PUT -H 'Content-Type: application/json' --data '{"ROSpecID": "1"}'# Delete ROSpececho "Deleting ROSpec 1"curl -so- "${dev_url}" | jq '.deviceCoreCommand.coreCommands[]|select(.name=="deleteROSpec")|.url+.path' | sed -e "s/edgex-core-command/${HOST}/" | xargs -L1 \ curl -so- -X PUT -H 'Content-Type: application/json' --data '{"ROSpecID": "1"}'# See collected EPCs (assuming EPC96)echo "Displaying EPCs"curl -so- ${HOST}:${DATA_PORT}/api/v2/reading/all?limit=1000 | \ jq '.readings[].objectValue.TagReportData[]?|.EPC96.EPC//.EPCData.EPC' | tr -d '"' | base64 -d | od --endian=big -t x2 -An -w12 -v | sort | uniq -c

TCP/IP协议的读写器,开发自有Device Service

在HandleReadCommands()函数中实现读写器的读操作,以下是示例代码:
func (s *SimpleDriver) HandleReadCommands(deviceName string, protocol***ap[string]models.ProtocolProperties, reqs []sdkModels.CommandRequest) (res []*sdkModels.CommandValue, err error) { s.lc.Debugf("SimpleDriver.HandleReadCommands: protocols: %v resource: %v attributes: %v", protocols, reqs[0].DeviceResourceName, reqs[0].Attributes) //fmt.Println("Protocol: ", protocols[Protocol]) httpInfo, ok := protocols[Protocol] re*****ake([]*sdkModels.CommandValue, len(reqs)) if !ok { return res, errors.New(fmt.Sprintf("'%s' protocol properties is not defined", Protocol)) } fmt.Sprintf("%s://%s:%s", Protocol, httpInfo[Host], httpInfo[Port]) // 建立连接 conn, err := net.Dial("tcp", httpInfo[Host]+":"+httpInfo[Port]) if err != nil { fmt.Println("连接失败:", err) return } defer conn.Close() // 构造要发送的数据,读取温度 data := []byte{0xA0, 0x03, 0x01, 0x7b, 0xe1} // 发送数据 _, err = conn.Write(data) if err != nil { fmt.Println("发送失败:", err) return } // 接收返回数据 buffer := make([]byte, 1024) length, err := conn.Read(buffer) if err != nil { fmt.Println("接收失败:", err) return } //判断返回值是否正确 if buffer[0] != 0xA0 || buffer[1] != 0x05 || buffer[2] != 0x01 || buffer[3] != 0x7b { fmt.Println("返回值的前4个bytes不匹配") return } // 解码返回数据 fmt.Printf("接收到的数据: 0x%02X\n", buffer[:length]) // 将接下来的2个字节转成温度String temperatureBytes := buffer[4:6] temperatureStr := fmt.Sprintf("%d", temperatureBytes) // 将第1字节解析为表示温度的标志位 temperatureFlag := buffer[4] if temperatureFlag == 0x00 { temperatureStr += "-" } else if temperatureFlag == 0x01 { temperatureStr += "" } else { fmt.Println("温度标志位不合法") return } // 将第2字节解析为温度值 temperatureValue := buffer[5] temperatureStr += fmt.Sprintf("%d°", temperatureValue) fmt.Println(temperatureStr) cmdResp, _ := sdkModels.NewCommandValue(reqs[0].DeviceResourceName, common.ValueTypeString, temperatureStr) res[0] = cmdResp s.readCommandsExecuted.Inc(1) return}

在HandleWriteCommands()函数中实现读写器的写操作,以下是示例代码:

func (s *SimpleDriver) HandleWriteCommands(deviceName string, protocol***ap[string]models.ProtocolProperties, reqs []sdkModels.CommandRequest, params []*sdkModels.CommandValue) error { //var err error for i, r := range reqs { s.lc.Debugf("SimpleDriver.HandleWriteCommands: protocols: %v, resource: %v, parameters: %v, attributes: %v", protocols, reqs[i].DeviceResourceName, params[i], reqs[i].Attributes) switch r.DeviceResourceName { case "setBeeperMode": param := struct {Param string}{Param: params[i].ValueToString()} httpInfo, ok := protocols[Protocol] if !ok { return errors.New(fmt.Sprintf("'%s' protocol properties is not defined", Protocol)) } // 建立连接 conn, err := net.Dial("tcp", httpInfo[Host]+":"+httpInfo[Port]) if err != nil { fmt.Println("连接失败:", err) return nil } defer conn.Close() // 构造要发送的数据 data := []byte{0xA0, 0x04, 0x01, 0x7a, 0x00,0xe1} if param.Param=="0" { data[4]=0x00 }else if param.Param=="1" { data[4]=0x01 }else{ data[4]=0x02 } data[5] = CheckSum(data[:5]) //计算CRC fmt.Println(param.Param) // 发送数据 _, err = conn.Write(data) if err != nil { fmt.Println("发送失败:", err) return nil } // 接收返回数据 buffer := make([]byte, 1024) length, err := conn.Read(buffer) if err != nil { fmt.Println("接收失败:", err) return nil } // 解码返回数据 fmt.Printf("接收到的数据: 0x%02X\n", buffer[:length]) //判断返回值是否正确 if buffer[0] != 0xA0 || buffer[1] != 0x04 || buffer[2] != 0x01 || buffer[3] != 0x7a { fmt.Println("返回值的前4个bytes不匹配") return nil }
} } return nil}

Device Service编写完成后即可以按照之前的逻辑添加设备,操作都是类似的

0个评论