Elasticsearch 和 Arduino:一起变得更好!

news2025/6/23 12:17:55

作者:Enrico Zimuel

使用 Arduino IoT 设备与 Elasticsearch 和 Elastic Cloud 进行通信的简单方法

在 Elastic®,我们不断寻找简化搜索体验的新方法,并开始关注物联网世界。 来自物联网的数据收集可能非常具有挑战性,尤其是当我们拥有数千台设备时。

Elasticsearch® 对于收集、探索、可视化和发现数据非常有用 - 对于来自多个设备的所有数据。 Elastic 的分布式特性使其能够轻松处理来自数百万台设备的请求。

感谢与物联网领域非常受欢迎的品牌 Arduino 的合作,我们测试了一些设备,并开发了一个实验性开源库,可以以非常简单的方式将 Arduino 板连接到 Elasticsearch。

什么是 Arduino?

Arduino 是全球开源硬件和软件领域的领导者,拥有超过 3000 万活跃用户! 该品牌旨在帮助各类创客创建自己的互动项目,随着用户面临新的挑战,该品牌不断成长和适应,并扩展到物联网、可穿戴设备、3D 打印和嵌入式环境。 它还开设了专门的专业业务部门,以支持公司成为各自领域的创新者,采用定制且用户友好的方法,避免供应商锁定,并利用 360° 生态系统,做好从原型设计到大规模生产的准备。

Arduino 专业版

在 Arduino Pro 的高性能技术解决方案中,你会发现低功耗且安全的模块系统,例如 Portenta H7,它可以使用高级语言和 AI 进行编程,同时在其可定制的上执行低延迟操作硬件和最新的 Portenta C33,凭借一系列精简且经济高效的功能,使物联网比以往任何时候都更容易访问。

此外,Arduino Pro 的工业级产品适合整个 Arduino 生态系统,其中包括云服务、无数软件库和社区共享的即用型草图,当然还有满足任何需求的各种组件。 其中包括 MKR WiFi 1010 和 Nano RP2040 板等流行产品 —— 创客运动名副其实的基石。

Elasticsearch 和 Arduino

我们开发了一个研发项目来提供一个在 Arduino 模块上运行的简单 Elasticsearch 客户端库(用 C++ 编写)。 没错:现在你可以直接从 Arduino 板与 Elasticsearch 集群通信!

我们使用 Portenta H7、MKR WiFi 1010 和 NANO RP2040 Connect 测试了该库。 任何具有 Wi-Fi 或以太网连接的 Arduino 板都可以使用该库。

我们使用 Elastic Cloud 作为数据库,从相邻设备收集所有数据并提供平均温度的详细信息。 这是控制工程应用中的典型场景。

想了解更多? 以下是包含所有详细信息的简单教程。

使用案例:监控多个物联网设备的温度

我们为一家需要管理位于意大利的多个物联网设备的公司设计了一个用例。 每个设备将来自传感器的数据(例如温度)发送到 Elastic Cloud。 使用 Elastic Cloud,该公司可以管理任何规模的物联网设备,而无需管理专用基础设施。 此外,该公司还需要根据相邻设备的平均温度来调整每个设备的一些内部参数,范围为 100 公里,这是控制工程应用中的典型场景。

使用 Elasticsearch,我们可以使用过滤、聚合、多重匹配、地理空间、向量搜索 (kNN)、语义搜索和机器学习等搜索功能提供多种反馈。

在此用例中,我们使用平均聚合 (average aggregation) 和地理距离 (geo-distance) 来检索 100 公里之间的所有设备。

使用 Elastic Cloud 中提供的 UI Kibana®,我们可以轻松创建仪表板来监控来自所有设备的数据。 由于我们还有地理数据,因此我们可以在地图中表示这些信息。

这是用不同颜色代表不同温度(蓝色是冷,绿色是热)创建的 heat map。

Elastic Cloud 设置

第一步是拥有 Elastic Cloud 帐户。 如果你没有,可以在此处注册试用(无需信用卡)。 登录后,你可以创建新部署,选择要使用的 Elasticsearch 实例的大小。

