EdgeX Foundry - 连接 MQTT 设备

文章目录

    • 一、概述
      • 1.安装说明
      • 2.MQTT 设备模拟器
        • 2.1.模拟器设计
        • 2.2.Spring Boot 程序源码
          • 2.2.1.MQTT
          • 2.2.2.JsonUtils
          • 2.2.3.Device
        • 2.3.程序配置
    • 二、连接 MQTT 设备
      • 1.docker-comepse
      • 2.设备配置文件
      • 3.启动 EdgeX Foundry
      • 4.访问 UI
        • 4.1. consul
        • 4.2. EdgeX Console
      • 5.创建 MQTT 设备
        • 5.1.创建设备配置文件
        • 5.2.添加设备
      • 6.运行模拟器
      • 7.测试
        • 7.1.命令
        • 7.2.事件
        • 7.3.读值

  • EdgeX Foundry
# EdgeX Foundryhttps://iothub.org.cn/docs/edgex/
https://iothub.org.cn/docs/edgex/device/link-mqtt/

一、概述

1.安装说明

在这里插入图片描述

# 官方文档https://docs.edgexfoundry.org/3.1/microservices/device/services/device-mqtt/Ch-ExamplesAddingMQTTDevice/

安装方式:

  • 使用 EdgeX Console 界面创建 MQTT 设备
  • 使用 Spring Boot 实现 MQTT 设备模拟器

2.MQTT 设备模拟器

2.1.模拟器设计

MQTT 设备模拟器使用 Spring Boot 开发。参考 mock-device.js。

mock-device.js

