Мост между MQTT и SNMP за управление на PicoIP

Мост между MQTT и SNMP за управление на PicoIP

Въведение

В тази статия ще разгледаме как да изградим МОСТ (BRIDGE) между MQTT (Message Queuing Telemetry Transport) и SNMP (Simple Network Management Protocol) за управление на PicoIP устройства. Този мост позволява на потребителите да управляват и мониторират устройства чрез MQTT топици, като използва SNMP за комуникация с PicoIP устройствата. Кодът е написан на Python и използва библиотеката “paho-mqtt” за комуникация с MQTT брокер, както и системни команди за изпълнение на SNMP заявки.

Основната цел на този код е да осигури лесен начин за автоматизация на процеси, мониторинг на състояния и управление на устройства в мрежа. Той е предназначен за платформи, които поддържат Python и имат инсталиран MQTT брокер (например Mosquitto). Ако използвате MQTT брокер който е на друг сървър, трябва само да замените “localhost” с “IP” на брокера. На практика този код може да се стартира на всяка Linux платформа като Raspberry Pi, Orange Pi или настолен компютър. Ако все още не сте инсалирали и конфигурирали вашия MQTT broker, разгледайте нашите статии и се научете как става това:

В статията ще разгледаме подробно всяка част от написания от нас код както и неговите функции, топици и команди, които се използват за управление на PicoIP платформата. Ако все още не сте се запознали с тази платформа може да посетите другите наши статии с ревюта, описания, код и написани от нас библиотеки за нейното използване.

В тази статия за демонстрация на кода ще използваме разработените от нас хардуерни платформи базирани на PicoIP. Преди да продължите напред може да се запознаете с тях в страниците ни:

Сега нека продължим напред…

Код на проекта за мост (bridge) между MQTT и SNMP за управление на PicoIP

import paho.mqtt.client as mqtt
import os
import time
import threading

# MQTT настройки
MQTT_BROKER = "localhost" # Ако стартирате кода на друг компютър, заметене с IP на MQTT сървъра
MQTT_USERNAME = "**********" # Заменете с вашия username на MQTT брокера
MQTT_PASSWORD = "**********" # Заменете с вашата парола на MQTT брокера
MQTT_TOPIC = "picoip/#"
MQTT_CLIENT_ID = "PicoIP_SNMP_Bridge"

# SNMP настройки
SNMP_COMMUNITY = "private"

# Интервали за изчитане (в секунди)
DIGITAL_READ_INTERVAL_JP3 = 1
DIGITAL_READ_INTERVAL_JP4 = 2
ANALOG_READ_INTERVAL = 3

# IP адреси на PicoIP устройствата
PICOIP_DEVICES = ["192.168.1.9", "192.168.1.40"] # заменете с вашото/вашите IP на PicoIP

# Lock за синхронизация на изхода в конзолата
print_lock = threading.Lock()

# Функция за изпълнение на SNMP команди
def run_snmp_command(command):
    result = os.popen(command).read().strip()
    return result

# Функция за проверка дали устройството е активно
def is_device_online(ip):
    # Проста проверка с команда ping (може да се използва и SNMP)
    response = os.popen(f"ping -c 1 {ip}").read()
    return "1 received" in response

# Функция за четене на аналоговите входове
def read_analog_inputs(client):
    while True:
        if ANALOG_READ_INTERVAL > 0:
            for ip in PICOIP_DEVICES:
                if not is_device_online(ip):  # Проверка дали устройството е активно
                    with print_lock:
                        print(f"Device {ip} is offline. Skipping analog inputs.")
                    continue
                for pin in range(1, 9):
                    oid = f"1.3.6.1.4.1.19865.1.2.3.{pin}.0"
                    snmp_cmd = f"snmpget -v1 -c {SNMP_COMMUNITY} {ip} {oid}"
                    value = run_snmp_command(snmp_cmd)
                    if value:
                        value = value.split()[-1]
                        client.publish(f"picoip/{ip}/status/ADC/{pin}", value)
                        with print_lock:
                            print(f"READ ADC {pin} on {ip}: {value}")
                    else:
                        with print_lock:
                            print(f"No response from {ip} for ADC {pin}")
        time.sleep(ANALOG_READ_INTERVAL)

