~nabijaczleweli/ORNO-OR-WE-504-505-507-snmp

Modbus and impulse ORNO electricity meter SNMP export and Prometheus adapter.
im fuckin done dude
ProtectSystem=strict

refs

trunk
browse  log 

clone

read-only
https://git.sr.ht/~nabijaczleweli/ORNO-OR-WE-504-505-507-snmp
read/write
git@git.sr.ht:~nabijaczleweli/ORNO-OR-WE-504-505-507-snmp

You can also use your local clone with git send-email.

#ORNO-OR-WE-504-505-507-snmp builds.sr.ht build status Licence

Modbus and impulse ORNO electricity meter SNMP export and Prometheus adapter.

https://cohost.org/nabijaczleweli/post/1310764-new-old-hardware

Build with CPPFLAGS=-DFAUX in the environment to build fake increment-only drivers.

You probably want make installmib on clients.

$ snmpbulkwalk -Cc ciastko-malinowe ORNO-MIB::orno
ORNO-MIB::ornoOrWe504DeviceIndex.1 = INTEGER: 1
ORNO-MIB::ornoOrWe504Voltage.1 = INTEGER: 239.9 V
ORNO-MIB::ornoOrWe504Current.1 = INTEGER: 1.3 A
ORNO-MIB::ornoOrWe504Frequency.1 = INTEGER: 50.0 Hz
ORNO-MIB::ornoOrWe504ActivePower.1 = INTEGER: 235 W
ORNO-MIB::ornoOrWe504ReactivePower.1 = INTEGER: 22 VAr
ORNO-MIB::ornoOrWe504ApparentPower.1 = INTEGER: 335 VA
ORNO-MIB::ornoOrWe504PowerFactor.1 = INTEGER: .753
ORNO-MIB::ornoOrWe504TotalEnergy.1 = INTEGER: 4049071 Wh
ORNO-MIB::ornoOrWe504TotalEnergyReactive.1 = INTEGER: 604615 VArh
ORNO-MIB::ornoOrWe504Serial.1 = STRING: Pmpa1F
ORNO-MIB::ornoOrWe504BaudRate.1 = INTEGER: 4
ORNO-MIB::ornoOrWe504Address.1 = INTEGER: 1
ORNO-MIB::ornoOrWe504DeviceIndex.2 = INTEGER: 2
ORNO-MIB::ornoOrWe504Voltage.2 = INTEGER: 242.9 V
ORNO-MIB::ornoOrWe504Current.2 = INTEGER: .2 A
ORNO-MIB::ornoOrWe504Frequency.2 = INTEGER: 50.0 Hz
ORNO-MIB::ornoOrWe504ActivePower.2 = INTEGER: 34 W
ORNO-MIB::ornoOrWe504ReactivePower.2 = INTEGER: 11 VAr
ORNO-MIB::ornoOrWe504ApparentPower.2 = INTEGER: 50 VA
ORNO-MIB::ornoOrWe504PowerFactor.2 = INTEGER: .699
ORNO-MIB::ornoOrWe504TotalEnergy.2 = INTEGER: 1290398 Wh
ORNO-MIB::ornoOrWe504TotalEnergyReactive.2 = INTEGER: 364336 VArh
ORNO-MIB::ornoOrWe504Serial.2 = STRING: wentyl
ORNO-MIB::ornoOrWe504BaudRate.2 = INTEGER: 4
ORNO-MIB::ornoOrWe504Address.2 = INTEGER: 2
ORNO-MIB::ornoOrWe504DeviceIndex.3 = INTEGER: 3
ORNO-MIB::ornoOrWe504Voltage.3 = INTEGER: 242.9 V
ORNO-MIB::ornoOrWe504Current.3 = INTEGER: .7 A
ORNO-MIB::ornoOrWe504Frequency.3 = INTEGER: 50.0 Hz
ORNO-MIB::ornoOrWe504ActivePower.3 = INTEGER: 139 W
ORNO-MIB::ornoOrWe504ReactivePower.3 = INTEGER: 172 VAr
ORNO-MIB::ornoOrWe504ApparentPower.3 = INTEGER: 186 VA
ORNO-MIB::ornoOrWe504PowerFactor.3 = INTEGER: .817
ORNO-MIB::ornoOrWe504TotalEnergy.3 = INTEGER: 8814154 Wh
ORNO-MIB::ornoOrWe504TotalEnergyReactive.3 = INTEGER: 7235196 VArh
ORNO-MIB::ornoOrWe504Serial.3 = STRING: ko+RTR
ORNO-MIB::ornoOrWe504BaudRate.3 = INTEGER: 4
ORNO-MIB::ornoOrWe504Address.3 = INTEGER: 3
ORNO-MIB::ornoOrWe505DeviceIndex.0 = INTEGER: 0
ORNO-MIB::ornoOrWe505Duration.0 = Timeticks: (8644738) 1 day, 0:00:47.38
ORNO-MIB::ornoOrWe505Energy.0 = INTEGER: 40841 Wh
ORNO-MIB::ornoOrWe505DeviceIndex.1 = INTEGER: 1
ORNO-MIB::ornoOrWe505Duration.1 = Timeticks: (8644750) 1 day, 0:00:47.50
ORNO-MIB::ornoOrWe505Energy.1 = INTEGER: 19601 Wh