创建部署后,你需要检索端点 URL 并生成 Elasticsearch 的 API 密钥。 你可以阅读本指南来获取此信息。 对于高级用例,你还可以创建用于不同设备的 API 密钥组,并更改 API 策略以仅允许使用特定索引。

如果你想有自己的本地部署,请参阅文章 “如何在 Linux,MacOS 及 Windows 上进行安装 Elasticsearch”。

准备Elasticsearch索引

我们需要创建一个索引来存储来自 Arduino 板的数据。 我们想要存储温度值、使用地理位置(纬度和经度)的设备位置、设备标识符名称和时间戳。

我们可以通过向 Elasticsearch 发出以下 HTTP 请求来创建索引 “temperature”:

PUT /temperature
{
  "mappings": {
    "properties": {
      "temperature": { "type": "float" }, 
      "timestamp":   { "type": "date" }, 
      "location":    { "type": "geo_point" },
      "device-id":   { "type": "keyword" }
    }
  }
}

要发送此 HTTP 请求,你可以使用 Elastic Cloud 中 Kibana 的 Dev Tools。

我们希望存储每次设备发送数据时操作的时间戳。 这可以使用 Elasticsearch 的摄取管道 (ingest pipeline) 功能来完成。 摄取管道是 Elasticsearch 在索引(存储)文档之前执行的操作。 例如,管道可以根据某些计算分配特定文档字段的值。

在我们的例子中,我们只需要存储时间戳,我们就可以创建一个 “set-timestamp” 管道:

PUT _ingest/pipeline/set-timestamp
{
  "description": "sets the timestamp",
  "processors": [
    {
      "set": {
        "field": "timestamp",
        "value": "{{{_ingest.timestamp}}}"
      }
    }
  ]
}

使用这个管道,我们可以将数据发送到 Elasticsearch,如下所示:

POST /temperature/_doc?pipeline=set-timestamp
{
  "temperature": 21.45,
  "device-id": "H7-001",
  "location": {
    "type": "Point",
    "coordinates": [12.4923, 41.8903]
  }
}

这里的 device-id H7-001 是 Arduino 板的名称,location 是用12.4923(经度)和41.8903(纬度)表示的地理点,这是罗马斗兽场(意大利)的位置。

请注意,我们没有指定时间戳值,因为这是使用 “set-timestamp” 管道(在 URL 中指定为查询字符串)自动生成的。

地理距离查询

要检索 100 公里以内设备的平均温度,我们可以使用以下 Elasticsearch 查询:

GET /temperature/_search
{
  "query": {
    "bool": {
      "must": {
        "match_all": {}
      },
      "filter": {
        "geo_distance": {
          "distance": "100km",
          "location": [12.4923, 41.8903]
        }
      }
    }
  },
  "aggs": {
    "avg_temp": { "avg": { "field": "temperature" } }
  }
}

该查询将返回一个 “avg_temp” 聚合字段,其中包含 100 公里半径内所有设备的平均温度。

Arduino 的 Elasticsearch 客户端的使用

终于到了展示一些 Arduino 代码的时候了! 在这里,我们报告了一个简单的草图,它将温度值发送到 Elastic Cloud,执行地理距离查询获取平均温度,然后等待 30 秒。

此处报告的代码可在 GitHub 存储库 elastic/elasticsearch-arduino 的 examples 文件夹中在线获取。 该草图使用 elasticsearch_config.h 文件,如下所示:

#define WIFI_SECRET_SSID ""
#define WIFI_SECRET_PASS ""
#define ELASTIC_ENDPOINT ""
#define ELASTIC_PORT 443
#define ELASTIC_CLOUD_API_KEY ""
#define DEVICE_GEO_LON 12.4923
#define DEVICE_GEO_LAT 41.8903
#define DEVICE_ID "x"
#define DEVICE_GEO_DISTANCE "50km"

在我们的示例中,我们使用 Wi-Fi 连接将 Arduino 板连接到互联网。