# Функция за четене на цифровите входове на JP3
def read_digital_inputs_jp3(client):
    while True:
        if DIGITAL_READ_INTERVAL_JP3 > 0:
            for ip in PICOIP_DEVICES:
                if not is_device_online(ip):  # Проверка дали устройството е активно
                    with print_lock:
                        print(f"Device {ip} is offline. Skipping JP3 inputs.")
                    continue
                for pin in range(1, 9):
                    oid = f"1.3.6.1.4.1.19865.1.2.1.{pin}.0"
                    snmp_cmd = f"snmpget -v1 -c {SNMP_COMMUNITY} {ip} {oid}"
                    state = run_snmp_command(snmp_cmd)
                    if state:
                        state = state.split()[-1]
                        client.publish(f"picoip/{ip}/status/JP3/{pin}", state)
                        with print_lock:
                            print(f"READ JP3 PIN {pin} on {ip}: {state}")
                    else:
                        with print_lock:
                            print(f"No response from {ip} for JP3 PIN {pin}")
        time.sleep(DIGITAL_READ_INTERVAL_JP3)

# Функция за четене на цифровите входове на JP4
def read_digital_inputs_jp4(client):
    while True:
        if DIGITAL_READ_INTERVAL_JP4 > 0:
            for ip in PICOIP_DEVICES:
                if not is_device_online(ip):  # Проверка дали устройството е активно
                    with print_lock:
                        print(f"Device {ip} is offline. Skipping JP4 inputs.")
                    continue
                for pin in range(1, 9):
                    oid = f"1.3.6.1.4.1.19865.1.2.2.{pin}.0"
                    snmp_cmd = f"snmpget -v1 -c {SNMP_COMMUNITY} {ip} {oid}"
                    state = run_snmp_command(snmp_cmd)
                    if state:
                        state = state.split()[-1]
                        client.publish(f"picoip/{ip}/status/JP4/{pin}", state)
                        with print_lock:
                            print(f"READ JP4 PIN {pin} on {ip}: {state}")
                    else:
                        with print_lock:
                            print(f"No response from {ip} for JP4 PIN {pin}")
        time.sleep(DIGITAL_READ_INTERVAL_JP4)

# Функция за обработка на входящите MQTT съобщения
def on_message(client, userdata, msg):
    topic = msg.topic
    payload = msg.payload.decode("utf-8").strip()

    if topic.startswith("picoip/") and "/digitalWrite/" in topic:
        parts = topic.split("/")
        if len(parts) == 5:
            ip = parts[1]
            port = parts[3]
            pin = parts[4]
            try:
                value = int(payload)
                if value not in [0, 1]:
                    with print_lock:
                        print(f"Invalid payload for digitalWrite: {payload}")
                    return
            except ValueError:
                with print_lock:
                    print(f"Invalid payload for digitalWrite: {payload}")
                return

            oid = f"1.3.6.1.4.1.19865.1.2.{1 if port == 'JP3' else 2}.{pin}.0"
            snmp_cmd = f"snmpset -v1 -c {SNMP_COMMUNITY} {ip} {oid} i {value}"
            run_snmp_command(snmp_cmd)
            with print_lock:
                print(f"SET {port} PIN {pin} to {value} on {ip}")

# Настройки на MQTT клиента
client = mqtt.Client(MQTT_CLIENT_ID)
client.username_pw_set(MQTT_USERNAME, MQTT_PASSWORD)
client.on_message = on_message
client.connect(MQTT_BROKER, 1883, 60)
client.subscribe(MQTT_TOPIC)

print("MQTT-SNMP bridge started...")

# Стартиране на нишки
if ANALOG_READ_INTERVAL > 0:
    analog_input_thread = threading.Thread(target=read_analog_inputs, args=(client,))
    analog_input_thread.daemon = True
    analog_input_thread.start()

if DIGITAL_READ_INTERVAL_JP3 > 0:
    digital_input_jp3_thread = threading.Thread(target=read_digital_inputs_jp3, args=(client,))
    digital_input_jp3_thread.daemon = True
    digital_input_jp3_thread.start()

if DIGITAL_READ_INTERVAL_JP4 > 0:
    digital_input_jp4_thread = threading.Thread(target=read_digital_inputs_jp4, args=(client,))
    digital_input_jp4_thread.daemon = True
    digital_input_jp4_thread.start()

client.loop_forever()

Кратка видео презентация на проекта мост (bridge) между SNMP и MQTT

Пълно описание на кода за мост (bridge) между SNMP и MQTT

Използвани библиотеки

  • threading: Стандартна библиотека на Python за работа с нишки.
  • paho-mqtt: Библиотека за работа с MQTT протокола трябва да е версия “paho-mqtt==1.6.1”, тъй като по-новите версии може да не са съвместими с кода!!!