#SNMP MIB

$ snmptranslate .iso.org.dod.internet.private.enterprises.orno -Tp
+--orno(69)
   |
   +--ornoMIB(0)
   |
   +--ornoWe504Table(504)
   |  |
   |  +--ornoOrWe504Entry(0)
   |     |  Index: ornoOrWe504DeviceIndex
   |     |
   |     +-- -R-- Integer32 ornoOrWe504DeviceIndex(0)
   |     |        Range: 1..255
   |     +-- -R-- Integer32 ornoOrWe504Voltage(1)
   |     |        Textual Convention: Tenths
   |     +-- -R-- Integer32 ornoOrWe504Current(2)
   |     |        Textual Convention: Tenths
   |     +-- -R-- Integer32 ornoOrWe504Frequency(3)
   |     |        Textual Convention: Tenths
   |     +-- -R-- Integer32 ornoOrWe504ActivePower(4)
   |     +-- -R-- Integer32 ornoOrWe504ReactivePower(5)
   |     +-- -R-- Integer32 ornoOrWe504ApparentPower(6)
   |     +-- -R-- Integer32 ornoOrWe504PowerFactor(7)
   |     |        Textual Convention: Thousandths
   |     +-- -R-- Integer32 ornoOrWe504TotalEnergy(8)
   |     +-- -R-- Integer32 ornoOrWe504TotalEnergyReactive(9)
   |     +-- -R-- String    ornoOrWe504Serial(10)
   |     |        Textual Convention: OrnoSerial
   |     |        Size: 6
   |     +-- -R-- Integer32 ornoOrWe504BaudRate(11)
   |     +-- -R-- Integer32 ornoOrWe504Address(12)
   |              Range: 1..247
   |
   +--ornoWe505Table(505)
      |
      +--ornoOrWe505Entry(0)
         |  Index: ornoOrWe505DeviceIndex
         |
         +-- -R-- Integer32 ornoOrWe505DeviceIndex(0)
         +-- -R-- TimeTicks ornoOrWe505Duration(1)
         +-- -R-- Integer32 ornoOrWe505Energy(2)

#ORNO-OR-WE-504 (other modbus? untested, unknown)

The manual is very good™.

Despite "the next step [being] to fill out the »groupBox2« field", it largely just works when you connect to it.

snmpd.conf:

pass_persist .1.3.6.1.4.1.69.504 /use/local/libexec/ORNO-OR-WE-504-snmp /dev/ttyUSB0

Where /dev/ttyUSB0 is the same path you gave to ORNO-OR-WE-504-modbus as the only argument; the IPC channel is created 0440, so take the credentials into account; the systemd service has it pre-accounted-for and you'd want to run ORNO-OR-WE-504-modbus@ttyUSB0 for this case.