function getRandomFloat(min, max) {return Math.random() * (max - min) + min;
}const deviceName = "my-custom-device";
let message = "test-message";
let json = {"name" : "My JSON"};// DataSender sends async value to MQTT broker every 15 seconds
schedule('*/15 * * * * *', ()=>{var data = {};data.randnum = getRandomFloat(25,29).toFixed(1);data.ping = "pong"data.message = "Hello World"publish( 'incoming/data/my-custom-device/values', JSON.stringify(data));
});// CommandHandler receives commands and sends response to MQTT broker
// 1. Receive the reading request, then return the response
// 2. Receive the set request, then change the device value
subscribe( "command/my-custom-device/#" , (topic, val) => {const words = topic.split('/');var cmd = words[2];var method = words[3];var uuid = words[4];var response = {};var data = val;if (method == "set") {switch(cmd) {case "message":message = data[cmd];break;case "json":json = data[cmd];break;}}else{switch(cmd) {case "ping":response.ping = "pong";break;case "message":response.message = message;break;case "randnum":response.randnum = 12.123;break;case "json":response.json = json;break;}}var sendTopic ="command/response/"+ uuid;publish( sendTopic, JSON.stringify(response));
});
2.2.Spring Boot 程序源码
2.2.1.MQTT
package com.iothub.mqtt;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;/*** Created by 传智播客*黑马程序员.*/
@Component
public class EmqClient {private static final Logger log = LoggerFactory.getLogger(EmqClient.class);private IMqttClient mqttClient;@Autowiredprivate MqttProperties mqttProperties;@Autowiredprivate MqttCallback mqttCallback;@PostConstructpublic void init(){MqttClientPersistence mempersitence = new MemoryPersistence();try {mqttClient = new MqttClient(mqttProperties.getBrokerUrl(),mqttProperties.getClientId(),mempersitence);} catch (MqttException e) {log.error("初始化客户端mqttClient对象失败,errormsg={},brokerUrl={},clientId={}",e.getMessage(),mqttProperties.getBrokerUrl(),mqttProperties.getClientId());}}/*** 连接broker* @param username* @param password*/public void connect(String username,String password){MqttConnectOptions options = new MqttConnectOptions();options.setAutomaticReconnect(true);options.setUserName(username);options.setPassword(password.toCharArray());options.setCleanSession(true);mqttClient.setCallback(mqttCallback);try {mqttClient.connect(options);} catch (MqttException e) {log.error("mqtt客户端连接服务端失败,失败原因{}",e.getMessage());}}/*** 断开连接*/@PreDestroypublic void disConnect(){try {mqttClient.disconnect();} catch (MqttException e) {log.error("断开连接产生异常,异常信息{}",e.getMessage());}}/*** 重连*/public void reConnect(){try {mqttClient.reconnect();} catch (MqttException e) {log.error("重连失败,失败原因{}",e.getMessage());}}/*** 发布消息* @param topic* @param msg* @param qos* @param retain*/public void publish(String topic, String msg, QosEnum qos, boolean retain){MqttMessage mqttMessage = new MqttMessage();mqttMessage.setPayload(msg.getBytes());mqttMessage.setQos(qos.value());mqttMessage.setRetained(retain);try {mqttClient.publish(topic,mqttMessage);} catch (MqttException e) {log.error("发布消息失败,errormsg={},topic={},msg={},qos={},retain={}",e.getMessage(),topic,msg,qos.value(),retain);}}/*** 订阅* @param topicFilter* @param qos*/public void subscribe(String topicFilter,QosEnum qos){try {mqttClient.subscribe(topicFilter,qos.value());} catch (MqttException e) {log.error("订阅主题失败,errormsg={},topicFilter={},qos={}",e.getMessage(),topicFilter,qos.value());}}/*** 取消订阅* @param topicFilter*/public void unSubscribe(String topicFilter){try {mqttClient.unsubscribe(topicFilter);} catch (MqttException e) {log.error("取消订阅失败,errormsg={},topicfiler={}",e.getMessage(),topicFilter);}}}
package com.iothub.mqtt;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.iothub.device.MqttData;
import com.iothub.utils.JsonUtils;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;/*** Created by 传智播客*黑马程序员.*/
@Component
public class MessageCallback implements MqttCallback {private static final Logger log = LoggerFactory.getLogger(MessageCallback.class);@Autowiredprivate EmqClient emqClient;@Autowiredprivate MqttData mqttData;/*** 丢失了对服务端的连接后触发的回调* @param cause*/@Overridepublic void connectionLost(Throwable cause) {// 资源的清理  重连log.info("丢失了对服务端的连接");}/*** 应用收到消息后触发的回调* @param topic* @param message* @throws Exception*/@Overridepublic void messageArrived(String topic, MqttMessage message) throws Exception {log.info("订阅者订阅到了消息,topic={},messageid={},qos={},payload={}",topic,message.getId(),message.getQos(),new String(message.getPayload()));String[] buff = topic.split("/");String cmd = buff[2];String method = buff[3];String uuid = buff[4];String response = "{}";String data = new String(message.getPayload());if (method.equals("set")) {log.info("修改 message ={}", data);switch (cmd) {case "message":String msg= JsonUtils.jsonToNodeString( data, "message");mqttData.setMessage(msg);break;case "json":String json= JsonUtils.jsonToNodeString( data, "json");mqttData.setJson(json);break;}} else {switch (cmd) {case "ping":response = mqttData.getPing();break;case "message":response = mqttData.getMessage();break;case "randnum":response = mqttData.getRandnum();break;case "json":response = mqttData.getJson();break;}}emqClient.publish("command/response/" + uuid, response, QosEnum.QoS1,false);}/*** 消息发布者消息发布完成产生的回调* @param token*/@Overridepublic void deliveryComplete(IMqttDeliveryToken token) {int messageId = token.getMessageId();String[] topics = token.getTopics();log.info("消息发布完成,messageid={},topics={}",messageId,topics);}
}
package com.iothub.mqtt;import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;/*** Created by 传智播客*黑马程序员.*/
@Configuration
@ConfigurationProperties(prefix = "mqtt")
public class MqttProperties {private String brokerUrl;private String clientId;private String username;private String password;public String getBrokerUrl() {return brokerUrl;}public void setBrokerUrl(String brokerUrl) {this.brokerUrl = brokerUrl;}public String getClientId() {return clientId;}public void setClientId(String clientId) {this.clientId = clientId;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}@Overridepublic String toString() {return "MqttProperties{" +"brokerUrl='" + brokerUrl + '\'' +", clientId='" + clientId + '\'' +", username='" + username + '\'' +", password='" + password + '\'' +'}';}
}
package com.iothub.mqtt;/*** Created by 传智播客*黑马程序员.*/
public enum QosEnum {QoS0(0),QoS1(1),QoS2(2);private final int value;QosEnum(int value) {this.value = value;}public int value(){return this.value;}
}
2.2.2.JsonUtils
package com.iothub.utils;import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import org.apache.commons.lang3.StringUtils;import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Map;/*** JSON转换工具类。** @date 2017-06-29 10:07:51*/
public final class JsonUtils {/*** 日期字符串的格式*/public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";private static ObjectMapper objectMapper = new ObjectMapper();static {// 设置日期字符串的格式objectMapper.setDateFormat(new SimpleDateFormat(DATE_FORMAT));// 反序列化时,允许字段名没有双引号objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);// 忽略未知属性objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);// 反序列化时,忽略未知的属性objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);}private JsonUtils() {}/*** 获取ObjectMapper** @return*/public static ObjectMapper getObjectMapper() {return objectMapper;}/*** 对象转JSON字符串** @param obj 对象* @return JSON字符串*/public static String toJSONString(Object obj) {try {return objectMapper.writeValueAsString(obj);}catch (JsonProcessingException e) {throw new RuntimeException("JSON序列化时出现错误!", e);}}/*** 对象转JSON字符串。** @param obj        对象。* @param dateFormat 日期格式。* @return JSON字符串。*/public static String toJSONString(Object obj, String dateFormat) {if (StringUtils.isBlank(dateFormat)) {dateFormat = DATE_FORMAT;}objectMapper.setDateFormat(new SimpleDateFormat(dateFormat));try {return objectMapper.writeValueAsString(obj);}catch (JsonProcessingException e) {throw new RuntimeException("JSON序列化时出现错误!", e);}}/*** 根据节点解析json中对象* @param json json* @param key 对象key* @param clazz clazz* @return json中对象*/public static <T> T jsonToNodeObject(String json,String key,Class<T> clazz){try {JsonNode jsonNode = objectMapper.readTree(json);return jsonNode.get(key).traverse(objectMapper).readValueAs(clazz);} catch (Exception e) {throw new RuntimeException("json解析节点错误");}}/*** 根据节点解析json中嵌套对象* @param json json* @param key 对象key* @param clazz clazz* @param nodeNames 节点名* @return json中对象*/public static <T> T jsonToNodeObject(String json,String key,Class<T> clazz,String... nodeNames){try {JsonNode jsonNode = objectMapper.readTree(json);for (String nodeName : nodeNames) {jsonNode = jsonNode.path(nodeName);}return jsonNode.get(key).traverse(objectMapper).readValueAs(clazz);} catch (Exception e) {throw new RuntimeException("json解析节点错误",e);}}public static String jsonToNodeString(String json,String key){try {JsonNode jsonNode = objectMapper.readTree(json);return jsonNode.get(key).asText();} catch (Exception e) {throw new RuntimeException("json解析节点错误",e);}}public static String jsonToNodeString(String json,String key,String nodeName){try {JsonNode jsonNode = objectMapper.readTree(json);jsonNode = jsonNode.path(nodeName);return jsonNode.get(key).toString();} catch (Exception e) {throw new RuntimeException("json解析节点错误",e);}}public static String jsonToNodeString(String json,String key,String... nodeName){try {JsonNode jsonNode = objectMapper.readTree(json);for (String name : nodeName) {jsonNode = jsonNode.path(name);}return jsonNode.get(key).asText();} catch (Exception e) {throw new RuntimeException("json解析节点错误",e);}}public static JsonNode jsonToNode(String json,String key,String... nodeName){try {JsonNode jsonNode = objectMapper.readTree(json);for (String name : nodeName) {jsonNode = jsonNode.path(name);}return jsonNode.get(key);} catch (Exception e) {throw new RuntimeException("json解析节点错误",e);}}public static ArrayNode jsonToArrayString(String json, String key, String... nodeName){try {JsonNode jsonNode = objectMapper.readTree(json);for (String name : nodeName) {jsonNode = jsonNode.path(name);}return (ArrayNode) jsonNode.get(key);} catch (Exception e) {throw new RuntimeException("json解析节点错误",e);}}/*** 根据节点* @param json* @param key* @param clazz* @param <T>* @return*/public static <T> T jsonToValue(String json,String key,Class<T> clazz){try {Map map = JsonUtils.jsonToMap(json);return (T)map.get(key);} catch (Exception e) {throw new RuntimeException("json解析节点错误",e);}}/*** JSON字符串转对象。** @param json  JSON字符串。* @param clazz 对象类型。* @param <T>   类型参数。* @return 指定类型的对象。*/public static <T> T jsonToObject(String json, Class<T> clazz) {if (StringUtils.isBlank(json)) {throw new IllegalArgumentException("参数json不能为空!");}if (clazz == null) {throw new IllegalArgumentException("参数clazz不能为空!");}try {return objectMapper.readValue(json.getBytes(StandardCharsets.UTF_8), clazz);}catch (Exception e) {throw new RuntimeException("JSON字符串转对象时出现错误!", e);}}/*** MAP转对象。** @param map  MAP。* @param clazz 对象类型。* @param <T>   类型参数。* @return 指定类型的对象。*/public static <T> T mapToObject(Map map, Class<T> clazz) {if (map == null) {throw new IllegalArgumentException("参数map不能为空!");}if (clazz == null) {throw new IllegalArgumentException("参数clazz不能为空!");}try {return objectMapper.convertValue(map, clazz);}catch (Exception e) {throw new RuntimeException("MAP字符串转对象时出现错误!", e);}}/*** JSON字符串转对象数组。** @param json  JSON字符串。* @param clazz 数组元素的对象类型。* @param <T>   类型参数。* @return 指定类型的对象数组。*/public static <T> List<T> jsonToList(String json, Class<T> clazz) {if (StringUtils.isBlank(json)) {throw new IllegalArgumentException("参数json不能为空!");}if (clazz == null) {throw new IllegalArgumentException("参数clazz不能为空!");}try {JavaType javaType = objectMapper.getTypeFactory().constructParametricType(List.class, clazz);return objectMapper.readValue(json.getBytes(StandardCharsets.UTF_8), javaType);}catch (Exception e) {throw new RuntimeException("JSON字符串转数组时出现错误!", e);}}/*** JSON字符串转MAP。** @param json JSON字符串。* @return MAP对象。*/public static Map jsonToMap(String json) {if (StringUtils.isBlank(json)) {throw new IllegalArgumentException("参数json不能为空!");}try {return objectMapper.readValue(json.getBytes(StandardCharsets.UTF_8), Map.class);}catch (Exception e) {throw new RuntimeException("JSON字符串转对象时出现错误!", e);}}/*** 拷贝对象属性,并返回指定类型的新对象。** @param src   源对象。* @param clazz 目标对象类型。* @param <T>   目标对象类型参数。* @return 与原对象属性值相同的新对象。*/public static <T> T copyProperties(Object src, Class<T> clazz) {return jsonToObject(toJSONString(src), clazz);}/*** 拷贝对象属性,并返回同类型的新对象。** @param src 源对象。* @param <T> 目标对象类型参数。* @return 与原对象属性值相同的新对象。*/public static <T> T copyProperties(T src) {return (T) copyProperties(src, src.getClass());}/*** @Description map转JSON* @Date 16:32 2020/7/6* @param map map对象* @return JSON字符串**/public static String mapToJson(Map map) {if (map == null) {throw new IllegalArgumentException("参数map不能为空!");}try {return objectMapper.writeValueAsString(map);} catch (JsonProcessingException e) {throw new RuntimeException("Map转JSON时出现错误!", e);}}
}
2.2.3.Device
package com.iothub.device;import com.iothub.mqtt.EmqClient;
import com.iothub.mqtt.MqttProperties;
import com.iothub.mqtt.QosEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;@Component
public class MqttDevice {@Autowiredprivate EmqClient emqClient;@Autowiredprivate MqttData mqttData;@Autowiredprivate MqttProperties properties;@PostConstructpublic void init(){//连接服务端emqClient.connect(properties.getUsername(),properties.getPassword());//订阅一个主题emqClient.subscribe("command/my-custom-device/#", QosEnum.QoS1);}@Scheduled(fixedRate = 50000)public void publish(){String data = getData(1);emqClient.publish("incoming/data/my-custom-device/values",data,QosEnum.QoS1,false);}private String getData(Integer type){if (type == 1) {// 携带时间戳String data = mqttData.getValues();return data;} else if (type == 2) {// 不携带时间戳String data = "";return data;}else {// 数组String data = "[{\"key1\":\"value1\"}, {\"key2\":true}]";return data;}}
}
package com.iothub.device;
import com.iothub.utils.JsonUtils;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;@Component
public class MqttData {public MqttData(){ping = "pong ......";randnum = 1000;message = "Hello World !!!";//json = "{\"name\" : \"My JSON\"}";Map<Object,Object> map = new HashMap<>();map.put("name", "My JSON ......");json = JsonUtils.toJSONString(map);}public void setRandnum(float randnum) {this.randnum = randnum;}public void setPing(String ping) {this.ping = ping;}public void setMessage(String message) {this.message = message;}public void setJson(String json) {this.json = json;}public String getRandnum() {Map<Object,Object> map = new HashMap<>();map.put("randnum", randnum );return JsonUtils.toJSONString(map);}public String getPing() {Map<Object,Object> map = new HashMap<>();map.put("ping", ping);return JsonUtils.toJSONString(map);}public String getMessage() {Map<Object,Object> map = new HashMap<>();map.put("message", message);return JsonUtils.toJSONString(map);}public String getJson() {Map<Object,Object> map = new HashMap<>();map.put("json", json);return JsonUtils.toJSONString(map);}public String getValues() {Map<Object,Object> map = new HashMap<>();map.put("randnum", randnum );map.put("ping", ping);map.put("message", message);return JsonUtils.toJSONString(map);}private float randnum;private String ping;private String message;private String json;
}
2.3.程序配置
  • pom.xml
        <dependency><groupId>org.eclipse.paho</groupId><artifactId>org.eclipse.paho.client.mqttv3</artifactId><version>1.2.5</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.4</version></dependency>
  • application.yaml
server:port: 8888
spring:application:name: device-mqtt-simulatormqtt:broker-url: tcp://192.168.202.233:1883client-id: device-mqtt-simulatorusername:password:

二、连接 MQTT 设备

1.docker-comepse

# 1.克隆 edgex-compose
$ git clone git@github.com:edgexfoundry/edgex-compose.git 
$ git clone https://github.com/edgexfoundry/edgex-compose.git
$ cd edgex-compose 
$ git checkout v3.1# 2.生成 docker-compose.yml 文件(注意这包括 mqtt-broker)
$ cd compose-builder
$ make gen ds-mqtt mqtt-broker no-secty# 3.检查生成的文件
$ ls | grep 'docker-compose.yml'
docker-compose.yml
[root@edgex mqtt-device]# git clone https://github.com/edgexfoundry/edgex-compose.git
Cloning into 'edgex-compose'...
remote: Enumerating objects: 4779, done.
remote: Counting objects: 100% (2916/2916), done.
remote: Compressing objects: 100% (173/173), done.
remote: Total 4779 (delta 2831), reused 2804 (delta 2741), pack-reused 1863
Receiving objects: 100% (4779/4779), 1.22 MiB | 450.00 KiB/s, done.
Resolving deltas: 100% (4042/4042), done.[root@edgex mqtt-device]# ll
total 4
drwxr-xr-x. 6 root root 4096 Feb  1 04:10 edgex-compose[root@edgex mqtt-device]# cd edgex-compose/
[root@edgex edgex-compose]# git checkout v3.1
Note: checking out 'v3.1'.You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:git checkout -b new_branch_nameHEAD is now at 488a3fe... Merge pull request #424 from lenny-intel/device-mqtt-secure-mode-napa[root@edgex edgex-compose]# cd compose-builder/[root@edgex compose-builder]# make gen ds-mqtt mqtt-broker no-secty
echo MQTT_VERBOSE=
MQTT_VERBOSE=
docker compose  -p edgex -f docker-compose-base.yml -f add-device-mqtt.yml -f add-mqtt-broker-mosquitto.yml convert > docker-compose.yml
rm -rf ./gen_ext_compose[root@edgex compose-builder]# ls | grep 'docker-compose.yml'
docker-compose.yml

2.设备配置文件

1.设备配置文件

name: "my-custom-device-profile"
manufacturer: "iot"
model: "MQTT-DEVICE"
description: "Test device profile"
labels:- "mqtt"- "test"
deviceResources:-name: randnumisHidden: truedescription: "device random number"properties:valueType: "Float32"readWrite: "R"-name: pingisHidden: truedescription: "device awake"properties:valueType: "String"readWrite: "R"-name: messageisHidden: falsedescription: "device message"properties:valueType: "String"readWrite: "RW"-name: jsonisHidden: falsedescription: "JSON message"properties:valueType: "Object"readWrite: "RW"mediaType: "application/json"deviceCommands:-name: valuesreadWrite: "R"isHidden: falseresourceOperations:- { deviceResource: "randnum" }- { deviceResource: "ping" }- { deviceResource: "message" }

2.设备配置

使用此配置文件来定义设备和调度作业。device-mqtt 在启动时生成一个相对实例。

# Pre-define Devices
deviceList:
- name: "my-custom-device"profileName: "my-custom-device-profile"description: "MQTT device is created for test purpose"labels: [ "MQTT", "test" ]protocols:mqtt:CommandTopic: "command/my-custom-device"autoEvents:- interval: "30s"onChange: falsesourceName: "message"

CommandTopic 用于发布GET或SET命令请求

3.启动 EdgeX Foundry

使用以下命令部署 EdgeX:

$ cd edgex-compose/compose-builder
$ docker compose pull
$ docker compose -f docker-compose.yml up -d
$ docker compose up -d# 修改配置文件
替换IP地址 127.0.0.1 为 0.0.0.0
# docker compose pull# docker compose up -d

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.访问 UI

4.1. consul
# 访问地址
http://192.168.202.233:8500

在这里插入图片描述

4.2. EdgeX Console
# 访问地址
http://192.168.202.233:4000/

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.创建 MQTT 设备

5.1.创建设备配置文件

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

设备配置文件

name: "my-custom-device-profile"
manufacturer: "iot"
model: "MQTT-DEVICE"
description: "Test device profile"
labels:- "mqtt"- "test"
deviceResources:-name: randnumisHidden: truedescription: "device random number"properties:valueType: "Float32"readWrite: "R"-name: pingisHidden: truedescription: "device awake"properties:valueType: "String"readWrite: "R"-name: messageisHidden: falsedescription: "device message"properties:valueType: "String"readWrite: "RW"-name: jsonisHidden: falsedescription: "JSON message"properties:valueType: "Object"readWrite: "RW"mediaType: "application/json"deviceCommands:-name: valuesreadWrite: "R"isHidden: falseresourceOperations:- { deviceResource: "randnum" }- { deviceResource: "ping" }- { deviceResource: "message" }
5.2.添加设备

设备配置

使用此配置文件来定义设备和调度作业。device-mqtt 在启动时生成一个相对实例。

# Pre-define Devices
deviceList:
- name: "my-custom-device"profileName: "my-custom-device-profile"description: "MQTT device is created for test purpose"labels: [ "MQTT", "test" ]protocols:mqtt:CommandTopic: "command/my-custom-device"autoEvents:- interval: "30s"onChange: falsesourceName: "message"

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.运行模拟器

在这里插入图片描述

7.测试

7.1.命令

在这里插入图片描述

7.2.事件

在这里插入图片描述

{"apiVersion": "v3","id": "af46944b-e7e0-4d8b-bb6d-18d42721399b","deviceName": "my-custom-device","profileName": "my-custom-device-profile","sourceName": "message","origin": 1708341080603620600,"readings": [{"id": "edb1630b-7d15-49b8-97e3-1662520f7799","origin": 1708341080603617300,"deviceName": "my-custom-device","resourceName": "message","profileName": "my-custom-device-profile","valueType": "String","value": "1111111"}]
}{"apiVersion": "v3","id": "99809eb7-83b9-49e3-9a5b-d65adc38a522","deviceName": "my-custom-device","profileName": "my-custom-device-profile","sourceName": "values","origin": 1708341090003785700,"readings": [{"id": "668df4eb-1404-4267-b0ee-464f1296e50b","origin": 1708341090003772400,"deviceName": "my-custom-device","resourceName": "randnum","profileName": "my-custom-device-profile","valueType": "Float32","value": "2.650000e+01"}, {"id": "65067425-d134-4fc3-922f-2337d228cf0f","origin": 1708341090003773700,"deviceName": "my-custom-device","resourceName": "ping","profileName": "my-custom-device-profile","valueType": "String","value": "pong"}, {"id": "fffe8f12-536c-43d9-9989-9d450d3b0b7b","origin": 1708341090003774200,"deviceName": "my-custom-device","resourceName": "message","profileName": "my-custom-device-profile","valueType": "String","value": "Hello World"}]
}
7.3.读值

在这里插入图片描述

{"id": "aa29ebc7-2d31-4a4d-b58a-a8d85ba7903e","origin": 1708341330008859000,"deviceName": "my-custom-device","resourceName": "message","profileName": "my-custom-device-profile","valueType": "String","value": "Hello World"
}{"id": "448591a3-d4d7-4188-9943-88a289f9f54a","origin": 1708341345006348800,"deviceName": "my-custom-device","resourceName": "randnum","profileName": "my-custom-device-profile","valueType": "Float32","value": "2.530000e+01"
}{"id": "4af98f08-888a-495d-a07e-4c87dc6d2b82","origin": 1708341345006350000,"deviceName": "my-custom-device","resourceName": "ping","profileName": "my-custom-device-profile","valueType": "String","value": "pong"
}{"id": "26600864-c850-4d45-bf42-835dcf966249","origin": 1708341345006350600,"deviceName": "my-custom-device","resourceName": "message","profileName": "my-custom-device-profile","valueType": "String","value": "Hello World"
}{"id": "d70a679e-3532-4f7d-a3d1-0e53c8ad5f08","origin": 1708341315007203800,"deviceName": "my-custom-device","resourceName": "message","profileName": "my-custom-device-profile","valueType": "String","value": "Hello World"
}
  • EdgeX Foundry
# EdgeX Foundryhttps://iothub.org.cn/docs/edgex/
https://iothub.org.cn/docs/edgex/device/link-mqtt/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/724040.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024年字节跳动+京东+美团面试总结,程序员经验分享

现在的IT行业竞争压力越来越大&#xff0c;尤其是Android开发行业。而很多Android程序员却每天都在重复CRUD,原地徘徊&#xff01; 今年年初&#xff0c;你就想改变现状&#xff0c;于是在网上刷了大量面试题&#xff0c;强行记下之后&#xff0c;开始参加面试&#xff01;但是…

微信小程序开发系列(七)·如何实现小程序页面的快速跳转、小程序样式·尺寸单位rpx以及全局样式和局部样式的区别

微信小程序开发_时光の尘的博客-CSDN博客 目录 1. 小程序样式和组件介绍 2. 如何实现页面的快速跳转 3. 小程序样式尺寸单位rpx 3.1 使用px 3.2 使用rpx 4. 全局样式和局部样式 4.1 全局样式 4.2 局部样式 1. 小程序样式和组件介绍 在开发 Web网站的时候&am…

15 实战:Kaggle房价预测 + 课程竞赛:加州2020年房价预测【李沐动手学深度学习课程笔记】

15 实战&#xff1a;Kaggle房价预测 课程竞赛&#xff1a;加州2020年房价预测【李沐动手学深度学习课程笔记】https://zhuanlan.zhihu.com/p/685343754 写在前面&#xff1a;这里格式很乱&#xff0c;代码直接去知乎copy 1 实战Kaggle比赛&#xff1a;预测房价 1.1 实现几个函…

【系统安全加固】Centos 设置禁用密码并打开密钥登录

文章目录 一&#xff0c;概述二&#xff0c;操作步骤1. 服务器端生成密钥2. 在服务器上安装公钥3.下载私钥到本地&#xff08;重要&#xff0c;否则后面无法登录&#xff09;4. 修改配置文件&#xff0c;禁用密码并打开密钥登录5. 重启sshd服务6. 配置xshell使用密钥登录 一&am…

自动化测试基础——Pytest框架之YAML详解以及Parametrize数据驱动

文章目录 一、YAML详解1.YAML作用2.YAML语法结构3.YAML数据类型3.1.对象3.2.数组3.3.标量 4.YAML的引用5.YAML类型转换 二、YAML的读写与清空1.YAML的读2.YAML的写3.YAML的清空 三、pytest的parametrize简单数据驱动四、pytest的parametrize结合yaml实现数据驱动五、解决pytest…

FreeRTOS操作系统学习——空闲任务及其钩子函数

空闲任务 当 FreeRTOS 的调度器启动以后就会自动的创建一个空闲任务&#xff0c;这样就可以确保至少有一任务可以运行。但是这个空闲任务使用最低优先级&#xff0c;如果应用中有其他高优先级任务处于就绪态的话这个空闲任务就不会跟高优先级的任务抢占 CPU 资源。空闲任务还有…

FlinkSQL ChangeLog

01 Changelog相关优化规则 0101 运行upsert-kafka作业 登录sql-client&#xff0c;创建一个upsert-kafka的sql作业&#xff08;注意&#xff0c;这里发送给kafka的消息必须带key&#xff0c;普通只有value的消息无法解析&#xff0c;这里的key即是主键的值&#xff09; CREA…

EC600模块通过AT指令接入阿里云物联网平台并发布属性

摘要&#xff1a;本文介绍一下如何通过EC600模块的AT指令&#xff0c;将设备属性值发送到阿里云物联网平台的方法。 这个模块供电可以是 5-16V 和电脑通过USB串口连接&#xff0c;4线即可。未来集成到自己的系统中的时候&#xff0c;可以直接发送指令即可。 使用的软件是FreeAT…

【Vue3】Hooks:一种全新的组件逻辑组织方式

&#x1f497;&#x1f497;&#x1f497;欢迎来到我的博客&#xff0c;你将找到有关如何使用技术解决问题的文章&#xff0c;也会找到某个技术的学习路线。无论你是何种职业&#xff0c;我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章&#xff0c;也欢…

STM32作为SPI slave与主机异步通信

背景 最近被测试提了个BUG&#xff0c;说某款产品在用户按下前面板的按键后&#xff0c;对应的按键灯没有亮起来。前面板跟主机是通过SPI口通信&#xff0c;前面板是从机&#xff0c;从机想要主动发送消息&#xff0c;需要通过GPIO中断来通知主机&#xff1a; 上图前面板是ST…

阿里云不杀熟,云服务器优惠价格99元一年,新老用户均可购买

2024阿里云服务器优惠活动政策整理&#xff0c;阿里云99计划ECS云服务器2核2G3M带宽99元一年、2核4G5M优惠价格199元一年&#xff0c;轻量应用服务器2核2G3M服务器61元一年、2核4G4M带宽165元1年&#xff0c;云服务器4核16G10M带宽26元1个月、149元半年&#xff0c;云服务器8核…

14 程序地址空间

背景 kernel 2.6.32 32位平台 空间布局图 如何理解地址划分 地址划分&#xff0c;本质是调整地址空间的定义start和end&#xff0c;内存中定义了管理每个区域范围的结构体&#xff0c;叫mm_struct&#xff0c;每个进程都有一个这个结构体指针变量 验证上面划分的结构&#…

十四经脉最全总结(什么是经脉、十二经脉动态图、经脉走向、人体和手足哪面为阳,哪面为阴?)

目录 一.什么是经脉二.人体和手足哪面为阳&#xff1f;哪面为阴&#xff1f;三.任督二脉3.1 任脉3.2 督脉 四.十二经脉4.1 什么是 厥阴&#xff0c;少阴&#xff0c;太阴&#xff1b;少阳&#xff0c;阳明&#xff0c;太阳&#xff1f;4.2 十二经脉总分布4.3 手三阴经1.手厥阴心…

4、pod运维replicationCtroller、replicaSet、DeamonSet、Job、Cronjob

1、kubenetes 会自动重新运行失败的pod应用 pod运行失败&#xff0c;会自动重启&#xff0c;但是节点失败&#xff0c;pod会被移除&#xff0c; 除非配置了relicationController来管理资源 2、保持pod的健康存活 配置探针&#xff0c;发送http请求 3、查看前一个pod的运行日…

mysql-视图,创建表,存储过程,循环,判断实操命令

数据库操作命令在IDEA工具database的console命令 数据库表结构与视图 -- 查询隔离级别 select transaction_isolation;-- 设置隔离级别 set session transaction isolation level read committed ; set session transaction isolation level REPEATABLE READ ;start transacti…

蚂蚁感冒c++

题目 思路 “两蚂蚁碰面会掉头&#xff0c;若其中一只蚂蚁感冒了&#xff0c;会把感冒传染给碰到的蚂蚁”&#xff0c;这句话看作是“两蚂蚁碰面会互相穿过&#xff0c;只是把感冒的状态传给了另一只蚂蚁”&#xff0c;因为哪只蚂蚁感冒了并不是题目的重点&#xff0c;重点是有…

如何在Word里一次性给全部汉字加拼音?

word是大家日常使用频率较高的工作软件&#xff0c;功能性很强&#xff0c;有上乘的文档格式设置工具&#xff0c;利用它可更轻松、高效地组织和编写文档&#xff0c;熟练运用word&#xff0c;在职场上很重要。那么word如何添加拼音呢?下面给大家介绍一下吧。 方法一&#xf…

线性dp P4310-绝世好题/P4933 大师【日记】

1.绝世好题&#xff08;P4310&#xff09; 绝世好题https://www.luogu.com.cn/problem/P4310 比较考验思维的一道题目&#xff0c;码量和理解难度都不大&#xff0c;重在思维。 一开始看错题&#xff0c;以为是求子串&#xff08;还在想为啥考的纯位运算枚举&#xff0c;whe…

vue iis 配置

下载安装两个IIS模块 1). 传送门&#xff1a;URL Rewrite 2). 传送门&#xff1a;Application Request Routing 注 : 只有在 服务器的主页 有Application Request Routing 部署VUE网站 生成网站 在VUE项目打包生成出发布文件,即文件夹 dist,此处忽略 复制到你需要存放网站的…

Skywalking官方的实战模拟项目Live-Demo

Skywalking 官方的实战模拟项目Live-Demo Live-Demo 是 Skywalking 官方的实战模拟项目&#xff0c;其中包含4个子模块项目 projectA访问projectB、projectC两个SpringBoot项目 projectB访问本地的H2数据库 projectC访问www.baidu.com并同时向一台Kafka消息队列写入数据 proje…