python3 -m pip install paho-mqtt==1.6.1
  • os: Стандартна библиотека на Python за изпълнение на системни команди.
  • time: Стандартна библиотека на Python за работа с времеви интервали.

Отделно от тези библиотеки, трябва да инсталирате и библиотеката за комуникация с SNMP под Linux. Тя е SNMP (както се казва протокола). Инсталацията и е лесна:

sudo apt-get install snmp

Променливи

  • MQTT_BROKER: Адресът на MQTT брокера. . Ако използвате MQTT брокер който е на друг сървър, трябва само да замените “localhost” с “IP” на брокера.
  • MQTT_USERNAME и MQTT_PASSWORD: Потребителско име и парола за достъп до MQTT брокера.
  • MQTT_TOPIC: Топик, към който клиентът се абонира.
  • MQTT_CLIENT_ID: Идентификатор на MQTT клиента.
  • SNMP_COMMUNITY: SNMP общност (community string), използвана за SNMP заявки.
  • DIGITAL_READ_INTERVAL_JP3DIGITAL_READ_INTERVAL_JP4ANALOG_READ_INTERVAL: Интервали за четене на състоянията на цифровите и аналоговите входове.
  • PICOIP_DEVICES: Списък с IP адреси на PicoIP устройствата.
  • print_lock: Lock за синхронизация на изхода в конзолата.

run_snmp_command(command)

def run_snmp_command(command):
    result = os.popen(command).read().strip()
    return result
  • Описание: Тази функция изпълнява SNMP команда и връща резултата.
  • Как работи: Използва os.popen за изпълнение на командата в shell и read().strip() за четене на резултата и премахване на празните пространства около него.
  • Реализация: Командата се изпълнява като системна команда, а резултатът се връща като низ (стринг).

is_device_online(ip)

def is_device_online(ip):
    response = os.popen(f"ping -c 1 {ip}").read()
    return "1 received" in response
  • Описание: Проверява дали устройството с даден IP адрес е активно чрез команда ping.
  • Как работи: Изпълнява команда ping -c 1 <ip> и проверява дали в резултата се съдържа фразата “1 received“, което означава, че устройството е активно.
  • Реализация: Ако устройството отговори на ping, функцията връща True, иначе връща False.

read_analog_inputs(client)

def read_analog_inputs(client):
    while True:
        if ANALOG_READ_INTERVAL > 0:
            for ip in PICOIP_DEVICES:
                if not is_device_online(ip):
                    with print_lock:
                        print(f"Device {ip} is offline. Skipping analog inputs.")
                    continue
                for pin in range(1, 9):
                    oid = f"1.3.6.1.4.1.19865.1.2.3.{pin}.0"
                    snmp_cmd = f"snmpget -v1 -c {SNMP_COMMUNITY} {ip} {oid}"
                    value = run_snmp_command(snmp_cmd)
                    if value:
                        value = value.split()[-1]
                        client.publish(f"picoip/{ip}/status/ADC/{pin}", value)
                        with print_lock:
                            print(f"READ ADC {pin} on {ip}: {value}")
                    else:
                        with print_lock:
                            print(f"No response from {ip} for ADC {pin}")
        time.sleep(ANALOG_READ_INTERVAL)
  • Описание: Чете аналоговите входове на всички PicoIP устройства и публикува състоянията им в MQTT топици.
  • Как работи: В безкраен цикъл проверява дали устройството е активно и изпълнява SNMP заявки за четене на аналоговите входове. След това публикува стойностите в MQTT топици.
  • Реализация: Използва snmpget за четене на стойностите и client.publish за публикуване на резултатите.

read_digital_inputs_jp3(client)

def read_digital_inputs_jp3(client):
    while True:
        if DIGITAL_READ_INTERVAL_JP3 > 0:
            for ip in PICOIP_DEVICES:
                if not is_device_online(ip):
                    with print_lock:
                        print(f"Device {ip} is offline. Skipping JP3 inputs.")
                    continue
                for pin in range(1, 9):
                    oid = f"1.3.6.1.4.1.19865.1.2.1.{pin}.0"
                    snmp_cmd = f"snmpget -v1 -c {SNMP_COMMUNITY} {ip} {oid}"
                    state = run_snmp_command(snmp_cmd)
                    if state:
                        state = state.split()[-1]
                        client.publish(f"picoip/{ip}/status/JP3/{pin}", state)
                        with print_lock:
                            print(f"READ JP3 PIN {pin} on {ip}: {state}")
                    else:
                        with print_lock:
                            print(f"No response from {ip} for JP3 PIN {pin}")
        time.sleep(DIGITAL_READ_INTERVAL_JP3)
  • Описание: Чете цифровите входове на порт JP3 на всички PicoIP устройства и публикува състоянията им в MQTT топици.
  • Как работи: Подобно на read_analog_inputs, но фокусирано върху цифровите входове на порт JP3.
  • Реализация: Използва snmpget за четене на състоянията и client.publish за публикуване на резултатите.