ORNO-OR-WE-dumpy 504 /dev/ttyUSB0 can be used to do a GETNEXT walk against the driver directly, cf. image.

Create /run/ORNO-OR-WE-504-modbus.debug to enable debug logging.

#Multidrop

By default, every meter has address 1. You must set the address of each meter explicitly for multidrop to work.

According to this unearthed manual, that is

01   06   00 06    00 02   e8 0a
addr func register newaddr crc

and the register to write to (6) is not the register that the address is returned in (15). Unsurprisingly it does not work.

I haven't managed to come up with a way to talk to the device via libmodbus or manually, most likely due to the special-sauce packet type you need to send the passphrase (0x28) and modbus's precise timing requirements that libmodbus boasts about not supporting (but see the recorded traces and partial setup program in Attic/).

The only way I've managed to find to configure the meter is via their EXE, and even then you must set the passphrase field to exactly 8 0s, otherwise it doesn't work.

If you set the serial number to bytes of an ASCII string (max 6), that's supported.

As-is, the driver expects to just directly enumerate modbus addresses 1:1 public devices, and EOFs when it finds the first one it doesn't know. It'd be relatively trivial to map public-facing device numbers to modbus addresses intelligently if thats the setup you have.

#PHY

Two wires (I used twisted pair because I have hundreds of meters of 4-pair UTP in the basement, but i started with a literal-trash cut-apart USB cable for testing, which also worked, so you can probably run it over wet string), tapped at each meter, connected to a CH340 (ch341) via USB. Just works™.

Tapping into the exposed UART pins directly doesn't appear to work at all, but replacing the horrific USB stack-up (and, on upstream device trees, disabling USB OTG handling) with more directly-soldered twisted pair works around that.

#IPC

SysV message queue on /dev/ttyUSB0, project 'ORNO', messages get_dev(device), devinfo[device](found, reading).
Clients send get_dev, then listen for devinfo[device-they-asked-for].
Clients cache the latest valid response for 5 seconds, sufficently fast GET(BULK)NEXT walks thus only probe each device once.
Driver destroys on exit/INT/TERM, clients get EIDRMed and exit automatically.

#ORNO-OR-WE-505 and ORNO-OR-WE-507 (and most likely other impulse)

The manuals are, I daresay, silent on what happens.

snmpd.conf:

pass_persist .1.3.6.1.4.1.69.505 /usr/local/libexec/ORNO-OR-WE-505-snmp /ORNO-OR-WE-505

Where /ORNO-OR-WE-505 is the same path you gave to ORNO-OR-WE-505-gpio – a shm_open(2)-compatible name. ORNO-OR-WE-dumpy 505 /ORNO-OR-WE-505 can be used to do a GETNEXT walk against the driver directly