WIFI_SECRET_SSID 和 WIFI_SECRET_PASS 是要使用的 SSID 网络的名称和 Wi-Fi 密码。

ELASTIC_ENDPOINT 是 Elastic Cloud 端点的 URL,ELASTIC_PORT 是 443,因为 Elastic Cloud 使用 TLS (https)。 ELASTIC_CLOUD_API_KEY 是要在 Elastic Cloud 管理界面中生成的 API 密钥。

该文件还包含与 Arduino 设备相关的其他信息。 我们有地理查询的经度 (DEVICE_GEO_LON) 和纬度 (DEVICE_GEO_LAT)、ID (DEVICE_ID) 和距离 (DEVICE_GEO_DISTANCE)。

填写完前面的信息后,我们可以看一下设计,报告如下。

#include <ArduinoJson.h>
#include <WiFi.h>
#include <WiFiSSLClient.h>
#include "ESClient.h" 
#include "elasticsearch_config.h"

// WiFi settings
char ssid[] = WIFI_SECRET_SSID;
char pass[] = WIFI_SECRET_PASS;

// Elastic settings
char serverAddress[] = ELASTIC_ENDPOINT;
int serverPort = ELASTIC_PORT;

WiFiSSLClient wifi;

ESClient client = ESClient(wifi, serverAddress, serverPort); 
int status = WL_IDLE_STATUS;

void setup() {
  Serial.begin(9600);
  Serial.println("Started");

  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to Network named: ");
    Serial.println(ssid);
  
    // Connect to WPA/WPA2 network:
    status = WiFi.begin(ssid, pass);
  }

  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your WiFi shield's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);
  
  client.setElasticCloudApiKey(ELASTIC_CLOUD_API_KEY);
}

void loop() {
 
  float temperature;
  // Set the temperature from a sensor (removing the randomness)
  temperature = random(10,30) + random(0,100)/100.00;
  
  // Prepare the JSON with temperature and geopoint for Elasticsearch
  StaticJsonDocument<200> doc;
  doc["temperature"] = temperature;
  doc["device-id"] = DEVICE_ID;
  doc["location"]["type"] = "Point";
  doc["location"]["coordinates"][0] = DEVICE_GEO_LON;
  doc["location"]["coordinates"][1] = DEVICE_GEO_LAT;

  String temp;
  serializeJson(doc, temp);

  Serial.println("Sending to Elasticsearch:");
  Serial.println(temp);
  ESResponse indexResult;
  // Send the temperature to Elastic Cloud
  indexResult = client.index("temperature", temp, "pipeline=set-timestamp");
  
  DynamicJsonDocument result(1024);
  deserializeJson(result, indexResult.body);

  if (result["result"] == "created") {
    Serial.println("Created with _id: " + result["_id"].as<String>());
  } else {
    Serial.println("Error sending data: " + indexResult.body);

  }
  
  StaticJsonDocument<512> query;
  query["query"]["bool"]["filter"]["geo_distance"]["distance"] = DEVICE_GEO_DISTANCE;
  query["query"]["bool"]["filter"]["geo_distance"]["location"][0] = DEVICE_GEO_LON;
  query["query"]["bool"]["filter"]["geo_distance"]["location"][1] = DEVICE_GEO_LAT;
  query["aggs"]["avg_temp"]["avg"]["field"] = "temperature";
  query["size"] = 0;

  String search;
  serializeJson(query, search);
  Serial.println("Geo-location query:");
  Serial.println(search);

  ESResponse searchResult;
  // Send the temperature to Elastic Cloud
  searchResult = client.search("temperature", search);

  DynamicJsonDocument avg(512);
  deserializeJson(avg, searchResult.body);
  float avgTemp = avg["aggregations"]["avg_temp"]["value"];
  int numDevices = avg["hits"]["total"]["value"];
  Serial.println("Average temperature of " + String(numDevices) + " devices in " + DEVICE_GEO_DISTANCE + ": " + String(avgTemp));

  Serial.println("Wait 30 seconds");
  delay(30000);
}