read_digital_inputs_jp4(client)

def read_digital_inputs_jp4(client):
    while True:
        if DIGITAL_READ_INTERVAL_JP4 > 0:
            for ip in PICOIP_DEVICES:
                if not is_device_online(ip):
                    with print_lock:
                        print(f"Device {ip} is offline. Skipping JP4 inputs.")
                    continue
                for pin in range(1, 9):
                    oid = f"1.3.6.1.4.1.19865.1.2.2.{pin}.0"
                    snmp_cmd = f"snmpget -v1 -c {SNMP_COMMUNITY} {ip} {oid}"
                    state = run_snmp_command(snmp_cmd)
                    if state:
                        state = state.split()[-1]
                        client.publish(f"picoip/{ip}/status/JP4/{pin}", state)
                        with print_lock:
                            print(f"READ JP4 PIN {pin} on {ip}: {state}")
                    else:
                        with print_lock:
                            print(f"No response from {ip} for JP4 PIN {pin}")
        time.sleep(DIGITAL_READ_INTERVAL_JP4)
  • Описание: Чете цифровите входове на порт JP4 на всички PicoIP устройства и публикува състоянията им в MQTT топици.
  • Как работи: Подобно на read_digital_inputs_jp3, но фокусирано върху цифровите входове на порт JP4.
  • Реализация: Използва snmpget за четене на състоянията и client.publish за публикуване на резултатите.

on_message(client, userdata, msg)

def on_message(client, userdata, msg):
    topic = msg.topic
    payload = msg.payload.decode("utf-8").strip()

    if topic.startswith("picoip/") and "/digitalWrite/" in topic:
        parts = topic.split("/")
        if len(parts) == 5:
            ip = parts[1]
            port = parts[3]
            pin = parts[4]
            try:
                value = int(payload)
                if value not in [0, 1]:
                    with print_lock:
                        print(f"Invalid payload for digitalWrite: {payload}")
                    return
            except ValueError:
                with print_lock:
                    print(f"Invalid payload for digitalWrite: {payload}")
                return

            oid = f"1.3.6.1.4.1.19865.1.2.{1 if port == 'JP3' else 2}.{pin}.0"
            snmp_cmd = f"snmpset -v1 -c {SNMP_COMMUNITY} {ip} {oid} i {value}"
            run_snmp_command(snmp_cmd)
            with print_lock:
                print(f"SET {port} PIN {pin} to {value} on {ip}")
  • Описание: Обработва входящите MQTT съобщения и изпълнява съответните SNMP команди за управление на цифровите изходи.
  • Как работи: Ако съобщението е за задаване на състояние на цифров изход, изпълнява съответната SNMP команда.
  • Реализация: Използва snmpset за задаване на състоянието на цифровия изход и print_lock за синхронизация на изхода в конзолата.

Обяснение на топиците

Топици за четене на състояния

  • picoip/<IP>/status/ADC/<pin>:
    • Описание: Състояние на аналогов вход (ADC) на устройството с IP <IP>.
    • Примерpicoip/192.168.1.9/status/ADC/1
    • Стойности: Числова стойност (например 153).
  • picoip/<IP>/status/JP3/<pin>:
    • Описание: Състояние на цифров вход/изход на порт JP3 на устройството с IP <IP>.
    • Примерpicoip/192.168.1.9/status/JP3/1
    • Стойности: 0 (ниско ниво) или 1 (високо ниво).
  • picoip/<IP>/status/JP4/<pin>:
    • Описание: Състояние на цифров вход/изход на порт JP4 на устройството с IP <IP>.
    • Примерpicoip/192.168.1.9/status/JP4/1
    • Стойности: 0 (ниско ниво) или 1 (високо ниво).

Топици за запис на състояния

  • picoip/<IP>/digitalWrite/JP3/<pin>:
    • Описание: Задаване на състояние на цифров изход на порт JP3 на устройството с IP <IP>.
    • Примерpicoip/192.168.1.9/digitalWrite/JP3/1
    • Стойности: 0 (ниско ниво) или 1 (високо ниво).
  • picoip/<IP>/digitalWrite/JP4/<pin>:
    • Описание: Задаване на състояние на цифров изход на порт JP4 на устройството с IP <IP>.
    • Примерpicoip/192.168.1.9/digitalWrite/JP4/1
    • Стойности: 0 (ниско ниво) или 1 (високо ниво).

