Added poll functions

master v1.1.0
marcel 10 months ago
parent 1ae761d989
commit 844b23c20c
  1. 3
      CHANGELOG.md
  2. 8
      README.md
  3. 105
      aprs-mqtt-bridge.py
  4. 57
      aprs-mqtt-bridge.yml

@ -14,3 +14,6 @@ First working version.
## [1.0.1] - 2023-01-14
Changed: aprs_status (published on the MQTT broker) now returns actual state of transmision (sending, retrying, send or failed) instead if just 'ready' and 'busy'.
## [1.1.0] - 2023-07-08
Added: APRS nodes can now be polled to get there actual status. An extra section in the YAML configuration file is added for this functionality.

@ -22,8 +22,14 @@ global:
retry: 3 # Try this often before giving up
destination: APRX29 # Destination or program ID
digi_path: WIDE2-1 # Digi path of APRS messages
poll_rate: 300 # Number of second between polling
#beacon_program: /usr/sbin/beacon # The external AX.25 beacon program => obsolete
# Poll the status af these clients (IMPORTANT: the calls must also be defined in the topics section)
poll:
- call: PE1RXF-5
- call: PE1RXF-6
- call: PE1RXF-8
topics:
# MQTT topic: 5GHz dish at workshop (must be unique name)
@ -54,7 +60,7 @@ topics:
```
The configuration entry 'global:retry' sets the amount of APRS message retries we attempt before giving up. The configuration entry 'global:transmit_rate' sets the time between retries.
For now, the state of the outputs of the APRS nodes is not published to the MQTT broker. In the future, this could be implemented in the software. It is possible to poll the nodes via command '06' to get the current state of the outputs.
When a node is added to the poll: call: entry its status will be polled on a regular base. The poll rate is set in the global entry. This is the poll interval between every transmision. When three nodes are defined, a node is polled every 3*poll_rate seconds.
## Requirements

@ -2,7 +2,7 @@
"""
A bridge between APRS messaging and MQTT, designed to control my lora_aprs_node_pico
(C)2022 M.T. Konstapel https://meezenest.nl/mees
(C)2022-2023 M.T. Konstapel https://meezenest.nl/mees
This file is part of aprs-mqtt-bridge.
@ -40,6 +40,7 @@ class mqtt_settings:
#transmit_rate
#retry
#topics
#poll
state = 'ready'
aprs_state = 'idle'
pass
@ -201,6 +202,7 @@ def read_config():
cfg = yaml.load(f, Loader=SafeLoader)
mqtt.topics = cfg['topics']
mqtt.poll = cfg['poll']
#print(mqtt.topics)
#for topic in mqtt.topics:
# print(topic['name'])
@ -240,6 +242,11 @@ def read_config():
except:
print ("Error in configuration file: no retry defined.")
sys.exit(1)
try:
mqtt.poll_rate = cfg['global']['poll_rate']
except:
print ("Error in configuration file: no poll_rate defined.")
sys.exit(1)
mqtt.client_id = f'{mqtt.topic_root}-{random.randint(0, 1000)}'
@ -255,6 +262,76 @@ def add_subscribtions_from_configfile(client):
subscribe(client,current_topic)
print('Topic ' + topics['name'] + ' added')
# Loop through poll list and activate then
def poll_clients(count=[0]):
# How many clients do we have to poll?
nr_of_clients = len(mqtt.poll)
current_call = mqtt.poll[count[0]]['call']
print ('Polling ' + current_call)
# Now we have to figure out the other paramters of the client. These are defined in the topics section of the configuration file
for topics in mqtt.topics:
if current_call == topics['call']:
current_port = topics['port']
source_call = topics['server']
#print ('Server: ' + source_call)
#print ('AX.25 port: ' + current_port)
break
# Find call of ax25 port
for position in range(len(axdevice)):
if axdevice[position] == current_port:
port_call = axaddress[position]
#print (port_call)
message = ':' + topics['call'].ljust(9) + ':' + '06'
#print (port_call + ' ' + source_call + ' ' + mqtt.destination + ' ' + message)
send_ax25(port_call, source_call, mqtt.destination, 0, message)
# Every time this functtion is called it moves up one call and wraps around
count[0] += 1
if count[0] >= nr_of_clients:
count[0] = 0
# Decode response of clients to sensible MQTT messages
def process_polling(call, data, mqtt_client):
print ('Received status: ' + call + " " + data)
if len(data) != 5:
return
for cnt in range(len(data)):
if data[-1*cnt] != '0' and data[-1*cnt] != '1':
print('Invalid response')
return
# Now we have to figure out the other paramters of the client. These are defined in the topics section of the configuration file
for topics in mqtt.topics:
if call == topics['call']:
current_name = topics['name']
current_poll_bit = topics['poll_bit']
print ('MQTT topic: ' + current_name)
#print (current_poll_bit)
inverted_poll_bit = -1*current_poll_bit
status_bit = data[inverted_poll_bit]
if status_bit == '1':
print ('ON')
state = 'ON'
else:
print('OFF')
state = 'OFF'
topic = mqtt.topic_root + '/' + current_name + '/state'
publish(mqtt_client,topic,state)
def process_message(data, payload):
#print ('Payload: '+ payload)
#print (data['call'])
@ -298,6 +375,7 @@ def run():
add_subscribtions_from_configfile(client)
#topic = mqtt.topic_root + '/set'
#subscribe(client,topic)
client.loop_start()
# Send ready to MQTT broker to indicate we are meaning business
@ -307,6 +385,8 @@ def run():
mqtt.state = 'busy'
aprs.time_out_timer = time.time()
aprs.poll_timer = time.time()
while True:
if aprs.request_to_send == 1:
@ -334,7 +414,22 @@ def run():
#print(digipeaters)
#print("Payload = %s"%payload)
#print("")
# Test if received packet is from a polled station
for poll in mqtt.poll:
if poll['call'] == source:
#print ('Received packet from polled station: ' + payload)
# split payload at colon. If it is a valid reply, we should get three
# substrings: the first in empty, the second with the call of the ax25
# interface and the thirth with the status of the outputs
split_message=payload.split(":")
if len(split_message) == 3:
if split_message[1].replace(" ", "") == axaddress[port]:
if len(split_message[2]) == 5:
#print ('Received status: ' + source + " " + split_message[2])
process_polling(source, split_message[2], client)
# Waiting for acknowledge...
if aprs.wait_for_ack != 0:
if source == aprs.call_of_wait_for_ack:
# split payload at colon. If it is a valid acknowledge, we should get three
@ -385,6 +480,14 @@ def run():
publish(client,topic,mqtt.aprs_state)
mqtt.state = 'ready'
if time.time() - aprs.poll_timer > mqtt.poll_rate:
poll_clients()
# Reset poll timer
aprs.poll_timer = time.time()
#print('Poll clients')
if __name__ == '__main__':
#sys.stdout = sys.stderr = open('debug.log', 'w')

@ -7,8 +7,14 @@ global:
retry: 3 # Try this often before giving up
destination: APRX29 # Destination or program ID
digi_path: WIDE2-1 # Digi path of APRS messages
poll_rate: 300 # Number of second between polling
#beacon_program: /usr/sbin/beacon # The external AX.25 beacon program => obsolete
# Poll the status af these clients (IMPORTANT: the calls must also be defined in the topics section)
poll:
- call: PE1RXF-5
- call: PE1RXF-6
- call: PE1RXF-8
topics:
# MQTT topic: 5GHz dish at workshop (must be unique name)
@ -16,6 +22,7 @@ topics:
call: PE1RXF-6 # Call of node to which commands below are send
server: PE1RXF-3 # Call of APRS server sending the commands
port: ax2 # Name of AX.25 port to use
poll_bit: 2 # Bit in status response of client (1=b00001, 2=b00010, 3=b00100, 4=b01000, 5=b10000)
command:
- payload: 'ON' # This is the payload we have to receive
cmd: 33{33 # This command is send to the node
@ -29,6 +36,7 @@ topics:
call: PE1RXF-6
server: PE1RXF-3
port: ax2
poll_bit: 1
command:
- payload: 'ON'
cmd: 31{31
@ -37,11 +45,12 @@ topics:
cmd: 30{30
response: ack30
# MQTT topic: 5GHz dish at tiny house
- name: ubiquity_dish_ptp_tiny_house
# Server at tiny house
- name: server_tiny_house
call: PE1RXF-5
server: PE1RXF-3
port: ax2
poll_bit: 3
command:
- payload: 'ON'
cmd: 35{35
@ -50,11 +59,26 @@ topics:
cmd: 34{34
response: ack34
# MQTT topic: 5GHz dish at tiny house
- name: ubiquity_dish_ptp_tiny_house
call: PE1RXF-5
server: PE1RXF-3
port: ax2
poll_bit: 1
command:
- payload: 'ON'
cmd: 31{31
response: ack31
- payload: 'OFF'
cmd: 30{30
response: ack30
# MQTT topic: 5GHz dish in orchard
- name: ubiquity_dish_ptmp_orchard
call: PE1RXF-8
server: PE1RXF-3
port: ax2
poll_bit: 1
command:
- payload: 'ON'
cmd: 31{31
@ -68,6 +92,7 @@ topics:
call: PE1RXF-8
server: PE1RXF-3
port: ax2
poll_bit: 2
command:
- payload: 'ON'
cmd: 33{33
@ -75,3 +100,31 @@ topics:
- payload: 'OFF'
cmd: 32{32
response: ack32
# MQTT topic: QRP-lab QDX transceiver at tiny house
- name: qdx_transceiver_tiny_house
call: PE1RXF-5
server: PE1RXF-3
port: ax2
poll_bit: 2
command:
- payload: 'ON'
cmd: 33{33
response: ack33
- payload: 'OFF'
cmd: 32{32
response: ack32
# MQTT topic: Switched 12V output at tiny house
- name: switched_12v_tiny_house
call: PE1RXF-5
server: PE1RXF-3
port: ax2
poll_bit: 4
command:
- payload: 'ON'
cmd: 37{37
response: ack37
- payload: 'OFF'
cmd: 36{36
response: ack36

Loading…
Cancel
Save