Helpex - Trao đổi & giúp đỡ Đăng nhập

Chơi với Docker, MQTT, Grafana, InfluxDB, Python và Arduino

Tôi phải thừa nhận rằng bài đăng này chỉ là một cái cớ để chơi với GrafanaInfluxDB . InfluxDB là một cơ sở dữ liệu tuyệt vời được thiết kế đặc biệt để làm việc với dữ liệu chuỗi thời gian. Grafana là một công cụ nguồn mở được sử dụng để phân tích chuỗi thời gian. Tôi muốn xây dựng một nguyên mẫu đơn giản. Ý tưởng là:

  • Một thiết bị Arduino ( ESP32 ) phát ra một sự kiện MQTT đến máy chủ Mosquitto . Tôi sẽ sử dụng một chiết áp để mô phỏng một cảm biến. Ví dụ, hãy tưởng tượng một cảm biến nhiệt độ thay vì chiết áp. Đây là một mạch tôi đã sử dụng trong các dự án trước đó  .
  • Một tập lệnh Python sẽ lắng nghe sự kiện MQTT trên Raspberry Pi của tôi và sẽ duy trì giá trị cho cơ sở dữ liệu InfluxDB.
  • Tôi sẽ theo dõi trạng thái của chuỗi thời gian được đưa ra bởi chiết áp với Grafana.
  • Tôi sẽ tạo một cảnh báo trong Grafana khi giá trị trung bình trong vòng 10 giây vượt quá ngưỡng. Điều này sẽ kích hoạt một WebHook khi cảnh báo thay đổi trạng thái.
  • Một máy chủ Python Flask , một dịch vụ siêu nhỏ, sẽ lắng nghe WebHook và phát ra một sự kiện MQTT, tùy thuộc vào trạng thái của nó.
  • Một NodeMcu , một loại thiết bị Arduino, sẽ lắng nghe sự kiện MQTT này và kích hoạt đèn LED. Nó sẽ báo hiệu đèn LED màu đỏ nếu cảnh báo BẬT và đèn LED màu xanh nếu cảnh báo TẮT.

Máy chủ

Như tôi đã nói trước đây, chúng tôi sẽ cần ba máy chủ:

  • Máy chủ MQTT (Mosquitto)
  • Máy chủ InfluxDB
  • Máy chủ Grafana

Chúng tôi sẽ sử dụng Docker. Máy chủ Docker sẽ chạy trên Raspberry Pi3. Raspberry Pi là một thiết bị ARM, vì vậy chúng tôi sẽ cần hình ảnh Docker cho kiến ​​trúc này.

version: '2'

services:
  mosquitto:
    image: pascaldevink/rpi-mosquitto
    container_name: moquitto
    ports:
     - "9001:9001"
     - "1883:1883"
    restart: always

  influxdb:
    image: hypriot/rpi-influxdb
    container_name: influxdb
    restart: always
    environment:
     - INFLUXDB_INIT_PWD="password"
     - PRE_CREATE_DB="iot"
    ports:
     - "8083:8083"
     - "8086:8086"
    volumes:
     - ~/docker/rpi-influxdb/data:/data

  grafana:
    image: fg2it/grafana-armhf:v4.6.3
    container_name: grafana
    restart: always
    ports:
     - "3000:3000"
    volumes:
      - grafana-db:/var/lib/grafana
      - grafana-log:/var/log/grafana
      - grafana-conf:/etc/grafana

volumes:
  grafana-db:
    driver: local  
  grafana-log:
    driver: local
  grafana-conf:
    driver: local


ESP32

Phần ESP32 rất đơn giản. Chúng tôi sẽ chỉ cần kết nối chiết áp của chúng tôi với Esp32. Chiết áp có ba chân: GND, Tín hiệu và Vcc. Đối với Tín hiệu, chúng tôi sẽ sử dụng chân 32.

Chúng tôi chỉ cần định cấu hình mạng Wi-Fi, kết nối với máy chủ MQTT của chúng tôi và phát ra giá trị chiết áp trong mỗi vòng lặp.

#include <pubsubclient.h> 
#include <wifi.h>

const int potentiometerPin = 32;

// Wi-Fi configuration
const char * ssid = "my_wifi_ssid";
const char * password = "my_wifi_password";

// MQTT configuration
const char * server = "192.168.1.111";
const char * topic = "/pot";
const char * clientName = "com.gonzalo123.esp32";

String payload;

WiFiClient wifiClient;
PubSubClient client(wifiClient);