ORNO-OR-WE-505-gpio expects to be run as-if ORNO-OR-WE-505-gpio /ORNO-OR-WE-505 5 6 < /dev/gpiochip0 2>/sys/class/leds/ACT/brightness (i.e. with a list of physical GPIO line numbers; on the RPi these match the "GPIOn" convention, but I'd recommend just lsgpioing it).
The standard error stream redirect is optional, but that file can be used as a sum impulse activity blinker.

The systemd service takes care of this, and you can systemd enable "ORNO-OR-WE-505-gpio@$(systemd-escape '5 6')" (O-O-W-5-g@5\x206), or something similar.

In this configuration, -DFAUX would have the meters trigger every 5 and 6 seconds, respectively.

#PHY

According to brain geniuses on Elektroda, the pulse output is optocoupler C-E, and my rudimentary tests agree with this diagnosis, so can be modelled as such:
GPIO56 connected directly to +, GND connected to -

What the brain geniuses get wrong is that the pulse width isn't anywhere close to 90ms; I measure an average of 37.54ms on the 507 and 33.88ms on the 505.

And triggering on the falling edge works with a RPi0W (yes, yes, I know; it's what I had, wouldn't be buying new) with the internal pull-ups, so no external passives, which is pog.

Except if you're in USB host mode. Then you need to trigger on both edges, because if you trigger on falling, you still get both, but both tagged as falling. It's unclear to me if this is a linux bug or a bcm2835 bug, but #1032042, so unfortunately we have to trigger on both and ignore rising:

# lsgpio | grep -e ^GPIO -e ORNO
GPIO chip: gpiochip0, "pinctrl-bcm2835", 54 GPIO lines
        line  5: "GPIO5" "ORNO-OR-WE-505-gpio.c" [used, input, pull-up, both-edges]
        line  6: "GPIO6" "ORNO-OR-WE-505-gpio.c" [used, input, pull-up, both-edges]

(or be in USB OTG mode and configure the OTG ID pin correctly, so I guess this is a cfringe case for if you're hard-wiring the USB).

Still, I see some bouncing, which appears proportional to the average power. Revert and adapt 037198b to compare and cumquat.

#IPC

POSIX shared memory on /ORNO-OR-WE-505, size+flexarray of (energy, duration), where duration is a size-2 ring buffer indexed by energy.
Clients wait until size is nonzero, then use transparently; driver updates the readings and the elapsed time when it gets impulses.
Driver sets device count to 0 before dying, clients check this and exit as well.

#Prometheus exporter

If you can get prometheus-snmp-exporter to work, then more power to ya, but it appeared fundamentally broken to me on bullseye and the sid version refused to build; and when I guessed the environment variables, it was still underwhelming.

ORNO-OR-WE-504-505-507-prometheus assigns the index, serial, and address as the label for all devices. Naturally, impulse meters don't have a serial or an address, so those are by default not set, but you can give index→serial mappings for them.

make installprom installs only the exporter; requires properly-configured (mibs +ORNO-MIB, mibdirs …:/usr/local/share/snmp/mibs) snmp.conf. If you have libsnmp-dev, it'll link to that; if not, it'll exec snmpbulkwalk.

$ systemctl status 'ORNO-OR-WE-504-505-507-prometheus@localhost:9928\x20ciastko\x2dmalinowe\x200\x3dwszystko\x201\x3dpompa'
● ORNO-OR-WE-504-505-507-prometheus@localhost:9928\x20ciastko\x2dmalinowe\x200\x3dwszystko\x201\x3dpompa.service - Extract ORNO OR-WE-504, -505, and -507 meters for consumption with Prometheus: localhost:9928 ciastko-malinowe 0=wszystko 1=pompa
     Loaded: loaded (/usr/local/lib/systemd/system/ORNO-OR-WE-504-505-507-prometheus@.service; enabled; vendor preset: enabled)
     Active: active (running) since Wed 2023-03-01 00:09:00 CET; 4min 58s ago
   Main PID: 4187087 (ORNO-OR-WE-504-)
      Tasks: 1 (limit: 115959)
     Memory: 196.0K
        CPU: 287ms
     CGroup: /system.slice/system-ORNO\x2dOR\x2dWE\x2d504\x2d505\x2d507\x2dprometheus.slice/ORNO-OR-WE-504-505-507-prometheus@localhost:9928\x20ciastko\x2dmalinowe\x200\x3dwszystko\x201\x3dpompa.service
             └─4187087 /usr/local/libexec/ORNO-OR-WE-504-505-507-prometheus localhost:9928 ciastko-malinowe 0 wszystko 1 pompa

Mar 01 00:09:00 tarta systemd[1]: Started Extract ORNO OR-WE-504, -505, and -507 meters for consumption with Prometheus: localhost:9928 ciastko-malinowe 0=wszystko 1=pompa.

$ curl localhost:9928
# TYPE ornoOrWe504Voltage gauge
# HELP ornoOrWe504Voltage [V]
ornoOrWe504Voltage{index="1",serial="Pmpa1F",address="1"} 242.1
# TYPE ornoOrWe504Current gauge
# HELP ornoOrWe504Current [A]
ornoOrWe504Current{index="1",serial="Pmpa1F",address="1"} 1.3
# TYPE ornoOrWe504Frequency gauge
# HELP ornoOrWe504Frequency [Hz]
ornoOrWe504Frequency{index="1",serial="Pmpa1F",address="1"} 50.0
# TYPE ornoOrWe504ActivePower gauge
# HELP ornoOrWe504ActivePower [W]
ornoOrWe504ActivePower{index="1",serial="Pmpa1F",address="1"} 216
# TYPE ornoOrWe504ReactivePower gauge
# HELP ornoOrWe504ReactivePower [VAr]
ornoOrWe504ReactivePower{index="1",serial="Pmpa1F",address="1"} 15
# TYPE ornoOrWe504ApparentPower gauge
# HELP ornoOrWe504ApparentPower [VA]
ornoOrWe504ApparentPower{index="1",serial="Pmpa1F",address="1"} 321
# TYPE ornoOrWe504PowerFactor gauge
ornoOrWe504PowerFactor{index="1",serial="Pmpa1F",address="1"} .686
# TYPE ornoOrWe504TotalEnergy gauge
# HELP ornoOrWe504TotalEnergy [Wh]
ornoOrWe504TotalEnergy{index="1",serial="Pmpa1F",address="1"} 4051671
# TYPE ornoOrWe504TotalEnergyReactive gauge
# HELP ornoOrWe504TotalEnergyReactive [VArh]
ornoOrWe504TotalEnergyReactive{index="1",serial="Pmpa1F",address="1"} 604993
# TYPE ornoOrWe504BaudRate gauge
ornoOrWe504BaudRate{index="1",serial="Pmpa1F",address="1"} 4
# TYPE ornoOrWe504Voltage gauge
# HELP ornoOrWe504Voltage [V]
ornoOrWe504Voltage{index="2",serial="wentyl",address="2"} 245.6
# TYPE ornoOrWe504Current gauge
# HELP ornoOrWe504Current [A]
ornoOrWe504Current{index="2",serial="wentyl",address="2"} .2
# TYPE ornoOrWe504Frequency gauge
# HELP ornoOrWe504Frequency [Hz]
ornoOrWe504Frequency{index="2",serial="wentyl",address="2"} 50.0
# TYPE ornoOrWe504ActivePower gauge
# HELP ornoOrWe504ActivePower [W]
ornoOrWe504ActivePower{index="2",serial="wentyl",address="2"} 34
# TYPE ornoOrWe504ReactivePower gauge
# HELP ornoOrWe504ReactivePower [VAr]
ornoOrWe504ReactivePower{index="2",serial="wentyl",address="2"} 13
# TYPE ornoOrWe504ApparentPower gauge
# HELP ornoOrWe504ApparentPower [VA]
ornoOrWe504ApparentPower{index="2",serial="wentyl",address="2"} 49
# TYPE ornoOrWe504PowerFactor gauge
ornoOrWe504PowerFactor{index="2",serial="wentyl",address="2"} .692
# TYPE ornoOrWe504TotalEnergy gauge
# HELP ornoOrWe504TotalEnergy [Wh]
ornoOrWe504TotalEnergy{index="2",serial="wentyl",address="2"} 1291292
# TYPE ornoOrWe504TotalEnergyReactive gauge
# HELP ornoOrWe504TotalEnergyReactive [VArh]
ornoOrWe504TotalEnergyReactive{index="2",serial="wentyl",address="2"} 364646
# TYPE ornoOrWe504BaudRate gauge
ornoOrWe504BaudRate{index="2",serial="wentyl",address="2"} 4
# TYPE ornoOrWe504Voltage gauge
# HELP ornoOrWe504Voltage [V]
ornoOrWe504Voltage{index="3",serial="ko+RTR",address="3"} 245.6
# TYPE ornoOrWe504Current gauge
# HELP ornoOrWe504Current [A]
ornoOrWe504Current{index="3",serial="ko+RTR",address="3"} .7
# TYPE ornoOrWe504Frequency gauge
# HELP ornoOrWe504Frequency [Hz]
ornoOrWe504Frequency{index="3",serial="ko+RTR",address="3"} 50.0
# TYPE ornoOrWe504ActivePower gauge
# HELP ornoOrWe504ActivePower [W]
ornoOrWe504ActivePower{index="3",serial="ko+RTR",address="3"} 130
# TYPE ornoOrWe504ReactivePower gauge
# HELP ornoOrWe504ReactivePower [VAr]
ornoOrWe504ReactivePower{index="3",serial="ko+RTR",address="3"} 175
# TYPE ornoOrWe504ApparentPower gauge
# HELP ornoOrWe504ApparentPower [VA]
ornoOrWe504ApparentPower{index="3",serial="ko+RTR",address="3"} 179
# TYPE ornoOrWe504PowerFactor gauge
ornoOrWe504PowerFactor{index="3",serial="ko+RTR",address="3"} .756
# TYPE ornoOrWe504TotalEnergy gauge
# HELP ornoOrWe504TotalEnergy [Wh]
ornoOrWe504TotalEnergy{index="3",serial="ko+RTR",address="3"} 8816128
# TYPE ornoOrWe504TotalEnergyReactive gauge
# HELP ornoOrWe504TotalEnergyReactive [VArh]
ornoOrWe504TotalEnergyReactive{index="3",serial="ko+RTR",address="3"} 7238745
# TYPE ornoOrWe504BaudRate gauge
ornoOrWe504BaudRate{index="3",serial="ko+RTR",address="3"} 4
# TYPE ornoOrWe505Duration counter
ornoOrWe505Duration{index="0",serial="wszystko"} 8168688
# TYPE ornoOrWe505Energy counter
# HELP ornoOrWe505Energy [Wh]
ornoOrWe505Energy{index="0",serial="wszystko"} 41043
# TYPE ornoOrWe505Duration counter
ornoOrWe505Duration{index="1",serial="pompa"} 8168688
# TYPE ornoOrWe505Energy counter
# HELP ornoOrWe505Energy [Wh]
ornoOrWe505Energy{index="1",serial="pompa"} 23183
# EOF

#Perf

On a Raspberry Pi Zero W, as polled by watch -pn60 snmpbulkwalk -Cc ciastko-malinowe ORNO-MIB::orno:

$ systemctl status ORNO-OR-WE-504-modbus@ttyUSB0.service 'ORNO-OR-WE-505-gpio@5\x206.service' snmpd
● ORNO-OR-WE-504-modbus@ttyUSB0.service - Scrape ORNO OR-WE-504 meters over modbus on device ttyUSB0
     Loaded: loaded (/usr/local/lib/systemd/system/ORNO-OR-WE-504-modbus@.service; enabled; vendor preset: enabled)
     Active: active (running) since Mon 2023-02-27 01:11:34 CET; 24h ago
   Main PID: 780 (ORNO-OR-WE-504-)
      Tasks: 1 (limit: 853)
     Memory: 96.0K
        CPU: 13.802s
     CGroup: /system.slice/system-ORNO\x2dOR\x2dWE\x2d504\x2dmodbus.slice/ORNO-OR-WE-504-modbus@ttyUSB0.service
             └─780 /usr/local/libexec/ORNO-OR-WE-504-modbus /dev/ttyUSB0

Feb 27 01:11:34 ciastko-malinowe systemd[1]: Started Scrape ORNO OR-WE-504 meters over modbus on device ttyUSB0.

● ORNO-OR-WE-505-gpio@5\x206.service - Scrape ORNO OR-WE-505 and -507 meters over GPIO pins 5 6
     Loaded: loaded (/usr/local/lib/systemd/system/ORNO-OR-WE-505-gpio@.service; enabled; vendor preset: enabled)
     Active: active (running) since Mon 2023-02-27 01:11:34 CET; 24h ago
   Main PID: 781 (ORNO-OR-WE-505-)
      Tasks: 1 (limit: 853)
     Memory: 148.0K
        CPU: 10.256s
     CGroup: /system.slice/system-ORNO\x2dOR\x2dWE\x2d505\x2dgpio.slice/ORNO-OR-WE-505-gpio@5\x206.service
             └─781 /usr/local/libexec/ORNO-OR-WE-505-gpio /ORNO-OR-WE-505 5 6

Feb 27 01:11:34 ciastko-malinowe systemd[1]: Started Scrape ORNO OR-WE-505 and -507 meters over GPIO pins 5 6.
Feb 27 01:11:34 ciastko-malinowe sh[781]: gpiochip0: pinctrl-bcm2835 w/54 lines
Feb 27 01:11:34 ciastko-malinowe sh[781]: meter 0: line 5: GPIO5
Feb 27 01:11:34 ciastko-malinowe sh[781]: meter 1: line 6: GPIO6

● snmpd.service - Simple Network Management Protocol (SNMP) Daemon.
     Loaded: loaded (/lib/systemd/system/snmpd.service; enabled; vendor preset: enabled)
    Drop-In: /etc/systemd/system/snmpd.service.d
             └─override.conf
     Active: active (running) since Mon 2023-02-27 01:11:34 CET; 24h ago
    Process: 782 ExecStartPre=/bin/mkdir -p /var/run/agentx (code=exited, status=0/SUCCESS)
   Main PID: 783 (snmpd)
      Tasks: 3 (limit: 853)
     Memory: 1.0M
        CPU: 2min 420ms
     CGroup: /system.slice/snmpd.service
             ├─783 /usr/sbin/snmpd -LOw -u Debian-snmp -g Debian-snmp -I system_mib snmp_mib hr_system lmsensorsMib pass_persist extend -f -p /run/snmpd.pid
             ├─786 /usr/local/libexec/ORNO-OR-WE-504-snmp /dev/ttyUSB0
             └─787 /usr/local/libexec/ORNO-OR-WE-505-snmp /ORNO-OR-WE-505

Feb 27 01:11:34 ciastko-malinowe snmpd[783]: Cannot find module (NET-SNMP-PASS-MIB): At line 0 in (none)
Feb 27 01:11:34 ciastko-malinowe snmpd[783]: Cannot find module (NET-SNMP-EXTEND-MIB): At line 0 in (none)
Feb 27 01:11:34 ciastko-malinowe snmpd[783]: Cannot find module (SNMP-NOTIFICATION-MIB): At line 0 in (none)
Feb 27 01:11:34 ciastko-malinowe snmpd[783]: Cannot find module (SNMPv2-TM): At line 0 in (none)
Feb 27 01:11:34 ciastko-malinowe snmpd[783]: Cannot find module (NET-SNMP-VACM-MIB): At line 0 in (none)
Feb 27 01:11:35 ciastko-malinowe snmpd[783]: Cannot rename /var/lib/snmp/snmpd.conf to /var/lib/snmp/snmpd.0.conf
Feb 27 01:11:35 ciastko-malinowe snmpd[783]: Cannot unlink /var/lib/snmp/snmpd.conf
Feb 27 01:11:35 ciastko-malinowe snmpd[783]: read_config_store open failure on /var/lib/snmp/snmpd.conf
Feb 27 01:11:35 ciastko-malinowe snmpd[783]: read_config_store open failure on /var/lib/snmp/snmpd.conf
Feb 27 01:11:35 ciastko-malinowe snmpd[783]: read_config_store open failure on /var/lib/snmp/snmpd.conf

Or, according to htop:

  PID+USER      PRI  NI  VIRT   RES   SHR S CPU% MEM%   TIME+  Command
  780 62627      20   0  1924   892   832 S  0.0  0.2  0:13.82 `- /usr/local/libexec/ORNO-OR-WE-504-modbus /dev/ttyUSB0
  781 64443      20   0  2400  1496  1396 S  0.0  0.3  0:10.28 `- /usr/local/libexec/ORNO-OR-WE-505-gpio /ORNO-OR-WE-505 5 6
  783 Debian-sn  20   0 16272  6584  5824 S  0.0  1.5  1:25.14 `- /usr/sbin/snmpd -LOw -u Debian-snmp -g Debian-snmp -I system_mib snmp_mib hr_system lmsensorsMib pass_pers
  786 Debian-sn  20   0  2008  1244  1176 S  0.0  0.3  0:22.82    `- /usr/local/libexec/ORNO-OR-WE-504-snmp /dev/ttyUSB0
  787 Debian-sn  20   0  2400  1424  1324 S  0.0  0.3  0:12.65    `- /usr/local/libexec/ORNO-OR-WE-505-snmp /ORNO-OR-WE-505

On a 2xE5645 system, libsnmp:

$ systemctl status 'ORNO-OR-WE-504-505-507-prometheus@localhost:9928\x20ciastko\x2dmalinowe\x200\x3dwszystko\x201\x3dpompa'
● ORNO-OR-WE-504-505-507-prometheus@localhost:9928\x20ciastko\x2dmalinowe\x200\x3dwszystko\x201\x3dpompa.service - Extract ORNO OR-WE-504, -505, and -507 meters for consumption with Prometheus: localhost:9928 ciastko-malinowe 0=wszystko 1=pompa
     Loaded: loaded (/usr/local/lib/systemd/system/ORNO-OR-WE-504-505-507-prometheus@.service; enabled; vendor preset: enabled)
     Active: active (running) since Thu 2023-03-02 00:14:12 CET; 24h ago
   Main PID: 2409827 (ORNO-OR-WE-504-)
      Tasks: 1 (limit: 115959)
     Memory: 1.3M
        CPU: 4.538s
     CGroup: /system.slice/system-ORNO\x2dOR\x2dWE\x2d504\x2d505\x2d507\x2dprometheus.slice/ORNO-OR-WE-504-505-507-prometheus@localhost:9928\x20ciastko\x2dmalinowe\x200\x3dwszystko\x201\x3dpompa.service
             └─2409827 /usr/local/libexec/ORNO-OR-WE-504-505-507-prometheus localhost:9928 ciastko-malinowe 0 wszystko 1 pompa

Mar 02 00:14:12 tarta systemd[1]: Started Extract ORNO OR-WE-504, -505, and -507 meters for consumption with Prometheus: localhost:9928 ciastko-malinowe 0=wszystko 1=pompa.

and snmpbulkwalk:

$ systemctl status 'ORNO-OR-WE-504-505-507-prometheus@localhost:9928\x20ciastko\x2dmalinowe\x200\x3dwszystko\x201\x3dpompa'
● ORNO-OR-WE-504-505-507-prometheus@localhost:9928\x20ciastko\x2dmalinowe\x200\x3dwszystko\x201\x3dpompa.service - Extract ORNO OR-WE-504, -505, and -507 meters for consumption with Prometheus: localhost:9928 ciastko-malinowe 0=wszystko 1=pompa
     Loaded: loaded (/usr/local/lib/systemd/system/ORNO-OR-WE-504-505-507-prometheus@.service; enabled; vendor preset: enabled)
     Active: active (running) since Wed 2023-03-01 00:09:00 CET; 24h ago
   Main PID: 4187087 (ORNO-OR-WE-504-)
      Tasks: 1 (limit: 115959)
     Memory: 196.0K
        CPU: 56.255s
     CGroup: /system.slice/system-ORNO\x2dOR\x2dWE\x2d504\x2d505\x2d507\x2dprometheus.slice/ORNO-OR-WE-504-505-507-prometheus@localhost:9928\x20ciastko\x2dmalinowe\x200\x3dwszystko\x201\x3dpompa.service
             └─4187087 /usr/local/libexec/ORNO-OR-WE-504-505-507-prometheus localhost:9928 ciastko-malinowe 0 wszystko 1 pompa

Mar 01 00:09:00 tarta systemd[1]: Started Extract ORNO OR-WE-504, -505, and -507 meters for consumption with Prometheus: localhost:9928 ciastko-malinowe 0=wszystko 1=pompa.

(indeed, while the actual SNMP transaxion is relatively cheap, the libsnmp initialisation, which reads and parses 644 files in the world's weirdest format, is the killer here).