
Въведение
В тази статия ще разгледаме как да изградим МОСТ (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, разгледайте нашите статии и се научете как става това:
- Инсталиране на Mosquitto MQTT Broker на Raspberry Pi 4
- Инсталиране Mosquitto MQTT Broker на Orange Pi Plus 2 и Armbian Linux
В статията ще разгледаме подробно всяка част от написания от нас код както и неговите функции, топици и команди, които се използват за управление на PicoIP платформата. Ако все още не сте се запознали с тази платформа може да посетите другите наши статии с ревюта, описания, код и написани от нас библиотеки за нейното използване.
- Ревю: PicoIP v1.2
- Ревю: PicoIP v2
- Първи стъпки с PicoIP: Конфигуриране чрез WEB интерфейс
- Управление на PicoIP с помощта на SNMP и Shell скриптове
- PicoIP Python библиотека за управление на пинове
В тази статия за демонстрация на кода ще използваме разработените от нас хардуерни платформи базирани на 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_JP3, DIGITAL_READ_INTERVAL_JP4, ANALOG_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).
- Описание: Състояние на аналогов вход (ADC) на устройството с IP
- picoip/<IP>/status/JP3/<pin>:
- Описание: Състояние на цифров вход/изход на порт JP3 на устройството с IP
<IP>
. - Пример:
picoip/192.168.1.9/status/JP3/1
- Стойности: 0 (ниско ниво) или 1 (високо ниво).
- Описание: Състояние на цифров вход/изход на порт JP3 на устройството с IP
- picoip/<IP>/status/JP4/<pin>:
- Описание: Състояние на цифров вход/изход на порт JP4 на устройството с IP
<IP>
. - Пример:
picoip/192.168.1.9/status/JP4/1
- Стойности: 0 (ниско ниво) или 1 (високо ниво).
- Описание: Състояние на цифров вход/изход на порт JP4 на устройството с IP
Топици за запис на състояния
- picoip/<IP>/digitalWrite/JP3/<pin>:
- Описание: Задаване на състояние на цифров изход на порт JP3 на устройството с IP
<IP>
. - Пример:
picoip/192.168.1.9/digitalWrite/JP3/1
- Стойности: 0 (ниско ниво) или 1 (високо ниво).
- Описание: Задаване на състояние на цифров изход на порт JP3 на устройството с IP
- picoip/<IP>/digitalWrite/JP4/<pin>:
- Описание: Задаване на състояние на цифров изход на порт JP4 на устройството с IP
<IP>
. - Пример:
picoip/192.168.1.9/digitalWrite/JP4/1
- Стойности: 0 (ниско ниво) или 1 (високо ниво).
- Описание: Задаване на състояние на цифров изход на порт JP4 на устройството с IP
Топици за грешки и съобщения
- picoip/<IP>/error:
- Описание: Съобщение за грешка, свързана с устройството с IP
<IP>
. - Пример:
picoip/192.168.1.40/error
- Стойности: Текстова стойност (например “No response from device“).
- Описание: Съобщение за грешка, свързана с устройството с IP
- picoip/<IP>/status:
- Описание: Съобщение за състоянието на устройството с IP
<IP>
. - Пример:
picoip/192.168.1.9/status
- Стойности: Текстова стойност (например “Device is online“).
- Описание: Съобщение за състоянието на устройството с IP
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).
- Описание: Задава състоянието на цифров изход на порт JP3, пин 1 на устройството с IP
- Задаване на 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).
- Описание: Задава състоянието на цифров изход на порт JP4, пин 2 на устройството с IP
Четене на аналогови входове (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) на пин 1 на устройството с IP
- Абониране за всички ADC входове: mosquitto_sub -h localhost -u ********** -P ********** -t “picoip/192.168.1.9/status/ADC/#”
- Описание: Абонира се за всички аналогови входове (ADC) на устройството с IP
192.168.1.9
.
- Описание: Абонира се за всички аналогови входове (ADC) на устройството с IP
Четене на цифрови входове (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
.
- Описание: Абонира се за състоянието на цифров вход/изход на порт JP3, пин 1 на устройството с IP
- Абониране за 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
.
- Описание: Абонира се за състоянието на цифров вход/изход на порт JP4, пин 2 на устройството с IP
- Абониране за всички цифрови входове: 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
.
- Описание: Абонира се за всички цифрови входове/изходи на портове JP3 и JP4 на устройството с IP
Проверка на състоянието на устройството и грешки
- Абониране за съобщения за грешки: mosquitto_sub -h localhost -u ********** -P ********** -t “picoip/192.168.1.9/error”
- Описание: Абонира се за съобщения за грешки, свързани с устройството с IP
192.168.1.9
.
- Описание: Абонира се за съобщения за грешки, свързани с устройството с IP
- Абониране за съобщения за състоянието: mosquitto_sub -h localhost -u ********** -P ********** -t “picoip/192.168.1.9/status”
- Описание: Абонира се за съобщения за състоянието на устройството с IP
192.168.1.9
.
- Описание: Абонира се за съобщения за състоянието на устройството с IP
Заключение
В тази статия разгледахме как да изградим мост между MQTT и SNMP за управление на PicoIP устройства. Кодът, който разгледахме, позволява на потребителите да управляват и мониторират устройства чрез MQTT топици, като използва SNMP за комуникация с устройствата. Разгледахме подробно всяка част от кода, функциите, топиците и командите, които се използват.
Основната идея на този код е да осигури лесен начин за автоматизация на процеси, мониторинг на състояния и управление на устройства в мрежа. Той е предназначен за платформи, които поддържат Python и имат инсталиран MQTT брокер. Надяваме се, че тази статия ви е била полезна и че ще успеете да изградите своя собствен мост между MQTT и SNMP за управление на PicoIP устройства. Линк към фирмата производител: NEOMONTANA ELECTRONICS