void wifiConnect() {
    Serial.println();
    Serial.print("Connecting to ");
    Serial.println(ssid);

    WiFi.begin(ssid, password);

    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }
    Serial.println("");
    Serial.print("WiFi connected.");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
}

void mqttReConnect() {
    while (!client.connected()) {
        Serial.print("Attempting MQTT connection...");
        if (client.connect(clientName)) {
            Serial.println("connected");
        } else {
            Serial.print("failed, rc=");
            Serial.print(client.state());
            Serial.println(" try again in 5 seconds");
            delay(5000);
        }
    }
}

void mqttEmit(String topic, String value) {
    client.publish((char * ) topic.c_str(), (char * ) value.c_str());
}

void setup() {
    Serial.begin(115200);

    wifiConnect();
    client.setServer(server, 1883);
    delay(1500);
}

void loop() {
    if (!client.connected()) {
        mqttReConnect();
    }
    int current = (int)((analogRead(potentiometerPin) * 100) / 4095);
    mqttEmit(topic, (String) current);
    delay(500);
}</wifi.h></pubsubclient.h>


Máy nghe nhạc MQTT

ESP32 phát ra một sự kiện (ngay lập tức / nồi) với giá trị của chiết áp. Vì vậy, chúng tôi sẽ tạo ra một trình nghe MQTT lắng nghe MQTT và duy trì giá trị cho InfluxDB.

import paho.mqtt.client as mqtt
from influxdb import InfluxDBClient
import datetime
import logging


def persists(msg):
    current_time = datetime.datetime.utcnow().isoformat()
    json_body = [
        {
            "measurement": "pot",
            "tags": {},
            "time": current_time,
            "fields": {
                "value": int(msg.payload)
            }
        }
    ]
    logging.info(json_body)
    influx_client.write_points(json_body)


logging.basicConfig(level=logging.INFO)
influx_client = InfluxDBClient('docker', 8086, database='iot')
client = mqtt.Client()

client.on_connect = lambda self, mosq, obj, rc: self.subscribe("/pot")
client.on_message = lambda client, userdata, msg: persists(msg)

client.connect("docker", 1883, 60)

client.loop_forever()


Grafana

Trong Grafana, chúng ta cần làm hai việc. Đầu tiên, chúng tôi sẽ tạo một nguồn dữ liệu từ máy chủ InfluxDB của chúng tôi. Từ đây, nó khá đơn giản.

Tiếp theo, chúng tôi sẽ tạo một bảng điều khiển. Chúng tôi chỉ có một chuỗi thời gian trong giá trị của chiết áp. Tôi phải thừa nhận rằng bảng điều khiển của tôi có rất nhiều thứ mà tôi đã tạo ra chỉ để cho vui.

Đó là truy vấn mà tôi đang sử dụng để vẽ đồ thị chính.

SELECT 
  last("value") FROM "pot" 
WHERE 
  time >= now() - 5m 
GROUP BY 
  time($interval) fill(previous)


Ở đây, chúng ta có thể thấy bảng điều khiển.

Chơi với Docker, MQTT, Grafana, InfluxDB, Python và Arduino

Ở đây, chúng ta có thể thấy cấu hình cảnh báo của tôi:

Chơi với Docker, MQTT, Grafana, InfluxDB, Python và Arduino

Tôi cũng đã tạo một kênh thông báo bằng WebHook. Grafana sẽ sử dụng WebHook này để thông báo khi trạng thái cảnh báo thay đổi.

Trình nghe WebHook

Grafana sẽ phát ra một WebHook, vì vậy chúng ta sẽ cần một điểm cuối REST để thu thập các cuộc gọi WebHook. Tôi thường sử dụng PHP / Lumen để tạo các máy chủ REST, nhưng, trong dự án này, tôi sẽ sử dụng Python và Flask.

Chúng ta cần xử lý HTTP Basic Auth và phát ra một sự kiện MQTT. MQTT là một giao thức rất đơn giản, nhưng nó có một tính năng rất hay, phù hợp như một chiếc găng tay ở đây. Hãy để tôi giải thích.

Hãy tưởng tượng rằng chúng tôi đã có hệ thống của chúng tôi và chạy và trạng thái là ok ok. Bây giờ, chúng tôi kết nối một thiết bị (ví dụ: một đèn đỏ / xanh lớn). Vì sự kiện ok ok đã được kích hoạt trước khi chúng tôi kết nối ánh sáng, đèn xanh của chúng tôi sẽ không bật. Chúng ta cần chờ đợi sự kiện cảnh báo trên đường cao tốc nếu chúng ta muốn thấy bất kỳ ánh sáng nào. Điều đó không tuyệt.