此设计需要 Wi-Fi、WiFiSSLClient(用于使用 TLS 进行连接)进行互联网连接、用于连接 Elasticsearch 的 EsClient 以及用于序列化和反序列化 Json 数据结构的 ArduinoJson 库。

在 setup() 函数中,我们启动 Wi-Fi 连接,并使用 client.setElasticCloudApiKey(ELASTIC_CLOUD_API_KEY) 函数调用设置 Elastic Cloud 的 API 密钥。

客户端对象在主区域中初始化,传递 Wi-Fi 对象、服务器地址(端点)和 HTTP 端口。

在 loop() 函数中,我们有将温度发送到 Elastic Cloud 的代码。 这里的温度只是一个 10 到 30 之间的随机浮点数; 通常它来自连接到 Arduino 板的传感器。

为了准备发送到 Elasticsearch 的文档,我们使用了 ArduinoJson 库。

我们使用以下代码创建一个 “doc” 对象:

StaticJsonDocument<200> doc;
doc["temperature"] = temperature;
doc["device-id"] = DEVICE_ID;
doc["location"]["type"] = "Point";
doc["location"]["coordinates"][0] = DEVICE_GEO_LON;
doc["location"]["coordinates"][1] = DEVICE_GEO_LAT;

该对象被序列化为 JSON 字符串,如下所示:

String temp;
serializeJson(doc, temp);

最后,可以使用索引 API 将存储在 “temp” 变量中的文档发送到 Elasticsearch,如下所示:

ESResponse indexResult;
indexResult = client.index("temperature", temp, "pipeline=set-timestamp");

此 API 使用 “set-timestamp” 管道在索引 “temp” 中添加 “temp” 文档。 结果存储在 “indexResult” 变量中,该变量是一个结构类型,如下所示:

struct ESResponse {
    int statusCode;
    String body;
};

“statusCode” 是响应的 HTTP 状态代码,“body” 是响应正文。 如果响应包含值为 “created” 的 “result” 字段,则索引操作成功。

为了获取半径 100 公里内设备的平均温度,我们使用了以下地理距离查询,使用 ArduinoJson 表示。

StaticJsonDocument<512> query;
query["query"]["bool"]["filter"]["geo_distance"]["distance"] = DEVICE_GEO_DISTANCE;
query["query"]["bool"]["filter"]["geo_distance"]["location"][0] = DEVICE_GEO_LON;
query["query"]["bool"]["filter"]["geo_distance"]["location"][1] = DEVICE_GEO_LAT;
query["aggs"]["avg_temp"]["avg"]["field"] = "temperature";
query["size"] = 0;

String search;
serializeJson(query, search);

ESResponse searchResult;
searchResult = client.search("temperature", search);

DynamicJsonDocument avg(512);
deserializeJson(avg, searchResult.body);
float avgTemp = avg["aggregations"]["avg_temp"]["value"];
int numDevices = avg["hits"]["total"]["value"];

搜索的响应包含平均温度作为聚合值。 此外,我们可以使用 Elasticsearch 的 JSON 响应中的 ['hits']['total']['value'] 字段来检索查询检索到的设备数量。

结论

由于与 Arduino 的合作,我们开发了一个非常简单的库,允许直接从 Arduino 板使用 Elasticsearch。 只需几行代码,我们就可以将数据发送到 Elasticsearch 并使用地理定位等执行复杂的阐述。

我们迫不及待地想看看 Arduino 用户将使用 Elasticsearch 设计出哪些其他创意用例。 例如,如果你对生成式人工智能感兴趣,你将享受 Elastic 的所有最新功能。

不要再等待了 —— 尝试一下 Elastic Cloud 和 elasticsearch-arduino 库。

致谢

我要感谢 Arduino 的所有员工,是他们使这个研发项目成为可能。 特别感谢 Giampaolo Mancini 在开发 elasticsearch-arduino 库方面的帮助和协作。 此外,我还要感谢 Arduino 的 Andrea Richetta 和 Stefano Implicito 使这次合作成为可能。

本文中描述的任何特性或功能的发布和时间安排均由 Elastic 自行决定。 当前不可用的任何特性或功能可能无法按时交付或根本无法交付。

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

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