Топици за грешки и съобщения

  • picoip/<IP>/error:
    • Описание: Съобщение за грешка, свързана с устройството с IP <IP>.
    • Примерpicoip/192.168.1.40/error
    • Стойности: Текстова стойност (например “No response from device“).
  • picoip/<IP>/status:
    • Описание: Съобщение за състоянието на устройството с IP <IP>.
    • Примерpicoip/192.168.1.9/status
    • Стойности: Текстова стойност (например “Device is online“).

MQTT команди

Контрол на релета (цифрови изходи)

  • Задаване на JP3, Pin 1 на HIGH: mosquitto_pub -h localhost -u ********** -P ********** -t “picoip/192.168.1.9/digitalWrite/JP3/1” -m 1
    • Описание: Задава състоянието на цифров изход на порт JP3, пин 1 на устройството с IP 192.168.1.9 на HIGH (1).
  • Задаване на JP4, Pin 2 на LOW: mosquitto_pub -h localhost -u ********** -P ********** -t “picoip/192.168.1.9/digitalWrite/JP4/2” -m 0
    • Описание: Задава състоянието на цифров изход на порт JP4, пин 2 на устройството с IP 192.168.1.9 на LOW (0).

Четене на аналогови входове (ADC)

  • Абониране за ADC, Pin 1: mosquitto_sub -h localhost -u ********** -P ********** -t “picoip/192.168.1.9/status/ADC/1”
    • Описание: Абонира се за състоянието на аналогов вход (ADC) на пин 1 на устройството с IP 192.168.1.9.
  • Абониране за всички ADC входове: mosquitto_sub -h localhost -u ********** -P ********** -t “picoip/192.168.1.9/status/ADC/#”
    • Описание: Абонира се за всички аналогови входове (ADC) на устройството с IP 192.168.1.9.

Четене на цифрови входове (JP3 и JP4)

  • Абониране за JP3, Pin 1: mosquitto_sub -h localhost -u ********** -P ********** -t “picoip/192.168.1.9/status/JP3/1”
    • Описание: Абонира се за състоянието на цифров вход/изход на порт JP3, пин 1 на устройството с IP 192.168.1.9.
  • Абониране за JP4, Pin 2: mosquitto_sub -h localhost -u ********** -P ********** -t “picoip/192.168.1.9/status/JP4/2”
    • Описание: Абонира се за състоянието на цифров вход/изход на порт JP4, пин 2 на устройството с IP 192.168.1.9.
  • Абониране за всички цифрови входове: mosquitto_sub -h localhost -u ********** -P ********** -t “picoip/192.168.1.9/status/JP3/#” mosquitto_sub -h localhost -u ********** -P ********** -t “picoip/192.168.1.9/status/JP4/#”
    • Описание: Абонира се за всички цифрови входове/изходи на портове JP3 и JP4 на устройството с IP 192.168.1.9.

Проверка на състоянието на устройството и грешки

  • Абониране за съобщения за грешки: mosquitto_sub -h localhost -u ********** -P ********** -t “picoip/192.168.1.9/error”
    • Описание: Абонира се за съобщения за грешки, свързани с устройството с IP 192.168.1.9.
  • Абониране за съобщения за състоянието: mosquitto_sub -h localhost -u ********** -P ********** -t “picoip/192.168.1.9/status”
    • Описание: Абонира се за съобщения за състоянието на устройството с IP 192.168.1.9.

Заключение

В тази статия разгледахме как да изградим мост между MQTT и SNMP за управление на PicoIP устройства. Кодът, който разгледахме, позволява на потребителите да управляват и мониторират устройства чрез MQTT топици, като използва SNMP за комуникация с устройствата. Разгледахме подробно всяка част от кода, функциите, топиците и командите, които се използват.

Основната идея на този код е да осигури лесен начин за автоматизация на процеси, мониторинг на състояния и управление на устройства в мрежа. Той е предназначен за платформи, които поддържат Python и имат инсталиран MQTT брокер. Надяваме се, че тази статия ви е била полезна и че ще успеете да изградите своя собствен мост между MQTT и SNMP за управление на PicoIP устройства. Линк към фирмата производител: NEOMONTANA ELECTRONICS

Translate »