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,一经查实,立即删除!

相关文章

由数据范围反推算法复杂度以及算法内容

一般ACM或者笔试题的时间限制是1秒或2秒。 在这种情况下&#xff0c;C代码中的操作次数控制在 1 0 7 ∼ 1 0 8 10^7\sim10^8 107∼108为最佳。 下面给出在不同数据范围下&#xff0c;代码的时间复杂度和算法该如何选择&#xff1a; n ≤ 30 n\leq30 n≤30&#xff0c;指数级别…

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…

C语言深入学习 --- 2.指针的进阶

文章目录 第二章指针的进阶1.字符指针2.指针数组3.数组指针3.1数组指针的定义3.2 &数组名与数组名的区别3.3 数组指针的使用 4.数组参数与指针参数4.1 一维数组的传参4.2 二维数组的传参4.3 一级指针传参4.4 二级指针传参 5.函数指针6.函数指针数组7.指向函数指针数组的指针…

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…

线段树及例题动态求连续区间和 Java代码

线段树 作用 1、单点修改 2、区间查询 相比于树状数组&#xff0c;线段树代码更复杂但应用更广泛 例题 Acwing 1264. 动态求连续区间和 给定 n n n 个数组成的一个数列&#xff0c;规定有两种操作&#xff0c;一是修改某个元素&#xff0c;二是求子数列 [ a , b ] [a,b] […

leetcode 200 . 岛屿数量

链接 : . - 力扣&#xff08;LeetCode&#xff09; 广搜 : BFS 适合于解决两个点之间的最短路问题 &#xff1b; 以起始点为中心一圈一圈的进行搜索 , 一旦遇到终点 &#xff0c; 记录之前走过的结点就是一条最短路 &#xff1b; 代码模板 : int dir[4][2] {0, 1, 1,…

自动化测试基础——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;每个进程都有一个这个结构体指针变量 验证上面划分的结构&#…

2024.3.1 训练记录(5)

昨晚cf上大分了开心捏&#xff0c;三月了得想想新的高效一点的训练方法了不能再像个无头苍蝇乱撞了 目前的想法是1700-2000板刷cf&#xff0c;遇到不会的算法就学&#xff0c;思维还需要再提升一下&#xff0c;暑假开始补没有接触过的算法 文章目录 CF 1937D PinballCF 1916D…

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

目录 一.什么是经脉二.人体和手足哪面为阳&#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…