相关文章

【面试经典150 | 哈希表】字母异位词分组

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;排序哈希表方法二&#xff1a;数组作为哈希表的键方法三&#xff1a;字符串作为哈希表的键知识回顾accumulate 写在最后 Tag 【自定义哈希】【哈希表】【数组】 题目来源 49. 字母异位词分组 题目解读 将字符串数组中…

面试全攻略:ElasticSearch分页与MySQL分页的底层逻辑与优化技巧

大家好&#xff0c;我是小米&#xff01;今天&#xff0c;我要和大家一起深入探讨一个在技术面试中经常被问到的问题&#xff1a;ElasticSearch中的分页与MySQL中的分页有什么区别&#xff1f;分页是数据库查询中非常常见的操作&#xff0c;但当我们在不同的数据库中执行分页操…

画面清晰如真:OLED透明拼接屏在金华市的画质表现

金华市位于中国浙江省中西部&#xff0c;是中国重要的历史文化名城之一。 金华市拥有悠久的历史和丰富的文化遗产&#xff0c;如古城墙、古建筑和古镇等。 这为OLED透明拼接屏技术的应用提供了丰富的创作素材和展示主题。 金华市的著名景点 义乌国际商贸城&#xff1a;义乌国…

如何开发一款跑酷游戏?

跑酷游戏&#xff08;Parkour Game&#xff09;是一种流行的视频游戏类型&#xff0c;玩家需要在游戏中控制角色进行极限动作、跳跃、爬墙和各种动作&#xff0c;以完成各种挑战和任务。如果你有兴趣开发一款跑酷游戏&#xff0c;以下是一些关键步骤和考虑事项&#xff1a; 游…

职场新人:大公司还是小公司,何去何从?

导言&#xff1a; 对于职场新人来说&#xff0c;选择进入大公司还是小公司是一个重要的决策。大公司通常具有知名度、资源丰富和完善的职业发展路径&#xff0c;而小公司则更加灵活、创新和有机会获得更多的责任。本文将探讨大公司和小公司的优势&#xff0c;并给出一些建议&am…

MySQL下载和安装详细步骤

下载步骤详解 本教程以 MySQL 5.7.29 为例介绍其在 Windows 10 操作系统下的安装和配置过程。 步骤 1)&#xff1a;打开 MySQL 官方网站&#xff08;http://www.mysql.com&#xff09; 步骤 2)&#xff1a;滑到网页底部&#xff0c;点击 DOWNLOADS 列表下的 MySQL Community…

基于SpringBoot的教学辅助平台

目录 前言 一、技术栈 二、系统功能介绍 学生信息管理 教师信息管理 课程信息管理 科目分类管理 班级分类管理 课程作业管理 交流论坛管理 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理…

一步步教你如何搭建家政小程序+家政管理系统

在当今数字化时代&#xff0c;家政服务行业也需要紧跟潮流&#xff0c;借助互联网技术提升服务品质和效率。而小程序作为一种轻量级应用&#xff0c;能够帮助家政服务公司在推广、预约、评价等方面实现便捷操作&#xff0c;吸引更多客户。本文将介绍如何轻松搭建家政服务行业的…

那些年你不一定会的磁盘、目录、文件操作(c语言版)

一、前言 今天和大家分享的是使用win32api操作磁盘&#xff0c;目录&#xff0c;文件&#xff0c;这里分为三大块&#xff0c;希望能帮到大家。在讲之前还是先给大家看看效果图&#xff0c;如下图&#xff1a; 二、磁盘 获取盘符下的所有文件夹.rar: https://url18.ctfile.co…

H3C交换机的40G堆叠线 ,可以插在普通光口做堆叠吗?

环境&#xff1a; S6520X-24ST-SI交换机 H3C LSWM1QSTK2万兆40G堆叠线QSFP 问题描述&#xff1a; H3C交换机的40G堆叠线 &#xff0c;可以插在普通光口做堆叠吗&#xff1f; 解答&#xff1a; 1.H3C交换机的40G堆叠线通常是用于连接堆叠模块或堆叠端口的。这些堆叠线通常使…