MQTT cho phép chúng tôi giữ lại các tin nhắn trên mạng. Điều đó có nghĩa là chúng ta có thể phát ra các tin nhắn với cờ giữ lại trên một chủ đề và khi chúng ta kết nối một thiết bị sau đó với chủ đề này, nó sẽ nhận được tin nhắn. Đây, chính xác là những gì chúng ta cần.

from flask import Flask
from flask import request
from flask_httpauth import HTTPBasicAuth
import paho.mqtt.client as mqtt
import json

client = mqtt.Client()

app = Flask(__name__)
auth = HTTPBasicAuth()

# http basic auth credentials
users = {
    "user": "password"
}


@auth.get_password
def get_pw(username):
    if username in users:
        return users.get(username)
    return None


@app.route('/alert', methods=['POST'])
@auth.login_required
def alert():
    client.connect("docker", 1883, 60)
    data = json.loads(request.data.decode('utf-8'))
    if data['state'] == 'alerting':
        client.publish(topic="/alert", payload="1", retain=True)
    elif data['state'] == 'ok':
        client.publish(topic="/alert", payload="0", retain=True)

    client.disconnect()

    return "ok"


if __name__ == "__main__":
    app.run(host='0.0.0.0')


NodeMcu

Cuối cùng là NodeMcu. Phần này tương tự như phần ESP32. Đèn LED của chúng tôi nằm trên chân 4 và 5. Chúng tôi cũng cần định cấu hình Wi-Fi và kết nối với máy chủ MQTT. NodeMcu và ESP32 là các thiết bị tương tự, nhưng không giống nhau. Ví dụ: chúng ta cần sử dụng các thư viện khác nhau để kết nối với Wi-Fi.

Thiết bị này sẽ lắng nghe sự kiện MQTT và kích hoạt một đèn LED này hoặc đèn LED khác, tùy thuộc vào trạng thái của nó.

#include <pubsubclient.h>
#include <esp8266wifi.h>

const int ledRed = 4;
const int ledGreen = 5;

// Wi-Fi configuration
const char * ssid = "my_wifi_ssid";
const char * password = "my_wifi_password";

// MQTT configuration
const char * server = "192.168.1.111";
const char * topic = "/alert";
const char * clientName = "com.gonzalo123.nodemcu";

int value;
int percent;
String payload;

WiFiClient wifiClient;
PubSubClient client(wifiClient);

void wifiConnect() {
    Serial.println();
    Serial.print("Connecting to ");
    Serial.println(ssid);

    WiFi.begin(ssid, password);

    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }
    Serial.println("");
    Serial.print("WiFi connected.");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
}

void mqttReConnect() {
    while (!client.connected()) {
        Serial.print("Attempting MQTT connection...");
        if (client.connect(clientName)) {
            Serial.println("connected");
            client.subscribe(topic);
        } else {
            Serial.print("failed, rc=");
            Serial.print(client.state());
            Serial.println(" try again in 5 seconds");
            delay(5000);
        }
    }
}

void callback(char * topic, byte * payload, unsigned int length) {

    Serial.print("Message arrived [");
    Serial.print(topic);

    String data;
    for (int i = 0; i < length; i++) {
        data += (char) payload[i];
    }
    cleanLeds();
    int value = data.toInt();
    switch (value) {
    case 1:
        digitalWrite(ledRed, HIGH);
        break;
    case 0:
        digitalWrite(ledGreen, HIGH);
        break;
    }
    Serial.print("] value:");
    Serial.println((int) value);
}

void cleanLeds() {
    digitalWrite(ledRed, LOW);
    digitalWrite(ledGreen, LOW);
}

void setup() {
    Serial.begin(9600);
    pinMode(ledRed, OUTPUT);
    pinMode(ledGreen, OUTPUT);
    cleanLeds();
    Serial.println("start");

    wifiConnect();
    client.setServer(server, 1883);
    client.setCallback(callback);

    delay(1500);
}

void loop() {
    Serial.print(".");
    if (!client.connected()) {
        mqttReConnect();
    }

    client.loop();
    delay(500);
}</esp8266wifi.h></pubsubclient.h>


Ở đây, bạn có thể thấy nguyên mẫu hoạt động.


Và, đây là nguồn gốc đang .

11 hữu ích 0 bình luận 7.0k xem chia sẻ

Có thể bạn quan tâm

loading