From 8afdc890f1033748e9f85e6105e3744ef40fda96 Mon Sep 17 00:00:00 2001 From: marcel Date: Mon, 11 Aug 2025 21:31:31 +0200 Subject: [PATCH] First sort of working mqtt routines --- README.md | 28 +++- .../__pycache__/config_reader.cpython-311.pyc | Bin 0 -> 4786 bytes .../__pycache__/config_reader.cpython-36.pyc | Bin 2868 -> 3267 bytes .../modbus_control.cpython-311.pyc | Bin 0 -> 5381 bytes .../__pycache__/modbus_control.cpython-36.pyc | Bin 3427 -> 3659 bytes ...bus_definition_file_reader.cpython-311.pyc | Bin 0 -> 2942 bytes .../mqtt_callbacks.cpython-311.pyc | Bin 0 -> 1092 bytes software/test_software/config.yml | 14 +- software/test_software/config_reader.py | 21 ++- software/test_software/modbus_control.py | 43 ++++-- software/test_software/modbus_registers.yaml | 25 ++-- software/test_software/mqtt_callbacks.py | 8 ++ software/test_software/mqtt_control.py | 132 ++++++++++++++++++ software/test_software/mqtt_control_simple.py | 89 ++++++++++++ .../weather_station_rs485_client.py | 114 ++++++++++++++- 15 files changed, 443 insertions(+), 31 deletions(-) create mode 100644 software/test_software/__pycache__/config_reader.cpython-311.pyc create mode 100644 software/test_software/__pycache__/modbus_control.cpython-311.pyc create mode 100644 software/test_software/__pycache__/modbus_definition_file_reader.cpython-311.pyc create mode 100644 software/test_software/__pycache__/mqtt_callbacks.cpython-311.pyc create mode 100644 software/test_software/mqtt_callbacks.py create mode 100644 software/test_software/mqtt_control.py create mode 100644 software/test_software/mqtt_control_simple.py diff --git a/README.md b/README.md index 1d6c284..2efc433 100644 --- a/README.md +++ b/README.md @@ -16,12 +16,38 @@ The Weipu SP1312 / S 4, a 4 pole connector with an IP68 rating can be used as th Each sensor has a bus address, which can be set by DIP switches. Input registers store sensor data while output coils can be used to send commands to the sensors. +The devices on the ModBus can be autodetected by reading input registers 1000-1039: + +|Register|description|Value| +|-|-|-| +|1000-1003 | DefineSensorSerial (unique value)| 0x4D45 followed by index. The index starts at 0x0 0x0 0x0 and is incremented for every unique device| +|1004 | Sensor Type (specifies sensor type)| See Type table| +|1005 | Sensor Version|any| +|1006-1025 | Human readable description of sensor| Every register holds two ASCII values, one in the MSByte and the other in the LSByte for a total of 40 characters. +|1026 | Number of available input registers|1-999| +|1027 | Number of available holding registers|0-999| +|1028 | Number of available coil registers|1-999| +|1029 | Number of available contact registers|0-999| + +|Type|Description| +|-|-| +|0x01|Dual temperature sensor| +|0x02|Weather station MK1| + +## Watchdog + +All devices have at least one Coil Register: the watchdog at address 0. The client software has to toggle this register at least once per minute in order to reset the watchdog. If the client fails to reset the watchdog in time or the server firmware has crashed, the device will hardware reset itself. + ## Main controller -All sensors are connected via an RS485 bus to the main controller, the client. This unit collects the sensor data and publishes the data on a tcp/ip network via MQTT and a web interface. This unit can also function as a data logger. The data logger data can be accessed via a simple API. +All sensors are connected via an RS485 bus to the main controller, the client. This unit collects the sensor data and publishes the data on a local MQTT broker and a web interface. This unit can also function as a data logger. The data logger data can be accessed via a simple API. The main controller has to know which sensors are connected. The ModBus addresses of all the sensors are stored in a configuration file. The client software can now poll these addresses and discover the sensors. +## APRS telemetry + +Data from the MQTT broker is used to generate PE1RXF APRS telemetry messages, which are send via LoRa or 1200bd APRS. + # Links Useful resources: diff --git a/software/test_software/__pycache__/config_reader.cpython-311.pyc b/software/test_software/__pycache__/config_reader.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cdfb182788c45e9f3076e6fb355ea15d41544bd3 GIT binary patch literal 4786 zcmb7IO>7fM7OwVx+|JVum@P!ompVU6B5WKF(?UwWOvo-xZ82F?QT=u zCL~KFgyz6u4w>22vWGdKIj})+;I>+6=P-x4+fhp!wS+X%?qzNsG15kJ+V{Hcj++F? zw&Ukluj*C3s`tHDuk*Lg&NzYRo79)3|0W3eH#S;NsL8zi6)+EpL}rP^Ndc9c<={7{ z2Fk(NAV&h^5|Kim5h?sQK*&@0y3cG#3QUo7MEnPc`r{M2^l86&4R6nfVwnP4WU@>Ry=X3oO#X;2>Q!10HJDyf43jByRa2&lNkvV{>Wr3kNhvPIK~U9j zd9x%_Q)Xp@>P4qLb$W(-NQDT&2iOwy?QFjDapK7j7 zM|7Cg+2bIU73gR5w#*FN^Xb+C&DjR2&f-T!6Aflmui(L?!OBIdieOMnFSB*SoBNWe zqB%yF^a^aF1hc`$78F&bb26Tu~L+92_MRHFFVcj!z6v4qb-vxlc#ON2h1NY&tSJogbc>q9a!(X^vja zO-_#vT^r9$(yP}duU?rN&QLl9#{&D0zu`EGjysr4CD{}e)qt(efb$G6NtI|xyp0$s z$jWUnNCX5`(44pUC@!kHHt%cMC;hK$Rl*k04gUkjb?lUqQlmb9QQV?hu zV`MfWg@Hz;2+)`m1==CSAWAyyPB$_HHyR3F7{T<&2jTm&7sx|$m%B>@;M_+dpw6L% zd~*g2s&v2(8M0brQ5a?51eE@KGh5Qja<(k8f~;mQ>V;}q)=VQSt8&3)x~3R~k}N<( z-GsKRWfx=-kt;yJAt;53u~XSrd}U49Fa@vWGd@#Uv}1yxXo@KaOMU*nmV6HymIQK- ztnV9m5VB%>YR=zFkdZ>`Sh3;lKMqpZKY4`GEze_+JoTg!_Xp<;8MtB!b=qh2M!iMH znZX;}WZt5Kp4#%fMW>Z&eR^-waZ+nA&(OdhaA)bTokZveZ6L6F9VgAJ`kbf=hHOH9 zoj00N$bWNHV^g}{8S6XZ|c3b#=lqR4_N$xM<-$W8h_aB+m(E9i}7HW4}1|2IKlTHU>=eV zVzvS4F1hKo9s~E(Yn>s>oEZj{=5y17#a+(C!)rdzIRC8)q3vl&aOX6PzIn97kpd2OosQA^0^rTXPd41=}rLf(|K!)Nd=!Acd!hJ6}42$t2>YI);-i zrG~cN+L`RqA(NXxxq3!b5 z@f#Z(uj^_gNN;N8zs`>pkadpUCwdxt=ImiDE5LbdU)nH#UqD zu#@|}xg`0^!CWx-_XKd?21B`M^xJ3<`8bC>@9@d}xopq}W6UrPj8}m{7y~eXb03M@ z+hPGmoBT4|G_M&I+|j5|`zVB%15hs%0FIGupcwU_7|YucH+?Pf%}Vcts?RUQptcen zjxXOb%@c-<`N{?B+2Ixdu`F}7;jS+@1!uEva8c4;g}Oj}5@2XCdlv}s7^im7ELVKk z+Xd34en0lMOSB)tIHLdr@E3}8q}IFptnTBhm#ywI&&yW##Ro&}lABg{&M$GtG~%yf zWvreUw-V#E#CRJzFbUuP@I3jZE7$cJ&;JgF?A-HGa2s!hw((Yb;; zbwOk4*fx>kU)js~S8;~l0)d(^wbx2zYT1vi)F%xB(c_Q@!;L_=bMJcJzQ2w_CSM66 zH(~W1TM4cNHyVLJ=iV2|RBiY5dh&*qyirTuSl_*8Texg9Eqs_OV1>OH4&w>ue4TKU_kx<9|G(stmt=o zH%MNs|9sUWI@tIlkd~KMZ(FIe4Z@?7fuNIP;N<-W9}RppQA?)5xdn9e$e+vY9!Jrb z)pyjD{l@A$@(Kzj>dCy7%-53ntth~J`Ngqwfqz224Cm8vd|}bVvMdO8To3@CRTcS! zAl#~os@ucxe4DErJg{UA%dO;A_ZfM&)mSh>M z$lw;m5O&OFr;)>o*xAp|v2*UZTwrHGhNZ4i0s^HH$29^;F4Q1M`U#h~$Kz+6^w<1< VjnE{=fpy!{A07EmujzFx`5)!nuL1x7 literal 0 HcmV?d00001 diff --git a/software/test_software/__pycache__/config_reader.cpython-36.pyc b/software/test_software/__pycache__/config_reader.cpython-36.pyc index f82822380342af941f2cb7ba445265fff68d7d13..144dfad7ac20f05ebbea969e54ec486e0c4e2009 100644 GIT binary patch delta 606 zcmZutOK;Oa5ZiG;_N+LF*C(!vb8tAG&l-9X#XU+8^W zh4DnMJ6HS$YQzeF&Uc&+z{dQITL*UqMRh`d@K)if{_0i7e+%Zlr5qD%>d&kRx8~ni z02ls7?M1Y5lB`sFs%bLaKj0ZJMA*`6yra+gjf01Hd)~*cvFC_%$b&mG*xQ_fv?4;u z%$ix~+Yx%m^oX4!b`5WnCjk&+B}nfOfJoLuZJS6R=O;Kn@AhdtQH6@IT15x(c-*ha zq$tz%EH2YL!%e2cR7~P>RIxNG6ZI?}?_a*DRIXZS)+$Fy3-|TWzj*NPsOl{K`QJh7 z7`TV0SgaV9!GZwT!UAv&Tl;4QdA5@dg-T+Xs14JkD3Y>FvtiL~^NIsxkZf2n3tF9@5P>s*IXO9444^hCR;PxvIH||@@;Nm=3r!WncU0bES$mw6k+x& zVq{=ocnKmjStfsGiJ2V7s>Lb<_MzhI$9JjO} z0w8TnMXVr6F%ZE9C)hzO9w6boS%{N|(Ga1Y8La*mYieFeQ6*Mwyg+Tfll{3|nEf<4 zCx7PFV&t1F$0IS>nnzX%WF!YO5DIW`bMSI-6)^(^G+A%4$H%ASC&$MZ2~4ixaR&e@ C)-c)t diff --git a/software/test_software/__pycache__/modbus_control.cpython-311.pyc b/software/test_software/__pycache__/modbus_control.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1d21c94c6f464b8880fbf98ccb6eb0890421c86b GIT binary patch literal 5381 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 Hz!Uxes+s~l delta 1008 zcmZuwO-~a+7@pak-A=b3HkJUfErK7&#t0hZVmt{F(X>H4h&85GW+>IBt+R_k(uPB? z-o`)RPw*rMZyvqqbuS(~)$k*H-)##Ex|@Aw=9zb%_nmoWzvq8na28W3=h4p(J1q6t z$(VFq=gi0K330P}&-_6*^cvgHpV$md>u>C<{%BpMh9R_ZfIr6|Cv!fxL{FacZ|q?D|R zijuXu=S$@pDmx1k<+zaV>RYl>7it{F1DJqbVArG%^mV-!TQ7x$c4?T1mDD7LhKXFN zaxg$WXv&G9od3Qp&kAxB#EIMbD0Z0b+pwTN1rQ9h)TI8xXU0%2Ft>J8V0pf(w_RIh z^${;lpQ-QMNUnlm&DtIPEdD0fzXHp{(!wHFBlYQD@!3Q~L}#2!^nqS3Xxh>mOUCuA)Y?ZZ}aFD+1{y z0f@mVqmGfLdA*s~FQN_6Xcz`5*{r^5$jXkag7=$1d3ZGl-JdG9#Jyc_xz+SltI?2Z z{#2+gB$r;!LnIvI!l3z|ep=MMNP_1OIZANxp9H64OKws(Q;P?ASfdIE*AVcO)D482 z2)7VUjv(yeB~`c$9C`Sg@fb6BlE=Bt)7;_-Zie%&rIs)jO;#KMtq!M-HTme~>xFdw EFYW--ga7~l diff --git a/software/test_software/__pycache__/modbus_definition_file_reader.cpython-311.pyc b/software/test_software/__pycache__/modbus_definition_file_reader.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4d89251bc4037fd07310fee11baacd0959d3d08f GIT binary patch literal 2942 zcmaJ@O-vhC5Ps`j8(5q2XK1TN$}5mG4%ALkrD|zJGxvY!QPj5koQpmb4_nuWSW!@{%l(0J%vFs?KLkLG#?na6LzXO>@SmaMJFo&e;$xIt7~2 zZP09(!sX_2!DZBRscstyhtr&6nCYSl3k4f*cC(DSjOPS((*ES;^a8!fY{qqq&J>m{ zGesxO6te}RI^2sZBFhZATvSnZ3~!&S-cH9H(5j<*gOr)D&*=)|0^R+hc0pC!Aa%HU zL3hz$PMtgo#lXrUwRAA3CKrF#;X}U+YTCZhStk!}WI-D^wrW}yU1qc(Sh`>hst^Hs zXMFzF?S*-ooL;1NlC!hP>G{QP;G~;{$Wyk$DtgRZ-ZH^C2qo8Tw+J??Q=_vZx8Qv8 z`uODd{34i5$HwQUN9X3~*zH-Gq%+Cc`SFp3$>c1ZS(u%8lQ96RGX2G<_~1uPm*Q`>O| zsQ}BaWnDKfh7yU@)zx^$F2o(4NkFPG%_1=zS6?BBr(=?*&gp43=^#o|Rb=}=QgyO_ z9+)k{2vh}G3K-HuvLqV;po$>_4PtF9g^VE9g4c71e+}`kRq&sxR}CmbUWR8s>T6z? z)`ZIH^L8*{(=1i=|xd z*rrHjnFhsl7v{`PtTG)zt3j?FARA=wZ2XZ@YCf~$fA?XHq-vqYNj*3=Ot#26`2|_b5vvf)ca)oRwhq#RPzd@A=**QU8DbA)Q?hw!!e0BgOET;%3FYXt-iEi zF7&;OSnF?)sKxQvsZUVHw?HsAc$1QUBsD#gzg3N+_c?Tzz2sXrK?rz1B8EoHVrh&Rr*Ez zB zJIVBJS9&*+DMd0nk&KUDIC*d&J_k=8M3e30=TDQe{AUEXXR?xPYI@crBdEjv;0Iox-4qk-R3E)xc;0aE9O$J+m_l2lnr+>5t z3H(oR@_(HwPy)OMF6SQtsbyYvbNOSrZ-dQKbAM8UxdZ}Y2ngg}3AJ?Zb@r4x2e!vc zonJm0DXWd)|9UqvREiAkM23z89P9N#YX|wMp7-ro~mik?F;a_(!7YK z-7Dx;WryRxM~Ys8*3MK$DH44mWh<5+}Hu$Wxm5`Gpr?0~+BZ=0`Gd>iJy xu!gNodc6 literal 0 HcmV?d00001 diff --git a/software/test_software/__pycache__/mqtt_callbacks.cpython-311.pyc b/software/test_software/__pycache__/mqtt_callbacks.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b1d64e3ae551a58e051c13c357f4d8b50cca80c5 GIT binary patch literal 1092 zcmZuvPiqrF6rb6hP1B@RsgWK;T@XyS*e>)eg3wzrpb>-~Ot-t!X4~DJ?aai|&_fTN z`~nrL(32wi0X+C2wo83jFb+{b0{jNADs6zqpiR(5fSSm$8opKB18ojxe@S26N5`NqOMt%_pWxZA zFb9H>4reyH*Ck6s-worGap@(}!MUZEz?c-Bb>Z`HojO)t@}rO@GB0-o<$))?+}en| zO_5hQ^#n^=J_~4GNjXa?mmw8G{nS=+zU{VIOx@VyK8@UE=67NUC0rU&Uvid&!f(?S zT-^enChjivWSjDqke&=7^wX6kSFjDa>v8HzDr9Tq#XC~Ad@qXDJ%3v)rh9os|0PLO z=qh|cB`s`#19V0zzew#Tsb#kxW~6pP7EZ}RR*XKJK|WD9lrm_=asLItA(A8h5DM_V zu{H9KP+)*xloEk&g0JpsIYJ*xS5|;Dx-)ansZYb#G_Ye}&D*4Qw`#M*)@88k-D=(5 z4Q1PY#+tT6ax3K_)L|m^_9Es5Ts@MTJ4`r)E6RCEEldn!@k#K6N=j9Nt2WdO727kg zpdyGT00-zdnLI16y?gngm6g|Ee4qVsXJBQ`)zjvyf&JGstkORO&>IIn1yROYRK2Vz zoCo6)g(eVlb!!@pZmn6gUvPl{%&$Qt$GVWK8HWMC0jq;#M}0TQb_)7o)X4l69GieC z?g8k0v&w_xn*cCQNd1)5vtsCM9NchB-AG( 5: + #break + msg_count=1 + + +def run(): + client = connect_mqtt() + client.loop_start() + while 1: + if client.is_connected(): + print(client.is_connected()) + publish(client) + + #else: + #client.loop_stop() + + client.loop_stop() + + +if __name__ == '__main__': + run() diff --git a/software/test_software/weather_station_rs485_client.py b/software/test_software/weather_station_rs485_client.py index 8548fba..70ec0c2 100644 --- a/software/test_software/weather_station_rs485_client.py +++ b/software/test_software/weather_station_rs485_client.py @@ -21,12 +21,68 @@ import sys import logging import minimalmodbus import datetime +import subprocess +import os from pathlib import Path from modbus_control import ModBusController from config_reader import config_reader from modbus_definition_file_reader import definition_file_reader +import paho.mqtt.client as mqtt +from paho.mqtt.client import CallbackAPIVersion +from paho.mqtt.properties import Properties +from paho.mqtt.packettypes import PacketTypes +properties=Properties(PacketTypes.PUBLISH) +properties.MessageExpiryInterval=30 # in seconds +import ssl + +version = '3' # or '5' +mqtt_transport = 'tcp' # or 'websockets' + +if version == '5': + client = mqtt.Client(CallbackAPIVersion.VERSION2, client_id="myPy", + transport=mqtt_transport, + protocol=mqtt.MQTTv5) +if version == '3': + client = mqtt.Client(CallbackAPIVersion.VERSION2, client_id="myPy", + transport=mqtt_transport, + protocol=mqtt.MQTTv311, + clean_session=True) +#client.username_pw_set("user", "password") + +import mqtt_callbacks +client.on_message = mqtt_callbacks.on_message; +client.on_connect = mqtt_callbacks.on_connect; +#client.on_publish = mqtt_callbacks.on_publish; +client.on_subscribe = mqtt_callbacks.on_subscribe; + +mqtt_broker = 'mqtt.meezenest.nl' +mqtt_port = 1883 +mqtt_keepalalive=60 +mqtt_topic = 'topic/important' + +if version == '5': + from paho.mqtt.properties import Properties + from paho.mqtt.packettypes import PacketTypes + properties=Properties(PacketTypes.CONNECT) + properties.SessionExpiryInterval=30*60 # in seconds + client.connect(mqtt_broker, + port=mqtt_port, + clean_start=mqtt.MQTT_CLEAN_START_FIRST_ONLY, + properties=properties, + keepalive=60); + +elif version == '3': + client.connect(mqtt_broker, port=mqtt_port, keepalive=mqtt_keepalalive); + +client.loop_start(); + +while 1: + client.publish(mqtt_topic,'Cedalo Mosquitto is awesome',2,properties=properties); + time.sleep(1) + + def setup(): config_file = "config.yml" definition_file = "modbus_registers.yaml" @@ -104,6 +160,35 @@ def data_logger(data, configuration): except: logging.warning("Could not write to file: " + new_filename) +# It is not possible to send multiple beacons in a short time. Call this function not faster than every 3 seconds or so. +def send_data_to_aprs(weather_data, configuration): + + # Define the Bash script and its arguments as a list + script = "/usr/sbin/beacon" + + if configuration['digi_path'] == 0: + arguments = ["-d", f"{configuration['destination']}", "-s", "ax1", ":PE1RXF-3 :test2"] + else: + arguments = ["-d", f"{configuration['destination']} {configuration['digi_path']}", "-s", "ax1", ":PE1RXF-3 :test3"] + + # Combine the script and its arguments into a single list + command = [script] + arguments + + # Run the script + logging.debug(command) + try: + result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + # Check the result + if result.returncode == 0: + logging.info("Send data to APRS radio.") + else: + logging.error("Failed to send data to APRS radio.") + logging.error(f"Reason: {result.stderr}") + except Exception as e: + logging.error("Failed to send data to APRS radio.") + logging.error(f"Command returned: {e}") + + def send_data_to_mqtt(data, configuration, modbus_registers): #logging.debug(modbus_registers) @@ -117,7 +202,10 @@ def send_data_to_mqtt(data, configuration, modbus_registers): # Go through every input register and match the unit and description with the value for index2, entry2 in enumerate(data['InputRegisters']): - logging.debug(entry1['input_register_names'][index2][0] + ": " + str(entry2) + entry1['input_register_names'][index2][1]) + print(entry1['input_register_names'][index2][2]) + # Scale values + entry2 = entry2/ (10^entry1['input_register_names'][index2][2]) + logging.debug(entry1['input_register_names'][index2][0] + ": " + str(round(entry2,1)) + entry1['input_register_names'][index2][1]) logging.debug("Send data to MQTT broker.") @@ -137,13 +225,24 @@ def ReconnectModBus(configuration): logging.info("Reconnected to ModBus dongle.") -#print("Enable heater function") -#controller.enable_heater() - Configuration, Controller, ModbusAddresses, ModbusRegisters = setup() +LoopCounter = 0 while (1): time.sleep(3) # Sleep for 3 seconds + + # Send APRS telemetry every 10 cycles = every 10 minutes + LoopCounter = LoopCounter + 1 + if LoopCounter >= 1: + + # Send data to LoRa radio via external program (/usr/sbin/beacon). Make sure we use all radios defined in the configuration file. + for entry in Configuration.config_file_settings['aprs']: + send_data_to_aprs(1, entry) + # We cannot send multiple APRS messages in a short period of time, so we wait 3 deconds between messages. + time.sleep(3) + + LoopCounter = 0 + ModBusData={} # Loop through all configured ModBus devices and try to read the sensor data @@ -157,6 +256,10 @@ while (1): ModBusData['Type'] = Controller[index].get_type() ModBusData['TypeString'] = Controller[index].get_type_string() ModBusData['InputRegisters'] = Controller[index].read_all_input_registers() + + # Keep the watchdog from resetting the ModBusdevice + #Controller[index].toggle_watchdog() + NoError = True except minimalmodbus.NoResponseError: # Handle communication timeout @@ -183,7 +286,6 @@ while (1): if NoError == True: try: - DeviceType = ModbusRegisters.definition_file_data['devices'][ModBusData['Type']] logging.debug("Found Mees Electronics sensor on ModBus address " + str(current_modbus_device)) logging.debug("Serial number: " + hex(ModBusData['ID'][1]) + " " + hex(ModBusData['ID'][2]) + " " + hex(ModBusData['ID'][3])) logging.debug("Device type: " + str(ModBusData['Type']) + " (" + ModBusData['TypeString'] + ")") @@ -198,3 +300,5 @@ while (1): # Send sensor data to MQTT broker send_data_to_mqtt(ModBusData, Configuration, ModbusRegisters.definition_file_data) + +