小谈设计模式(25)—职责链模式

小谈设计模式&#xff08;25&#xff09;—职责链模式 专栏介绍专栏地址专栏介绍 职责链模式分析角色分析抽象处理者&#xff08;Handler&#xff09;具体处理者&#xff08;ConcreteHandler&#xff09;客户端&#xff08;Client&#xff09; 优缺点分析优点123 缺点12 应用场…

Flink on k8s容器日志生成原理及与Yarn部署时的日志生成模式对比

Flink on k8s部署日志详解及与Yarn部署时的日志生成模式对比 最近需要将flink由原先部署到Yarn集群切换到kubernetes集群&#xff0c;在切换之后需要熟悉flink on k8s的运行模式。在使用过程中针对日志模块发现&#xff0c;在k8s的容器中&#xff0c;flink的系统日志只有jobma…

【yolov5】改进系列——特征图可视化

文章目录 前言一、特征图可视化二、可视化指定层三、合并通道可视化总结 前言 对于特征图可视化感兴趣可以参考我的另一篇记录&#xff1a;六行代码实现&#xff1a;特征图提取与特征图可视化&#xff0c;可以实现分类网络的特征图可视化 最近忙论文&#xff0c;想在yolov5上…

使用VSCode进行linux内核代码开发(一)

0. 前言 Linux 内核代码量非常的庞大,其中又包含了各种平台的宏定义开关、配置,外加各种结构体指针的注册,这使得阅读内核代码变成一件令人头疼的事。针对这个问题常见有如下几种方案: source insight 创建项目工程。但是如上所说,对于阅读 linux 代码来说非常困难。而且…

Linux中怎么启动Zookeeper

首先进入Zookeeper安装目录下的bin目录 比如&#xff1a; cd /root/zookeeper-3.4.9/bin 然后在此目录下执行命令。 1. 启动Zookeeper Server端 ./zkServer.sh start 2.启动Zookeeper Client端 ./zkCli.sh 启动Zookeeper Client端后如下&#xff1a;

接口自动化测试_L1

目录&#xff1a; 接口自动化测试框架介绍 接口测试场景自动化测试场景接口测试在分层测试中的位置接口自动化测试与 Web/App 自动化测试对比接口自动化测试与 Web/App 自动化测试对比接口测试工具类型为什么推荐 RequestsRequests 优势Requests 环境准备接口请求方法接口请求…

Web自动化测试进阶:网页中难点之等待机制 —— 强制等待,隐式等待

为什么要添加等待 避免页面未渲染完成后操作&#xff0c;导致的报错 经常会遇到报错&#xff1a;selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"xpath","selector":&q…

[LitCTF 2023]导弹迷踪

这道题相较于其他的分数类型的js题有一点不一样&#xff0c;他不是像常规的有用bp多次抓包修改最后得分来获取flag的。 本题将flag藏到了他的前端文件中本身没有任何难度&#xff0c;只是为了记录一种新的做法 按照我们平常做js的思路就是先随便玩一下然后bp抓包看得分或者抓包…

如何提升设备投资回报率:预测性维护在制造业的应用

在当今竞争激烈的制造业市场中&#xff0c;企业需要不断寻求提高生产效率和降低成本的方法。作为重要资产之一&#xff0c;设备投资回报率成为制造企业关注的焦点。然而&#xff0c;许多企业在设备维护和管理方面面临着一些挑战&#xff0c;这可能导致设备投资回报率的下降。为…

【重拾C语言】八、表单数据组织——结构体(类型、类型别名、直接/间接访问;典例:复数、成绩单)

目录 前言 八、结构体 8.1 结构体类型 8.2 结构体类型名 8.2.1 typedef关键字 8.2.1 结构体类型别名 8.3 结构体变量 8.3.1 使用结构体类型引用 8.3.2 使用结构体类型定义 8.3.3 使用typedef定义的结构体类型别名 8.4 访问结构体变量 8.4.1 直接成员选择表达式 8.…